Skip to content

Commit

Permalink
Bleeding edge - private method called through static::
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Aug 17, 2021
1 parent 716fe52 commit bad2607
Show file tree
Hide file tree
Showing 8 changed files with 154 additions and 1 deletion.
1 change: 1 addition & 0 deletions conf/bleedingEdge.neon
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@ parameters:
crossCheckInterfaces: true
finalByPhpDocTag: true
classConstants: true
privateStaticCall: true
stubFiles:
- ../stubs/arrayFunctions.stub
4 changes: 4 additions & 0 deletions conf/config.level2.neon
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ parameters:
conditionalTags:
PHPStan\Rules\PhpDoc\IncompatibleClassConstantPhpDocTypeRule:
phpstan.rules.rule: %featureToggles.classConstants%
PHPStan\Rules\Methods\CallPrivateMethodThroughStaticRule:
phpstan.rules.rule: %featureToggles.privateStaticCall%

rules:
- PHPStan\Rules\Cast\EchoRule
Expand Down Expand Up @@ -54,6 +56,8 @@ services:
crossCheckInterfaces: %featureToggles.crossCheckInterfaces%
tags:
- phpstan.rules.rule
-
class: PHPStan\Rules\Methods\CallPrivateMethodThroughStaticRule
-
class: PHPStan\Rules\PhpDoc\IncompatibleClassConstantPhpDocTypeRule
-
Expand Down
4 changes: 3 additions & 1 deletion conf/config.neon
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ parameters:
crossCheckInterfaces: false
finalByPhpDocTag: false
classConstants: false
privateStaticCall: false
fileExtensions:
- php
checkAlwaysTrueCheckTypeFunctionCall: false
Expand Down Expand Up @@ -227,7 +228,8 @@ parametersSchema:
validateOverridingMethodsInStubs: bool(),
crossCheckInterfaces: bool(),
finalByPhpDocTag: bool(),
classConstants: bool()
classConstants: bool(),
privateStaticCall: bool()
])
fileExtensions: listOf(string())
checkAlwaysTrueCheckTypeFunctionCall: bool()
Expand Down
61 changes: 61 additions & 0 deletions src/Rules/Methods/CallPrivateMethodThroughStaticRule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php declare(strict_types = 1);

namespace PHPStan\Rules\Methods;

use PhpParser\Node;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Name;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;

/**
* @implements Rule<StaticCall>
*/
class CallPrivateMethodThroughStaticRule implements Rule
{

public function getNodeType(): string
{
return StaticCall::class;
}

public function processNode(Node $node, Scope $scope): array
{
if (!$node->name instanceof Node\Identifier) {
return [];
}
if (!$node->class instanceof Name) {
return [];
}

$methodName = $node->name->name;
$className = $node->class;
if ($className->toLowerString() !== 'static') {
return [];
}

$classType = $scope->resolveTypeByName($className);
if (!$classType->hasMethod($methodName)->yes()) {
return [];
}

$method = $classType->getMethod($methodName, $scope);
if (!$method->isPrivate()) {
return [];
}

if ($scope->isInClass() && $scope->getClassReflection()->isFinal()) {
return [];
}

return [
RuleErrorBuilder::message(sprintf(
'Unsafe call to private method %s::%s() through static::.',
$method->getDeclaringClass()->getDisplayName(),
$method->getName()
))->build(),
];
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php declare(strict_types = 1);

namespace PHPStan\Rules\Methods;

use PHPStan\Testing\RuleTestCase;

/**
* @extends RuleTestCase<CallPrivateMethodThroughStaticRule>
*/
class CallPrivateMethodThroughStaticRuleTest extends RuleTestCase
{

protected function getRule(): \PHPStan\Rules\Rule
{
return new CallPrivateMethodThroughStaticRule();
}

public function testRule(): void
{
$this->analyse([__DIR__ . '/data/call-private-method-static.php'], [
[
'Unsafe call to private method CallPrivateMethodThroughStatic\Foo::doBar() through static::.',
12,
],
]);
}

}
4 changes: 4 additions & 0 deletions tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,10 @@ public function testCallStaticMethods(): void
328,
'Learn more at https://phpstan.org/user-guide/discovering-symbols',
],
[
'Call to an undefined static method static(CallStaticMethods\CallWithStatic)::nonexistent().',
344,
],
]);
}

Expand Down
37 changes: 37 additions & 0 deletions tests/PHPStan/Rules/Methods/data/call-private-method-static.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace CallPrivateMethodThroughStatic;

class Foo
{

public function doFoo()
{
static::doFoo();
static::nonexistent();
static::doBar();
}

private function doBar()
{

}

}

final class Bar
{

public function doFoo()
{
static::doFoo();
static::nonexistent();
static::doBar();
}

private function doBar()
{

}

}
16 changes: 16 additions & 0 deletions tests/PHPStan/Rules/Methods/data/call-static-methods.php
Original file line number Diff line number Diff line change
Expand Up @@ -329,3 +329,19 @@ public function doBar(TraitWithStaticMethod $a): void
}

}

class CallWithStatic
{

private function doFoo()
{

}

public function doBar()
{
static::doFoo(); // reported by different rule
static::nonexistent();
}

}

0 comments on commit bad2607

Please sign in to comment.