├── .codeclimate.yml
├── .gitignore
├── .travis.yml
├── README.md
├── composer.json
├── docs
├── daydream.php
└── features.php
├── phpunit.xml
├── src
├── Concept
│ ├── EmitTrait.php
│ ├── Emittable.php
│ └── Value.php
├── Filter
│ ├── AppendTrait.php
│ ├── ChunkTrait.php
│ ├── EachTrait.php
│ ├── FilesTrait.php
│ ├── FilterTrait.php
│ ├── InfiniteTrait.php
│ ├── LimitTrait.php
│ ├── MapTrait.php
│ ├── SkipTrait.php
│ ├── SleepTrait.php
│ ├── StopIfTrait.php
│ └── ValuesTrait.php
├── Iterator
│ ├── AppendIterator.php
│ ├── ChunkIterator.php
│ ├── MapIterator.php
│ ├── PlainArrayIterator.php
│ ├── SkipIterator.php
│ └── ValuesIterator.php
├── Pipe.php
├── PipeIterator.php
├── PipenessTrait.php
└── functions.php
└── tests
├── AppendTest.php
├── BaseTestCase.php
├── BasicTest.php
├── ChunkTest.php
├── EachTest.php
├── EmittableTest.php
├── FilesTest.php
├── FilterTest.php
├── InfiniteTest.php
├── LimitTest.php
├── MapTest.php
├── OuterIteratorTest.php
├── PipeIterationTest.php
├── SkipTest.php
├── SleepTest.php
├── StopIfTest.php
├── TestCase
├── BaseIteratorTestCase.php
├── CallbackTestCase.php
└── TestListener.php
├── Tools
├── TestIterator.php
└── TestIteratorAggregate.php
├── ValueTest.php
└── ValuesTest.php
/.codeclimate.yml:
--------------------------------------------------------------------------------
1 | ---
2 | engines:
3 | phpcodesniffer:
4 | enabled: true
5 | phpmd:
6 | enabled: true
7 | ratings:
8 | paths:
9 | - "**.php"
10 | - "**.module"
11 | - "**.inc"
12 | exclude_paths: ["tests"]
13 |
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled source #
2 | ###################
3 | *.com
4 | *.class
5 | *.dll
6 | *.exe
7 | *.o
8 | *.so
9 |
10 | # Packages #
11 | ############
12 | # it's better to unpack these files and commit the raw source
13 | # git has its own built in compression methods
14 | *.7z
15 | *.dmg
16 | *.gz
17 | *.iso
18 | *.jar
19 | *.rar
20 | *.tar
21 | *.zip
22 |
23 | # Logs and databases #
24 | ######################
25 | *.log
26 | *.sql
27 | *.sqlite
28 |
29 | # OS generated files #
30 | ######################
31 | .DS_Store
32 | .DS_Store?
33 | ._*
34 | .Spotlight-V100
35 | .Trashes
36 | ehthumbs.db
37 | Thumbs.db
38 |
39 | # MISC #
40 | ########
41 |
42 | nbproject
43 | vendor
44 | composer.lock
45 | .idea
46 | playground
47 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - 7.0
5 | - 5.6
6 | - 5.5
7 | - hhvm
8 |
9 | install:
10 | - composer require satooshi/php-coveralls:~0.6@stable
11 |
12 | before_script:
13 | - curl -s http://getcomposer.org/installer | php
14 | - php composer.phar install --dev
15 | - mkdir -p build/logs
16 |
17 | script:
18 | - phpunit --coverage-clover build/logs/clover.xml
19 |
20 | after_success:
21 | - sh -c 'if [ "$TRAVIS_PHP_VERSION" != "hhvm" ]; then php vendor/bin/coveralls -v; fi;'
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Pipes
2 | ==============
3 | [](https://travis-ci.org/tacone/pipes)
4 | [](https://coveralls.io/r/tacone/pipes)
5 | [](https://scrutinizer-ci.com/g/tacone/pipes/?branch=master)
6 |
7 | Pipes is a thin wrapper around PHP SPL iterators and generators.
8 |
9 | With Pipes, you can write code like this:
10 |
11 | ```php
12 | $result = p($array)->filter(function($v) {
13 | return $v % 2;
14 | })
15 | ->each('var_dump')
16 | ->limit(100)
17 | ->toArray();
18 | ```
19 |
20 | Unlike many collection libraries (such as underscore.php or
21 | Laravel's Illuminate/Collection) each step will be executed
22 | sequentially for each item. For instance, in the sample code
23 | above, only the first 101 even numbers would be printed
24 | (the 101th will reach each() but not be passed through limit())
25 |
26 | The advantages are:
27 | - you can traverse enourmous arrays using less memory
28 | - you don't execute unnecessary operations when you need just
29 | a subset.
30 | - you can control the flow
31 | - the resulting code is neat and pretty
32 |
33 | Of course this is just the beginning.
34 |
35 |
36 | ## Features
37 |
38 | The current feature set is pretty minimal. Below is a syntetic description
39 | of the current methods. Keep in mind they are all documented and available
40 | for your favorite IDE auto-completion.
41 |
42 | ```php
43 | // filters
44 | p($array)->chunk($size); // spits chunks like array_chunk
45 | p($array)->each($function); // executes $function for each element
46 | p($array)->filter($function); // uses \FilterIteratorCallback
47 | p()->values(); // returns all the elements, reindexing the keys
48 |
49 | // flow control
50 | p($array)->limit($skip = 0, $max); // uses \LimitIterator
51 | p($array)->skip($num); // skips the first $num elements
52 | p($array)->continueIf($callback); // continues as long as callback gives true
53 | p($array)->stopIf($callback); // stops if the callback is true
54 | p($array)->sleep($seconds); // sleeps for $seconds seconds at each iteration
55 |
56 | // transformators
57 | p($array)->map($function); // sort of array_map. Take a look to the tests.
58 |
59 | // iterator factories
60 | p()->files($globPattern); // uses \GlobIterator, accepts the same args
61 |
62 | // terminators
63 | p()->toArray(); // returns a key=>value array. Last key wins.
64 | p()->toValues(); // returns an indexed array. No key collision.
65 |
66 | // variants
67 | p()->toIterator(); // returns a full IteratorIterator/OuterIterator Pipe
68 | p()->toTraversable(); // returns an IteratorAggregate Pipe (much faster)
69 | ```
70 |
71 | ## Example:
72 |
73 | Here is a quasi real world example: a simple scraper that handles retries,
74 | failures, etc.
75 |
76 | ```php
77 | $website = new Website();
78 |
79 | $initialState = new stdClass();
80 | $initialState->categoryId = 1;
81 | $initialState->postId = 1;
82 | $initialState->tries = 1;
83 | $initialState->failures = 0;
84 |
85 | $context = clone $initialState;
86 |
87 | $pipe = p([$context])
88 | // transform the single item array into an infinite stream of
89 | // items consisting of the same context instance
90 | ->infinite()
91 | // make sure not to loop more than 100 times, we don't need
92 | // too much data
93 | ->limit(100)
94 | // call some method to download the HTML of the page
95 | ->map([$website, 'downloadPage'])
96 | // pass the HTML to some method to turn it into a database record
97 | ->map([$website, 'pageToRecord'])
98 | // perform some custom check or transformation if you wish
99 | ->map(function ($record) use ($website, $context) {
100 | return $record;
101 | })
102 | // dump every record to the screen or to your custom logger
103 | ->each(function () use ($context) {
104 | var_dump($context);
105 | })
106 | // here is a sample logic for handling retries, failures and enumeration
107 | // the beauty here is you keep all the state into a single external object
108 | // that you can easily serialize and save somewhere.
109 | ->each(function ($record) use ($context, $initialState) {
110 | if ($record['page_found']) {
111 | $context->postId++;
112 | $context->tries = $initialState->tries;
113 | $context->failures = $initialState->failures;
114 | return;
115 | }
116 |
117 | if ($context->tries < 3) {
118 | $context->tries++;
119 | return;
120 | }
121 |
122 | $context->postId++;
123 | $context->failures++;
124 | $context->tries = $initialState->tries;
125 |
126 | if ($context->failures >= 3) {
127 | $context->categoryId++;
128 | $context->postId = 1;
129 | $context->failures = $initialState->failures;
130 | $context->tries = $initialState->tries;
131 | }
132 | })
133 | // don't save the errors in the database
134 | ->filter(function ($record) {
135 | return !empty($record['page_found']);
136 | })
137 | // save the record in the database
138 | ->each(function ($record) {
139 | // .. up to you!
140 | })
141 | // sleep for 1.5 seconds to avoid bringing down the website
142 | ->sleep(1.5);
143 |
144 | // Everything ok, isn't it? Notice than nothing happened yet.
145 | // $pipe is now an aggregate iterator, which does not do anything
146 | // until you cycle on it or you call toArray();
147 |
148 | foreach ($pipe as $record) {
149 | // you can leave this empty or insert your custom logic here
150 | }
151 |
152 | // if you don't need logic, you may just run it with
153 | $results = $pipe->toArray();
154 |
155 | // and if you do it again, it will run again :)
156 | $results = $pipe->toArray();
157 | ```
158 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pipes/pipes",
3 | "description": "A thin wrapper over PHP iterators",
4 | "license": "MIT",
5 | "authors": [
6 | {
7 | "name": "tacone",
8 | "email": "known-only@to.me"
9 | }
10 | ],
11 | "autoload": {
12 | "psr-4": {
13 | "Pipes\\": "src/",
14 | "Pipes\\Test\\": "tests/"
15 | },
16 | "files": ["src/functions.php"]
17 | },
18 | "require-dev": {
19 | "phpunit/phpunit": "@stable"
20 | },
21 | "prefer-stable" : true,
22 | "minimum-stability": "dev"
23 | }
24 |
--------------------------------------------------------------------------------
/docs/daydream.php:
--------------------------------------------------------------------------------
1 | getUsers())->limit(1000)->map(function($user) {
9 | // process the user
10 | return p::emit($user['id'], $user);
11 | })->each(function($userData){
12 | // save the data
13 | });
14 |
15 | // alternative (PHP 5.5) syntax
16 | p($this)->map(function($value, &$key){
17 | $key = 4545;
18 | });
19 |
20 | // alternative (PHP 5.5) syntax
21 | p($this)->map(function($value, $key){
22 | yield $key => $value;
23 | });
24 |
25 | p($this)->map('$k=$v*2');
26 |
27 |
28 | /**
29 | * process max 100 users at once, 1000 times
30 | */
31 |
32 | p($this->getUsers())->chunk(100)->limit(1000)->each(function($usersChunk) {
33 | foreach ( $usersChunk as $user )
34 | {
35 | print "$user->id \n";
36 | }
37 | });
38 |
39 | /**
40 | * Push and next
41 | */
42 |
43 | $p()->push($user)->next(); // $user
44 |
45 | /*
46 | * auto-next
47 | */
48 |
49 | $p($array)->each('myfunc')->autonext(); // loops to the end
50 | $p->push($item); // each will be executed
51 |
52 | $a = new ReflectionFunction('');
53 | //$a->get
54 |
55 |
56 |
57 |
58 | /**
59 | * distributing the effort
60 | */
61 | p($this->getUsers())->shard(3, function($v, $k, Pipe $p){
62 | echo "shard n. {$p->getShard()}: $k => $v \n";
63 | });
64 |
65 | // in memory sqlite?
66 | p($files)->where('size > 1000 AND mtime < NOW()-1000 ORDER BY extension, filename');
67 |
--------------------------------------------------------------------------------
/docs/features.php:
--------------------------------------------------------------------------------
1 | next();
9 | p()->current();
10 | p()->valid();
11 | p()->rewind();
12 | p()->getInnerIterator();
13 |
14 | // pipes
15 | p()->chunk($num); // ok
16 | p()->each($function, $step = 1); //ok
17 | p()->filter($function); //ok
18 | p()->limit($skip = 0, $max); //ok
19 | p()->map($function); //okdi
20 |
21 | // factory methods
22 | p()->emit($key = null, $value); //ok
23 | p()->flags($flag1, $flag2 =null, $flagN = null);
24 |
25 | // terminals
26 | p()->toValues(); // ok. outputs an indexed array.
27 | p()->toArray(); // ok. outputs an array. Keys preserved (last key wins)
28 | p()->toIterator(); // ok. outputs an array. Keys preserved (last key wins)
29 | p()->reduce($function = null); // outputs an array. Keys preserved. Conflicts handled by $function
30 |
31 |
32 | // timed pipes
33 | p()->maxTime($seconds); //also floats 0.001 etc
34 | p()->wait($seconds, $function = null); // !$function ? wait again
35 |
36 | // push to other queues (array, queues, chains)
37 | p()->filter($function, $queue);
38 | p()->map($function, $queue);
39 |
40 | // accumulators
41 | p()->groupBy($function);
42 | p()->sort();
43 |
44 | // queues
45 | p()->queues->sqlite('queue.db');
46 | p()->queues->file('queue.txt');
47 | p()->queues->json('queue.json'); // one json per line
48 | p()->queues->post($url, $moreParams);
49 |
50 | // anonymous pipes
51 | $func = p()->filter($foo)->map($bar);
52 | $result = $func('hello');
53 |
54 | $func->wrap($array);
55 |
56 |
57 | p($array)->map('p()->emit($k, $v)');
58 |
59 | // --- advanced stuff
60 |
61 | // caching
62 | p()->keep(3)->each(function(){
63 | $previous = p()->kept(-1); // also -2, -3
64 | });
65 |
66 | // map reduce
67 | p()->shard($function);
68 | p()->reduce($shard);
69 |
70 |
71 | /*
72 |
73 | Figure out how to:
74 |
75 | - properly replace an inneriterator with another in PipeIterator
76 | - properly append new iterms in PipeIterator
77 | - how to make PipeIterator a simple reference to a Pipe Instance
78 |
79 | - Pipe iterator should not wrap the Pipe instance but iterate on its
80 | inner iterator.
81 | - Thus, a new Pipe iterator should be initialized for each chainWith
82 | operation.
83 | - Pipe iterator should keep an instance of its pipe for easy retrieval.
84 | - Each chain operation on Pipe iterator should be passed on to its
85 | Pipe parent, but should be reflected on the iterator (?)
86 |
87 | Should we return a new Pipe instance for each chainWith?
88 | $pipe = p($collection)->filter('isEmpty');
89 | $map = $pipe->map('myfunc')->each('func')->limit(3);
90 | $discard = $pipe->each('logme');
91 | $result = $map->toArray();
92 | $discarded = $discard->toArray();
93 |
94 | */
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
17 | ./tests/
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | ./src
32 |
33 | ./docs
34 | ./vendor
35 | ./tests
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/src/Concept/EmitTrait.php:
--------------------------------------------------------------------------------
1 | = 2) {
12 | $emit = new Emittable($value);
13 | $emit->setKey($key);
14 | }
15 |
16 | return $emit;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/Concept/Emittable.php:
--------------------------------------------------------------------------------
1 | key) {
12 | throw new \LogicException('Emitted value has no key defined');
13 | }
14 |
15 | return $this->key->getValue();
16 | }
17 | public function setKey($key)
18 | {
19 | $this->key = new Value($key);
20 | }
21 | public function hasKey()
22 | {
23 | return is_object($this->key);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Concept/Value.php:
--------------------------------------------------------------------------------
1 | value = $value;
12 | }
13 | public function getValue()
14 | {
15 | return $this->value;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/Filter/AppendTrait.php:
--------------------------------------------------------------------------------
1 | getInnerIterator();
19 | // }
20 | //
21 | // if (is_a($me, '\\Pipes\\Iterator\\AppendIterator')) {
22 | // $appendIterator = $me;
23 | // } else {
24 | // $appendIterator = new AppendIterator();
25 | // $appendIterator->append($me->toIterator());
26 | // }
27 | //
28 | // $appendIterator->append($iterator);
29 | //
30 | // return $this->getRoot()->chainWith($appendIterator);
31 | // }
32 | //}
33 |
34 |
--------------------------------------------------------------------------------
/src/Filter/ChunkTrait.php:
--------------------------------------------------------------------------------
1 | chainWith(new \Pipes\Iterator\ChunkIterator($this->getIterator(), $size));
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Filter/EachTrait.php:
--------------------------------------------------------------------------------
1 | chainWith(
29 | new \CallbackFilterIterator($this->getIterator(),
30 | function () use ($______callback, $______allArgs
31 | ) {
32 | call_user_func_array($______callback, $______allArgs ? func_get_args() : [func_get_arg(0)]);
33 |
34 | return true;
35 | }));
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Filter/FilesTrait.php:
--------------------------------------------------------------------------------
1 |
12 | * $files = p->files('/tmp/*.txt');
13 | * foreach ($files as $f) {
14 | * echo $f .PHP_EOL;
15 | * }
16 | *
17 | *
18 | * Use true to get a SplFileInfo object for each file:
19 | *
20 | * $files = p->files('/tmp/*.txt');
21 | *
22 | *
23 | * You can also pass any GlobIterator or FilesystemIterator flag
24 | *
25 | *
26 | * $files = p->files('/tmp/*.txt', \FilesystemIterator::FOLLOW_SYMLINKS);
27 | *
28 | *
29 | * You are incoraged to download and use Symfony Finder for any advanced need:
30 | *
31 | *
32 | * $finder = new Finder();
33 | * $iterator = $finder
34 | * ->files()
35 | * ->name('*.php')
36 | * ->depth(0)
37 | * ->size('>= 1K')
38 | * ->in(__DIR__);
39 | *
40 | * // then you can use your new iterator with pipes :)
41 | * p($iterator)->each(function () {
42 | * //... do stuff
43 | * });
44 | *
45 | *
46 | *
47 | * @param string $path any file wildcard (ie:/tmp/*.txt). Use the full path!
48 | * @param int $flags any GlobIterator or FilesystemIterator flag
49 | *
50 | * @return \Pipes\Pipe
51 | */
52 | public function files($path, $flags = \GlobIterator::CURRENT_AS_PATHNAME)
53 | {
54 | if ($flags === true) {
55 | $flags = 0;
56 | }
57 |
58 | return new static (new \GlobIterator($path, $flags));
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/Filter/FilterTrait.php:
--------------------------------------------------------------------------------
1 | chainWith(new \CallbackFilterIterator($this->toIterator(), $callback));
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Filter/InfiniteTrait.php:
--------------------------------------------------------------------------------
1 | chainWith(new \InfiniteIterator($this->toIterator()));
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/Filter/LimitTrait.php:
--------------------------------------------------------------------------------
1 | p()->limit(5) will return only the first five elements.
13 | * p()->limit(2, 5)
will skip 2 element, and return the next 5.
14 | *
15 | * @param $boundary1
16 | * @param bool $boundary2
17 | *
18 | * @return \Pipes\Pipe
19 | */
20 | public function limit($boundary1, $boundary2 = false)
21 | {
22 | if (func_num_args() == 1 || $boundary2 === false) {
23 | $offset = 0;
24 | $count = $boundary1;
25 | } else {
26 | $offset = $boundary1;
27 | $count = $boundary2;
28 | }
29 | if (!$count) {
30 | return $this->chainWith(new \ArrayIterator([]));
31 | }
32 |
33 | return $this->chainWith(new \LimitIterator($this->toIterator(), $offset, $count));
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Filter/MapTrait.php:
--------------------------------------------------------------------------------
1 | p()->emit($key,$value)
16 | *
17 | * p()->map(function ($value, $key, $iterator) {
18 | * return p()->emit($key.'_new', $value);
19 | * });
20 | *
21 | *
22 | * @param callable $callback
23 | *
24 | * @return \Pipes\Pipe
25 | */
26 | public function map(callable $______callback)
27 | {
28 | $iterator = $this->getIterator();
29 | $pipe = $this;
30 |
31 | if (!is_array($______callback)) {
32 | $reflectionFunction = new \ReflectionFunction($______callback);
33 | } else {
34 | $reflectionFunction = new \ReflectionMethod($______callback[0], $______callback[1]);
35 | }
36 |
37 | if ($reflectionFunction->isGenerator()) {
38 | $generator = $______callback;
39 | } else {
40 | $generator = function ($iterator) use ($pipe, $______callback) {
41 | foreach ($iterator as $key => $value) {
42 | // yield $key => $pipe->executeCallback($______callback, true, $value, $key, $iterator);
43 | $value = $pipe->executeCallback($______callback, true, $value, $key, $iterator);
44 | if ($value instanceof Emittable) {
45 | if ($value->hasKey()) {
46 | $key = $value->getKey();
47 | }
48 | $value = $value->getValue();
49 | }
50 | yield $key => $value;
51 | }
52 | };
53 | }
54 |
55 | return $this->chainWith($generator($iterator));
56 | // return $this->chainWith(new \Pipes\Iterator\MapIterator($this->toIterator(), $callback));
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/Filter/SkipTrait.php:
--------------------------------------------------------------------------------
1 | chainWith(new \Pipes\Iterator\SkipIterator($this->getIterator(), $num));
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/Filter/SleepTrait.php:
--------------------------------------------------------------------------------
1 | chainWith(
17 | new \CallbackFilterIterator($this->getIterator(),
18 | function () use ($seconds) {
19 | usleep($seconds * 1000000);
20 |
21 | return true;
22 | }));
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Filter/StopIfTrait.php:
--------------------------------------------------------------------------------
1 | getIterator();
29 | $pipe = $this;
30 |
31 | $generator = function () use ($pipe, $iterator, $______callback, $______allArgs) {
32 | foreach ($iterator as $key => $value) {
33 | if ($pipe->executeCallback($______callback, $______allArgs, $value, $key, $iterator)
34 | ) {
35 | return;
36 | } // @codeCoverageIgnore
37 | yield $key => $value;
38 | }
39 | };
40 |
41 | return $this->chainWith($generator());
42 | }
43 |
44 | /**
45 | * Stops the iteration if the callback returns a true-ish value.
46 | * The current element will not be included.
47 | *
48 | * The passed callback will be invoked with the following argument:
49 | *
50 | * - $value (iterator's current())
51 | *
52 | * If the second parameter is true the following arguments will be added
53 | * to the call:
54 | *
55 | * - $key (iterator's key())
56 | * - $iterator (the iterator itself)
57 | *
58 | * @param $______callback
59 | * @param bool $______allArgs
60 | *
61 | * @return \Pipes\Pipe
62 | */
63 | public function continueIf($______callback, $______allArgs = false)
64 | {
65 | $iterator = $this->getIterator();
66 | $pipe = $this;
67 |
68 | $generator = function () use ($pipe, $iterator, $______callback, $______allArgs) {
69 | foreach ($iterator as $key => $value) {
70 | if (!$pipe->executeCallback($______callback, $______allArgs, $value, $key, $iterator)
71 | ) {
72 | return;
73 | } // @codeCoverageIgnore
74 | yield $key => $value;
75 | }
76 | };
77 |
78 | return $this->chainWith($generator());
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/Filter/ValuesTrait.php:
--------------------------------------------------------------------------------
1 | chainWith(new \Pipes\Iterator\ValuesIterator($this->getIterator()));
17 | }
18 |
19 | /*
20 | * Discards the keys, returns an indexed array.
21 | *
22 | * Shortcut for p($iterator)->values->toArray();
23 | */
24 | public function toValues()
25 | {
26 | return $this->values()->toArray();
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Iterator/AppendIterator.php:
--------------------------------------------------------------------------------
1 | getArrayIterator()->append($iterator);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/Iterator/ChunkIterator.php:
--------------------------------------------------------------------------------
1 | size = $size;
34 | }
35 |
36 | public function rewind()
37 | {
38 | parent::rewind();
39 | $this->next();
40 | $this->key = 0;
41 | }
42 |
43 | public function next()
44 | {
45 | $this->chunk = array();
46 | for ($i = 0; $i < $this->size && parent::valid(); ++$i) {
47 | $this->chunk[] = parent::current();
48 | parent::next();
49 | }
50 | $this->chunk ? $this->key++ : null;
51 | }
52 |
53 | public function key()
54 | {
55 | return $this->key;
56 | }
57 |
58 | public function current()
59 | {
60 | return $this->chunk;
61 | }
62 |
63 | public function valid()
64 | {
65 | return (bool) $this->chunk;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/Iterator/MapIterator.php:
--------------------------------------------------------------------------------
1 | callback = $callback;
15 | // }
16 | //
17 | // protected function map($value, $key)
18 | // {
19 | // $this->result = call_user_func($this->callback, $value, $key, $this);
20 | // }
21 | //
22 | // public function key()
23 | // {
24 | // if (!is_a($this->result, '\\Pipes\\Concept\\Emittable') || !$this->result->hasKey()) {
25 | // return $this->getInnerIterator()->key();
26 | // }
27 | //
28 | // return $this->result->getKey();
29 | // }
30 | //
31 | // public function current()
32 | // {
33 | // if (!is_a($this->result, '\\Pipes\\Concept\\Emittable')) {
34 | // return $this->result;
35 | // }
36 | //
37 | // return $this->result->getValue();
38 | // }
39 | //
40 | // public function accept()
41 | // {
42 | // return true;
43 | // }
44 | //
45 | // public function valid()
46 | // {
47 | // if (!$valid = $this->getInnerIterator()->valid()) {
48 | // return false;
49 | // }
50 | // $this->map(
51 | // $this->getInnerIterator()->current(),
52 | // $this->getInnerIterator()->key()
53 | // );
54 | //
55 | // return true;
56 | // }
57 | //}
58 |
59 |
--------------------------------------------------------------------------------
/src/Iterator/PlainArrayIterator.php:
--------------------------------------------------------------------------------
1 | num = $num;
19 | }
20 |
21 | /**
22 | * Check whether the current element of the iterator is acceptable.
23 | *
24 | * @link http://php.net/manual/en/filteriterator.accept.php
25 | *
26 | * @return bool true if the current element is acceptable, otherwise false.
27 | */
28 | public function accept()
29 | {
30 | if ($this->skipped >= $this->num) {
31 | return true;
32 | }
33 |
34 | ++$this->skipped;
35 |
36 | return false;
37 | }
38 |
39 | public function rewind()
40 | {
41 | $this->skipped = 0;
42 | parent::rewind();
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/Iterator/ValuesIterator.php:
--------------------------------------------------------------------------------
1 | key = 0;
17 | }
18 |
19 | public function key()
20 | {
21 | return $this->key;
22 | }
23 |
24 | public function next()
25 | {
26 | parent::next();
27 | ++$this->key;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Pipe.php:
--------------------------------------------------------------------------------
1 | var = new PlainArrayIterator($var);
18 | }
19 | if ($var instanceof \Traversable) {
20 | $this->var = $var;
21 | }
22 | if (!func_num_args()) {
23 | $this->var = [];
24 | }
25 | }
26 |
27 | /**
28 | * This method is implemented just because it's required by the
29 | * \IteratorAggregate interface.
30 | *
31 | * Returns an instance of the last array/Traversable of the chain
32 | * Don't use this method: it won't return a Pipe instance:
33 | * no more chaining magic.
34 | * It may very well be a plain array.
35 | *
36 | * If you need an iterator, use toIterator() instead.
37 | *
38 | * @return array|\Traversable
39 | */
40 | public function getIterator()
41 | {
42 | return $this->var;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/PipeIterator.php:
--------------------------------------------------------------------------------
1 | var = $iterator;
31 |
32 | return $this;
33 | }
34 |
35 | public function toArray()
36 | {
37 | $iterator = $this->var;
38 |
39 | return iterator_to_array($iterator, true);
40 | }
41 |
42 | /**
43 | * Returns a complete iterator.
44 | * (an instance of \IteratorIterator which in turn
45 | * implements \OuterIterator).
46 | *
47 | * Use this method if you need to comply with type-hinting
48 | * from external libraries
49 | *
50 | * @return \IteratorIterator
51 | */
52 | public function toIterator()
53 | {
54 | $iterator = $this->unwrap();
55 |
56 | if (is_array($iterator)) {
57 | $iterator = new \ArrayIterator($iterator);
58 | }
59 |
60 | $appendIterator = new AppendIterator();
61 | $appendIterator->append($iterator);
62 |
63 | return new PipeIterator($appendIterator);
64 | }
65 |
66 | // /**
67 | // * @return \Traversable
68 | // */
69 | // protected function getRoot()
70 | // {
71 | // return $this->getBaseOfChain($this, true);
72 | // }
73 |
74 | /**
75 | * Returns the latest non pipe Iterator/Traversable in the
76 | * chain.
77 | *
78 | * @return \Traversable
79 | */
80 | public function unwrap()
81 | {
82 | $iterator = func_num_args() ? func_get_arg(0) : $this;
83 |
84 | return $this->getBaseOfChain($iterator);
85 | }
86 |
87 | public function getBaseOfChain($iterator, $pipeInstance = false)
88 | {
89 | $last = $iterator;
90 | while (true) {
91 | switch (true) {
92 | case is_a($iterator, '\\Pipes\\PipeIterator'):
93 | $last = $iterator;
94 | $iterator = $last->getInnerIterator();
95 | break;
96 | case is_a($iterator, '\\Pipes\\Pipe'):
97 | $last = $iterator;
98 | $iterator = $last->getIterator();
99 | break;
100 | case is_a($iterator, '\\ArrayIterator'):
101 | $iterator = $iterator->getArrayCopy();
102 | break;
103 | default:
104 | // --- was used by getRoot()
105 | // if ($pipeInstance) {
106 | // return $last;
107 | // }
108 |
109 | return $iterator;
110 | }
111 | }
112 | } // @codeCoverageIgnore
113 |
114 | protected function executeCallback($______callback, $______allArgs, $value, $key, $iterator)
115 | {
116 | return call_user_func_array(
117 | $______callback,
118 | $______allArgs ? [$value, $key, $iterator] : [$value]
119 | );
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/src/functions.php:
--------------------------------------------------------------------------------
1 | associative();
11 | // $obj = $this->p($array)->append($this->numerics());
12 | // $expected = $this->associative() + $this->numerics();
13 | // $this->assertSame($expected, $obj->toArray());
14 | // }
15 | //
16 | //// public function testAppendedToTheRoot()
17 | //// {
18 | //// $array = $this->associative();
19 | //// $obj = p($array);
20 | ////
21 | //// $obj->toIterator()->append($this->numerics());
22 | //// $expected = $this->associative() + $this->numerics();
23 | //// $this->assertSame($expected, $obj->toArray());
24 | //// }
25 | //}
26 |
27 |
--------------------------------------------------------------------------------
/tests/BaseTestCase.php:
--------------------------------------------------------------------------------
1 | toIterator() : $pipe;
24 | }
25 |
26 | public function run(\PHPUnit_Framework_TestResult $result = null)
27 | {
28 | if ($result === null) {
29 | $result = $this->createResult();
30 | }
31 | // test every request-format
32 | $first = 0;
33 | foreach ([false, true] as $useToIterator) {
34 | static::$useToIterator = $useToIterator;
35 | if (!$first) {
36 | $this->setUp();
37 | }
38 | $result->run($this);
39 | if ($first) {
40 | $this->tearDown();
41 | }
42 | }
43 |
44 | return $result;
45 | }
46 |
47 | protected function numerics()
48 | {
49 | return [1, 2, 3, 5];
50 | }
51 |
52 | protected function associative()
53 | {
54 | return [
55 | 'a' => 'apples',
56 | 'b' => 'bananas',
57 | 'c' => 'cherries',
58 | 'd' => 'damsons',
59 | 'e' => 'elderberries',
60 | 'f' => 'figs',
61 | ];
62 | }
63 |
64 | protected function types()
65 | {
66 | return [
67 | 'boolean' => true,
68 | 'boolean_false' => false,
69 | 'number' => 12,
70 | 'string' => 'hello world',
71 | 'array' => ['a', 'b', 'c'],
72 | 'float' => 1.7,
73 | 'object' => new \stdclass(),
74 | 'null' => null,
75 | ];
76 | }
77 |
78 | public function testMe()
79 | {
80 | //todo: strip this method without having phpunit fail all over
81 | }
82 |
83 | public function foreachArray($iterator)
84 | {
85 | $result = [];
86 | foreach ($iterator as $key => $value) {
87 | $result[$key] = $value;
88 | }
89 |
90 | return $result;
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/tests/BasicTest.php:
--------------------------------------------------------------------------------
1 | numerics();
12 | $this->assertEquals(p($array)->toArray(), $array);
13 |
14 | list($a, $b, $c, $d) = p($array)->toArray();
15 | $this->assertEquals($b, 2);
16 |
17 | $result = [];
18 | foreach (p($array)->toArray() as $value) {
19 | $result[] = $value;
20 | }
21 | $this->assertEquals($array, $result);
22 | $this->assertEquals(p($array)->toArray(), $result);
23 | }
24 |
25 | public function testForeach()
26 | {
27 | $array = $this->numerics();
28 | $obj = p($array);
29 | foreach ($obj as $v) {
30 | $this->assertSame(array_shift($array), $v);
31 | }
32 | }
33 |
34 | public function testDecorate()
35 | {
36 | $array = $this->numerics();
37 | $pipe = p($array);
38 | $obj = p($pipe);
39 | foreach ($obj as $v) {
40 | $this->assertSame(array_shift($array), $v);
41 | }
42 | }
43 |
44 | public function testChaining()
45 | {
46 | $array = $this->numerics();
47 | $pipe = p($array)->filter(function () {
48 | return false;
49 | });
50 | foreach ($pipe as $v) {
51 | $this->fail();
52 | }
53 |
54 | $array = $this->numerics();
55 | $pipe = p($array)
56 | ->filter(function () {
57 | return false;
58 | })
59 | ->filter(function () {
60 | return false;
61 | })
62 | ;
63 | foreach ($pipe as $v) {
64 | $this->fail();
65 | }
66 |
67 | $this->assertEquals(get_class(p($array)), get_class($pipe));
68 | }
69 |
70 | public function testToIterator()
71 | {
72 | $array = $this->associative();
73 | $this->assertInstanceOf(\Iterator::class, p($array)->toIterator());
74 | }
75 |
76 | public function testUnwrap()
77 | {
78 | $expected = $this->associative();
79 | $obj = p($expected);
80 | $this->assertSame($expected, $obj->unwrap());
81 |
82 | $expected = new TestIteratorAggregate($this->associative());
83 | $obj = p($expected);
84 | $this->assertSame($expected, $obj->unwrap());
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/tests/ChunkTest.php:
--------------------------------------------------------------------------------
1 | associative();
10 | $obj = p($array)->chunk(3);
11 | $expected = [
12 | ['apples', 'bananas', 'cherries']
13 | ,
14 | ['damsons', 'elderberries', 'figs'],
15 | ];
16 | $this->assertSame($expected, $obj->toArray());
17 | }
18 |
19 | public function testChunkOdd()
20 | {
21 | $array = $this->associative();
22 | $obj = p($array)->chunk(5);
23 | $expected = [
24 | ['apples', 'bananas', 'cherries', 'damsons', 'elderberries']
25 | ,
26 | ['figs'],
27 | ];
28 | $this->assertSame($expected, $obj->toArray());
29 | }
30 |
31 | public function testException()
32 | {
33 | $this->setExpectedException('\InvalidArgumentException');
34 | $obj = p($this->associative())->chunk(-3);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/tests/EachTest.php:
--------------------------------------------------------------------------------
1 | associative();
10 | $counter = 0;
11 | $obj = p($array)->each(function () use (&$counter) {
12 | ++$counter;
13 | }, true);
14 | $obj->toArray();
15 | $this->assertEquals(6, $counter);
16 |
17 | $array = $this->associative();
18 | $result = [];
19 | $obj = p($array)->each(function ($value, $key) use (&$result) {
20 | $result[$key] = $value;
21 | }, true);
22 | $obj->toArray();
23 | $this->assertEquals($array, $result);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/tests/EmittableTest.php:
--------------------------------------------------------------------------------
1 | types() as $var) {
10 | $emitted = new \Pipes\Concept\Emittable($var);
11 | $this->assertSame($emitted->getValue(), $var);
12 | $this->assertFalse($emitted->hasKey());
13 | try {
14 | $emitted->getKey();
15 | $this->assertTrue(false, 'LogicException expected');
16 | } catch (\LogicException $e) {
17 | // we expect this
18 | }
19 | }
20 | }
21 | public function testKeyValue()
22 | {
23 | foreach ($this->types() as $key => $var) {
24 | $emitted = new \Pipes\Concept\Emittable($var);
25 | $emitted->setKey($key);
26 | $this->assertSame($emitted->getValue(), $var);
27 | $this->assertSame($emitted->getKey(), $key);
28 | $this->assertTrue($emitted->hasKey());
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/tests/FilesTest.php:
--------------------------------------------------------------------------------
1 | associative();
30 | $path = $this->createTest($array);
31 | $obj = p()->files("$path/*");
32 | $expected = [
33 | "$path/apples",
34 | "$path/bananas",
35 | "$path/cherries",
36 | "$path/damsons",
37 | "$path/elderberries",
38 | "$path/figs",
39 | ];
40 |
41 | $expected = array_combine($expected, $expected);
42 | $this->assertSame($expected, $obj->toArray());
43 |
44 | $obj = p()->files("$path/*", true);
45 | $result = $obj->toArray();
46 | $this->assertSame(count($expected), count($result));
47 | foreach ($result as $fileInfo) {
48 | $this->assertInstanceOf('\\SplFileInfo', $fileInfo);
49 | }
50 |
51 | $path = $this->removeTest($array, $path);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/tests/FilterTest.php:
--------------------------------------------------------------------------------
1 | numerics();
12 | $obj = p($array)->filter(function ($v, $k) {
13 | return $v % 2;
14 | });
15 | $result = $obj->toArray();
16 | $this->assertEquals([
17 | 0 => 1,
18 | 2 => 3,
19 | 3 => 5,
20 | ], $result);
21 |
22 | // keys should not be preserved
23 | // but we do it when possible
24 | $array = $this->associative();
25 | $obj = p($array)->filter(function ($v, $k) {
26 | return in_array($k, ['a', 'c']);
27 | });
28 | $result = $obj->toArray();
29 | $this->assertEquals([
30 | 'a' => 'apples',
31 | 'c' => 'cherries',
32 | ], $result);
33 | }
34 |
35 | public function testArguments()
36 | {
37 | $me = $this;
38 | $obj = p(['a' => 3])->filter(function ($v, $k, $pipe) use ($me) {
39 | $me->assertSame(3, $v);
40 | $me->assertSame('a', $k);
41 | $me->assertInstanceOf('\Iterator', $pipe);
42 | $me->assertInstanceOf('\Pipes\PipeIterator', $pipe);
43 | })->toArray();
44 | }
45 | // public function testAppend()
46 | // {
47 | // $array = $obj = p(['a' => 3])->filter(function ($v, $k, $pipe) {
48 | // if ($v === 3) {
49 | // $pipe->append(['b' => 4]);
50 | // }
51 | //
52 | // return true;
53 | // })->toArray();
54 | // $this->assertSame(['a' => 3, 'b' => 4], $array);
55 | // }
56 | }
57 |
--------------------------------------------------------------------------------
/tests/InfiniteTest.php:
--------------------------------------------------------------------------------
1 | associative(), 0, 2);
10 | $obj = p($array)->infinite();
11 |
12 | $result = [];
13 | $i = 0;
14 | foreach ($obj as $value) {
15 | $result[] = $value;
16 | ++$i;
17 | if ($i >= 6) {
18 | break;
19 | }
20 | }
21 |
22 | $expected = [
23 | 'apples', 'bananas',
24 | 'apples', 'bananas',
25 | 'apples', 'bananas',
26 | ];
27 |
28 | $this->assertSame($expected, $result);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/tests/LimitTest.php:
--------------------------------------------------------------------------------
1 | associative();
10 | $obj = p($array)->limit(3);
11 | $expected = ['a' => 'apples', 'b' => 'bananas', 'c' => 'cherries'];
12 | $this->assertEquals($expected, $obj->toArray());
13 |
14 | $obj = p($array)->limit(1, 2);
15 | $expected = ['b' => 'bananas', 'c' => 'cherries'];
16 | $this->assertEquals($expected, $obj->toArray());
17 |
18 | $obj = p($array)->limit(1, 0);
19 | $this->assertEquals([], $obj->toArray());
20 |
21 | try {
22 | $obj = p($array)->limit(-2);
23 | $this->fail('\\OutOfRangeException expected');
24 | } catch (\OutOfRangeException $ex) {
25 | }
26 | }
27 | public function testToIterator()
28 | {
29 | $array = $this->associative();
30 |
31 | $obj = p($array)->toIterator()->limit(3);
32 | $expected = ['a' => 'apples', 'b' => 'bananas', 'c' => 'cherries'];
33 | $this->assertEquals($expected, $obj->toArray());
34 |
35 | $obj = p($array)->toIterator()->limit(1, 2);
36 | $expected = ['b' => 'bananas', 'c' => 'cherries'];
37 | $this->assertEquals($expected, $obj->toArray());
38 |
39 | $obj = p($array)->toIterator()->limit(1, 0);
40 | $this->assertEquals([], $obj->toArray());
41 |
42 | try {
43 | $obj = p($array)->toIterator()->limit(-2);
44 | $this->fail('\\OutOfRangeException expected');
45 | } catch (\OutOfRangeException $ex) {
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/tests/MapTest.php:
--------------------------------------------------------------------------------
1 | numerics();
12 | $obj = p($array)->map(function ($value) {
13 | return $value;
14 | });
15 | $result = $obj->toArray();
16 | $this->assertEquals($array, $result);
17 |
18 | $array = $this->associative();
19 | $obj = p($array)->map(function ($value) {
20 | return strtoupper($value);
21 | });
22 | $result = $obj->toArray();
23 | $expected = array_map('strtoupper', $array);
24 | $this->assertEquals($expected, $result);
25 |
26 | // same as previous, but using emit()
27 | $array = $this->associative();
28 | $obj = p($array)->map(function ($value) {
29 | return p()->emit(strtoupper($value));
30 | });
31 | $result = $obj->toArray();
32 | $expected = array_map('strtoupper', $array);
33 | $this->assertEquals($expected, $result);
34 |
35 | // same as previous, emitting the key
36 | $array = $this->associative();
37 | $obj = p($array)->map(function ($value, $key) {
38 | return p()->emit(strtoupper($key), strtoupper($value));
39 | });
40 | $result = $obj->toArray();
41 | $expected = array_combine(array_map('strtoupper', array_keys($array)), array_map('strtoupper', $array));
42 | $this->assertEquals($expected, $result);
43 | }
44 |
45 | /**
46 | * In case of multiple items with the same key, the last one should win.
47 | */
48 | public function testKeyConflicts()
49 | {
50 | // test with string keys
51 | $array = $this->numerics();
52 | $obj = p($array)->map(function ($value) {
53 | return p()->emit('a', $value);
54 | });
55 | $result = $obj->toArray();
56 | $this->assertEquals([
57 | 'a' => 5,
58 | ], $result);
59 |
60 | // test with numeric indexes
61 | $array = $this->numerics();
62 | $obj = p($array)->map(function ($value) {
63 | return p()->emit(0, $value);
64 | });
65 | $result = $obj->toArray();
66 | $this->assertEquals([
67 | 0 => 5,
68 | ], $result);
69 | }
70 |
71 | public function testIterateAGenerator()
72 | {
73 | $array = function () {
74 | $a = 0;
75 | while ($a <= 1e4) {
76 | yield $a++;
77 | }
78 | };
79 |
80 | $obj = p($array())->map(function ($value) {
81 | return p()->emit(0, $value);
82 | });
83 | $result = $obj->toArray();
84 | $this->assertEquals([
85 | 0 => 1e4,
86 | ], $result);
87 | }
88 |
89 | public function generatorInClassMethod($iterator) {
90 | foreach ($iterator as $key => $value) {
91 | yield $key => $value;
92 | yield $key . '_2' => $value . ':2';
93 | }
94 | }
95 |
96 | public function testGenerator()
97 | {
98 | $array = $this->associative();
99 | $result = p($array)->map(function ($iterator) {
100 | foreach ($iterator as $key => $value) {
101 | yield $key => $value;
102 | yield $key . '_2' => $value . ':2';
103 | }
104 | })->toArray();
105 |
106 | $expected = [
107 | 'a' => 'apples',
108 | 'a_2' => 'apples:2',
109 | 'b' => 'bananas',
110 | 'b_2' => 'bananas:2',
111 | 'c' => 'cherries',
112 | 'c_2' => 'cherries:2',
113 | 'd' => 'damsons',
114 | 'd_2' => 'damsons:2',
115 | 'e' => 'elderberries',
116 | 'e_2' => 'elderberries:2',
117 | 'f' => 'figs',
118 | 'f_2' => 'figs:2',
119 | ];
120 |
121 | $this->assertEquals($expected, $result);
122 |
123 | // let's see if passing a class method works also
124 |
125 | $result = p($array)->map([$this, 'generatorInClassMethod'])->toArray();
126 | $this->assertEquals($expected, $result);
127 | }
128 |
129 | // public function testAppend()
130 | // {
131 | // $me = $this;
132 | // $array = $obj = p(['a' => 3])->map(function ($v, $k, $pipe) {
133 | // var_dump($pipe);
134 | // if ($v === 3) {
135 | // $pipe->append(['b' => 4]);
136 | // }
137 | //
138 | // return $v;
139 | // })->toArray();
140 | // $this->assertSame(['a' => 3, 'b' => 4], $array);
141 | // }
142 | }
143 |
--------------------------------------------------------------------------------
/tests/OuterIteratorTest.php:
--------------------------------------------------------------------------------
1 | associative());
12 | $this->assertInstanceOf('\IteratorAggregate', $obj);
13 | }
14 |
15 | protected function getObject()
16 | {
17 | return p($this->associative())->toIterator();
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/tests/PipeIterationTest.php:
--------------------------------------------------------------------------------
1 | associative());
12 | $this->assertInstanceOf('\IteratorAggregate', $obj);
13 | }
14 |
15 | protected function getObject()
16 | {
17 | return p(p($this->associative()))->toIterator();
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/tests/SkipTest.php:
--------------------------------------------------------------------------------
1 | associative();
10 | $obj = p($array)->skip(0);
11 | $expected = [
12 | 'a' => 'apples',
13 | 'b' => 'bananas',
14 | 'c' => 'cherries',
15 | 'd' => 'damsons',
16 | 'e' => 'elderberries',
17 | 'f' => 'figs',
18 | ];
19 | $this->assertSame($expected, $obj->toArray());
20 |
21 | $array = $this->associative();
22 | $obj = p($array)->skip(2);
23 | $expected = [
24 | 'c' => 'cherries',
25 | 'd' => 'damsons',
26 | 'e' => 'elderberries',
27 | 'f' => 'figs',
28 | ];
29 | $this->assertSame($expected, $obj->toArray());
30 | // and repeat
31 | $this->assertSame($expected, $obj->toArray());
32 |
33 | $array = $this->associative();
34 | $obj = p($array)->skip(1000);
35 | $expected = [];
36 | $this->assertSame($expected, $obj->toArray());
37 | }
38 |
39 | public function testNoRewind()
40 | {
41 | $iterator = new \NoRewindIterator(new \ArrayIterator($this->associative()));
42 | $obj = p($iterator)->skip(2);
43 | $expected = [
44 | 'c' => 'cherries',
45 | 'd' => 'damsons',
46 | 'e' => 'elderberries',
47 | 'f' => 'figs',
48 | ];
49 | $this->assertSame($expected, $obj->toArray());
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/tests/SleepTest.php:
--------------------------------------------------------------------------------
1 | associative(), 0, 1);
11 | $obj = p($array)->sleep($delay);
12 | $start = microtime(true);
13 | $array2 = $obj->toArray();
14 | $end = microtime(true);
15 | $this->assertEquals($delay, $end - $start, '', 0.05);
16 | $this->assertEquals($array, $array2);
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/tests/StopIfTest.php:
--------------------------------------------------------------------------------
1 | associative();
10 |
11 | $obj = p($array)->stopIf(function ($value, $key) {
12 | return $key == 'd';
13 | }, true);
14 |
15 | $expected = ['a' => 'apples', 'b' => 'bananas', 'c' => 'cherries'];
16 | $this->assertSame($expected, $obj->toArray());
17 |
18 | $obj = p($array)->stopIf(function ($value) {
19 | return $value == 'damsons';
20 | });
21 |
22 | $expected = ['a' => 'apples', 'b' => 'bananas', 'c' => 'cherries'];
23 | $this->assertSame($expected, $obj->toArray());
24 | }
25 |
26 | public function testContinueIf()
27 | {
28 | $array = $this->associative();
29 |
30 | $obj = p($array)->continueIf(function ($value, $key) {
31 | return $key != 'd';
32 | }, true);
33 |
34 | $expected = ['a' => 'apples', 'b' => 'bananas', 'c' => 'cherries'];
35 | $this->assertSame($expected, $obj->toArray());
36 |
37 | $obj = p($array)->continueIf(function ($value) {
38 | return $value != 'damsons';
39 | });
40 |
41 | $expected = ['a' => 'apples', 'b' => 'bananas', 'c' => 'cherries'];
42 | $this->assertSame($expected, $obj->toArray());
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/tests/TestCase/BaseIteratorTestCase.php:
--------------------------------------------------------------------------------
1 | getObject();
19 | $this->assertInstanceOf('\Iterator', $obj);
20 | }
21 |
22 | public function assertIteration($iterator, $expected)
23 | {
24 | $this->assertInstanceOf('\IteratorIterator', $iterator);
25 |
26 | $iterator->rewind();
27 | $this->assertTrue($iterator->valid());
28 | $this->assertSame($expected[0][0], $iterator->key());
29 | $this->assertSame($expected[0][1], $iterator->current());
30 |
31 | $iterator->next();
32 | $this->assertTrue($iterator->valid());
33 | $this->assertSame($expected[1][0], $iterator->key());
34 | $this->assertSame($expected[1][1], $iterator->current());
35 |
36 | foreach (range(1, 10) as $u) {
37 | $iterator->next();
38 | }
39 | $this->assertFalse($iterator->valid());
40 | }
41 |
42 | public function testIteration()
43 | {
44 | $this->assertIteration($this->getObject(), $this->expected);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/tests/TestCase/CallbackTestCase.php:
--------------------------------------------------------------------------------
1 | getMessage().PHP_EOL."[ using toIterator: $useToIterator ]".PHP_EOL;
27 | }
28 |
29 | /**
30 | * A failure occurred.
31 | *
32 | * @param PHPUnit_Framework_Test $test
33 | * @param PHPUnit_Framework_AssertionFailedError $e
34 | * @param float $time
35 | */
36 | public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
37 | {
38 | $useToIterator = BaseTestCase::$useToIterator ? 'yes' : 'no';
39 | echo $e->toString().PHP_EOL."[ using toIterator: $useToIterator ]".PHP_EOL;
40 | }
41 |
42 | /**
43 | * Incomplete test.
44 | *
45 | * @param PHPUnit_Framework_Test $test
46 | * @param Exception $e
47 | * @param float $time
48 | */
49 | public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
50 | {
51 | }
52 |
53 | /**
54 | * Skipped test.
55 | *
56 | * @param PHPUnit_Framework_Test $test
57 | * @param Exception $e
58 | * @param float $time
59 | *
60 | * @since Method available since Release 3.0.0
61 | */
62 | public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
63 | {
64 | }
65 |
66 | /**
67 | * A test suite started.
68 | *
69 | * @param PHPUnit_Framework_TestSuite $suite
70 | *
71 | * @since Method available since Release 2.2.0
72 | */
73 | public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
74 | {
75 | }
76 |
77 | /**
78 | * A test suite ended.
79 | *
80 | * @param PHPUnit_Framework_TestSuite $suite
81 | *
82 | * @since Method available since Release 2.2.0
83 | */
84 | public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
85 | {
86 | }
87 |
88 | /**
89 | * A test started.
90 | *
91 | * @param PHPUnit_Framework_Test $test
92 | */
93 | public function startTest(PHPUnit_Framework_Test $test)
94 | {
95 | }
96 | }
97 | ?>
98 |
--------------------------------------------------------------------------------
/tests/Tools/TestIterator.php:
--------------------------------------------------------------------------------
1 | key.') '.__METHOD__.PHP_EOL;
22 |
23 | return parent::current();
24 | }
25 |
26 | public function next()
27 | {
28 | echo $this->key.') '.__METHOD__.PHP_EOL;
29 | ++$this->key;
30 | parent::next();
31 | }
32 |
33 | public function key()
34 | {
35 | echo $this->key.') '.__METHOD__.PHP_EOL;
36 |
37 | return parent::key();
38 | }
39 |
40 | public function valid()
41 | {
42 | echo $this->key.') '.__METHOD__.PHP_EOL;
43 |
44 | return parent::valid();
45 | }
46 |
47 | public function rewind()
48 | {
49 | parent::rewind();
50 | $this->key = 0;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/tests/Tools/TestIteratorAggregate.php:
--------------------------------------------------------------------------------
1 | inner = $var;
14 | }
15 |
16 | /**
17 | * Retrieve an external iterator.
18 | *
19 | * @link http://php.net/manual/en/iteratoraggregate.getiterator.php
20 | *
21 | * @return Traversable An instance of an object implementing Iterator or
22 | * Traversable
23 | *
24 | * @since 5.0.0
25 | */
26 | public function getIterator()
27 | {
28 | return $this->inner;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/tests/ValueTest.php:
--------------------------------------------------------------------------------
1 | types() as $var) {
10 | $value = new \Pipes\Concept\Value($var);
11 | $this->assertSame($value->getValue(), $var);
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/tests/ValuesTest.php:
--------------------------------------------------------------------------------
1 | associative();
10 | $obj = p($array)->values();
11 | $expected = [
12 | 'apples',
13 | 'bananas',
14 | 'cherries',
15 | 'damsons',
16 | 'elderberries',
17 | 'figs',
18 | ];
19 | $this->assertSame($expected, $obj->toArray());
20 | }
21 |
22 | public function testRewind()
23 | {
24 | $array = $this->associative();
25 | $obj = p($array)->values();
26 | $expected = [
27 | 'apples',
28 | 'bananas',
29 | 'cherries',
30 | 'damsons',
31 | 'elderberries',
32 | 'figs',
33 | ];
34 |
35 | $this->assertSame($expected, $this->foreachArray($obj));
36 | // foreach calls a rewind
37 | $this->assertSame($expected, $this->foreachArray($obj));
38 | }
39 | public function testToValues()
40 | {
41 | $array = $this->associative();
42 | $obj = p($array)->toValues();
43 | $expected = [
44 | 'apples',
45 | 'bananas',
46 | 'cherries',
47 | 'damsons',
48 | 'elderberries',
49 | 'figs',
50 | ];
51 | $this->assertSame($expected, $obj);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------