Passed
Pull Request — master (#67)
by Alexander
02:18
created

WildcardPattern::isDynamic()   C

Complexity

Conditions 12
Paths 6

Size

Total Lines 19
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 156

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 12
eloc 11
c 1
b 0
f 0
nc 6
nop 1
dl 0
loc 19
ccs 0
cts 12
cp 0
crap 156
rs 6.9666

How to fix   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
namespace Yiisoft\Strings;
6
7
/**
8
 * A wildcard pattern to match strings against.
9
 *
10
 * - `\` escapes other special characters if usage of escape character is not turned off.
11
 * - `*` matches any string including the empty string. Slashes do not match.
12
  * - `**` matches any string including the empty string and slashes.
13
 * - `?` matches any single character.
14
 * - `[seq]` matches any character in seq.
15
 * - `[a-z]` matches any character from a to z.
16
 * - `[!seq]` matches any character not in seq.
17
 * - `[[:alnum:]]` matches POSIX style character classes,
18
 *   see {@see https://www.php.net/manual/en/regexp.reference.character-classes.php}.
19
 */
20
final class WildcardPattern
21
{
22
    private bool $withoutEscape = false;
23
    private bool $matchLeadingPeriodExactly = false;
24
    private bool $ignoreCase = false;
25
    private bool $matchEnding = false;
26
    private string $pattern;
27
28
    /**
29
     * @param string $pattern The shell wildcard pattern to match against.
30
     */
31 68
    public function __construct(string $pattern)
32
    {
33 68
        $this->pattern = $pattern;
34 68
    }
35
36
    /**
37
     * Checks if the passed string would match the given shell wildcard pattern.
38
     *
39
     * @param string $string The tested string.
40
     *
41
     * @return bool Whether the string matches pattern or not.
42
     */
43 67
    public function match(string $string): bool
44
    {
45 67
        if ($this->pattern === '**' && !$this->matchLeadingPeriodExactly) {
46
            return true;
47
        }
48
49 67
        $pattern = $this->pattern;
50
51 67
        if ($this->matchLeadingPeriodExactly) {
52 3
            $pattern = preg_replace('/^[*?]/', '[!.]', $pattern);
53
        }
54
55
        $replacements = [
56 67
            '\*\*' => '.*',
57
            '\\\\\\\\' => '\\\\',
58
            '\\\\\\*' => '[*]',
59
            '\\\\\\?' => '[?]',
60
            '\*' => '[^/\\\\]*',
61
            '\?' => '[^/\\\\]',
62
            '\[\!' => '[^',
63
            '\[' => '[',
64
            '\]' => ']',
65
            '\-' => '-',
66
        ];
67
68 67
        if ($this->withoutEscape) {
69 5
            unset($replacements['\\\\\\\\'], $replacements['\\\\\\*'], $replacements['\\\\\\?']);
70
        }
71
72 67
        $pattern = strtr(preg_quote($pattern, '#'), $replacements);
73 67
        $pattern = '#' . ($this->matchEnding ? '' : '^') . $pattern . '$#us';
74
75 67
        if ($this->ignoreCase) {
76 1
            $pattern .= 'i';
77
        }
78
79 67
        return preg_match($pattern, $string) === 1;
80
    }
81
82
    /**
83
     * Disables using `\` to escape following special character. `\` becomes regular character.
84
     *
85
     * @param bool $flag
86
     *
87
     * @return self
88
     */
89 7
    public function withoutEscape(bool $flag = true): self
90
    {
91 7
        $new = clone $this;
92 7
        $new->withoutEscape = $flag;
93 7
        return $new;
94
    }
95
96
    /**
97
     * Make pattern case insensitive.
98
     *
99
     * @param bool $flag
100
     *
101
     * @return self
102
     */
103 3
    public function ignoreCase(bool $flag = true): self
104
    {
105 3
        $new = clone $this;
106 3
        $new->ignoreCase = $flag;
107 3
        return $new;
108
    }
109
110
    /**
111
     * Do not match `.` character at the beginning of string with wildcards.
112
     * Useful for matching file paths.
113
     *
114
     * @param bool $flag
115
     *
116
     * @return self
117
     */
118 5
    public function exactLeadingPeriod(bool $flag = true): self
119
    {
120 5
        $new = clone $this;
121 5
        $new->matchLeadingPeriodExactly = $flag;
122 5
        return $new;
123
    }
124
125
    /**
126
     * Match ending only.
127
     * By default wildcard pattern matches string exactly. By using this mode, beginning of the string could be anything.
128
     *
129
     * @param bool $flag
130
     *
131
     * @return self
132
     */
133 8
    public function matchEnding(bool $flag = true): self
134
    {
135 8
        $new = clone $this;
136 8
        $new->matchEnding = $flag;
137 8
        return $new;
138
    }
139
140
    /**
141
     * Returns whether the pattern contains a dynamic part i.e.
142
     * has unescaped "*",  "{", "?", or "[" character.
143
     *
144
     * @param string $pattern The pattern to check.
145
     *
146
     * @return bool Whether the pattern contains a dynamic part.
147
     */
148
    public static function isDynamic(string $pattern): bool
149
    {
150
        $position = strpos($pattern, '*');
151
        if ($position !== false && ($position === 0 || $pattern[$position - 1] !== '\\')) {
152
            return true;
153
        }
154
155
        $position = strpos($pattern, '{');
156
        if ($position !== false && ($position === 0 || $pattern[$position - 1] !== '\\')) {
157
            return true;
158
        }
159
160
        $position = strpos($pattern, '?');
161
        if ($position !== false && ($position === 0 || $pattern[$position - 1] !== '\\')) {
162
            return true;
163
        }
164
165
        $position = strpos($pattern, '[');
166
        return $position !== false && ($position === 0 || $pattern[$position - 1] !== '\\');
167
    }
168
}
169