Slide 1

Slide 1 text

PHP Parserで学ぶPHP 2023/03/24 PHPerKaigi 2023

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

PHP ParserとはPHPのコードをパースするライブラリです。これは静的 解析やコードの操作等に用いられます。 そんな言わばPHPを最もよく知るライブラリからPHPのことを学ぼうとい うのが本トークのテーマです。 トーク概要 3 https://fortee.jp/phperkaigi-2023/proposal/86b0f058-2b05-4a06-a7d3-825aec7997bb

Slide 4

Slide 4 text

本トークではPHPを構成する要素に着目します。PHP Parserはコードを パースし、ノードと呼ばれる単位に分解します。 ノードの定義を読み解くことで、PHPやPHP Parserについて理解を深め たいと思います。 トーク概要 4

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

アジェンダ 1. 注意事項 2. 基礎知識 3. ノード鳥瞰 6 4. PHPクイズ 5. ノードの実装 6. まとめ

Slide 7

Slide 7 text

注意事項 7

Slide 8

Slide 8 text

PHPを正しく理解するために1次情報で裏どりを 本トークはPHP Parserを、気づきを得るきっかけとして活用することを目指していま す。 8

Slide 9

Slide 9 text

基礎知識 9

Slide 10

Slide 10 text

PHP Parser超概要 PHPのコードを読み込んでAST(抽象構文木)を生成する。 10

Slide 11

Slide 11 text

AST(抽象構文木) “通常の構文木(具象構文木あるいは解析木とも言う)から、言語の意味に 関係ない情報を取り除き、意味に関係ある情報のみを取り出した(抽象し た)木構造の木である。” (Wikipedia) “構文木(こうぶんぎ)とは、構文解析の経過や結果(またはそれら両方)を 木構造で表したもの。” (Wikipedia) 11

Slide 12

Slide 12 text

AST(抽象構文木) 12 x = (1 + 2) * 3 = * x + 3 1 2

Slide 13

Slide 13 text

AST(抽象構文木) 13 x = (1 + 2) * 3 = * x + 3 1 2 ノード ノード ノード

Slide 14

Slide 14 text

AST(抽象構文木) 14 x = (1 + 2) * 3 = * x + 3 1 2 親 子 子 親 子 子

Slide 15

Slide 15 text

PHP ParserにおけるAST

Slide 16

Slide 16 text

object(PhpParser\Node\Stmt\Echo_)#1180 (2) { ["exprs"]=> array(1) { [0]=> object(PhpParser\Node\Scalar\String_)#1179 (2) { ["value"]=> string(9) "あしたっていまさ" ["attributes":protected]=> array(4) { ["startLine"]=> int(2) ["endLine"]=> int(2) ["kind"]=> int(1) ["rawValue"]=> string(11) "'あしたっていまさ'" } } } (略) } }

Slide 17

Slide 17 text

object(PhpParser\Node\Stmt\Echo_)#1180 (2) { ["exprs"]=> array(1) { [0]=> object(PhpParser\Node\Scalar\String_)#1179 (2) { ["value"]=> string(9) "あしたっていまさ" ["attributes":protected]=> array(4) { ["startLine"]=> int(2) ["endLine"]=> int(2) ["kind"]=> int(1) ["rawValue"]=> string(11) "'あしたっていまさ'" } } } (略) } }

Slide 18

Slide 18 text

object(PhpParser\Node\Stmt\Echo_)#1180 (2) { ["exprs"]=> array(1) { [0]=> object(PhpParser\Node\Scalar\String_)#1179 (2) { ["value"]=> string(9) "あしたっていまさ" ["attributes":protected]=> array(4) { ["startLine"]=> int(2) ["endLine"]=> int(2) ["kind"]=> int(1) ["rawValue"]=> string(11) "'あしたっていまさ'" } } } (略) } }

Slide 19

Slide 19 text

PHP ParserにおけるAST Node(親)がSub Node(子)を持つという階層構造になっている。 19 class Echo_ extends Node\Stmt { /** @var Node\Expr[] Expressions */ public $exprs; (略) public function getSubNodeNames() : array { return ['exprs']; } public function getType() : string { return 'Stmt_Echo'; } }

Slide 20

Slide 20 text

abstract class Stmt extends NodeAbstract PHP ParserにおけるAST 20 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); }

Slide 21

Slide 21 text

PHP ParserにおけるAST

Slide 22

Slide 22 text

PHP ParserにおけるAST

Slide 23

Slide 23 text

array(1) { [0]=> object(PhpParser\Node\Stmt\Expression)#1184 (2) { ["expr"]=> object(PhpParser\Node\Expr\Assign)#1183 (3) { ["var"]=> object(PhpParser\Node\Expr\Variable)#1179 (2) { ["name"]=> string(3) "x" (略) } ["expr"]=> object(PhpParser\Node\Expr\BinaryOp\Plus)#1182 (3) { ["left"]=> object(PhpParser\Node\Scalar\LNumber)#1180 (2) { ["value"]=> int(1) (略) } ["right"]=> object(PhpParser\Node\Scalar\LNumber)#1181 (2) { ["value"]=> int(2) PHP ParserにおけるAST

Slide 24

Slide 24 text

ノード鳥瞰 24

Slide 25

Slide 25 text

前提 • PHP Parser v4.14.0 に基づいて発表資料を作成する。 25

Slide 26

Slide 26 text

4種類のノード[1] • 文に相当するノード • 式に相当するノード • スカラ値に相当するノード • その他のノード 26 1. https://github.com/nikic/PHP-Parser/blob/4.x/doc/2_Usage_of_basic_components.markdown#node-tree-structure

Slide 27

Slide 27 text

文と式 27 PHPのコードは文(と式)から成る。 • 文[1] • ;で区切られた塊 • {}で囲まれた制御構造(if文等) など • 式[2] • 値があるもの全て 1. https://www.php.net/manual/ja/control-structures.intro.php 2. https://www.php.net/manual/ja/language.expressions.php

Slide 28

Slide 28 text

文と式 28 statement と expression

Slide 29

Slide 29 text

文(PhpParser\Node\Stmt) “PhpParser\Node\Stmts are statement nodes, i.e. language constructs that do not return a value and can not occur in an expression. For example a class definition is a statement. It doesn't return a value and you can't write something like func(class A {});.”[1] PhpParser\Node\Stmtsは文のノードです。つまり値を返さず式の中に含めら れない言語構造です。例えばクラスの定義は文です。それは値を返さず func(class A {});のように書くことができません。 29 1. https://github.com/nikic/PHP-Parser/blob/4.x/doc/2_Usage_of_basic_components.markdown#node-tree-structure

Slide 30

Slide 30 text

文(PhpParser\Node\Stmt) 30 Break_ DeclareDeclare Foreach_ Namespace_ TraitUse Case_ Do_ Function_ Nop TraitUseAdaptation Catch_ Echo_ Global_ Property TryCatch ClassConst Else_ Goto_ PropertyProperty Unset_ ClassLike ElseIf_ GroupUse Return_ Use_ ClassMethod Enum_ HaltCompiler Static_ UseUse Class_ EnumCase If_ StaticVar While_ Const_ Expression InlineHTML Switch_ Alias Continue_ Finally_ Interface_ Throw_ Precedence Declare_ For_ Label Trait_

Slide 31

Slide 31 text

文(PhpParser\Node\Stmt) 31 break; declare(strict_types=1); foreach ($foos as $foo) {} namespace Foo; class Bar { use Foo; } switch ($foo) { case 'bar'; } do {} while(true); function foo() {} // 空行 traitの衝突回避機構 try {} catch (Exception $e) {} echo 'foo'; global $foo, $bar; class Foo { private string $bar; } try {} catch (Exception $e) {} class Foo { private const BAR = 'bar'; } if ($foo) {} elseif ($bar) {} else {}; goto foo; foo: class Foo { private string $bar; } unset($foo); クラスの定義等 if ($foo) {} elseif ($bar) {} else {}; use Foo\{ Bar, Baz as Qux }; return; use Foo; class Foo { private function foo() {} } enum Foo {} __halt_compiler(); function foo() { static $bar; } use function foo as f; class Foo {} enum Foo{ case Foo; } if ($foo) {} elseif ($bar) {} else {}; function foo() { static $bar; } while (true) {}; const FOO = 'foo'; foo(); foo switch ($foo) { case 'bar'; } class Qux { use Foo, Bar { Bar::baz insteadof Foo; Bar::baz as bazz; }} continue; try {} catch (Exception $e) {} filnaly {} interface Foo{} throw new Exception(''); class Qux { use Foo, Bar { Bar::baz insteadof Foo; Bar::baz as bazz; }} declare(strict_types=1); for ($i = 0; $i < 21; $i++) {} goto foo; foo: trait Foo {}

Slide 32

Slide 32 text

式(PhpParser\Node\Expr) "PhpParser\Node\Exprs are expression nodes, i.e. language constructs that return a value and thus can occur in other expressions. Examples of expressions are $var (PhpParser\Node\Expr\Variable) and func() (PhpParser\Node\Expr\FuncCall)."[1] PhpParser\Node\Exprsは式のノードです。つまり値を返すため式の中に含 められる言語構造です。例えば$varやfunc()は式です。 32 1. https://github.com/nikic/PHP-Parser/blob/4.x/doc/2_Usage_of_basic_components.markdown#node-tree-structure

Slide 33

Slide 33 text

式(PhpParser\Node\Expr) 33 Array_ ClassConstFetch Instanceof_ Print_ BitwiseAnd ShiftRight Identical ShiftRight ArrayDimFetch Clone_ Isset_ PropertyFetch BitwiseOr BitwiseAnd LogicalAnd Smaller ArrayItem Closure List_ ShellExec BitwiseXor BitwiseOr LogicalOr SmallerOrEqual ArrowFunction ClosureUse Match_ StaticCall Coalesce BitwiseXor LogicalXor Spaceship Assign ConstFetch MethodCall StaticPropertyFetch Concat BooleanAnd Minus Array_ AssignOp Empty_ New_ Ternary Div BooleanOr Mod Bool_ AssignRef Error NullsafeMethodCall Throw_ Minus Coalesce Mul Double BinaryOp ErrorSuppress NullsafePropertyFetch UnaryMinus Mod Concat NotEqual Int_ BitwiseNot Eval_ PostDec UnaryPlus Mul Div NotIdentical Object_ BooleanNot Exit_ PostInc Variable Plus Equal Plus String_ CallLike FuncCall PreDec Yield_ Pow Greater Pow Unset_ Cast Include_ PreInc YieldFrom ShiftLeft GreaterOrEqual ShiftLeft

Slide 34

Slide 34 text

式(PhpParser\Node\Expr) 34 array(); Foo::BAR; $foo instanceof Foo; print 'foo'; $foo &= $bar; $foo >>= $bar; $foo === $bar; $foo >> $bar; $foo[0]; $foo = clone $bar; isset($foo); $foo->bar; $foo |= $bar; $foo & $bar; $foo and $bar; $foo < $bar; $foo(['bar']); function () {}; list($foo) = []; `ls -al`; $foo ^= $bar; $foo | $bar; $foo or $bar; $foo <= $bar; fn() => 'foo'; function () use ($foo) {}; match ($foo) { 'bar' => 'baz' }; Foo::bar(); $foo ??= $bar; $foo ^ $bar; $foo xor $bar; $foo <=> $bar; $foo = 'bar'; FOO; $foo->bar(); Foo::bar; $foo .= $bar; $foo && $bar; $foo - $bar; (array)$foo; 代入演算子 empty($foo); new Foo(); ($foo) ? 'bar' : 'baz'; $foo /= $bar; $foo || $bar; $foo % $bar; (bool)$foo; $foo =& $bar; $foo->; $foo?->bar(); throw new Exception(''); $foo -= $bar; $foo ?? $bar; $foo * $bar; (float)$foo; ビット演算子 @$foo->; $foo?->bar; -77; $foo %= $bar; $bar . $baz; $foo != $bar; (int)$foo; ~ $foo; eval('foo'); $foo--; +77; $foo *= $bar; $foo / $bar; $foo !== $bar; (object)$foo; !$foo; exit(); $foo++; $foo; $foo += $bar; $foo == $bar; $foo + $bar; (string)$foo; 関数コール等 foo(); --$foo; yield $foo; $foo **= $bar; $foo > $bar; $foo ** $bar; (unset)$foo; キャスト include 'foo'; ++$foo; yield from $foo; $foo <<= $bar; $foo >= $bar; $foo << $bar;

Slide 35

Slide 35 text

スカラ値(PhpParser\Node\Scalar) "PhpParser\Node\Scalars are nodes representing scalar values, like 'string' (PhpParser\Node\Scalar\String_), 0 (PhpParser\Node\Scalar\LNumber) or magic constants like __FILE__ (PhpParser\Node\Scalar\MagicConst\File). All PhpParser\Node\Scalars extend PhpParser\Node\Expr, as scalars are expressions, too."[1] PhpParser\Node\Scalarsはスカラ値を表すノードです。例えば’string’, 0や __FILE__のようなマジック定数です。全てのスカラ値は式でもあるため PhpParser\Node\Exprを拡張します。 35 1. https://github.com/nikic/PHP-Parser/blob/4.x/doc/2_Usage_of_basic_components.markdown#node-tree-structure

Slide 36

Slide 36 text

スカラ値(PhpParser\Node\Scalar) 36 DNumber LNumber Class_ Function_ Namespace_ Encapsed MagicConst Dir Line Trait_ EncapsedStringPart String_ File Method

Slide 37

Slide 37 text

スカラ値(PhpParser\Node\Scalar) 37 77.0; 77; __CLASS__; __FUNCTION__; __NAMESPACE__; "$foo is not bar"; マジック定数 __DIR__; __LINE__; __TRAIT__; `ls -al`; 'foo'; __FILE__; __METHOD__;

Slide 38

Slide 38 text

その他 "There are some nodes not in either of these groups, for example names (PhpParser\Node\Name) and call arguments (PhpParser\Node\Arg)."[1] 以上の3つのどれにも属さないノードがあります。例えばnamesやarguments です。 38 1. https://github.com/nikic/PHP-Parser/blob/4.x/doc/2_Usage_of_basic_components.markdown#node-tree-structure

Slide 39

Slide 39 text

その他 39 Arg Const_ IntersectionType Param VariadicPlaceholder Attribute Expr MatchArm Scalar VarLikeIdentifier AttributeGroup FunctionLike Name Stmt FullyQualified ComplexType Identifier NullableType UnionType Relative

Slide 40

Slide 40 text

その他 40 foo($bar); const FOO = 'foo'; function foo(Bar&Baz $qux) {} function foo(string $bar) {} foo(...); #[foo] class Bar{} foo(); match ($foo) { 'bar' => 'baz', }; $foo = 'bar'; class Foo { public string $bar; } #[foo(0), foo(1)] class Bar{} 関数の定義等 namespace foo; $foo = 'bar'; new \foo\Bar(); Nullable型等 class Foo {} function foo(?string $bar) {} function foo(Bar|Baz $qux) {} new namespace\Bar();

Slide 41

Slide 41 text

PHPクイズ (6問) 41 #phperkaigi #b

Slide 42

Slide 42 text

①throwは式?それとも文? 42

Slide 43

Slide 43 text

①throwは式?それとも文? 【正解】 8.0.0からは式、それより前は文。 https://www.php.net/manual/ja/language.exceptions.php 43

Slide 44

Slide 44 text

①throwは式?それとも文? 【正解】 8.0.0からは式、それより前は文。 PHP Parserには2種類のthrowが定義されている。 • PhpParser\Node\Expr\Throw_ • PhpParser\Node\Stmt\Throw_ 44

Slide 45

Slide 45 text

②issetの引数として正しいのはどちら? 1. isset($foo) 2. isset($foo, $bar) 45

Slide 46

Slide 46 text

②issetの引数として正しいのはどちら? 【正解】 両方正しい。 https://www.php.net/manual/ja/function.isset.php 46

Slide 47

Slide 47 text

②issetの引数として正しいのはどちら? 【正解】 両方正しい。 47 isset( $foo, $bar ); 親ノード 子ノード

Slide 48

Slide 48 text

class Isset_ extends Expr { /** @var Expr[] Variables */ public $vars; /** * Constructs an array node. * * @param Expr[] $vars Variables * @param array $attributes Additional attributes */ public function __construct(array $vars, array $attributes = []) { $this->attributes = $attributes; $this->vars = $vars; } ②issetの引数として正しいのはどちら? 【正解】 両方正しい。 48 複数の式を受け取ることを想定し ている。 isset( $foo, $bar ); 親ノード 子ノード

Slide 49

Slide 49 text

関数と言語構造 PHPには言語構造という関数とぱっとみ見分けがつかない代物がある。 https://www.php.net/manual/ja/language.types.callable.phpによると “ array(), echo, empty(), eval(), exit(), isset(), list(), print あるいは unset()” https://www.php.net/manual/ja/functions.variable-functions.phpによると "echo, print, isset(), empty(), include, require" ここに記載されていない言語構造が他にもありますが… 49

Slide 50

Slide 50 text

関数と言語構造 PHP Parserでは関数呼び出しをFuncCallノードで表現するが、 言語構造は独自のノードで表現する。 50 Array_ Eval_ Include_ List_ Empty_ Exit_ Isset_ Print_ Echo_ Return_ Unset_ # Stmt(文) # Expr(式)

Slide 51

Slide 51 text

関数と言語構造 ちなみに… assertはPHP7で言語構造になったが、FuncCallノードで表現する。 51 https://www.php.net/manual/ja/function.assert.php

Slide 52

Slide 52 text

③die()関数(言語構造)とは何か? 52

Slide 53

Slide 53 text

③die()関数(言語構造)とは何か? 【正解】 exit()と同等。 https://www.php.net/manual/ja/function.die.php 53

Slide 54

Slide 54 text

③die()関数(言語構造)とは何か? 【正解】 exit()と同等。 54 class Exit_ extends Expr { /* For use in "kind" attribute */ const KIND_EXIT = 1; const KIND_DIE = 2; /** @var null|Expr Expression */ public $expr; エイリアスをconstで見分けてい る。

Slide 55

Slide 55 text

エイリアスやバリエーション 他にも例えば… 55 class Array_ extends Expr { // For use in "kind" attribute const KIND_LONG = 1; // array() syntax const KIND_SHORT = 2; // [] syntax class String_ extends Scalar { /* For use in "kind" attribute */ const KIND_SINGLE_QUOTED = 1; const KIND_DOUBLE_QUOTED = 2; const KIND_HEREDOC = 3; const KIND_NOWDOC = 4;

Slide 56

Slide 56 text

④foo(...)とは何か? 56

Slide 57

Slide 57 text

④foo(...)とは何か? 【正解】 第一級callableを生成する記法。 https://www.php.net/manual/ja/functions.first_class_callable_syntax.php 57

Slide 58

Slide 58 text

④foo(...)とは何か? 【正解】 第一級callableを生成する記法。 58 class VariadicPlaceholder extends NodeAbstract { /** * Create a variadic argument placeholder (first-class callable syntax). * * @param array $attributes Additional attributes */ public function __construct(array $attributes = []) { $this->attributes = $attributes; }

Slide 59

Slide 59 text

⑤定数の宣言として正しいのはどちら? 1. const FOO = 'foo'; const BAR = 'bar'; 2. const FOO = 'foo', BAR = 'bar'; 59

Slide 60

Slide 60 text

⑤定数の宣言として正しいのはどちら? 【正解】 両方正しい。 60

Slide 61

Slide 61 text

⑤定数の宣言として正しいのはどちら? 【正解】 両方正しい。 61 class Const_ extends Node\Stmt { /** @var Node\Const_[] Constant declarations */ public $consts; /** * Constructs a const list node. * * @param Node\Const_[] $consts Constant declarations * @param array $attributes Additional attributes */ public function __construct(array $consts, array $attributes = []) { $this->attributes = $attributes; $this->consts = $consts; } 複数の定数宣言を受け取ること を想定している。

Slide 62

Slide 62 text

⑥関数の書き方(※)は何種類ある? ※関数の様に引数をとり戻り値を返す構文 62

Slide 63

Slide 63 text

⑥関数の書き方は何種類ある? 【正解】 4種類。 63

Slide 64

Slide 64 text

⑥関数の書き方は何種類ある? 【正解】 4種類。 FunctionLikeというインタフェースがあり、その実装が4種類ある。 64 ● ArrowFunction アロー関数 ● ClassMethod メソッド ● Closure クロージャ ● Function_ 関数

Slide 65

Slide 65 text

便宜的なノード PHPの要素を直接的に表すわけではないノードがいくつかある。 interfaceやabstractで定義され、性質の似たノードがまとめられている。 65

Slide 66

Slide 66 text

1 FunctionLike interface アロー関数、クロージャ、メソッド、関数の定義が実装するインタフェース 2 ComplexType abstract IntersectionType, NullableType, UnionTypeが拡張する抽象クラス 3 Expr abstract 式が拡張する抽象クラス 4 Scalar abstract スカラ値が拡張する抽象クラス 5 Stmt abstract 文が拡張する抽象クラス 6 AssignOp abstract 代入演算子が拡張する抽象クラス 7 BinaryOp abstract ビット演算子が拡張する抽象クラス 8 CallLike abstract オブジェクト生成、メソッド、関数の呼び出しが拡張する抽象クラス 9 Cast abstract キャストが拡張する抽象クラス 10 MagicConst abstract マジック定数が拡張する抽象クラス 11 ClassLike abstract trait, interface, enum, classが拡張する抽象クラス 12 TraitUseAdaptation abstract Alias, Precedenceが拡張する抽象クラス 便宜的なノード 66

Slide 67

Slide 67 text

ノードの実装 67

Slide 68

Slide 68 text

目のつけどころ 68 • const • getSubNodeNames() • プロパティ

Slide 69

Slide 69 text

目のつけどころ 69 • const バリエーション(上述) • getSubNodeNames() サブノードの一覧 • プロパティ サブノードの概要

Slide 70

Slide 70 text

目のつけどころ 70 • const バリエーション(上述) • getSubNodeNames() サブノードの一覧 • プロパティ サブノードの概要 ものによっては+α

Slide 71

Slide 71 text

ケーススタディ(while) 71 class While_ extends Node\Stmt { /** @var Node\Expr Condition */ public $cond; /** @var Node\Stmt[] Statements */ public $stmts; /** * Constructs a while node. * * @param Node\Expr $cond Condition * @param Node\Stmt[] $stmts Statements * @param array $attributes Additional attributes */ public function __construct(Node\Expr $cond, array $stmts = [], array $attributes = []) { $this->attributes = $attributes; $this->cond = $cond; $this->stmts = $stmts; } public function getSubNodeNames() : array { return ['cond', 'stmts']; }

Slide 72

Slide 72 text

ケーススタディ(while) 72 class While_ extends Node\Stmt { /** @var Node\Expr Condition */ public $cond; /** @var Node\Stmt[] Statements */ public $stmts; /** * Constructs a while node. * * @param Node\Expr $cond Condition * @param Node\Stmt[] $stmts Statements * @param array $attributes Additional attributes */ public function __construct(Node\Expr $cond, array $stmts = [], array $attributes = []) { $this->attributes = $attributes; $this->cond = $cond; $this->stmts = $stmts; } public function getSubNodeNames() : array { return ['cond', 'stmts']; }

Slide 73

Slide 73 text

ケーススタディ(while) 73 class While_ extends Node\Stmt { /** @var Node\Expr Condition */ public $cond; /** @var Node\Stmt[] Statements */ public $stmts; /** * Constructs a while node. * * @param Node\Expr $cond Condition * @param Node\Stmt[] $stmts Statements * @param array $attributes Additional attributes */ public function __construct(Node\Expr $cond, array $stmts = [], array $attributes = []) { $this->attributes = $attributes; $this->cond = $cond; $this->stmts = $stmts; } public function getSubNodeNames() : array { return ['cond', 'stmts']; }

Slide 74

Slide 74 text

ケーススタディ(while) 74 class While_ extends Node\Stmt { /** @var Node\Expr Condition */ public $cond; /** @var Node\Stmt[] Statements */ public $stmts; /** * Constructs a while node. * * @param Node\Expr $cond Condition * @param Node\Stmt[] $stmts Statements * @param array $attributes Additional attributes */ public function __construct(Node\Expr $cond, array $stmts = [], array $attributes = []) { $this->attributes = $attributes; $this->cond = $cond; $this->stmts = $stmts; } public function getSubNodeNames() : array { return ['cond', 'stmts']; } while (true) { echo “無”; echo “駄”; }

Slide 75

Slide 75 text

ケーススタディ(関数) 75 interface FunctionLike extends Node { /** * Whether to return by reference * * @return bool */ public function returnsByRef() : bool; /** * List of parameters * * @return Param[] */ public function getParams() : array; /** * Get the declared return type or null * * @return null|Identifier|Name|ComplexType */ public function getReturnType(); /** * The function body * * @return Stmt[]|null */ public function getStmts(); /** * Get PHP attribute groups. * * @return AttributeGroup[] */ public function getAttrGroups() : array; }

Slide 76

Slide 76 text

class Function_ extends Node\Stmt implements FunctionLike { /** @var bool Whether function returns by reference */ public $byRef; /** @var Node\Identifier Name */ public $name; /** @var Node\Param[] Parameters */ public $params; /** @var null|Node\Identifier|Node\Name|Node\ComplexType Return type */ public $returnType; /** @var Node\Stmt[] Statements */ public $stmts; /** @var Node\AttributeGroup[] PHP attribute groups */ public $attrGroups; (略) public function getSubNodeNames() : array { return ['attrGroups', 'byRef', 'name', 'params', 'returnType', 'stmts']; } ケーススタディ(関数) 76

Slide 77

Slide 77 text

class Function_ extends Node\Stmt implements FunctionLike { /** @var bool Whether function returns by reference */ public $byRef; /** @var Node\Identifier Name */ public $name; /** @var Node\Param[] Parameters */ public $params; /** @var null|Node\Identifier|Node\Name|Node\ComplexType Return type */ public $returnType; /** @var Node\Stmt[] Statements */ public $stmts; /** @var Node\AttributeGroup[] PHP attribute groups */ public $attrGroups; (略) public function getSubNodeNames() : array { return ['attrGroups', 'byRef', 'name', 'params', 'returnType', 'stmts']; } ケーススタディ(関数) 77

Slide 78

Slide 78 text

class Function_ extends Node\Stmt implements FunctionLike { /** @var bool Whether function returns by reference */ public $byRef; /** @var Node\Identifier Name */ public $name; /** @var Node\Param[] Parameters */ public $params; /** @var null|Node\Identifier|Node\Name|Node\ComplexType Return type */ public $returnType; /** @var Node\Stmt[] Statements */ public $stmts; /** @var Node\AttributeGroup[] PHP attribute groups */ public $attrGroups; (略) public function getSubNodeNames() : array { return ['attrGroups', 'byRef', 'name', 'params', 'returnType', 'stmts']; } ケーススタディ(関数) 78 function &f($param) {} function f($param) {}

Slide 79

Slide 79 text

class Function_ extends Node\Stmt implements FunctionLike { /** @var bool Whether function returns by reference */ public $byRef; /** @var Node\Identifier Name */ public $name; /** @var Node\Param[] Parameters */ public $params; /** @var null|Node\Identifier|Node\Name|Node\ComplexType Return type */ public $returnType; /** @var Node\Stmt[] Statements */ public $stmts; /** @var Node\AttributeGroup[] PHP attribute groups */ public $attrGroups; (略) public function getSubNodeNames() : array { return ['attrGroups', 'byRef', 'name', 'params', 'returnType', 'stmts']; } ケーススタディ(関数) 79 function f($param) {}

Slide 80

Slide 80 text

class Function_ extends Node\Stmt implements FunctionLike { /** @var bool Whether function returns by reference */ public $byRef; /** @var Node\Identifier Name */ public $name; /** @var Node\Param[] Parameters */ public $params; /** @var null|Node\Identifier|Node\Name|Node\ComplexType Return type */ public $returnType; /** @var Node\Stmt[] Statements */ public $stmts; /** @var Node\AttributeGroup[] PHP attribute groups */ public $attrGroups; (略) public function getSubNodeNames() : array { return ['attrGroups', 'byRef', 'name', 'params', 'returnType', 'stmts']; } ケーススタディ(関数) 80 function f($param) {}

Slide 81

Slide 81 text

class Function_ extends Node\Stmt implements FunctionLike { /** @var bool Whether function returns by reference */ public $byRef; /** @var Node\Identifier Name */ public $name; /** @var Node\Param[] Parameters */ public $params; /** @var null|Node\Identifier|Node\Name|Node\ComplexType Return type */ public $returnType; /** @var Node\Stmt[] Statements */ public $stmts; /** @var Node\AttributeGroup[] PHP attribute groups */ public $attrGroups; (略) public function getSubNodeNames() : array { return ['attrGroups', 'byRef', 'name', 'params', 'returnType', 'stmts']; } ケーススタディ(関数) 81 function f(): bool {} function f(): SomeType {} function f(): bool|int {}

Slide 82

Slide 82 text

class Function_ extends Node\Stmt implements FunctionLike { /** @var bool Whether function returns by reference */ public $byRef; /** @var Node\Identifier Name */ public $name; /** @var Node\Param[] Parameters */ public $params; /** @var null|Node\Identifier|Node\Name|Node\ComplexType Return type */ public $returnType; /** @var Node\Stmt[] Statements */ public $stmts; /** @var Node\AttributeGroup[] PHP attribute groups */ public $attrGroups; (略) public function getSubNodeNames() : array { return ['attrGroups', 'byRef', 'name', 'params', 'returnType', 'stmts']; } ケーススタディ(関数) 82 function f() { $awesomeValue = awesome(); return $awesomeValue; }

Slide 83

Slide 83 text

class Function_ extends Node\Stmt implements FunctionLike { /** @var bool Whether function returns by reference */ public $byRef; /** @var Node\Identifier Name */ public $name; /** @var Node\Param[] Parameters */ public $params; /** @var null|Node\Identifier|Node\Name|Node\ComplexType Return type */ public $returnType; /** @var Node\Stmt[] Statements */ public $stmts; /** @var Node\AttributeGroup[] PHP attribute groups */ public $attrGroups; (略) public function getSubNodeNames() : array { return ['attrGroups', 'byRef', 'name', 'params', 'returnType', 'stmts']; } ケーススタディ(関数) 83 #[Attribute] function f() {}

Slide 84

Slide 84 text

ケーススタディ(関数) getSubNodeNames()を比較することで、相違点も分かる。 84 ArrowFunction 'attrGroups', 'byRef', 'params', 'returnType', 'static', 'expr' ClassMethod 'attrGroups', 'byRef', 'name', 'params', 'returnType', 'stmts', 'flags' Closure 'attrGroups', 'byRef', 'params', 'returnType', 'stmts', 'static', 'uses' Function_ 'attrGroups', 'byRef', 'name', 'params', 'returnType', 'stmts'

Slide 85

Slide 85 text

class Param extends NodeAbstract { /** @var null|Identifier|Name|ComplexType Type declaration */ public $type; /** @var bool Whether parameter is passed by reference */ public $byRef; /** @var bool Whether this is a variadic argument */ public $variadic; /** @var Expr\Variable|Expr\Error Parameter variable */ public $var; /** @var null|Expr Default value */ public $default; /** @var int */ public $flags; /** @var AttributeGroup[] PHP attribute groups */ public $attrGroups; (略) public function getSubNodeNames() : array { return ['attrGroups', 'flags', 'type', 'byRef', 'variadic', 'var', 'default']; } ケーススタディ(Param) 85

Slide 86

Slide 86 text

class Param extends NodeAbstract { /** @var null|Identifier|Name|ComplexType Type declaration */ public $type; /** @var bool Whether parameter is passed by reference */ public $byRef; /** @var bool Whether this is a variadic argument */ public $variadic; /** @var Expr\Variable|Expr\Error Parameter variable */ public $var; /** @var null|Expr Default value */ public $default; /** @var int */ public $flags; /** @var AttributeGroup[] PHP attribute groups */ public $attrGroups; (略) public function getSubNodeNames() : array { return ['attrGroups', 'flags', 'type', 'byRef', 'variadic', 'var', 'default']; } ケーススタディ(Param) 86

Slide 87

Slide 87 text

class Param extends NodeAbstract { /** @var null|Identifier|Name|ComplexType Type declaration */ public $type; /** @var bool Whether parameter is passed by reference */ public $byRef; /** @var bool Whether this is a variadic argument */ public $variadic; /** @var Expr\Variable|Expr\Error Parameter variable */ public $var; /** @var null|Expr Default value */ public $default; /** @var int */ public $flags; /** @var AttributeGroup[] PHP attribute groups */ public $attrGroups; (略) public function getSubNodeNames() : array { return ['attrGroups', 'flags', 'type', 'byRef', 'variadic', 'var', 'default']; } ケーススタディ(Param) 87 function f(bool $param){} function f(SomeType $param) {} function f(bool|int $param) {}

Slide 88

Slide 88 text

class Param extends NodeAbstract { /** @var null|Identifier|Name|ComplexType Type declaration */ public $type; /** @var bool Whether parameter is passed by reference */ public $byRef; /** @var bool Whether this is a variadic argument */ public $variadic; /** @var Expr\Variable|Expr\Error Parameter variable */ public $var; /** @var null|Expr Default value */ public $default; /** @var int */ public $flags; /** @var AttributeGroup[] PHP attribute groups */ public $attrGroups; (略) public function getSubNodeNames() : array { return ['attrGroups', 'flags', 'type', 'byRef', 'variadic', 'var', 'default']; } ケーススタディ(Param) 88 function f(&$param) {} function f($param) {}

Slide 89

Slide 89 text

class Param extends NodeAbstract { /** @var null|Identifier|Name|ComplexType Type declaration */ public $type; /** @var bool Whether parameter is passed by reference */ public $byRef; /** @var bool Whether this is a variadic argument */ public $variadic; /** @var Expr\Variable|Expr\Error Parameter variable */ public $var; /** @var null|Expr Default value */ public $default; /** @var int */ public $flags; /** @var AttributeGroup[] PHP attribute groups */ public $attrGroups; (略) public function getSubNodeNames() : array { return ['attrGroups', 'flags', 'type', 'byRef', 'variadic', 'var', 'default']; } ケーススタディ(Param) 89 function f(...$param) {}

Slide 90

Slide 90 text

class Param extends NodeAbstract { /** @var null|Identifier|Name|ComplexType Type declaration */ public $type; /** @var bool Whether parameter is passed by reference */ public $byRef; /** @var bool Whether this is a variadic argument */ public $variadic; /** @var Expr\Variable|Expr\Error Parameter variable */ public $var; /** @var null|Expr Default value */ public $default; /** @var int */ public $flags; /** @var AttributeGroup[] PHP attribute groups */ public $attrGroups; (略) public function getSubNodeNames() : array { return ['attrGroups', 'flags', 'type', 'byRef', 'variadic', 'var', 'default']; } ケーススタディ(Param) 90 function f($param) {} function f(param) {}

Slide 91

Slide 91 text

class Param extends NodeAbstract { /** @var null|Identifier|Name|ComplexType Type declaration */ public $type; /** @var bool Whether parameter is passed by reference */ public $byRef; /** @var bool Whether this is a variadic argument */ public $variadic; /** @var Expr\Variable|Expr\Error Parameter variable */ public $var; /** @var null|Expr Default value */ public $default; /** @var int */ public $flags; /** @var AttributeGroup[] PHP attribute groups */ public $attrGroups; (略) public function getSubNodeNames() : array { return ['attrGroups', 'flags', 'type', 'byRef', 'variadic', 'var', 'default']; } ケーススタディ(Param) 91 function f($param = “di molto”) {}

Slide 92

Slide 92 text

class Param extends NodeAbstract { /** @var null|Identifier|Name|ComplexType Type declaration */ public $type; /** @var bool Whether parameter is passed by reference */ public $byRef; /** @var bool Whether this is a variadic argument */ public $variadic; /** @var Expr\Variable|Expr\Error Parameter variable */ public $var; /** @var null|Expr Default value */ public $default; /** @var int */ public $flags; /** @var AttributeGroup[] PHP attribute groups */ public $attrGroups; (略) public function getSubNodeNames() : array { return ['attrGroups', 'flags', 'type', 'byRef', 'variadic', 'var', 'default']; } ケーススタディ(Param) 92 ???

Slide 93

Slide 93 text

class Param extends NodeAbstract { /** @var null|Identifier|Name|ComplexType Type declaration */ public $type; /** @var bool Whether parameter is passed by reference */ public $byRef; /** @var bool Whether this is a variadic argument */ public $variadic; /** @var Expr\Variable|Expr\Error Parameter variable */ public $var; /** @var null|Expr Default value */ public $default; /** @var int */ public $flags; /** @var AttributeGroup[] PHP attribute groups */ public $attrGroups; (略) public function getSubNodeNames() : array { return ['attrGroups', 'flags', 'type', 'byRef', 'variadic', 'var', 'default']; } ケーススタディ(Param) 93 コンストラクタの@paramによると Optional visibility flags

Slide 94

Slide 94 text

class Param extends NodeAbstract { /** @var null|Identifier|Name|ComplexType Type declaration */ public $type; /** @var bool Whether parameter is passed by reference */ public $byRef; /** @var bool Whether this is a variadic argument */ public $variadic; /** @var Expr\Variable|Expr\Error Parameter variable */ public $var; /** @var null|Expr Default value */ public $default; /** @var int */ public $flags; /** @var AttributeGroup[] PHP attribute groups */ public $attrGroups; (略) public function getSubNodeNames() : array { return ['attrGroups', 'flags', 'type', 'byRef', 'variadic', 'var', 'default']; } ケーススタディ(Param) 94 function f(#[Attribute]$param) {}

Slide 95

Slide 95 text

まとめ 95

Slide 96

Slide 96 text

ノードの定義からPHPの仕様を垣間みることができる 96

Slide 97

Slide 97 text

PHP Parserで静的解析を実装するにはノードを理解する必要がある 97

Slide 98

Slide 98 text

PHP Parserをいかに活用するか いかに活用するかはクライアント次第です。 クライアントの例としてPsalm, PHPStan, Rector等があります。 PHP Parserを用いた静的解析に興味のある方は 拙作『Introduction to Static Analysis through Psalm[1]』も参照下さい。 1. https://fortee.jp/phpcon-2022/proposal/0c110d95-4068-4854-a190-18cc6f6b6555 98

Slide 99

Slide 99 text

Enjoy PHP!! 99

Slide 100

Slide 100 text

Enjoy 静的解析!! 100

Slide 101

Slide 101 text

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