Skip to content

Commit 9bb6fb2

Browse files
authored
Merge pull request #914 from ergebnis/feature/no-assign-by-reference
Enhancement: Implement `NoAssignByReferenceRule`
2 parents 45d1852 + 3b45950 commit 9bb6fb2

File tree

9 files changed

+142
-0
lines changed

9 files changed

+142
-0
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ For a full diff see [`2.7.0...main`][2.7.0...main].
1111
### Added
1212

1313
- Added `allRules` parameter to allow disabling and enabling all rules ([#913]), by [@localheinz]
14+
- Added `Expressions\NoAssignByReferenceRule`, which reports an error when a variable is assigned by reference ([#914]), by [@localheinz]
1415

1516
## [`2.7.0`][2.7.0]
1617

@@ -617,6 +618,7 @@ For a full diff see [`362c7ea...0.1.0`][362c7ea...0.1.0].
617618
[#911]: https://github.com/ergebnis/phpstan-rules/pull/911
618619
[#912]: https://github.com/ergebnis/phpstan-rules/pull/912
619620
[#913]: https://github.com/ergebnis/phpstan-rules/pull/913
621+
[#914]: https://github.com/ergebnis/phpstan-rules/pull/914
620622

621623
[@cosmastech]: https://github.com/cosmastech
622624
[@enumag]: https://github.com/enumag

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ This package provides the following rules for use with [`phpstan/phpstan`](https
5151
- [`Ergebnis\PHPStan\Rules\Closures\NoParameterPassedByReferenceRule`](https://github.com/ergebnis/phpstan-rules#closuresnoparameterpassedbyreferencerule)
5252
- [`Ergebnis\PHPStan\Rules\Closures\NoParameterWithNullableTypeDeclarationRule`](https://github.com/ergebnis/phpstan-rules#closuresnoparameterwithnullabletypedeclarationrule)
5353
- [`Ergebnis\PHPStan\Rules\Closures\NoParameterWithNullDefaultValueRule`](https://github.com/ergebnis/phpstan-rules#closuresnoparameterwithnulldefaultvaluerule)
54+
- [`Ergebnis\PHPStan\Rules\Expressions\NoAssignByReferenceRule`](https://github.com/ergebnis/phpstan-rules#expressionsnoassignbyreferencerule)
5455
- [`Ergebnis\PHPStan\Rules\Expressions\NoCompactRule`](https://github.com/ergebnis/phpstan-rules#expressionsnocompactrule)
5556
- [`Ergebnis\PHPStan\Rules\Expressions\NoErrorSuppressionRule`](https://github.com/ergebnis/phpstan-rules#expressionsnoerrorsuppressionrule)
5657
- [`Ergebnis\PHPStan\Rules\Expressions\NoEvalRule`](https://github.com/ergebnis/phpstan-rules#expressionsnoevalrule)
@@ -235,6 +236,21 @@ parameters:
235236

236237
### Expressions
237238

239+
#### `Expressions\NoAssignByReferenceRule`
240+
241+
This rule reports an error when [a variable is assigned by reference](https://www.php.net/manual/en/language.references.whatdo.php#language.references.whatdo.assign).
242+
243+
##### Disabling the rule
244+
245+
You can set the `enabled` parameter to `false` to disable this rule.
246+
247+
```neon
248+
parameters:
249+
ergebnis:
250+
noAssignByReference:
251+
enabled: false
252+
```
253+
238254
#### `Expressions\NoCompactRule`
239255

240256
This rule reports an error when the function [`compact()`](https://www.php.net/compact) is used.

composer-require-checker.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"null",
88
"PhpParser\\Comment\\Doc",
99
"PhpParser\\Node",
10+
"PhpParser\\Node\\Expr\\AssignRef",
1011
"PhpParser\\Node\\Expr\\Closure",
1112
"PhpParser\\Node\\Expr\\ConstFetch",
1213
"PhpParser\\Node\\Expr\\ErrorSuppress",

rules.neon

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ conditionalTags:
1111
phpstan.rules.rule: %ergebnis.noParameterPassedByReference.enabled%
1212
Ergebnis\PHPStan\Rules\Closures\NoParameterWithNullableTypeDeclarationRule:
1313
phpstan.rules.rule: %ergebnis.noParameterWithNullableTypeDeclaration.enabled%
14+
Ergebnis\PHPStan\Rules\Expressions\NoAssignByReferenceRule:
15+
phpstan.rules.rule: %ergebnis.noAssignByReference.enabled%
1416
Ergebnis\PHPStan\Rules\Expressions\NoCompactRule:
1517
phpstan.rules.rule: %ergebnis.noCompact.enabled%
1618
Ergebnis\PHPStan\Rules\Expressions\NoErrorSuppressionRule:
@@ -63,6 +65,8 @@ parameters:
6365
enabled: %ergebnis.allRules%
6466
finalInAbstractClass:
6567
enabled: %ergebnis.allRules%
68+
noAssignByReference:
69+
enabled: %ergebnis.allRules%
6670
noCompact:
6771
enabled: %ergebnis.allRules%
6872
noConstructorParameterWithDefaultValue:
@@ -112,6 +116,9 @@ parametersSchema:
112116
finalInAbstractClass: structure([
113117
enabled: bool(),
114118
])
119+
noAssignByReference: structure([
120+
enabled: bool(),
121+
])
115122
noCompact: structure([
116123
enabled: bool(),
117124
])
@@ -189,6 +196,9 @@ services:
189196
-
190197
class: Ergebnis\PHPStan\Rules\Closures\NoParameterWithNullableTypeDeclarationRule
191198

199+
-
200+
class: Ergebnis\PHPStan\Rules\Expressions\NoAssignByReferenceRule
201+
192202
-
193203
class: Ergebnis\PHPStan\Rules\Expressions\NoCompactRule
194204

src/ErrorIdentifier.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ public static function noConstructorParameterWithDefaultValue(): self
5050
return new self('noConstructorParameterWithDefaultValue');
5151
}
5252

53+
public static function noAssignByReference(): self
54+
{
55+
return new self('noAssignByReference');
56+
}
57+
5358
public static function noErrorSuppression(): self
5459
{
5560
return new self('noErrorSuppression');
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* Copyright (c) 2018-2025 Andreas Möller
7+
*
8+
* For the full copyright and license information, please view
9+
* the LICENSE.md file that was distributed with this source code.
10+
*
11+
* @see https://github.com/ergebnis/phpstan-rules
12+
*/
13+
14+
namespace Ergebnis\PHPStan\Rules\Expressions;
15+
16+
use Ergebnis\PHPStan\Rules\ErrorIdentifier;
17+
use PhpParser\Node;
18+
use PHPStan\Analyser;
19+
use PHPStan\Rules;
20+
21+
/**
22+
* @implements Rules\Rule<Node\Expr\AssignRef>
23+
*/
24+
final class NoAssignByReferenceRule implements Rules\Rule
25+
{
26+
public function getNodeType(): string
27+
{
28+
return Node\Expr\AssignRef::class;
29+
}
30+
31+
public function processNode(
32+
Node $node,
33+
Analyser\Scope $scope
34+
): array {
35+
return [
36+
Rules\RuleErrorBuilder::message('Assign by reference should not be used.')
37+
->identifier(ErrorIdentifier::noAssignByReference()->toString())
38+
->build(),
39+
];
40+
}
41+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Ergebnis\PHPStan\Rules\Test\Fixture\Expressions\NoAssignByReferenceRule;
6+
7+
$foo = 'bar';
8+
9+
$bar = $foo;
10+
11+
$baz = &$foo;
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* Copyright (c) 2018-2025 Andreas Möller
7+
*
8+
* For the full copyright and license information, please view
9+
* the LICENSE.md file that was distributed with this source code.
10+
*
11+
* @see https://github.com/ergebnis/phpstan-rules
12+
*/
13+
14+
namespace Ergebnis\PHPStan\Rules\Test\Integration\Expressions;
15+
16+
use Ergebnis\PHPStan\Rules\Expressions;
17+
use Ergebnis\PHPStan\Rules\Test;
18+
use PHPStan\Rules;
19+
use PHPStan\Testing;
20+
21+
/**
22+
* @covers \Ergebnis\PHPStan\Rules\Expressions\NoAssignByReferenceRule
23+
*
24+
* @uses \Ergebnis\PHPStan\Rules\ErrorIdentifier
25+
*
26+
* @extends Testing\RuleTestCase<Expressions\NoAssignByReferenceRule>
27+
*/
28+
final class NoAssignByReferenceRuleTest extends Testing\RuleTestCase
29+
{
30+
use Test\Util\Helper;
31+
32+
public function testNoAssignByReferenceRule(): void
33+
{
34+
$this->analyse(
35+
self::phpFilesIn(__DIR__ . '/../../Fixture/Expressions/NoAssignByReferenceRule'),
36+
[
37+
[
38+
'Assign by reference should not be used.',
39+
11,
40+
],
41+
],
42+
);
43+
}
44+
45+
protected function getRule(): Rules\Rule
46+
{
47+
return new Expressions\NoAssignByReferenceRule();
48+
}
49+
}

test/Unit/ErrorIdentifierTest.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@ public function testFinalReturnsErrorIdentifier(): void
4242
self::assertSame('ergebnis.final', $errorIdentifier->toString());
4343
}
4444

45+
public function testNoAssignByReferenceReturnsErrorIdentifier(): void
46+
{
47+
$errorIdentifier = ErrorIdentifier::noAssignByReference();
48+
49+
self::assertSame('ergebnis.noAssignByReference', $errorIdentifier->toString());
50+
}
51+
4552
public function testNoCompactReturnsErrorIdentifier(): void
4653
{
4754
$errorIdentifier = ErrorIdentifier::noCompact();

0 commit comments

Comments
 (0)