Skip to content

Commit

Permalink
Use iterable instead of \Traversable.
Browse files Browse the repository at this point in the history
  • Loading branch information
drupol committed Aug 28, 2019
1 parent 5b2a0cf commit 4eb5c88
Show file tree
Hide file tree
Showing 40 changed files with 244 additions and 100 deletions.
37 changes: 36 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,41 @@ Collection::with(
)
->limit(10)
->all(); // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

// Use an existing Generator as input data.
$readFileLineByLine = static function (string $filepath): \Generator {
$fh = fopen($filepath, 'rb');
$i = 0;

while (!feof($fh)) {
$line = '';

while (PHP_EOL !== $chunk = fread($fh, 1)) {
$line .= $chunk;
}

yield [
'number' => $i++,
'content' => $line
];
}

fclose($fh);
};

$hugeFile = __DIR__ .'/vendor/composer/autoload_static.php';

Collection::with($readFileLineByLine($hugeFile))
// Find public static fields or methods.
->filter(static function (array $line) {return false !== strpos($line['content'], 'public static');})
// Skip the first one.
->skip(1)
// Limit the list to 3 results only.
->limit(3)
// Add the line number at the end of each line.
->map(static function (array $line) {return sprintf('%s // Line %s', $line['content'], $line['number']);})
// Implode into a string.
->implode(PHP_EOL);
```

## Advanced usage
Expand All @@ -153,7 +188,7 @@ $square = new class() extends Operation {
/**
* {@inheritdoc}
*/
public function on(\Traversable $collection): \Closure
public function on(iterable $collection): \Closure
{
return static function () use ($collection) {
foreach ($collection as $item) {
Expand Down
10 changes: 7 additions & 3 deletions spec/drupol/collection/CollectionSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,10 @@ public function it_can_contains(): void
->contains('A')
->shouldReturn(true);

$this
->contains('unknown')
->shouldReturn(false);

$this
->contains(static function ($item) {
return 'A' === $item;
Expand Down Expand Up @@ -756,7 +760,7 @@ static function ($carry, $item) {
public function it_can_run_an_operation(Operation $operation): void
{
$square = new class() extends \drupol\collection\Operation\Operation {
public function on(\Traversable $collection)
public function on(iterable $collection)
{
return Collection::with(
static function () use ($collection) {
Expand All @@ -769,7 +773,7 @@ static function () use ($collection) {
};

$sqrt = new class() extends \drupol\collection\Operation\Operation {
public function on(\Traversable $collection)
public function on(iterable $collection)
{
return Collection::with(
static function () use ($collection) {
Expand All @@ -782,7 +786,7 @@ static function () use ($collection) {
};

$map = new class() extends \drupol\collection\Operation\Operation {
public function on(\Traversable $collection)
public function on(iterable $collection)
{
return Collection::with(
static function () use ($collection) {
Expand Down
85 changes: 85 additions & 0 deletions spec/drupol/collection/Iterator/ClosureIteratorSpec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?php

declare(strict_types=1);

namespace spec\drupol\collection\Iterator;

use drupol\collection\Iterator\ClosureIterator;
use PhpSpec\ObjectBehavior;

class ClosureIteratorSpec extends ObjectBehavior
{
public function it_can_be_constructed_with_a_callable_which_is_not_a_generator(): void
{
$callback = static function () {
return \range(1, 5);
};

$this
->beConstructedWith($callback);
}

public function it_can_get_a_key(): void
{
$callback = static function () {
return \range(1, 5);
};

$this
->beConstructedWith($callback);

$this
->key()
->shouldReturn(0);
}

public function it_can_send(): void
{
$callback = static function () {
yield from \range(1, 5);
};

$this
->beConstructedWith($callback);

$this->send('foo');
}

public function it_can_use_next(): void
{
$callback = static function () {
return \range(1, 5);
};

$this
->beConstructedWith($callback);

$this
->next()
->shouldReturn($this);

$this
->current()
->shouldReturn(2);
}

public function it_is_initializable(): void
{
$callback = static function ($a, $b, $c) {
yield $a;

yield $b;

yield $c;
};

$this
->beConstructedWith($callback, 'foo', 'bar', 'baz');

$this->shouldHaveType(ClosureIterator::class);

$this
->current()
->shouldBeEqualTo('foo');
}
}
2 changes: 1 addition & 1 deletion src/BaseCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public function __construct($data = [])
$this->source = $data;

break;
case $data instanceof \Traversable:
case \is_iterable($data):
$this->source = static function () use ($data) {
foreach ($data as $k => $v) {
yield $k => $v;
Expand Down
4 changes: 2 additions & 2 deletions src/Contract/Operation.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
interface Operation
{
/**
* @param \Traversable $collection
* @param iterable $collection
*
* @return mixed
*/
public function on(\Traversable $collection);
public function on(iterable $collection);
}
90 changes: 47 additions & 43 deletions src/Iterator/ClosureIterator.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,93 +22,97 @@ class ClosureIterator implements \Iterator
/**
* ClosureIterator constructor.
*
* @param \Closure $closure
*
* @throws \ReflectionException
* @param callable $callable
* @param array ...$arguments
*/
public function __construct(\Closure $closure)
public function __construct(callable $callable, ...$arguments)
{
$reflection = new \ReflectionFunction($closure);

if (!$reflection->isGenerator()) {
$closure = static function () use ($closure) {
foreach ($closure() as $k => $v) {
yield $k => $v;
}
};
}

$this->source = static function () use ($closure) {
foreach ($closure() as $k => $v) {
$this->source = static function () use ($callable, $arguments) {
foreach ($callable(...$arguments) as $k => $v) {
yield $k => $v;
}
};

$this->generator = $closure();
}

/**
* Return the current element.
* {@inheritdoc}
*
* @see https://php.net/manual/en/iterator.current.php
*
* @return mixed Can return any type.
*
* @since 5.0.0
* @return mixed
*/
public function current()
{
if (null === $this->generator) {
$this->rewind();
}

return $this->generator->current();
}

/**
* Return the key of the current element.
*
* @see https://php.net/manual/en/iterator.key.php
*
* @return mixed scalar on success, or null on failure.
* {@inheritdoc}
*
* @since 5.0.0
* @return int|string
*/
public function key()
{
if (null === $this->generator) {
$this->rewind();
}

return $this->generator->key();
}

/**
* Move forward to next element.
* {@inheritdoc}
*
* @see https://php.net/manual/en/iterator.next.php
* @since 5.0.0
* @return $this|void
*/
public function next(): void
public function next()
{
if (null === $this->generator) {
$this->rewind();
}

$this->generator->next();

return $this;
}

/**
* Rewind the Iterator to the first element.
*
* @see https://php.net/manual/en/iterator.rewind.php
* @since 5.0.0
* {@inheritdoc}
*/
public function rewind(): void
{
$this->generator = ($this->source)();
}

/**
* Checks if current position is valid.
* Send a value to the inner Generator.
*
* @see https://php.net/manual/en/iterator.valid.php
* @param null|mixed $value
*
* @return bool The return value will be casted to boolean and then evaluated.
* Returns true on success or false on failure.
* @return mixed
*/
public function send($value = null)
{
if (null === $this->generator) {
$this->rewind();
}

return $this->generator->send($value);
}

/**
* {@inheritdoc}
*
* @since 5.0.0
* @return bool
*/
public function valid()
{
if (null === $this->generator) {
$this->rewind();
}

return $this->generator->valid();
}
}
2 changes: 1 addition & 1 deletion src/Operation/All.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public function __construct()
/**
* {@inheritdoc}
*/
public function on(\Traversable $collection)
public function on(iterable $collection)
{
$result = [];

Expand Down
2 changes: 1 addition & 1 deletion src/Operation/Append.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ final class Append extends Operation
/**
* {@inheritdoc}
*/
public function on(\Traversable $collection): \Closure
public function on(iterable $collection): \Closure
{
[$items] = $this->parameters;

Expand Down
2 changes: 1 addition & 1 deletion src/Operation/Apply.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ final class Apply extends Operation
/**
* {@inheritdoc}
*/
public function on(\Traversable $collection): \Traversable
public function on(iterable $collection): iterable
{
[$callbacks] = $this->parameters;

Expand Down
6 changes: 3 additions & 3 deletions src/Operation/Chunk.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ public function __construct(int $size)
/**
* {@inheritdoc}
*/
public function on(\Traversable $collection): \Closure
public function on(iterable $collection): \Closure
{
[$size] = $this->parameters;

if (0 >= $size) {
return static function () {
return static function (): \Generator {
yield from [];
};
}
Expand All @@ -50,7 +50,7 @@ static function () use ($collection) {
$values[$iterator->key()] = $iterator->current();
}

yield new \ArrayObject($values);
yield new \ArrayIterator($values);
}
};
}
Expand Down
2 changes: 1 addition & 1 deletion src/Operation/Collapse.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ final class Collapse extends Operation
/**
* {@inheritdoc}
*/
public function on(\Traversable $collection): \Closure
public function on(iterable $collection): \Closure
{
return static function () use ($collection): \Generator {
foreach ($collection as $item) {
Expand Down

0 comments on commit 4eb5c88

Please sign in to comment.