forked from phpstan/phpstan-src
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathExponentiateHelper.php
More file actions
131 lines (110 loc) · 2.93 KB
/
Copy pathExponentiateHelper.php
File metadata and controls
131 lines (110 loc) · 2.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
<?php declare(strict_types = 1);
namespace PHPStan\Type;
use PHPStan\Type\Constant\ConstantFloatType;
use PHPStan\Type\Constant\ConstantIntegerType;
use function is_float;
use function is_int;
use function is_numeric;
use function is_string;
use function pow;
final class ExponentiateHelper
{
public static function exponentiate(Type $base, Type $exponent): Type
{
if ($exponent instanceof UnionType) {
$results = [];
foreach ($exponent->getTypes() as $unionType) {
$results[] = self::exponentiate($base, $unionType);
}
return TypeCombinator::union(...$results);
}
if ($exponent instanceof NeverType) {
return new NeverType();
}
$allowedExponentTypes = new UnionType([
new IntegerType(),
new FloatType(),
new StringType(),
new BooleanType(),
new NullType(),
]);
if (!$allowedExponentTypes->isSuperTypeOf($exponent)->yes()) {
return new ErrorType();
}
if ($base instanceof ConstantScalarType) {
$result = self::exponentiateConstantScalar($base, $exponent);
if ($result !== null) {
return $result;
}
}
// exponentiation of a float, stays a float
$isFloatBase = $base->isFloat()->yes();
$isLooseZero = (new ConstantIntegerType(0))->isSuperTypeOf($exponent->toNumber());
if ($isLooseZero->yes()) {
if ($isFloatBase) {
return new ConstantFloatType(1);
}
return new ConstantIntegerType(1);
}
$isLooseOne = (new ConstantIntegerType(1))->isSuperTypeOf($exponent->toNumber());
if ($isLooseOne->yes()) {
$possibleResults = new UnionType([
new FloatType(),
new IntegerType(),
]);
if ($possibleResults->isSuperTypeOf($base)->yes()) {
return $base;
}
}
if ($isFloatBase) {
return new FloatType();
}
return new BenevolentUnionType([
new FloatType(),
new IntegerType(),
]);
}
private static function exponentiateConstantScalar(ConstantScalarType $base, Type $exponent): ?Type
{
if ($exponent instanceof IntegerRangeType) {
$min = null;
$max = null;
if ($exponent->getMin() !== null) {
$min = self::pow($base->getValue(), $exponent->getMin());
if ($min === null) {
return new ErrorType();
}
}
if ($exponent->getMax() !== null) {
$max = self::pow($base->getValue(), $exponent->getMax());
if ($max === null) {
return new ErrorType();
}
}
if (!is_float($min) && !is_float($max)) {
return IntegerRangeType::fromInterval($min, $max);
}
}
if ($exponent instanceof ConstantScalarType) {
$result = self::pow($base->getValue(), $exponent->getValue());
if ($result === null) {
return new ErrorType();
}
if (is_int($result)) {
return new ConstantIntegerType($result);
}
return new ConstantFloatType($result);
}
return null;
}
private static function pow(mixed $base, mixed $exp): float|int|null
{
if (is_string($base) && !is_numeric($base)) {
return null;
}
if (is_string($exp) && !is_numeric($exp)) {
return null;
}
return pow($base, $exp);
}
}