{"record":{"id":"4c64fb9e32bc8169","repo":"rectorphp/rector","slug":"does-not-have-a-matching-4c64fb","errorCode":null,"errorMessage":"\"{\" does not have a matching \"}\"","messagePattern":"\"\\{\" does not have a matching \"\\}\"","errorType":"validation","errorClass":"InvalidEregException","httpStatus":null,"severity":"error","filePath":"rules/Php70/EregToPcreTransformer.php","lineNumber":255,"sourceCode":"    }\n    private function _ere2pcre_escape(string $content): string\n    {\n        if ($content === \"\\x00\") {\n            throw new InvalidEregException('a literal null byte in the regex');\n        }\n        if (strpos('\\^$.[]|()?*+{}-/', $content) !== \\false) {\n            return '\\\\' . $content;\n        }\n        return $content;\n    }\n    /**\n     * @param array<int, mixed> $r\n     */\n    private function processCurlyBracket(string $s, int $i, array &$r, int $rr): int\n    {\n        $ii = strpos($s, '}', $i);\n        if ($ii === \\false) {\n            throw new InvalidEregException('\"{\" does not have a matching \"}\"');\n        }\n        $start = $i + 1;\n        $length = $ii - ($i + 1);\n        $bound = Strings::substring($s, $start, $length);\n        $matches = Strings::match($bound, self::BOUND_REGEX);\n        if ($matches === null) {\n            throw new InvalidEregException('an invalid bound');\n        }\n        if (isset($matches[self::MAXIMAL_NUMBER_PART])) {\n            if ($matches[self::MINIMAL_NUMBER_PART] > $matches[self::MAXIMAL_NUMBER_PART]) {\n                throw new InvalidEregException('an invalid bound');\n            }\n            $r[$rr] .= '{' . $matches[self::MINIMAL_NUMBER_PART] . ',' . $matches[self::MAXIMAL_NUMBER_PART] . '}';\n        } elseif (isset($matches['comma'])) {\n            $r[$rr] .= '{' . $matches[self::MINIMAL_NUMBER_PART] . ',}';\n        } else {\n            $r[$rr] .= '{' . $matches[self::MINIMAL_NUMBER_PART] . '}';\n        }","sourceCodeStart":237,"sourceCodeEnd":273,"githubUrl":"https://github.com/rectorphp/rector/blob/408fcb0ff1833e3d26cbc9b04c23a77565814a16/rules/Php70/EregToPcreTransformer.php#L237-L273","documentation":"processCurlyBracket() is called when an atom is followed by '{' beginning a bound like 'a{2,3}'. It searches for the closing '}' from that position; if none exists in the rest of the pattern, the bound is unterminated and InvalidEregException('\"{\" does not have a matching \"}\"') is thrown (EregToPcreTransformer.php:255), aborting the ereg-to-preg conversion of that call.","triggerScenarios":"Rector (Php70 set / EregToPregMatchRector, target PHP >= 7.0) processes an ereg-family call whose literal pattern has a '{' bound that is never closed, e.g. `ereg('a{2', $s)`, `split('x{1,2', $s)`, or a literal '{' that follows an atom and a digit but has no '}' anywhere after it (strpos returns false). Note: a literal '{' directly after an atom with no digit following takes the escape path, so this throw specifically needs a digit-led bound without '}'.","commonSituations":"Truncated quantifiers after edits; patterns mixing template placeholders with real bounds ('a{2{name}') where the first '}' was removed; hand-merged alternations that lost the closing brace.","solutions":["Close the bound in the flagged pattern: 'a{2' -> 'a{2}'.","If the braces are literal placeholders, escape them ('a\\{2\\}') so they take the literal path.","Re-run rector; preflight literal ereg patterns for '{' with a digit after it and no '}' to the end of the pattern."],"exampleFix":"// before - unterminated bound\nereg('a{2', $subject);\n\n// after - closed bound\nereg('a{2}', $subject); // rector: preg_match('#a{2}#m', $subject)","handlingStrategy":"try-catch","validationCode":"// quick lint: digit-led '{' must find a '}' later in the pattern\nfunction boundClosed(string $p): bool {\n    return !preg_match('~\\{\\d~', $p) || (bool) preg_match('~\\{\\d[^}]*\\}~', $p);\n}","typeGuard":null,"tryCatchPattern":"try {\n    $pcre = $eregToPcreTransformer->transform($pattern, $ignoreCase);\n} catch (InvalidEregException $e) {\n    // '{' bound never closed — add the '}' or escape the brace as literal\n    continue;\n}","preventionTips":["Every '{' that starts a bound needs a matching '}' — check long alternations where the tail was edited.","Escape braces you mean literally ('\\{').","Batch-validate literal patterns with the transformer before the full rector run."],"tags":["rector","php","regex","posix-ere","quantifier","bound","migration"],"backgroundTag":"invalid-regex-syntax","analyzedSha":"408fcb0ff1833e3d26cbc9b04c23a77565814a16","analyzedAt":"2026-08-21T05:11:02.643Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}