Passed
Pull Request — master (#587)
by Théo
02:07
created

createSymbolsConfiguration()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 30
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 21
nc 1
nop 1
dl 0
loc 30
rs 9.584
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Humbug\PhpScoper\Configuration;
6
7
use Humbug\PhpScoper\RegexChecker;
8
use Humbug\PhpScoper\Symbol\NamespaceRegistry;
9
use Humbug\PhpScoper\Whitelist;
10
use InvalidArgumentException;
11
use function array_key_exists;
12
use function array_keys;
13
use function array_values;
14
use function gettype;
15
use function is_array;
16
use function is_bool;
17
use function is_string;
18
use function Safe\sprintf;
19
20
final class ConfigurationWhitelistFactory
21
{
22
    private RegexChecker $regexChecker;
23
24
    public function __construct(RegexChecker $regexChecker)
25
    {
26
        $this->regexChecker = $regexChecker;
27
    }
28
29
    public function createSymbolsConfiguration(array $config): SymbolsConfiguration
30
    {
31
        [
32
            $excludedNamespaceRegexes,
33
            $excludedNamespaceNames,
34
        ] = $this->retrieveExcludedNamespaces($config);
35
36
        $exposedElements = self::retrieveExposedElements($config);
37
38
        $exposeGlobalConstants = self::retrieveExposeGlobalSymbol(
39
            $config,
40
            ConfigurationKeys::EXPOSE_GLOBAL_CONSTANTS_KEYWORD,
41
        );
42
        $exposeGlobalClasses = self::retrieveExposeGlobalSymbol(
43
            $config,
44
            ConfigurationKeys::EXPOSE_GLOBAL_CLASSES_KEYWORD,
45
        );
46
        $exposeGlobalFunctions = self::retrieveExposeGlobalSymbol(
47
            $config,
48
            ConfigurationKeys::EXPOSE_GLOBAL_FUNCTIONS_KEYWORD,
49
        );
50
51
        return SymbolsConfiguration::fromWhitelist(
52
            Whitelist::create(
53
                $exposeGlobalConstants,
54
                $exposeGlobalClasses,
55
                $exposeGlobalFunctions,
56
                $excludedNamespaceRegexes,
57
                $excludedNamespaceNames,
58
                ...$exposedElements,
59
            ),
60
        );
61
    }
62
63
    /**
64
     * @return array{string[], string[]}
0 ignored issues
show
Documentation Bug introduced by
The doc comment array{string[], string[]} at position 2 could not be parsed: Expected ':' at position 2, but found 'string'.
Loading history...
65
     */
66
    private function retrieveExcludedNamespaces(array $config): array
67
    {
68
        $key = ConfigurationKeys::EXCLUDE_NAMESPACES_KEYWORD;
69
70
        if (!array_key_exists($key, $config)) {
71
            return [[], []];
72
        }
73
74
        $regexesAndNamespaceNames = $config[$key];
75
76
        if (!is_array($regexesAndNamespaceNames)) {
77
            throw new InvalidArgumentException(
78
                sprintf(
79
                    'Expected "%s" to be an array of strings, got "%s" instead.',
80
                    $key,
81
                    gettype($regexesAndNamespaceNames),
82
                ),
83
            );
84
        }
85
86
        // Store the strings in the keys for avoiding a unique check later on
87
        $regexes = [];
88
        $namespaceNames = [];
89
90
        foreach ($regexesAndNamespaceNames as $index => $regexOrNamespaceName) {
91
            if (!is_string($regexOrNamespaceName)) {
92
                throw new InvalidArgumentException(
93
                    sprintf(
94
                        'Expected "%s" to be an array of strings, got "%s" for the element with the index "%s".',
95
                        $key,
96
                        gettype($regexOrNamespaceName),
97
                        $index,
98
                    ),
99
                );
100
            }
101
102
            if (!$this->regexChecker->isRegexLike($regexOrNamespaceName)) {
103
                $namespaceNames[$regexOrNamespaceName] = null;
104
105
                continue;
106
            }
107
108
            $excludeNamespaceRegex = $regexOrNamespaceName;
109
110
            $errorMessage = $this->regexChecker->validateRegex($excludeNamespaceRegex);
111
112
            if (null !== $errorMessage) {
113
                throw new InvalidArgumentException(
114
                    sprintf(
115
                        'Expected "%s" to be an array of valid regexes. The element "%s" with the index "%s" is not: %s.',
116
                        $key,
117
                        $excludeNamespaceRegex,
118
                        $index,
119
                        $errorMessage,
120
                    ),
121
                );
122
            }
123
124
            // Ensure namespace comparisons are always case-insensitive
125
            // TODO: double check that we are not adding it twice or that adding it twice does not break anything
126
            $excludeNamespaceRegex .= 'i';
127
            $regexes[$excludeNamespaceRegex] = null;
128
        }
129
130
        return [
131
            array_keys($regexes),
132
            array_keys($namespaceNames),
133
        ];
134
    }
135
136
    /**
137
     * return list<string>
138
     */
139
    private static function retrieveExposedElements(array $config): array
140
    {
141
        $key = ConfigurationKeys::WHITELIST_KEYWORD;
142
143
        if (!array_key_exists($key, $config)) {
144
            return [];
145
        }
146
147
        $whitelist = $config[$key];
148
149
        if (!is_array($whitelist)) {
150
            throw new InvalidArgumentException(
151
                sprintf(
152
                    'Expected "%s" to be an array of strings, found "%s" instead.',
153
                    $key,
154
                    gettype($whitelist),
155
                ),
156
            );
157
        }
158
159
        foreach ($whitelist as $index => $className) {
160
            if (is_string($className)) {
161
                continue;
162
            }
163
164
            throw new InvalidArgumentException(
165
                sprintf(
166
                    'Expected whitelist to be an array of string, the "%d" element is not.',
167
                    $index,
168
                ),
169
            );
170
        }
171
172
        return array_values($whitelist);
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(
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