PHPの入り口ファイル(bootstrap point)の調べ方

JUGEMテーマ:PHP

巨大なレガシーWebサービスを扱っていると、一体どれが最初にアクセスされるPHPファイルなのかよく分からず、色々と困ったことにぶつかります。
そういった問題を解決するために入り口ファイルを特定するにあたり、僕が使った方法をまとめてみます。

入り口ファイルが分からない時の問題点

まずこの問題によって困るポイントをまとめてみますと、以下の項目が思いつきます。

  • 全処理で必ず最初に実行してほしい処理をどこに入れればいいのか分からない

  • 入り口ファイルごとにバラバラな初期化処理をしているので、ビジネスロジック部分でどの初期化処理がされているのを期待していいのか分からない

  • Webアプリの公開/非公開ファイルの線引きが出来ない(入り口ファイルは公開ファイルでなければならない)

  • アプリケーションでルーティングするように修正したくても、ルーティング先のファイル(現入り口ファイル)が分からなくて出来ない

  • (初期化処理がバラバラであることに対する問題点だけど・・・)自動テスト実行時にアプリの初期化処理と同じ初期化処理しないと正しくテストできないけど、入り口ごとにバラバラなので合わせることができない

ビジネスロジック部分でどの初期化処理がされているのを期待していいのか分からない、というのが特に開発効率に影響を与えていると僕は思っています。
一般的なWebフレームワークは1個の公開ファイル内でルーティング・初期化処理を行っているので、「どの部分でも確実にこの処理が実行されている」という期待をしても良いのですが、入り口がバラバラだとそれが出来ないので、常にどこからアクセスされたのか?を意識しなくてはなりません。

入り口ファイルを特定する方法を考える

上記問題を解決するために入り口ファイルを特定を始めたのですが、ひたすらソースやアクセスログを追うのはさすがに無理なので、もっと手っ取り早い方法がないか探していました。

そこで見つけたのが  $_SERVER['SCRIPT_NAME']  で、公式サイトによれば現在のスクリプトのパスがこの変数に入っています。
現在のスクリプトのパス、というのはカレントのファイル(= __FILE__ )という意味ではなく、 HTTPリクエストによって呼び出されたファイルパス=最初に実行されたPHPファイル が入っています。
$_SERVER ってApacheやPHP-FPMなどのWebサーバから提供される情報だと思っていたのですが、$_SERVER['SCRIPT_NAME'] はどうやらクーロンなどCLIからの実行でもきちんと値が入っていて、常に取得できます。
これをトランザクション毎にログ収集すれば全ての入り口ファイルを特定することが出来ると考えました。

$_SERVER['SCRIPT_NAME']をログ収集する

ログ収集するにもどうやってログに吐き出すか?という問題があるのですが、それにはphp.iniのauto_prepend_fileregister_shutdown_functionの合わせ技を採用しました。

auto_prepend_fileはメインファイルの実行前に自動的に呼び出すファイルを指定するディレクティブです。PHP実行時に必ず呼び出されるので、全てのトランザクションでログ収集することが可能になります。ですがここでログ出力すると 何らか想定しない理由でエラーが発生すると、全てのトランザクションで500 Internal Server Errorを返してしまいます。 全アクセスがエラーになって、仮にすぐ復旧できたとしてもWebサービスとしては致命的です。

そこでauto_prepend_fileではログ出力せずに、 register_shutdown_function を呼ぶだけにしました。 register_shutdown_function はPHP終了時に自動的に呼び出すfunctionを登録するメソッドで、ここにログ出力用の自作functionを登録しました。 register_shutdown_function で実行された処理は、仮にそこでエラーが発生したとしても、既にレスポンスを返していればユーザのもとには画面が普通に表示されます。
ここらへんの挙動の原理はよく分かっていません・・・。
ですが意図的にエラーを発生させてもブラウザ上は問題なく表示できているので、Webサービス的にはほぼノーリスクで入り口ファイルのログ収集を行うことが出来ました。

「PHP実行後にログ出力したいならauto_append_fileで良くね?」って話もあったんですが、こっちは exit() でPHPが終了する場合には呼び出されないので採用しませんでした。 register_shutdown_function exit() で終了する場合も呼び出されます。

登場人物が多くなったので雑にまとめると・・・

  • auto_prepend_file : PHP実行前に呼び出されるファイル指定。ここでエラー起こしちゃうと全トランザクションでエラーになっちゃうからヤバイ。

  • register_shutdown_function : PHP実行後に呼び出されるfunctionを登録する。そのfunctionでエラーが起こってもユーザ的には問題なし。

  • auto_append_file : PHP実行後に呼び出されるファイル指定。 exit() でPHP終了されると呼び出されない。

そんなこんなで入り口ファイルが特定できるようになった

上記の対応をして無事に入り口ファイルのログを集められるようになりました。

ただそもそもの話、最初の問題点で挙がっていた共通の初期化処理したいなら、php.iniのauto_prepend_file使うだけでいいんじゃない?
という考え方もあるのですが、それだとphp.iniを見ないと挙動の分からないアプリケーションが完成してしまいます。
アプリの挙動はリポジトリを見るだけで把握できるのが理想だと思うので、特殊な理由が無い限りはなるべく使いたくありません。
今回のログ収集で使ってしまいましたが、1カ月もすればとりあえず全ての入り口ファイルが特定できる情報が集められるので、用が済んだら削除します。

スポンサーサイト

コメント