rectorphp/rector · error · InvalidEregException
unescaped metacharacter ")"
Error message
unescaped metacharacter ")"
What it means
EregToPcreTransformer::transform() converts a POSIX ERE pattern (from an ereg()/eregi()/ereg_replace()/split()/spliti() call handled by EregToPregMatchRector) into a PCRE pattern. After the recursive conversion returns, it checks that the whole input was consumed; leftover input means the pattern contained an unbalanced closing ')' at the top level, which is invalid ERE. The resulting InvalidEregException aborts the Rector run on that file.
Source
Thrown at rules/Php70/EregToPcreTransformer.php:81
* Single type is chosen to prevent every regular with different delimiter.
*/
public function __construct(string $pcreDelimiter = '#')
{
$this->pcreDelimiter = $pcreDelimiter;
}
// converts the ERE $s into the PCRE $r. triggers error on any invalid input.
public function transform(string $content, bool $ignorecase): string
{
if ($ignorecase) {
if (isset($this->icache[$content])) {
return $this->icache[$content];
}
} elseif (isset($this->cache[$content])) {
return $this->cache[$content];
}
[$r, $i] = $this->_ere2pcre($content, 0);
if ($i !== strlen($content)) {
throw new InvalidEregException('unescaped metacharacter ")"');
}
if ($ignorecase) {
return $this->icache[$content] = $this->pcreDelimiter . $r . $this->pcreDelimiter . 'mi';
}
return $this->cache[$content] = $this->pcreDelimiter . $r . $this->pcreDelimiter . 'm';
}
/**
* Recursively converts ERE into PCRE, starting at the position $i.
*
* @return float[]|int[]|string[]
*/
private function _ere2pcre(string $content, int $i): array
{
$r = [''];
$rr = 0;
$l = strlen($content);
$normalizeUnprintableChar = \false;
while ($i < $l) {View on GitHub (pinned to 408fcb0ff1)
Solutions
- Find the failing ereg call (the Rector error output names the file) and fix the literal pattern: escape the parenthesis as '\)' if it is meant literally, or delete the stray one.
- Re-run rector to confirm the pattern now converts cleanly.
- If the pattern is intentionally odd, convert that call to preg_match() by hand and let rector skip it.
- Before large migrations, preflight-scan the codebase for ereg/split calls and validate their literal patterns with EregToPcreTransformer::transform() in a try/catch.
Example fix
// before - unbalanced top-level ')'
ereg('abc)', $subject);
// after - escaped literal parenthesis
ereg('abc\)', $subject); // rector rewrites to: preg_match('#abc\)#m', $subject) Defensive patterns
Strategy: try-catch
Validate before calling
// preflight: validate literal ereg patterns before running rector
use Rector\Php70\EregToPcreTransformer;
use Rector\Php70\Exception\InvalidEregException;
$transformer = new EregToPcreTransformer();
try {
$transformer->transform($literalPattern, false);
} catch (InvalidEregException $e) {
echo "Fix pattern before rector run: {$literalPattern} — {$e->getMessage()}\n";
} Type guard
function isConvertibleEreg(string $pattern): bool
{
try {
(new \Rector\Php70\EregToPcreTransformer())->transform($pattern, false);
return true;
} catch (\Rector\Php70\Exception\InvalidEregException $e) {
return false;
}
} Try / catch
try {
$pcre = $eregToPcreTransformer->transform($pattern, $ignoreCase);
} catch (\Rector\Php70\Exception\InvalidEregException $e) {
// log the file/pattern, fix the source literal by hand, then re-run rector
$this->errors[] = [$file, $pattern, $e->getMessage()];
continue;
} Prevention
- Before running the Php70 set, grep the codebase for ereg/eregi/ereg_replace/eregi_replace/split/spliti and eyeball each literal pattern.
- Balance parentheses/brackets in legacy patterns; a stray ')' at top level is exactly what this error reports.
- Validate suspicious literals up front with EregToPcreTransformer::transform() inside a try/catch harness.
When it happens
Trigger: Running rector with the Php70 set (or EregToPregMatchRector enabled) at a target PHP >= 7.0 on code containing an ereg-family call whose first argument is a string literal with a stray ')', e.g. `ereg('abc)', $s)` or `split(')|', $s)`. The inner parser breaks out at the ')' and transform() detects $i !== strlen($content).
Common situations: Migrating a legacy PHP 5 codebase to PHP 7+ where hand-written ereg patterns were never validated; patterns built by string concatenation that ended up with an extra ')'; copy-pasted legacy regexes that only worked because ereg tolerated them or were never executed.
Related errors
- "[" does not have a matching "]"
- unescaped metacharacter "%s"
- an invalid escape sequence at the end
- empty regular expression or branch
- "(" does not have a matching ")"
AI-assisted analysis of rectorphp/rector@408fcb0ff1 (2026-08-21).
Data as JSON: /api/errors/805b12bf3ccec70f.
Report an issue: GitHub.