Passed
Push — master ( 01e41e...21756f )
by Théo
02:37
created

Configuration::retrieveWhitelist()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 32
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 17
nc 5
nop 1
dl 0
loc 32
rs 8.439
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the humbug/php-scoper package.
7
 *
8
 * Copyright (c) 2017 Théo FIDRY <[email protected]>,
9
 *                    Pádraic Brady <[email protected]>
10
 *
11
 * For the full copyright and license information, please view the LICENSE
12
 * file that was distributed with this source code.
13
 */
14
15
namespace Humbug\PhpScoper\Console;
16
17
use Closure;
18
use InvalidArgumentException;
19
use Iterator;
20
use RuntimeException;
21
use Symfony\Component\Finder\Finder;
22
use function Humbug\PhpScoper\iterables_to_iterator;
23
24
final class Configuration
25
{
26
    private const FINDER_KEYWORD = 'finders';
27
    private const PATCHERS_KEYWORD = 'patchers';
28
    private const WHITELIST_KEYWORD = 'whitelist';
29
    private const GLOBAL_NAMESPACE_KEYWORD = 'global_namespace_whitelist';
30
31
    private const KEYWORDS = [
32
        self::FINDER_KEYWORD,
33
        self::PATCHERS_KEYWORD,
34
        self::WHITELIST_KEYWORD,
35
        self::GLOBAL_NAMESPACE_KEYWORD,
36
    ];
37
38
    private $path;
39
    private $filesWithContents;
40
    private $patchers;
41
    private $whitelist;
42
43
    /**
44
     * @param string|null $path  Absolute path to the configuration file.
45
     * @param string[]    $paths List of paths to append besides the one configured
46
     *
47
     * @return self
48
     */
49
    public static function load(string $path = null, array $paths = []): self
50
    {
51
        if (null === $path) {
52
            $config = [];
53
        } else {
54
            $config = include $path;
55
56
            if (false === is_array($config)) {
57
                throw new InvalidArgumentException(
58
                    sprintf(
59
                        'Expected configuration to be an array, found "%s" instead.',
60
                        gettype($config)
61
                    )
62
                );
63
            }
64
        }
65
66
        self::validateConfigKeys($config);
67
68
        $patchers = self::retrievePatchers($config);
69
        $whitelist = self::retrieveWhitelist($config);
70
71
        $finders = self::retrieveFinders($config);
72
        $filesFromPaths = self::retrieveFilesFromPaths($paths);
73
        $filesWithContents = self::retrieveFilesWithContents(iterables_to_iterator($filesFromPaths, ...$finders));
74
75
        return new self($path, $filesWithContents, $patchers, $whitelist);
76
    }
77
78
    /**
79
     * @param string|null        $path                        Absolute path to the configuration file loaded.
80
     * @param [string, string][] $filesWithContents           Array of tuple with the first argument being the file path and the second its contents
81
     * @param callable[]         $patchers                    List of closures which can alter the content of the files being
82
     *                                                        scoped.
83
     * @param string[]           $whitelist                   List of classes that will not be scoped.
84
     * @param Closure            $globalNamespaceWhitelisters Closure taking a class name from the global namespace as an argument and
0 ignored issues
show
Documentation introduced by
There is no parameter named $globalNamespaceWhitelisters. Did you maybe mean $whitelist?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
85
     *                                                        returning a boolean which if `true` means the class should be scoped
86
     *                                                        (i.e. is ignored) or scoped otherwise.
87
     */
88
    private function __construct(
89
        ?string $path,
90
        array $filesWithContents,
91
        array $patchers,
92
        array $whitelist
93
    ) {
94
        $this->path = $path;
95
        $this->filesWithContents = $filesWithContents;
96
        $this->patchers = $patchers;
97
        $this->whitelist = $whitelist;
98
    }
99
100
    public function withPaths(array $paths): self
101
    {
102
        $filesWithContents = self::retrieveFilesWithContents(
103
            iterables_to_iterator(
104
                self::retrieveFilesFromPaths(
105
                    array_unique($paths)
106
                )
107
            )
108
        );
109
110
        return new self(
111
            $this->path,
112
            array_merge($this->filesWithContents, $filesWithContents),
113
            $this->patchers,
114
            $this->whitelist
115
        );
116
    }
117
118
    public function getPath(): string
119
    {
120
        return $this->path;
121
    }
122
123
    public function getFilesWithContents(): array
124
    {
125
        return $this->filesWithContents;
126
    }
127
128
    /**
129
     * @return callable[]
130
     */
131
    public function getPatchers(): array
132
    {
133
        return $this->patchers;
134
    }
135
136
    public function getWhitelist(): array
137
    {
138
        return $this->whitelist;
139
    }
140
141
    private static function validateConfigKeys(array $config): void
142
    {
143
        array_map(
144
            ['self', 'validateConfigKey'],
145
            array_keys($config)
146
        );
147
    }
148
149
    private static function validateConfigKey(string $key): void
150
    {
151
        if (false === in_array($key, self::KEYWORDS)) {
152
            throw new InvalidArgumentException(
153
                sprintf(
154
                    'Invalid configuration key value "%s" found.',
155
                    $key
156
                )
157
            );
158
        }
159
    }
160
161
    private static function retrievePatchers(array $config): array
162
    {
163
        if (false === array_key_exists(self::PATCHERS_KEYWORD, $config)) {
164
            return [];
165
        }
166
167
        $patchers = $config[self::PATCHERS_KEYWORD];
168
169
        if (false === is_array($patchers)) {
170
            throw new InvalidArgumentException(
171
                sprintf(
172
                    'Expected patchers to be an array of callables, found "%s" instead.',
173
                    gettype($patchers)
174
                )
175
            );
176
        }
177
178
        foreach ($patchers as $index => $patcher) {
179
            if (is_callable($patcher)) {
180
                continue;
181
            }
182
183
            throw new InvalidArgumentException(
184
                sprintf(
185
                    'Expected patchers to be an array of callables, the "%d" element is not.',
186
                    $index
187
                )
188
            );
189
        }
190
191
        return $patchers;
192
    }
193
194
    private static function retrieveWhitelist(array $config): array
195
    {
196
        if (false === array_key_exists(self::WHITELIST_KEYWORD, $config)) {
197
            return [];
198
        }
199
200
        $whitelist = $config[self::WHITELIST_KEYWORD];
201
202
        if (false === is_array($whitelist)) {
203
            throw new InvalidArgumentException(
204
                sprintf(
205
                    'Expected whitelist to be an array of strings, found "%s" instead.',
206
                    gettype($whitelist)
207
                )
208
            );
209
        }
210
211
        foreach ($whitelist as $index => $className) {
212
            if (is_string($className)) {
213
                continue;
214
            }
215
216
            throw new InvalidArgumentException(
217
                sprintf(
218
                    'Expected whitelist to be an array of string, the "%d" element is not.',
219
                    $index
220
                )
221
            );
222
        }
223
224
        return $whitelist;
225
    }
226
227
    private static function retrieveFinders(array $config): array
228
    {
229
        if (false === array_key_exists(self::FINDER_KEYWORD, $config)) {
230
            return [];
231
        }
232
233
        $finders = $config[self::FINDER_KEYWORD];
234
235
        if (false === is_array($finders)) {
236
            throw new InvalidArgumentException(
237
                sprintf(
238
                    'Expected finders to be an array of "%s", found "%s" instead.',
239
                    Finder::class,
240
                    gettype($finders)
241
                )
242
            );
243
        }
244
245
        foreach ($finders as $index => $finder) {
246
            if ($finder instanceof Finder) {
247
                continue;
248
            }
249
250
            throw new InvalidArgumentException(
251
                sprintf(
252
                    'Expected finders to be an array of "%s", the "%d" element is not.',
253
                    Finder::class,
254
                    $index
255
                )
256
            );
257
        }
258
259
        return $finders;
260
    }
261
262
    /**
263
     * @param string[] $paths
264
     *
265
     * @return iterable
266
     */
267
    private static function retrieveFilesFromPaths(array $paths): iterable
268
    {
269
        if ([] === $paths) {
270
            return [];
271
        }
272
273
        $pathsToSearch = [];
274
        $filesToAppend = [];
275
276
        foreach ($paths as $path) {
277
            if (false === file_exists($path)) {
278
                throw new RuntimeException(
279
                    sprintf(
280
                        'Could not find the file "%s".',
281
                        $path
282
                    )
283
                );
284
            }
285
286
            if (is_dir($path)) {
287
                $pathsToSearch[] = $path;
288
            } else {
289
                $filesToAppend[] = $path;
290
            }
291
        }
292
293
        $finder = new Finder();
294
295
        $finder->files()
296
            ->in($pathsToSearch)
297
            ->append($filesToAppend)
298
            ->sortByName()
299
        ;
300
301
        return $finder;
302
    }
303
304
    /**
305
     * @param Iterator $files
306
     *
307
     * @return [string, string][] Array of tuple with the first argument being the file path and the second its contents
0 ignored issues
show
Documentation introduced by
The doc-type string,">[string, could not be parsed: Unknown type name "[" at position 0. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
308
     */
309
    private static function retrieveFilesWithContents(Iterator $files): array
310
    {
311
        return array_reduce(
312
            iterator_to_array($files),
313
            function (array $files, $fileInfo): array {
314
                $file = (string) $fileInfo;
315
316
                if (false === file_exists($file)) {
317
                    throw new RuntimeException(
318
                        sprintf(
319
                            'Could not find the file "%s".',
320
                            $file
321
                        )
322
                    );
323
                }
324
325
                if (false === is_readable($file)) {
326
                    throw new RuntimeException(
327
                        sprintf(
328
                            'Could not read the file "%s".',
329
                            $file
330
                        )
331
                    );
332
                }
333
334
                $files[$fileInfo->getRealPath()] = [$fileInfo->getRealPath(), file_get_contents($file)];
335
336
                return $files;
337
            },
338
            []
339
        );
340
    }
341
}
342