PHPのrequire_once

PHPのrequire_once()は、このrequire_once文が書かれた箇所にファイルを読み込みます。ただし、1度だけなので、

<?php
require_once("a.php");
require_once("a.php");
?>

と記述した場合、2度目のrequire_once()ではa.phpは読み込まれません。例えば、a.phpではz.phpを読み込んでいて、b.phpでもz.phpを読み込んでいる場合、とあるスクリプトでa.phpとb.phpを読み込むと、z.phpを2度、間接的に読み込んでしまうような事態を防ぐ事ができます。
逆に何度でも読み込むのがrequire()で、これはC言語でいうところのincludeみたいなものです。

大抵の場合、同じファイルを2度読み込む理由はないからのだからと、何でもかんでもrequire_once()にしておけば安心だ、というもんでもありません。いや、自戒の意を込めて、以下は、つまらないバグに悩まされてしまった記録です。

例えば、
mysqlへアクセスするためのIDとパスワードの保存場所という記事。データベースの設定を外部ファイルにして、それをrequire_once()で読み込むという処理です。ここで、このDBへの接続部分を関数化してみます。

function connectDB()
{
    require_once('cfg.php');
    $dbc = mysql_connect($db['host'], $db['user'], $db['pass']);
    mysql_select_db($db['dbname']);
    return $dbc;
}

この関数を使ってDBに接続して、戻り値を使ってSQLを実行して、mysql_close()でDBへの接続を解除したとします。ここで処理が終わせずに、続けて再びSQLを実行するためにconnectDB()を呼び出すと・・・つまり、こんなソースです。

$id = connectDB();
mysql_query(...);
mysql_close($id);
$id = connectDB();
mysql_query(...);
mysql_close($id);

レコードの更新を実行したあと、画面を更新するためにデータを取り直すというような処理ですね。これがなぜ、動かないのかというと、2度目のconnectDB()では require_once()が実行されないのです。2度目の読み込みになりますから。なので、$db変数は定義されずにDBへの接続に失敗するわけです。

PHP的に考えたら当たり前なのですが、C言語のincludeようにプリプロセッサが外部スクリプトを展開して、その外部スクリプトはその箇所に恒久的に存在すると勘違いしてしまったのですね。つまり、関数の呼び出し毎にrequire_once()が実行されていると思っていなかったわけです。

そんなの、mysql_connect()した段階でエラーチェックしてりゃ気がつくだろ、と思うのですが、mysql_connect()のパラメータが指定されない場合、デフォ ルト値 server = ‘localhost:3306′, username = サーバプロセスの所有ユーザ名 password = 空のパスワードで接続するらしく、
Warning: mysql_connect() [function.mysql-connect]: Can’t connect to local MySQL server through socket ‘/tmp/mysql.sock’ (2)
という警告メッセージはですのですが、戻り値は得られてしまうのです。もっともその先のmysql_select_db()の段階でエラーになって、こちらは検出できていたので、あれあれなんかおかしい、と気がついたわけです。

ちなみにPHPではmysql_close()を呼び出さなくてもいいそうです、と、ドキュメントに書いてあります。が、ほったらかしは気持ち悪いよね。

コメントは受け付けていません。