🐶
PHPStanとPHP Parserのノードの違い
PHPStanは1.10.x、PHP Parserは4.17.xを想定。
以下のコードを解析した時、ノードはいくつあるか?
<?php
class Foo {}
PHP Parserに慣れていると1つ(Node\Stmt\Class_
)と思うかもしれないが、PHPStanで解析すると7つある。
- FileNode
- Class_ <- PHP Parserのノード
- InClassNode
- ClassPropertiesNode
- ClassMethodsNode
- ClassConstantsNode
- CollectedDataNode
PHPStanの公式ドキュメントに書いてあるんだけど、CollectedDataNode
はcollectorsと呼ばれるもので、これとPHP ParserのノードであるClass_
を除いた残りの5つはvirtual nodes(仮想ノード)と呼ばれる。詳細はドキュメントを参照するとして、ざっくり、非仮想ノードとの違いは解析対象のノードに付随するscopeの有無だったり、Constructor Property Promotionを考慮してプロパティを認識するか否かだったり。
カスタムルールに必要な情報を取得するのに適したノードを選ぶとよい。
scopeについてはこちら。ざっくり、解析しているコードについての情報をもっているもの。例えば、ファイルパス、名前空間、変数の型、呼び出したノードがクラスの中にいるか否かなど。僕は"文脈的なもの"と捉えている。雰囲気を掴むにはシグネチャを見るのがよいかもしれない。
interface Scope extends ClassMemberAccessAnswerer, NamespaceAnswerer
{
public function getFile(): string;
public function getFileDescription(): string;
public function isDeclareStrictTypes(): bool;
public function isInTrait(): bool;
public function getTraitReflection(): ?ClassReflection;
public function getFunction();
public function getFunctionName(): ?string;
public function getParentScope(): ?self;
public function hasVariableType(string $variableName): TrinaryLogic;
public function getVariableType(string $variableName): Type;
public function canAnyVariableExist(): bool;
public function getDefinedVariables(): array;
public function hasConstant(Name $name): bool;
public function getPropertyReflection(Type $typeWithProperty, string $propertyName): ?PropertyReflection;
public function getMethodReflection(Type $typeWithMethod, string $methodName): ?ExtendedMethodReflection;
public function getConstantReflection(Type $typeWithConstant, string $constantName): ?ConstantReflection;
public function getIterableKeyType(Type $iteratee): Type;
public function getIterableValueType(Type $iteratee): Type;
public function isInAnonymousFunction(): bool;
public function getAnonymousFunctionReflection(): ?ParametersAcceptor;
public function getAnonymousFunctionReturnType(): ?Type;
public function getType(Expr $node): Type;
public function getNativeType(Expr $expr): Type;
public function doNotTreatPhpDocTypesAsCertain(): self;
public function resolveName(Name $name): string;
public function resolveTypeByName(Name $name): TypeWithClassName;
public function getTypeFromValue($value): Type;
public function isSpecified(Expr $node): bool;
public function hasExpressionType(Expr $node): TrinaryLogic;
public function isInClassExists(string $className): bool;
public function isInFunctionExists(string $functionName): bool;
public function isInClosureBind(): bool;
public function isParameterValueNullable(Param $parameter): bool;
public function getFunctionType($type, bool $isNullable, bool $isVariadic): Type;
public function isInExpressionAssign(Expr $expr): bool;
public function isUndefinedExpressionAllowed(Expr $expr): bool;
public function filterByTruthyValue(Expr $expr): self;
public function filterByFalseyValue(Expr $expr): self;
public function isInFirstLevelStatement(): bool;
}
以上。
Discussion