rectorphp/rector · error · InvalidEregException
unescaped metacharacter "%s"
Error message
unescaped metacharacter "%s"
What it means
This InvalidEregException is thrown when the ERE-to-PCRE converter meets a quantifier character '*', '+' or '?' at a position where there is no preceding atom to quantify — the start of the pattern or the start of a branch after '|', or two quantifiers in a row. In POSIX ERE such input is invalid, so EregToPcreTransformer refuses it (EregToPcreTransformer.php:125) and the ereg-to-preg conversion stops.
Source
Thrown at rules/Php70/EregToPcreTransformer.php:125
$cls = '';
if ($i < $l && $content[$i] === '^') {
$cls .= '^';
++$i;
}
if ($i >= $l) {
throw new InvalidEregException('"[" does not have a matching "]"');
}
$start = \true;
$i = (int) $i;
[$cls, $i] = $this->processSquareBracket($content, $i, $l, $cls, $start);
if ($i >= $l) {
throw new InvalidEregException('"[" does not have a matching "]"');
}
$r[$rr] .= '[' . $cls . ']';
} elseif ($char === ')') {
break;
} elseif (in_array($char, ['*', '+', '?'], \true)) {
throw new InvalidEregException('unescaped metacharacter "' . $char . '"');
} elseif ($char === '{') {
if ($i + 1 < $l && strpos('0123456789', $content[$i + 1]) !== \false) {
$r[$rr] .= '\{';
} else {
throw new InvalidEregException('unescaped metacharacter "' . $char . '"');
}
} elseif ($char === '.') {
$r[$rr] .= $char;
} elseif ($char === '^' || $char === '$') {
$r[$rr] .= $char;
++$i;
continue;
} elseif ($char === '|') {
if ($r[$rr] === '') {
$normalizeUnprintableChar = \true;
}
$r[] = '';
++$rr;View on GitHub (pinned to 408fcb0ff1)
Solutions
- Fix the pattern in the source file Rector reported: remove the orphan quantifier ('*abc' -> 'abc') or escape it ('\*\.txt') if it is meant literally.
- If the string was a glob, not a regex, switch the legacy call to fnmatch()-style logic instead of letting rector convert it to preg_match.
- Re-run rector; preflight-scan literal ereg/split patterns for leading or doubled quantifiers in big migrations.
Example fix
// before - quantifier with no atom (glob habit)
ereg('*.txt', $filename);
// after - escaped literal
ereg('\*\.txt', $filename); // rector: preg_match('#\*\.txt#m', $filename) Defensive patterns
Strategy: try-catch
Validate before calling
// quick lint: quantifier at pattern start or after '|' / another quantifier
function hasOrphanQuantifier(string $p): bool {
return (bool) preg_match('~(^|\||[+*?])[+*?]~', $p);
} Try / catch
try {
$pcre = $eregToPcreTransformer->transform($pattern, $ignoreCase);
} catch (InvalidEregException $e) {
// '*'/'+'/'?' with no atom to quantify — remove or escape it, then re-run
continue;
} Prevention
- Remember ereg()/split() take POSIX ERE, not globs: '*.txt' is invalid, '\*\.txt' is correct.
- Escape literal *, +, ? characters in legacy patterns.
- Lint literal patterns for quantifiers at start-of-pattern or start-of-branch before migrations.
When it happens
Trigger: Rector (Php70 set / EregToPregMatchRector, target PHP >= 7.0) encounters an ereg-family call whose literal pattern begins with or contains a bare quantifier, e.g. `ereg('*abc', $s)`, `split('a|+b', $s)`, or `ereg('a**', $s)` where the second '*' is parsed as a new atom. The atom loop sees '*', '+' or '?' where an atom is required and throws with the offending character in the message.
Common situations: Legacy grep/sed-style patterns pasted into ereg calls where a leading '*' was 'grep-ish' habit; broken edits that deleted the atom but kept the quantifier; glob-like patterns ('*.txt') mistakenly used as regexes in split()/ereg().
Related errors
- "{" does not have a matching "}"
- an invalid bound
- unescaped metacharacter ")"
- "[" does not have a matching "]"
- an invalid escape sequence at the end
AI-assisted analysis of rectorphp/rector@408fcb0ff1 (2026-08-21).
Data as JSON: /api/errors/50048422e3e6b453.
Report an issue: GitHub.