1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Type\Constant;
4:
5: use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprIntegerNode;
6: use PHPStan\PhpDocParser\Ast\Type\ConstTypeNode;
7: use PHPStan\PhpDocParser\Ast\Type\TypeNode;
8: use PHPStan\Type\CompoundType;
9: use PHPStan\Type\ConstantScalarType;
10: use PHPStan\Type\GeneralizePrecision;
11: use PHPStan\Type\IntegerRangeType;
12: use PHPStan\Type\IntegerType;
13: use PHPStan\Type\IsSuperTypeOfResult;
14: use PHPStan\Type\Traits\ConstantNumericComparisonTypeTrait;
15: use PHPStan\Type\Traits\ConstantScalarTypeTrait;
16: use PHPStan\Type\Type;
17: use PHPStan\Type\TypeCombinator;
18: use PHPStan\Type\VerbosityLevel;
19: use function abs;
20: use function sprintf;
21: use const PHP_INT_MIN;
22:
23: /** @api */
24: class ConstantIntegerType extends IntegerType implements ConstantScalarType
25: {
26:
27: use ConstantScalarTypeTrait;
28: use ConstantScalarToBooleanTrait;
29: use ConstantNumericComparisonTypeTrait;
30:
31: /** @api */
32: public function __construct(private int $value)
33: {
34: parent::__construct();
35: }
36:
37: public function getValue(): int
38: {
39: return $this->value;
40: }
41:
42: public function isSuperTypeOf(Type $type): IsSuperTypeOfResult
43: {
44: if ($type instanceof self) {
45: return $this->value === $type->value ? IsSuperTypeOfResult::createYes() : IsSuperTypeOfResult::createNo();
46: }
47:
48: if ($type instanceof IntegerRangeType) {
49: $min = $type->getMin();
50: $max = $type->getMax();
51: if (($min === null || $min <= $this->value) && ($max === null || $this->value <= $max)) {
52: return IsSuperTypeOfResult::createMaybe();
53: }
54:
55: return IsSuperTypeOfResult::createNo();
56: }
57:
58: if ($type instanceof parent) {
59: return IsSuperTypeOfResult::createMaybe();
60: }
61:
62: if ($type instanceof CompoundType) {
63: return $type->isSubTypeOf($this);
64: }
65:
66: return IsSuperTypeOfResult::createNo();
67: }
68:
69: public function describe(VerbosityLevel $level): string
70: {
71: return $level->handle(
72: static fn (): string => 'int',
73: fn (): string => sprintf('%s', $this->value),
74: );
75: }
76:
77: public function toFloat(): Type
78: {
79: return new ConstantFloatType($this->value);
80: }
81:
82: public function toBitwiseNotType(): Type
83: {
84: return new self(~$this->value);
85: }
86:
87: public function toAbsoluteNumber(): Type
88: {
89: if ($this->value === PHP_INT_MIN) {
90: // The absolute value of the smallest integer is not representable as an int.
91: // Checking is_int(abs($this->value)) instead is dead code to PHPStan itself,
92: // which infers abs(int) as int<0, max>.
93: return new ConstantFloatType(-(float) $this->value);
94: }
95:
96: return new self(abs($this->value));
97: }
98:
99: public function toString(): Type
100: {
101: return new ConstantStringType((string) $this->value);
102: }
103:
104: public function toArrayKey(): Type
105: {
106: return $this;
107: }
108:
109: public function toCoercedArgumentType(bool $strictTypes): Type
110: {
111: if (!$strictTypes) {
112: return TypeCombinator::union($this, $this->toFloat(), $this->toString(), $this->toBoolean());
113: }
114:
115: return TypeCombinator::union($this, $this->toFloat());
116: }
117:
118: public function generalize(GeneralizePrecision $precision): Type
119: {
120: return new IntegerType();
121: }
122:
123: /**
124: * @return ConstTypeNode
125: */
126: public function toPhpDocNode(): TypeNode
127: {
128: return new ConstTypeNode(new ConstExprIntegerNode((string) $this->value));
129: }
130:
131: }
132: