1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Type\Constant;
4:
5: use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprFloatNode;
6: use PHPStan\PhpDocParser\Ast\Type\ConstTypeNode;
7: use PHPStan\PhpDocParser\Ast\Type\TypeNode;
8: use PHPStan\Turbo\ShadowedByTurboExtension;
9: use PHPStan\Type\ConstantScalarType;
10: use PHPStan\Type\FloatType;
11: use PHPStan\Type\GeneralizePrecision;
12: use PHPStan\Type\Traits\ConstantNumericComparisonTypeTrait;
13: use PHPStan\Type\Traits\ConstantScalarTypeTrait;
14: use PHPStan\Type\Type;
15: use PHPStan\Type\UnionType;
16: use PHPStan\Type\VerbosityLevel;
17: use function abs;
18: use function ini_get;
19: use function ini_set;
20: use function is_finite;
21: use function is_nan;
22: use function str_contains;
23:
24: /** @api */
25: #[ShadowedByTurboExtension(implementation: __DIR__ . '/../../../turbo-ext/src/ConstantFloatType.cpp')]
26: class ConstantFloatType extends FloatType implements ConstantScalarType
27: {
28:
29: use ConstantScalarTypeTrait;
30: use ConstantScalarToBooleanTrait;
31: use ConstantNumericComparisonTypeTrait;
32:
33: /** @api */
34: public function __construct(private float $value)
35: {
36: parent::__construct();
37: }
38:
39: public function getValue(): float
40: {
41: return $this->value;
42: }
43:
44: public function equals(Type $type): bool
45: {
46: return $type instanceof self && ($this->value === $type->value || is_nan($this->value) && is_nan($type->value));
47: }
48:
49: private function castFloatToString(float $value): string
50: {
51: $precisionBackup = ini_get('precision');
52: ini_set('precision', '-1');
53: try {
54: if (is_nan($value)) {
55: return 'NAN';
56: }
57:
58: $valueStr = (string) $value;
59: if (is_finite($value) && !str_contains($valueStr, '.')) {
60: $valueStr .= '.0';
61: }
62:
63: return $valueStr;
64: } finally {
65: ini_set('precision', $precisionBackup);
66: }
67: }
68:
69: public function describe(VerbosityLevel $level): string
70: {
71: return $level->handle(
72: static fn (): string => 'float',
73: fn (): string => $this->castFloatToString($this->value),
74: );
75: }
76:
77: public function toString(): Type
78: {
79: if ($this->value === 0.0) {
80: return new UnionType([
81: new ConstantStringType('0'),
82: new ConstantStringType('-0'),
83: ]);
84: }
85:
86: return new ConstantStringType((string) $this->value);
87: }
88:
89: public function toInteger(): Type
90: {
91: return new ConstantIntegerType((int) $this->value);
92: }
93:
94: public function toBitwiseNotType(): Type
95: {
96: return new ConstantIntegerType(~ (int) $this->value);
97: }
98:
99: public function toAbsoluteNumber(): Type
100: {
101: return new self(abs($this->value));
102: }
103:
104: public function toArrayKey(): Type
105: {
106: return new ConstantIntegerType((int) $this->value);
107: }
108:
109: public function getFiniteTypes(): array
110: {
111: if (is_nan($this->value)) {
112: return [];
113: }
114:
115: return [$this];
116: }
117:
118: public function generalize(GeneralizePrecision $precision): Type
119: {
120: return new FloatType();
121: }
122:
123: /**
124: * @return ConstTypeNode
125: */
126: public function toPhpDocNode(): TypeNode
127: {
128: return new ConstTypeNode(new ConstExprFloatNode($this->castFloatToString($this->value)));
129: }
130:
131: }
132: