abstract class Stmt extends NodeAbstract PHP ParserにおけるAST 23 abstract class NodeAbstract implements Node, \JsonSerializable interface Node { public function getType() : string; public function getSubNodeNames() : array; public function getLine() : int; public function getStartLine() : int; public function getEndLine() : int; public function getStartTokenPos() : int; public function getEndTokenPos() : int; public function getStartFilePos() : int; public function getEndFilePos() : int; public function getComments() : array; public function getDocComment(); public function setDocComment(Comment\Doc $docComment); public function setAttribute(string $key, $value); public function hasAttribute(string $key) : bool; public function getAttribute(string $key, $default = null); public function getAttributes() : array; public function setAttributes(array $attributes); }
PHP Parser -traverse 30 interface NodeVisitor { public function beforeTraverse(array $nodes); public function enterNode(Node $node); public function leaveNode(Node $node); public function afterTraverse(array $nodes); } ノードを見つけると、enterNode()が呼び出される。 一方、leaveNode()はすべての子ノードを走査した後に呼び出される。 A B C enterNode(A) enterNode(B) leaveNode(B) enterNode(C) leaveNode(C) leaveNode(A)
前提 コードベースの規模、複雑さは以下のような感じ。 (PhpMetricsでsrc以下を計測) 35 LOC Lines of code 83657 Comment lines of code 13108 Object oriented programming Classes 1006 Interface 65 Methods 2588 Lack of cohesion of methods 1.5 Coupling Average instability 0.57 Complexity Average Cyclomatic complexity by class 22.81 Average Relative system complexity 210.65 Average Difficulty 8.71
Populator - 継承の事例 57 1. Subクラスをスキャンした際、inherited()が読み込まれない。 (subSpecific()や親クラスの情報はClassLikeStorageに格納される。) 2. Populatorによりinherited()の情報が追加される。 class Super { public function inherited(): void {} } class Sub extends Super { // inherited()が明記されていない public function subSpecific(): void {} } スキャン工程
UnionTypeComparator 86 “An annotation of the form Type1|Type2|Type3 is a Union Type. Type1, Type2 and Type3 are all acceptable possible types of that union type. Type1, Type2 and Type3 are each atomic types.” (Union Types) 分析工程
参考: Psalmの機能は型チェックだけじゃない 89 function missingParamType($foo): bool { return is_numeric($foo); } ERROR: MissingParamType - 略 - Parameter $foo has no provided type (see https://psalm.dev/154) function missingParamType($foo): bool
参考: Psalmの機能は型チェックだけじゃない 90 ERROR: UnusedParam - 略 - Param $foo is never referenced in this method (see https://psalm.dev/135) function unUsedParam(string $foo): void function unusedParam(string $foo): void { return; }
参考: Psalmの機能は型チェックだけじゃない 91 class User { public $name; } $user = new User; $user->nane = "foo"; ERROR: UndefinedPropertyAssignment - 略 - Instance property User::$nane is not defined (see https://psalm.dev/038) $user->nane PHP9から例外を出力 するようになる Dynamic Properties[1] 1. https://wiki.php.net/rfc/deprecate_dynamic_properties
戻り値の型違反 93 function string(): string { return ''; } function int(): int { return string(); } 型の不一致 ERROR: InvalidReturnType - 略 - The declared return type 'int' for int is incorrect, got 'string' (see https://psalm.dev/011) function int(): int ERROR: InvalidReturnStatement - 略 - The inferred type 'string' does not match the declared return type 'int' for int (see https://psalm.dev/128) return string();
戻り値の型違反 94 function string(): string { return ''; } function int(): int { return string(); } 型の不一致 ERROR: InvalidReturnType - 略 - The declared return type 'int' for int is incorrect, got 'string' (see https://psalm.dev/011) function int(): int ERROR: InvalidReturnStatement - 略 - The inferred type 'string' does not match the declared return type 'int' for int (see https://psalm.dev/128) return string(); 検出の流れを確認する
InvalidReturnTypeの検出 112 [コールスタック] Psalm\Internal\Analyzer\FunctionLike\ReturnTypeAnalyzer::verifyReturnType // 戻り値の型宣言が妥当かどうかをチェック Psalm\Internal\Analyzer\FunctionLikeAnalyzer->verifyReturnType Psalm\Internal\Analyzer\FunctionAnalyzer::analyzeStatement Psalm\Internal\Analyzer\StatementsAnalyzer::analyzeStatement Psalm\Internal\Analyzer\StatementsAnalyzer->analyze Psalm\Internal\Analyzer\FileAnalyzer->analyze Psalm\Internal\Codebase\Analyzer->Psalm\Internal\Codebase\{closure:/略/Analyzer.php:357-368} Psalm\Internal\Codebase\Analyzer->doAnalysis Psalm\Internal\Codebase\Analyzer->analyzeFiles // 分析工程の入口 Psalm\Internal\Analyzer\ProjectAnalyzer->checkPaths Psalm\Internal\Cli\Psalm::run {main} ERROR: InvalidReturnType - 略 - The declared return type 'int' for int is incorrect, got 'string' (see https://psalm.dev/011) function int(): int
CallMap 126 "Callmap is a data file (formatted as a PHP file returning an array) that tells Psalm what arguments function/method takes and what it returns." (Altering callmaps)
plugin - Psalm API 135 “AfterAnalysisInterface - called after Psalm has completed its analysis. Use this hook if you want to do something with the analysis results.” (Psalm API) interface AfterAnalysisInterface { /** * Called after analysis is complete */ public static function afterAnalysis(AfterAnalysisEvent $event): void; }