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\Type\ConstantScalarType;
9: use PHPStan\Type\FloatType;
10: use PHPStan\Type\GeneralizePrecision;
11: use PHPStan\Type\Traits\ConstantNumericComparisonTypeTrait;
12: use PHPStan\Type\Traits\ConstantScalarTypeTrait;
13: use PHPStan\Type\Type;
14: use PHPStan\Type\UnionType;
15: use PHPStan\Type\VerbosityLevel;
16: use function abs;
17: use function ini_get;
18: use function ini_set;
19: use function is_finite;
20: use function is_nan;
21: use function str_contains;
22:
23: /** @api */
24: class ConstantFloatType extends FloatType implements ConstantScalarType
25: {
26:
27: use ConstantScalarTypeTrait;
28: use ConstantScalarToBooleanTrait;
29: use ConstantNumericComparisonTypeTrait;
30:
31: /** @api */
32: public function __construct(private float $value)
33: {
34: parent::__construct();
35: }
36:
37: public function getValue(): float
38: {
39: return $this->value;
40: }
41:
42: public function equals(Type $type): bool
43: {
44: return $type instanceof self && ($this->value === $type->value || is_nan($this->value) && is_nan($type->value));
45: }
46:
47: private function castFloatToString(float $value): string
48: {
49: $precisionBackup = ini_get('precision');
50: ini_set('precision', '-1');
51: try {
52: $valueStr = (string) $value;
53: if (is_finite($value) && !str_contains($valueStr, '.')) {
54: $valueStr .= '.0';
55: }
56:
57: return $valueStr;
58: } finally {
59: ini_set('precision', $precisionBackup);
60: }
61: }
62:
63: public function describe(VerbosityLevel $level): string
64: {
65: return $level->handle(
66: static fn (): string => 'float',
67: fn (): string => $this->castFloatToString($this->value),
68: );
69: }
70:
71: public function toString(): Type
72: {
73: if ($this->value === 0.0) {
74: return new UnionType([
75: new ConstantStringType('0'),
76: new ConstantStringType('-0'),
77: ]);
78: }
79:
80: return new ConstantStringType((string) $this->value);
81: }
82:
83: public function toInteger(): Type
84: {
85: return new ConstantIntegerType((int) $this->value);
86: }
87:
88: public function toAbsoluteNumber(): Type
89: {
90: return new self(abs($this->value));
91: }
92:
93: public function toArrayKey(): Type
94: {
95: return new ConstantIntegerType((int) $this->value);
96: }
97:
98: public function generalize(GeneralizePrecision $precision): Type
99: {
100: return new FloatType();
101: }
102:
103: /**
104: * @return ConstTypeNode
105: */
106: public function toPhpDocNode(): TypeNode
107: {
108: return new ConstTypeNode(new ConstExprFloatNode($this->castFloatToString($this->value)));
109: }
110:
111: }
112: