Skip to content

Commit c1e3a33

Browse files
authored
PHPLIB-1184: Improve CallbackIterator (#1126)
* Pass key to callback in CallbackIterator * Add template params to CallbackIterator * Add tests for CallbackIterator * Accept any callable as callback in CallbackIterator * Rename callback variable to convey purpose * Rework CallbackIterator test to use data providers
1 parent 4d21326 commit c1e3a33

File tree

2 files changed

+102
-7
lines changed

2 files changed

+102
-7
lines changed

src/Model/CallbackIterator.php

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,44 +17,54 @@
1717

1818
namespace MongoDB\Model;
1919

20-
use Closure;
2120
use Iterator;
2221
use IteratorIterator;
2322
use ReturnTypeWillChange;
2423
use Traversable;
2524

25+
use function call_user_func;
26+
2627
/**
2728
* Iterator to apply a callback before returning an element
2829
*
2930
* @internal
31+
*
32+
* @template TKey
33+
* @template TValue
34+
* @template TCallbackValue
35+
* @template-implements Iterator<TKey, TCallbackValue>
3036
*/
3137
class CallbackIterator implements Iterator
3238
{
33-
/** @var Closure */
39+
/** @var callable(TValue, TKey): TCallbackValue */
3440
private $callback;
3541

36-
/** @var Iterator */
42+
/** @var Iterator<TKey, TValue> */
3743
private $iterator;
3844

39-
public function __construct(Traversable $traversable, Closure $callback)
45+
/**
46+
* @param Traversable<TKey, TValue> $traversable
47+
* @param callable(TValue, TKey): TCallbackValue $callback
48+
*/
49+
public function __construct(Traversable $traversable, callable $callback)
4050
{
4151
$this->iterator = $traversable instanceof Iterator ? $traversable : new IteratorIterator($traversable);
4252
$this->callback = $callback;
4353
}
4454

4555
/**
4656
* @see https://php.net/iterator.current
47-
* @return mixed
57+
* @return TCallbackValue
4858
*/
4959
#[ReturnTypeWillChange]
5060
public function current()
5161
{
52-
return ($this->callback)($this->iterator->current());
62+
return call_user_func($this->callback, $this->iterator->current(), $this->iterator->key());
5363
}
5464

5565
/**
5666
* @see https://php.net/iterator.key
57-
* @return mixed
67+
* @return TKey
5868
*/
5969
#[ReturnTypeWillChange]
6070
public function key()

tests/Model/CallbackIteratorTest.php

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<?php
2+
3+
namespace MongoDB\Tests\Model;
4+
5+
use ArrayIterator;
6+
use Generator;
7+
use Iterator;
8+
use IteratorAggregate;
9+
use MongoDB\Model\CallbackIterator;
10+
use MongoDB\Tests\TestCase;
11+
12+
use function iterator_to_array;
13+
14+
class CallbackIteratorTest extends TestCase
15+
{
16+
/** @dataProvider provideTests */
17+
public function testIteration($expected, $source, $callback): void
18+
{
19+
$callbackIterator = new CallbackIterator($source, $callback);
20+
21+
$this->assertEquals($expected, iterator_to_array($callbackIterator));
22+
}
23+
24+
public static function provideTests(): Generator
25+
{
26+
$listIterator = new ArrayIterator([1, 2, 3]);
27+
$hashIterator = new ArrayIterator(['a' => 1, 'b' => 2, 'c' => 3]);
28+
29+
$iteratorAggregate = new class ($listIterator) implements IteratorAggregate
30+
{
31+
private $iterator;
32+
33+
public function __construct(Iterator $iterator)
34+
{
35+
$this->iterator = $iterator;
36+
}
37+
38+
public function getIterator(): Iterator
39+
{
40+
return $this->iterator;
41+
}
42+
};
43+
44+
yield 'List with closure' => [
45+
'expected' => [2, 4, 6],
46+
'source' => $listIterator,
47+
'callback' => function ($value, $key) use ($listIterator) {
48+
self::assertSame($listIterator->key(), $key);
49+
50+
return $value * 2;
51+
},
52+
];
53+
54+
yield 'List with callable' => [
55+
'expected' => [2, 4, 6],
56+
'source' => $listIterator,
57+
'callback' => [self::class, 'doubleValue'],
58+
];
59+
60+
yield 'Hash with closure' => [
61+
'expected' => ['a' => 2, 'b' => 4, 'c' => 6],
62+
'source' => $hashIterator,
63+
'callback' => function ($value, $key) use ($hashIterator) {
64+
self::assertSame($hashIterator->key(), $key);
65+
66+
return $value * 2;
67+
},
68+
];
69+
70+
yield 'IteratorAggregate with closure' => [
71+
'expected' => [2, 4, 6],
72+
'source' => $iteratorAggregate,
73+
'callback' => function ($value, $key) use ($listIterator) {
74+
self::assertSame($listIterator->key(), $key);
75+
76+
return $value * 2;
77+
},
78+
];
79+
}
80+
81+
public static function doubleValue($value, $key)
82+
{
83+
return $value * 2;
84+
}
85+
}

0 commit comments

Comments
 (0)