Skip to content

psalmの働き

すべての分析の入り口はProjectAnalyzer

ProjectAnalyzer は2つのことを担当する:スキャンと分析

スキャン

解析フェーズをマルチスレッドで実行できるようにするため、Psalmはファイルやファイル群に対して、可能性のあるすべての依存関係を決定し、その関数のシグネチャと定数を取得する必要がある。

スキャンはPsalm\Internal\Codebase\Scanner で行われます。

最初のタスクは、ファイルをPHP Parser の文の集合に変換することです。PHP Parserは、PHPコードを抽象構文木に変換し、Psalmがすべての解析に使用します。

ディープスキャンとシャロースキャンの比較

NodeVisitor ReflectorVisitor これは、関数のシグネチャ、戻り値型、定数、継承を取得するだけの浅いスキャンと、関数のすべての文に潜り込んで依存関係(関数がインスタンス化するクラス名など)を取得する深いスキャンの2つのモードがあります。ディープスキャンは、後で分析されることがわかっているファイルに対してのみ行われます(したがって、例えばベンダーディレクトリの大部分は、シャロースキャンを受けるだけです)。

そのため、src ディレクトリを分析する場合、Psalm は以下のファイルをディープスキャンします:

src/A.php

<?php use Vendor\VendorClass; use Vendor\OtherVendorClass;

class A extends VendorClass {     public function foo(OtherVendorClass $c): void {} }

また、Vendor\VendorClass に属するファイルもディープスキャンします。これは、ある時点でプロパティのインスタンスをチェックする必要があるかもしれないからです。

変数$c のメソッドシグネチャと戻り値の型にしか関心がないため、Vendor\OtherVendorClass (および依存するすべてのファイル) を浅くスキャンします。

クラス名からファイルを見つける

ClassName =>src/FileName.php のマッピングを把握するために、プロジェクト・ファイルにはリフレクションを使い、ベンダー・ファイルにはComposerクラスマップを使う。

スキャンからデータを保存する

ReflectorVisitor がアクセスする各ファイルに対して、Psalm はFileStorage インスタンスを作成し、ファイルの内容に応じてClassLikeStorageFunctionLikeStorage インスタンスも作成する。

すべてのファイルとそのクラスと関数シグネチャのセットができたら、Populator クラスのすべての継承を計算し、分析に移る。

スキャン・ステップの最後には、ClassLikesFunctionsMethods クラスに必要な情報をすべて入力し、プロジェクトで使用するすべてのクラスとファイルについて、FileStorageClassLikeStorage オブジェクトの完全なリスト(それぞれFileStorageProviderClassLikeStorageProvider 内)を作成しました。

分析

にあるファイルを分析する。FileAnalyzer

FileAnalyzer 、与えられたファイルを受け取り、トップレベルのコンポーネント(クラス、特性、インターフェイス、関数)を探します。名前空間の内部を調べ、その中のクラス、インターフェイス、特性、関数を抽出することもできます。

これらのコンポーネントの解析は、ClassAnalyzerInterfaceAnalyzerFunctionAnalyzer に委譲されます。

行ごとの解析は最も基本的なユースケースなので(クラス継承の心配はない)、FunctionAnalyzer

関数解析

FunctionAnalyzer::analyzeFunctionLikeAnalyzer で定義されています。このメソッドはまず、スキャン・ステップで作成した、指定された関数のFunctionLikeStorage オブジェクトを取得します。このFunctionLikeStorage オブジェクトには、関数のパラメータに関する情報があり、それをContext オブジェクトに渡します。Context には、変数とプロパティに関するすべての型情報 (Context::$vars_in_scope に格納) が含まれています。

FunctionLikeAnalyzer::analyze のどこかで新しいStatementsAnalyzer を作成し、そのanalyze() メソッドを呼び出して、PhpParser ノードのセットを渡します。StatementAnalyzer::analyze は、より詳細な分析を行うために、さまざまなチェッカー (IfAnalyzer,ForeachAnalyzer,ExpressionAnalyzer など) に渡します。

各行では、Context オブジェクトが操作されたりされなかったりします。分岐点(if文、ループ、3項など)では、Context オブジェクトがクローンされ、分岐の最後で、Psalmが変更を解決する方法を見つけ出し、クローンされていないContext オブジェクトを更新します。

NodeDataProvider は各 PhpParser ノードの型を保存します。

すべてのステートメントが分析された後、すべての戻り値の型を集め、与えられた戻り値の型と比較します。

型の照合

Context オブジェクトの更新は簡単なものもありますが、そうでないものもあります。新しい型情報に照らしてContext オブジェクトを更新することは、Reconciler で行われる。 は、[“$a” => “!null”] のような配列アサーションと、$a => string|null のような既存の型情報のリストを受け取り、 のような更新された情報のセットを返す。$a => string

ユーザーノート