class InvalidRegexPatternRule
Copy of PHPStan's RegularExpressionPatternRule
@implements Rule<StaticCall>
Hierarchy
- class \Composer\Pcre\PHPStan\InvalidRegexPatternRule implements \PHPStan\Rules\Rule
Expanded class hierarchy of InvalidRegexPatternRule
File
-
vendor/
composer/ pcre/ src/ PHPStan/ InvalidRegexPatternRule.php, line 24
Namespace
Composer\Pcre\PHPStanView source
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;
}
}
Members
Title Sort descending | Modifiers | Object type | Summary |
---|---|---|---|
InvalidRegexPatternRule::extractPatterns | private | function | |
InvalidRegexPatternRule::getNodeType | public | function | |
InvalidRegexPatternRule::processNode | public | function | |
InvalidRegexPatternRule::validatePattern | private | function |