Skip to content

Commit

Permalink
Bleeding edge - private constant accessed through static::
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Aug 17, 2021
1 parent d8e8953 commit 270326a
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 3 deletions.
4 changes: 4 additions & 0 deletions conf/config.level2.neon
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ parameters:
checkPhpDocMissingReturn: true

conditionalTags:
PHPStan\Rules\Classes\AccessPrivateConstantThroughStaticRule:
phpstan.rules.rule: %featureToggles.privateStaticCall%
PHPStan\Rules\PhpDoc\IncompatibleClassConstantPhpDocTypeRule:
phpstan.rules.rule: %featureToggles.classConstants%
PHPStan\Rules\Methods\CallPrivateMethodThroughStaticRule:
Expand Down Expand Up @@ -40,6 +42,8 @@ rules:
- PHPStan\Rules\PhpDoc\InvalidThrowsPhpDocValueRule

services:
-
class: PHPStan\Rules\Classes\AccessPrivateConstantThroughStaticRule
-
class: PHPStan\Rules\Classes\MixinRule
arguments:
Expand Down
60 changes: 60 additions & 0 deletions src/Rules/Classes/AccessPrivateConstantThroughStaticRule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php declare(strict_types = 1);

namespace PHPStan\Rules\Classes;

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

/**
* @implements Rule<Node\Expr\ClassConstFetch>
*/
class AccessPrivateConstantThroughStaticRule implements Rule
{

public function getNodeType(): string
{
return Node\Expr\ClassConstFetch::class;
}

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

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

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

$constant = $classType->getConstant($constantName);
if (!$constant->isPrivate()) {
return [];
}

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

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

}
2 changes: 1 addition & 1 deletion src/Type/Php/ArrayFillFunctionReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
if (
$startIndexType instanceof ConstantIntegerType
&& $numberType instanceof ConstantIntegerType
&& $numberType->getValue() <= static::MAX_SIZE_USE_CONSTANT_ARRAY
&& $numberType->getValue() <= self::MAX_SIZE_USE_CONSTANT_ARRAY
) {
$arrayBuilder = ConstantArrayTypeBuilder::createEmpty();
$nextIndex = $startIndexType->getValue();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php declare(strict_types = 1);

namespace PHPStan\Rules\Classes;

use PHPStan\Rules\Rule;
use PHPStan\Testing\RuleTestCase;

/**
* @extends RuleTestCase<AccessPrivateConstantThroughStaticRule>
*/
class AccessPrivateConstantThroughStaticRuleTest extends RuleTestCase
{

protected function getRule(): Rule
{
return new AccessPrivateConstantThroughStaticRule();
}

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

}
8 changes: 6 additions & 2 deletions tests/PHPStan/Rules/Classes/ClassConstantRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -143,13 +143,17 @@ public function testClassConstantVisibility(): void
'Cannot access constant FOO on int|string.',
116,
],
[
'Access to undefined constant static(ClassConstantVisibility\AccessWithStatic)::BAR.',
129,
],
[
'Class ClassConstantVisibility\Foo referenced with incorrect case: ClassConstantVisibility\FOO.',
122,
135,
],
[
'Access to private constant PRIVATE_FOO of class ClassConstantVisibility\Foo.',
122,
135,
],
]);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace AccessPrivateConstantThroughStatic;

class Foo
{

private const FOO = 1;

public function doFoo()
{
static::FOO;
static::BAR; // reported by a different rule
}

}

final class Bar
{

private const FOO = 1;

public function doFoo()
{
static::FOO;
static::BAR; // reported by a different rule
}

}
13 changes: 13 additions & 0 deletions tests/PHPStan/Rules/Classes/data/class-constant-visibility.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,19 @@ public function doIpsum(WithFooConstant $foo)

}

class AccessWithStatic
{

private const FOO = 1;

public function doFoo()
{
static::FOO; // reported by a different rule
static::BAR;
}

}

function () {
FOO::PRIVATE_FOO;
};

0 comments on commit 270326a

Please sign in to comment.