Completed
Push — master ( ce5d9a...b8256c )
by Jaap
08:59
created

Glob::toRegEx()   F

Complexity

Conditions 41
Paths 114

Size

Total Lines 105

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 105
rs 3.24
c 0
b 0
f 0
cc 41
nc 114
nop 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * This file is part of phpDocumentor.
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 *
11
 * Many thanks to webmozart by providing the original code in webmozart/glob
12
 *
13
 * @link https://github.com/webmozart/glob/blob/master/src/Glob.php
14
 * @link      http://phpdoc.org
15
 */
16
17
namespace Flyfinder\Specification;
18
19
use InvalidArgumentException;
20
use function preg_match;
21
use function sprintf;
22
use function strlen;
23
use function strpos;
24
25
/**
26
 * Glob specification class
27
 *
28
 * @psalm-immutable
29
 */
30
final class Glob extends CompositeSpecification
31
{
32
    /** @var string */
33
    private $regex;
34
35
    /**
36
     * The "static prefix" is the part of the glob up to the first wildcard "*".
37
     * If the glob does not contain wildcards, the full glob is returned.
38
     *
39
     * @var string
40
     */
41
    private $staticPrefix;
42
43
    public function __construct(string $glob)
44
    {
45
        $this->regex        = self::toRegEx($glob);
46
        $this->staticPrefix = self::getStaticPrefix($glob);
47
    }
48
49
    /**
50
     * @inheritDoc
51
     */
52
    public function isSatisfiedBy(array $value) : bool
53
    {
54
        //Flysystem paths are not absolute, so make it that way.
55
        $path = '/' . $value['path'];
56
        if (strpos($path, $this->staticPrefix) !== 0) {
57
            return false;
58
        }
59
60
        if (preg_match($this->regex, $path)) {
61
            return true;
62
        }
63
64
        return false;
65
    }
66
67
    /**
68
     * Returns the static prefix of a glob.
69
     *
70
     * The "static prefix" is the part of the glob up to the first wildcard "*".
71
     * If the glob does not contain wildcards, the full glob is returned.
72
     *
73
     * @param string $glob The canonical glob. The glob should contain forward
74
     *                      slashes as directory separators only. It must not
75
     *                      contain any "." or ".." segments.
76
     *
77
     * @return string The static prefix of the glob.
78
     *
79
     * @psalm-pure
80
     */
81
    private static function getStaticPrefix(string $glob) : string
82
    {
83
        if (strpos($glob, '/') !== 0 && strpos($glob, '://') === false) {
84
            throw new InvalidArgumentException(sprintf(
85
                'The glob "%s" is not absolute and not a URI.',
86
                $glob
87
            ));
88
        }
89
        $prefix = '';
90
        $length = strlen($glob);
91
        for ($i = 0; $i < $length; ++$i) {
92
            $c = $glob[$i];
93
            switch ($c) {
94
                case '/':
95
                    $prefix .= '/';
96
                    if (self::isRecursiveWildcard($glob, $i)) {
97
                        break 2;
98
                    }
99
                    break;
100
                case '*':
101
                case '?':
102
                case '{':
103
                case '[':
104
                    break 2;
105
                case '\\':
106
                    if (isset($glob[$i + 1])) {
107
                        switch ($glob[$i + 1]) {
108
                            case '*':
109
                            case '?':
110
                            case '{':
111
                            case '}':
112
                            case '[':
113
                            case ']':
114
                            case '-':
115
                            case '^':
116
                            case '$':
117
                            case '~':
118
                            case '\\':
119
                                $prefix .= $glob[$i + 1];
120
                                ++$i;
121
                                break;
122
                            default:
123
                                $prefix .= '\\';
124
                        }
125
                    }
126
                    break;
127
                default:
128
                    $prefix .= $c;
129
                    break;
130
            }
131
        }
132
        return $prefix;
133
    }
134
135
    /**
136
     * Checks if the current position the glob is start of a Recursive directory wildcard
137
     *
138
     * @psalm-pure
139
     */
140
    private static function isRecursiveWildcard(string $glob, int $i) : bool
141
    {
142
        return isset($glob[$i + 3]) && $glob[$i + 1] . $glob[$i + 2] . $glob[$i + 3] === '**/';
143
    }
144
145
    /**
146
     * Converts a glob to a regular expression.
147
     *
148
     * @param string $glob The canonical glob. The glob should contain forward
149
     *                      slashes as directory separators only. It must not
150
     *                      contain any "." or ".." segments.
151
     *
152
     * @return string The regular expression for matching the glob.
153
     *
154
     * @psalm-pure
155
     */
156
    private static function toRegEx(string $glob) : string
157
    {
158
        $delimiter   = '~';
159
        $inSquare    = false;
160
        $curlyLevels = 0;
161
        $regex       = '';
162
        $length      = strlen($glob);
163
        for ($i = 0; $i < $length; ++$i) {
164
            $c = $glob[$i];
165
            switch ($c) {
166
                case '.':
167
                case '(':
168
                case ')':
169
                case '|':
170
                case '+':
171
                case '^':
172
                case '$':
173
                case $delimiter:
174
                    $regex .= '\\' . $c;
175
                    break;
176
                case '/':
177
                    if (self::isRecursiveWildcard($glob, $i)) {
178
                        $regex .= '/([^/]+/)*';
179
                        $i     += 3;
180
                    } else {
181
                        $regex .= '/';
182
                    }
183
                    break;
184
                case '*':
185
                    $regex .= '[^/]*';
186
                    break;
187
                case '?':
188
                    $regex .= '.';
189
                    break;
190
                case '{':
191
                    $regex .= '(';
192
                    ++$curlyLevels;
193
                    break;
194
                case '}':
195
                    if ($curlyLevels > 0) {
196
                        $regex .= ')';
197
                        --$curlyLevels;
198
                    } else {
199
                        $regex .= '}';
200
                    }
201
                    break;
202
                case ',':
203
                    $regex .= $curlyLevels > 0 ? '|' : ',';
204
                    break;
205
                case '[':
206
                    $regex   .= '[';
207
                    $inSquare = true;
208
                    if (isset($glob[$i + 1]) && $glob[$i + 1] === '^') {
209
                        $regex .= '^';
210
                        ++$i;
211
                    }
212
                    break;
213
                case ']':
214
                    $regex   .= $inSquare ? ']' : '\\]';
215
                    $inSquare = false;
216
                    break;
217
                case '-':
218
                    $regex .= $inSquare ? '-' : '\\-';
219
                    break;
220
                case '\\':
221
                    if (isset($glob[$i + 1])) {
222
                        switch ($glob[$i + 1]) {
223
                            case '*':
224
                            case '?':
225
                            case '{':
226
                            case '}':
227
                            case '[':
228
                            case ']':
229
                            case '-':
230
                            case '^':
231
                            case '$':
232
                            case '~':
233
                            case '\\':
234
                                $regex .= '\\' . $glob[$i + 1];
235
                                ++$i;
236
                                break;
237
                            default:
238
                                $regex .= '\\\\';
239
                        }
240
                    }
241
                    break;
242
                default:
243
                    $regex .= $c;
244
                    break;
245
            }
246
        }
247
        if ($inSquare) {
248
            throw new InvalidArgumentException(sprintf(
249
                'Invalid glob: missing ] in %s',
250
                $glob
251
            ));
252
        }
253
        if ($curlyLevels > 0) {
254
            throw new InvalidArgumentException(sprintf(
255
                'Invalid glob: missing } in %s',
256
                $glob
257
            ));
258
        }
259
        return $delimiter . '^' . $regex . '$' . $delimiter;
260
    }
261
}
262