PluginProbe
TablePress – Tables in WordPress made easy / 3.2.1
TablePress – Tables in WordPress made easy v3.2.1
3.3.4 3.3.3 3.3.2 3.3.1 trunk 1.12 1.14 1.9.2 2.0.4 2.1.7 2.1.8 2.2 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.3 2.3.1 2.3.2 2.4 2.4.1 2.4.2 2.4.3 2.4.4 All 44 releases
tablepress / libraries / vendor / composer / pcre / PHPStan / UnsafeStrictGroupsCallRule.php

UnsafeStrictGroupsCallRule.php in TablePress – Tables in WordPress made easy 3.2.1, at libraries/vendor/composer/pcre/PHPStan/UnsafeStrictGroupsCallRule.php

113 lines 3.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php declare(strict_types=1);
2
3 namespace TablePress\Composer\Pcre\PHPStan;
4
5 use TablePress\Composer\Pcre\Preg;
6 use TablePress\Composer\Pcre\Regex;
7 use PhpParser\Node;
8 use PhpParser\Node\Expr\StaticCall;
9 use PhpParser\Node\Name\FullyQualified;
10 use PHPStan\Analyser\Scope;
11 use PHPStan\Analyser\SpecifiedTypes;
12 use PHPStan\Rules\Rule;
13 use PHPStan\Rules\RuleErrorBuilder;
14 use PHPStan\TrinaryLogic;
15 use PHPStan\Type\ObjectType;
16 use PHPStan\Type\Type;
17 use PHPStan\Type\TypeCombinator;
18 use PHPStan\Type\Php\RegexArrayShapeMatcher;
19 use function sprintf;
20
21 /**
22 * @implements Rule<StaticCall>
23 */
24 final class UnsafeStrictGroupsCallRule implements Rule
25 {
26 /**
27 * @var RegexArrayShapeMatcher
28 */
29 private $regexShapeMatcher;
30
31 public function __construct(RegexArrayShapeMatcher $regexShapeMatcher)
32 {
33 $this->regexShapeMatcher = $regexShapeMatcher;
34 }
35
36 public function getNodeType(): string
37 {
38 return StaticCall::class;
39 }
40
41 public function processNode(Node $node, Scope $scope): array
42 {
43 if (!$node->class instanceof FullyQualified) {
44 return [];
45 }
46 $isRegex = $node->class->toString() === Regex::class;
47 $isPreg = $node->class->toString() === Preg::class;
48 if (!$isRegex && !$isPreg) {
49 return [];
50 }
51 if (!$node->name instanceof Node\Identifier || !in_array($node->name->name, ['matchStrictGroups', 'isMatchStrictGroups', 'matchAllStrictGroups', 'isMatchAllStrictGroups'], true)) {
52 return [];
53 }
54
55 $args = $node->getArgs();
56 if (!isset($args[0])) {
57 return [];
58 }
59
60 $patternArg = $args[0] ?? null;
61 if ($isPreg) {
62 if (!isset($args[2])) { // no matches set, skip as the matches won't be used anyway
63 return [];
64 }
65 $flagsArg = $args[3] ?? null;
66 } else {
67 $flagsArg = $args[2] ?? null;
68 }
69
70 if ($patternArg === null) {
71 return [];
72 }
73
74 $flagsType = PregMatchFlags::getType($flagsArg, $scope);
75 if ($flagsType === null) {
76 return [];
77 }
78
79 $matchedType = $this->regexShapeMatcher->matchExpr($patternArg->value, $flagsType, TrinaryLogic::createYes(), $scope);
80 if ($matchedType === null) {
81 return [
82 RuleErrorBuilder::message(sprintf('The %s call is potentially unsafe as $matches\' type could not be inferred.', $node->name->name))
83 ->identifier('composerPcre.maybeUnsafeStrictGroups')
84 ->build(),
85 ];
86 }
87
88 if (count($matchedType->getConstantArrays()) === 1) {
89 $matchedType = $matchedType->getConstantArrays()[0];
90 $nullableGroups = [];
91 foreach ($matchedType->getValueTypes() as $index => $type) {
92 if (TypeCombinator::containsNull($type)) {
93 $nullableGroups[] = $matchedType->getKeyTypes()[$index]->getValue();
94 }
95 }
96
97 if (\count($nullableGroups) > 0) {
98 return [
99 RuleErrorBuilder::message(sprintf(
100 'The %s call is unsafe as match group%s "%s" %s optional and may be null.',
101 $node->name->name,
102 \count($nullableGroups) > 1 ? 's' : '',
103 implode('", "', $nullableGroups),
104 \count($nullableGroups) > 1 ? 'are' : 'is'
105 ))->identifier('composerPcre.unsafeStrictGroups')->build(),
106 ];
107 }
108 }
109
110 return [];
111 }
112 }
113