aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe-php-shim/src
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-07-05 00:59:45 +0900
committernsfisis <nsfisis@gmail.com>2026-07-05 00:59:45 +0900
commit23860a736f539064a64284a673a1b0e69d24e980 (patch)
treed33d7f31dd1c378f83bf9b27337d94840453c54e /crates/shirabe-php-shim/src
parenta73e44e327776ac4ba79737eea6b6ad00cf00da6 (diff)
downloadphp-shirabe-23860a736f539064a64284a673a1b0e69d24e980.tar.gz
php-shirabe-23860a736f539064a64284a673a1b0e69d24e980.tar.zst
php-shirabe-23860a736f539064a64284a673a1b0e69d24e980.zip
perf(pcre): cache compiled regex patterns across preg_* calls
Composer's classmap generator re-issues the same PHP-derived pattern string for every scanned file (and every token within it), relying on PCRE's built-in compiled-pattern cache to make that free. shirabe had no equivalent, so every Preg::* call recompiled the pattern from scratch via regex::Regex::new(), making `composer create-project` autoload generation ~130x slower than Composer on a fresh laravel/laravel install (130s vs ~1s). Memoizing compiled patterns by their raw string in compile_php_pattern closes that gap. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe-php-shim/src')
-rw-r--r--crates/shirabe-php-shim/src/preg.rs24
1 files changed, 24 insertions, 0 deletions
diff --git a/crates/shirabe-php-shim/src/preg.rs b/crates/shirabe-php-shim/src/preg.rs
index 4b43467..3da8ae7 100644
--- a/crates/shirabe-php-shim/src/preg.rs
+++ b/crates/shirabe-php-shim/src/preg.rs
@@ -1,3 +1,6 @@
+use indexmap::IndexMap;
+use std::sync::{LazyLock, Mutex};
+
pub const PREG_PATTERN_ORDER: i64 = 1;
pub const PREG_SET_ORDER: i64 = 2;
pub const PREG_OFFSET_CAPTURE: i64 = 256;
@@ -439,7 +442,28 @@ fn translate_pcre_literals(inner: &str) -> String {
out
}
+// PHP's PCRE engine keeps a per-process cache of compiled patterns (pcre.cache_size, default 4096),
+// so repeated preg_* calls with the same pattern string are effectively free. The `regex` crate has
+// no such cache, and callers like the classmap generator re-issue the same pattern string for every
+// file (or even every scanned token), so compilation must be memoized here to match PHP's amortized
+// cost.
+static PATTERN_CACHE: LazyLock<Mutex<IndexMap<String, (regex::Regex, bool)>>> =
+ LazyLock::new(|| Mutex::new(IndexMap::new()));
+
fn compile_php_pattern(pattern: &str) -> anyhow::Result<(regex::Regex, bool)> {
+ if let Some(cached) = PATTERN_CACHE.lock().unwrap().get(pattern) {
+ return Ok(cached.clone());
+ }
+
+ let compiled = compile_php_pattern_uncached(pattern)?;
+ PATTERN_CACHE
+ .lock()
+ .unwrap()
+ .insert(pattern.to_string(), compiled.clone());
+ Ok(compiled)
+}
+
+fn compile_php_pattern_uncached(pattern: &str) -> anyhow::Result<(regex::Regex, bool)> {
let delimiter = pattern
.chars()
.next()