Slide 1

Slide 1 text

PHP Parserで学ぶPHPと静的解析 2024/03/09 PHPerKaigi 2024 レギュラートーク

Slide 2

Slide 2 text

2 ● Hiroki Inoue ● Software Engineer ● Engineering Manager @ WHITEPLUS, Inc. About Me

Slide 3

Slide 3 text

3 トーク概要 このトークではPHP Parserを利用して静的解析ツールを作る話をします。 具体例を通じて静的解析というものの解像度を高め、より身近に感じられるようになることを目指しま す。また、静的解析を通して解析対象であるPHPの理解を深めることも目指します。 https://fortee.jp/phperkaigi-2024/proposal/15b92894-11cb-4022-85d5-bf8279f61d43

Slide 4

Slide 4 text

4 1. PHP Parserに興味がある方 2. 静的解析の仕組みに興味がある方 3. PHPに興味がある方 誰向けのトーク?

Slide 5

Slide 5 text

5 アジェンダ はじめに 題材の説明 01 02 初期設計 実装・設計見直し 03 04 学び 05 まとめ 06

Slide 6

Slide 6 text

6 はじめに

Slide 7

Slide 7 text

7 紹介する設計・実装は唯一の正解を示すものではありません。 一つのアイデアとして、あるいは、反面教師として受け取っていただければ幸いです。 できればフィードバックいただけるとうれしいです。 注意事項

Slide 8

Slide 8 text

8 ● わかりにくかった事 ● 発見した事 ● 役に立った?立たない? etc. フィードバックを是非お願いしますmm

Slide 9

Slide 9 text

9 コードを実行することなく、コードを解析すること。 コードを解析して品質等を検査できる。 静的解析とは

Slide 10

Slide 10 text

10 コードを実行することなく、コードを解析すること。 コードを解析して品質等を検査できる。 PHPの静的解析ツールは、PHPを解析するのだから、PHPのことをよく知っている。 静的解析を理解するということは、PHPを理解することでもある。 静的解析とは

Slide 11

Slide 11 text

11 ● PHPのコードを抽象構文木と呼ばれるオブジェクト群に変換する。 ● オブジェクト1つ1つをノードと呼び、200種類弱のノードがある。 ○ if → If_ノード ○ 関数宣言 → Function_ノード ○ = → Assignノード etc. ● 静的解析ツールは、そのオブジェクトを操作することでコードを解析する。コードを書き換える ことだってできる。 PHP Parserの基礎知識 参考: PHP Parserで学ぶPHP

Slide 12

Slide 12 text

12 PHP Parserの基礎知識

Slide 13

Slide 13 text

13 このトークではPHP Parser v5.0.xを使用します。 PHP Parserの基礎知識

Slide 14

Slide 14 text

14 題材の説明

Slide 15

Slide 15 text

15 簡易的な依存可視化ツールを作ります。 クラス間の依存の方向、数、深さからコードの複雑さを認識し、バッドスメルを察知して設計を見直す こと等を意図したものです。 どんな静的解析ツールを作るのか

Slide 16

Slide 16 text

16 簡易的な どんな静的解析ツールを作るのか

Slide 17

Slide 17 text

17 どんな静的解析ツールを作るのか final class A { public function __construct(private readonly B $b) {} } A B

Slide 18

Slide 18 text

18 どんな静的解析ツールを作るのか A B 循環依存

Slide 19

Slide 19 text

19 どんな静的解析ツールを作るのか A B D E F C 広い

Slide 20

Slide 20 text

20 どんな静的解析ツールを作るのか A B D C 深い

Slide 21

Slide 21 text

21 どんな静的解析ツールを作るのか A B D C E F G H I 広くて深くて循環して… J

Slide 22

Slide 22 text

22 出力イメージ ※Class, Interface, Trait, Enumの判別がつかないものはStereotypeとして描画します

Slide 23

Slide 23 text

23 初期設計

Slide 24

Slide 24 text

24 1. コードの中からクラス(への依存)を見つけ出す。 2. 見つけたクラスのコードを読み込んで1へ。 3. 図示する。 ツールに期待する振る舞い

Slide 25

Slide 25 text

25 ● 型宣言 ○ 引数、戻り値、プロパティ ● new ● static ○ メソッドコール、プロパティアクセス etc. 1. コードの中からクラスを見つけ出す function foo(Bar $bar): Baz class Foo { private Bar $bar; } new Foo() Foo::bar Foo::baz() class Foo {}

Slide 26

Slide 26 text

26 クラス名はPHP ParserのNameノードに格納されている。 1. コードの中からクラスを見つけ出す

Slide 27

Slide 27 text

27 1. クラス名(Nameノード)を収集する。 2. useと組み合わせて修飾名を得る。 3. 同一の名前空間にあるクラスの場合、namespaceと組み合わせて修飾名を得る。 1. コードの中からクラスを見つけ出す

Slide 28

Slide 28 text

28 1. クラス名(Nameノード)を収集する。 2. useと組み合わせて修飾名を得る。 3. 同一の名前空間にあるクラスの場合、namespaceと組み合わせて修飾名を得る。 1. コードの中からクラスを見つけ出す PHP Parserが提供するNodeFinderが使えそう

Slide 29

Slide 29 text

29 1. 修飾名からファイルパスを得る。 2. そのファイルを読み込む。 2. 見つけたクラスのコードを読み込む

Slide 30

Slide 30 text

30 1. PlantUML等に変換して出力する。 3. 図示する

Slide 31

Slide 31 text

31 1. グローバル関数の戻り値の型 2. 配列の型 3. 式の解決 4. PHPDocの解釈 5. self, parent, staticの読み替え 6. 並列処理 etc. 非対応事項

Slide 32

Slide 32 text

32 実装・設計見直し

Slide 33

Slide 33 text

33 1. コードの中からクラス(への依存)を見つけ出す。 2. 見つけたクラスのコードを読み込んで1へ。 3. 図示する。 ツールに期待する振る舞い(再掲)

Slide 34

Slide 34 text

34 1. NodeFinderを用いてNameノードを取得する。 2. NodeFinderを用いてUse_ノードを取得する。 3. NodeFinderを用いてNamespace_ノードを取得する。 4. 1~3を組み合わせて修飾名を得る。 a. Nameノードからクラス名を得る。 b. クラス名が完全修飾名の場合、何もしない。 c. クラス名とUse_から得た名前空間を結合して、修飾名を得る。 d. cがない場合、Namespace_から得た名前空間を結合して、修飾名を得る。 クラスを見つけ出す

Slide 35

Slide 35 text

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ノードを探す

Slide 36

Slide 36 text

36 1. NodeFinderを用いてNameノードを取得する。 2. NodeFinderを用いてUse_ノードを取得する。 3. NodeFinderを用いてNamespace_ノードを取得する。 4. 1~3を組み合わせて修飾名を得る。 a. Nameノードからクラス名を得る。 b. クラス名が完全修飾名の場合、何もしない。 c. そうでない場合、クラス名とUse_から得た名前空間を結合して、修飾名を得る。 d. cがない場合、Namespace_から得た名前空間を結合して、修飾名を得る。 クラスを見つけ出す

Slide 37

Slide 37 text

37 1. NodeFinderを用いてNameノードを取得する。 2. NodeFinderを用いてUse_ノードを取得する。 3. NodeFinderを用いてNamespace_ノードを取得する。 4. 1~3を組み合わせて修飾名を得る。 a. Nameノードからクラス名を得る。 b. クラス名が完全修飾名の場合、何もしない。 c. そうでない場合、クラス名とUse_から得た名前空間を結合して、修飾名を得る。 d. cがない場合、Namespace_から得た名前空間を結合して、修飾名を得る。 クラスを見つけ出す 煩雑で ちょっと面倒…

Slide 38

Slide 38 text

38 それ、NameResolverでシンプルにできます クラスを見つけ出す

Slide 39

Slide 39 text

39 NameResolverはクラス名等の名前を解決する。 クラスを見つけ出す - NameResolver[1] 1. https://github.com/nikic/PHP-Parser/blob/ce019e9ad711e31ee87c2c4c72e538b5240970c3/doc/component/Name_resolution.markdown

Slide 40

Slide 40 text

40 NameResolverはクラス名等の名前を解決する。 クラスを見つけ出す - NameResolver $nameResolver = new PhpParser\NodeVisitor\NameResolver; $nodeTraverser = new PhpParser\NodeTraverser; $nodeTraverser->addVisitor($nameResolver); $ast = $nodeTraverser->traverse($ast); この中に含まれるクラス名等を 完全修飾名にできる

Slide 41

Slide 41 text

41 設計見直し クラスの修飾名を得るのに、NodeFinder+自作ロジックではなくNameResolverを使う。

Slide 42

Slide 42 text

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); } サンプルコード

Slide 43

Slide 43 text

43 1. 修飾名からファイルパスを得る。例えば、ReflectionClassを利用してみる。 2. file_get_contents()でコードを読み込む。 見つけたクラスのコードを読み込む

Slide 44

Slide 44 text

44 できた🎉 ReflectionClass ありがとう!

Slide 45

Slide 45 text

45 クラス名が取得できるようになったが、クラス以外の名前も混在していた。 これらにはクラスのファイルが存在しない。 ● 定数 ● キーワード ● 関数名 ● 特殊なクラス名: self, parent, static[1] ● namespace[1] しかし、この実装には問題がある 1. Nameノードの代わりにFullyQualifiedノードでフィルターするとこれらは混入しない。 実装例: https://github.com/hirokinoue/phperkaigi-2024/tree/main/src/Example2

Slide 46

Slide 46 text

46 クラスにも2種類ある。 ● ユーザー定義クラス ● 定義済みのクラス ユーザー定義クラスと違って定義済みのクラスにはPHPで書かれたファイルが存在しない。 しかし、この実装には問題がある

Slide 47

Slide 47 text

47 設計見直し2 ● ReflectionClassの例外をハンドリングする。 ○ クラス以外 ⇒ 例外をキャッチして次のノードの解析へ ○ ユーザー定義クラス ⇒ クラス名も中身も取得できる ○ 定義済みのクラス ⇒ クラス名のみ取得できる

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

49 1. (実装済み)ファイルを開く。 2. (実装済み)クラスを見つけ出す。 a. クラスが見つかったら1へ。 b. クラスが見つからなくなったら終わり。 読み込んだコードからまたクラスを見つけ出す

Slide 50

Slide 50 text

50 読み込んだコードからまたクラスを見つけ出す class Foo { public function foo(): Bar {} } class Bar { public function bar(): Qux {} }

Slide 51

Slide 51 text

51 読み込んだコードからまたクラスを見つけ出す class Foo { public function foo(): Bar {} } class Bar { public function bar(): Qux {} }

Slide 52

Slide 52 text

52 読み込んだコードからまたクラスを見つけ出す class Foo { public function foo(): Bar {} } class Bar { public function bar(): Qux {} }

Slide 53

Slide 53 text

53 読み込んだコードからまたクラスを見つけ出す Foo.php Bar.php

Slide 54

Slide 54 text

54 読み込んだコードからまたクラスを見つけ出す Foo.php Bar.php ノードを1つず辿る

Slide 55

Slide 55 text

55 読み込んだコードからまたクラスを見つけ出す Foo.php Bar.php ノードを1つず辿る PHP Parserが提供する NodeTraverserを利用

Slide 56

Slide 56 text

56 読み込んだコードからまたクラスを見つけ出す Foo.php Bar.php クラスを見つけ ファイルを開く

Slide 57

Slide 57 text

57 読み込んだコードからまたクラスを見つけ出す Foo.php Bar.php クラスを見つけ ファイルを開く ノードを処理する ビジターを作る

Slide 58

Slide 58 text

58 まとめ ● トラバーサ … ノードを一つずつ辿る。 ● ビジター … ノードを処理する。 読み込んだコードからまたクラスを見つけ出す

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

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; } 解析結果を 格納する箱

Slide 61

Slide 61 text

61 ● 解析結果を格納する箱。 ● DiagramUnitはDiagramUnitの配列を内包する。 DiagramUnit A B C D

Slide 62

Slide 62 text

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; } 入力ファイルがクラスの場合、クラス名 そうでない場合、便宜的な名前とする

Slide 63

Slide 63 text

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; } クラスを見つけて ファイルを開き クラスを見つけて...

Slide 64

Slide 64 text

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); } サンプルコード

Slide 65

Slide 65 text

65 サンプルコード public function enterNode(Node $node) { if (!$node instanceof FullyQualified) { // ノードに対して何も処理せず次のノードへ return $node; } $classFile = ClassLoader::create($node); if ($classFile->isClass()) { // ノードがクラスだった場合の処理 } return $node; } ClassVisitor

Slide 66

Slide 66 text

66 サンプルコード public function enterNode(Node $node) { if (!$node instanceof FullyQualified) { // ノードに対して何も処理せず次のノードへ return $node; } $classFile = ClassLoader::create($node); if ($classFile->isClass()) { // ノードがクラスだった場合の処理 } return $node; } ClassVisitor

Slide 67

Slide 67 text

67 サンプルコード public function enterNode(Node $node) { if (!$node instanceof FullyQualified) { // ノードに対して何も処理せず次のノードへ return $node; } $classFile = ClassLoader::create($node); if ($classFile->isClass()) { // ノードがクラスだった場合の処理 } return $node; } ClassVisitor

Slide 68

Slide 68 text

68 サンプルコード public function enterNode(Node $node) { if (!$node instanceof FullyQualified) { // ノードに対して何も処理せず次のノードへ return $node; } $classFile = ClassLoader::create($node); if ($classFile->isClass()) { // ノードがクラスだった場合の処理 } return $node; } ClassVisitor

Slide 69

Slide 69 text

69 サンプルコード public function enterNode(Node $node) { if (!$node instanceof FullyQualified) { // ノードに対して何も処理せず次のノードへ return $node; } $classFile = ClassLoader::create($node); if ($classFile->isClass()) { // ノードがクラスだった場合の処理 } return $node; } ClassVisitor

Slide 70

Slide 70 text

70 サンプルコード public function enterNode(Node $node) { if (!$node instanceof FullyQualified) { // ノードに対して何も処理せず次のノードへ return $node; } $classFile = ClassLoader::create($node); if ($classFile->isClass()) { // ノードがクラスだった場合の処理 } return $node; } ClassVisitor

Slide 71

Slide 71 text

71 サンプルコード public function enterNode(Node $node) { if (!$node instanceof FullyQualified) { // ノードに対して何も処理せず次のノードへ return $node; } $classFile = ClassLoader::create($node); if ($classFile->isClass()) { // ノードがクラスだった場合の処理 } return $node; } ClassVisitor

Slide 72

Slide 72 text

72 サンプルコード if ($classFile->isClass()) { // 見つけたクラスをサブクラスとして格納する。 $subClass = new DiagramUnit($classFile->className()); $this->diagramUnit->push($subClass); // クラスのファイルがない場合、パースせずに次のノードへ if ($classFile->codeNotFound()) { return $node; } ClassVisitor

Slide 73

Slide 73 text

73 サンプルコード if ($classFile->isClass()) { // 前述 // クラスのファイルをパースしてASTを生成する $parser = (new ParserFactory())->createForHostVersion(); $ast = $parser->parse($classFile->content()); if ($ast === null) { // ASTが生成できない場合、次のノードへ return $node; } ClassVisitor

Slide 74

Slide 74 text

74 サンプルコード if ($classFile->isClass()) { // 前述 // 依存先クラスが依存するクラスを探しに行く $nodeTraverser = new NodeTraverser(); $nodeTraverser->addVisitor(new NameResolver()); $nodeTraverser->addVisitor(new ClassVisitor($subClass)); $nodeTraverser->traverse($ast); } ClassVisitor

Slide 75

Slide 75 text

75 ● 分析結果の結果得られたオブジェクトを入力として ● PlantUML記法のテキストを出力する 図示する 実装例: https://github.com/hirokinoue/dependency-visualizer/blob/main/src/Exporter/PlantUmlExporter.php

Slide 76

Slide 76 text

76 ● 分析結果の結果得られたオブジェクトを入力として ● PlantUML記法のテキストを出力する 図示する 要するに テキスト出力 すればよい

Slide 77

Slide 77 text

77 完成🎉

Slide 78

Slide 78 text

78 完成🎉したと思われた…

Slide 79

Slide 79 text

79 循環依存がある場合、無限ループに陥りメモリリークする。 しかし、まだ問題があった A B

Slide 80

Slide 80 text

80 設計見直し3 無限ループを回避する。

Slide 81

Slide 81 text

81 設計見直し3 想定する3つのパターン ● A→A … 自身への依存 ● A→B→A … 直接依存するクラスからの循環依存 ● A→B→C→A … 2ステップ以上離れたクラスからの循環依存

Slide 82

Slide 82 text

82 設計見直し3 ● DiagramUnitに依存元クラスの履歴を保持する。 ● 読み込み済みのクラスは解析をスキップして次のノードへ。 例えば ● A→B→C→A の場合、CはA, B, Cの履歴を保持しており Aへの依存を見つけた時、循環依存を検知する。 ● この時、Aは読み込み済みなので解析しない。 実装例: https://github.com/hirokinoue/dependency-visualizer/pull/1/files

Slide 83

Slide 83 text

83 完成🎉

Slide 84

Slide 84 text

84 完成🎉したと思われた…

Slide 85

Slide 85 text

85 依存がある程度複雑になると処理がなかなか終わらない。 しかし、まだ問題があった

Slide 86

Slide 86 text

86 設計見直し4 パフォーマンスを改善する。

Slide 87

Slide 87 text

87 設計見直し4 1. これにより設計見直し3が不要になりもします。 実装例: https://github.com/hirokinoue/dependency-visualizer/pull/2/files 想定する3つのパターン 1. クラスの中で、複数回同じクラスに依存する場合、結果を束ねる。 2. 別の枝で同じクラスに依存したらトラバースをやめる。[1] 3. 読み込み済みのクラスをメモリ上にキャッシュしてディスクアクセスを減らす。

Slide 88

Slide 88 text

88 設計見直し4 1. クラスの中で、複数回同じクラスに依存する場合、結果を束ねる。 A B C class A { public function foo(): B {} public function bar(): B {} public function baz(): C {} }

Slide 89

Slide 89 text

89 設計見直し4 対策しないとこうなる…

Slide 90

Slide 90 text

90 設計見直し4 2. 別の枝で同じクラスに依存したらトラバースをやめる。 A B C D E D E A B C D E CがDに依存するとわかったら E以降の解析を止める

Slide 91

Slide 91 text

91 設計見直し4 3. 読み込み済みのクラスをメモリ上にキャッシュしてディスクアクセスを減らす。 A B C D E BからDを辿った時Dをロードするが CからDを辿った時はロードしない

Slide 92

Slide 92 text

92 完成🎉

Slide 93

Slide 93 text

93 完成🎉

Slide 94

Slide 94 text

94 完成🎉 改善はつづくよ、どこまでも ひとまず使えるようにはなった。 しかし、プロダクトとは常に進化しつづけるもの。

Slide 95

Slide 95 text

95 学び

Slide 96

Slide 96 text

96 一例を挙げます。 見聞きした事から何を学ぶかは人それぞれ。 前置き

Slide 97

Slide 97 text

97 ● 引数のオブジェクトはリファレンス渡し PHPに関する事

Slide 98

Slide 98 text

98 ● 引数のオブジェクトはリファレンス渡し ということ自体は常識だとして、その使い道の一事例として。 PHPに関する事

Slide 99

Slide 99 text

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

Slide 100

Slide 100 text

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が尊い。

Slide 101

Slide 101 text

101 ● PHP Parserが生成する抽象構文木(AST)の成り立ち ● ビジターパターン PHP Parser/静的解析に関すること

Slide 102

Slide 102 text

102 ● PHP Parserが生成する抽象構文木(AST)の成り立ち ○ 15:40〜 Track Aにて AST の解像度を高めるLTやります! ● ビジターパターン PHP Parser/静的解析に関すること

Slide 103

Slide 103 text

103 ● 再帰処理 ● パフォーマンス対策 PHP Parser/静的解析に関すること

Slide 104

Slide 104 text

104 ● lib/PhpParser/NodeVisitorに便利なクラスがある ● PHPDocの解析とPHPの解析は別物 ○ PHP ParserはPHPDocをパースしない ● PHPで書かれていない内部関数や定義済みのクラスを考慮した静的解析には一手間必要 PHP Parser/静的解析に関すること

Slide 105

Slide 105 text

105 15:40〜 Track AにてLTやります! PHP Parser、抽象構文木(AST)に興味がわいた方は是非 PHP Parser/静的解析に関すること

Slide 106

Slide 106 text

106 PHPerKaigi 2023で、PHPやPHP Parserについて話しました。 興味があれば『PHP Parserで学ぶPHP』[1]もご覧下さい。 ※PHP Parser v4.14.0前提です。 参考資料 1. https://speakerdeck.com/inouehi/php-parserdexue-buphp

Slide 107

Slide 107 text

107 まとめ

Slide 108

Slide 108 text

108 まとめ ● ケーススタディを通じて静的解析の作り方を紹介した。 ● PHP Parserや静的解析の仕組みを確認した。 ● PHPの仕様を確認した。

Slide 109

Slide 109 text

ご清聴ありがとうございました

Slide 110

Slide 110 text

110 ● わかりにくかった事 ● 発見した事 ● 役に立った?立たない? etc. フィードバックを是非お願いしますmm