diff --git a/src/Type/ExponentiateHelper.php b/src/Type/ExponentiateHelper.php index 4e62ed9a5d..10da4dd484 100644 --- a/src/Type/ExponentiateHelper.php +++ b/src/Type/ExponentiateHelper.php @@ -6,6 +6,9 @@ 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 { @@ -83,10 +86,16 @@ private static function exponentiateConstantScalar(ConstantScalarType $base, Typ $min = null; $max = null; if ($exponent->getMin() !== null) { - $min = $base->getValue() ** $exponent->getMin(); + $min = self::pow($base->getValue(), $exponent->getMin()); + if ($min === null) { + return new ErrorType(); + } } if ($exponent->getMax() !== null) { - $max = $base->getValue() ** $exponent->getMax(); + $max = self::pow($base->getValue(), $exponent->getMax()); + if ($max === null) { + return new ErrorType(); + } } if (!is_float($min) && !is_float($max)) { @@ -95,7 +104,11 @@ private static function exponentiateConstantScalar(ConstantScalarType $base, Typ } if ($exponent instanceof ConstantScalarType) { - $result = $base->getValue() ** $exponent->getValue(); + $result = self::pow($base->getValue(), $exponent->getValue()); + if ($result === null) { + return new ErrorType(); + } + if (is_int($result)) { return new ConstantIntegerType($result); } @@ -105,4 +118,15 @@ private static function exponentiateConstantScalar(ConstantScalarType $base, Typ 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); + } + } diff --git a/tests/PHPStan/Analyser/nsrt/pow.php b/tests/PHPStan/Analyser/nsrt/pow.php index 82f03b1bae..328de3f172 100644 --- a/tests/PHPStan/Analyser/nsrt/pow.php +++ b/tests/PHPStan/Analyser/nsrt/pow.php @@ -177,3 +177,23 @@ function doFoo(int $intA, int $intB, string $s, bool $bool, $numericS, float $fl assertType('*ERROR*', $bool ** $arr); assertType('*ERROR*', $bool ** []); }; + +function invalidConstantOperands(): void { + assertType('*ERROR*', 'a' ** 1); + assertType('*ERROR*', 1 ** 'a'); + + assertType('*ERROR*', [] ** 1); + assertType('*ERROR*', 1 ** []); + + assertType('*ERROR*', (new \stdClass()) ** 1); + assertType('*ERROR*', 1 ** (new \stdClass())); +} + +function validConstantOperands(): void { + assertType('1', '1' ** 1); + assertType('1', 1 ** '1'); + assertType('1', '1' ** '1'); + + assertType('1', true ** 1); + assertType('1', 1 ** false); +}