-
Notifications
You must be signed in to change notification settings - Fork 566
Expand file tree
/
Copy pathCtypeDigitFunctionTypeSpecifyingExtension.php
More file actions
88 lines (74 loc) · 2.64 KB
/
CtypeDigitFunctionTypeSpecifyingExtension.php
File metadata and controls
88 lines (74 loc) · 2.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
<?php declare(strict_types = 1);
namespace PHPStan\Type\Php;
use PhpParser\Node\Expr\Cast;
use PhpParser\Node\Expr\FuncCall;
use PHPStan\Analyser\Scope;
use PHPStan\Analyser\SpecifiedTypes;
use PHPStan\Analyser\TypeSpecifier;
use PHPStan\Analyser\TypeSpecifierAwareExtension;
use PHPStan\Analyser\TypeSpecifierContext;
use PHPStan\DependencyInjection\AutowiredService;
use PHPStan\Reflection\FunctionReflection;
use PHPStan\ShouldNotHappenException;
use PHPStan\Type\Accessory\AccessoryDecimalIntegerStringType;
use PHPStan\Type\Constant\ConstantBooleanType;
use PHPStan\Type\FunctionTypeSpecifyingExtension;
use PHPStan\Type\IntegerRangeType;
use PHPStan\Type\IntersectionType;
use PHPStan\Type\StringType;
use PHPStan\Type\TypeCombinator;
use PHPStan\Type\UnionType;
use function strtolower;
#[AutowiredService]
final class CtypeDigitFunctionTypeSpecifyingExtension implements FunctionTypeSpecifyingExtension, TypeSpecifierAwareExtension
{
private TypeSpecifier $typeSpecifier;
public function isFunctionSupported(FunctionReflection $functionReflection, FuncCall $node, TypeSpecifierContext $context): bool
{
return strtolower($functionReflection->getName()) === 'ctype_digit'
&& !$context->null();
}
public function specifyTypes(FunctionReflection $functionReflection, FuncCall $node, Scope $scope, TypeSpecifierContext $context): SpecifiedTypes
{
if (!isset($node->getArgs()[0])) {
return new SpecifiedTypes();
}
if ($context->null()) {
throw new ShouldNotHappenException();
}
$exprArg = $node->getArgs()[0]->value;
if ($context->true() && $scope->getType($exprArg)->isNumericString()->yes()) {
return new SpecifiedTypes();
}
$types = [
IntegerRangeType::fromInterval(48, 57), // ASCII-codes for 0-9
IntegerRangeType::createAllGreaterThanOrEqualTo(256), // Starting from 256 ints are interpreted as strings
];
if ($context->true()) {
$types[] = new IntersectionType([
new StringType(),
new AccessoryDecimalIntegerStringType(),
]);
}
$unionType = TypeCombinator::union(...$types);
$specifiedTypes = $this->typeSpecifier->create($exprArg, $unionType, $context, $scope);
if ($exprArg instanceof Cast\String_) {
$castedType = new UnionType([
IntegerRangeType::fromInterval(0, null),
new IntersectionType([
new StringType(),
new AccessoryDecimalIntegerStringType(),
]),
new ConstantBooleanType(true),
]);
$specifiedTypes = $specifiedTypes->unionWith(
$this->typeSpecifier->create($exprArg->expr, $castedType, $context, $scope),
);
}
return $specifiedTypes;
}
public function setTypeSpecifier(TypeSpecifier $typeSpecifier): void
{
$this->typeSpecifier = $typeSpecifier;
}
}