PHPにPHPを埋め込みさせる
この記事は,OIT Advent Calendar 2018の2日目の記事です。
また,本記事は雑に書いてます.自己責任で読んでください.
何をしたいか
下記のようなPHPファイルがあるとします.
<?php $hoge = "Hello World!";
それに"代入演算子がある次の行"になにか任意の処理を埋め込みたい.
<?php $hoge = "Hello World!"; # ここに下のコードを追加したい echo("ここに代入演算子があったぞおおおおお!!");
もちろん手で書き換えるのもいいが,3000近くのファイルに対し同様のことを行いたいと考えると厳しい.
解決策
PHP-Parserというライブラリを利用する.
(composerで導入するので,composerの知識はちょっと必要)
このライブラリはPHPのソースコードを構文木の状態にして解析しやすいようにするものである.(多分)
構文木の状態にせずに直接ソースコードの状態でやろうとすると色々とダメなパターンが出てきて無駄に苦労する.
<?php // composerで管理してるライブラリをrequire(各自ファイルパスが違う気がする) require_once 'vendor/autoload.php'; use PhpParser\ParserFactory; use PhpParser\Node; use PhpParser\NodeTraverser; use PhpParser\NodeVisitorAbstract; $code = <<<'CODE' <?php $hoge = "Hello World!"; CODE; // array_insert関数の中身は略してます. function array_insert(&$base_array, $insert_value, $position=null) {..} class MyNodeVisitor extends NodeVisitorAbstract { public function enterNode(Node $node) { for($i = 0 ; $i < count($node->stmts) ; $i++){ $value = $node->stmts[$i]; $valueType = $value->getType(); if($valueType == "Expr_Assign"){ // ここで自ら埋め込みたいソースコードの構文木を構築する $echoStr = new \PhpParser\Node\Scalar\String_("ここに代入演算子があったぞおおおおお!!") $echoArg = new \PhpParser\Node\Arg($echoStr); $echoArgs = array($echoArg); $echoName = new \PhpParser\Node\Name("echo"); $echoFunc = new \PhpParser\Node\Expr\FuncCall($echoName, $echoArgs); $i++; // array_insert関数は自作関数 array_insert($node->stmts, $echoFunc, $i); } } } } $parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP5); $traverser = new NodeTraverser; $prettyPrinter = new PhpParser\PrettyPrinter\Standard; $traverser->addVisitor(new MyNodeVisitor); // parse $stmts = $parser->parse($code); // traverse $stmts = $traverser->traverse($stmts); // pretty print $code = $prettyPrinter->prettyPrintFile($stmts); echo($code);
array_insert関数は以下のサイトから持ってきた.
構文木構築パートは,事前に構築したいソースコードの構文木がどうなるか確認しておく必要がある.
構文木は$parser->parse($code)
した後の$stmts
変数をvar_dump()
あたりで出力すると確認できる.
その後は頑張って構文木パズルをするんだよ.
なんでこんなことをしてたか
埋め込みたい気分になった.(詳細は省く)
PHP-Parserの情報が少なくて結局ライブラリのコードを読んでこの方法に辿り着いた.
多分正しい使い方ではなさそうなので自己責任で.(これしたい人が存在するかわからないけど)