Test Failed
Pull Request — master (#31)
by Björn
02:52
created

SourceFileIteratorFactory.php$0 ➔ preparePattern()   A

Complexity

Conditions 1

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
dl 0
loc 14
ccs 6
cts 6
cp 1
crap 1
rs 9.7998
c 0
b 0
f 0
1
<?php
2
/**
3
 * Copyright MediaCT. All rights reserved.
4
 * https://www.mediact.nl
5
 */
6
7
namespace Mediact\DependencyGuard\Composer\Iterator;
8
9
use AppendIterator;
10
use ArrayIterator;
11
use Composer\Composer;
12
use FilterIterator;
13
use Iterator;
14
use Mediact\DependencyGuard\Iterator\FileIterator;
15
use Mediact\DependencyGuard\Iterator\FileIteratorFactoryInterface;
16
use Mediact\DependencyGuard\Iterator\FileIteratorInterface;
17
use RecursiveDirectoryIterator;
18
use RecursiveIteratorIterator;
19
use SplFileInfo;
20
21
class SourceFileIteratorFactory implements FileIteratorFactoryInterface
22
{
23
    /**
24
     * Get an iterable list of source files for the root package of the given
25
     * Composer instance.
26
     *
27
     * @param Composer $composer
28
     *
29
     * @return FileIteratorInterface
30
     */
31 9
    public function create(Composer $composer): FileIteratorInterface
32
    {
33 9
        $config              = $composer->getConfig();
34 9
        $autoloadGenerator   = $composer->getAutoloadGenerator();
35 9
        $installationManager = $composer->getInstallationManager();
36 9
        $package             = $composer->getPackage();
37
38 9
        $autoloadGenerator->setClassMapAuthoritative(
39 9
            $config->get('classmap-authoritative')
40
        );
41 9
        $autoloadGenerator->setDevMode(false);
42
43 9
        $packageMap = $autoloadGenerator->buildPackageMap(
44 9
            $installationManager,
45 9
            $package,
46 9
            [$package]
47
        );
48
49 9
        $directives = $autoloadGenerator->parseAutoloads($packageMap, $package);
50
51 9
        $files = new AppendIterator();
52
53 9
        $files->append(
54 9
            $this->createClassmapIterator(
55 9
                $directives['classmap'] ?? [],
56 9
                $directives['exclude-from-classmap'] ?? []
57
            )
58
        );
59 9
        $files->append(
60 9
            $this->createFilesIterator(
61 9
                ...array_values($directives['files'] ?? [])
62
            )
63
        );
64 9
        $files->append(
65 9
            $this->createNamespaceIterator($directives['psr-0'] ?? [])
66
        );
67 9
        $files->append(
68 9
            $this->createNamespaceIterator($directives['psr-4'] ?? [])
69
        );
70
71 9
        return new FileIterator($files);
72
    }
73
74
    /**
75
     * Create an iterator for the given file paths.
76
     *
77
     * @param string ...$paths
78
     *
79
     * @return Iterator
80
     */
81 9
    private function createFilesIterator(string ...$paths): Iterator
82
    {
83 9
        return new ArrayIterator(
84 9
            array_map(
85
                function (string $path) : SplFileInfo {
86 1
                    return new SplFileInfo($path);
87 9
                },
88 9
                array_filter(
89 9
                    $paths,
90
                    function (string $path) : bool {
91 1
                        return is_readable($path);
92 9
                    }
93
                )
94
            )
95
        );
96
    }
97
98
    /**
99
     * Create an iterator for the given namespaces.
100
     *
101
     * @param array $namespaces
102
     *
103
     * @return Iterator
104
     */
105 9
    private function createNamespaceIterator(array $namespaces): Iterator
106
    {
107 9
        $files = new AppendIterator();
108
109 9
        foreach ($namespaces as $classmap) {
110 2
            $files->append(
111 2
                $this->createClassmapIterator($classmap)
112
            );
113
        }
114
115 9
        return $files;
116
    }
117
118
    /**
119
     * Create a class map iterator using the given class maps and exclude patterns.
120
     *
121
     * @param iterable|string[] $classmap
122
     * @param iterable|string[] $exclude
123
     *
124
     * @return Iterator|SplFileInfo[]
125
     */
126 9
    private function createClassmapIterator(
127
        iterable $classmap,
128
        iterable $exclude = []
129
    ): Iterator {
130 9
        $files = new AppendIterator();
131
132 9
        foreach ($classmap as $directory) {
133 5
            if (!is_dir($directory)) {
134 1
                continue;
135
            }
136
137 5
            $files->append(
138 5
                new RecursiveIteratorIterator(
139 5
                    new RecursiveDirectoryIterator($directory)
140
                )
141
            );
142
        }
143
144
        return new class ($files, ...$exclude) extends FilterIterator {
145
            /** @var string|null */
146
            private $excludePattern;
147
148
            /**
149
             * Constructor.
150
             *
151
             * @param Iterator $iterator
152
             * @param string   ...$excludePatterns
153
             */
154 9
            public function __construct(
155
                Iterator $iterator,
156
                string ...$excludePatterns
157
            ) {
158 9
                if (!empty($excludePatterns)) {
159 1
                    $this->excludePattern = $this->preparePattern(...$excludePatterns);
160 1
                }
161 1
162
                parent::__construct($iterator);
163
            }
164
165 9
            /**
166 9
             * @param string ...$excludePatterns
167
             *
168
             * @return string
169
             */
170
            private function preparePattern(string ...$excludePatterns): string
171
            {
172
                return sprintf(
173 5
                    '@^(%s)$@',
174
                    implode(
175
                        '|',
176 5
                        array_map(
177
                            function (string $pattern): string {
178
                                return preg_quote(
179 5
                                    str_replace('\\', '/', $pattern),
180
                                    '@'
181 5
                                );
182 1
                            },
183 1
                            $excludePatterns
184 5
                        )
185
                    )
186
                );
187
            }
188
189
            /**
190
             * Check whether the current element of the iterator is acceptable.
191
             *
192
             * @return bool
193
             */
194
            public function accept(): bool
195
            {
196
                /** @var SplFileInfo $file */
197
                $file = $this->getInnerIterator()->current();
198
199
                return (
200
                    $file->isFile()
201
                    && (
202
                        $this->excludePattern === null
203
                        ?: !preg_match(
204
                            $this->excludePattern,
205
                            str_replace('\\', '/', $file->getRealPath())
206
                        )
207
                    )
208
                );
209
            }
210
        };
211
    }
212
}
213