-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce class
ipl\Stdlib\CallbackFilterIterator
This class stems from the motivation to have a filter iterator that uses callbacks, which behaves like any other iterator. Just like the SPL's `CallbackFilterIterator`, but that works differently: It isn't valid unless explicitly rewound first. Any other iterator, especially generators, doesn't need to be rewound first. That's most obvious with a generator that yields conditionally, just like a filter iterator would. Any call to `valid()`, `key()` or `current()` should return the same result as the very first iteration step would.
- Loading branch information
Showing
2 changed files
with
140 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
<?php | ||
|
||
namespace ipl\Stdlib; | ||
|
||
use CallbackFilterIterator as SplCallbackFilterIterator; | ||
|
||
/** | ||
* CallbackFilterIterator that behaves like generators in terms of validity before the first call to `rewind()` | ||
* | ||
* This class stems from the motivation to have a filter iterator | ||
* that uses callbacks, which behaves like any other iterator. | ||
* Just like the SPL's `CallbackFilterIterator`, but that works | ||
* differently: It isn't valid unless explicitly rewound first. | ||
* | ||
* Any other iterator, especially generators, doesn't need to be | ||
* rewound first. That's most obvious with a generator that yields | ||
* conditionally, just like a filter iterator would. | ||
* | ||
* Any call to `valid()`, `key()` or `current()` should return the | ||
* same result as the very first iteration step would. | ||
*/ | ||
class CallbackFilterIterator extends SplCallbackFilterIterator | ||
{ | ||
/** @var bool Whether iteration has started */ | ||
private $started = false; | ||
|
||
public function rewind(): void | ||
{ | ||
$this->started = true; | ||
|
||
parent::rewind(); | ||
} | ||
|
||
public function valid(): bool | ||
{ | ||
if ($this->started) { | ||
return parent::valid(); | ||
} | ||
|
||
// As per php-src, \CallbackFilterIterator::rewind() forwards the iterator to the first valid element | ||
// (https://github.com/php/php-src/blob/5cba2a3dc59ef2a0e432b05ab27f2b3ab4da48d0/ext/spl/spl_iterators.c#L1686) | ||
$this->rewind(); | ||
|
||
return parent::valid(); | ||
} | ||
|
||
#[\ReturnTypeWillChange] | ||
public function key() | ||
Check failure on line 48 in src/CallbackFilterIterator.php GitHub Actions / Static analysis for php 7.2 on ubuntu-latest
Check failure on line 48 in src/CallbackFilterIterator.php GitHub Actions / Static analysis for php 7.3 on ubuntu-latest
Check failure on line 48 in src/CallbackFilterIterator.php GitHub Actions / Static analysis for php 7.4 on ubuntu-latest
|
||
{ | ||
if ($this->started) { | ||
return parent::key(); | ||
} | ||
|
||
$this->rewind(); | ||
|
||
return parent::key(); | ||
} | ||
|
||
#[\ReturnTypeWillChange] | ||
public function current() | ||
Check failure on line 60 in src/CallbackFilterIterator.php GitHub Actions / Static analysis for php 7.2 on ubuntu-latest
Check failure on line 60 in src/CallbackFilterIterator.php GitHub Actions / Static analysis for php 7.3 on ubuntu-latest
Check failure on line 60 in src/CallbackFilterIterator.php GitHub Actions / Static analysis for php 7.4 on ubuntu-latest
|
||
{ | ||
if ($this->started) { | ||
return parent::current(); | ||
} | ||
|
||
$this->rewind(); | ||
|
||
return parent::current(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
<?php | ||
|
||
namespace ipl\Tests\Stdlib; | ||
|
||
use ipl\Stdlib\CallbackFilterIterator; | ||
|
||
class CallbackFilterIteratorTest extends TestCase | ||
{ | ||
public function testFirstIterationStep() | ||
{ | ||
$iterator = new CallbackFilterIterator(new \ArrayIterator([1, 2, 3]), function (int $i) { | ||
return $i === 2; | ||
}); | ||
|
||
foreach ($iterator as $k => $v) { | ||
$this->assertSame(1, $k); | ||
$this->assertSame(2, $v); | ||
} | ||
} | ||
|
||
public function testValidBeforeRewind() | ||
{ | ||
$iterator = new CallbackFilterIterator(new \ArrayIterator([1, 2, 3]), function (int $i) { | ||
return $i === 2; | ||
}); | ||
|
||
$this->assertTrue($iterator->valid()); | ||
|
||
$iterator->rewind(); | ||
|
||
$this->assertTrue($iterator->valid()); | ||
} | ||
|
||
public function testKeyBeforeRewind() | ||
{ | ||
$iterator = new CallbackFilterIterator(new \ArrayIterator([1, 2, 3]), function (int $i) { | ||
return $i === 2; | ||
}); | ||
|
||
$this->assertSame(1, $iterator->key()); | ||
|
||
$iterator->rewind(); | ||
|
||
$this->assertSame(1, $iterator->key()); | ||
} | ||
|
||
public function testCurrentBeforeRewind() | ||
{ | ||
$iterator = new CallbackFilterIterator(new \ArrayIterator([1, 2, 3]), function (int $i) { | ||
return $i === 2; | ||
}); | ||
|
||
$this->assertSame(2, $iterator->current()); | ||
|
||
$iterator->rewind(); | ||
|
||
$this->assertSame(2, $iterator->current()); | ||
} | ||
|
||
public function testInvalidBeforeRewind() | ||
{ | ||
$iterator = new CallbackFilterIterator(new \ArrayIterator([1, 2, 3]), function (int $i) { | ||
return $i === 4; | ||
}); | ||
|
||
$this->assertFalse($iterator->valid()); | ||
$this->assertNull($iterator->key()); | ||
$this->assertNull($iterator->current()); | ||
} | ||
} |