1<?php
2
3/*
4 * This file is part of the Symfony package.
5 *
6 * (c) Fabien Potencier <fabien@symfony.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12namespace Symfony\Component\Finder\Iterator;
13
14/**
15 * ExcludeDirectoryFilterIterator filters out directories.
16 *
17 * @author Fabien Potencier <fabien@symfony.com>
18 */
19class ExcludeDirectoryFilterIterator extends \FilterIterator implements \RecursiveIterator
20{
21    private $iterator;
22    private $isRecursive;
23    private $excludedDirs = [];
24    private $excludedPattern;
25
26    /**
27     * @param \Iterator $iterator    The Iterator to filter
28     * @param string[]  $directories An array of directories to exclude
29     */
30    public function __construct(\Iterator $iterator, array $directories)
31    {
32        $this->iterator = $iterator;
33        $this->isRecursive = $iterator instanceof \RecursiveIterator;
34        $patterns = [];
35        foreach ($directories as $directory) {
36            $directory = rtrim($directory, '/');
37            if (!$this->isRecursive || false !== strpos($directory, '/')) {
38                $patterns[] = preg_quote($directory, '#');
39            } else {
40                $this->excludedDirs[$directory] = true;
41            }
42        }
43        if ($patterns) {
44            $this->excludedPattern = '#(?:^|/)(?:'.implode('|', $patterns).')(?:/|$)#';
45        }
46
47        parent::__construct($iterator);
48    }
49
50    /**
51     * Filters the iterator values.
52     *
53     * @return bool True if the value should be kept, false otherwise
54     */
55    public function accept()
56    {
57        if ($this->isRecursive && isset($this->excludedDirs[$this->getFilename()]) && $this->isDir()) {
58            return false;
59        }
60
61        if ($this->excludedPattern) {
62            $path = $this->isDir() ? $this->current()->getRelativePathname() : $this->current()->getRelativePath();
63            $path = str_replace('\\', '/', $path);
64
65            return !preg_match($this->excludedPattern, $path);
66        }
67
68        return true;
69    }
70
71    /**
72     * @return bool
73     */
74    public function hasChildren()
75    {
76        return $this->isRecursive && $this->iterator->hasChildren();
77    }
78
79    public function getChildren()
80    {
81        $children = new self($this->iterator->getChildren(), []);
82        $children->excludedDirs = $this->excludedDirs;
83        $children->excludedPattern = $this->excludedPattern;
84
85        return $children;
86    }
87}
88