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\Expr\StaticCall;
8
use PHPStan\Analyser\Scope;
9
use PHPStan\Reflection\MethodReflection;
10
use PHPStan\Reflection\Native\NativeParameterReflection;
11
use PHPStan\Reflection\ParameterReflection;
12
use PHPStan\TrinaryLogic;
13
use PHPStan\Type\ClosureType;
14
use PHPStan\Type\Constant\ConstantArrayType;
15
use PHPStan\Type\Php\RegexArrayShapeMatcher;
16
use PHPStan\Type\StaticMethodParameterClosureTypeExtension;
17
use PHPStan\Type\StringType;
18
use PHPStan\Type\TypeCombinator;
19
use PHPStan\Type\Type;
20
 
21
final class PregReplaceCallbackClosureTypeExtension implements StaticMethodParameterClosureTypeExtension
22
{
23
    /**
24
     * @var RegexArrayShapeMatcher
25
     */
26
    private $regexShapeMatcher;
27
 
28
    public function __construct(RegexArrayShapeMatcher $regexShapeMatcher)
29
    {
30
        $this->regexShapeMatcher = $regexShapeMatcher;
31
    }
32
 
33
    public function isStaticMethodSupported(MethodReflection $methodReflection, ParameterReflection $parameter): bool
34
    {
35
        return in_array($methodReflection->getDeclaringClass()->getName(), [Preg::class, Regex::class], true)
36
            && in_array($methodReflection->getName(), ['replaceCallback', 'replaceCallbackStrictGroups'], true)
37
            && $parameter->getName() === 'replacement';
38
    }
39
 
40
    public function getTypeFromStaticMethodCall(MethodReflection $methodReflection, StaticCall $methodCall, ParameterReflection $parameter, Scope $scope): ?Type
41
    {
42
        $args = $methodCall->getArgs();
43
        $patternArg = $args[0] ?? null;
44
        $flagsArg = $args[5] ?? null;
45
 
46
        if (
47
            $patternArg === null
48
        ) {
49
            return null;
50
        }
51
 
52
        $flagsType = PregMatchFlags::getType($flagsArg, $scope);
53
 
54
        $matchesType = $this->regexShapeMatcher->matchExpr($patternArg->value, $flagsType, TrinaryLogic::createYes(), $scope);
55
        if ($matchesType === null) {
56
            return null;
57
        }
58
 
59
        if ($methodReflection->getName() === 'replaceCallbackStrictGroups' && count($matchesType->getConstantArrays()) === 1) {
60
            $matchesType = $matchesType->getConstantArrays()[0];
61
            $matchesType = new ConstantArrayType(
62
                $matchesType->getKeyTypes(),
63
                array_map(static function (Type $valueType): Type {
64
                    if (count($valueType->getConstantArrays()) === 1) {
65
                        $valueTypeArray = $valueType->getConstantArrays()[0];
66
                        return new ConstantArrayType(
67
                            $valueTypeArray->getKeyTypes(),
68
                            array_map(static function (Type $valueType): Type {
69
                                return TypeCombinator::removeNull($valueType);
70
                            }, $valueTypeArray->getValueTypes()),
71
                            $valueTypeArray->getNextAutoIndexes(),
72
                            [],
73
                            $valueTypeArray->isList()
74
                        );
75
                    }
76
                    return TypeCombinator::removeNull($valueType);
77
                }, $matchesType->getValueTypes()),
78
                $matchesType->getNextAutoIndexes(),
79
                [],
80
                $matchesType->isList()
81
            );
82
        }
83
 
84
        return new ClosureType(
85
            [
86
                new NativeParameterReflection($parameter->getName(), $parameter->isOptional(), $matchesType, $parameter->passedByReference(), $parameter->isVariadic(), $parameter->getDefaultValue()),
87
            ],
88
            new StringType()
89
        );
90
    }
91
}