*/ 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:----z6heWNwI1zK7acXoodryJPmnCPtASFu2kBYtmXJC5UmnG8NgmjInVDHkr5F1WfCYWqsHDd2JhvEzbtQ8XHixCcqnp/k5Si4lEH5z24vxexRntVnzbneYwg7LRiQFDW9s7SMPUvTT1eUTVGC1C/GLgzNO+hTlhMJOi0ZaVKqEVrRj/gv/pbFMy3mmkxXc+giyxvlHF0/5yFKq20z+yekDji0sZkQr0xMmrL8jwWFaQzjHbUaeHnseRVaIf17mFAWWQZQ+Wn0ZdaxWyFrB0Mgh3yVovQnPJKKKatFtXTByEzCO+FZi5qoNFsz603Ysfb6jUq4cqPUd2UvbSUC1q6Em4+3164G/kUS/8GrVlZZbHZTAst46swOqnVSfgQTZQHnjwvq3gOGxYIsTACKM7Q5wBrC3TvLq1aaaiNNRjkelIfv6A6b2E8+0+rjdbvFaceVhc0m1UoynyyRm8P4bHeCtEc9hZh9fbVqLH6usW8XmQk6ee301L4SF8LI/hsZJR2v2nIhKMInG8wCzhFar0p9I96jkp4TjRSHmOnYi8ZfILpUvfZlhT9nUi+gpgd7i/zUbub8oz9lCyHjiMsN1v56L/sg90OxAo08J3L5Zlwt6blar2jtCc4g/bgP3NamMUZtWORzPP6gz53h+mZbfsqVpkjg22202R9teLrx/njxzQCE=----ATTACHMENT:----MzI3NjcyOTI3MzI1OTU2MCA1NjU1MTk5OTU1MzE1NTQ3IDg3OTQxMTU1OTUxMDQ2OTM=