Passed
Push — master ( 61518e...62af2e )
by Théo
08:29 queued 01:52
created

Configuration::validateConfigKeys()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 1
dl 0
loc 7
ccs 0
cts 4
cp 0
crap 2
rs 9.4285
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;
16
17
use Closure;
18
use InvalidArgumentException;
19
use Iterator;
20
use RuntimeException;
21
use SplFileInfo;
22
use Symfony\Component\Finder\Finder;
23
24
/**
25
 * @final
26
 * TODO: make this class as final as soon as the underlying deprecated class is removed.
27
 */
28
class Configuration
29
{
30
    private const FINDER_KEYWORD = 'finders';
31
    private const PATCHERS_KEYWORD = 'patchers';
32
    private const WHITELIST_KEYWORD = 'whitelist';
33
    private const GLOBAL_NAMESPACE_KEYWORD = 'global_namespace_whitelist';
34
35
    private const KEYWORDS = [
36
        self::FINDER_KEYWORD,
37
        self::PATCHERS_KEYWORD,
38
        self::WHITELIST_KEYWORD,
39
        self::GLOBAL_NAMESPACE_KEYWORD,
40
    ];
41
42
    private $path;
43
    private $filesWithContents;
44
    private $patchers;
45
    private $whitelist;
46
47
    /**
48
     * @param string|null $path  Absolute path to the configuration file.
49
     * @param string[]    $paths List of paths to append besides the one configured
50
     *
51
     * @return self
52
     */
53
    public static function load(string $path = null, array $paths = []): self
54
    {
55
        if (null === $path) {
56
            $config = [];
57
        } else {
58
            $config = include $path;
59
60
            if (false === is_array($config)) {
61
                throw new InvalidArgumentException(
62
                    sprintf(
63
                        'Expected configuration to be an array, found "%s" instead.',
64
                        gettype($config)
65
                    )
66
                );
67
            }
68
        }
69
70
        self::validateConfigKeys($config);
71
72
        $patchers = self::retrievePatchers($config);
73
        $whitelist = self::retrieveWhitelist($config);
74
75
        $finders = self::retrieveFinders($config);
76
        $filesFromPaths = self::retrieveFilesFromPaths($paths);
77
        $filesWithContents = self::retrieveFilesWithContents(chain($filesFromPaths, ...$finders));
78
79
        return new self($path, $filesWithContents, $patchers, $whitelist);
80
    }
81
82
    /**
83
     * @param string|null        $path                        Absolute path to the configuration file loaded.
84
     * @param [string, string][] $filesWithContents           Array of tuple with the first argument being the file path and the second its contents
85
     * @param callable[]         $patchers                    List of closures which can alter the content of the files being
86
     *                                                        scoped.
87
     * @param string[]           $whitelist                   List of classes that will not be scoped.
88
     * @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...
89
     *                                                        returning a boolean which if `true` means the class should be scoped
90
     *                                                        (i.e. is ignored) or scoped otherwise.
91
     */
92
    private function __construct(
93
        ?string $path,
94
        array $filesWithContents,
95
        array $patchers,
96
        array $whitelist
97
    ) {
98
        $this->path = $path;
99
        $this->filesWithContents = $filesWithContents;
100
        $this->patchers = $patchers;
101
        $this->whitelist = $whitelist;
102
    }
103
104
    public function withPaths(array $paths): self
105
    {
106
        $filesWithContents = self::retrieveFilesWithContents(
107
            chain(
108
                self::retrieveFilesFromPaths(
109
                    array_unique($paths)
110
                )
111
            )
112
        );
113
114
        return new self(
115
            $this->path,
116
            array_merge($this->filesWithContents, $filesWithContents),
117
            $this->patchers,
118
            $this->whitelist
119
        );
120
    }
121
122
    public function getPath(): string
123
    {
124
        return $this->path;
125
    }
126
127
    public function getFilesWithContents(): array
128
    {
129
        return $this->filesWithContents;
130
    }
131
132
    /**
133
     * @return callable[]
134
     */
135
    public function getPatchers(): array
136
    {
137
        return $this->patchers;
138
    }
139
140
    public function getWhitelist(): array
141
    {
142
        return $this->whitelist;
143
    }
144
145
    private static function validateConfigKeys(array $config): void
146
    {
147
        array_map(
148
            ['self', 'validateConfigKey'],
149
            array_keys($config)
150
        );
151
    }
152
153
    private static function validateConfigKey(string $key): void
154
    {
155
        if (false === in_array($key, self::KEYWORDS)) {
156
            throw new InvalidArgumentException(
157
                sprintf(
158
                    'Invalid configuration key value "%s" found.',
159
                    $key
160
                )
161
            );
162
        }
163
    }
164
165
    private static function retrievePatchers(array $config): array
166
    {
167
        if (false === array_key_exists(self::PATCHERS_KEYWORD, $config)) {
168
            return [];
169
        }
170
171
        $patchers = $config[self::PATCHERS_KEYWORD];
172
173
        if (false === is_array($patchers)) {
174
            throw new InvalidArgumentException(
175
                sprintf(
176
                    'Expected patchers to be an array of callables, found "%s" instead.',
177
                    gettype($patchers)
178
                )
179
            );
180
        }
181
182
        foreach ($patchers as $index => $patcher) {
183
            if (is_callable($patcher)) {
184
                continue;
185
            }
186
187
            throw new InvalidArgumentException(
188
                sprintf(
189
                    'Expected patchers to be an array of callables, the "%d" element is not.',
190
                    $index
191
                )
192
            );
193
        }
194
195
        return $patchers;
196
    }
197
198
    private static function retrieveWhitelist(array $config): array
199
    {
200
        if (false === array_key_exists(self::WHITELIST_KEYWORD, $config)) {
201
            return [];
202
        }
203
204
        $whitelist = $config[self::WHITELIST_KEYWORD];
205
206
        if (false === is_array($whitelist)) {
207
            throw new InvalidArgumentException(
208
                sprintf(
209
                    'Expected whitelist to be an array of strings, found "%s" instead.',
210
                    gettype($whitelist)
211
                )
212
            );
213
        }
214
215
        foreach ($whitelist as $index => $className) {
216
            if (is_string($className)) {
217
                continue;
218
            }
219
220
            throw new InvalidArgumentException(
221
                sprintf(
222
                    'Expected whitelist to be an array of string, the "%d" element is not.',
223
                    $index
224
                )
225
            );
226
        }
227
228
        return $whitelist;
229
    }
230
231
    private static function retrieveFinders(array $config): array
232
    {
233
        if (false === array_key_exists(self::FINDER_KEYWORD, $config)) {
234
            return [];
235
        }
236
237
        $finders = $config[self::FINDER_KEYWORD];
238
239
        if (false === is_array($finders)) {
240
            throw new InvalidArgumentException(
241
                sprintf(
242
                    'Expected finders to be an array of "%s", found "%s" instead.',
243
                    Finder::class,
244
                    gettype($finders)
245
                )
246
            );
247
        }
248
249
        foreach ($finders as $index => $finder) {
250
            if ($finder instanceof Finder) {
251
                continue;
252
            }
253
254
            throw new InvalidArgumentException(
255
                sprintf(
256
                    'Expected finders to be an array of "%s", the "%d" element is not.',
257
                    Finder::class,
258
                    $index
259
                )
260
            );
261
        }
262
263
        return $finders;
264
    }
265
266
    /**
267
     * @param string[] $paths
268
     *
269
     * @return iterable
270
     */
271
    private static function retrieveFilesFromPaths(array $paths): iterable
272
    {
273
        if ([] === $paths) {
274
            return [];
275
        }
276
277
        $pathsToSearch = [];
278
        $filesToAppend = [];
279
280
        foreach ($paths as $path) {
281
            if (false === file_exists($path)) {
282
                throw new RuntimeException(
283
                    sprintf(
284
                        'Could not find the file "%s".',
285
                        $path
286
                    )
287
                );
288
            }
289
290
            if (is_dir($path)) {
291
                $pathsToSearch[] = $path;
292
            } else {
293
                $filesToAppend[] = $path;
294
            }
295
        }
296
297
        $finder = new Finder();
298
299
        $finder->files()
300
            ->in($pathsToSearch)
301
            ->append($filesToAppend)
302
            ->sortByName()
303
        ;
304
305
        return $finder;
306
    }
307
308
    /**
309
     * @param Iterator $files
310
     *
311
     * @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...
312
     */
313
    private static function retrieveFilesWithContents(Iterator $files): array
314
    {
315
        return array_reduce(
316
            iterator_to_array($files),
317
            function (array $files, SplFileInfo $fileInfo): array {
318
                $file = $fileInfo->getRealPath();
319
320
                if (false === $file) {
321
                    throw new RuntimeException(
322
                        sprintf(
323
                            'Could not find the file "%s".',
324
                            (string) $fileInfo
325
                        )
326
                    );
327
                }
328
329
                if (false === is_readable($file)) {
330
                    throw new RuntimeException(
331
                        sprintf(
332
                            'Could not read the file "%s".',
333
                            $file
334
                        )
335
                    );
336
                }
337
338
                $files[$fileInfo->getRealPath()] = [$fileInfo->getRealPath(), file_get_contents($file)];
339
340
                return $files;
341
            },
342
            []
343
        );
344
    }
345
}
346