Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
FileExtensionFilterIterator
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 2
20
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 accept
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
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 FilesystemIterator;
22use RecursiveIterator;
23use RecursiveIteratorIterator;
24use IteratorIterator;
25use RecursiveDirectoryIterator;
26use SplFileInfo;
27
28/**
29 * A filter iterator that only accepts files matching specified extensions.
30 *
31 * This iterator is designed to traverse a directory and filter files
32 * by their extension. It supports both `FilesystemIterator` and `RecursiveIterator`.
33 *
34 * ## Usage Example:
35 *
36 * @example
37 * ```php
38 * use FastForward\Iterator\FileExtensionFilterIterator;
39 * use FilesystemIterator;
40 *
41 * $directory = new FilesystemIterator('/path/to/directory');
42 *
43 * $iterator = new FileExtensionFilterIterator($directory, 'txt', 'php');
44 *
45 * foreach ($iterator as $file) {
46 *     echo $file->getFilename(); // Outputs only .txt and .php files
47 * }
48 * ```
49 *
50 * **Note:** If a `RecursiveIterator` is provided, the iterator will traverse subdirectories.
51 *
52 * @since 1.0.0
53 */
54class FileExtensionFilterIterator extends CountableFilterIterator
55{
56    /**
57     * The file extensions to accept.
58     *
59     * @var string[]
60     */
61    private  array $extensions;
62
63    /**
64     * Initializes the FileExtensionFilterIterator.
65     *
66     * @param FilesystemIterator|RecursiveDirectoryIterator $iterator the directory iterator to wrap
67     * @param string ...$extensions The allowed file extensions.
68     */
69    public function __construct(FilesystemIterator $iterator, string ...$extensions)
70    {
71        $this->extensions = array_map(static fn(string $fileType): string => mb_ltrim($fileType, '.'), $extensions);
72
73        $innerIterator = $iterator instanceof RecursiveIterator
74            ? new RecursiveIteratorIterator($iterator)
75            : new IteratorIterator($iterator);
76
77        parent::__construct($innerIterator);
78    }
79
80    /**
81     * Determines whether the current file should be accepted.
82     *
83     * - Directories are accepted only if the underlying iterator is recursive.
84     * - Files are accepted only if their extension matches the allowed list.
85     *
86     * @return bool true if the file is accepted, false otherwise
87     */
88    public function accept(): bool
89    {
90        /** @var SplFileInfo $current */
91        $current = $this->current();
92
93        if ($current->isDir()) {
94            return $this->getInnerIterator() instanceof RecursiveIterator;
95        }
96
97        $extension = pathinfo($current->getFilename(), \PATHINFO_EXTENSION);
98
99        return \in_array($extension, $this->extensions, true);
100    }
101}