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 PhpParser\Node;use PhpParser\Node\Expr\StaticCall;use PhpParser\Node\Name\FullyQualified;use PHPStan\Analyser\Scope;use PHPStan\Analyser\SpecifiedTypes;use PHPStan\Rules\Rule;use PHPStan\Rules\RuleErrorBuilder;use PHPStan\TrinaryLogic;use PHPStan\Type\ObjectType;use PHPStan\Type\Type;use PHPStan\Type\TypeCombinator;use PHPStan\Type\Php\RegexArrayShapeMatcher;use function sprintf;/*** @implements Rule<StaticCall>*/final class UnsafeStrictGroupsCallRule implements Rule{/*** @var RegexArrayShapeMatcher*/private $regexShapeMatcher;public function __construct(RegexArrayShapeMatcher $regexShapeMatcher){$this->regexShapeMatcher = $regexShapeMatcher;}public function getNodeType(): string{return StaticCall::class;}public function processNode(Node $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 || !in_array($node->name->name, ['matchStrictGroups', 'isMatchStrictGroups', 'matchAllStrictGroups', 'isMatchAllStrictGroups'], true)) {return [];}$args = $node->getArgs();if (!isset($args[0])) {return [];}$patternArg = $args[0] ?? null;if ($isPreg) {if (!isset($args[2])) { // no matches set, skip as the matches won't be used anywayreturn [];}$flagsArg = $args[3] ?? null;} else {$flagsArg = $args[2] ?? null;}if ($patternArg === null) {return [];}$flagsType = PregMatchFlags::getType($flagsArg, $scope);if ($flagsType === null) {return [];}$matchedType = $this->regexShapeMatcher->matchExpr($patternArg->value, $flagsType, TrinaryLogic::createYes(), $scope);if ($matchedType === null) {return [RuleErrorBuilder::message(sprintf('The %s call is potentially unsafe as $matches\' type could not be inferred.', $node->name->name))->identifier('composerPcre.maybeUnsafeStrictGroups')->build(),];}if (count($matchedType->getConstantArrays()) === 1) {$matchedType = $matchedType->getConstantArrays()[0];$nullableGroups = [];foreach ($matchedType->getValueTypes() as $index => $type) {if (TypeCombinator::containsNull($type)) {$nullableGroups[] = $matchedType->getKeyTypes()[$index]->getValue();}}if (\count($nullableGroups) > 0) {return [RuleErrorBuilder::message(sprintf('The %s call is unsafe as match group%s "%s" %s optional and may be null.',$node->name->name,\count($nullableGroups) > 1 ? 's' : '',implode('", "', $nullableGroups),\count($nullableGroups) > 1 ? 'are' : 'is'))->identifier('composerPcre.unsafeStrictGroups')->build(),];}}return [];}}