Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
2 / 2
CRAP
100.00% covered (success)
100.00%
1 / 1
GeneratorCachingIteratorAggregate
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
2 / 2
4
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getIterator
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3declare(strict_types=1);
4
5/**
6 * This file is part of php-fast-forward/iterators.
7 *
8 * This source file is subject to the license that is bundled
9 * with this source code in the file LICENSE.
10 *
11 * @copyright Copyright (c) 2025-2026 Felipe SayĆ£o Lobato Abreu <github@mentordosnerds.com>
12 * @license   https://opensource.org/licenses/MIT MIT License
13 *
14 * @see       https://github.com/php-fast-forward/iterators
15 * @see       https://github.com/php-fast-forward
16 * @see       https://datatracker.ietf.org/doc/html/rfc2119
17 */
18
19namespace FastForward\Iterator;
20
21use CachingIterator;
22use Generator;
23use Traversable;
24
25/**
26 * A caching iterator aggregate designed to wrap a generator and cache its results.
27 *
28 * This class allows wrapping a `Generator` or a `callable` returning a generator,
29 * caching its values to enable multiple iterations over the same dataset.
30 *
31 * ## Usage Example:
32 *
33 * @example Using a Generator
34 * ```php
35 * use FastForward\Iterator\GeneratorCachingIteratorAggregate;
36 *
37 * $iterator = new GeneratorCachingIteratorAggregate(
38 *     (function () {
39 *         yield 'A';
40 *         yield 'B';
41 *         yield 'C';
42 *     })()
43 * );
44 *
45 * foreach ($iterator as $value) {
46 *     echo $value; // Outputs: A B C
47 * }
48 *
49 * foreach ($iterator as $value) {
50 *     echo $value; // Outputs: A B C (cached)
51 * }
52 * ```
53 * @example Using a Callable that Returns a Generator
54 * ```php
55 * use FastForward\Iterator\GeneratorCachingIteratorAggregate;
56 *
57 * $iterator = new GeneratorCachingIteratorAggregate(fn () => (function () {
58 *     yield 1;
59 *     yield 2;
60 *     yield 3;
61 * })());
62 *
63 * foreach ($iterator as $value) {
64 *     echo $value; // Outputs: 1 2 3
65 * }
66 *
67 * foreach ($iterator as $value) {
68 *     echo $value; // Outputs: 1 2 3 (cached)
69 * }
70 * ```
71 *
72 * **Note:** If a callable is provided, it is automatically converted to a generator
73 * using `ClosureFactoryIteratorAggregate`.
74 *
75 * @since 1.0.0
76 */
77class GeneratorCachingIteratorAggregate extends CountableIteratorAggregate
78{
79    /**
80     * @var CachingIterator the caching iterator that stores generated values
81     */
82    private  CachingIterator $cachingIterator;
83
84    /**
85     * Initializes the caching iterator with a generator or a callable returning a generator.
86     *
87     * If a callable is provided, it is wrapped in a `ClosureFactoryIteratorAggregate` to generate the iterator.
88     *
89     * @param callable|Generator $generator the generator or a callable returning a generator
90     */
91    public function __construct(callable|Generator $generator)
92    {
93        if (\is_callable($generator)) {
94            $generator = (new ClosureFactoryIteratorAggregate($generator))->getIterator();
95        }
96
97        $this->cachingIterator = new CachingIterator($generator, CachingIterator::FULL_CACHE);
98    }
99
100    /**
101     * Retrieves the iterator, either from the cache or the generator itself.
102     *
103     * This method ensures that once a generator is iterated, its values
104     * remain available for subsequent iterations.
105     *
106     * @return Traversable the cached or fresh iterator
107     */
108    public function getIterator(): Traversable
109    {
110        if ($this->cachingIterator->getCache()) {
111            yield from $this->cachingIterator->getCache();
112        } else {
113            yield from $this->cachingIterator;
114        }
115    }
116}