Troubleshooting Guide
This guide helps you resolve common issues when using PHPRegex.
Common Error Messages
“Pattern exceeds maximum length”
Problem:
PHPRegex\Parser\Exception\ResourceLimitException: Regex pattern exceeds maximum length of 100000 characters.
Causes:
- You’re parsing a generated pattern or loading patterns from database
- Pattern is too large for your application’s needs
Solutions:
- Increase limit:
$regex = Regex::create([
'max_pattern_length' => 1_000_000, // 1 million characters
]);
- Validate pattern before storing:
$regex = Regex::create();
$validation = $regex->validate($pattern);
if (!$validation->isValid) {
throw new \InvalidArgumentException("Invalid pattern: {$validation->error}");
}
- Use cache to skip parsing:
$regex = Regex::create([
'cache' => new \PHPRegex\Parser\Cache\FilesystemCache(__DIR__.'/var/cache/regex'),
// Pattern parsed once, cached for all workers
]);
- Check pattern length before parsing:
if (strlen($pattern) > 100_000) {
throw new \RuntimeException("Pattern too long: " . strlen($pattern) . " characters");
}
ReDoS False Positives
Problem: The ReDoS analyzer reports a pattern that works fine on your inputs.
Cause: Read the headline of the verdict first:
Exponential backtracking (proven)orPolynomial backtracking, degree N (proven): the analysis built an input that makes one match attempt blow up, printed asAttack:. Your inputs may never look like it; an attacker’s can. The verdict is about a model of PCRE’s backtracking: a lookaround it cannot evaluate, or an abstraction listed on theModel:line, can make it stricter than PCRE.Potential backtracking (heuristic): the pattern holds a construct outside the model (a backreference, a conditional, recursion, …), and structural rules decided. They are conservative by design.
Solutions:
- Run confirmed mode: the attack is replayed on your PCRE, without the JIT.
use PHPRegex\Redos\ConfirmationOptions;
use PHPRegex\Redos\RedosMode;
use PHPRegex\Redos\RedosSeverity;
use PHPRegex\Toolkit\Regex;
$result = Regex::create()->redos(
$pattern,
RedosSeverity::High,
RedosMode::Confirmed,
new ConfirmationOptions(backtrackLimit: 100_000),
);
echo 'Confirmed: ', $result->isConfirmed() ? 'yes' : 'no', "\n";
echo 'Confidence: ', $result->confidenceLevel()->value, "\n";
A proven verdict PCRE does not reproduce keeps medium confidence, and the CLI says Not reproduced on PCRE2 …. It is never an error in confirmed mode.
- Add to ignore list:
$regex = Regex::create([
'redos_ignored_patterns' => [
'your-safe-pattern-1',
'your-safe-pattern-2',
],
]);
- Check what the verdict is about:
vendor/bin/regex analyze '/(a{1,20})+$/'
# Status : Exponential backtracking (proven)
# Model: {1,20} at offset 1 analysed as {1,}
# Attack: "a" x n . "!"
Invalid Escape Sequences
Problem:
PHPRegex\Parser\Exception\LexerException: \c must be followed by a printable ASCII character.
Causes:
- An escape sequence cut short, as
\cdangling at the end of the pattern ('/ab\c/') - Using PCRE escape syntax your target PHP version does not support
- Typo in escape sequence
Solutions:
- Check the PCRE2 version your PHP runs:
php --ri pcre
# PCRE Library Version => 10.49 2026-09-28
- Use correct escape syntax:
// Wrong: '\c' dangles at the end of the pattern
$pattern = '/ab\c/';
// Correct: '\c' followed by a printable ASCII letter
$pattern = '/ab\cA/'; // matches "ab" . "\x01"
// Or use the hexadecimal escape instead
$pattern = '/\x01/'; // Hexadecimal (recommended)
$pattern = '/\x{41}'; // Hexadecimal with braces
- Fix common typos:
// Wrong Correct
'\N{' → '\N{U+XXXX}' // Full Unicode name
'\p{' → '\p{...}' // Property needs a letter or a braced name
'\u{' → '\x{...}' // Portable hex escape; \u{...} only exists on the newest PCRE2 releases
Run the validator to see these messages for your target PHP version:
vendor/bin/regex validate '/\u{41}/' --php-version 8.2
# Status : INVALID
# PCRE does not support the escape "\u" (\F, \L, \l, \N{name}, \U and \u are not supported).
Parser/Lexer Errors
“Unable to tokenize pattern at position X”
Problem:
PHPRegex\Parser\Exception\LexerException: Unable to tokenize pattern at position 15. Context: "abc..."
Causes:
- Malformed UTF-8 in a pattern read as UTF-8 (
/uor a leading(*UTF)) - Unsupported PCRE syntax
- Invalid character class nesting
Solutions:
- Check the encoding of a UTF-8 pattern. Without
/ua pattern is read as bytes, as PCRE reads it, and any byte is accepted (/\xE9/written with a raw byte is valid); with/uthe pattern must be valid UTF-8, as PCRE requires:
if (str_contains($flags, 'u') && !mb_check_encoding($pattern, 'UTF-8')) {
throw new \RuntimeException('A /u pattern must be valid UTF-8');
}
- Check for obvious syntax errors:
// Check for unmatched brackets
$openBrackets = substr_count($pattern, '[');
$closeBrackets = substr_count($pattern, ']');
if ($openBrackets !== $closeBrackets) {
throw new \RuntimeException('Unmatched character class brackets');
}
// Check for unmatched parentheses
$openParens = substr_count($pattern, '(');
$closeParens = substr_count($pattern, ')');
if ($openParens !== $closeParens) {
throw new \RuntimeException('Unmatched parentheses');
}
Performance Issues
Slow Pattern Matching
Problem: Regex matching is taking too long on your input.
Diagnosis:
- Check for catastrophic backtracking:
vendor/bin/regex analyze '/(a+)+$/' --redos-mode=confirmed
# A "Replayed on PCRE2 …: preg_match fails from length N" line means the
# attack printed on the "Attack:" line makes PCRE give up: a ReDoS issue
- Check for unnecessary backtracking:
vendor/bin/regex debug '/.*a.*b.*a.*/'
# Heatmap will show where backtracking occurs
- Test with realistic data:
// Test with your actual data, not edge cases
$realisticData = $yourActualProductionData();
$start = microtime(true);
preg_match($yourPattern, $realisticData);
$elapsed = microtime(true) - $start;
echo "Time: " . ($elapsed * 1000) . " ms\n";
- Use caching:
$regex = Regex::create([
'cache' => new \PHPRegex\Parser\Cache\FilesystemCache(__DIR__.'/var/cache/regex'),
]);
// Parse once, reuse across requests
$ast = $regex->parse($pattern);
Cache Issues
Cache Not Working
Problem: Cache is not being used, patterns are parsed on every request.
Solutions:
- Verify cache is configured:
$regex = Regex::create([
'cache' => new \PHPRegex\Parser\Cache\FilesystemCache(__DIR__.'/var/cache/regex'),
]);
// Test
$ast1 = $regex->parse('/\d+/');
$ast2 = $regex->parse('/\d+/'); // Should use cache
echo "Cache enabled: " . ($regex->getCacheStats()['hits']) . " hits\n";
- Clear cache for long-running processes:
# CLI
vendor/bin/regex clear-cache
# Programmatically
$cache = $regex->getCache();
if ($cache instanceof RemovableCacheInterface) {
$cache->clear();
}
- Check cache key generation:
// Cache keys include pattern, flags, and PHP version
// Make sure these are correct for your use case
$cacheKey = $cache->generateKey('/\d+/i');
echo "Cache key: {$cacheKey}\n";
CLI Issues
Command Not Found
Problem:
Command "regex:test" is not defined.
Solutions:
- Use
helpcommand to list available commands:
vendor/bin/regex help
- Update to latest version:
composer update
# or
composer require --dev php-regex/regex-cli:^2.0
- Check if command is deprecated:
vendor/bin/regex help | grep -i deprecated
# Use alternative command
A Very Large File Reports No Patterns
Problem: regex lint finds nothing in a multi-megabyte generated file —
a call map, a fixture dump, a compiled container.
Cause: reading such a file into tokens costs around 55 times its size in
memory, and building an AST about twice that. A 2 MB file therefore needs more
than the default memory_limit of 128M. Rather than let the process die and
lose the results of every other file, the extractor skips the file.
Solution: give the process more memory:
php -d memory_limit=1G vendor/bin/regex lint src/
Or exclude the file, since generated code rarely carries patterns worth linting:
{
"exclude": ["src/Generated"]
}
Integration Issues
Symfony Bridge Not Working
Problem: Symfony bundle commands not found or not working.
Solutions:
- Verify bundle is installed:
composer show php-regex/regex-symfony
# Check if the Symfony bundle is listed
- Register bundle in Symfony:
// config/bundles.php
return [
PHPRegex\Symfony\PHPRegexBundle::class => ['dev' => true, 'test' => true],
];
- Clear Symfony cache:
php bin/console cache:clear
# Or
rm -rf var/cache/*
Testing Issues
Tests Failing After Upgrade
Problem: Tests pass with one version but fail with newer version.
Solutions:
- Run tests after upgrade:
composer phpunit
# Run specific test file
composer phpunit tests/Unit/Parser/ParserTest.php
- Update test expectations:
// Check if test needs updating after API changes
// Before
public function test_parse_handles_new_feature(): void
{
$result = $this->regex->parse('/new-syntax/');
$this->assertInstanceOf(NewNode::class, $result->child);
}
// After API change
public function test_parse_handles_new_feature(): void
{
$result = $this->regex->parse('/new-syntax/');
$this->assertInstanceOf(DifferentNode::class, $result->child); // Updated expectation
}
- Check deprecation warnings:
# Run with error reporting
composer phpunit --display-deprecations
# Fix deprecations before running full test suite
Getting Help
Documentation Not Clear
Problem: Documentation doesn’t explain what you need to do.
Solutions:
-
Check the Quick Start guide.
-
Check the reference index: rules, API, diagnostics and JSON output each have their own page.
-
Look at examples:
ls -la examples/
php examples/basic/validate.php
- Search issues:
# Search GitHub issues
https://github.com/php-regex/php-regex/issues
# Create new issue with question
- Join the community on GitHub Discussions.
Additional Resources
- Quick Start Guide
- API Reference
- ReDoS Guide
- Architecture Documentation
- Contributing Guide
- Upgrading to 2.0
Still Need Help?
- Check the GitHub Issues for similar problems
- Search for your specific error message in the codebase
- Enable verbose mode for more details:
vendor/bin/regex analyze '/pattern/' -v