Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 2091311

Browse files
lookymanondrejmirtes
authored andcommitted
Rule for checking a dynamic call on static methods
1 parent e4a2045 commit 2091311

File tree

5 files changed

+178
-0
lines changed

5 files changed

+178
-0
lines changed

‎README.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
* Functions `in_array` and `array_search` must be called with third parameter `$strict` set to `true` to search values with matching types only.
1111
* Variables assigned in `while` loop condition and `for` loop initial assignment cannot be used after the loop.
1212
* Types in `switch` condition and `case` value must match. PHP compares them loosely by default and that can lead to unexpected results.
13+
* Statically declared methods are called statically.
1314

1415
Additional rules are coming in subsequent releases!
1516

‎rules.neon‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ services:
3232
tags:
3333
- phpstan.rules.rule
3434

35+
-
36+
class: PHPStan\Rules\StrictCalls\DynamicCallOnStaticMethodsRule
37+
tags:
38+
- phpstan.rules.rule
39+
3540
-
3641
class: PHPStan\Rules\StrictCalls\StrictFunctionCallsRule
3742
tags:
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\StrictCalls;
4+
5+
use PhpParser\Node;
6+
use PhpParser\Node\Expr\MethodCall;
7+
use PHPStan\Analyser\Scope;
8+
use PHPStan\Rules\RuleLevelHelper;
9+
use PHPStan\Type\ErrorType;
10+
11+
class DynamicCallOnStaticMethodsRule implements \PHPStan\Rules\Rule
12+
{
13+
14+
/**
15+
* @var \PHPStan\Rules\RuleLevelHelper
16+
*/
17+
private $ruleLevelHelper;
18+
19+
public function __construct(RuleLevelHelper $ruleLevelHelper)
20+
{
21+
$this->ruleLevelHelper = $ruleLevelHelper;
22+
}
23+
24+
public function getNodeType(): string
25+
{
26+
return MethodCall::class;
27+
}
28+
29+
/**
30+
* @param \PhpParser\Node\Expr\MethodCall $node
31+
* @param \PHPStan\Analyser\Scope $scope
32+
* @return string[]
33+
*/
34+
public function processNode(Node $node, Scope $scope): array
35+
{
36+
if (!is_string($node->name)) {
37+
return [];
38+
}
39+
40+
$name = $node->name;
41+
$type = $this->ruleLevelHelper->findTypeToCheck(
42+
$scope,
43+
$node->var,
44+
''
45+
)->getType();
46+
47+
if ($type instanceof ErrorType || !$type->canCallMethods() || !$type->hasMethod($name)) {
48+
return [];
49+
}
50+
51+
$methodReflection = $type->getMethod($name, $scope);
52+
if ($methodReflection->isStatic()) {
53+
return [sprintf(
54+
'Dynamic call to static method %s::%s().',
55+
$methodReflection->getDeclaringClass()->getDisplayName(),
56+
$methodReflection->getName()
57+
)];
58+
}
59+
60+
return [];
61+
}
62+
63+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\StrictCalls;
4+
5+
use PHPStan\Rules\Rule;
6+
use PHPStan\Rules\RuleLevelHelper;
7+
8+
class DynamicCallOnStaticMethodsRuleTest extends \PHPStan\Testing\RuleTestCase
9+
{
10+
11+
/**
12+
* @var bool
13+
*/
14+
private $checkThisOnly;
15+
16+
protected function getRule(): Rule
17+
{
18+
$broker = $this->createBroker();
19+
$ruleLevelHelper = new RuleLevelHelper($broker, true, $this->checkThisOnly, true);
20+
return new DynamicCallOnStaticMethodsRule($ruleLevelHelper);
21+
}
22+
23+
public function testRule(): void
24+
{
25+
$this->checkThisOnly = false;
26+
$this->analyse([__DIR__ . '/data/dynamic-calls-on-static-methods.php'], [
27+
[
28+
'Dynamic call to static method StrictCalls\ClassWithStaticMethod::foo().',
29+
14,
30+
],
31+
[
32+
'Dynamic call to static method StrictCalls\ClassWithStaticMethod::foo().',
33+
21,
34+
],
35+
[
36+
'Dynamic call to static method StrictCalls\ClassUsingTrait::foo().',
37+
34,
38+
],
39+
[
40+
'Dynamic call to static method StrictCalls\ClassUsingTrait::foo().',
41+
46,
42+
],
43+
]);
44+
}
45+
46+
public function testRuleOnThisOnly(): void
47+
{
48+
$this->checkThisOnly = true;
49+
$this->analyse([__DIR__ . '/data/dynamic-calls-on-static-methods.php'], [
50+
[
51+
'Dynamic call to static method StrictCalls\ClassWithStaticMethod::foo().',
52+
14,
53+
],
54+
[
55+
'Dynamic call to static method StrictCalls\ClassUsingTrait::foo().',
56+
34,
57+
],
58+
]);
59+
}
60+
61+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
namespace StrictCalls;
4+
5+
class ClassWithStaticMethod
6+
{
7+
public static function foo()
8+
{
9+
10+
}
11+
12+
public function bar()
13+
{
14+
$this->foo();
15+
$this->bar();
16+
}
17+
}
18+
19+
function () {
20+
$classWithStaticMethod = new ClassWithStaticMethod();
21+
$classWithStaticMethod->foo();
22+
$classWithStaticMethod->bar();
23+
};
24+
25+
trait TraitWithStaticMethod
26+
{
27+
public static function foo()
28+
{
29+
30+
}
31+
32+
public function bar()
33+
{
34+
$this->foo();
35+
$this->bar();
36+
}
37+
}
38+
39+
class ClassUsingTrait
40+
{
41+
use TraitWithStaticMethod;
42+
}
43+
44+
function () {
45+
$classUsingTrait = new ClassUsingTrait();
46+
$classUsingTrait->foo();
47+
$classUsingTrait->bar();
48+
};

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /