SymbolsConfigurationFactory::assertValidElement()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 2
nop 1
dl 0
loc 10
rs 10
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\Configuration;
16
17
use Humbug\PhpScoper\Symbol\NamespaceRegistry;
18
use Humbug\PhpScoper\Symbol\SymbolRegistry;
19
use InvalidArgumentException;
20
use function array_key_exists;
21
use function array_keys;
22
use function array_map;
23
use function array_merge;
24
use function array_pop;
25
use function array_values;
26
use function explode;
27
use function get_debug_type;
28
use function gettype;
29
use function implode;
30
use function is_array;
31
use function is_bool;
32
use function is_string;
33
use function ltrim;
34
use function Safe\preg_match as native_preg_match;
35
use function Safe\sprintf;
36
use function Safe\substr;
37
use function str_replace;
38
use function strpos;
39
use function strtolower;
40
use function trim;
41
42
final class SymbolsConfigurationFactory
43
{
44
    private RegexChecker $regexChecker;
45
46
    public function __construct(RegexChecker $regexChecker)
47
    {
48
        $this->regexChecker = $regexChecker;
49
    }
50
51
    public function createSymbolsConfiguration(array $config): SymbolsConfiguration
52
    {
53
        [
54
            $excludedNamespaceNames,
55
            $excludedNamespaceRegexes,
56
        ] = $this->retrieveElements(
57
            $config,
58
            ConfigurationKeys::EXCLUDE_NAMESPACES_KEYWORD,
59
        );
60
61
        [
62
            $exposedNamespaceNames,
63
            $exposedNamespaceRegexes,
64
        ] = $this->retrieveElements(
65
            $config,
66
            ConfigurationKeys::EXPOSE_NAMESPACES_KEYWORD,
67
        );
68
69
        $legacyExposedElements = self::retrieveLegacyExposedElements($config);
70
71
        [
72
            $legacyExposedSymbols,
73
            $legacyExposedSymbolsPatterns,
74
            $legacyExposedConstants,
75
            $excludedNamespaceNames,
76
        ] = self::parseLegacyExposedElements($legacyExposedElements, $excludedNamespaceNames);
0 ignored issues
show
Deprecated Code introduced by
The function Humbug\PhpScoper\Configu...LegacyExposedElements() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

76
        ] = /** @scrutinizer ignore-deprecated */ self::parseLegacyExposedElements($legacyExposedElements, $excludedNamespaceNames);
Loading history...
77
78
        $exposeGlobalConstants = self::retrieveExposeGlobalSymbol(
79
            $config,
80
            ConfigurationKeys::EXPOSE_GLOBAL_CONSTANTS_KEYWORD,
81
        );
82
        $exposeGlobalClasses = self::retrieveExposeGlobalSymbol(
83
            $config,
84
            ConfigurationKeys::EXPOSE_GLOBAL_CLASSES_KEYWORD,
85
        );
86
        $exposeGlobalFunctions = self::retrieveExposeGlobalSymbol(
87
            $config,
88
            ConfigurationKeys::EXPOSE_GLOBAL_FUNCTIONS_KEYWORD,
89
        );
90
91
        [$exposedClassNames, $exposedClassRegexes] = $this->retrieveElements(
92
            $config,
93
            ConfigurationKeys::EXPOSE_CLASSES_SYMBOLS_KEYWORD,
94
        );
95
96
        [$exposedFunctionNames, $exposedFunctionRegexes] = $this->retrieveElements(
97
            $config,
98
            ConfigurationKeys::EXPOSE_FUNCTIONS_SYMBOLS_KEYWORD,
99
        );
100
101
        [$exposedConstantNames, $exposedConstantRegexes] = $this->retrieveElements(
102
            $config,
103
            ConfigurationKeys::EXPOSE_CONSTANTS_SYMBOLS_KEYWORD,
104
        );
105
106
        $excludedClasses = SymbolRegistry::create(
107
            ...$this->retrieveElements(
0 ignored issues
show
Bug introduced by
$this->retrieveElements(...TERNAL_SYMBOLS_KEYWORD) is expanded, but the parameter $names of Humbug\PhpScoper\Symbol\SymbolRegistry::create() does not expect variable arguments. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

107
            /** @scrutinizer ignore-type */ ...$this->retrieveElements(
Loading history...
108
                $config,
109
                ConfigurationKeys::CLASSES_INTERNAL_SYMBOLS_KEYWORD,
110
            ),
111
        );
112
113
        $excludedFunctions = SymbolRegistry::create(
114
            ...$this->retrieveElements(
115
                $config,
116
                ConfigurationKeys::FUNCTIONS_INTERNAL_SYMBOLS_KEYWORD,
117
            ),
118
        );
119
120
        $excludedConstants = SymbolRegistry::createForConstants(
121
            ...$this->retrieveElements(
0 ignored issues
show
Bug introduced by
$this->retrieveElements(...TERNAL_SYMBOLS_KEYWORD) is expanded, but the parameter $names of Humbug\PhpScoper\Symbol\...y::createForConstants() does not expect variable arguments. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

121
            /** @scrutinizer ignore-type */ ...$this->retrieveElements(
Loading history...
122
                $config,
123
                ConfigurationKeys::CONSTANTS_INTERNAL_SYMBOLS_KEYWORD,
124
            ),
125
        );
126
127
        return SymbolsConfiguration::create(
128
            $exposeGlobalConstants,
129
            $exposeGlobalClasses,
130
            $exposeGlobalFunctions,
131
            NamespaceRegistry::create(
132
                $excludedNamespaceNames,
133
                $excludedNamespaceRegexes,
134
            ),
135
            NamespaceRegistry::create(
136
                $exposedNamespaceNames,
137
                $exposedNamespaceRegexes,
138
            ),
139
            SymbolRegistry::create(
140
                array_merge(
141
                    $exposedClassNames,
142
                    $legacyExposedSymbols,
143
                ),
144
                array_merge(
145
                    $exposedClassRegexes,
146
                    $legacyExposedSymbolsPatterns,
147
                ),
148
            ),
149
            SymbolRegistry::create(
150
                array_merge(
151
                    $exposedFunctionNames,
152
                    $legacyExposedSymbols,
153
                ),
154
                array_merge(
155
                    $exposedFunctionRegexes,
156
                    $legacyExposedSymbolsPatterns,
157
                ),
158
            ),
159
            SymbolRegistry::createForConstants(
160
                array_merge(
161
                    $exposedConstantNames,
162
                    $legacyExposedConstants,
163
                ),
164
                array_merge(
165
                    $exposedConstantRegexes,
166
                    $legacyExposedSymbolsPatterns,
167
                ),
168
            ),
169
            $excludedClasses,
170
            $excludedFunctions,
171
            $excludedConstants,
172
        );
173
    }
174
175
    private static function retrieveExposeGlobalSymbol(array $config, string $key): bool
176
    {
177
        if (!array_key_exists($key, $config)) {
178
            return false;
179
        }
180
181
        $value = $config[$key];
182
183
        if (!is_bool($value)) {
184
            throw new InvalidArgumentException(
185
                sprintf(
0 ignored issues
show
Deprecated Code introduced by
The function Safe\sprintf() has been deprecated: The Safe version of this function is no longer needed in PHP 8.0+ ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

185
                /** @scrutinizer ignore-deprecated */ sprintf(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
186
                    'Expected %s to be a boolean, found "%s" instead.',
187
                    $key,
188
                    gettype($value),
189
                ),
190
            );
191
        }
192
193
        return $value;
194
    }
195
196
    /**
197
     * @return list<string>
0 ignored issues
show
Bug introduced by
The type Humbug\PhpScoper\Configuration\list was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
198
     */
199
    private static function retrieveLegacyExposedElements(array $config): array
200
    {
201
        $key = ConfigurationKeys::WHITELIST_KEYWORD;
202
203
        if (!array_key_exists($key, $config)) {
204
            return [];
0 ignored issues
show
Bug Best Practice introduced by
The expression return array() returns the type array which is incompatible with the documented return type Humbug\PhpScoper\Configuration\list.
Loading history...
205
        }
206
207
        $whitelist = $config[$key];
208
209
        if (!is_array($whitelist)) {
210
            throw new InvalidArgumentException(
211
                sprintf(
0 ignored issues
show
Deprecated Code introduced by
The function Safe\sprintf() has been deprecated: The Safe version of this function is no longer needed in PHP 8.0+ ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

211
                /** @scrutinizer ignore-deprecated */ sprintf(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
212
                    'Expected "%s" to be an array of strings, found "%s" instead.',
213
                    $key,
214
                    gettype($whitelist),
215
                ),
216
            );
217
        }
218
219
        foreach ($whitelist as $index => $className) {
220
            if (is_string($className)) {
221
                continue;
222
            }
223
224
            throw new InvalidArgumentException(
225
                sprintf(
0 ignored issues
show
Deprecated Code introduced by
The function Safe\sprintf() has been deprecated: The Safe version of this function is no longer needed in PHP 8.0+ ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

225
                /** @scrutinizer ignore-deprecated */ sprintf(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
226
                    'Expected whitelist to be an array of string, the "%d" element is not.',
227
                    $index,
228
                ),
229
            );
230
        }
231
232
        return array_values($whitelist);
0 ignored issues
show
Bug Best Practice introduced by
The expression return array_values($whitelist) returns the type array which is incompatible with the documented return type Humbug\PhpScoper\Configuration\list.
Loading history...
233
    }
234
235
    /**
236
     * @return array{list<string>, list<string>}
0 ignored issues
show
Documentation Bug introduced by
The doc comment array{list<string>, list<string>} at position 2 could not be parsed: Expected ':' at position 2, but found 'list'.
Loading history...
237
     */
238
    private function retrieveElements(array $config, string $key): array
239
    {
240
        if (!array_key_exists($key, $config)) {
241
            return [[], []];
242
        }
243
244
        $symbolNamesAndRegexes = $config[$key];
245
246
        self::assertIsArrayOfStrings($config[$key], $key);
247
248
        // Store the strings in the keys for avoiding a unique check later on
249
        $names = [];
250
        $regexes = [];
251
252
        foreach ($symbolNamesAndRegexes as $index => $nameOrRegex) {
253
            if (!$this->regexChecker->isRegexLike($nameOrRegex)) {
254
                $names[$nameOrRegex] = null;
255
256
                continue;
257
            }
258
259
            $regex = $nameOrRegex;
260
261
            $this->assertValidRegex($regex, $key, (string) $index);
262
263
            $errorMessage = $this->regexChecker->validateRegex($regex);
264
265
            if (null !== $errorMessage) {
266
                throw new InvalidArgumentException(
267
                    sprintf(
0 ignored issues
show
Deprecated Code introduced by
The function Safe\sprintf() has been deprecated: The Safe version of this function is no longer needed in PHP 8.0+ ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

267
                    /** @scrutinizer ignore-deprecated */ sprintf(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
268
                        'Expected "%s" to be an array of valid regexes. The element "%s" with the index "%s" is not: %s.',
269
                        $key,
270
                        $regex,
271
                        $index,
272
                        $errorMessage,
273
                    ),
274
                );
275
            }
276
277
            // Ensure namespace comparisons are always case-insensitive
278
            // TODO: double check that we are not adding it twice or that adding it twice does not break anything
279
            $regex .= 'i';
280
            $regexes[$regex] = null;
281
        }
282
283
        return [
284
            array_keys($names),
285
            array_keys($regexes),
286
        ];
287
    }
288
289
    /**
290
     * @deprecated
291
     *
292
     * @param list<string> $elements
293
     * @param list<string> $excludedNamespaceNames
294
     */
295
    private static function parseLegacyExposedElements(array $elements, array $excludedNamespaceNames): array
296
    {
297
        $exposedSymbols = [];
298
        $exposedConstants = [];
299
        $exposedSymbolsPatterns = [];
300
        $excludedNamespaceNames = array_map('strtolower', $excludedNamespaceNames);
301
302
        foreach ($elements as $element) {
303
            $element = ltrim(trim($element), '\\');
304
305
            self::assertValidElement($element);
0 ignored issues
show
Deprecated Code introduced by
The function Humbug\PhpScoper\Configu...y::assertValidElement() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

305
            /** @scrutinizer ignore-deprecated */ self::assertValidElement($element);
Loading history...
306
307
            if ('\*' === substr($element, -2)) {
0 ignored issues
show
Deprecated Code introduced by
The function Safe\substr() has been deprecated: The Safe version of this function is no longer needed in PHP 8.0+ ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

307
            if ('\*' === /** @scrutinizer ignore-deprecated */ substr($element, -2)) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
308
                $excludedNamespaceNames[] = strtolower(substr($element, 0, -2));
0 ignored issues
show
Deprecated Code introduced by
The function Safe\substr() has been deprecated: The Safe version of this function is no longer needed in PHP 8.0+ ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

308
                $excludedNamespaceNames[] = strtolower(/** @scrutinizer ignore-deprecated */ substr($element, 0, -2));

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
309
            } elseif ('*' === $element) {
310
                $excludedNamespaceNames[] = '';
311
            } elseif (false !== strpos($element, '*')) {
312
                $exposedSymbolsPatterns[] = self::createExposePattern($element);
0 ignored issues
show
Deprecated Code introduced by
The function Humbug\PhpScoper\Configu...::createExposePattern() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

312
                $exposedSymbolsPatterns[] = /** @scrutinizer ignore-deprecated */ self::createExposePattern($element);
Loading history...
313
            } else {
314
                $exposedSymbols[] = strtolower($element);
315
                $exposedConstants[] = self::lowerCaseConstantName($element);
0 ignored issues
show
Deprecated Code introduced by
The function Humbug\PhpScoper\Configu...lowerCaseConstantName() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

315
                $exposedConstants[] = /** @scrutinizer ignore-deprecated */ self::lowerCaseConstantName($element);
Loading history...
316
            }
317
        }
318
319
        return [
320
            $exposedSymbols,
321
            $exposedSymbolsPatterns,
322
            $exposedConstants,
323
            $excludedNamespaceNames,
324
        ];
325
    }
326
327
    /**
328
     * @psalm-assert string[] $value
329
     *
330
     * @param mixed $value
331
     */
332
    private static function assertIsArrayOfStrings($value, string $key): void
333
    {
334
        if (!is_array($value)) {
335
            throw new InvalidArgumentException(
336
                sprintf(
0 ignored issues
show
Deprecated Code introduced by
The function Safe\sprintf() has been deprecated: The Safe version of this function is no longer needed in PHP 8.0+ ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

336
                /** @scrutinizer ignore-deprecated */ sprintf(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
337
                    'Expected "%s" to be an array of strings, found "%s" instead.',
338
                    $key,
339
                    get_debug_type($value),
340
                ),
341
            );
342
        }
343
344
        foreach ($value as $index => $element) {
345
            if (is_string($element)) {
346
                continue;
347
            }
348
349
            throw new InvalidArgumentException(
350
                sprintf(
0 ignored issues
show
Deprecated Code introduced by
The function Safe\sprintf() has been deprecated: The Safe version of this function is no longer needed in PHP 8.0+ ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

350
                /** @scrutinizer ignore-deprecated */ sprintf(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
351
                    'Expected "%s" to be an array of strings, found "%s" for the element with the index "%s".',
352
                    $key,
353
                    get_debug_type($element),
354
                    $index,
355
                ),
356
            );
357
        }
358
    }
359
360
    private function assertValidRegex(string $regex, string $key, string $index): void
361
    {
362
        $errorMessage = $this->regexChecker->validateRegex($regex);
363
364
        if (null !== $errorMessage) {
365
            throw new InvalidArgumentException(
366
                sprintf(
0 ignored issues
show
Deprecated Code introduced by
The function Safe\sprintf() has been deprecated: The Safe version of this function is no longer needed in PHP 8.0+ ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

366
                /** @scrutinizer ignore-deprecated */ sprintf(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
367
                    'Expected "%s" to be an array of valid regexes. The element "%s" with the index "%s" is not: %s.',
368
                    $key,
369
                    $regex,
370
                    $index,
371
                    $errorMessage,
372
                ),
373
            );
374
        }
375
    }
376
377
    /**
378
     * @deprecated
379
     */
380
    private static function assertValidElement(string $element): void
381
    {
382
        if ('' !== $element) {
383
            return;
384
        }
385
386
        throw new InvalidArgumentException(
387
            sprintf(
0 ignored issues
show
Deprecated Code introduced by
The function Safe\sprintf() has been deprecated: The Safe version of this function is no longer needed in PHP 8.0+ ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

387
            /** @scrutinizer ignore-deprecated */ sprintf(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
388
                'Invalid whitelist element "%s": cannot accept an empty string',
389
                $element,
390
            ),
391
        );
392
    }
393
394
    /**
395
     * @deprecated
396
     */
397
    private static function createExposePattern(string $element): string
398
    {
399
        self::assertValidPattern($element);
0 ignored issues
show
Deprecated Code introduced by
The function Humbug\PhpScoper\Configu...y::assertValidPattern() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

399
        /** @scrutinizer ignore-deprecated */ self::assertValidPattern($element);
Loading history...
400
401
        return sprintf(
0 ignored issues
show
Deprecated Code introduced by
The function Safe\sprintf() has been deprecated: The Safe version of this function is no longer needed in PHP 8.0+ ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

401
        return /** @scrutinizer ignore-deprecated */ sprintf(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
402
            '/^%s$/u',
403
            str_replace(
404
                '\\',
405
                '\\\\',
406
                str_replace(
407
                    '*',
408
                    '.*',
409
                    $element,
410
                ),
411
            ),
412
        );
413
    }
414
415
    /**
416
     * @deprecated
417
     */
418
    private static function assertValidPattern(string $element): void
419
    {
420
        if (1 !== native_preg_match('/^(([\p{L}_]+\\\\)+)?[\p{L}_]*\*$/u', $element)) {
421
            throw new InvalidArgumentException(
422
                sprintf(
0 ignored issues
show
Deprecated Code introduced by
The function Safe\sprintf() has been deprecated: The Safe version of this function is no longer needed in PHP 8.0+ ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

422
                /** @scrutinizer ignore-deprecated */ sprintf(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
423
                    'Invalid whitelist pattern "%s".',
424
                    $element,
425
                ),
426
            );
427
        }
428
    }
429
430
    /**
431
     * @deprecated
432
     */
433
    private static function lowerCaseConstantName(string $name): string
434
    {
435
        $parts = explode('\\', $name);
436
437
        $lastPart = array_pop($parts);
438
439
        $parts = array_map('strtolower', $parts);
440
441
        $parts[] = $lastPart;
442
443
        return implode('\\', $parts);
444
    }
445
}
446