Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1441 ariadna 1
<?php declare(strict_types=1);
2
 
3
namespace Composer\Pcre\PHPStan;
4
 
5
use Composer\Pcre\Preg;
6
use 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
}