Viewing file: InvalidRegexPatternRule.php (4.07 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
<?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; }
}
|