From 95e1a464e3849a0b63468332fba3f36881e36ae3 Mon Sep 17 00:00:00 2001 From: Michael Dowling Date: Wed, 5 Sep 2012 21:18:53 -0700 Subject: [PATCH] Adding an iterator component --- src/Guzzle/Iterator/ChunkedIterator.php | 66 +++++++++++++++++++ src/Guzzle/Iterator/FilterIterator.php | 39 +++++++++++ src/Guzzle/Iterator/MapIterator.php | 37 +++++++++++ src/Guzzle/Iterator/MethodProxyIterator.php | 27 ++++++++ src/Guzzle/Iterator/README.md | 41 ++++++++++++ src/Guzzle/Iterator/composer.json | 23 +++++++ .../Tests/Iterator/ChunkedIteratorTest.php | 31 +++++++++ .../Tests/Iterator/FilterIteratorTest.php | 28 ++++++++ .../Guzzle/Tests/Iterator/MapIteratorTest.php | 28 ++++++++ .../Iterator/MethodProxyIteratorTest.php | 28 ++++++++ 10 files changed, 348 insertions(+) create mode 100644 src/Guzzle/Iterator/ChunkedIterator.php create mode 100644 src/Guzzle/Iterator/FilterIterator.php create mode 100644 src/Guzzle/Iterator/MapIterator.php create mode 100644 src/Guzzle/Iterator/MethodProxyIterator.php create mode 100644 src/Guzzle/Iterator/README.md create mode 100644 src/Guzzle/Iterator/composer.json create mode 100644 tests/Guzzle/Tests/Iterator/ChunkedIteratorTest.php create mode 100644 tests/Guzzle/Tests/Iterator/FilterIteratorTest.php create mode 100644 tests/Guzzle/Tests/Iterator/MapIteratorTest.php create mode 100644 tests/Guzzle/Tests/Iterator/MethodProxyIteratorTest.php diff --git a/src/Guzzle/Iterator/ChunkedIterator.php b/src/Guzzle/Iterator/ChunkedIterator.php new file mode 100644 index 000000000..41a062821 --- /dev/null +++ b/src/Guzzle/Iterator/ChunkedIterator.php @@ -0,0 +1,66 @@ +chunkSize = $chunkSize; + } + + /** + * {@inheritdoc} + */ + public function rewind() + { + $this->next(); + } + + /** + * {@inheritdoc} + */ + public function next() + { + $this->chunk = array(); + $inner = $this->getInnerIterator(); + for ($i = 0; $i < $this->chunkSize && $inner->valid(); $i++) { + $this->chunk[] = $inner->current(); + $inner->next(); + } + } + + /** + * {@inheritdoc} + */ + public function current() + { + return $this->chunk; + } + + /** + * {@inheritdoc} + */ + public function valid() + { + return !empty($this->chunk); + } +} diff --git a/src/Guzzle/Iterator/FilterIterator.php b/src/Guzzle/Iterator/FilterIterator.php new file mode 100644 index 000000000..d89b16902 --- /dev/null +++ b/src/Guzzle/Iterator/FilterIterator.php @@ -0,0 +1,39 @@ +callback = $callback; + } + + /** + * {@inheritdoc} + */ + public function accept() + { + return call_user_func($this->callback, $this->current()); + } +} diff --git a/src/Guzzle/Iterator/MapIterator.php b/src/Guzzle/Iterator/MapIterator.php new file mode 100644 index 000000000..af830f7b0 --- /dev/null +++ b/src/Guzzle/Iterator/MapIterator.php @@ -0,0 +1,37 @@ +callback = $callback; + } + + /** + * {@inheritdoc} + */ + public function current() + { + return call_user_func($this->callback, parent::current()); + } +} diff --git a/src/Guzzle/Iterator/MethodProxyIterator.php b/src/Guzzle/Iterator/MethodProxyIterator.php new file mode 100644 index 000000000..de4ab0360 --- /dev/null +++ b/src/Guzzle/Iterator/MethodProxyIterator.php @@ -0,0 +1,27 @@ +getInnerIterator(); + while ($i instanceof \OuterIterator) { + $i = $i->getInnerIterator(); + } + + return call_user_func_array(array($i, $name), $args); + } +} diff --git a/src/Guzzle/Iterator/README.md b/src/Guzzle/Iterator/README.md new file mode 100644 index 000000000..61e8f5dae --- /dev/null +++ b/src/Guzzle/Iterator/README.md @@ -0,0 +1,41 @@ +Guzzle Iterator +=============== + +[![Build Status](https://secure.travis-ci.org/guzzle/iterator.png?branch=master)](http://travis-ci.org/guzzle/guzzle) + +Component library that provides useful Iterators and Iterator decorators + +- ChunkedIterator: Pulls out chunks from an inner iterator and yields the chunks as arrays +- FilterIterator: Used when PHP 5.4's CallbackFilterIterator is not available +- MapIterator: Maps values before yielding +- MethodProxyIterator: Proxies missing method calls to the innermost iterator + +### Installing via Composer + +The recommended way to install is through [Composer](http://getcomposer.org). + +1. Add ``guzzle/iterator`` as a dependency in your project's ``composer.json`` file: + + { + "require": { + "guzzle/iterator": "*" + } + } + + Consider tightening your dependencies to a known version when deploying mission critical applications (e.g. ``2.7.*``). + +2. Download and install Composer: + + curl -s http://getcomposer.org/installer | php + +3. Install your dependencies: + + php composer.phar install + +4. Require Composer's autoloader + + Composer also prepares an autoload file that's capable of autoloading all of the classes in any of the libraries that it downloads. To use it, just add the following line to your code's bootstrap process: + + require 'vendor/autoload.php'; + +You can find out more on how to install Composer, configure autoloading, and other best-practices for defining dependencies at [getcomposer.org](http://getcomposer.org). diff --git a/src/Guzzle/Iterator/composer.json b/src/Guzzle/Iterator/composer.json new file mode 100644 index 000000000..7021c2230 --- /dev/null +++ b/src/Guzzle/Iterator/composer.json @@ -0,0 +1,23 @@ +{ + "name": "guzzle/iterator", + "type": "component", + "description": "Provides helpful iterators and iterator decorators", + "keywords": ["iterator", "guzzle"], + "homepage": "http://guzzlephp.org/", + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2" + }, + "autoload": { + "psr-0": { + "Guzzle\\Iterator": "src/" + } + } +} diff --git a/tests/Guzzle/Tests/Iterator/ChunkedIteratorTest.php b/tests/Guzzle/Tests/Iterator/ChunkedIteratorTest.php new file mode 100644 index 000000000..cfbd7516c --- /dev/null +++ b/tests/Guzzle/Tests/Iterator/ChunkedIteratorTest.php @@ -0,0 +1,31 @@ +assertEquals(11, count($chunks)); + foreach ($chunks as $j => $chunk) { + $this->assertEquals(range($j * 10, min(100, $j * 10 + 9)), $chunk); + } + } + + public function testChunksIteratorWithOddValues() + { + $chunked = new ChunkedIterator(new \ArrayIterator(array(1, 2, 3, 4, 5)), 2); + $chunks = iterator_to_array($chunked, false); + $this->assertEquals(3, count($chunks)); + $this->assertEquals(array(1, 2), $chunks[0]); + $this->assertEquals(array(3, 4), $chunks[1]); + $this->assertEquals(array(5), $chunks[2]); + } +} diff --git a/tests/Guzzle/Tests/Iterator/FilterIteratorTest.php b/tests/Guzzle/Tests/Iterator/FilterIteratorTest.php new file mode 100644 index 000000000..73b4f6987 --- /dev/null +++ b/tests/Guzzle/Tests/Iterator/FilterIteratorTest.php @@ -0,0 +1,28 @@ +assertEquals(range(1, 99, 2), iterator_to_array($i, false)); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesCallable() + { + $i = new FilterIterator(new \ArrayIterator(), new \stdClass()); + } +} diff --git a/tests/Guzzle/Tests/Iterator/MapIteratorTest.php b/tests/Guzzle/Tests/Iterator/MapIteratorTest.php new file mode 100644 index 000000000..4de4a6bc1 --- /dev/null +++ b/tests/Guzzle/Tests/Iterator/MapIteratorTest.php @@ -0,0 +1,28 @@ +assertEquals(range(0, 1000, 10), iterator_to_array($i, false)); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidatesCallable() + { + $i = new MapIterator(new \ArrayIterator(), new \stdClass()); + } +} diff --git a/tests/Guzzle/Tests/Iterator/MethodProxyIteratorTest.php b/tests/Guzzle/Tests/Iterator/MethodProxyIteratorTest.php new file mode 100644 index 000000000..5bcf06fb0 --- /dev/null +++ b/tests/Guzzle/Tests/Iterator/MethodProxyIteratorTest.php @@ -0,0 +1,28 @@ +append('a'); + $proxy->append('b'); + $this->assertEquals(array('a', 'b'), $i->getArrayCopy()); + $this->assertEquals(array('a', 'b'), $proxy->getArrayCopy()); + } + + public function testUsesInnerIterator() + { + $i = new MethodProxyIterator(new ChunkedIterator(new \ArrayIterator(array(1, 2, 3, 4, 5)), 2)); + $this->assertEquals(3, count(iterator_to_array($i, false))); + } +}