Test Setup Failed
Push — master ( c4a7db...8049ea )
by Théo
02:17
created

Whitelist::whitelistGlobalConstants()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 4
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;
16
17
use Countable;
18
use Humbug\PhpScoper\PhpParser\NodeVisitor\Collection\UserGlobalFunctionCollection;
19
use InvalidArgumentException;
20
use PhpParser\Node\Name\FullyQualified;
21
use function array_filter;
22
use function array_map;
23
use function array_pop;
24
use function array_unique;
25
use function count;
26
use function explode;
27
use function implode;
28
use function in_array;
29
use function sprintf;
30
use function strtolower;
31
use function substr;
32
use function trim;
33
34
final class Whitelist implements Countable
35
{
36
    private $original;
37
    private $classes;
38
    private $constants;
39
    private $namespaces;
40
    private $whitelistGlobalConstants;
41
    private $whitelistGlobalFunctions;
42
    private $userGlobalFunctions;
43
44
    public static function create(bool $whitelistGlobalConstants, bool $whitelistGlobalFunctions, string ...$elements): self
45
    {
46
        $classes = [];
47
        $constants = [];
48
        $namespaces = [];
49
        $original = [];
50
51
        foreach ($elements as $element) {
52
            if (isset($element[0]) && '\\' === $element[0]) {
53
                $element = substr($element, 1);
54
            }
55
56
            if ('' === trim($element)) {
57
                throw new InvalidArgumentException(
58
                    sprintf(
59
                        'Invalid whitelist element "%s": cannot accept an empty string',
60
                        $element
61
                    )
62
                );
63
            }
64
65
            $original[] = $element;
66
67
            if ('\*' === substr($element, -2)) {
68
                $namespaces[] = strtolower(substr($element, 0, -2));
69
            } elseif ('*' === $element) {
70
                $namespaces[] = '';
71
            } else {
72
                $classes[] = strtolower($element);
73
                $constants[] = self::lowerConstantName($element);
74
            }
75
        }
76
77
        return new self(
78
            $whitelistGlobalConstants,
79
            $whitelistGlobalFunctions,
80
            array_unique($original),
81
            array_unique($classes),
82
            array_unique($constants),
83
            array_unique($namespaces)
84
        );
85
    }
86
87
    /**
88
     * @param string[] $original
89
     * @param string[] $classes
90
     * @param string[] $constants
91
     * @param string[] $namespaces
92
     */
93
    private function __construct(
94
        bool $whitelistGlobalConstants,
95
        bool $whitelistGlobalFunctions,
96
        array $original,
97
        array $classes,
98
        array $constants,
99
        array $namespaces
100
    ) {
101
        $this->whitelistGlobalConstants = $whitelistGlobalConstants;
102
        $this->whitelistGlobalFunctions = $whitelistGlobalFunctions;
103
        $this->original = $original;
104
        $this->classes = $classes;
105
        $this->constants = $constants;
106
        $this->namespaces = $namespaces;
107
        $this->userGlobalFunctions = new UserGlobalFunctionCollection();
108
    }
109
110
    public function recordUserGlobalFunction(FullyQualified $original, FullyQualified $alias): void
111
    {
112
        $this->userGlobalFunctions->add($original, $alias);
113
    }
114
115
    public function getUserGlobalFunctions(): UserGlobalFunctionCollection
116
    {
117
        return $this->userGlobalFunctions;
118
    }
119
120
    public function whitelistGlobalConstants(): bool
121
    {
122
        return $this->whitelistGlobalConstants;
123
    }
124
125
    public function whitelistGlobalFunctions(): bool
126
    {
127
        // TODO: check that nothing is appended/collected if everything is being whitelisted; avoid the collection in this case to avoid performance issues
128
        return $this->whitelistGlobalFunctions;
129
    }
130
131
    public function isClassWhitelisted(string $name): bool
132
    {
133
        return in_array(strtolower($name), $this->classes, true);
134
    }
135
136
    public function isConstantWhitelisted(string $name): bool
137
    {
138
        return in_array(self::lowerConstantName($name), $this->constants, true);
139
    }
140
141
    /**
142
     * @return string[]
143
     */
144
    public function getClassWhitelistArray(): array
145
    {
146
        return array_filter(
147
            $this->original,
148
            function (string $name): bool {
149
                return '*' !== $name && '\*' !== substr($name, -2);
150
            }
151
        );
152
    }
153
154
    public function isNamespaceWhitelisted(string $name): bool
155
    {
156
        $name = strtolower($name);
157
158
        foreach ($this->namespaces as $namespace) {
159
            if ('' === $namespace || 0 === strpos($name, $namespace)) {
160
                return true;
161
            }
162
        }
163
164
        return false;
165
    }
166
167
    /**
168
     * {@inheritdoc}
169
     */
170
    public function count(): int
171
    {
172
        return count($this->classes) + count($this->namespaces);
173
    }
174
175
    public function toArray(): array
176
    {
177
        return $this->original;
178
    }
179
180
    private static function lowerConstantName(string $name): string
181
    {
182
        $parts = explode('\\', $name);
183
184
        $lastPart = array_pop($parts);
185
186
        $parts = array_map('strtolower', $parts);
187
188
        $parts[] = $lastPart;
189
190
        return implode('\\', $parts);
191
    }
192
}
193