-
-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Can get method/function attributes for params of disallowed classes
When a disallowed classname is detected in method or function params, and it is allowed by method/function attributes, this change will allow to get the method/func attributes. It's way hacky as it seems to be not possible natively in PHPStan because `$scope->getFunction()` returns null for `FullyQualified` nodes in method/func parameter types. So we store the method name in `ClassMethod` rule, use it in `Allowed` service when set, and unset the function name in `InClassMethodNode` which is a virtual node which marks the beginning of the method body. Similar for functions in `Function_` and `InFunctionBody`. Close #314
- Loading branch information
Showing
11 changed files
with
415 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
<?php | ||
declare(strict_types = 1); | ||
|
||
namespace Spaze\PHPStan\Rules\Disallowed\Allowed; | ||
|
||
use PHPStan\Analyser\Scope; | ||
use PHPStan\BetterReflection\Reflection\Adapter\FakeReflectionAttribute; | ||
use PHPStan\BetterReflection\Reflection\Adapter\ReflectionAttribute; | ||
use PHPStan\BetterReflection\Reflection\ReflectionAttribute as BetterReflectionAttribute; | ||
use PHPStan\BetterReflection\Reflector\Reflector; | ||
|
||
class GetAttributesWhenInSignature | ||
{ | ||
|
||
private Reflector $reflector; | ||
|
||
/** @var class-string|null */ | ||
private ?string $currentClass = null; | ||
|
||
private ?string $currentMethod = null; | ||
|
||
/** @var non-empty-string|null */ | ||
private ?string $currentFunction = null; | ||
|
||
|
||
public function __construct(Reflector $reflector) | ||
{ | ||
$this->reflector = $reflector; | ||
} | ||
|
||
|
||
/** | ||
* Emulates the missing $scope->getMethodOrFunctionSignature(). | ||
* | ||
* Because $scope->getFunction() returns null when the node, like for example a namespace node (instance of FullyQualified), | ||
* is inside the method or the function signature, it's impossible to get to the current method or function reflection using $scope to get its attributes. | ||
* The hacky solution is to store the current method name in a ClassMethod rule, read it here, and unset it in a InClassMethodNode rule, | ||
* or the function name in a Function_ and a InFunctionNode rules. | ||
* | ||
* @param Scope $scope | ||
* @return list<FakeReflectionAttribute|ReflectionAttribute|BetterReflectionAttribute>|null | ||
*/ | ||
public function get(Scope $scope): ?array | ||
{ | ||
if ( | ||
$this->currentClass !== null | ||
&& $this->currentMethod !== null | ||
&& $scope->isInClass() | ||
&& $scope->getClassReflection()->getName() === $this->currentClass | ||
) { | ||
return $scope->getClassReflection()->getNativeReflection()->getMethod($this->currentMethod)->getAttributes(); | ||
} elseif ($this->currentFunction !== null) { | ||
return $this->reflector->reflectFunction($this->currentFunction)->getAttributes(); | ||
} | ||
return null; | ||
} | ||
|
||
|
||
/** | ||
* @param class-string $className | ||
* @param string $methodName | ||
* @return void | ||
*/ | ||
public function setCurrentClassMethodName(string $className, string $methodName): void | ||
{ | ||
$this->currentClass = $className; | ||
$this->currentMethod = $methodName; | ||
} | ||
|
||
|
||
public function unsetCurrentClassMethodName(): void | ||
{ | ||
$this->currentClass = $this->currentMethod = null; | ||
} | ||
|
||
|
||
/** | ||
* @param non-empty-string $functionName | ||
* @return void | ||
*/ | ||
public function setCurrentFunctionName(string $functionName): void | ||
{ | ||
$this->currentFunction = $functionName; | ||
} | ||
|
||
|
||
public function unsetCurrentFunctionName(): void | ||
{ | ||
$this->currentFunction = null; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
<?php | ||
declare(strict_types = 1); | ||
|
||
namespace Spaze\PHPStan\Rules\Disallowed\HelperRules; | ||
|
||
use PhpParser\Node; | ||
use PhpParser\Node\Stmt\ClassMethod; | ||
use PHPStan\Analyser\Scope; | ||
use PHPStan\Rules\Rule; | ||
use Spaze\PHPStan\Rules\Disallowed\Allowed\GetAttributesWhenInSignature; | ||
|
||
/** | ||
* @implements Rule<ClassMethod> | ||
*/ | ||
class SetCurrentClassMethodNameHelperRule implements Rule | ||
{ | ||
|
||
private GetAttributesWhenInSignature $attributesWhenInSignature; | ||
|
||
|
||
public function __construct(GetAttributesWhenInSignature $attributesWhenInSignature) | ||
{ | ||
$this->attributesWhenInSignature = $attributesWhenInSignature; | ||
} | ||
|
||
|
||
public function getNodeType(): string | ||
{ | ||
return ClassMethod::class; | ||
} | ||
|
||
|
||
/** | ||
* @param ClassMethod $node | ||
* @param Scope $scope | ||
* @return array{} | ||
*/ | ||
public function processNode(Node $node, Scope $scope): array | ||
{ | ||
if ($scope->isInClass()) { | ||
$this->attributesWhenInSignature->setCurrentClassMethodName($scope->getClassReflection()->getName(), $node->name->name); | ||
} | ||
return []; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
<?php | ||
declare(strict_types = 1); | ||
|
||
namespace Spaze\PHPStan\Rules\Disallowed\HelperRules; | ||
|
||
use PhpParser\Node; | ||
use PhpParser\Node\Stmt\Function_; | ||
use PHPStan\Analyser\Scope; | ||
use PHPStan\Rules\Rule; | ||
use Spaze\PHPStan\Rules\Disallowed\Allowed\GetAttributesWhenInSignature; | ||
|
||
/** | ||
* @implements Rule<Function_> | ||
*/ | ||
class SetCurrentFunctionNameHelperRule implements Rule | ||
{ | ||
|
||
private GetAttributesWhenInSignature $attributesWhenInSignature; | ||
|
||
|
||
public function __construct(GetAttributesWhenInSignature $attributesWhenInSignature) | ||
{ | ||
$this->attributesWhenInSignature = $attributesWhenInSignature; | ||
} | ||
|
||
|
||
public function getNodeType(): string | ||
{ | ||
return Function_::class; | ||
} | ||
|
||
|
||
/** | ||
* @param Function_ $node | ||
* @param Scope $scope | ||
* @return array{} | ||
*/ | ||
public function processNode(Node $node, Scope $scope): array | ||
{ | ||
if ($node->namespacedName !== null) { | ||
$this->attributesWhenInSignature->setCurrentFunctionName($node->namespacedName->name); | ||
Check failure on line 41 in src/HelperRules/SetCurrentFunctionNameHelperRule.php
|
||
} | ||
return []; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
<?php | ||
declare(strict_types = 1); | ||
|
||
namespace Spaze\PHPStan\Rules\Disallowed\HelperRules; | ||
|
||
use PhpParser\Node; | ||
use PHPStan\Analyser\Scope; | ||
use PHPStan\Node\InClassMethodNode; | ||
use PHPStan\Rules\Rule; | ||
use Spaze\PHPStan\Rules\Disallowed\Allowed\GetAttributesWhenInSignature; | ||
|
||
/** | ||
* @implements Rule<InClassMethodNode> | ||
*/ | ||
class UnsetCurrentClassMethodNameHelperRule implements Rule | ||
{ | ||
|
||
private GetAttributesWhenInSignature $attributesWhenInSignature; | ||
|
||
|
||
public function __construct(GetAttributesWhenInSignature $attributesWhenInSignature) | ||
{ | ||
$this->attributesWhenInSignature = $attributesWhenInSignature; | ||
} | ||
|
||
|
||
public function getNodeType(): string | ||
{ | ||
return InClassMethodNode::class; | ||
} | ||
|
||
|
||
/** | ||
* @param InClassMethodNode $node | ||
* @param Scope $scope | ||
* @return array{} | ||
*/ | ||
public function processNode(Node $node, Scope $scope): array | ||
{ | ||
$this->attributesWhenInSignature->unsetCurrentClassMethodName(); | ||
return []; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
<?php | ||
declare(strict_types = 1); | ||
|
||
namespace Spaze\PHPStan\Rules\Disallowed\HelperRules; | ||
|
||
use PhpParser\Node; | ||
use PHPStan\Analyser\Scope; | ||
use PHPStan\Node\InFunctionNode; | ||
use PHPStan\Rules\Rule; | ||
use Spaze\PHPStan\Rules\Disallowed\Allowed\GetAttributesWhenInSignature; | ||
|
||
/** | ||
* @implements Rule<InFunctionNode> | ||
*/ | ||
class UnsetCurrentFunctionNameHelperRule implements Rule | ||
{ | ||
|
||
private GetAttributesWhenInSignature $attributesWhenInSignature; | ||
|
||
|
||
public function __construct(GetAttributesWhenInSignature $attributesWhenInSignature) | ||
{ | ||
$this->attributesWhenInSignature = $attributesWhenInSignature; | ||
} | ||
|
||
|
||
public function getNodeType(): string | ||
{ | ||
return InFunctionNode::class; | ||
} | ||
|
||
|
||
/** | ||
* @param InFunctionNode $node | ||
* @param Scope $scope | ||
* @return array{} | ||
*/ | ||
public function processNode(Node $node, Scope $scope): array | ||
{ | ||
$this->attributesWhenInSignature->unsetCurrentFunctionName(); | ||
return []; | ||
} | ||
|
||
} |
Oops, something went wrong.