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

WildcardPattern::match()   B

Complexity

Conditions 7
Paths 17

Size

Total Lines 51
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 7.0061

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 7
eloc 32
c 2
b 0
f 0
nc 17
nop 1
dl 0
loc 51
ccs 19
cts 20
cp 0.95
crap 7.0061
rs 8.4746

How to fix   Long Method   

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 except it has a delimiter (`/` and `\` by default).
12
  * - `**` matches any string including the empty string and delimiters.
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 string $pattern;
26
    private array $delimiters;
27
28
    /**
29
     * @param string $pattern The shell wildcard pattern to match against.
30
     * @param array $delimiters Delimiters to consider for "*" (`/` and `\` by default).
31
     */
32 65
    public function __construct(string $pattern, array $delimiters = ['\\\\', '/'])
33
    {
34 65
        $this->pattern = $pattern;
35 65
        $this->delimiters = $delimiters;
36 65
    }
37
38
    /**
39
     * Checks if the passed string would match the given shell wildcard pattern.
40
     *
41
     * @param string $string The tested string.
42
     *
43
     * @return bool Whether the string matches pattern or not.
44
     */
45 64
    public function match(string $string): bool
46
    {
47 64
        if ($this->pattern === '**' && !$this->matchLeadingPeriodExactly) {
48
            return true;
49
        }
50
51 64
        $pattern = $this->pattern;
52
53 64
        if ($this->matchLeadingPeriodExactly) {
54 3
            $pattern = preg_replace('/^[*?]/', '[!.]', $pattern);
55
        }
56
57
        $replacements = [
58 64
            '\*\*' => '.*',
59
            '\\\\\\\\' => '\\\\',
60
            '\\\\\\*' => '[*]',
61
            '\\\\\\?' => '[?]',
62
        ];
63
64 64
        if ($this->delimiters === []) {
65
            $replacements += [
66 1
                '\*' => '.*',
67
                '\?' => '?',
68
            ];
69
        } else {
70 63
            $notDelimiters = '[^' . preg_quote(implode('', $this->delimiters), '#') . ']';
71
            $replacements += [
72 63
                '\*' => "$notDelimiters*",
73 63
                '\?' => $notDelimiters,
74
            ];
75
        }
76
77
        $replacements += [
78 64
            '\[\!' => '[^',
79
            '\[' => '[',
80
            '\]' => ']',
81
            '\-' => '-',
82
        ];
83
84 64
        if ($this->withoutEscape) {
85 5
            unset($replacements['\\\\\\\\'], $replacements['\\\\\\*'], $replacements['\\\\\\?']);
86
        }
87
88 64
        $pattern = strtr(preg_quote($pattern, '#'), $replacements);
89 64
        $pattern = '#^' . $pattern . '$#us';
90
91 64
        if ($this->ignoreCase) {
92 1
            $pattern .= 'i';
93
        }
94
95 64
        return preg_match($pattern, $string) === 1;
96
    }
97
98
    /**
99
     * Disables using `\` to escape following special character. `\` becomes regular character.
100
     *
101
     * @param bool $flag
102
     *
103
     * @return self
104
     */
105 7
    public function withoutEscape(bool $flag = true): self
106
    {
107 7
        $new = clone $this;
108 7
        $new->withoutEscape = $flag;
109 7
        return $new;
110
    }
111
112
    /**
113
     * Make pattern case insensitive.
114
     *
115
     * @param bool $flag
116
     *
117
     * @return self
118
     */
119 3
    public function ignoreCase(bool $flag = true): self
120
    {
121 3
        $new = clone $this;
122 3
        $new->ignoreCase = $flag;
123 3
        return $new;
124
    }
125
126
    /**
127
     * Do not match `.` character at the beginning of string with wildcards.
128
     * Useful for matching file paths.
129
     *
130
     * @param bool $flag
131
     *
132
     * @return self
133
     */
134 5
    public function exactLeadingPeriod(bool $flag = true): self
135
    {
136 5
        $new = clone $this;
137 5
        $new->matchLeadingPeriodExactly = $flag;
138 5
        return $new;
139
    }
140
141
    /**
142
     * Returns whether the pattern contains a dynamic part i.e.
143
     * has unescaped "*",  "{", "?", or "[" character.
144
     *
145
     * @param string $pattern The pattern to check.
146
     *
147
     * @return bool Whether the pattern contains a dynamic part.
148
     */
149 5
    public static function isDynamic(string $pattern): bool
150
    {
151 5
        $pattern = preg_replace('/\\\\./', '', $pattern);
152 5
        return (bool)preg_match('/[*{?\[]/', $pattern);
153
    }
154
}
155