SourceFileIteratorFactory   A
last analyzed

Complexity

Total Complexity 9

Size/Duplication

Total Lines 189
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 5
Bugs 1 Features 0
Metric Value
eloc 70
c 5
b 1
f 0
dl 0
loc 189
ccs 73
cts 73
cp 1
rs 10
wmc 9

9 Methods

Rating   Name   Duplication   Size   Complexity  
A createFilesIterator() 0 11 1
A createNamespaceIterator() 0 11 2
A create() 0 41 1
preparePattern() 0 18 ?
A hp$0 ➔ createClassmapIterator() 0 54 3
A hp$0 ➔ accept() 0 12 3
A hp$0 ➔ __construct() 0 7 1
A hp$0 ➔ preparePattern() 0 18 2
createClassmapIterator() 0 54 ?
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, $this->preparePattern(...$exclude)) extends FilterIterator {
145
            /** @var string|null */
146
            private $excludePattern;
147
148
            /**
149
             * Constructor.
150
             *
151
             * @param Iterator    $iterator
152
             * @param string|null $excludePattern
153
             */
154 9
            public function __construct(
155
                Iterator $iterator,
156
                ?string $excludePattern
157
            ) {
158 9
                $this->excludePattern = $excludePattern;
159
160 9
                parent::__construct($iterator);
161 9
            }
162
163
            /**
164
             * Check whether the current element of the iterator is acceptable.
165
             *
166
             * @return bool
167
             */
168 5
            public function accept(): bool
169
            {
170
                /** @var SplFileInfo $file */
171 5
                $file = $this->getInnerIterator()->current();
172
173
                return (
174 5
                    $file->isFile()
175
                    && (
176 5
                        $this->excludePattern === null
177 1
                        ?: !preg_match(
178 1
                            $this->excludePattern,
179 5
                            str_replace('\\', '/', $file->getRealPath())
180
                        )
181
                    )
182
                );
183
            }
184
        };
185
    }
186
187
    /**
188
     * @param string ...$excludePatterns
189
     *
190
     * @return string|null
191
     */
192 9
    private function preparePattern(string ...$excludePatterns): ?string
193
    {
194 9
        if (count($excludePatterns) === 0) {
195 8
            return null;
196
        }
197
198 1
        return sprintf(
199 1
            '@^(%s)$@',
200 1
            implode(
201 1
                '|',
202 1
                array_map(
203
                    function (string $pattern): string {
204 1
                        return preg_quote(
205 1
                            str_replace('\\', '/', $pattern),
206 1
                            '@'
207
                        );
208 1
                    },
209 1
                    $excludePatterns
210
                )
211
            )
212
        );
213
    }
214
}
215