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

WildcardPattern::isDynamic()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 2
b 0
f 0
nc 1
nop 1
dl 0
loc 4
ccs 3
cts 3
cp 1
crap 1
rs 10
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 $ignoreCase = false;
24
    private string $pattern;
25
    private array $delimiters;
26
27
    /**
28
     * @param string $pattern The shell wildcard pattern to match against.
29
     * @param array $delimiters Delimiters to consider for "*" (`/` and `\` by default).
30
     */
31 60
    public function __construct(string $pattern, array $delimiters = ['\\\\', '/'])
32
    {
33 60
        $this->pattern = $pattern;
34 60
        $this->delimiters = $delimiters;
35 60
    }
36
37
    /**
38
     * Checks if the passed string would match the given shell wildcard pattern.
39
     *
40
     * @param string $string The tested string.
41
     *
42
     * @return bool Whether the string matches pattern or not.
43
     */
44 59
    public function match(string $string): bool
45
    {
46 59
        if ($this->pattern === '**') {
47
            return true;
48
        }
49
50 59
        $pattern = $this->pattern;
51
52
        $replacements = [
53 59
            '\*\*' => '.*',
54
            '\\\\\\\\' => '\\\\',
55
            '\\\\\\*' => '[*]',
56
            '\\\\\\?' => '[?]',
57
        ];
58
59 59
        if ($this->delimiters === []) {
60
            $replacements += [
61 1
                '\*' => '.*',
62
                '\?' => '?',
63
            ];
64
        } else {
65 58
            $notDelimiters = '[^' . preg_quote(implode('', $this->delimiters), '#') . ']';
66
            $replacements += [
67 58
                '\*' => "$notDelimiters*",
68 58
                '\?' => $notDelimiters,
69
            ];
70
        }
71
72
        $replacements += [
73 59
            '\[\!' => '[^',
74
            '\[' => '[',
75
            '\]' => ']',
76
            '\-' => '-',
77
        ];
78
79 59
        if ($this->withoutEscape) {
80 5
            unset($replacements['\\\\\\\\'], $replacements['\\\\\\*'], $replacements['\\\\\\?']);
81
        }
82
83 59
        $pattern = strtr(preg_quote($pattern, '#'), $replacements);
84 59
        $pattern = '#^' . $pattern . '$#us';
85
86 59
        if ($this->ignoreCase) {
87 1
            $pattern .= 'i';
88
        }
89
90 59
        return preg_match($pattern, $string) === 1;
91
    }
92
93
    /**
94
     * Disables using `\` to escape following special character. `\` becomes regular character.
95
     *
96
     * @param bool $flag
97
     *
98
     * @return self
99
     */
100 7
    public function withoutEscape(bool $flag = true): self
101
    {
102 7
        $new = clone $this;
103 7
        $new->withoutEscape = $flag;
104 7
        return $new;
105
    }
106
107
    /**
108
     * Make pattern case insensitive.
109
     *
110
     * @param bool $flag
111
     *
112
     * @return self
113
     */
114 3
    public function ignoreCase(bool $flag = true): self
115
    {
116 3
        $new = clone $this;
117 3
        $new->ignoreCase = $flag;
118 3
        return $new;
119
    }
120
121
    /**
122
     * Returns whether the pattern contains a dynamic part i.e.
123
     * has unescaped "*",  "{", "?", or "[" character.
124
     *
125
     * @param string $pattern The pattern to check.
126
     *
127
     * @return bool Whether the pattern contains a dynamic part.
128
     */
129 5
    public static function isDynamic(string $pattern): bool
130
    {
131 5
        $pattern = preg_replace('/\\\\./', '', $pattern);
132 5
        return (bool)preg_match('/[*{?\[]/', $pattern);
133
    }
134
}
135