{"record":{"id":"805b12bf3ccec70f","repo":"rectorphp/rector","slug":"unescaped-metacharacter","errorCode":null,"errorMessage":"unescaped metacharacter \")\"","messagePattern":"unescaped metacharacter \"\\)\"","errorType":"validation","errorClass":"InvalidEregException","httpStatus":null,"severity":"error","filePath":"rules/Php70/EregToPcreTransformer.php","lineNumber":81,"sourceCode":"     * Single type is chosen to prevent every regular with different delimiter.\n     */\n    public function __construct(string $pcreDelimiter = '#')\n    {\n        $this->pcreDelimiter = $pcreDelimiter;\n    }\n    // converts the ERE $s into the PCRE $r. triggers error on any invalid input.\n    public function transform(string $content, bool $ignorecase): string\n    {\n        if ($ignorecase) {\n            if (isset($this->icache[$content])) {\n                return $this->icache[$content];\n            }\n        } elseif (isset($this->cache[$content])) {\n            return $this->cache[$content];\n        }\n        [$r, $i] = $this->_ere2pcre($content, 0);\n        if ($i !== strlen($content)) {\n            throw new InvalidEregException('unescaped metacharacter \")\"');\n        }\n        if ($ignorecase) {\n            return $this->icache[$content] = $this->pcreDelimiter . $r . $this->pcreDelimiter . 'mi';\n        }\n        return $this->cache[$content] = $this->pcreDelimiter . $r . $this->pcreDelimiter . 'm';\n    }\n    /**\n     * Recursively converts ERE into PCRE, starting at the position $i.\n     *\n     * @return float[]|int[]|string[]\n     */\n    private function _ere2pcre(string $content, int $i): array\n    {\n        $r = [''];\n        $rr = 0;\n        $l = strlen($content);\n        $normalizeUnprintableChar = \\false;\n        while ($i < $l) {","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/rectorphp/rector/blob/408fcb0ff1833e3d26cbc9b04c23a77565814a16/rules/Php70/EregToPcreTransformer.php#L63-L99","documentation":"EregToPcreTransformer::transform() converts a POSIX ERE pattern (from an ereg()/eregi()/ereg_replace()/split()/spliti() call handled by EregToPregMatchRector) into a PCRE pattern. After the recursive conversion returns, it checks that the whole input was consumed; leftover input means the pattern contained an unbalanced closing ')' at the top level, which is invalid ERE. The resulting InvalidEregException aborts the Rector run on that file.","triggerScenarios":"Running rector with the Php70 set (or EregToPregMatchRector enabled) at a target PHP >= 7.0 on code containing an ereg-family call whose first argument is a string literal with a stray ')', e.g. `ereg('abc)', $s)` or `split(')|', $s)`. The inner parser breaks out at the ')' and transform() detects $i !== strlen($content).","commonSituations":"Migrating a legacy PHP 5 codebase to PHP 7+ where hand-written ereg patterns were never validated; patterns built by string concatenation that ended up with an extra ')'; copy-pasted legacy regexes that only worked because ereg tolerated them or were never executed.","solutions":["Find the failing ereg call (the Rector error output names the file) and fix the literal pattern: escape the parenthesis as '\\)' if it is meant literally, or delete the stray one.","Re-run rector to confirm the pattern now converts cleanly.","If the pattern is intentionally odd, convert that call to preg_match() by hand and let rector skip it.","Before large migrations, preflight-scan the codebase for ereg/split calls and validate their literal patterns with EregToPcreTransformer::transform() in a try/catch."],"exampleFix":"// before - unbalanced top-level ')'\nereg('abc)', $subject);\n\n// after - escaped literal parenthesis\nereg('abc\\)', $subject); // rector rewrites to: preg_match('#abc\\)#m', $subject)","handlingStrategy":"try-catch","validationCode":"// preflight: validate literal ereg patterns before running rector\nuse Rector\\Php70\\EregToPcreTransformer;\nuse Rector\\Php70\\Exception\\InvalidEregException;\n$transformer = new EregToPcreTransformer();\ntry {\n    $transformer->transform($literalPattern, false);\n} catch (InvalidEregException $e) {\n    echo \"Fix pattern before rector run: {$literalPattern} — {$e->getMessage()}\\n\";\n}","typeGuard":"function isConvertibleEreg(string $pattern): bool\n{\n    try {\n        (new \\Rector\\Php70\\EregToPcreTransformer())->transform($pattern, false);\n        return true;\n    } catch (\\Rector\\Php70\\Exception\\InvalidEregException $e) {\n        return false;\n    }\n}","tryCatchPattern":"try {\n    $pcre = $eregToPcreTransformer->transform($pattern, $ignoreCase);\n} catch (\\Rector\\Php70\\Exception\\InvalidEregException $e) {\n    // log the file/pattern, fix the source literal by hand, then re-run rector\n    $this->errors[] = [$file, $pattern, $e->getMessage()];\n    continue;\n}","preventionTips":["Before running the Php70 set, grep the codebase for ereg/eregi/ereg_replace/eregi_replace/split/spliti and eyeball each literal pattern.","Balance parentheses/brackets in legacy patterns; a stray ')' at top level is exactly what this error reports.","Validate suspicious literals up front with EregToPcreTransformer::transform() inside a try/catch harness."],"tags":["rector","php","regex","posix-ere","preg-match","migration"],"backgroundTag":"invalid-regex-syntax","analyzedSha":"408fcb0ff1833e3d26cbc9b04c23a77565814a16","analyzedAt":"2026-08-21T05:11:02.643Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}