Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Learning PHP and Static Analysis with PHP Parser
Search
inouehi
March 09, 2024
Programming
2
510
Learning PHP and Static Analysis with PHP Parser
『PHP Parserで学ぶPHPと静的解析』
PHPerKaigi 2024
2024-03-09 14:40〜 Track A
https://phperkaigi.jp/2024/
inouehi
March 09, 2024
Tweet
Share
More Decks by inouehi
See All by inouehi
Learning numeric-like string based on RFC
inouehi
0
51
What We Can Learn From OSS
inouehi
0
610
Understanding Ast By Looking
inouehi
0
1.4k
Improve Our Development Habits by Measuring Productivity and Maintainability
inouehi
1
1.4k
Simple Strategy to Read PHP More Easily
inouehi
0
760
What We Learned and What We Didn't from Our Efforts to Visualize Productivity
inouehi
0
450
Paying Off Technical Dept with Rector -The First Step-
inouehi
1
900
Learning PHP with PHP Parser
inouehi
1
2.2k
Introduction to Static Analysis through Psalm
inouehi
0
1.1k
Other Decks in Programming
See All in Programming
C++でシェーダを書く
fadis
6
4.1k
Better Code Design in PHP
afilina
PRO
0
120
Tauriでネイティブアプリを作りたい
tsucchinoko
0
370
WebフロントエンドにおけるGraphQL(あるいはバックエンドのAPI)との向き合い方 / #241106_plk_frontend
izumin5210
4
1.4k
最新TCAキャッチアップ
0si43
0
140
Enabling DevOps and Team Topologies Through Architecture: Architecting for Fast Flow
cer
PRO
0
310
レガシーシステムにどう立ち向かうか 複雑さと理想と現実/vs-legacy
suzukihoge
14
2.2k
アジャイルを支えるテストアーキテクチャ設計/Test Architecting for Agile
goyoki
9
3.3k
イベント駆動で成長して委員会
happymana
1
320
ピラミッド、アイスクリームコーン、SMURF: 自動テストの最適バランスを求めて / Pyramid Ice-Cream-Cone and SMURF
twada
PRO
10
1.3k
Macとオーディオ再生 2024/11/02
yusukeito
0
370
.NET のための通信フレームワーク MagicOnion 入門 / Introduction to MagicOnion
mayuki
1
1.4k
Featured
See All Featured
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
226
22k
jQuery: Nuts, Bolts and Bling
dougneiner
61
7.5k
Done Done
chrislema
181
16k
The Cost Of JavaScript in 2023
addyosmani
45
6.7k
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
38
1.8k
StorybookのUI Testing Handbookを読んだ
zakiyama
27
5.3k
Building Your Own Lightsaber
phodgson
103
6.1k
Stop Working from a Prison Cell
hatefulcrawdad
267
20k
Six Lessons from altMBA
skipperchong
27
3.5k
Bash Introduction
62gerente
608
210k
Intergalactic Javascript Robots from Outer Space
tanoku
269
27k
Build The Right Thing And Hit Your Dates
maggiecrowley
33
2.4k
Transcript
PHP Parserで学ぶPHPと静的解析 2024/03/09 PHPerKaigi 2024 レギュラートーク
2 • Hiroki Inoue • Software Engineer • Engineering Manager
@ WHITEPLUS, Inc. About Me
3 トーク概要 このトークではPHP Parserを利用して静的解析ツールを作る話をします。 具体例を通じて静的解析というものの解像度を高め、より身近に感じられるようになることを目指しま す。また、静的解析を通して解析対象であるPHPの理解を深めることも目指します。 https://fortee.jp/phperkaigi-2024/proposal/15b92894-11cb-4022-85d5-bf8279f61d43
4 1. PHP Parserに興味がある方 2. 静的解析の仕組みに興味がある方 3. PHPに興味がある方 誰向けのトーク?
5 アジェンダ はじめに 題材の説明 01 02 初期設計 実装・設計見直し 03 04
学び 05 まとめ 06
6 はじめに
7 紹介する設計・実装は唯一の正解を示すものではありません。 一つのアイデアとして、あるいは、反面教師として受け取っていただければ幸いです。 できればフィードバックいただけるとうれしいです。 注意事項
8 • わかりにくかった事 • 発見した事 • 役に立った?立たない? etc. フィードバックを是非お願いしますmm
9 コードを実行することなく、コードを解析すること。 コードを解析して品質等を検査できる。 静的解析とは
10 コードを実行することなく、コードを解析すること。 コードを解析して品質等を検査できる。 PHPの静的解析ツールは、PHPを解析するのだから、PHPのことをよく知っている。 静的解析を理解するということは、PHPを理解することでもある。 静的解析とは
11 • PHPのコードを抽象構文木と呼ばれるオブジェクト群に変換する。 • オブジェクト1つ1つをノードと呼び、200種類弱のノードがある。 ◦ if → If_ノード ◦
関数宣言 → Function_ノード ◦ = → Assignノード etc. • 静的解析ツールは、そのオブジェクトを操作することでコードを解析する。コードを書き換える ことだってできる。 PHP Parserの基礎知識 参考: PHP Parserで学ぶPHP
12 PHP Parserの基礎知識 <?php function foo() { echo 'bar'; }
13 このトークではPHP Parser v5.0.xを使用します。 PHP Parserの基礎知識
14 題材の説明
15 簡易的な依存可視化ツールを作ります。 クラス間の依存の方向、数、深さからコードの複雑さを認識し、バッドスメルを察知して設計を見直す こと等を意図したものです。 どんな静的解析ツールを作るのか
16 簡易的な どんな静的解析ツールを作るのか
17 どんな静的解析ツールを作るのか final class A { public function __construct(private readonly
B $b) {} } A B
18 どんな静的解析ツールを作るのか A B 循環依存
19 どんな静的解析ツールを作るのか A B D E F C 広い
20 どんな静的解析ツールを作るのか A B D C 深い
21 どんな静的解析ツールを作るのか A B D C E F G H
I 広くて深くて循環して… J
22 出力イメージ ※Class, Interface, Trait, Enumの判別がつかないものはStereotypeとして描画します
23 初期設計
24 1. コードの中からクラス(への依存)を見つけ出す。 2. 見つけたクラスのコードを読み込んで1へ。 3. 図示する。 ツールに期待する振る舞い
25 • 型宣言 ◦ 引数、戻り値、プロパティ • new • static ◦
メソッドコール、プロパティアクセス etc. 1. コードの中からクラスを見つけ出す function foo(Bar $bar): Baz class Foo { private Bar $bar; } new Foo() Foo::bar Foo::baz() class Foo {}
26 クラス名はPHP ParserのNameノードに格納されている。 1. コードの中からクラスを見つけ出す
27 1. クラス名(Nameノード)を収集する。 2. useと組み合わせて修飾名を得る。 3. 同一の名前空間にあるクラスの場合、namespaceと組み合わせて修飾名を得る。 1. コードの中からクラスを見つけ出す
28 1. クラス名(Nameノード)を収集する。 2. useと組み合わせて修飾名を得る。 3. 同一の名前空間にあるクラスの場合、namespaceと組み合わせて修飾名を得る。 1. コードの中からクラスを見つけ出す PHP
Parserが提供するNodeFinderが使えそう
29 1. 修飾名からファイルパスを得る。 2. そのファイルを読み込む。 2. 見つけたクラスのコードを読み込む
30 1. PlantUML等に変換して出力する。 3. 図示する
31 1. グローバル関数の戻り値の型 2. 配列の型 3. 式の解決 4. PHPDocの解釈 5.
self, parent, staticの読み替え 6. 並列処理 etc. 非対応事項
32 実装・設計見直し
33 1. コードの中からクラス(への依存)を見つけ出す。 2. 見つけたクラスのコードを読み込んで1へ。 3. 図示する。 ツールに期待する振る舞い(再掲)
34 1. NodeFinderを用いてNameノードを取得する。 2. NodeFinderを用いてUse_ノードを取得する。 3. NodeFinderを用いてNamespace_ノードを取得する。 4. 1~3を組み合わせて修飾名を得る。 a.
Nameノードからクラス名を得る。 b. クラス名が完全修飾名の場合、何もしない。 c. クラス名とUse_から得た名前空間を結合して、修飾名を得る。 d. cがない場合、Namespace_から得た名前空間を結合して、修飾名を得る。 クラスを見つけ出す
35 NodeFinderはASTからノードを取得する手軽な手段。 • callbackとして渡された条件に合うノードを取得する。 • 指定したノードを取得する。 クラスを見つけ出す - NodeFinder[1] 1.
https://github.com/nikic/PHP-Parser/blob/ce019e9ad711e31ee87c2c4c72e538b5240970c3/doc/component/Walking_the_AST.markdown#s imple-node-finding $classes = $nodeFinder->findInstanceOf($ast, Node\Name::class); この中から探す Nameノードを探す
36 1. NodeFinderを用いてNameノードを取得する。 2. NodeFinderを用いてUse_ノードを取得する。 3. NodeFinderを用いてNamespace_ノードを取得する。 4. 1~3を組み合わせて修飾名を得る。 a.
Nameノードからクラス名を得る。 b. クラス名が完全修飾名の場合、何もしない。 c. そうでない場合、クラス名とUse_から得た名前空間を結合して、修飾名を得る。 d. cがない場合、Namespace_から得た名前空間を結合して、修飾名を得る。 クラスを見つけ出す
37 1. NodeFinderを用いてNameノードを取得する。 2. NodeFinderを用いてUse_ノードを取得する。 3. NodeFinderを用いてNamespace_ノードを取得する。 4. 1~3を組み合わせて修飾名を得る。 a.
Nameノードからクラス名を得る。 b. クラス名が完全修飾名の場合、何もしない。 c. そうでない場合、クラス名とUse_から得た名前空間を結合して、修飾名を得る。 d. cがない場合、Namespace_から得た名前空間を結合して、修飾名を得る。 クラスを見つけ出す 煩雑で ちょっと面倒…
38 それ、NameResolverでシンプルにできます クラスを見つけ出す
39 NameResolverはクラス名等の名前を解決する。 クラスを見つけ出す - NameResolver[1] 1. https://github.com/nikic/PHP-Parser/blob/ce019e9ad711e31ee87c2c4c72e538b5240970c3/doc/component/Name_resolution.markdown
40 NameResolverはクラス名等の名前を解決する。 クラスを見つけ出す - NameResolver $nameResolver = new PhpParser\NodeVisitor\NameResolver; $nodeTraverser
= new PhpParser\NodeTraverser; $nodeTraverser->addVisitor($nameResolver); $ast = $nodeTraverser->traverse($ast); この中に含まれるクラス名等を 完全修飾名にできる
41 設計見直し クラスの修飾名を得るのに、NodeFinder+自作ロジックではなくNameResolverを使う。
42 実装例: https://github.com/hirokinoue/phperkaigi-2024/blob/main/src/Example1/NameNodesExtractor.php /** * @return Name[] */ public function
extractName(): array { // 名前を解決する $traverser = new NodeTraverser(); $traverser->addVisitor(new NameResolver()); $namedNodes = $traverser->traverse($this->ast); // Nameノードでフィルターする $nodeFinder = new NodeFinder(); return $nodeFinder->findInstanceOf($namedNodes, Name::class); } サンプルコード
43 1. 修飾名からファイルパスを得る。例えば、ReflectionClassを利用してみる。 2. file_get_contents()でコードを読み込む。 見つけたクラスのコードを読み込む
44 できた🎉 ReflectionClass ありがとう!
45 クラス名が取得できるようになったが、クラス以外の名前も混在していた。 これらにはクラスのファイルが存在しない。 • 定数 • キーワード • 関数名 •
特殊なクラス名: self, parent, static[1] • namespace[1] しかし、この実装には問題がある 1. Nameノードの代わりにFullyQualifiedノードでフィルターするとこれらは混入しない。 実装例: https://github.com/hirokinoue/phperkaigi-2024/tree/main/src/Example2
46 クラスにも2種類ある。 • ユーザー定義クラス • 定義済みのクラス ユーザー定義クラスと違って定義済みのクラスにはPHPで書かれたファイルが存在しない。 しかし、この実装には問題がある
47 設計見直し2 • ReflectionClassの例外をハンドリングする。 ◦ クラス以外 ⇒ 例外をキャッチして次のノードの解析へ ◦ ユーザー定義クラス
⇒ クラス名も中身も取得できる ◦ 定義済みのクラス ⇒ クラス名のみ取得できる
48 サンプルコード try { $reflector = new ReflectionClass($fullyQualified->toCodeString()); } catch
(\ReflectionException $r) { // クラスではない場合: クラス名も内容も取得できない // 第一引数がクラス名、第二引数がクラスの内容 return new self('', ''); } // クラスの場合: クラス名は取得でき、内容はファイルの有無次第 $path = ($reflector->getFileName() === false) ? '' : $reflector->getFileName(); $code = self::readFile($path); return new self($fullyQualified->toCodeString(), $code); 実装例: https://github.com/hirokinoue/phperkaigi-2024/blob/main/src/Example3/ClassLoader.php
49 1. (実装済み)ファイルを開く。 2. (実装済み)クラスを見つけ出す。 a. クラスが見つかったら1へ。 b. クラスが見つからなくなったら終わり。 読み込んだコードからまたクラスを見つけ出す
50 読み込んだコードからまたクラスを見つけ出す class Foo { public function foo(): Bar {}
} class Bar { public function bar(): Qux {} }
51 読み込んだコードからまたクラスを見つけ出す class Foo { public function foo(): Bar {}
} class Bar { public function bar(): Qux {} }
52 読み込んだコードからまたクラスを見つけ出す class Foo { public function foo(): Bar {}
} class Bar { public function bar(): Qux {} }
53 読み込んだコードからまたクラスを見つけ出す Foo.php Bar.php
54 読み込んだコードからまたクラスを見つけ出す Foo.php Bar.php ノードを1つず辿る
55 読み込んだコードからまたクラスを見つけ出す Foo.php Bar.php ノードを1つず辿る PHP Parserが提供する NodeTraverserを利用
56 読み込んだコードからまたクラスを見つけ出す Foo.php Bar.php クラスを見つけ ファイルを開く
57 読み込んだコードからまたクラスを見つけ出す Foo.php Bar.php クラスを見つけ ファイルを開く ノードを処理する ビジターを作る
58 まとめ • トラバーサ … ノードを一つずつ辿る。 • ビジター … ノードを処理する。
読み込んだコードからまたクラスを見つけ出す
59 サンプルコード public function analyze(): DiagramUnit { $diagramUnit = new
DiagramUnit($this->rootClassName()); $nodeTraverser = new NodeTraverser(); $nodeTraverser->addVisitor(new NameResolver()); $nodeTraverser->addVisitor(new ClassVisitor($diagramUnit)); $nodeTraverser->traverse($this->ast); return $diagramUnit; } 実装例: https://github.com/hirokinoue/phperkaigi-2024/tree/main/src/Example4
60 サンプルコード public function analyze(): DiagramUnit { $diagramUnit = new
DiagramUnit($this->rootClassName()); $nodeTraverser = new NodeTraverser(); $nodeTraverser->addVisitor(new NameResolver()); $nodeTraverser->addVisitor(new ClassVisitor($diagramUnit)); $nodeTraverser->traverse($this->ast); return $diagramUnit; } 解析結果を 格納する箱
61 • 解析結果を格納する箱。 • DiagramUnitはDiagramUnitの配列を内包する。 DiagramUnit A B C D
62 サンプルコード public function analyze(): DiagramUnit { $diagramUnit = new
DiagramUnit($this->rootClassName()); $nodeTraverser = new NodeTraverser(); $nodeTraverser->addVisitor(new NameResolver()); $nodeTraverser->addVisitor(new ClassVisitor($diagramUnit)); $nodeTraverser->traverse($this->ast); return $diagramUnit; } 入力ファイルがクラスの場合、クラス名 そうでない場合、便宜的な名前とする
63 サンプルコード public function analyze(): DiagramUnit { $diagramUnit = new
DiagramUnit($this->rootClassName()); $nodeTraverser = new NodeTraverser(); $nodeTraverser->addVisitor(new NameResolver()); $nodeTraverser->addVisitor(new ClassVisitor($diagramUnit)); $nodeTraverser->traverse($this->ast); return $diagramUnit; } クラスを見つけて ファイルを開き クラスを見つけて...
64 ビジターはNodeVisitorを実装する。[1] NodeVisitor[2]のメソッドを抜粋 1. NodeVisitorのデフォルト実装であるNodeVisitorAbstractを拡張してもよい。 2. https://github.com/nikic/PHP-Parser/blob/ce019e9ad711e31ee87c2c4c72e538b5240970c3/lib/PhpParser/NodeVisitor.php interface NodeVisitor {
public function beforeTraverse(array $nodes); public function enterNode(Node $node); public function leaveNode(Node $node); public function afterTraverse(array $nodes); } サンプルコード
65 サンプルコード public function enterNode(Node $node) { if (!$node instanceof
FullyQualified) { // ノードに対して何も処理せず次のノードへ return $node; } $classFile = ClassLoader::create($node); if ($classFile->isClass()) { // ノードがクラスだった場合の処理 } return $node; } ClassVisitor
66 サンプルコード public function enterNode(Node $node) { if (!$node instanceof
FullyQualified) { // ノードに対して何も処理せず次のノードへ return $node; } $classFile = ClassLoader::create($node); if ($classFile->isClass()) { // ノードがクラスだった場合の処理 } return $node; } ClassVisitor
67 サンプルコード public function enterNode(Node $node) { if (!$node instanceof
FullyQualified) { // ノードに対して何も処理せず次のノードへ return $node; } $classFile = ClassLoader::create($node); if ($classFile->isClass()) { // ノードがクラスだった場合の処理 } return $node; } ClassVisitor
68 サンプルコード public function enterNode(Node $node) { if (!$node instanceof
FullyQualified) { // ノードに対して何も処理せず次のノードへ return $node; } $classFile = ClassLoader::create($node); if ($classFile->isClass()) { // ノードがクラスだった場合の処理 } return $node; } ClassVisitor
69 サンプルコード public function enterNode(Node $node) { if (!$node instanceof
FullyQualified) { // ノードに対して何も処理せず次のノードへ return $node; } $classFile = ClassLoader::create($node); if ($classFile->isClass()) { // ノードがクラスだった場合の処理 } return $node; } ClassVisitor
70 サンプルコード public function enterNode(Node $node) { if (!$node instanceof
FullyQualified) { // ノードに対して何も処理せず次のノードへ return $node; } $classFile = ClassLoader::create($node); if ($classFile->isClass()) { // ノードがクラスだった場合の処理 } return $node; } ClassVisitor
71 サンプルコード public function enterNode(Node $node) { if (!$node instanceof
FullyQualified) { // ノードに対して何も処理せず次のノードへ return $node; } $classFile = ClassLoader::create($node); if ($classFile->isClass()) { // ノードがクラスだった場合の処理 } return $node; } ClassVisitor
72 サンプルコード if ($classFile->isClass()) { // 見つけたクラスをサブクラスとして格納する。 $subClass = new
DiagramUnit($classFile->className()); $this->diagramUnit->push($subClass); // クラスのファイルがない場合、パースせずに次のノードへ if ($classFile->codeNotFound()) { return $node; } ClassVisitor
73 サンプルコード if ($classFile->isClass()) { // 前述 // クラスのファイルをパースしてASTを生成する $parser
= (new ParserFactory())->createForHostVersion(); $ast = $parser->parse($classFile->content()); if ($ast === null) { // ASTが生成できない場合、次のノードへ return $node; } ClassVisitor
74 サンプルコード if ($classFile->isClass()) { // 前述 // 依存先クラスが依存するクラスを探しに行く $nodeTraverser
= new NodeTraverser(); $nodeTraverser->addVisitor(new NameResolver()); $nodeTraverser->addVisitor(new ClassVisitor($subClass)); $nodeTraverser->traverse($ast); } ClassVisitor
75 • 分析結果の結果得られたオブジェクトを入力として • PlantUML記法のテキストを出力する 図示する 実装例: https://github.com/hirokinoue/dependency-visualizer/blob/main/src/Exporter/PlantUmlExporter.php
76 • 分析結果の結果得られたオブジェクトを入力として • PlantUML記法のテキストを出力する 図示する 要するに テキスト出力 すればよい
77 完成🎉
78 完成🎉したと思われた…
79 循環依存がある場合、無限ループに陥りメモリリークする。 しかし、まだ問題があった A B
80 設計見直し3 無限ループを回避する。
81 設計見直し3 想定する3つのパターン • A→A … 自身への依存 • A→B→A …
直接依存するクラスからの循環依存 • A→B→C→A … 2ステップ以上離れたクラスからの循環依存
82 設計見直し3 • DiagramUnitに依存元クラスの履歴を保持する。 • 読み込み済みのクラスは解析をスキップして次のノードへ。 例えば • A→B→C→A の場合、CはA,
B, Cの履歴を保持しており Aへの依存を見つけた時、循環依存を検知する。 • この時、Aは読み込み済みなので解析しない。 実装例: https://github.com/hirokinoue/dependency-visualizer/pull/1/files
83 完成🎉
84 完成🎉したと思われた…
85 依存がある程度複雑になると処理がなかなか終わらない。 しかし、まだ問題があった
86 設計見直し4 パフォーマンスを改善する。
87 設計見直し4 1. これにより設計見直し3が不要になりもします。 実装例: https://github.com/hirokinoue/dependency-visualizer/pull/2/files 想定する3つのパターン 1. クラスの中で、複数回同じクラスに依存する場合、結果を束ねる。 2.
別の枝で同じクラスに依存したらトラバースをやめる。[1] 3. 読み込み済みのクラスをメモリ上にキャッシュしてディスクアクセスを減らす。
88 設計見直し4 1. クラスの中で、複数回同じクラスに依存する場合、結果を束ねる。 A B C class A {
public function foo(): B {} public function bar(): B {} public function baz(): C {} }
89 設計見直し4 対策しないとこうなる…
90 設計見直し4 2. 別の枝で同じクラスに依存したらトラバースをやめる。 A B C D E D
E A B C D E CがDに依存するとわかったら E以降の解析を止める
91 設計見直し4 3. 読み込み済みのクラスをメモリ上にキャッシュしてディスクアクセスを減らす。 A B C D E BからDを辿った時Dをロードするが
CからDを辿った時はロードしない
92 完成🎉
93 完成🎉
94 完成🎉 改善はつづくよ、どこまでも ひとまず使えるようにはなった。 しかし、プロダクトとは常に進化しつづけるもの。
95 学び
96 一例を挙げます。 見聞きした事から何を学ぶかは人それぞれ。 前置き
97 • 引数のオブジェクトはリファレンス渡し PHPに関する事
98 • 引数のオブジェクトはリファレンス渡し ということ自体は常識だとして、その使い道の一事例として。 PHPに関する事
99 • 引数のオブジェクトはリファレンス渡し • 循環参照を言語仕様が許容 • 型(クラス)の導出に手間がかかるケース ◦ 式を解決して得られる型 ◦
配列の型 ◦ PHPDocから得られる型 • self, parent, staticという特別なクラス名[1] • 定義済みのクラス[2]がありPHPで書かれていない PHPに関する事 1. https://github.com/nikic/PHP-Parser/blob/ce019e9ad711e31ee87c2c4c72e538b5240970c3/lib/PhpParser/Node/Name.php#L12 2. https://www.php.net/manual/ja/reserved.classes.php
100 • 引数のオブジェクトはリファレンス渡し • 循環参照を言語仕様が許容 • 型(クラス)の導出に手間がかかるケース ◦ 式を解決して得られる型 ◦
配列の型 ◦ PHPDocから得られる型 • self, parent, staticという特別なクラス名[1] • 定義済みのクラス[2]がありPHPで書かれていない PHPに関する事 1. https://github.com/nikic/PHP-Parser/blob/ce019e9ad711e31ee87c2c4c72e538b5240970c3/lib/PhpParser/Node/Name.php#L12 2. https://www.php.net/manual/ja/reserved.classes.php そして、だからこそ PHPStanやPsalmが尊い。
101 • PHP Parserが生成する抽象構文木(AST)の成り立ち • ビジターパターン PHP Parser/静的解析に関すること
102 • PHP Parserが生成する抽象構文木(AST)の成り立ち ◦ 15:40〜 Track Aにて AST の解像度を高めるLTやります!
• ビジターパターン PHP Parser/静的解析に関すること
103 • 再帰処理 • パフォーマンス対策 PHP Parser/静的解析に関すること
104 • lib/PhpParser/NodeVisitorに便利なクラスがある • PHPDocの解析とPHPの解析は別物 ◦ PHP ParserはPHPDocをパースしない • PHPで書かれていない内部関数や定義済みのクラスを考慮した静的解析には一手間必要
PHP Parser/静的解析に関すること
105 15:40〜 Track AにてLTやります! PHP Parser、抽象構文木(AST)に興味がわいた方は是非 PHP Parser/静的解析に関すること
106 PHPerKaigi 2023で、PHPやPHP Parserについて話しました。 興味があれば『PHP Parserで学ぶPHP』[1]もご覧下さい。 ※PHP Parser v4.14.0前提です。 参考資料
1. https://speakerdeck.com/inouehi/php-parserdexue-buphp
107 まとめ
108 まとめ • ケーススタディを通じて静的解析の作り方を紹介した。 • PHP Parserや静的解析の仕組みを確認した。 • PHPの仕様を確認した。
ご清聴ありがとうございました
110 • わかりにくかった事 • 発見した事 • 役に立った?立たない? etc. フィードバックを是非お願いしますmm