diff --git a/README.md b/README.md index d911a55..0a035f4 100644 --- a/README.md +++ b/README.md @@ -227,10 +227,11 @@ class EnforceReadonlyPublicPropertyRule { ``` ### forbidArithmeticOperationOnNonNumber -- Disallows using [arithmetic operators](https://www.php.net/manual/en/language.operators.arithmetic.php) with non-numeric types (only float and int is allowed) +- Disallows using [arithmetic operators](https://www.php.net/manual/en/language.operators.arithmetic.php) with non-numeric types (only `float`, `int` and `BcMath\Number` is allowed) - You can allow numeric-string by using `allowNumericString: true` configuration - Modulo operator (`%`) allows only integers as it [emits deprecation otherwise](https://3v4l.org/VpVoq) - Plus operator is allowed for merging arrays +- `float` and `BcMath\Number` cannot be combined as it emits deprecations ```php function add(string $a, string $b) { diff --git a/src/Rule/ForbidArithmeticOperationOnNonNumberRule.php b/src/Rule/ForbidArithmeticOperationOnNonNumberRule.php index f95f947..af48809 100644 --- a/src/Rule/ForbidArithmeticOperationOnNonNumberRule.php +++ b/src/Rule/ForbidArithmeticOperationOnNonNumberRule.php @@ -18,7 +18,9 @@ use PHPStan\Rules\RuleErrorBuilder; use PHPStan\Type\FloatType; use PHPStan\Type\IntegerType; +use PHPStan\Type\ObjectType; use PHPStan\Type\Type; +use PHPStan\Type\TypeTraverser; use PHPStan\Type\UnionType; use PHPStan\Type\VerbosityLevel; use function sprintf; @@ -113,6 +115,12 @@ private function processBinary( return []; // array merge syntax } + if (($this->containsBcMathNumber($leftType) && $this->isFloat($rightType)) + || ($this->isFloat($leftType) && $this->containsBcMathNumber($rightType)) + ) { + return $this->buildBinaryErrors($operator, 'BcMath\\Number and float', $leftType, $rightType); + } + if ( $operator === '%' && (!$leftType->isInteger()->yes() || !$rightType->isInteger()->yes()) @@ -129,13 +137,13 @@ private function processBinary( private function isNumeric(Type $type): bool { - $int = new IntegerType(); - $float = new FloatType(); - $intOrFloat = new UnionType([$int, $float]); + $numericUnion = new UnionType([ + new IntegerType(), + new FloatType(), + new ObjectType('BcMath\Number'), + ]); - return $int->isSuperTypeOf($type)->yes() - || $float->isSuperTypeOf($type)->yes() - || $intOrFloat->isSuperTypeOf($type)->yes() + return $numericUnion->isSuperTypeOf($type)->yes() || ($this->allowNumericString && $type->isNumericString()->yes()); } @@ -164,4 +172,25 @@ private function buildBinaryErrors( return [$error]; } + private function isFloat(Type $type): bool + { + $float = new FloatType(); + + return $type->isSuperTypeOf($float)->yes(); + } + + private function containsBcMathNumber(Type $type): bool + { + $bcMathFound = false; + $bcMathNumber = new ObjectType('BcMath\Number'); + + TypeTraverser::map($type, static function (Type $traversedTyped, callable $traverse) use (&$bcMathFound, $bcMathNumber): Type { + if ($bcMathNumber->isSuperTypeOf($traversedTyped)->yes()) { + $bcMathFound = true; + } + return $traverse($traversedTyped); + }); + return $bcMathFound; + } + } diff --git a/tests/Rule/ForbidArithmeticOperationOnNonNumberRuleTest.php b/tests/Rule/ForbidArithmeticOperationOnNonNumberRuleTest.php index 458030e..81f938e 100644 --- a/tests/Rule/ForbidArithmeticOperationOnNonNumberRuleTest.php +++ b/tests/Rule/ForbidArithmeticOperationOnNonNumberRuleTest.php @@ -35,6 +35,18 @@ public function testNoNumericString(): void $this->analyseFile(__DIR__ . '/data/ForbidArithmeticOperationOnNonNumberRule/no-numeric-string.php'); } + public function testBcMathNumber(): void + { + $this->allowNumericString = true; + $this->analyseFile(__DIR__ . '/data/ForbidArithmeticOperationOnNonNumberRule/bcmath-number.php'); + } + + public function testBcMathNumberNoNumeric(): void + { + $this->allowNumericString = false; + $this->analyseFile(__DIR__ . '/data/ForbidArithmeticOperationOnNonNumberRule/bcmath-number-no-numeric.php'); + } + protected function shouldFailOnPhpErrors(): bool { return false; // https://github.com/phpstan/phpstan-src/pull/3031 diff --git a/tests/Rule/data/ForbidArithmeticOperationOnNonNumberRule/bcmath-number-no-numeric.php b/tests/Rule/data/ForbidArithmeticOperationOnNonNumberRule/bcmath-number-no-numeric.php new file mode 100644 index 0000000..7d198ef --- /dev/null +++ b/tests/Rule/data/ForbidArithmeticOperationOnNonNumberRule/bcmath-number-no-numeric.php @@ -0,0 +1,38 @@ +