Skip to content

Commit 4c470a7

Browse files
committed
Add rules for operands in arithmetic operations
1 parent 2dfbf80 commit 4c470a7

16 files changed

+638
-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
@@ -15,6 +15,12 @@ rules:
1515
- PHPStan\Rules\DisallowedConstructs\DisallowedEmptyRule
1616
- PHPStan\Rules\DisallowedConstructs\DisallowedImplicitArrayCreationRule
1717
- PHPStan\Rules\Methods\WrongCaseOfInheritedMethodRule
18+
- PHPStan\Rules\Operators\OperandsInArithmeticAddition
19+
- PHPStan\Rules\Operators\OperandsInArithmeticDivision
20+
- PHPStan\Rules\Operators\OperandsInArithmeticExponentiation
21+
- PHPStan\Rules\Operators\OperandsInArithmeticModulo
22+
- PHPStan\Rules\Operators\OperandsInArithmeticMultiplication
23+
- PHPStan\Rules\Operators\OperandsInArithmeticSubtraction
1824
- PHPStan\Rules\StrictCalls\DynamicCallOnStaticMethodsRule
1925
- PHPStan\Rules\StrictCalls\StrictFunctionCallsRule
2026
- PHPStan\Rules\SwitchConditions\MatchingTypeInSwitchCaseConditionRule

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

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

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

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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+
9+
class OperatorRuleHelper
10+
{
11+
12+
public static function isValidForArithmeticOperation(Type $leftType, Type $rightType): bool
13+
{
14+
if (!$leftType instanceof IntegerType && !$leftType instanceof FloatType) {
15+
return false;
16+
}
17+
18+
if (!$rightType instanceof IntegerType && !$rightType instanceof FloatType) {
19+
return false;
20+
}
21+
22+
return true;
23+
}
24+
25+
}
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+
21,
21+
],
22+
[
23+
'Only numeric types or arrays are allowed in arithmetic addition.',
24+
22,
25+
],
26+
[
27+
'Only numeric types or arrays are allowed in arithmetic addition.',
28+
23,
29+
],
30+
[
31+
'Only numeric types or arrays are allowed in arithmetic addition.',
32+
24,
33+
],
34+
[
35+
'Only numeric types or arrays are allowed in arithmetic addition.',
36+
25,
37+
],
38+
[
39+
'Only numeric types or arrays are allowed in arithmetic addition.',
40+
25,
41+
],
42+
[
43+
'Only numeric types or arrays are allowed in arithmetic addition.',
44+
26,
45+
],
46+
[
47+
'Only numeric types or arrays are allowed in arithmetic addition.',
48+
26,
49+
],
50+
[
51+
'Only numeric types or arrays are allowed in arithmetic addition.',
52+
26,
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+
57,
21+
],
22+
[
23+
'Only numeric types are allowed in arithmetic division.',
24+
58,
25+
],
26+
[
27+
'Only numeric types are allowed in arithmetic division.',
28+
59,
29+
],
30+
[
31+
'Only numeric types are allowed in arithmetic division.',
32+
60,
33+
],
34+
[
35+
'Only numeric types are allowed in arithmetic division.',
36+
61,
37+
],
38+
[
39+
'Only numeric types are allowed in arithmetic division.',
40+
61,
41+
],
42+
[
43+
'Only numeric types are allowed in arithmetic division.',
44+
62,
45+
],
46+
[
47+
'Only numeric types are allowed in arithmetic division.',
48+
62,
49+
],
50+
[
51+
'Only numeric types are allowed in arithmetic division.',
52+
62,
53+
],
54+
]);
55+
}
56+
57+
}

0 commit comments

Comments
 (0)