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