Skip to content

Commit

Permalink
Support dynamic class constant fetch available in PHP 8.3
Browse files Browse the repository at this point in the history
  • Loading branch information
spaze committed Jan 22, 2024
1 parent 3d18e0f commit bbe8fdf
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 5 deletions.
33 changes: 28 additions & 5 deletions src/Usages/ClassConstantUsages.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@

use PhpParser\Node;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Identifier;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleError;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\ShouldNotHappenException;
use PHPStan\Type\Constant\ConstantStringType;
use PHPStan\Type\Type;
use PHPStan\Type\VerbosityLevel;
use Spaze\PHPStan\Rules\Disallowed\DisallowedConstant;
use Spaze\PHPStan\Rules\Disallowed\DisallowedConstantFactory;
Expand Down Expand Up @@ -80,17 +82,38 @@ public function processNode(Node $node, Scope $scope): array
if (!($node instanceof ClassConstFetch)) {
throw new ShouldNotHappenException(sprintf('$node should be %s but is %s', ClassConstFetch::class, get_class($node)));
}
if (!($node->name instanceof Identifier)) {
throw new ShouldNotHappenException(sprintf('$node->name should be %s but is %s', Identifier::class, get_class($node->name)));
if ($node->name instanceof Identifier) {
return $this->getConstantRuleErrors($scope, (string)$node->name, $this->typeResolver->getType($node->class, $scope));
}
$constant = (string)$node->name;
$type = $this->typeResolver->getType($node->class, $scope);
$usedOnType = $type->getObjectTypeOrClassStringObjectType();
if ($node->name instanceof Variable) {
$type = $scope->getType($node->name);
$errors = [];
foreach ($type->getConstantStrings() as $constantString) {
$errors = array_merge(
$errors,
$this->getConstantRuleErrors($scope, $constantString->getValue(), $this->typeResolver->getType($node->class, $scope))
);
}
return $errors;
}
throw new ShouldNotHappenException(sprintf('$node->name should be %s but is %s', Identifier::class, get_class($node->name)));
}


/**
* @param Scope $scope
* @param string $constant
* @param Type $type
* @return list<RuleError>
* @throws ShouldNotHappenException
*/
private function getConstantRuleErrors(Scope $scope, string $constant, Type $type): array
{
if (strtolower($constant) === 'class') {
return [];
}

$usedOnType = $type->getObjectTypeOrClassStringObjectType();
$displayName = $usedOnType->getObjectClassNames() ? $this->getFullyQualified($usedOnType->getObjectClassNames(), $constant) : null;
if ($usedOnType->getConstantStrings()) {
$classNames = array_map(
Expand Down
12 changes: 12 additions & 0 deletions tests/Usages/ClassConstantUsagesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,18 @@ public function testRule(): void
'Using PhpOption\Option::NAME (as PhpOption\None::NAME) is forbidden, no PhpOption.',
37,
],
[
'Using Waldo\Quux\Blade::RUNNER is forbidden, not a replicant.',
44,
],
[
'Using Waldo\Quux\Blade::DECKARD is forbidden, maybe a replicant.',
46,
],
[
'Using Waldo\Quux\Blade::RUNNER is forbidden, not a replicant.',
46,
],
]);
$this->analyse([__DIR__ . '/../src/disallowed-allow/constantUsages.php'], []);
}
Expand Down
2 changes: 2 additions & 0 deletions tests/src/Blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ class Blade

public const WESLEY = 'Snipes';

public const MOVIE = Blade::WESLEY;


public function runner(int $everything = 0, bool $yes = false, string $roland = '303'): void
{
Expand Down
6 changes: 6 additions & 0 deletions tests/src/disallowed-allow/constantUsages.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,9 @@

// not disallowed by path
PHP_EOL;

// allowed by path
$kind = 'RUNNER';
echo Blade::{$kind};
/** @var 'DECKARD'|'MOVIE'|'RUNNER' $kind2 */
echo Blade::{$kind2};
6 changes: 6 additions & 0 deletions tests/src/disallowed/constantUsages.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,9 @@

// disallowed by path
PHP_EOL;

// disallowed
$kind = 'RUNNER';
echo Blade::{$kind};
/** @var 'DECKARD'|'MOVIE'|'RUNNER' $kind2 */
echo Blade::{$kind2};

0 comments on commit bbe8fdf

Please sign in to comment.