aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-06-14 04:10:38 +0900
committernsfisis <nsfisis@gmail.com>2026-06-14 04:24:37 +0900
commit4116323e9d55745c17b2d8008affa3394b726f6b (patch)
treeb829253d58d546d4c8b95ca90e9e2cc2ae6e4652
parent78157ab3ffeca37023381a422fccda2d4a0c64af (diff)
downloadphp-shirabe-4116323e9d55745c17b2d8008affa3394b726f6b.tar.gz
php-shirabe-4116323e9d55745c17b2d8008affa3394b726f6b.tar.zst
php-shirabe-4116323e9d55745c17b2d8008affa3394b726f6b.zip
refactor(pcre): drop PcreException
Composer never feeds a pattern that fails to compile at runtime, so a preg_*() failure is a programming error rather than a recoverable condition. Replace the PcreException path with a panic. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-rw-r--r--Cargo.lock1
-rw-r--r--crates/shirabe-external-packages/Cargo.toml1
-rw-r--r--crates/shirabe-external-packages/src/composer/pcre/mod.rs1
-rw-r--r--crates/shirabe-external-packages/src/composer/pcre/pcre_exception.rs36
-rw-r--r--crates/shirabe-external-packages/src/composer/pcre/preg.rs42
5 files changed, 30 insertions, 51 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 2dc9f89..0268a1c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1002,6 +1002,7 @@ version = "0.0.1"
dependencies = [
"anyhow",
"indexmap",
+ "regex",
"shirabe-php-shim",
"shirabe-semver",
]
diff --git a/crates/shirabe-external-packages/Cargo.toml b/crates/shirabe-external-packages/Cargo.toml
index d51701b..02e6a7a 100644
--- a/crates/shirabe-external-packages/Cargo.toml
+++ b/crates/shirabe-external-packages/Cargo.toml
@@ -8,6 +8,7 @@ shirabe-php-shim.workspace = true
shirabe-semver.workspace = true
anyhow.workspace = true
indexmap.workspace = true
+regex.workspace = true
[lints]
workspace = true
diff --git a/crates/shirabe-external-packages/src/composer/pcre/mod.rs b/crates/shirabe-external-packages/src/composer/pcre/mod.rs
index 5d1a373..20c37d0 100644
--- a/crates/shirabe-external-packages/src/composer/pcre/mod.rs
+++ b/crates/shirabe-external-packages/src/composer/pcre/mod.rs
@@ -1,4 +1,3 @@
-pub mod pcre_exception;
pub mod preg;
pub use preg::*;
diff --git a/crates/shirabe-external-packages/src/composer/pcre/pcre_exception.rs b/crates/shirabe-external-packages/src/composer/pcre/pcre_exception.rs
deleted file mode 100644
index 4ebe2c3..0000000
--- a/crates/shirabe-external-packages/src/composer/pcre/pcre_exception.rs
+++ /dev/null
@@ -1,36 +0,0 @@
-//! ref: composer/vendor/composer/pcre/src/PcreException.php
-
-use super::preg::{preg_last_error, preg_last_error_msg};
-
-#[derive(Debug)]
-pub struct PcreException(pub shirabe_php_shim::RuntimeException);
-
-impl PcreException {
- pub fn from_function(function: &str, pattern: &str) -> PcreException {
- let code = preg_last_error();
-
- PcreException(shirabe_php_shim::RuntimeException {
- message: format!(
- "{}(): failed executing \"{}\": {}",
- function,
- pattern,
- Self::pcre_last_error_message(code)
- ),
- code,
- })
- }
-
- // Modern PHP always provides preg_last_error_msg(), so the legacy fallbacks
- // that scanned the pcre constants are not reproduced.
- fn pcre_last_error_message(_code: i64) -> String {
- preg_last_error_msg()
- }
-}
-
-impl std::fmt::Display for PcreException {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(f, "{}", self.0)
- }
-}
-
-impl std::error::Error for PcreException {}
diff --git a/crates/shirabe-external-packages/src/composer/pcre/preg.rs b/crates/shirabe-external-packages/src/composer/pcre/preg.rs
index 4c0c2bf..f14cb03 100644
--- a/crates/shirabe-external-packages/src/composer/pcre/preg.rs
+++ b/crates/shirabe-external-packages/src/composer/pcre/preg.rs
@@ -1,6 +1,14 @@
//! ref: composer/vendor/composer/pcre/src/Preg.php
+//!
+//! The following two exception classes are intentionally not ported:
+//!
+//! - `PcreException`: thrown when a `preg_*()` call returns false. Composer never feeds a pattern
+//! that fails to compile at runtime, so such a failure would be a programming error rather than
+//! a recoverable condition; they panic instead.
+//! - `UnexpectedNullMatchException`: thrown by the `Preg::*StrictGroups()` variants when a capture
+//! group did not participate. Those variants were dropped because Rust's `Option` already
+//! distinguishes participating from non-participating groups.
-use super::pcre_exception::PcreException;
use indexmap::IndexMap;
pub const PREG_PATTERN_ORDER: i64 = 1;
@@ -41,7 +49,7 @@ impl Preg {
flags | PREG_UNMATCHED_AS_NULL,
offset,
)
- .ok_or_else(|| PcreException::from_function("preg_match", pattern))?;
+ .unwrap_or_else(|| invalid_regex());
if let Some(out) = matches {
*out = drop_null_matches(internal);
@@ -80,7 +88,7 @@ impl Preg {
flags | PREG_UNMATCHED_AS_NULL,
offset,
)
- .ok_or_else(|| PcreException::from_function("preg_match_all", pattern))?;
+ .unwrap_or_else(|| invalid_regex());
if let Some(out) = matches {
*out = null_to_empty_match_all(internal);
@@ -106,7 +114,7 @@ impl Preg {
flags | PREG_UNMATCHED_AS_NULL | PREG_OFFSET_CAPTURE,
offset,
)
- .ok_or_else(|| PcreException::from_function("preg_match_all", pattern))?;
+ .unwrap_or_else(|| invalid_regex());
if let Some(out) = matches {
*out = null_to_empty_offset_match_all(internal);
@@ -148,8 +156,8 @@ impl Preg {
// `$subject` is statically a string here, so the is_scalar/is_array
// guards (ARRAY_MSG / INVALID_TYPE_MSG) of the PHP original are
// unreachable and not reproduced.
- preg_replace(pattern, replacement, subject, limit, count)
- .ok_or_else(|| PcreException::from_function("preg_replace", pattern).into())
+ Ok(preg_replace(pattern, replacement, subject, limit, count)
+ .unwrap_or_else(|| invalid_regex()))
}
pub fn replace_callback<F: FnMut(&IndexMap<CaptureKey, String>) -> String>(
@@ -172,8 +180,10 @@ impl Preg {
replacement(&drop_null_matches_ref(internal))
};
- preg_replace_callback(pattern, adapter, subject, limit, count, flags)
- .ok_or_else(|| PcreException::from_function("preg_replace_callback", pattern).into())
+ Ok(
+ preg_replace_callback(pattern, adapter, subject, limit, count, flags)
+ .unwrap_or_else(|| invalid_regex()),
+ )
}
pub fn split(pattern: &str, subject: &str) -> anyhow::Result<Vec<String>> {
@@ -191,8 +201,7 @@ impl Preg {
"PREG_SPLIT_OFFSET_CAPTURE is not supported as it changes the type of $matches, use splitWithOffsets() instead"
);
- preg_split(pattern, subject, limit, flags)
- .ok_or_else(|| PcreException::from_function("preg_split", pattern).into())
+ Ok(preg_split(pattern, subject, limit, flags).unwrap_or_else(|| invalid_regex()))
}
pub fn grep(pattern: &str, array: &[&str]) -> anyhow::Result<Vec<String>> {
@@ -200,8 +209,7 @@ impl Preg {
}
pub fn grep3(pattern: &str, array: &[&str], flags: i64) -> anyhow::Result<Vec<String>> {
- preg_grep(pattern, array, flags)
- .ok_or_else(|| PcreException::from_function("preg_grep", pattern).into())
+ Ok(preg_grep(pattern, array, flags).unwrap_or_else(|| invalid_regex()))
}
pub fn is_match(pattern: &str, subject: &str) -> anyhow::Result<bool> {
@@ -239,7 +247,7 @@ impl Preg {
PREG_UNMATCHED_AS_NULL,
0,
)
- .ok_or_else(|| PcreException::from_function("preg_match", pattern))?;
+ .unwrap_or_else(|| invalid_regex());
matches.clear();
for (key, value) in internal {
@@ -259,7 +267,7 @@ impl Preg {
// unmatched groups are truncated, interior unmatched groups become "".
let mut internal: IndexMap<CaptureKey, Option<String>> = IndexMap::new();
let result = preg_match(pattern, subject, Some(&mut internal), 0, 0)
- .ok_or_else(|| PcreException::from_function("preg_match", pattern))?;
+ .unwrap_or_else(|| invalid_regex());
if result == 0 {
return Ok(None);
@@ -448,3 +456,9 @@ pub fn preg_split(_pattern: &str, _subject: &str, _limit: i64, _flags: i64) -> O
pub fn preg_grep(_pattern: &str, _array: &[&str], _flags: i64) -> Option<Vec<String>> {
todo!()
}
+
+/// Panics if a pattern is invalid instead of throwing a PcreException.
+/// TODO: takes regex::Error and shows its message
+fn invalid_regex() -> ! {
+ panic!("invalid regex");
+}