Test Setup Failed
Push — master ( bdd6cd...7f37fa )
by Théo
04:30
created

Whitelist::lowerConstantName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 12
rs 9.8666
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 Humbug\PhpScoper\PhpParser\NodeVisitor\Collection\WhitelistedFunctionCollection;
18
use InvalidArgumentException;
19
use PhpParser\Node\Name\FullyQualified;
20
use function array_filter;
21
use function array_flip;
22
use function array_key_exists;
23
use function array_map;
24
use function array_pop;
25
use function array_unique;
26
use function count;
27
use function explode;
28
use function implode;
29
use function preg_match;
30
use function sprintf;
31
use function str_replace;
32
use function strpos;
33
use function strtolower;
34
use function substr;
35
use function trim;
36
37
final class Whitelist
38
{
39
    private $original;
40
    private $classes;
41
    private $constants;
42
    private $namespaces;
43
    private $patterns;
44
    private $whitelistGlobalConstants;
45
    private $whitelistGlobalFunctions;
46
    private $whitelistedFunctions;
47
48
    public static function create(bool $whitelistGlobalConstants, bool $whitelistGlobalFunctions, string ...$elements): self
49
    {
50
        $classes = [];
51
        $constants = [];
52
        $namespaces = [];
53
        $patterns = [];
54
        $original = [];
55
56
        foreach ($elements as $element) {
57
            if (isset($element[0]) && '\\' === $element[0]) {
58
                $element = substr($element, 1);
59
            }
60
61
            if ('' === trim($element)) {
62
                throw new InvalidArgumentException(
63
                    sprintf(
64
                        'Invalid whitelist element "%s": cannot accept an empty string',
65
                        $element
66
                    )
67
                );
68
            }
69
70
            $original[] = $element;
71
72
            if ('\*' === substr($element, -2)) {
73
                $namespaces[] = strtolower(substr($element, 0, -2));
74
            } elseif ('*' === $element) {
75
                $namespaces[] = '';
76
            } elseif (false !== strpos($element, '*')) {
77
                self::assertValidPattern($element);
78
79
                $patterns[] = sprintf(
80
                    '/^%s$/ui',
81
                    strtolower(
82
                        str_replace(
83
                            '\\',
84
                            '\\\\',
85
                            str_replace(
86
                                '*',
87
                                '.*',
88
                                $element
89
                            )
90
                        )
91
                    )
92
                );
93
            } else {
94
                $classes[] = strtolower($element);
95
                $constants[] = self::lowerConstantName($element);
96
            }
97
        }
98
99
        return new self(
100
            $whitelistGlobalConstants,
101
            $whitelistGlobalFunctions,
102
            array_unique($original),
103
            array_flip($classes),
104
            array_flip($constants),
105
            array_unique($patterns),
106
            array_unique($namespaces)
107
        );
108
    }
109
110
    private static function assertValidPattern(string $element): void
111
    {
112
        if (1 !== preg_match('/^(([\p{L}_]+\\\\)+)?[\p{L}_]*\*$/u', $element)) {
113
            throw new \InvalidArgumentException(
114
                sprintf(
115
                    'Invalid whitelist pattern "%s".',
116
                    $element
117
                )
118
            );
119
        }
120
    }
121
122
    /**
123
     * @param string[] $original
124
     * @param string[] $patterns
125
     * @param string[] $namespaces
126
     */
127
    private function __construct(
128
        bool $whitelistGlobalConstants,
129
        bool $whitelistGlobalFunctions,
130
        array $original,
131
        array $classes,
132
        array $constants,
133
        array $patterns,
134
        array $namespaces
135
    ) {
136
        $this->whitelistGlobalConstants = $whitelistGlobalConstants;
137
        $this->whitelistGlobalFunctions = $whitelistGlobalFunctions;
138
        $this->original = $original;
139
        $this->classes = $classes;
140
        $this->constants = $constants;
141
        $this->namespaces = $namespaces;
142
        $this->patterns = $patterns;
143
        $this->whitelistedFunctions = new WhitelistedFunctionCollection();
144
    }
145
146
    public function recordWhitelistedFunction(FullyQualified $original, FullyQualified $alias): void
147
    {
148
        $this->whitelistedFunctions->add($original, $alias);
149
    }
150
151
    public function getWhitelistedFunctions(): WhitelistedFunctionCollection
152
    {
153
        return $this->whitelistedFunctions;
154
    }
155
156
    public function whitelistGlobalConstants(): bool
157
    {
158
        return $this->whitelistGlobalConstants;
159
    }
160
161
    public function whitelistGlobalFunctions(): bool
162
    {
163
        return $this->whitelistGlobalFunctions;
164
    }
165
166 View Code Duplication
    public function isClassWhitelisted(string $name): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
167
    {
168
        if (array_key_exists(strtolower($name), $this->classes)) {
169
            return true;
170
        }
171
172
        foreach ($this->patterns as $pattern) {
173
            if (1 === preg_match($pattern, $name)) {
174
                return true;
175
            }
176
        }
177
178
        return false;
179
    }
180
181 View Code Duplication
    public function isConstantWhitelisted(string $name): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
182
    {
183
        if (array_key_exists(self::lowerConstantName($name), $this->constants)) {
184
            return true;
185
        }
186
187
        foreach ($this->patterns as $pattern) {
188
            if (1 === preg_match($pattern, $name)) {
189
                return true;
190
            }
191
        }
192
193
        return false;
194
    }
195
196
    /**
197
     * @return string[]
198
     */
199
    public function getClassWhitelistArray(): array
200
    {
201
        return array_filter(
202
            $this->original,
203
            function (string $name): bool {
204
                return '*' !== $name && '\*' !== substr($name, -2);
205
            }
206
        );
207
    }
208
209
    public function isNamespaceWhitelisted(string $name): bool
210
    {
211
        $name = strtolower($name);
212
213
        foreach ($this->namespaces as $namespace) {
214
            if ('' === $namespace || 0 === strpos($name, $namespace)) {
215
                return true;
216
            }
217
        }
218
219
        return false;
220
    }
221
222
    public function toArray(): array
223
    {
224
        return $this->original;
225
    }
226
227
    public function hasWhitelistStatements(): bool
228
    {
229
        return count($this->classes) + count($this->whitelistedFunctions) + count($this->patterns) > 0;
230
    }
231
232
    private static function lowerConstantName(string $name): string
233
    {
234
        $parts = explode('\\', $name);
235
236
        $lastPart = array_pop($parts);
237
238
        $parts = array_map('strtolower', $parts);
239
240
        $parts[] = $lastPart;
241
242
        return implode('\\', $parts);
243
    }
244
}
245