An intuitive, small, and fast regular expression helper for PHP 8.3+ with clean result objects and a few ergonomic utilities around PCRE.
Package:
liquidrazor/liquid-regexβ’ License: MIT
- Fluent, tiny API for matching and replacing.
- Lazy, rewindable match results with
Iterator,ArrayAccess,Countable, andJsonSerializablesupport. - Deterministic result shapes via
ResultTypes(numeric, associative, or both) and aSTRICTfilter. - PCRE helpers to check version/JIT support and validate pattern syntax.
- Exceptions you can catch with informative messages bubbled from PCRE.
- PHP 8.3 or newer
- PCRE (bundled with PHP)
composer require liquidrazor/regexThe library is namespaced under LiquidRazor\Regex and PSRβ4 autoloaded per composer.json.
The Regex class is the main faΓ§ade. It also memoizes compiled instances by pattern:
use LiquidRazor\Regex\Regex;
$re = Regex::compiled('/(?P<word>[a-z]+)/i');From there you can match or replace (see examples below).
Choose how match arrays are exposed:
use LiquidRazor\Regex\Result\ResultTypes;
// Types
ResultTypes::INDEXED; // numeric keys only (0, 1, 2, ...)
ResultTypes::ASSOCIATIVE; // named keys only ('foo', 'bar', ...)
ResultTypes::BOTH; // default: include both kinds
// Modifier
ResultTypes::STRICT; // when used, filters to "pure" keys only:
// - with INDEXED: keeps only truly integer keys (0,1,2...)
// - with ASSOCIATIVE: keeps only truly string, non-numeric keysThis lets you request exactly the view you need (e.g., only named groups). You can bitβOR flags:
$flags = ResultTypes::ASSOCIATIVE | ResultTypes::STRICT;Extra capture behavior forwarded to PCRE:
use LiquidRazor\Regex\Lib\Flags;
Flags::OffsetCapture; // include offsets for each capture
Flags::UnmatchedAsNull; // unmatched subpatterns become nulluse LiquidRazor\Regex\Regex;
use LiquidRazor\Regex\Result\ResultTypes;
$re = Regex::compiled('/(?P<word>[a-z]+)/i');
$result = $re->match(
haystack: "Hello, World!",
resultType: ResultTypes::ASSOCIATIVE | ResultTypes::STRICT // named keys only
);
if ($result->didMatch) {
// ArrayAccess
$first = $result[0]; // or $result['word'] in ASSOCIATIVE mode
// Countable
$count = count($result);
// Iteration (rewindable)
foreach ($result as $k => $v) {
// ...
}
// JSON
$json = json_encode($result, JSON_PRETTY_PRINT);
}use LiquidRazor\Regex\Regex;
use LiquidRazor\Regex\Result\Replace\Result;
$re = Regex::compiled('/(\d+)/');
$replace = $re->replace('abc 123 xyz 45', '#');
$replace->pattern; // the pattern used
$replace->count; // number of replacements performed
(string)$replace; // casts to the replaced string (or JSON if array)LiquidRazor\Regex\Result\Replace\Result implements Stringable. If the replacement result is an array (e.g., when replacing in arrays), __toString() returns JSON.
The LiquidRazor\Regex\Result\Matches\Result object exposes a lazy, memoized sequence of match entries. You decide which keys appear using ResultTypes:
INDEXED: keeps only integer keys (0,1,2β¦). WithSTRICT, filters out any non-integer keys.ASSOCIATIVE: keeps only named group keys. WithSTRICT, filters out numeric-looking strings.BOTH: includes both numeric and associative keys (default). Combine withSTRICTto prune mixed keys.
The object supports:
Iterator(rewindable)ArrayAccess(read-only; attempting to mutate throwsImmutableException)CountableJsonSerializable
// Read-only access (mutations throw)
$value = $result['name'] ?? $result[0];If there was no match, the Result is empty with didMatch === false (safe to iterate/count/JSON-encode).
Use the Pcre static utility for environment checks:
use LiquidRazor\Regex\Pcre;
Pcre::versionString(); // "PCRE x.y (major.minor)"
Pcre::isJitSupported(); // bool
Pcre::isValid('/^[a-z]+$/i'); // quick sanity check for "/.../flags"-style patternsNote:
isValid()only checks a basic/.../flagsshape, also does compile the pattern and checks the result. If compilation fails, it returns false.
The library centralizes PCRE errors through LiquidRazor\Regex\Exception classes:
PcreExceptionβ wrapspreg_last_error()& messageExceptionβ currently extendsPcreExceptionfor convenienceImmutableExceptionβ thrown on attempts to mutate read-only results
In practice, most API calls either return a Result object or throw on internal PCRE errors (e.g., bad backtrack limit).
use LiquidRazor\Regex\Exception\PcreException;
try {
$re = Regex::compiled('/(?P<name>[a-z]+)/i');
$res = $re->match('...');
} catch (PcreException $e) {
// inspect $e->getMessage(), $e->getCode()
}The following reflects the current source structure at a high level. Some signatures may evolve.
Regex::compiled(string $pattern): RegexRegex->match(string $haystack, int $resultType = ResultTypes::BOTH, Flags ...$captureFlags): LiquidRazor\Regex\Result\Matches\ResultRegex->replace(string|array $subject, string|callable $replacement, int $limit = -1): LiquidRazor\Regex\Result\Replace\Result
Result types:
LiquidRazor\Regex\Result\Matches\Result- Properties:
pattern,haystack,didMatch - Interfaces:
Iterator,ArrayAccess(read-only),Countable,JsonSerializable
- Properties:
LiquidRazor\Regex\Result\Replace\Result implements Stringable- Properties:
pattern,count,replaced - Casting:
(string)$resultyields the final string (or JSON if array)
- Properties:
Helpers:
LiquidRazor\Regex\PcreversionString(): stringisJitSupported(): boolisValid(?string $pattern): bool
Enums / constants:
LiquidRazor\Regex\Lib\FlagsβDefault,OffsetCapture,UnmatchedAsNullLiquidRazor\Regex\Result\ResultTypesβINDEXED,ASSOCIATIVE,BOTH,STRICT
use LiquidRazor\Regex\Regex;
use LiquidRazor\Regex\Result\ResultTypes;
$re = Regex::compiled('/(?P<num>\d+)-(?P<word>[a-z]+)/i');
$res = $re->match('42-Answer', ResultTypes::ASSOCIATIVE | ResultTypes::STRICT);
echo json_encode($res, JSON_PRETTY_PRINT);
/*
{
"num": "42",
"word": "Answer"
}
*/use LiquidRazor\Regex\Regex;
use LiquidRazor\Regex\Lib\Flags;
$re = Regex::compiled('/(a)?(b)/');
$res = $re->match('xb', Flags::OffsetCapture, Flags::UnmatchedAsNull);
// Offsets included; the first group is null because it didn't match.You can plug this library into any test runner. The result objects are easy to snapshot by JSON encoding.
$this->assertJsonStringEqualsJsonString(
'{"word":"Hello"}',
json_encode($result, JSON_THROW_ON_ERROR)
);- Open an issue for design/API discussions.
- Keep the API minimal and predictable.
- PRs welcome with tests and docs for new behavior.
MIT Β© LiquidRazor