AutorÃa | Ultima modificación | Ver Log |
<?php declare(strict_types = 1);namespace Composer\Pcre\PHPStan;use Composer\Pcre\Preg;use Composer\Pcre\Regex;use Composer\Pcre\PcreException;use Nette\Utils\RegexpException;use Nette\Utils\Strings;use PhpParser\Node;use PhpParser\Node\Expr\StaticCall;use PhpParser\Node\Name\FullyQualified;use PHPStan\Analyser\Scope;use PHPStan\Rules\Rule;use PHPStan\Rules\RuleErrorBuilder;use function in_array;use function sprintf;/*** Copy of PHPStan's RegularExpressionPatternRule** @implements Rule<StaticCall>*/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;}}