{"record":{"id":"3c716c8a85e4c368","repo":"rectorphp/rector","slug":"does-not-have-a-matching-3c716c","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":194,"sourceCode":"        if ($normalizeUnprintableChar) {\n            $content = str_replace(\"\\f\", '\\\\\\\\f', $content);\n        }\n        return str_replace($this->pcreDelimiter, '\\\\' . $this->pcreDelimiter, $content);\n    }\n    /**\n     * @param array<int, mixed> $r\n     */\n    private function processBracket(string $content, int $i, int $l, array &$r, int $rr): int\n    {\n        // special case\n        if ($i + 1 < $l && $content[$i + 1] === ')') {\n            $r[$rr] .= '()';\n            ++$i;\n        } else {\n            $position = $i + 1;\n            [$t, $ii] = $this->_ere2pcre($content, $position);\n            if ($ii >= $l || $content[$ii] !== ')') {\n                throw new InvalidEregException('\"(\" does not have a matching \")\"');\n            }\n            $r[$rr] .= '(' . $t . ')';\n            $i = $ii;\n        }\n        // retype\n        $i = (int) $i;\n        return $i;\n    }\n    /**\n     * @return float[]|int[]|string[]\n     */\n    private function processSquareBracket(string $s, int $i, int $l, string $cls, bool $start): array\n    {\n        do {\n            if ($s[$i] === '[' && $i + 1 < $l && strpos('.=:', $s[$i + 1]) !== \\false) {\n                /** @var string $cls */\n                [$cls, $i] = $this->processCharacterClass($s, $i, $cls);\n            } else {","sourceCodeStart":176,"sourceCodeEnd":212,"githubUrl":"https://github.com/rectorphp/rector/blob/408fcb0ff1833e3d26cbc9b04c23a77565814a16/rules/Php70/EregToPcreTransformer.php#L176-L212","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before - unclosed group\nereg('(abc', $subject);\n\n// after - closed group\nereg('(abc)', $subject); // rector: preg_match('#(abc)#m', $subject)","handlingStrategy":"try-catch","validationCode":"function groupsBalanced(string $p): bool {\n    $depth = 0;\n    for ($i = 0, $n = strlen($p); $i < $n; ++$i) {\n        if ($p[$i] === '\\\\') { ++$i; continue; }\n        if ($p[$i] === '(') { ++$depth; }\n        elseif ($p[$i] === ')') { --$depth; if ($depth < 0) return false; }\n    }\n    return $depth === 0;\n}","typeGuard":null,"tryCatchPattern":"try {\n    $pcre = $eregToPcreTransformer->transform($pattern, $ignoreCase);\n} catch (InvalidEregException $e) {\n    // group opened but never closed — balance '(' and ')' then re-run\n    continue;\n}","preventionTips":["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."],"tags":["rector","php","regex","posix-ere","grouping","migration"],"backgroundTag":"invalid-regex-syntax","analyzedSha":"408fcb0ff1833e3d26cbc9b04c23a77565814a16","analyzedAt":"2026-08-21T05:11:02.643Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}