├── .gitignore
├── CHANGELOG.md
├── phpunit.xml.dist
├── composer.json
├── src
└── compose.php
├── LICENSE
├── tests
└── ComposeTest.php
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | vendor
2 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | CHANGELOG
2 | =========
3 |
4 | ### 1.0.1 (2013-04-20)
5 |
6 | * Validate arguments as callable (@bsdlite)
7 |
8 | ### 1.0.0 (2013-04-18)
9 |
10 | * Initial release
11 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 | ./tests/
10 |
11 |
12 |
13 |
14 |
15 | ./src/
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "igorw/compose",
3 | "description": "Function composition.",
4 | "keywords": ["functional", "composition"],
5 | "license": "MIT",
6 | "authors": [
7 | {
8 | "name": "Igor Wiedler",
9 | "email": "igor@wiedler.ch"
10 | }
11 | ],
12 | "autoload": {
13 | "files": ["src/compose.php"]
14 | },
15 | "extra": {
16 | "branch-alias": { "dev-master": "1.0-dev" }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/compose.php:
--------------------------------------------------------------------------------
1 | $fn) {
12 | if (!is_callable($fn)) {
13 | throw new \InvalidArgumentException(
14 | sprintf('Argument %d to compose() is not callable.', $i));
15 | }
16 | }
17 |
18 | $fns = func_get_args();
19 | $prev = array_shift($fns);
20 |
21 | foreach ($fns as $fn) {
22 | $prev = function ($x) use ($fn, $prev) {
23 | $args = func_get_args();
24 | return $prev(compose\apply($fn, $args));
25 | };
26 | }
27 |
28 | return $prev;
29 | }
30 |
31 | /** @api */
32 | function pipeline() {
33 | return compose\apply('igorw\compose', array_reverse(func_get_args()));
34 | }
35 |
36 | namespace igorw\compose;
37 |
38 | function apply($fn, $args) {
39 | return call_user_func_array($fn, $args);
40 | }
41 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013 Igor Wiedler
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is furnished
8 | to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/tests/ComposeTest.php:
--------------------------------------------------------------------------------
1 | assertNull($composed(null));
17 | $this->assertTrue($composed(true));
18 | $this->assertFalse($composed(false));
19 | $this->assertSame('foo', $composed('foo'));
20 | }
21 |
22 | function testComposeWithMultipleFuncs() {
23 | $composed = compose(
24 | function ($x) { return "baz($x)"; },
25 | function ($x) { return "bar($x)"; },
26 | function ($x) { return "foo($x)"; }
27 | );
28 | $this->assertSame('baz(bar(foo(x)))', $composed('x'));
29 | }
30 |
31 | function testComposeWithMultipleArgs() {
32 | $composed = compose(
33 | function ($x) { return "bar($x)"; },
34 | function ($a, $b, $c) { return "foo($a, $b, $c)"; }
35 | );
36 | $this->assertSame('bar(foo(a, b, c))', $composed('a', 'b', 'c'));
37 | }
38 |
39 | /**
40 | * @expectedException InvalidArgumentException
41 | */
42 | function testComposeWithInvalidArg() {
43 | compose('foo');
44 | }
45 |
46 | /**
47 | * @expectedException InvalidArgumentException
48 | */
49 | function testComposeWithJustOneInvalidArg() {
50 | $fn = function () {};
51 | compose($fn, 'foo', $fn);
52 | }
53 |
54 | function testPipelineWithMultipleFuncs() {
55 | $composed = pipeline(
56 | function ($x) { return "foo($x)"; },
57 | function ($x) { return "bar($x)"; },
58 | function ($x) { return "baz($x)"; }
59 | );
60 | $this->assertSame('baz(bar(foo(x)))', $composed('x'));
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # igorw/compose
2 |
3 | Function composition.
4 |
5 | Allows you to stitch functions together to form a pipeline. This can be useful
6 | if you have to transform data in many steps and you want to describe those
7 | steps on a high level.
8 |
9 | ## compose
10 |
11 | Generally, function composition means taking two functions `f` and `g`, and
12 | producing a new function `z`, which applies `f` to the result of `g`.
13 |
14 | z = compose(f, g)
15 | ; z(x) => f(g(x))
16 |
17 | This library provides a `compose` function that does just this.
18 |
19 | $z = igorw\compose($f, $g);
20 | var_dump($z($x));
21 |
22 | It supports an arbitrary number of functions to be composed via varargs.
23 |
24 | $z = igorw\compose($f, $g, $h, $i);
25 |
26 | The innermost function (the last one in the list) can take an arbitrary number
27 | of arguments, whereas the others may only take a single argument.
28 |
29 | $z = igorw\compose($f, $g);
30 | $z('a', 'b', 'c');
31 | // => $f($g('a', 'b', 'c'))
32 |
33 | ## pipeline
34 |
35 | `pipeline` is the same as `compose`, but the arguments are reversed. This is
36 | more easy to read in some cases, because you can list the functions in the
37 | order they will be called.
38 |
39 | It is quite similar to a unix pipe in that regard.
40 |
41 | ## Examples
42 |
43 | function transform_data($data) {
44 | return [
45 | 'name' => $data['firstname'].' '.$data['lastname'],
46 | ];
47 | }
48 |
49 | $transformJson = igorw\pipeline(
50 | function ($json) { return json_decode($json, true); },
51 | 'transform_data',
52 | 'json_encode'
53 | );
54 |
55 | $json = <<
65 | // {"name": "Igor Wiedler"}
66 | // {"name": "Beau Simensen"}
67 |
--------------------------------------------------------------------------------