rectorphp/rector · error · InvalidEregException
"(" does not have a matching ")"
Error message
"(" does not have a matching ")" What it means
EregToPcreTransformer::processBracket() parses a '(' group by recursively converting the sub-expression and then requiring the next character to be the closing ')'. If the input ends or the character differs (e.g. a '|' consumed by the recursive branch split), the group is unterminated and InvalidEregException('"(" does not have a matching ")"') is thrown (EregToPcreTransformer.php:194), stopping the ereg-to-preg conversion.
Source
Thrown at rules/Php70/EregToPcreTransformer.php:194
if ($normalizeUnprintableChar) {
$content = str_replace("\f", '\\\\f', $content);
}
return str_replace($this->pcreDelimiter, '\\' . $this->pcreDelimiter, $content);
}
/**
* @param array<int, mixed> $r
*/
private function processBracket(string $content, int $i, int $l, array &$r, int $rr): int
{
// special case
if ($i + 1 < $l && $content[$i + 1] === ')') {
$r[$rr] .= '()';
++$i;
} else {
$position = $i + 1;
[$t, $ii] = $this->_ere2pcre($content, $position);
if ($ii >= $l || $content[$ii] !== ')') {
throw new InvalidEregException('"(" does not have a matching ")"');
}
$r[$rr] .= '(' . $t . ')';
$i = $ii;
}
// retype
$i = (int) $i;
return $i;
}
/**
* @return float[]|int[]|string[]
*/
private function processSquareBracket(string $s, int $i, int $l, string $cls, bool $start): array
{
do {
if ($s[$i] === '[' && $i + 1 < $l && strpos('.=:', $s[$i + 1]) !== \false) {
/** @var string $cls */
[$cls, $i] = $this->processCharacterClass($s, $i, $cls);
} else {View on GitHub (pinned to 408fcb0ff1)
Solutions
- Balance the group in the file Rector reported: '(abc' -> '(abc)'.
- While there, check the whole pattern for other unbalanced '('/')' — the fix usually surfaces more.
- Re-run rector to confirm conversion; in bulk migrations preflight literal ereg patterns with a bracket-balance check before running the Php70 set.
Example fix
// before - unclosed group
ereg('(abc', $subject);
// after - closed group
ereg('(abc)', $subject); // rector: preg_match('#(abc)#m', $subject) Defensive patterns
Strategy: try-catch
Validate before calling
function groupsBalanced(string $p): bool {
$depth = 0;
for ($i = 0, $n = strlen($p); $i < $n; ++$i) {
if ($p[$i] === '\\') { ++$i; continue; }
if ($p[$i] === '(') { ++$depth; }
elseif ($p[$i] === ')') { --$depth; if ($depth < 0) return false; }
}
return $depth === 0;
} Try / catch
try {
$pcre = $eregToPcreTransformer->transform($pattern, $ignoreCase);
} catch (InvalidEregException $e) {
// group opened but never closed — balance '(' and ')' then re-run
continue;
} Prevention
- Balance-check every literal pattern that contains '(' before running the Php70 set.
- Prefer non-capturing grouping habits when hand-writing regexes to keep intent clear.
- Batch-validate patterns with the transformer so all unbalanced groups surface in one report.
When it happens
Trigger: Rector (Php70 set / EregToPregMatchRector, target PHP >= 7.0) processes an ereg-family call with a literal pattern containing an unclosed group: `ereg('(abc', $s)`, `split('(a|b', $s)`, or nested cases like '((a)' where an inner '(' is never closed. The recursive _ere2pcre() returns an index that is not a ')' and the exception fires.
Common situations: Legacy patterns with a missing ')' after hand edits; alternations where the author intended '(a|b)' but wrote '(a|b' ; long patterns where balancing brackets by eye failed; patterns converted from other regex dialects that group differently.
Related errors
- unescaped metacharacter ")"
- "[" does not have a matching "]"
- unescaped metacharacter "%s"
- an invalid escape sequence at the end
- empty regular expression or branch
AI-assisted analysis of rectorphp/rector@408fcb0ff1 (2026-08-21).
Data as JSON: /api/errors/3c716c8a85e4c368.
Report an issue: GitHub.