Skip to content

Commit 2baaf8e

Browse files
committed
Add rules for operands in arithmetic operations
1 parent a323b04 commit 2baaf8e

16 files changed

+645
-0
lines changed

Diff for: README.md

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
[PHPStan](https://github.com/phpstan/phpstan) focuses on finding bugs in your code. But in PHP there's a lot of leeway in how stuff can be written. This repository contains additional rules that revolve around strictly and strongly typed code with no loose casting for those who want additional safety in extremely defensive programming:
88

99
* Require booleans in `if`, `elseif`, ternary operator, after `!`, and on both sides of `&&` and `||`.
10+
* Require numeric operands or arrays in `+` and numeric operands in `-`/`*`/`/`/`**`/`%`.
1011
* These functions contain a `$strict` parameter for better type safety, it must be set to `true`:
1112
* `in_array` (3rd parameter)
1213
* `array_search` (3rd parameter)

Diff for: rules.neon

+6
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ rules:
1717
- PHPStan\Rules\Methods\MissingMethodParameterTypehintRule
1818
- PHPStan\Rules\Methods\MissingMethodReturnTypehintRule
1919
- PHPStan\Rules\Methods\WrongCaseOfInheritedMethodRule
20+
- PHPStan\Rules\Operators\OperandsInArithmeticAddition
21+
- PHPStan\Rules\Operators\OperandsInArithmeticDivision
22+
- PHPStan\Rules\Operators\OperandsInArithmeticExponentiation
23+
- PHPStan\Rules\Operators\OperandsInArithmeticModulo
24+
- PHPStan\Rules\Operators\OperandsInArithmeticMultiplication
25+
- PHPStan\Rules\Operators\OperandsInArithmeticSubtraction
2026
- PHPStan\Rules\Properties\MissingPropertyTypehintRule
2127
- PHPStan\Rules\StrictCalls\DynamicCallOnStaticMethodsRule
2228
- PHPStan\Rules\StrictCalls\StrictFunctionCallsRule

Diff for: src/Rules/Operators/OperandsInArithmeticAddition.php

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Operators;
4+
5+
use PHPStan\Type\ArrayType;
6+
use PHPStan\Type\MixedType;
7+
8+
class OperandsInArithmeticAddition implements \PHPStan\Rules\Rule
9+
{
10+
11+
public function getNodeType(): string
12+
{
13+
return \PhpParser\Node\Expr\BinaryOp\Plus::class;
14+
}
15+
16+
/**
17+
* @param \PhpParser\Node\Expr\BinaryOp\BooleanAnd $node
18+
* @param \PHPStan\Analyser\Scope $scope
19+
* @return string[] errors
20+
*/
21+
public function processNode(\PhpParser\Node $node, \PHPStan\Analyser\Scope $scope): array
22+
{
23+
$leftType = $scope->getType($node->left);
24+
$rightType = $scope->getType($node->right);
25+
26+
if (OperatorRuleHelper::isValidForArithmeticOperation($leftType, $rightType)) {
27+
return [];
28+
}
29+
30+
$mixedArrayType = new ArrayType(new MixedType(), new MixedType());
31+
32+
if ($mixedArrayType->isSuperTypeOf($leftType)->yes() && $mixedArrayType->isSuperTypeOf($rightType)->yes()) {
33+
return [];
34+
}
35+
36+
return ['Only numeric types or arrays are allowed in arithmetic addition.'];
37+
}
38+
39+
}

Diff for: src/Rules/Operators/OperandsInArithmeticDivision.php

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Operators;
4+
5+
class OperandsInArithmeticDivision implements \PHPStan\Rules\Rule
6+
{
7+
8+
public function getNodeType(): string
9+
{
10+
return \PhpParser\Node\Expr\BinaryOp\Div::class;
11+
}
12+
13+
/**
14+
* @param \PhpParser\Node\Expr\BinaryOp\BooleanAnd $node
15+
* @param \PHPStan\Analyser\Scope $scope
16+
* @return string[] errors
17+
*/
18+
public function processNode(\PhpParser\Node $node, \PHPStan\Analyser\Scope $scope): array
19+
{
20+
$leftType = $scope->getType($node->left);
21+
$rightType = $scope->getType($node->right);
22+
23+
if (OperatorRuleHelper::isValidForArithmeticOperation($leftType, $rightType)) {
24+
return [];
25+
}
26+
27+
return ['Only numeric types are allowed in arithmetic division.'];
28+
}
29+
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Operators;
4+
5+
class OperandsInArithmeticExponentiation implements \PHPStan\Rules\Rule
6+
{
7+
8+
public function getNodeType(): string
9+
{
10+
return \PhpParser\Node\Expr\BinaryOp\Pow::class;
11+
}
12+
13+
/**
14+
* @param \PhpParser\Node\Expr\BinaryOp\BooleanAnd $node
15+
* @param \PHPStan\Analyser\Scope $scope
16+
* @return string[] errors
17+
*/
18+
public function processNode(\PhpParser\Node $node, \PHPStan\Analyser\Scope $scope): array
19+
{
20+
$leftType = $scope->getType($node->left);
21+
$rightType = $scope->getType($node->right);
22+
23+
if (OperatorRuleHelper::isValidForArithmeticOperation($leftType, $rightType)) {
24+
return [];
25+
}
26+
27+
return ['Only numeric types are allowed in arithmetic exponentiation.'];
28+
}
29+
30+
}

Diff for: src/Rules/Operators/OperandsInArithmeticModulo.php

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Operators;
4+
5+
class OperandsInArithmeticModulo implements \PHPStan\Rules\Rule
6+
{
7+
8+
public function getNodeType(): string
9+
{
10+
return \PhpParser\Node\Expr\BinaryOp\Mod::class;
11+
}
12+
13+
/**
14+
* @param \PhpParser\Node\Expr\BinaryOp\BooleanAnd $node
15+
* @param \PHPStan\Analyser\Scope $scope
16+
* @return string[] errors
17+
*/
18+
public function processNode(\PhpParser\Node $node, \PHPStan\Analyser\Scope $scope): array
19+
{
20+
$leftType = $scope->getType($node->left);
21+
$rightType = $scope->getType($node->right);
22+
23+
if (OperatorRuleHelper::isValidForArithmeticOperation($leftType, $rightType)) {
24+
return [];
25+
}
26+
27+
return ['Only numeric types are allowed in arithmetic modulo.'];
28+
}
29+
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Operators;
4+
5+
class OperandsInArithmeticMultiplication implements \PHPStan\Rules\Rule
6+
{
7+
8+
public function getNodeType(): string
9+
{
10+
return \PhpParser\Node\Expr\BinaryOp\Mul::class;
11+
}
12+
13+
/**
14+
* @param \PhpParser\Node\Expr\BinaryOp\BooleanAnd $node
15+
* @param \PHPStan\Analyser\Scope $scope
16+
* @return string[] errors
17+
*/
18+
public function processNode(\PhpParser\Node $node, \PHPStan\Analyser\Scope $scope): array
19+
{
20+
$leftType = $scope->getType($node->left);
21+
$rightType = $scope->getType($node->right);
22+
23+
if (OperatorRuleHelper::isValidForArithmeticOperation($leftType, $rightType)) {
24+
return [];
25+
}
26+
27+
return ['Only numeric types are allowed in arithmetic multiplication.'];
28+
}
29+
30+
}
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Operators;
4+
5+
class OperandsInArithmeticSubtraction implements \PHPStan\Rules\Rule
6+
{
7+
8+
public function getNodeType(): string
9+
{
10+
return \PhpParser\Node\Expr\BinaryOp\Minus::class;
11+
}
12+
13+
/**
14+
* @param \PhpParser\Node\Expr\BinaryOp\BooleanAnd $node
15+
* @param \PHPStan\Analyser\Scope $scope
16+
* @return string[] errors
17+
*/
18+
public function processNode(\PhpParser\Node $node, \PHPStan\Analyser\Scope $scope): array
19+
{
20+
$leftType = $scope->getType($node->left);
21+
$rightType = $scope->getType($node->right);
22+
23+
if (OperatorRuleHelper::isValidForArithmeticOperation($leftType, $rightType)) {
24+
return [];
25+
}
26+
27+
return ['Only numeric types are allowed in arithmetic subtraction.'];
28+
}
29+
30+
}

Diff for: src/Rules/Operators/OperatorRuleHelper.php

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Operators;
4+
5+
use PHPStan\Type\FloatType;
6+
use PHPStan\Type\IntegerType;
7+
use PHPStan\Type\Type;
8+
use PHPStan\Type\UnionType;
9+
10+
class OperatorRuleHelper
11+
{
12+
13+
public static function isValidForArithmeticOperation(Type $leftType, Type $rightType): bool
14+
{
15+
$acceptedType = new UnionType([new IntegerType(), new FloatType()]);
16+
17+
return $acceptedType->isSuperTypeOf($leftType)->yes() && $acceptedType->isSuperTypeOf($rightType)->yes();
18+
}
19+
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Operators;
4+
5+
use PHPStan\Rules\Rule;
6+
7+
class OperandsInArithmeticAdditionTest extends \PHPStan\Testing\RuleTestCase
8+
{
9+
10+
protected function getRule(): Rule
11+
{
12+
return new OperandsInArithmeticAddition();
13+
}
14+
15+
public function testRule(): void
16+
{
17+
$this->analyse([__DIR__ . '/data/operators.php'], [
18+
[
19+
'Only numeric types or arrays are allowed in arithmetic addition.',
20+
25,
21+
],
22+
[
23+
'Only numeric types or arrays are allowed in arithmetic addition.',
24+
26,
25+
],
26+
[
27+
'Only numeric types or arrays are allowed in arithmetic addition.',
28+
27,
29+
],
30+
[
31+
'Only numeric types or arrays are allowed in arithmetic addition.',
32+
28,
33+
],
34+
[
35+
'Only numeric types or arrays are allowed in arithmetic addition.',
36+
29,
37+
],
38+
[
39+
'Only numeric types or arrays are allowed in arithmetic addition.',
40+
29,
41+
],
42+
[
43+
'Only numeric types or arrays are allowed in arithmetic addition.',
44+
30,
45+
],
46+
[
47+
'Only numeric types or arrays are allowed in arithmetic addition.',
48+
30,
49+
],
50+
[
51+
'Only numeric types or arrays are allowed in arithmetic addition.',
52+
30,
53+
],
54+
]);
55+
}
56+
57+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Operators;
4+
5+
use PHPStan\Rules\Rule;
6+
7+
class OperandsInArithmeticDivisionTest extends \PHPStan\Testing\RuleTestCase
8+
{
9+
10+
protected function getRule(): Rule
11+
{
12+
return new OperandsInArithmeticDivision();
13+
}
14+
15+
public function testRule(): void
16+
{
17+
$this->analyse([__DIR__ . '/data/operators.php'], [
18+
[
19+
'Only numeric types are allowed in arithmetic division.',
20+
64,
21+
],
22+
[
23+
'Only numeric types are allowed in arithmetic division.',
24+
65,
25+
],
26+
[
27+
'Only numeric types are allowed in arithmetic division.',
28+
66,
29+
],
30+
[
31+
'Only numeric types are allowed in arithmetic division.',
32+
67,
33+
],
34+
[
35+
'Only numeric types are allowed in arithmetic division.',
36+
68,
37+
],
38+
[
39+
'Only numeric types are allowed in arithmetic division.',
40+
68,
41+
],
42+
[
43+
'Only numeric types are allowed in arithmetic division.',
44+
69,
45+
],
46+
[
47+
'Only numeric types are allowed in arithmetic division.',
48+
69,
49+
],
50+
[
51+
'Only numeric types are allowed in arithmetic division.',
52+
69,
53+
],
54+
]);
55+
}
56+
57+
}

0 commit comments

Comments
 (0)