[PHP]require(require_once)するときは必ず絶対パスを使いましょう


「いやいやrequireは絶対パスで書くだろ、常識的に」

PHPの神様もこう言ってるわけですし、require(require_once,include,include_onceも同じ)するときは必ず絶対パスを使いましょう。

なぜrequireは絶対パスで書くべきなのか?

まず絶対パスで書いた場合、
require_once '/path/to/a.php';
当たり前ですが'/path/to/a.php'というファイルがダイレクトにオープンされて読み込まれます。
何の問題ないですね。

一方、絶対じゃないパス名(ファイル名だけとか)を書いたらどうなるでしょうか?
require_once 'PEAR.php'
この場合、PHP実行エンジンはinclude_pathで指定されたディレクトリを順番に探索します。
もしinclude_pathが'/home/dqn/app:/home/dqn/extlib:/home/dqn/lib:/usr/share/pear'となっている場合、 PHP実行エンジンは下記のようにファイルを探索します。
/home/dqn/app/PEAR.php     あるかな・・・ないぉ!
/home/dqn/extlib/PEAR.php  あるかな・・・ないぉ!
/home/dqn/lib/PEAR.php     あるかな・・・ないぉ!
/usr/share/pear/PEAR.php   あるかな・・・あった!
しかもこの探索処理はHTTPリクエストが来るたびに行われてしまうので、require_onceがたくさん書かれているウェブアプリケーションでは無視できないコストになります。
(オートローダ使えという話もありますが、今回はあくまでrequireの仕組みについての解説です。)

実際に挙動を確かめてみる

straceコマンドを使えばこの挙動を実際に見ることができます。

まず実験用のPHPスクリプトを用意します。
hello.php
<?php
ini_set('include_path', '/home/dqn/app:/home/dqn/extlib:/home/dqn/lib:/usr/share/pear');

require_once 'PEAR.php';

echo "hello\n";
次にApache設定ファイルを編集して、子プロセスの数(MaxClients)を1に制限します。
/etc/httpd/conf/httpd.conf
# prefork MPM
<IfModule prefork.c>
[...]
MaxClients       1    ← コレ
[...]
</IfModule>
次に、straceでhttpd子プロセスを監視します。
sudo strace -p 12345
(12345のところは実際には子プロセスのプロセスIDです。)

ここでブラウザからhello.phpを叩くと、だーっとシステムコールのログが流れます。
途中よく見てみるとこのようなログが出ています。
lstat("/home/dqn/app/PEAR.php", 0x7fffd92a4a60) = -1 ENOENT (No such file or directory)
lstat("/home/dqn/extlib/PEAR.php", 0x7fffd92a4a60) = -1 ENOENT (No such file or directory)
lstat("/home/dqn/lib/PEAR.php", 0x7fffd92a4a60) = -1 ENOENT (No such file or directory)
lstat("/usr/share/pear/PEAR.php", {st_mode=S_IFREG|0644, st_size=33913, ...}) = 0
PHP実行エンジンが、カーネルに対してlstatシステムコールを発行して、空振りに終わった(No such file or directory)様子が見てとれます。
無駄なことをしているのがよくわかりますね。

自分のプロジェクトでやってみたら・・・

実際に、自分が昔作った大きめのPHPアプリでrequire_onceをしまくっている(150箇所も!)やつがあったので、絶対パスに書き換えて測定してみました。
ab -c 1 -n 200  http://localhost/myapp/index.php
改修前 (require_once 'lib/hoge.php' みたいな書き方してた )
Requests per second:    6.99 [#/sec] (mean)
Time per request:       142.977 [ms] (mean)
改修後 (require_onceを絶対パスで書き直した)
Requests per second:    22.70 [#/sec] (mean)
Time per request:       44.060 [ms] (mean)

すんげーーーーーーー!!!!!!!
3倍も速くなった!!!

まとめ

require/includeは絶対パスで書こう
straceはプログラマのともだち

(そしてオートローダ使えるなら使おう。)

補足

「絶対パスで書くと環境依存になる」という声がありましたが、絶対パスを相対的に指定すれば問題ありません。
require_once dirname(__FILE__) . '/lib/hoge.php';

参考

カテゴリ:

人気記事