yoeunes / regex-parser
A PCRE regex parser with lexer, AST builder, validation, ReDoS analysis, and syntax highlighting. Zero runtime dependencies.
Fund package maintenance!
Requires
- php: >=8.2
Requires (Dev)
None
Suggests
- phpstan/extension-installer: To automatically enable the PHPStan rule for regex validation.
- phpstan/phpstan: To run static analysis and detect invalid regex patterns.
- psr/cache: To share AST cache via PSR-6 pools.
- psr/simple-cache: To share AST cache via PSR-16 caches.
Provides
None
Conflicts
None
Replaces
None
- 2.x-dev
- 1.x-dev
- v1.3.0
- v1.2.0
- v1.1.0
- v1.0.10
- v1.0.9
- v1.0.8
- v1.0.7
- v1.0.6
- v1.0.5
- v1.0.4
- v1.0.3
- v1.0.2
- v1.0.1
- v1.0.0
- v1.0.0-RC1
- v0.31.0
- v0.30.0
- v0.29.0
- v0.28.0
- v0.27.0
- v0.26.0
- v0.25.2
- v0.25.1
- v0.25.0
- v0.24.0
- v0.23.0
- v0.22.0
- v0.21.0
- v0.20.0
- v0.19.0
- v0.18.0
- v0.17.4
- v0.17.3
- v0.17.2
- v0.17.1
- v0.17.0
- v0.16.0
- v0.15.1
- v0.15.0
- v0.14.13
- v0.14.12
- v0.14.11
- v0.14.10
- v0.14.9
- v0.14.8
- v0.14.7
- v0.14.6
- v0.14.5
- v0.14.4
- v0.14.3
- v0.14.2
- v0.14.1
- v0.14.0
- v0.13.0
- v0.12.0
- v0.11.0
- v0.10.0
- v0.9.0
- v0.8.0
- v0.7.0
- v0.6.0
- v0.5.1
- v0.5.0
- v0.4.0
- v0.3.0
- v0.2.0
- v0.1.10
- v0.1.9
- v0.1.8
- v0.1.7
- v0.1.6
- v0.1.5
- v0.1.4
- v0.1.3
- v0.1.2
- v0.1.1
- v0.1.0
- dev-research-gap
- dev-solver-on-hir
This package is auto-updated.
Last update: 2026-10-04 03:49:18 UTC
README
PHPRegex: Static Analysis, Linter & Logic Solver
PHPRegex is a PHP 8.2+ library that treats regular expressions as code.
Unlike simple wrappers around preg_match, PHPRegex implements a complete compiler pipeline (Lexer → Parser → AST) and an Automata-based Logic Solver (normalized form → NFA → DFA).
This architecture allows for advanced static analysis:
- Linting: Detect redundancy, useless flags, and common mistakes.
- Safety: Prove a pattern safe from catastrophic backtracking (ReDoS), or hand you the input that triggers it.
- Logic: Compare patterns via NFA/DFA (Intersection, Equivalence, Subset) for the regular subset it supports.
Built for learning, validation, and robust tooling in PHP projects.
⚠️ What this is and is not. PHPRegex is a side project and a learning exercise. It is not a hardened security product and should not be your only line of defense. A ReDoS verdict is proven only for the subset of PCRE the analysis models, one match attempt at a time; outside it, structural heuristics decide, and the verdict says so. The parser aims for PCRE compatibility but does not cover every edge case of the PCRE engine.
If you are new to regex, start with the Regex Tutorial. If you want a short overview, see the Quick Start Guide.
Getting started
# Install the library composer require php-regex/regex-toolkit # Install the command-line tool, and try it composer require --dev php-regex/regex-cli vendor/bin/regex explain '/\d{4}-\d{2}-\d{2}/'
PHPRegex is a family of packages, developed together in php-regex/php-regex and released with one version number. Install the one you need:
| package | what it holds |
|---|---|
php-regex/regex-toolkit |
the Regex facade: every library below in one call |
php-regex/regex-parser |
lexer, parser, immutable AST, validator, PCRE2 release targeting, AST cache |
php-regex/regex-explain |
explanations, highlighting, ASCII tree, Mermaid and railroad diagrams |
php-regex/regex-optimizer |
shorter equivalent patterns, modernized syntax |
php-regex/regex-generator |
sample strings and test cases |
php-regex/regex-automata |
language equivalence, intersection and subset |
php-regex/regex-redos |
catastrophic backtracking (ReDoS) analysis |
php-regex/regex-transpiler |
JavaScript and Python targets |
php-regex/regex-linter |
lint rules and pattern extraction from PHP sources |
php-regex/regex-cli |
the regex command |
php-regex/regex-language-server |
diagnostics in any LSP editor |
php-regex/regex-phpstan |
the PHPStan extension |
php-regex/regex-symfony |
the Symfony bundle |
php-regex/regex-laravel |
the Laravel integration |
Coming from 1.x (yoeunes/regex-parser)? See UPGRADE-2.0.md.
What PHPRegex provides
- 🏗️ Deep Parsing: Parse
/pattern/flagsinto a structured, typed AST. - 🧠 Logic Solver: Compare two regexes using NFA/DFA transformation (intersection, equivalence, subset). Works for patterns in the regular subset it supports — every character set asked from the running PCRE2 — and refuses the rest with the reason named.
- 🛡️ ReDoS Analysis: Prove the backtracking cost of a pattern — linear, polynomial or exponential — with the attack input when it is vulnerable, and replay that attack on the running PCRE. Outside the modelled subset, structural heuristics decide and say so.
- 🧹 Linter: Detect useless flags, redundant groups, and common mistakes via the CLI.
- 📖 Explanation: Explain patterns in plain English.
- 🔧 Visitor API: A flexible API for building custom regex tooling.
Philosophy & Accuracy
PHPRegex separates what it can guarantee from what is heuristic:
- Guaranteed: parsing and AST structure for the targeted PHP/PCRE version.
- Measured: syntax validation and error offsets follow PHP's engine; the PCRE2 conformance page publishes how closely, case by case.
- Proven: a ReDoS verdict marked
(proven)holds for one match attempt on a model of PCRE's backtracking;safe (proven)means no input makes that attempt backtrack beyond a linear number of steps. The ReDoS guide lists its limits. - Heuristic: patterns outside that model (backreferences, conditionals, recursion, …) are judged by structural rules, marked
(heuristic); treat those as potential risk unless confirmed. - Context matters: PCRE version, JIT, and backtrack/recursion limits change practical impact.
Tested against the real engine
Round-trip correctness (compile(parse(x)) behaves like the original) is
verified in CI by differential tests that compare PHPRegex's output against
PHP's native preg_match():
- A fixture of 212 PCRE patterns is checked for validity, match result,
and captured groups (
OfficialPcreComplianceTest). - Additional behavioral tests cover named groups, lookarounds, conditionals,
atomic groups, and other features (
BehavioralComplianceTest,AdvancedFeaturesComplianceTest). - PCRE2's own official test suite (10.48, pinned) is replayed at compile level under PHP's compile options: the compile verdict agrees on more than 4,100 of the roughly 4,400 extractable cases, and fewer than 100 patterns that PHP refuses to compile are accepted. The exact counts, the skipped cases and the fix plan are on the PCRE2 conformance page.
These tests compare against a limited set of subjects, so they catch clear regressions but are not a formal proof of full PCRE equivalence. There may be edge cases that the test suite does not yet cover.
Separately, the linter and ReDoS analyzer have been run over a corpus of
over 1,700 unique patterns collected from around 200 real-world PHP
projects (Symfony,
Laravel, Composer, PHPUnit, …). The lint results are in var/log/corpus.log.
This corpus run is not part of the automated CI differential test — it is
a snapshot used to validate that the lint and ReDoS rules produce sensible
output on real code.
The corpus checkouts themselves are not committed. corpus.json, at the root
next to composer.json, lists every repository with its URL, branch and the
commit it was pinned to, much like a lock file. bin/corpus manages the
checkouts, composer-style:
php bin/corpus install # rebuild corpus/ exactly at the pinned commits php bin/corpus update # prune, clone what is missing, then pull everything, # and write the new commits back to corpus.json php bin/corpus update --no-prune # keep checkouts that are no longer listed php bin/corpus update --add https://github.com/vendor/repo.git [--as path] [--branch main] php bin/corpus update --write-manifest # rewrite corpus.json from what is on disk
install never writes to corpus.json: delete corpus/ at any time and one
command rebuilds it, repository by repository, at the exact commits the
manifest pins. update is the only command that moves the pins. A checkout
with local changes is never reset or removed unless --force is given, and a
repository cloned by hand is removed on the next run unless it is added with
--add or listed with --write-manifest first.
Regenerate var/log/corpus.log after updating the corpus, from a terminal:
php bin/regex lint corpus/ --php-version=runtime --output=var/log/corpus.log
--php-version=runtime judges the corpus for the PHP running the command and
the PCRE2 it links; without it, the command would judge for the lowest PHP this
repository's composer.json allows.
Piping the command instead of running it in a terminal renders the severity badges without their padding, which reformats every severity line of the tracked file.
How to report a vulnerability responsibly
If you believe a pattern is exploitable:
- Run confirmed mode: it replays the attack on your PCRE and prints the input length that makes
preg_match()fail. - Include the pattern, input lengths, timings, JIT setting, and PCRE limits.
- Verify impact in the real code path before filing a security issue.
See SECURITY.md for reporting channels.
Safer rewrites (verify behavior)
These techniques reduce backtracking but can change matching behavior. Always validate with tests.
/(a+)+$/ -> /a+$/ (semantics often preserved, but verify captures)
/(a+)+$/ -> /a++$/ (possessive, no backtracking)
/(a|aa)+/ -> /a+/ (only if alternation is redundant)
/(a|aa)+/ -> /(?>a|aa)+/ (atomic, avoids backtracking)
How it works
Regex::parse()splits the literal into pattern and flags.- The lexer produces a token stream.
- The parser builds an AST (
RegexNode). - Visitors walk the AST to validate, explain, analyze, or transform.
For the full architecture, see docs/ARCHITECTURE.md.
CLI quick tour
# Parse and validate a pattern vendor/bin/regex parse '/^hello world$/' # Get plain English explanation vendor/bin/regex explain '/\d{4}-\d{2}-\d{2}/' # Check for ReDoS: a proven verdict, and the attack when vulnerable vendor/bin/regex analyze '/(a+)+$/' # Colorize pattern for better readability vendor/bin/regex highlight '/\d+/' # Lint your entire codebase vendor/bin/regex lint src/
PHP API at a glance
use PHPRegex\Toolkit\Regex; use PHPRegex\Redos\RedosMode; $regex = Regex::create([ 'runtime_pcre_validation' => true, ]); // Parse a pattern into AST $ast = $regex->parse('/^hello world$/i'); // Validate pattern syntax and semantics $result = $regex->validate('/(?<=test)foo/'); if (!$result->isValid) { echo $result->error; } // Check for ReDoS (theoretical by default) $analysis = $regex->redos('/(a+)+$/'); echo $analysis->severity->value; // 'critical' echo $analysis->headline(); // 'Exponential backtracking (proven)' echo $analysis->witness->render(); // '"a" x n . "!"': the input that triggers it // Optional: replay the attack on the running PCRE $confirmed = $regex->redos('/(a+)+$/', mode: RedosMode::Confirmed); echo $confirmed->isConfirmed() ? 'confirmed' : 'theoretical'; // 'confirmed' // Get human-readable explanation echo $regex->explain('/\d{4}-\d{2}-\d{2}/');
Integrations
PHPRegex integrates with common PHP tooling:
- Symfony bundle: the Symfony guide
- Laravel: the Laravel guide
- Language server: the language server guide
- PHPStan: enabled by extension-installer, or through
vendor/php-regex/regex-phpstan/extension.neon. It reports a pattern your target PHP refuses while the PHP running PHPStan compiles it; lint rules and ReDoS analysis come withrules.neon. See the PHPStan guide - GitHub Actions:
vendor/bin/regex lintin your CI pipeline
Performance
PHPRegex ships lightweight benchmark scripts in benchmarks/ to track parser, compiler, and formatter throughput.
- Run formatter benchmarks:
php benchmarks/benchmark_formatters.php - Run all benchmarks:
for file in benchmarks/benchmark_*.php; do echo "Running $file"; php "$file"; echo; done
Documentation
Start here:
Key references:
Contributing
Contributions are welcome! See CONTRIBUTING.md to get started.
# Set up development environment composer install # Run tests composer phpunit # Check code style composer phpcs # Run static analysis composer phpstan
Sponsors
If PHPRegex saves you time, consider sponsoring its maintenance.
Support
If you run into issues or have questions, please open an issue on GitHub: https://github.com/php-regex/php-regex/issues.
License
Released under the MIT License.
