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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  7. 注意事項
    7

    View full-size slide

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

    View full-size slide

  9. 基礎知識
    9

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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


    子 親


    View full-size slide

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

    View full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size slide

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

    View full-size slide

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

    View full-size 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 full-size slide

  24. ノード鳥瞰
    24

    View full-size slide

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

    View full-size 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 full-size 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 full-size slide

  28. 文と式
    28
    statement と expression

    View full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size slide

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

    View full-size 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 full-size 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 full-size slide

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

    View full-size 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size 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 full-size 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size 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 full-size 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 full-size slide

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

    View full-size slide

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

    View full-size 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 full-size slide

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

    View full-size slide

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

    View full-size 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size 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 full-size slide

  67. ノードの実装
    67

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size slide

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

    View full-size slide

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

    View full-size slide

  97. 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 full-size slide

  98. Enjoy PHP!!
    99

    View full-size slide

  99. Enjoy 静的解析!!
    100

    View full-size slide

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

    View full-size slide