Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
85.71% covered (warning)
85.71%
6 / 7
0.00% covered (danger)
0.00%
0 / 1
CRAP
0.00% covered (danger)
0.00%
0 / 1
CountableIteratorIteratorTrait
85.71% covered (warning)
85.71%
6 / 7
0.00% covered (danger)
0.00%
0 / 1
3.03
0.00% covered (danger)
0.00%
0 / 1
 count
85.71% covered (warning)
85.71%
6 / 7
0.00% covered (danger)
0.00%
0 / 1
3.03
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 ReflectionClass;
22use Countable;
23use IteratorIterator;
24
25/**
26 * Provides counting behavior for iterators and iterator aggregates.
27 *
28 * This trait SHALL add a {@see count()} implementation that delegates to the
29 * inner iterator when possible. When the inner iterator does not implement
30 * {@see Countable}, the trait MUST determine whether the iterator can be safely
31 * cloned before counting its elements through traversal. Implementing classes
32 * MUST provide a compatible {@see getInnerIterator()} method that returns the
33 * traversable object to be counted.
34 */
35trait CountableIteratorIteratorTrait
36{
37    /**
38     * Counts the number of elements exposed by the inner iterator.
39     *
40     * If the inner iterator implements {@see Countable}, this method SHALL
41     * return the value provided by that implementation. Otherwise, it MUST count
42     * elements by iterating over the iterator. If the inner iterator is not
43     * cloneable, this method SHALL wrap the current object in an
44     * {@see IteratorIterator} instance and count through that wrapper to avoid
45     * performing an invalid clone operation. If the inner iterator is cloneable,
46     * this method SHOULD count over a clone so that the original iterator state
47     * is preserved as much as possible.
48     *
49     * @return int the total number of elements available from the inner iterator
50     */
51    public function count(): int
52    {
53        $inner = $this->getInnerIterator();
54
55        if ($inner instanceof Countable) {
56            return $inner->count();
57        }
58
59        $isCloneable = (new ReflectionClass($inner))->isCloneable();
60
61        if (! $isCloneable) {
62            return iterator_count(new IteratorIterator($this));
63        }
64
65        return iterator_count(clone $inner);
66    }
67}