Test Failed
Push — master ( 200868...356be9 )
by Théo
02:05
created

Configuration::retrieveWhitelist()   C

Complexity

Conditions 11
Paths 32

Size

Total Lines 80

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 132

Importance

Changes 0
Metric Value
cc 11
nc 32
nop 1
dl 0
loc 80
ccs 0
cts 40
cp 0
crap 132
rs 6.2896
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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;
16
17
use Closure;
18
use InvalidArgumentException;
19
use Iterator;
20
use RuntimeException;
21
use SplFileInfo;
22
use Symfony\Component\Finder\Finder;
23
use function is_bool;
24
25
/**
26
 * @final
27
 * TODO: make this class as final as soon as the underlying deprecated class is removed.
28
 */
29
class Configuration
30
{
31
    private const PREFIX = 'prefix';
32
    private const FINDER_KEYWORD = 'finders';
33
    private const PATCHERS_KEYWORD = 'patchers';
34
    private const WHITELIST_KEYWORD = 'whitelist';
35
    private const WHITELIST_GLOBAL_CONSTANTS_KEYWORD = 'whitelist-global-constants';
36
    private const WHITELIST_GLOBAL_CLASSES_KEYWORD = 'whitelist-global-classes';
37
    private const WHITELIST_GLOBAL_FUNCTIONS_KEYWORD = 'whitelist-global-functions';
38
39
    private const KEYWORDS = [
40
        self::PREFIX,
41
        self::FINDER_KEYWORD,
42
        self::PATCHERS_KEYWORD,
43
        self::WHITELIST_KEYWORD,
44
        self::WHITELIST_GLOBAL_FUNCTIONS_KEYWORD,
45
    ];
46
47
    private $path;
48
    private $prefix;
49
    private $filesWithContents;
50
    private $patchers;
51
    private $whitelist;
52
53
    /**
54
     * @param string|null $path  Absolute path to the configuration file.
55
     * @param string[]    $paths List of paths to append besides the one configured
56
     *
57
     * @return self
58
     */
59
    public static function load(string $path = null, array $paths = []): self
60
    {
61
        if (null === $path) {
62
            $config = [];
63
        } else {
64
            $config = include $path;
65
66
            if (false === is_array($config)) {
67
                throw new InvalidArgumentException(
68
                    sprintf(
69
                        'Expected configuration to be an array, found "%s" instead.',
70
                        gettype($config)
71
                    )
72
                );
73
            }
74
        }
75
76
        self::validateConfigKeys($config);
77
78
        $prefix = self::retrievePrefix($config);
79
80
        $patchers = self::retrievePatchers($config);
81
        $whitelist = self::retrieveWhitelist($config);
82
83
        $finders = self::retrieveFinders($config);
84
        $filesFromPaths = self::retrieveFilesFromPaths($paths);
85
        $filesWithContents = self::retrieveFilesWithContents(chain($filesFromPaths, ...$finders));
86
87
        return new self($path, $prefix, $filesWithContents, $patchers, $whitelist);
88
    }
89
90
    /**
91
     * @param string|null        $path                        Absolute path to the configuration file loaded.
92
     * @param string|null        $prefix                      The prefix applied.
93
     * @param [string, string][] $filesWithContents           Array of tuple with the first argument being the file path and the second its contents
94
     * @param callable[]         $patchers                    List of closures which can alter the content of the files being
95
     *                                                        scoped.
96
     * @param Whitelist          $whitelist                   List of classes that will not be scoped.
97
     * @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...
98
     *                                                        returning a boolean which if `true` means the class should be scoped
99
     *                                                        (i.e. is ignored) or scoped otherwise.
100
     */
101
    private function __construct(
102
        ?string $path,
103
        ?string $prefix,
104
        array $filesWithContents,
105
        array $patchers,
106
        Whitelist $whitelist
107
    ) {
108
        $this->path = $path;
109
        $this->prefix = $prefix;
110
        $this->filesWithContents = $filesWithContents;
111
        $this->patchers = $patchers;
112
        $this->whitelist = $whitelist;
113
    }
114
115
    public function withPaths(array $paths): self
116
    {
117
        $filesWithContents = self::retrieveFilesWithContents(
118
            chain(
119
                self::retrieveFilesFromPaths(
120
                    array_unique($paths)
121
                )
122
            )
123
        );
124
125
        return new self(
126
            $this->path,
127
            $this->prefix,
128
            array_merge($this->filesWithContents, $filesWithContents),
129
            $this->patchers,
130
            $this->whitelist
131
        );
132
    }
133
134
    public function withPrefix(?string $prefix): self
135
    {
136
        $prefix = self::retrievePrefix([self::PREFIX => $prefix]);
137
138
        return new self(
139
            $this->path,
140
            $prefix,
141
            $this->filesWithContents,
142
            $this->patchers,
143
            $this->whitelist
144
        );
145
    }
146
147
    public function getPath(): string
148
    {
149
        return $this->path;
150
    }
151
152
    public function getPrefix(): ?string
153
    {
154
        return $this->prefix;
155
    }
156
157
    public function getFilesWithContents(): array
158
    {
159
        return $this->filesWithContents;
160
    }
161
162
    /**
163
     * @return callable[]
164
     */
165
    public function getPatchers(): array
166
    {
167
        return $this->patchers;
168
    }
169
170
    public function getWhitelist(): Whitelist
171
    {
172
        return $this->whitelist;
173
    }
174
175
    private static function validateConfigKeys(array $config): void
176
    {
177
        array_map(
178
            ['self', 'validateConfigKey'],
179
            array_keys($config)
180
        );
181
    }
182
183
    private static function validateConfigKey(string $key): void
184
    {
185
        if (false === in_array($key, self::KEYWORDS)) {
186
            throw new InvalidArgumentException(
187
                sprintf(
188
                    'Invalid configuration key value "%s" found.',
189
                    $key
190
                )
191
            );
192
        }
193
    }
194
195
    /**
196
     * If the prefix is set to null in the config file/argument then a random prefix is being used. However if set to
197
     * empty, the configuration will use a null prefix.
198
     *
199
     * TL:DR; setting the prefix is a big confusing because it is not properly split in "set prefix" & prefix strategy".
200
     */
201
    private static function retrievePrefix(array $config): ?string
202
    {
203
        $prefix = array_key_exists(self::PREFIX, $config) ? $config[self::PREFIX] : null;
204
205
        if (null === $prefix) {
206
            return null;
207
        }
208
209
        $prefix = trim($prefix);
210
211
        return '' === $prefix ? null : $prefix;
212
    }
213
214
    private static function retrievePatchers(array $config): array
215
    {
216
        if (false === array_key_exists(self::PATCHERS_KEYWORD, $config)) {
217
            return [];
218
        }
219
220
        $patchers = $config[self::PATCHERS_KEYWORD];
221
222
        if (false === is_array($patchers)) {
223
            throw new InvalidArgumentException(
224
                sprintf(
225
                    'Expected patchers to be an array of callables, found "%s" instead.',
226
                    gettype($patchers)
227
                )
228
            );
229
        }
230
231
        foreach ($patchers as $index => $patcher) {
232
            if (is_callable($patcher)) {
233
                continue;
234
            }
235
236
            throw new InvalidArgumentException(
237
                sprintf(
238
                    'Expected patchers to be an array of callables, the "%d" element is not.',
239
                    $index
240
                )
241
            );
242
        }
243
244
        return $patchers;
245
    }
246
247
    private static function retrieveWhitelist(array $config): Whitelist
248
    {
249
        if (false === array_key_exists(self::WHITELIST_KEYWORD, $config)) {
250
            $whitelist = [];
251
        } else {
252
            $whitelist = $config[self::WHITELIST_KEYWORD];
253
254
            if (false === is_array($whitelist)) {
255
                throw new InvalidArgumentException(
256
                    sprintf(
257
                        'Expected whitelist to be an array of strings, found "%s" instead.',
258
                        gettype($whitelist)
259
                    )
260
                );
261
            }
262
263
            foreach ($whitelist as $index => $className) {
264
                if (is_string($className)) {
265
                    continue;
266
                }
267
268
                throw new InvalidArgumentException(
269
                    sprintf(
270
                        'Expected whitelist to be an array of string, the "%d" element is not.',
271
                        $index
272
                    )
273
                );
274
            }
275
        }
276
277
        if (false === array_key_exists(self::WHITELIST_GLOBAL_CONSTANTS_KEYWORD, $config)) {
278
            $whitelistGlobalConstants = true;
279
        } else {
280
            $whitelistGlobalConstants = $config[self::WHITELIST_GLOBAL_CONSTANTS_KEYWORD];
281
282
            if (false === is_bool($whitelistGlobalConstants)) {
283
                throw new InvalidArgumentException(
284
                    sprintf(
285
                        'Expected %s to be a boolean, found "%s" instead.',
286
                        self::WHITELIST_GLOBAL_CONSTANTS_KEYWORD,
287
                        gettype($whitelistGlobalConstants)
288
                    )
289
                );
290
            }
291
        }
292
293
        if (false === array_key_exists(self::WHITELIST_GLOBAL_CLASSES_KEYWORD, $config)) {
294
            $whitelistGlobalClasses = true;
295
        } else {
296
            $whitelistGlobalClasses = $config[self::WHITELIST_GLOBAL_CLASSES_KEYWORD];
297
298
            if (false === is_bool($whitelistGlobalClasses)) {
299
                throw new InvalidArgumentException(
300
                    sprintf(
301
                        'Expected %s to be a boolean, found "%s" instead.',
302
                        self::WHITELIST_GLOBAL_CLASSES_KEYWORD,
303
                        gettype($whitelistGlobalClasses)
304
                    )
305
                );
306
            }
307
        }
308
309
        if (false === array_key_exists(self::WHITELIST_GLOBAL_FUNCTIONS_KEYWORD, $config)) {
310
            $whitelistGlobalFunctions = true;
311
        } else {
312
            $whitelistGlobalFunctions = $config[self::WHITELIST_GLOBAL_FUNCTIONS_KEYWORD];
313
314
            if (false === is_bool($whitelistGlobalFunctions)) {
315
                throw new InvalidArgumentException(
316
                    sprintf(
317
                        'Expected %s to be a boolean, found "%s" instead.',
318
                        self::WHITELIST_GLOBAL_FUNCTIONS_KEYWORD,
319
                        gettype($whitelistGlobalFunctions)
320
                    )
321
                );
322
            }
323
        }
324
325
        return Whitelist::create($whitelistGlobalConstants, $whitelistGlobalClasses, $whitelistGlobalFunctions, ...$whitelist);
326
    }
327
328
    private static function retrieveFinders(array $config): array
329
    {
330
        if (false === array_key_exists(self::FINDER_KEYWORD, $config)) {
331
            return [];
332
        }
333
334
        $finders = $config[self::FINDER_KEYWORD];
335
336
        if (false === is_array($finders)) {
337
            throw new InvalidArgumentException(
338
                sprintf(
339
                    'Expected finders to be an array of "%s", found "%s" instead.',
340
                    Finder::class,
341
                    gettype($finders)
342
                )
343
            );
344
        }
345
346
        foreach ($finders as $index => $finder) {
347
            if ($finder instanceof Finder) {
348
                continue;
349
            }
350
351
            throw new InvalidArgumentException(
352
                sprintf(
353
                    'Expected finders to be an array of "%s", the "%d" element is not.',
354
                    Finder::class,
355
                    $index
356
                )
357
            );
358
        }
359
360
        return $finders;
361
    }
362
363
    /**
364
     * @param string[] $paths
365
     *
366
     * @return iterable
367
     */
368
    private static function retrieveFilesFromPaths(array $paths): iterable
369
    {
370
        if ([] === $paths) {
371
            return [];
372
        }
373
374
        $pathsToSearch = [];
375
        $filesToAppend = [];
376
377
        foreach ($paths as $path) {
378
            if (false === file_exists($path)) {
379
                throw new RuntimeException(
380
                    sprintf(
381
                        'Could not find the file "%s".',
382
                        $path
383
                    )
384
                );
385
            }
386
387
            if (is_dir($path)) {
388
                $pathsToSearch[] = $path;
389
            } else {
390
                $filesToAppend[] = $path;
391
            }
392
        }
393
394
        $finder = new Finder();
395
396
        $finder->files()
397
            ->in($pathsToSearch)
398
            ->append($filesToAppend)
399
            ->filter(function (SplFileInfo $fileInfo): ?bool {
400
                if ($fileInfo->isLink()) {
401
                    return false;
402
                }
403
404
                return null;
405
            })
406
            ->sortByName()
407
        ;
408
409
        return $finder;
410
    }
411
412
    /**
413
     * @param Iterator $files
414
     *
415
     * @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...
416
     */
417
    private static function retrieveFilesWithContents(Iterator $files): array
418
    {
419
        return array_reduce(
420
            iterator_to_array($files),
421
            function (array $files, SplFileInfo $fileInfo): array {
422
                $file = $fileInfo->getRealPath();
423
424
                if (false === $file) {
425
                    throw new RuntimeException(
426
                        sprintf(
427
                            'Could not find the file "%s".',
428
                            (string) $fileInfo
429
                        )
430
                    );
431
                }
432
433
                if (false === is_readable($file)) {
434
                    throw new RuntimeException(
435
                        sprintf(
436
                            'Could not read the file "%s".',
437
                            $file
438
                        )
439
                    );
440
                }
441
442
                $files[$fileInfo->getRealPath()] = [$fileInfo->getRealPath(), file_get_contents($file)];
443
444
                return $files;
445
            },
446
            []
447
        );
448
    }
449
}
450