🌲

mago ast コマンドによる PHP AST のダンプ

に公開

※ この記事は、現時点で最新のバージョン 1.0.0-alpha.7 を対象にしています。

Mago にはコマンド一覧にあるようにast コマンドが存在します。

mago help
Welcome to Mago!

Mago is a powerful and versatile toolchain for PHP developers, designed to help you write better code, faster.

Features:

* **Linting:** Identify and fix code style issues and potential bugs.
* **Formatting:** Format your code consistently and automatically.
* **Finding:** Quickly locate symbols and references in your codebase.
* **Analyzing:** Analyze your code for structure, complexity, and dependencies.

Get started by exploring the commands below!

Usage: mago [OPTIONS] <COMMAND>

Commands:
  init         Initialize the configuration for Mago
  ast          Analyze the abstract syntax tree (AST) of PHP code
  lint         Lint PHP code using Mago's linter
  analyze      Analyze PHP code using Mago's type checker
  format       Format PHP code using Mago's formatter
  find         Find references to symbols in PHP code
  self-update  Update Mago to the latest version
  help         Print this message or the help of the given subcommand(s)

Options:
      --workspace <WORKSPACE>
          The path to the workspace directory. This is the root directory of your project. If not specified, defaults to the current working directory.

      --config <CONFIG>
          The path to the configuration file. If not specified, Mago will search for a `mago.toml` file in the workspace directory.

      --php-version <PHP_VERSION>
          The PHP version to use for parsing and analysis. This should be a valid PHP version number (e.g., 8.0, 8.1). This value overrides the `php_version` setting in the configuration file and the `MAGO_PHP_VERSION` environment variable.

      --threads <THREADS>
          The number of threads to use for linting and formatting. If not specified, Mago will use all available logical CPUs. This value overrides the `threads` setting in the configuration file and the `MAGO_THREADS` environment variable.

      --allow-unsupported-php-version
          Allow using an unsupported PHP version. This is not recommended, as it may lead to unexpected behavior. This value overrides the `allow_unsupported_php_version` setting in the configuration file and the `MAGO_ALLOW_UNSUPPORTED_PHP_VERSION` environment
          variable.

  -h, --help
          Print help (see a summary with '-h')

  -V, --version
          Print version

実行サンプル

  • 以下のようなコードのファイルがあるとき
<?php

declare(strict_types=1);

namespace Laminas\Hydrator\Exception;

use Throwable;

/**
 * Exception marker interface
 */
interface ExceptionInterface extends Throwable
{
}

--file にて対象を指定してAST(抽象構文木) のダンプが行えます。

✗ mago ast --file src/Exception/ExceptionInterface.php
Program
├── Statement
│   └── OpeningTag
│       └── FullOpeningTag
├── Statement
│   └── Declare
│       ├── Keyword
│       ├── DeclareItem
│       │   ├── LocalIdentifier
│       │   └── Expression
│       │       └── Literal
│       │           └── LiteralInteger
│       └── DeclareBody
│           └── Statement
└── Statement
    └── Namespace
        ├── Keyword
        ├── Identifier
        │   └── QualifiedIdentifier
        └── NamespaceBody
            └── NamespaceImplicitBody
                ├── Terminator
                ├── Statement
                │   └── Use
                │       ├── Keyword
                │       ├── UseItems
                │       │   └── UseItemSequence
                │       │       └── UseItem
                │       │           └── Identifier
                │       │               └── LocalIdentifier
                │       └── Terminator
                └── Statement
                    └── Interface
                        ├── Keyword
                        ├── LocalIdentifier
                        └── Extends
                            ├── Keyword
                            └── Identifier
                                └── LocalIdentifier

json での出力

  • 出力は、--json オプションをつけることでJSON出力が行えます。
✗ mago ast --file src/Exception/ExceptionInterface.php --json
{
  "error": null,
  "interner": [
    [
      19,
      "extends"
    ],
    [
      10,
      "namespace"
    ],
    [
      2,
      "\n\n"
    ],
    [
      14,
      "Throwable"
    ],
    [
      1,
      "<?php"
    ],
    [
      18,
      "ExceptionInterface"
    ],
    [
      3,
      "declare"
    ],
    [
      16,
      "\n"
    ],
    [
      20,
      "{"
    ],
    [
      12,
      "Laminas\\Hydrator\\Exception"
    ],
    [
      9,
      ";"
    ],
    [
      7,
      "1"
    ],
    [
      5,
      "strict_types"
    ],
    [
      4,
      "("
    ],
    [
      15,
      "/**\n * Exception marker interface\n */"
    ],
    [
      11,
      " "
    ],
    [
      6,
      "="
    ],
    [
      13,
      "use"
    ],
    [
      21,
      "}"
    ],
    [
      17,
      "interface"
    ],
    [
      8,
      ")"
    ]
  ],
  "program": {
    "file_id": 10878188936224073747,
    "statements": {
      "nodes": [
        {
          "type": "OpeningTag",
          "value": {
            "type": "Full",
            "value": {
              "span": {
                "end": {
                  "offset": 5
                },
                "file_id": 10878188936224073747,
                "start": {
                  "offset": 0
                }
              },
              "value": 1
            }
          }
        },
        {
          "type": "Declare",
          "value": {
            "body": {
              "type": "Statement",
              "value": {
                "type": "Noop",
                "value": {
                  "end": {
                    "offset": 31
                  },
                  "file_id": 10878188936224073747,
                  "start": {
                    "offset": 30
                  }
                }
              }
            },
            "declare": {
              "span": {
                "end": {
                  "offset": 14
                },
                "file_id": 10878188936224073747,
                "start": {
                  "offset": 7
                }
              },
              "value": 3
            },
            "items": {
              "nodes": [
                {
                  "equal": {
                    "end": {
                      "offset": 28
                    },
                    "file_id": 10878188936224073747,
                    "start": {
                      "offset": 27
                    }
                  },
                  "name": {
                    "span": {
                      "end": {
                        "offset": 27
                      },
                      "file_id": 10878188936224073747,
                      "start": {
                        "offset": 15
                      }
                    },
                    "value": 5
                  },
                  "value": {
                    "type": "Literal",
                    "value": {
                      "type": "Integer",
                      "value": {
                        "raw": 7,
                        "span": {
                          "end": {
                            "offset": 29
                          },
                          "file_id": 10878188936224073747,
                          "start": {
                            "offset": 28
                          }
                        },
                        "value": 1
                      }
                    }
                  }
                }
              ],
              "tokens": []
            },
            "left_parenthesis": {
              "end": {
                "offset": 15
              },
              "file_id": 10878188936224073747,
              "start": {
                "offset": 14
              }
            },
            "right_parenthesis": {
              "end": {
                "offset": 30
              },
              "file_id": 10878188936224073747,
              "start": {
                "offset": 29
              }
            }
          }
        },
        {
          "type": "Namespace",
          "value": {
            "body": {
              "type": "Implicit",
              "value": {
                "statements": {
                  "nodes": [
                    {
                      "type": "Use",
                      "value": {
                        "items": {
                          "type": "Sequence",
                          "value": {
                            "file_id": 10878188936224073747,
                            "items": {
                              "nodes": [
                                {
                                  "alias": null,
                                  "name": {
                                    "type": "Local",
                                    "value": {
                                      "span": {
                                        "end": {
                                          "offset": 85
                                        },
                                        "file_id": 10878188936224073747,
                                        "start": {
                                          "offset": 76
                                        }
                                      },
                                      "value": 14
                                    }
                                  }
                                }
                              ],
                              "tokens": []
                            },
                            "start": {
                              "offset": 76
                            }
                          }
                        },
                        "terminator": {
                          "type": "Semicolon",
                          "value": {
                            "end": {
                              "offset": 86
                            },
                            "file_id": 10878188936224073747,
                            "start": {
                              "offset": 85
                            }
                          }
                        },
                        "use": {
                          "span": {
                            "end": {
                              "offset": 75
                            },
                            "file_id": 10878188936224073747,
                            "start": {
                              "offset": 72
                            }
                          },
                          "value": 13
                        }
                      }
                    },
                    {
                      "type": "Interface",
                      "value": {
                        "attribute_lists": {
                          "nodes": []
                        },
                        "extends": {
                          "extends": {
                            "span": {
                              "end": {
                                "offset": 162
                              },
                              "file_id": 10878188936224073747,
                              "start": {
                                "offset": 155
                              }
                            },
                            "value": 19
                          },
                          "types": {
                            "nodes": [
                              {
                                "type": "Local",
                                "value": {
                                  "span": {
                                    "end": {
                                      "offset": 172
                                    },
                                    "file_id": 10878188936224073747,
                                    "start": {
                                      "offset": 163
                                    }
                                  },
                                  "value": 14
                                }
                              }
                            ],
                            "tokens": []
                          }
                        },
                        "interface": {
                          "span": {
                            "end": {
                              "offset": 135
                            },
                            "file_id": 10878188936224073747,
                            "start": {
                              "offset": 126
                            }
                          },
                          "value": 17
                        },
                        "left_brace": {
                          "end": {
                            "offset": 174
                          },
                          "file_id": 10878188936224073747,
                          "start": {
                            "offset": 173
                          }
                        },
                        "members": {
                          "nodes": []
                        },
                        "name": {
                          "span": {
                            "end": {
                              "offset": 154
                            },
                            "file_id": 10878188936224073747,
                            "start": {
                              "offset": 136
                            }
                          },
                          "value": 18
                        },
                        "right_brace": {
                          "end": {
                            "offset": 176
                          },
                          "file_id": 10878188936224073747,
                          "start": {
                            "offset": 175
                          }
                        }
                      }
                    }
                  ]
                },
                "terminator": {
                  "type": "Semicolon",
                  "value": {
                    "end": {
                      "offset": 70
                    },
                    "file_id": 10878188936224073747,
                    "start": {
                      "offset": 69
                    }
                  }
                }
              }
            },
            "name": {
              "type": "Qualified",
              "value": {
                "span": {
                  "end": {
                    "offset": 69
                  },
                  "file_id": 10878188936224073747,
                  "start": {
                    "offset": 43
                  }
                },
                "value": 12
              }
            },
            "namespace": {
              "span": {
                "end": {
                  "offset": 42
                },
                "file_id": 10878188936224073747,
                "start": {
                  "offset": 33
                }
              },
              "value": 10
            }
          }
        }
      ]
    },
    "trivia": {
      "nodes": [
        {
          "kind": {
            "type": "WhiteSpace"
          },
          "span": {
            "end": {
              "offset": 7
            },
            "file_id": 10878188936224073747,
            "start": {
              "offset": 7
            }
          },
          "value": 2
        },
        {
          "kind": {
            "type": "WhiteSpace"
          },
          "span": {
            "end": {
              "offset": 33
            },
            "file_id": 10878188936224073747,
            "start": {
              "offset": 33
            }
          },
          "value": 2
        },
        {
          "kind": {
            "type": "WhiteSpace"
          },
          "span": {
            "end": {
              "offset": 43
            },
            "file_id": 10878188936224073747,
            "start": {
              "offset": 43
            }
          },
          "value": 11
        },
        {
          "kind": {
            "type": "WhiteSpace"
          },
          "span": {
            "end": {
              "offset": 72
            },
            "file_id": 10878188936224073747,
            "start": {
              "offset": 72
            }
          },
          "value": 2
        },
        {
          "kind": {
            "type": "WhiteSpace"
          },
          "span": {
            "end": {
              "offset": 76
            },
            "file_id": 10878188936224073747,
            "start": {
              "offset": 76
            }
          },
          "value": 11
        },
        {
          "kind": {
            "type": "WhiteSpace"
          },
          "span": {
            "end": {
              "offset": 88
            },
            "file_id": 10878188936224073747,
            "start": {
              "offset": 88
            }
          },
          "value": 2
        },
        {
          "kind": {
            "type": "DocBlockComment"
          },
          "span": {
            "end": {
              "offset": 125
            },
            "file_id": 10878188936224073747,
            "start": {
              "offset": 88
            }
          },
          "value": 15
        },
        {
          "kind": {
            "type": "WhiteSpace"
          },
          "span": {
            "end": {
              "offset": 126
            },
            "file_id": 10878188936224073747,
            "start": {
              "offset": 126
            }
          },
          "value": 16
        },
        {
          "kind": {
            "type": "WhiteSpace"
          },
          "span": {
            "end": {
              "offset": 136
            },
            "file_id": 10878188936224073747,
            "start": {
              "offset": 136
            }
          },
          "value": 11
        },
        {
          "kind": {
            "type": "WhiteSpace"
          },
          "span": {
            "end": {
              "offset": 155
            },
            "file_id": 10878188936224073747,
            "start": {
              "offset": 155
            }
          },
          "value": 11
        },
        {
          "kind": {
            "type": "WhiteSpace"
          },
          "span": {
            "end": {
              "offset": 163
            },
            "file_id": 10878188936224073747,
            "start": {
              "offset": 163
            }
          },
          "value": 11
        },
        {
          "kind": {
            "type": "WhiteSpace"
          },
          "span": {
            "end": {
              "offset": 173
            },
            "file_id": 10878188936224073747,
            "start": {
              "offset": 173
            }
          },
          "value": 16
        },
        {
          "kind": {
            "type": "WhiteSpace"
          },
          "span": {
            "end": {
              "offset": 175
            },
            "file_id": 10878188936224073747,
            "start": {
              "offset": 175
            }
          },
          "value": 16
        },
        {
          "kind": {
            "type": "WhiteSpace"
          },
          "span": {
            "end": {
              "offset": 177
            },
            "file_id": 10878188936224073747,
            "start": {
              "offset": 177
            }
          },
          "value": 16
        }
      ]
    }
  }
}

[参考] nikic/php-paser のダンプ

  • 参考として、nikic/php-paser にてJSON今回対象としたサンプルのPHPコードをJSONダンプした場合は、以下のような出力となります。
./vendor/bin/php-parse -j src/Exception/ExceptionInterface.php 2>/dev/null
[
    {
        "nodeType": "Stmt_Declare",
        "attributes": {
            "startLine": 3,
            "startFilePos": 7,
            "endLine": 3,
            "endFilePos": 30
        },
        "declares": [
            {
                "nodeType": "Stmt_DeclareDeclare",
                "attributes": {
                    "startLine": 3,
                    "startFilePos": 15,
                    "endLine": 3,
                    "endFilePos": 28
                },
                "key": {
                    "nodeType": "Identifier",
                    "attributes": {
                        "startLine": 3,
                        "startFilePos": 15,
                        "endLine": 3,
                        "endFilePos": 26
                    },
                    "name": "strict_types"
                },
                "value": {
                    "nodeType": "Scalar_LNumber",
                    "attributes": {
                        "startLine": 3,
                        "startFilePos": 28,
                        "endLine": 3,
                        "endFilePos": 28,
                        "rawValue": "1",
                        "kind": 10
                    },
                    "value": 1
                }
            }
        ],
        "stmts": null
    },
    {
        "nodeType": "Stmt_Namespace",
        "attributes": {
            "startLine": 5,
            "startFilePos": 33,
            "endLine": 14,
            "endFilePos": 175,
            "kind": 1
        },
        "name": {
            "nodeType": "Name",
            "attributes": {
                "startLine": 5,
                "startFilePos": 43,
                "endLine": 5,
                "endFilePos": 68
            },
            "parts": [
                "Laminas",
                "Hydrator",
                "Exception"
            ]
        },
        "stmts": [
            {
                "nodeType": "Stmt_Use",
                "attributes": {
                    "startLine": 7,
                    "startFilePos": 72,
                    "endLine": 7,
                    "endFilePos": 85
                },
                "type": 1,
                "uses": [
                    {
                        "nodeType": "Stmt_UseUse",
                        "attributes": {
                            "startLine": 7,
                            "startFilePos": 76,
                            "endLine": 7,
                            "endFilePos": 84
                        },
                        "type": 0,
                        "name": {
                            "nodeType": "Name",
                            "attributes": {
                                "startLine": 7,
                                "startFilePos": 76,
                                "endLine": 7,
                                "endFilePos": 84
                            },
                            "parts": [
                                "Throwable"
                            ]
                        },
                        "alias": null
                    }
                ]
            },
            {
                "nodeType": "Stmt_Interface",
                "attributes": {
                    "startLine": 12,
                    "startFilePos": 126,
                    "comments": [
                        {
                            "nodeType": "Comment_Doc",
                            "text": "\/**\n * Exception marker interface\n *\/",
                            "line": 9,
                            "filePos": 88,
                            "tokenPos": 20,
                            "endLine": 11,
                            "endFilePos": 124,
                            "endTokenPos": 20
                        }
                    ],
                    "endLine": 14,
                    "endFilePos": 175
                },
                "name": {
                    "nodeType": "Identifier",
                    "attributes": {
                        "startLine": 12,
                        "startFilePos": 136,
                        "endLine": 12,
                        "endFilePos": 153
                    },
                    "name": "ExceptionInterface"
                },
                "stmts": [],
                "attrGroups": [],
                "namespacedName": null,
                "extends": [
                    {
                        "nodeType": "Name",
                        "attributes": {
                            "startLine": 12,
                            "startFilePos": 163,
                            "endLine": 12,
                            "endFilePos": 171
                        },
                        "parts": [
                            "Throwable"
                        ]
                    }
                ]
            }
        ]
    }
]

Discussion