🐶
さわって慣れるPHP Parser - Nameノードを内包しているノード探し(訂正版)
さわって慣れるPHP Parser - Nameノードを内包しているノード探しの訂正版です。
Nameノードを内包しているノードをPHPStanのカスタムルールを用いて調べ直してみました。
namespace | ノード |
---|---|
PhpParser\Node\Expr | ArrowFunction |
PhpParser\Node\Expr | ClassConstFetch |
PhpParser\Node\Expr | ConstFetch |
PhpParser\Node\Expr | Closure |
PhpParser\Node\Expr | FuncCall |
PhpParser\Node\Expr | Instanceof_ |
PhpParser\Node\Expr | New_ |
PhpParser\Node\Expr | StaticCall |
PhpParser\Node\Expr | StaticPropertyFetch |
PhpParser\Node\Stmt\TraitUseAdaptation | Precedence |
PhpParser\Node\Stmt | Catch_ |
PhpParser\Node\Stmt | Class_ |
PhpParser\Node\Stmt | ClassLike |
PhpParser\Node\Stmt | ClassMethod |
PhpParser\Node\Stmt | Enum_ |
PhpParser\Node\Stmt | Function_ |
PhpParser\Node\Stmt | GroupUse |
PhpParser\Node\Stmt | Interface_ |
PhpParser\Node\Stmt | Namespace_ |
PhpParser\Node\Stmt | Property |
PhpParser\Node\Stmt | TraitUseAdaptation |
PhpParser\Node\Stmt | TraitUse |
PhpParser\Node\Stmt | UseUse |
PhpParser\Node | Attribute |
PhpParser\Node | Const_ |
PhpParser\Node | IntersectionType |
PhpParser\Node | NullableType |
PhpParser\Node | Param |
PhpParser\Node | UnionType |
抽出方法
考え方
- Nameノードを内包しているがコンストラクタから渡さないものがあった。
→Name
型のプロパティを持つノードのクラスを探す。 - 配列型の場合、配列の要素の型を評価する必要がある。
- mixed型は偽陽性になるので除外する。
- Union型にName型が含まれるケースは
$propertyType->isSuperTypeOf(new ObjectType('PhpParser\Node\Name'));
で判定できる。 - NameノードはNameノードを内包するわけではない。
前提
- PHPStan 1.10.25
- PHP Parser 4.16.0
実装
<?php
namespace Rules;
use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use PHPStan\Node\InClassNode;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\Type\ArrayType;
use PHPStan\Type\MixedType;
use PHPStan\Type\ObjectType;
class NameNodeHolderFindRule implements Rule
{
public function getNodeType(): string
{
return InClassNode::class;
}
public function processNode(Node $node, Scope $scope): array
{
if (!$node instanceof InClassNode) {
return [];
}
$classLike = $node->getOriginalNode();
$properties = $classLike->getProperties();
$classReflection = $scope->getClassReflection();
foreach ($properties as $property) {
foreach($property->props as $prop) {
$propertyName = $prop->name->toString();
$propertyReflection = $classReflection->getNativeProperty($propertyName);
$propertyType = $propertyReflection->getReadableType();
if ($propertyType instanceof ArrayType) {
$propertyType = $propertyType->getItemType();
}
if ($propertyType instanceof MixedType) {
continue;
}
$trinary = $propertyType->isSuperTypeOf(new ObjectType('PhpParser\Node\Name'));
if (!$trinary->no()) {
return [
RuleErrorBuilder::message(
$classLike->namespacedName->toString()
)->build(),
];
}
}
}
return [];
}
}
実装備忘録
- ノードはクラスとして定義されているので分析したいのはClass_だがプロパティの型をとるためにReflectionが欲しいので仮想ノードのInClassNodeを分析対象にする。
Discussion