Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Learning PHP with PHP Parser

inouehi
March 24, 2023

Learning PHP with PHP Parser

『PHP Parserで学ぶPHP』

PHPerKaigi 2023
2023-03-24 14:05~ Track B
https://phperkaigi.jp/2023/

inouehi

March 24, 2023
Tweet

More Decks by inouehi

Other Decks in Programming

Transcript

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  7. 注意事項
    7

    View Slide

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

    View Slide

  9. 基礎知識
    9

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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


    子 親


    View Slide

  15. PHP ParserにおけるAST
    echo 'あしたっていまさ';
    15

    View Slide

  16. echo 'あしたっていまさ';
    PHP ParserにおけるAST
    16
    array(1) {
    [0]=>
    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) "'あしたっていまさ'"
    }
    }
    }
    (略)
    }
    }

    View Slide

  17. echo 'あしたっていまさ';
    PHP ParserにおけるAST
    17
    array(1) {
    [0]=>
    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) "'あしたっていまさ'"
    }
    }
    }
    (略)
    }
    }

    View Slide

  18. echo 'あしたっていまさ';
    PHP ParserにおけるAST
    18
    array(1) {
    [0]=>
    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) "'あしたっていまさ'"
    }
    }
    }
    (略)
    }
    }

    View Slide

  19. 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';
    }
    }

    View Slide

  20. 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);
    }

    View Slide

  21. PHP ParserにおけるAST
    $x = 1 + 2;
    21

    View Slide

  22. PHP ParserにおけるAST
    $x = 1 + 2;
    22
    =
    +
    $x
    1 2

    View Slide

  23. 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
    $x = 1 + 2;
    23
    =
    +
    $x
    1 2
    Assign
    Variable Plus
    LNumber
    LNumber
    Expression

    View Slide

  24. ノード鳥瞰
    24

    View Slide

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

    View Slide

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

    View Slide

  27. 文と式
    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

    View Slide

  28. 文と式
    28
    statement と expression

    View Slide

  29. 文(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

    View Slide

  30. 文(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_

    View Slide

  31. 文(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 {}

    View Slide

  32. 式(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

    View Slide

  33. 式(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

    View Slide

  34. 式(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;

    View Slide

  35. スカラ値(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

    View Slide

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

    View Slide

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

    View Slide

  38. その他
    "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

    View Slide

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

    View Slide

  40. その他
    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();

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  48. 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 );
    親ノード
    子ノード

    View Slide

  49. 関数と言語構造
    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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  54. ③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で見分けてい
    る。

    View Slide

  55. エイリアスやバリエーション
    他にも例えば…
    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;

    View Slide

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

    View Slide

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

    View Slide

  58. ④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;
    }

    View Slide

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

    View Slide

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

    View Slide

  61. ⑤定数の宣言として正しいのはどちら?
    【正解】
    両方正しい。
    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;
    }
    複数の定数宣言を受け取ること
    を想定している。

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  66. 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

    View Slide

  67. ノードの実装
    67

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  71. ケーススタディ(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'];
    }

    View Slide

  72. ケーススタディ(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'];
    }

    View Slide

  73. ケーススタディ(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'];
    }

    View Slide

  74. ケーススタディ(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 “駄”;
    }

    View Slide

  75. ケーススタディ(関数)
    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;
    }

    View Slide

  76. 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

    View Slide

  77. 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

    View Slide

  78. 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) {}

    View Slide

  79. 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) {}

    View Slide

  80. 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) {}

    View Slide

  81. 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 {}

    View Slide

  82. 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;
    }

    View Slide

  83. 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() {}

    View Slide

  84. ケーススタディ(関数)
    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'

    View Slide

  85. 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

    View Slide

  86. 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

    View Slide

  87. 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) {}

    View Slide

  88. 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) {}

    View Slide

  89. 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) {}

    View Slide

  90. 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) {}

    View Slide

  91. 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”) {}

    View Slide

  92. 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
    ???

    View Slide

  93. 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

    View Slide

  94. 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) {}

    View Slide

  95. まとめ
    95

    View Slide

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

    View Slide

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

    View Slide

  98. 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

    View Slide

  99. Enjoy PHP!!
    99

    View Slide

  100. Enjoy 静的解析!!
    100

    View Slide

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

    View Slide