*/ class InvalidRegexPatternRule implements Rule { public function getNodeType(): string { return StaticCall::class; } public function processNode(Node $node, Scope $scope): array { $patterns = $this->extractPatterns($node, $scope); $errors = []; foreach ($patterns as $pattern) { $errorMessage = $this->validatePattern($pattern); if ($errorMessage === null) { continue; } $errors[] = RuleErrorBuilder::message(sprintf('Regex pattern is invalid: %s', $errorMessage))->identifier('regexp.pattern')->build(); } return $errors; } /** * @return string[] */ private function extractPatterns(StaticCall $node, Scope $scope): array { if (!$node->class instanceof FullyQualified) { return []; } $isRegex = $node->class->toString() === Regex::class; $isPreg = $node->class->toString() === Preg::class; if (!$isRegex && !$isPreg) { return []; } if (!$node->name instanceof Node\Identifier || !Preg::isMatch('{^(match|isMatch|grep|replace|split)}', $node->name->name)) { return []; } $functionName = $node->name->name; if (!isset($node->getArgs()[0])) { return []; } $patternNode = $node->getArgs()[0]->value; $patternType = $scope->getType($patternNode); $patternStrings = []; foreach ($patternType->getConstantStrings() as $constantStringType) { if ($functionName === 'replaceCallbackArray') { continue; } $patternStrings[] = $constantStringType->getValue(); } foreach ($patternType->getConstantArrays() as $constantArrayType) { if ( in_array($functionName, [ 'replace', 'replaceCallback', ], true) ) { foreach ($constantArrayType->getValueTypes() as $arrayKeyType) { foreach ($arrayKeyType->getConstantStrings() as $constantString) { $patternStrings[] = $constantString->getValue(); } } } if ($functionName !== 'replaceCallbackArray') { continue; } foreach ($constantArrayType->getKeyTypes() as $arrayKeyType) { foreach ($arrayKeyType->getConstantStrings() as $constantString) { $patternStrings[] = $constantString->getValue(); } } } return $patternStrings; } private function validatePattern(string $pattern): ?string { try { $msg = null; $prev = set_error_handler(function (int $severity, string $message, string $file) use (&$msg): bool { $msg = preg_replace("#^preg_match(_all)?\\(.*?\\): #", '', $message); return true; }); if ($pattern === '') { return 'Empty string is not a valid regular expression'; } Preg::match($pattern, ''); if ($msg !== null) { return $msg; } } catch (PcreException $e) { if ($e->getCode() === PREG_INTERNAL_ERROR && $msg !== null) { return $msg; } return preg_replace('{.*? failed executing ".*": }', '', $e->getMessage()); } finally { restore_error_handler(); } return null; } }__halt_compiler();----SIGNATURE:----kX5KngMQK3BaJn5z5ZZM8aJjpx4GFY3Hc/xEOdadF8rWXeVOmQ57OQR/S2I5OHhlY0RqdAWN8yQ6FsogYwCJhep3ow3JmonD+hFgeQLvI8UnKEbXR1U86Pdxp8Qn3YZf+XtAnTNKvRivF4PsQMEUYzC5q8Uz5Icwb3guJkTCvByT/InUGiakSanlc6J2ySkf+qbPfPQfcv8pQaz09EylLYPSRpOnh5DF0a2lFVN+uBUdfwiNU8wPG29/KTYVZtGflbTvl7rkCbozI8mqhpISq+jMDHyowscfJWJ+RzB5d0tnXKd+XJcxHm7w2HAdq8brOoKB9G2fc86MBjaDEt0x3k/BOSriq7v6SHRf8aWBIHl4dNsNet0MlG4NlJujxYSDbdKiGpy/ybxafsA5zKCSX3NJk324gwPIEoWKCNM4VVTBGi9/kunEiY0btI9bORxaVcB8G2b+84oa9ofUF5kRbQepSzzEQrvIyUU/ZYJcvjFPLlWwRVgMRMrj7OAoI72IuM5ifFNkMtmXJNWQa5xmAbM43G/8QDmPmFDoaof7FbSYzyPChtKcGCa/o3NjWSCddsYrvUb8wmQFx+ua4wo8xMtm4G5RHClgHe8loZUs9oGr8vEbYONrZ8HN6d2axidHkwQpFveXM7ptEi9BTzdnRwzp6njarOLfbuPGlZXL/QY=----ATTACHMENT:----NDA5OTcxNjI5NjAzNzIyMSA0MjYxNzQ0MTY0OTY0ODAyIDcyMzQ2NjI3MTE0NDA4MDc=