[PHP]PHP-Parserを用いたメタプログラミングについて

こんにちは。24年度入社の吉岡です。
案件で、システムで用いられているPHPソースコードとフレームワークのバージョンアップ作業を行いっています。作業の内容として大量に同様の変更を行いたい箇所があり、自分たちのチームはPHP-Parserを使用しました。PHP-Parserは今後の案件でも作業の効率化に役立ってくれると思いまので、共有させていただきます。

〇PHP-Parserとは
PHP-Parserは「PHPで書かれたPHPパーサー」のことです。
PHPパーサーはPHPソースコードをAST(抽象構文木)に変換し、変更を加え、変更後のASTをPHPソースコードに出力することができます。
AST(抽象構文木)は指定されたプログラミング言語の文法に従ってソースコードの構造を表す中間プログラムです。ソースコードのアイテムごとに対応するノードを用いて表現されます。

「手作業でよいのでは?」と思われるかもしれませんね。確かに変更箇所が少数であったり複雑な変更を加えたい場合は手作業の方が楽な場合も多いでしょう。
しかし、単純な変更を大量にしなければならない場合はどうでしょうか?
システム内のすべてのファイルに declare(strict_types=1); を追加したい」「システム内のすべての配列を、array() から [] に記述を統一したい」という場合に、手作業で行うと多くの時間が掛かりミスが生じやすくなってしまうかもしれません。

そんな時はPHP-Parserの使用を検討されてはいかがでしょうか?


〇PHP-Parserを用いた変更処理の流れ
1 PHPソースコードからASTへパース
2 ASTへ変更を加える
3 新ASTから新PHPソースコードへパース


*インストール
パースしたいPHPソースコードのバージョンによって、PHP-Parserのバージョンを選択してください。

・PHP-Parser 4.x
動作環境: PHP7.0以上で動く
サポート範囲: PHP5.2~8.3( PHP5.xの解析を完全にサポート)
https://github.com/nikic/PHP-Parser/blob/4.x/doc/0_Introduction.markdown

・PHP-Parser 5.x
動作環境:PHP7.4以上で動く
サポート範囲:PHP7~8( PHP5.xの解析も部分的にサポート)
https://github.com/nikic/PHP-Parser/blob/master/doc/0_Introduction.markdown

コマンド

1 PHPソースコードからASTへパース
PHPソースコードを取得し、パーサーを用いてASTへパースを行います。

・入力用PHPソースコード (inputSampleData.php)

・パース処理(changePhp.php)

・実行

・実行結果(変換されたAST)
コストコのレシートくらい長いですので、ざっと目を通していただければ十分です。「Stmt_Classってやつはクラス定義のことなんだろうな」「Expr_Array_って書いてあるから配列のなんだろうな」ぐらいでOKです。

ノードを用いて変更箇所を指定して、追加・変更・削除などの変更処理を行います。それぞれのノードクラス定義に関する詳細は、nikic/php-parserの公式ドキュメント(5.x版) または vendor/nikic/php-parser/lib/PhpParser/Node/ ディレクトリ配下から探してください。(どのクラスなのか名前で推測できるものが多いと思います)

2 ASTへ変更を加える
今回の本命である、パースされたASTに対して変更を行います。変更を行うにNodeVisitorクラスを基に任意の操作を行うクラスを作成し、「どのタイミングでどの操作を」定義します。

・変更処理セットして実行(changePhp.php 続き)

・PHPの厳密な型判定を設定する記述を追加するNodeVisitor(StrictModeRevisingVisitor.php)

・配列をarray()から[]に統一するNodeVisitor(ArrayKindRevisingVisitor.php)

これで変更を加えることができました。

3  新ASTから新PHPソースコードへパース

・changePhp.php 続き

・出力ファイル(outputSampleData.php)

変更できました。ただこの記述だとパース時に空欄削除などのフォーマットが行われます。

元のソースコードの書式を保ったままパースする記述を載せておきます。

・変更処理セットして実行(changePhpNoFormat.php)

・実行結果

入力したPHPソースコードと同じく、クラスメソッドが値を返す処理の前に空欄行が残したままにできていますね!

〇おわりに
プログラミングやITの知識だけではなく、「必要な情報を得る力」は本当に重要ですね。特に、ネットの記事で親切丁寧に紹介されていない知識を得たい場合や古いドキュメントから使用を知りたい場合などだと、自分の「必要な情報を得る力」のなさを痛感することが多くあります…
今後AIがより身近になってきて仕事上でも活用しそうだからこそ、検索の仕方や質問の仕方をより一層鍛えていきます!

・参考にさせていただいた記事
https://www.komtaki.com/posts/php-meta-programming-introduction
https://qiita.com/ktplato/items/23ad2893d741bfb564f9meta-programming-introduction

https://qiita.com/ktplato/items/23ad2893d741bfb564f9