Skip to content

Commit

Permalink
feat: Add ClosureIteratorAggregate and IterableIteratorAggregate.
Browse files Browse the repository at this point in the history
  • Loading branch information
drupol committed Dec 16, 2021
1 parent 3077769 commit 7c99ea1
Show file tree
Hide file tree
Showing 5 changed files with 231 additions and 0 deletions.
2 changes: 2 additions & 0 deletions README.md
Expand Up @@ -19,7 +19,9 @@ The missing PHP iterators.
2 Iterators:

* `ClosureIterator`: ClosureIterator(callable $callable, array $arguments = [])
* `ClosureIteratorAggregate`: ClosureIteratorAggregate(callable $callable, array $arguments = [])
* `IterableIterator`: IterableIterator(iterable $iterable)
* `IterableIteratorAggregate`: IterableIterator(iterable $iterable)

## Installation

Expand Down
47 changes: 47 additions & 0 deletions src/ClosureIteratorAggregate.php
@@ -0,0 +1,47 @@
<?php

/**
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

declare(strict_types=1);

namespace loophp\iterators;

use IteratorAggregate;
use Traversable;

/**
* @template TKey
* @template T
*
* @implements IteratorAggregate<TKey, T>
*/
final class ClosureIteratorAggregate implements IteratorAggregate
{
/**
* @var callable(mixed ...$parameters): iterable<TKey, T>
*/
private $callable;

/**
* @var iterable<int, mixed>
*/
private iterable $parameters;

/**
* @param callable(mixed ...$parameters): iterable<TKey, T> $callable
* @param iterable<int, mixed> $parameters
*/
public function __construct(callable $callable, iterable $parameters = [])
{
$this->callable = $callable;
$this->parameters = $parameters;
}

public function getIterator(): Traversable
{
return yield from ($this->callable)(...$this->parameters);
}
}
40 changes: 40 additions & 0 deletions src/IterableIteratorAggregate.php
@@ -0,0 +1,40 @@
<?php

/**
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

declare(strict_types=1);

namespace loophp\iterators;

use IteratorAggregate;
use Traversable;

/**
* @template TKey
* @template T
*
* @implements IteratorAggregate<TKey, T>
*/
final class IterableIteratorAggregate implements IteratorAggregate
{
/**
* @var IteratorAggregate<TKey, T>
*/
private IteratorAggregate $iterator;

/**
* @param iterable<TKey, T> $iterable
*/
public function __construct(iterable $iterable)
{
$this->iterator = new ClosureIteratorAggregate(static fn () => yield from $iterable);
}

public function getIterator(): Traversable
{
return $this->iterator->getIterator();
}
}
56 changes: 56 additions & 0 deletions tests/unit/ClosureIteratorAggregateTest.php
@@ -0,0 +1,56 @@
<?php

/**
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

declare(strict_types=1);

namespace tests\loophp\iterators;

use loophp\iterators\ClosureIteratorAggregate;
use PHPUnit\Framework\TestCase;

/**
* @internal
* @coversDefaultClass \loophp\iterators
*/
final class ClosureIteratorAggregateTest extends TestCase
{
private const LIST_DATA = [1, 2, 3];

private const MAP_DATA = ['foo' => 1, 'bar' => 2];

public function testInitializeFromCallableWithArray(): void
{
$iterator = new ClosureIteratorAggregate(
static fn (array $iterable): array => $iterable,
[self::LIST_DATA]
);

self::assertTrue($iterator->getIterator()->valid());

self::assertEquals(
self::LIST_DATA,
iterator_to_array($iterator->getIterator())
);
}

public function testRewind(): void
{
$iterator = new ClosureIteratorAggregate(
static fn (array $iterable): array => $iterable,
[self::MAP_DATA]
);
$newIterator = $iterator->getIterator();

self::assertEquals(1, $newIterator->current());
$newIterator->next();
self::assertEquals(2, $newIterator->current());

$newIterator = $iterator->getIterator();

self::assertEquals(1, $newIterator->current());
}
}
86 changes: 86 additions & 0 deletions tests/unit/IterableIteratorAggregateTest.php
@@ -0,0 +1,86 @@
<?php

/**
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

declare(strict_types=1);

namespace tests\loophp\iterators;

use ArrayIterator;
use Generator;
use loophp\iterators\IterableIteratorAggregate;
use PHPUnit\Framework\TestCase;

/**
* @internal
* @coversDefaultClass \loophp\iterators
*/
final class IterableIteratorAggregateTest extends TestCase
{
public function testGetAnIntKey(): void
{
$iterator = (new IterableIteratorAggregate(range(1, 5)))->getIterator();

self::assertEquals(0, $iterator->key());
$iterator->next();
self::assertEquals(1, $iterator->key());
}

public function testGetAStringKey(): void
{
$iterator = (new IterableIteratorAggregate(['foo' => 1, 'bar' => 2]))->getIterator();
self::assertEquals('foo', $iterator->key());
$iterator->next();
self::assertEquals('bar', $iterator->key());
}

public function testIsInitializableFromArray(): void
{
$iterator = (new IterableIteratorAggregate(['foo', 'bar', 'baz']))->getIterator();

self::assertEquals('foo', $iterator->current());
}

public function testIsInitializableFromGenerator(): void
{
$gen = static fn (): Generator => yield from ['foo', 'bar', 'baz'];

$iterator = (new IterableIteratorAggregate($gen()))->getIterator();

self::assertEquals('foo', $iterator->current());
}

public function testIsInitializableFromIterator(): void
{
$iterator = (new IterableIteratorAggregate(new ArrayIterator(['foo', 'bar', 'baz'])))->getIterator();

self::assertEquals('foo', $iterator->current());
}

public function testRenewGenerator(): void
{
$iia = new IterableIteratorAggregate(['foo']);
$iterator = $iia->getIterator();

self::assertEquals('foo', $iterator->current());
$iterator->next();
self::assertNull($iterator->current());

// Iterator renewal here (instead of using rewind)
$iterator = $iia->getIterator();

self::assertEquals('foo', $iterator->current());
}

public function testUseNext(): void
{
$iterator = (new IterableIteratorAggregate(range(1, 5)))->getIterator();

self::assertNull($iterator->next());

self::assertEquals(2, $iterator->current());
}
}

0 comments on commit 7c99ea1

Please sign in to comment.