rectorphp/rector · error · InvalidEregException
an invalid character range %d-%d"
Error message
an invalid character range %d-%d"
What it means
processSquareBracket() parsed a range 'a-b' inside a character class and found ord($a) > ord($b) — the range is inverted (EregToPcreTransformer.php:226). POSIX ERE requires the start code point to be <= the end code point, so '[z-a]' is invalid and InvalidEregException('an invalid character range %d-%d"') is thrown with the offending code points, aborting the ereg-to-preg conversion.
Source
Thrown at rules/Php70/EregToPcreTransformer.php:226
do {
if ($s[$i] === '[' && $i + 1 < $l && strpos('.=:', $s[$i + 1]) !== \false) {
/** @var string $cls */
[$cls, $i] = $this->processCharacterClass($s, $i, $cls);
} else {
$a = $s[$i];
++$i;
if ($a === '-' && !$start && ($i >= $l || $s[$i] !== ']')) {
throw new InvalidEregException('"-" is invalid for the start character in the brackets');
}
if ($i < $l && $s[$i] === '-') {
$b = $s[++$i];
if ($b === ']') {
$cls .= $this->_ere2pcre_escape($a) . '\-';
break;
}
if (ord($a) > ord($b)) {
$errorMessage = sprintf('an invalid character range %d-%d"', (int) $a, (int) $b);
throw new InvalidEregException($errorMessage);
}
$cls .= $this->_ere2pcre_escape($a) . '-' . $this->_ere2pcre_escape($b);
++$i;
} else {
$cls .= $this->_ere2pcre_escape($a);
}
}
$start = \false;
} while ($i < $l && $s[$i] !== ']');
return [$cls, $i];
}
private function _ere2pcre_escape(string $content): string
{
if ($content === "\x00") {
throw new InvalidEregException('a literal null byte in the regex');
}
if (strpos('\^$.[]|()?*+{}-/', $content) !== \false) {
return '\\' . $content;View on GitHub (pinned to 408fcb0ff1)
Solutions
- Swap the endpoints in the flagged pattern: '[z-a]' -> '[a-z]'.
- If the intent was 'either case plus punctuation', write it explicitly, e.g. '[A-Za-z]'.
- Re-run rector; preflight-scan literal ereg patterns for '-'-separated pairs inside classes where the left code point exceeds the right.
Example fix
// before - inverted range
ereg('[z-a]', $subject);
// after - correct order
ereg('[a-z]', $subject); // rector: preg_match('#[a-z]#m', $subject) Defensive patterns
Strategy: validation
Validate before calling
// quick lint: every a-b range inside a class must satisfy ord(a) <= ord(b)
function rangesOrdered(string $class): bool {
return !preg_match('~(.)-(.)~', $class, $m) || ord($m[1]) <= ord($m[2]);
} Try / catch
try {
$pcre = $eregToPcreTransformer->transform($pattern, $ignoreCase);
} catch (InvalidEregException $e) {
// inverted range like [z-a] — swap the endpoints and re-run
continue;
} Prevention
- Write ranges low-to-high by code point ('[a-z]', '[0-9]', '[A-Za-z]').
- Remember case-sensitivity: 'a-Z' is invalid because ord('Z') < ord('a').
- Lint class ranges for endpoint order before the Php70 migration run.
When it happens
Trigger: Rector (Php70 set / EregToPregMatchRector, target PHP >= 7.0) processes an ereg-family call whose literal pattern contains an inverted class range, e.g. `ereg('[z-a]', $s)`, `split('[Z-a]', $s)` is fine (Z < a) but `'[9-0]'` or `'[b-A]'` throw. The sprintf fills in (int) ord values of both endpoints.
Common situations: Typos when hand-writing ranges; 'make it match everything A-z' attempts that swap the endpoints; ranges copied between case-sensitive and case-insensitive contexts (eregi) where the author reversed letters expecting case folding.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- "[" does not have a matching "]"
- "-" is invalid for the start character in the brackets
- an invalid or unsupported character class [%s]
- unescaped metacharacter ")"
- unescaped metacharacter "%s"
AI-assisted analysis of rectorphp/rector@408fcb0ff1 (2026-08-21).
Data as JSON: /api/errors/d4b97d62a7ac83a7.
Report an issue: GitHub.