Skip to content

Commit 13ad016

Browse files
viktorproggervjik
andauthored
Increase Wildcard Pattern speed (#117)
Co-authored-by: Sergei Predvoditelev <sergei@predvoditelev.ru>
1 parent 447b68c commit 13ad016

2 files changed

Lines changed: 59 additions & 40 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## 2.3.1 under development
44

5-
- no changes in this release.
5+
- Enh #117: `WildcardPatters` uses memoization and accelerates ~2 times on repeated calls (@viktorprogger)
66

77
## 2.3.0 October 23, 2023
88

src/WildcardPattern.php

Lines changed: 58 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*
1616
* - `\` escapes other special characters if usage of escape character is not turned off.
1717
* - `*` matches any string including the empty string except it has a delimiter (`/` and `\` by default).
18-
* - `**` matches any string including the empty string and delimiters.
18+
* - `**` matches any string including the empty string and delimiters.
1919
* - `?` matches any single character.
2020
* - `[seq]` matches any character in seq.
2121
* - `[a-z]` matches any character from a to z.
@@ -27,6 +27,11 @@ final class WildcardPattern
2727
{
2828
private bool $ignoreCase = false;
2929

30+
/**
31+
* @psalm-var non-empty-string|null
32+
*/
33+
private ?string $patternPrepared = null;
34+
3035
/**
3136
* @param string $pattern The shell wildcard pattern to match against.
3237
* @param string[] $delimiters Delimiters to consider for "*" (`/` and `\` by default).
@@ -50,7 +55,55 @@ public function match(string $string): bool
5055
return true;
5156
}
5257

53-
$pattern = $this->pattern;
58+
return preg_match($this->getPatternPrepared(), $string) === 1;
59+
}
60+
61+
/**
62+
* Make pattern case insensitive.
63+
*/
64+
public function ignoreCase(bool $flag = true): self
65+
{
66+
$new = clone $this;
67+
$new->patternPrepared = null;
68+
$new->ignoreCase = $flag;
69+
70+
return $new;
71+
}
72+
73+
/**
74+
* Returns whether the pattern contains a dynamic part i.e.
75+
* has unescaped "*", "{", "?", or "[" character.
76+
*
77+
* @param string $pattern The pattern to check.
78+
*
79+
* @return bool Whether the pattern contains a dynamic part.
80+
*/
81+
public static function isDynamic(string $pattern): bool
82+
{
83+
$pattern = preg_replace('/\\\\./', '', $pattern);
84+
return preg_match('/[*{?\[]/', $pattern) === 1;
85+
}
86+
87+
/**
88+
* Escapes pattern characters in a string.
89+
*
90+
* @param string $string Source string.
91+
*
92+
* @return string String with pattern characters escaped.
93+
*/
94+
public static function quote(string $string): string
95+
{
96+
return preg_replace('#([\\\\?*\\[\\]])#', '\\\\$1', $string);
97+
}
98+
99+
/**
100+
* @return non-empty-string
101+
*/
102+
private function getPatternPrepared(): string
103+
{
104+
if ($this->patternPrepared !== null) {
105+
return $this->patternPrepared;
106+
}
54107

55108
$replacements = [
56109
'\*\*' => '.*',
@@ -81,49 +134,15 @@ public function match(string $string): bool
81134
'\-' => '-',
82135
];
83136

84-
$pattern = strtr(preg_quote($pattern, '#'), $replacements);
137+
$pattern = strtr(preg_quote($this->pattern, '#'), $replacements);
85138
$pattern = '#^' . $pattern . '$#us';
86139

87140
if ($this->ignoreCase) {
88141
$pattern .= 'i';
89142
}
90143

91-
return preg_match($pattern, $string) === 1;
92-
}
93-
94-
/**
95-
* Make pattern case insensitive.
96-
*/
97-
public function ignoreCase(bool $flag = true): self
98-
{
99-
$new = clone $this;
100-
$new->ignoreCase = $flag;
101-
return $new;
102-
}
103-
104-
/**
105-
* Returns whether the pattern contains a dynamic part i.e.
106-
* has unescaped "*", "{", "?", or "[" character.
107-
*
108-
* @param string $pattern The pattern to check.
109-
*
110-
* @return bool Whether the pattern contains a dynamic part.
111-
*/
112-
public static function isDynamic(string $pattern): bool
113-
{
114-
$pattern = preg_replace('/\\\\./', '', $pattern);
115-
return preg_match('/[*{?\[]/', $pattern) === 1;
116-
}
144+
$this->patternPrepared = $pattern;
117145

118-
/**
119-
* Escapes pattern characters in a string.
120-
*
121-
* @param string $string Source string.
122-
*
123-
* @return string String with pattern characters escaped.
124-
*/
125-
public static function quote(string $string): string
126-
{
127-
return preg_replace('#([\\\\?*\\[\\]])#', '\\\\$1', $string);
146+
return $this->patternPrepared;
128147
}
129148
}

0 commit comments

Comments
 (0)