🐶

さわって慣れるPHP Parser - IdentifierとNameとFullyQualified

2023/07/01に公開

さわって慣れるPHP Parserの続編です。

簡単なコードを解析してみることでPHP Parserに慣れ、ノードの理解を深めることを目指しています。今回はIdentifier, Name, FullyQualifiedという3つのノードの注目します。

さっそくPHP Parserをインストールします。

$ composer require --dev nikic/php-parser

解析対象のコード(Thoth.php)をしたためます。(PHPとしての適切さは度外視し、ノードを観察するためだけのコードです。)

<?php
Boingo::foresee();
\Boingo::foresee();
Oingo\Boingo::foresee();
\Oingo\Boingo::foresee();

解析します。

$ vendor/bin/php-parse Thoth.php 
====> File Thoth.php:
==> Node dump:
array(
    0: Stmt_Expression(
        expr: Expr_StaticCall(
            class: Name(
                parts: array(
                    0: Boingo
                )
            )
            name: Identifier(
                name: foresee
            )
            args: array(
            )
        )
    )
    1: Stmt_Expression(
        expr: Expr_StaticCall(
            class: Name_FullyQualified(
                parts: array(
                    0: Boingo
                )
            )
            name: Identifier(
                name: foresee
            )
            args: array(
            )
        )
    )
    2: Stmt_Expression(
        expr: Expr_StaticCall(
            class: Name(
                parts: array(
                    0: Oingo
                    1: Boingo
                )
            )
            name: Identifier(
                name: foresee
            )
            args: array(
            )
        )
    )
    3: Stmt_Expression(
        expr: Expr_StaticCall(
            class: Name_FullyQualified(
                parts: array(
                    0: Oingo
                    1: Boingo
                )
            )
            name: Identifier(
                name: foresee
            )
            args: array(
            )
        )
    )
)

色々でてますがサラリと流して、Expr_StaticCallに注目します。
--var-dumpモードで実行するとStaticCallクラスだと分かります。

StaticCallクラスのgetSubNodeNames()を確認します。

    public function getSubNodeNames() : array {
        return ['class', 'name', 'args'];
    }

プロパティも確認します。

class StaticCall extends CallLike
{
    /** @var Node\Name|Expr Class name */
    public $class;
    /** @var Identifier|Expr Method name */
    public $name;
    /** @var array<Arg|VariadicPlaceholder> Arguments */
    public $args;

Boingo::foresee();においてBoingo$classforesee$name 、もしあればforesee()の引数が$argsで、Boingoの型はNameExprforeseeの型はIdentifierExprだとわかります。Exprになりえるのは例えば変数のケースがあるからです。

変数のケースというのは、一例をあげると、以下のようにclassの部分が変数になっているケースです。

<?php
$boingo::foresee();
$ vendor/bin/php-parse ThothVariable.php 
====> File ThothVariable.php:
==> Node dump:
array(
    0: Stmt_Expression(
        expr: Expr_StaticCall(
            class: Expr_Variable(
                name: boingo
            )
            name: Identifier(
                name: foresee
            )
            args: array(
            )
        )
    )
)

Expr_StaticCallclassExpr_Variableになりました。
Exprは式を表すノードで、Variableは変数を表すノードです。そして変数は式です。

さてNameIdentifierに注目します。Identifierの定義を見てみます。
https://apiref.phpstan.org/1.9.x/PhpParser.Node.Identifier.html

Represents a non-namespaced name. Namespaced names are represented using Name nodes.

とあります。

メソッド名は名前空間付きで表記されるものではない一方でクラス名は名前空間付きで表記されますし、観察結果とも整合的です。

さて、冒頭の解析結果にはExpr_StaticCallが4つありました。これらの相違点にも注目してみます。

            class: Name(
                parts: array(
                    0: Boingo
                )
            )
            class: Name_FullyQualified(
                parts: array(
                    0: Boingo
                )
            )
            class: Name(
                parts: array(
                    0: Oingo
                    1: Boingo
                )
            )
            class: Name_FullyQualified(
                parts: array(
                    0: Oingo
                    1: Boingo
                )
            )

先頭に\をつけるとFullyQualified、つけないとNameになるようです。また、名前空間は配列で表現されるようです。
Fully Qualifiedは日本語でいうと完全修飾名で、PHPマニュアルも参考になります。

FullyQualifiedの定義を見てみます。
https://apiref.phpstan.org/1.9.x/PhpParser.Node.Name.FullyQualified.html

FullyQualifiedNameを拡張したものということも分かりました。

Discussion