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: if (is_nan($value)) {
53: return 'NAN';
54: }
55:
56: $valueStr = (string) $value;
57: if (is_finite($value) && !str_contains($valueStr, '.')) {
58: $valueStr .= '.0';
59: }
60:
61: return $valueStr;
62: } finally {
63: ini_set('precision', $precisionBackup);
64: }
65: }
66:
67: public function describe(VerbosityLevel $level): string
68: {
69: return $level->handle(
70: static fn (): string => 'float',
71: fn (): string => $this->castFloatToString($this->value),
72: );
73: }
74:
75: public function toString(): Type
76: {
77: if ($this->value === 0.0) {
78: return new UnionType([
79: new ConstantStringType('0'),
80: new ConstantStringType('-0'),
81: ]);
82: }
83:
84: return new ConstantStringType((string) $this->value);
85: }
86:
87: public function toInteger(): Type
88: {
89: return new ConstantIntegerType((int) $this->value);
90: }
91:
92: public function toAbsoluteNumber(): Type
93: {
94: return new self(abs($this->value));
95: }
96:
97: public function toArrayKey(): Type
98: {
99: return new ConstantIntegerType((int) $this->value);
100: }
101:
102: public function generalize(GeneralizePrecision $precision): Type
103: {
104: return new FloatType();
105: }
106:
107: /**
108: * @return ConstTypeNode
109: */
110: public function toPhpDocNode(): TypeNode
111: {
112: return new ConstTypeNode(new ConstExprFloatNode($this->castFloatToString($this->value)));
113: }
114:
115: }
116: