Skip to content

About

[READ-ONLY] One entry point to every PHPRegex library: parse, validate, explain, check ReDoS, optimize, generate, transpile and lint. Split of php-regex/php-regex.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

PHPRegex Toolkit

Documentation Badge Author Badge GitHub Release Badge License Badge Packagist Downloads Badge GitHub Stars Badge Supported PHP Version Badge

PHPRegex Toolkit

One entry point to every PHPRegex analysis library: parse, validate, explain, check ReDoS, optimize, generate, transpile and lint.

Requires PHP 8.1+. MIT licensed.

Documentation: php-regex.com — the quick start opens with the facade at work.

Features

  • One facade, Regex::create(), over the seven analysis packages it installs: parser, explain, generator, linter, optimizer, redos, transpiler.
  • Parse strictly (errors throw) or tolerantly (errors come back in the result), with byte-offset tokens; validation reports the error offset, a caret snippet and a hint.
  • Explain a pattern in plain English, as text or HTML; highlight it for console or HTML output.
  • Check for ReDoS: a verdict proven on a model of PCRE's backtracking, with the attack input when the pattern is vulnerable; theoretical by default, or replayed on the running PCRE.
  • Optimize with rewrites proven equivalent to the original, generate a sample the running engine has verified to match, transpile to JavaScript or Python.

Installation

composer require php-regex/regex-toolkit

The seven libraries behind the facade come with it; nothing else to install. To drive the same analyses from the terminal, add php-regex/regex-cli. Upgrading from yoeunes/regex-parser 1.3? Run the Rector set shipped as Resources/rector/upgrade-2.0.php.

Configuration

Regex::create() reads an options array. Every key is validated; an unknown key throws.

Option Default What it does
max_pattern_length 100000 Longest pattern accepted, in bytes
max_lookbehind_length 255 Longest variable-length lookbehind; a fixed-length one is only limited by PCRE's 65535
max_recursion_depth 1024 How deep the parser may nest groups before it stops
cache ArrayCache null disables caching, a string names a directory for a FilesystemCache (it survives restarts), a CacheInterface is used as is
redos_ignored_patterns [] Patterns the ReDoS analysis skips
runtime_pcre_validation false Compile each pattern with the running PCRE to validate it; only when the running engine is the target (php_version unset or "runtime", no other pcre_version), otherwise create() throws
php_version, pcre_version running releases The PHP ("8.3", 80300, "runtime") and PCRE2 ("10.44") the patterns are judged for

Usage

Parse a pattern, then read it as an AST or as an English sentence:

use PHPRegex\Toolkit\Regex;

$regex = Regex::create();
$ast = $regex->parse('/\d{3}-\d{4}/'); // RegexNode AST
echo $regex->explain('/\d{3}-\d{4}/'), "\n";
Regex matches
    Character Type: A digit: [0-9] (exactly 3 times)
  '-'
    Character Type: A digit: [0-9] (exactly 4 times)

Validate a pattern before it reaches preg_match — the failing branch says where and why:

$result = $regex->validate('/(?<year>\d{4})-(\k<month>/');
echo $result->error, "\n";
echo $result->caretSnippet, "\n";
Expected ) at end of input (found eof)
Line 1: (?<year>\d{4})-(\k<month>
                                 ^

Check a pattern for ReDoS, theoretically by default, or with the attack replayed on the running PCRE:

use PHPRegex\Redos\RedosMode;

$analysis = $regex->redos('/(a+)+$/', mode: RedosMode::Confirmed);
echo $analysis->severity->value, "\n";
echo $analysis->headline(), "\n";
echo $analysis->witness->render(), "\n";
echo $analysis->isConfirmed() ? "confirmed\n" : "theoretical\n";
critical
Exponential backtracking (proven)
"a" x n . "!"
confirmed

Rewrite, sample and port a pattern:

echo $regex->optimize('/[0-90-9a-fA-F]+/')->optimized, "\n";
echo $regex->generate('/[a-z]+@[a-z]+\.[a-z]{2,}/'), "\n"; // random sample, one possible output
echo $regex->transpile('/^\d{4}-\d{2}-\d{2}$/', 'javascript')->constructor, "\n";
/[0-9A-Fa-f]+/
rcu@d.rx            # one possible output (random)
new RegExp("^\\d{4}-\\d{2}-\\d{2}$", "")

Or run every check at once — analyze() validates, lints, and reports ReDoS and optimizations in one report:

$report = $regex->analyze('/(a+)+b/');
foreach ($report->lintIssues as $issue) {
    echo $issue->id, ': ', $issue->message, "\n";
}
regex.lint.quantifier.nested: Nested quantifiers can cause catastrophic backtracking.
regex.lint.group.quantifiedCapture: Quantified capturing group "(...)" with "+": only the last iteration's capture is retained.

Documentation

Resources

Sponsors

Sponsor

If PHPRegex saves you time, consider sponsoring its maintenance.

License

MIT. See LICENSE.

About

[READ-ONLY] One entry point to every PHPRegex library: parse, validate, explain, check ReDoS, optimize, generate, transpile and lint. Split of php-regex/php-regex.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages