1: <?php declare(strict_types=1);
2:
3: namespace Salient\Iterator;
4:
5: use Salient\Utility\Get;
6: use Closure;
7: use IteratorIterator;
8: use RecursiveCallbackFilterIterator;
9: use RecursiveIterator;
10:
11: /**
12: * Iterates over a recursive iterator, using a callback to determine which items
13: * with children to descend into
14: *
15: * Similar to {@see RecursiveCallbackFilterIterator}, but the callback is only
16: * used to filter the return value of {@see RecursiveIterator::hasChildren()},
17: * allowing values to be treated as leaf nodes even if they have children.
18: *
19: * @api
20: *
21: * @template TKey
22: * @template TValue
23: *
24: * @extends IteratorIterator<TKey,TValue,RecursiveIterator<TKey,TValue>>
25: * @implements RecursiveIterator<TKey,TValue>
26: */
27: class RecursiveCallbackIterator extends IteratorIterator implements RecursiveIterator
28: {
29: /** @var RecursiveIterator<TKey,TValue> */
30: private RecursiveIterator $Iterator;
31: /** @var Closure(TValue, TKey, RecursiveIterator<TKey,TValue>): bool */
32: private Closure $Callback;
33:
34: /**
35: * @param RecursiveIterator<TKey,TValue> $iterator
36: * @param callable(TValue, TKey, RecursiveIterator<TKey,TValue>): bool $callback
37: */
38: public function __construct(RecursiveIterator $iterator, callable $callback)
39: {
40: $this->Iterator = $iterator;
41: $this->Callback = Get::closure($callback);
42:
43: parent::__construct($iterator);
44: }
45:
46: /**
47: * @inheritDoc
48: */
49: public function hasChildren(): bool
50: {
51: if (!$this->Iterator->hasChildren()) {
52: return false;
53: }
54:
55: return ($this->Callback)(
56: $this->Iterator->current(),
57: $this->Iterator->key(),
58: $this->Iterator,
59: );
60: }
61:
62: /**
63: * @return self<TKey,TValue>|null
64: */
65: public function getChildren(): ?self
66: {
67: /** @var RecursiveIterator<TKey,TValue>|null */
68: $children = $this->Iterator->getChildren();
69:
70: return $children === null
71: // @codeCoverageIgnoreStart
72: ? null
73: // @codeCoverageIgnoreEnd
74: : new self($children, $this->Callback);
75: }
76: }
77: