Quick Start Guide

This guide gets you from installation to a first analysis in a few steps. It is intentionally brief; the tutorial covers concepts in depth.

This is the fast hands-on tour: every step is a command or snippet you can run as-is. If you are completely new to regex, the tutorial is the place to start; it assumes nothing. If you already know regex, you can jump to Advanced Features.

What This Guide Covers

  • Install PHPRegex.
  • Use the CLI for quick analysis.
  • Parse and validate patterns in PHP.
  • Explain patterns in plain English.
  • Check a pattern for ReDoS, and get the input that triggers it.
  • Build custom analysis tools.

Requires PHP 8.2 or later and the mbstring extension (PCRE ships with PHP).

Installation

composer require --dev php-regex/php-regex:2.x-dev

regex-toolkit is the parser and the PHP API. The monorepo install above also provides the vendor/bin/regex binary used throughout this guide; from the 2.0.0 release, the CLI is its own package (composer require --dev php-regex/regex-cli).

No Composer? The CLI also ships as a self-contained PHAR:

curl -Ls https://github.com/php-regex/php-regex/releases/latest/download/regex.phar \
  -o ~/.local/bin/regex
chmod +x ~/.local/bin/regex

To explore what a pattern matches without installing anything, use https://regex101.com in PCRE2 mode — for the semantics. Proven verdicts, witnesses and equivalence stay PHPRegex’s job: vendor/bin/regex analyze '/(a+)+$/'.

How PHPRegex Works (Short Version)

You do not need the pipeline details to use the API: the literal is split from its flags, lexed, parsed into an immutable AST, and visitors walk that AST to validate, explain, analyze, or transform. For background, see What is an AST?.

CLI Quick Start

The CLI gives you direct feedback. Try these commands:

# 1. Explain a pattern in plain English
vendor/bin/regex explain '/\d{4}-\d{2}-\d{2}/'

# 2. Visualize the pattern structure
vendor/bin/regex diagram '/\d{4}-\d{2}-\d{2}/'

# 3. Check for ReDoS: a proven verdict and, when vulnerable, the attack
vendor/bin/regex analyze '/(a+)+$/'

# 4. Colorize the pattern for better readability
vendor/bin/regex highlight '/\d{4}-\d{2}-\d{2}/'

# 5. Lint your entire codebase
vendor/bin/regex lint src/

Example output:

$ vendor/bin/regex explain '/\d{4}-\d{2}-\d{2}/' --no-visuals
Regex matches
    Character Type: A digit: [0-9] (exactly 4 times)
  '-'
    Character Type: A digit: [0-9] (exactly 2 times)
  '-'
    Character Type: A digit: [0-9] (exactly 2 times)

Comparing Patterns

PHPRegex can compare two patterns as mathematical sets of strings.

# Intersection: do the patterns overlap?
vendor/bin/regex compare '/edit/' '/[a-z]+/'

# Subset: is pattern 1 fully contained in pattern 2?
vendor/bin/regex compare '/edit/' '/[a-z]+/' --method=subset

# Equivalence: do both patterns accept the same strings?
vendor/bin/regex compare '/[0-9]+/' '/\d+/' --method=equivalence

PHP API: Five Essential Operations

1. Parse a pattern (turn regex into structured data)

use PHPRegex\Toolkit\Regex;

$regex = Regex::create();
$ast = $regex->parse('/\d{3}-\d{4}/');

// Now you have a structured AST (Abstract Syntax Tree)
// You can analyze, transform, or validate it

Use when you need to understand or analyze pattern structure.

Learn more: What is an AST?

2. Validate a pattern (check for errors)

use PHPRegex\Toolkit\Regex;

$regex = Regex::create();
$result = $regex->validate('/(?<year>\d{4})-(?<month>\d{2})/');

if ($result->isValid) {
    echo "Pattern is valid.\n";
    echo "Complexity score: " . $result->complexityScore . "\n";
} else {
    echo "Error: " . $result->error . "\n";
    echo "Hint: " . $result->hint . "\n";
}

Checks performed:

  • Syntax errors (missing brackets, invalid escapes)
  • Invalid backreferences
  • Variable-length lookbehinds
  • Invalid Unicode properties

3. Explain a pattern (get a plain English description)

use PHPRegex\Toolkit\Regex;

$regex = Regex::create();
$explanation = $regex->explain('/(?<email>\w+@\w+\.\w+)/');

echo $explanation;

Example output:

Regex matches
  Capturing group (named: 'email')
        Character Type: A word character: [a-zA-Z_0-9] (one or more times)
    '@'
        Character Type: A word character: [a-zA-Z_0-9] (one or more times)
    '.'
        Character Type: A word character: [a-zA-Z_0-9] (one or more times)
  End group

Use when documenting patterns, doing code reviews, or teaching regex.

4. Check for ReDoS (theoretical by default)

use PHPRegex\Toolkit\Regex;
use PHPRegex\Redos\RedosMode;

$regex = Regex::create();

// A vulnerable pattern: the verdict is proven, and comes with the attack
$analysis = $regex->redos('/(a+)+b/');
echo $analysis->severity->value, "\n";   // critical
echo $analysis->headline(), "\n";        // Exponential backtracking (proven)
echo $analysis->witness->render(), "\n"; // "a" x n . "!b"

// A safe pattern, proven safe
$analysis = $regex->redos('/a+b/');
echo $analysis->headline(), "\n";        // safe (proven)

// Optional: replay the attack on the running PCRE
$confirmed = $regex->redos('/(a+)+b/', mode: RedosMode::Confirmed);
echo $confirmed->isConfirmed() ? "confirmed\n" : "theoretical\n"; // confirmed

ReDoS (Regular Expression Denial of Service) is a performance risk where certain inputs can make a backtracking engine take a very long time; in PHP, preg_match() then gives up and returns false. validate() does not check for it: call redos(). The ReDoS guide explains the verdicts.

Learn more: ReDoS Deep Dive

5. Highlight patterns (make regex readable)

use PHPRegex\Toolkit\Regex;
use PHPRegex\Explain\Highlighter\ConsoleHighlighter;

$regex = Regex::create();
$ast = $regex->parse('/^[0-9]+(\w+)$/');

$consoleOutput = $ast->accept(new ConsoleHighlighter());
echo $consoleOutput;

This is useful for documentation and reviews.

Practical Use Cases

1. Parse and Understand Complex Patterns

$regex = Regex::create();
$ast = $regex->parse('/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/');

// Now you can analyze the email validation pattern

2. Validate User Input Patterns

$regex = Regex::create();
$userPattern = $_POST['regex_pattern'];

$result = $regex->validate($userPattern);
if (!$result->isValid) {
    die("Invalid pattern: " . $result->error);
}

3. Document Your Regex Patterns

function documentPattern(string $pattern, string $description): void
{
    $regex = Regex::create();
    $explanation = $regex->explain($pattern);
    
    echo "### $description\n";
    echo "Pattern: $pattern\n";
    echo "Explanation: $explanation\n";
}

documentPattern('/\d{4}-\d{2}-\d{2}/', 'Date format');

4. Find ReDoS in Your Codebase

# Scan your entire project
vendor/bin/regex lint src/ --redos --no-lint --no-optimize

5. Generate Test Data

$regex = Regex::create();
$sample = $regex->generate('/\d{3}-[A-Z]{2}/');
echo $sample;  // Example: "123-AB"

6. Optimize Patterns

$regex = Regex::create();
$literals = $regex->literals('/prefix-\d+-suffix/');

// Extract fixed parts for optimization
$prefix = $literals->literalSet->getLongestPrefix();
$suffix = $literals->literalSet->getLongestSuffix();

7. Build Custom Analysis Tools

// Create a visitor to count quantifiers
// AbstractTraversingVisitor visits the children of every node you do not override
class QuantifierCounter extends \PHPRegex\Parser\AbstractTraversingVisitor
{
    private int $count = 0;

    public function visitQuantifier(\PHPRegex\Parser\Node\QuantifierNode $node)
    {
        $this->count++;

        return parent::visitQuantifier($node);
    }

    public function getCount(): int { return $this->count; }
}

$regex = Regex::create();
$ast = $regex->parse('/a+b*c?/');
$counter = new QuantifierCounter();
$ast->accept($counter);
echo "Quantifiers: " . $counter->getCount(); // "3"

Learn more: Understanding Visitors

Common Pattern Examples

Each block assumes the primer below — copy it once, then any block works on its own:

use PHPRegex\Toolkit\Regex;

$regex = Regex::create();

Email Validation

$pattern = '/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/';
$result = $regex->validate($pattern);

URL Matching

$pattern = '/^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&\/\/=]*)$/';
$result = $regex->validate($pattern);

Phone Number (US)

$pattern = '/^\+?1?\s*\(?([0-9]{3})\)?\s*-?\s*([0-9]{3})\s*-?\s*([0-9]{4})$/';
$result = $regex->validate($pattern);

Date (YYYY-MM-DD)

$pattern = '/^(?<year>\d{4})-(?<month>0[1-9]|1[0-2])-(?<day>0[1-9]|[12][0-9]|3[01])$/';
$result = $regex->validate($pattern);

Error Handling

use PHPRegex\Toolkit\Regex;
use PHPRegex\Parser\Exception\ParserException;

$regex = Regex::create();

try {
    $ast = $regex->parse('/(?P<1x>a)/');  // Invalid group name
} catch (ParserException $e) {
    echo "Parse error: " . $e->getMessage() . "\n";
    echo "Position: " . $e->getPosition() . "\n";
    echo "Snippet:\n" . $e->getSnippet() . "\n";
}

Performance Tips

  1. Parse Once, Reuse AST: Don’t re-parse the same pattern repeatedly
  2. Validate Early: Check patterns during development, not in production
  3. Cache Results: Store validated patterns and analysis results
  4. Reuse Regex Instance: Create one Regex instance and reuse it
  5. Avoid Complex Patterns: Simple patterns parse faster

Advanced Features

Working with Named Groups

$regex = Regex::create();
$ast = $regex->parse('/(?<first>\w+)\s+(?<last>\w+)/');

// AST contains named group information
// Use PatternPrinter to regenerate pattern
// Or custom visitor to extract group names

Conditional Patterns

$pattern = '/(a)(?(1)b|c)/';  // If group 1 matches, then 'b', else 'c'
$result = $regex->validate($pattern);

Recursion

$pattern = '/\((?:[^()]|(?R))*\)/';
$result = $regex->validate($pattern);

Atomic Groups (Performance)

$pattern = '/(?>a+)b/';
$result = $regex->validate($pattern);

Possessive Quantifiers (Performance)

$pattern = '/a++b/';
$result = $regex->validate($pattern);

Next Steps

Now that you’ve seen what PHPRegex can do, here’s where to go next:

For tool authors:

For app developers:

New to regex:

Reference:

Getting Help

Issues, real-world examples and the playground are listed on the documentation home page.

Edit on GitHub