PHP Note
category: デバッグ デバッグの第一歩 正規表現は怖くない

今日のエラーメッセージ

created: 2008-04-27 | modified: 2009-02-11

PHPは問題があればエラーメッセージを表示します。慣れないうちは頭を抱えることもあるでしょうが、その意味さえ知っていれば問題個所の特定にこれほど役立つ情報はありません。
よくお目にかかるエラーメッセージと原因&対処方法をまとめましたので、エラーメッセージを味方につけてしまいましょう。出会ったメッセージから原因を判断してますので誤解があるかもしれません。

2008-05-10 このページの最後に、エラーメッセージの表示・非表示の設定について追記しました。開発(デバッグ)時と運用時は上手く使い分けてください。


エラーメッセージの構成

PHPが出すエラーメッセージは大きく分けて3つの要素で構成されています。

 Parse error: syntax error, unexpected ';' in /***/***.php on line ***
|エラーレベル|        メッセージ            |   ファイルのパスと行番号  |

はじめが「エラーレベル」で、構文が違ってるとか実行不可能な致命的なものであるといった、エラーの性質(重要度)をあらわしています。

2つ目は、問題の要素やエラーの性質を補足する「メッセージ」です。ここはエラーを発生させたモジュール名やPHPのトークンが含まれていて原因の特定に大いに役立ちます。

最後がエラーの発生個所を示す「ファイルのパスと行番号」です。あくまでもエラーを発生させた位置を示していて、問題個所とは限りません。大抵はこれ以前に原因があると念頭においておきましょう。



Parse error:

PHPの構文から外れスクリプトとして成り立っていないことを示すエラーです。構文が間違っているのでこれが出ている限りスクリプトが実行されることはありません。

対応するカッコや " が無い、変数頭の "$" や文末の ";" 忘れ、演算子の評価順序違いなど、ほとんどが単純な記述ミスや勘違いによるものです。


Parse error: syntax error, unexpected ';' in /***/***.php on line ***
構文エラー, 予想外のところで ";" が出現した」となりますが、この場合「予想外のところで文末が出現した」ということで、本来あるべき演算子や値が無い(又は多い)まま文末に至ってしまったことを表しています。
カッコの閉じ忘れがないか、不要な演算子がないかなど確かめましょう。行番号と問題個所がずっと離れていることもあります。
Parse error: syntax error, unexpected T_STRING in /***/***.php on line ***
構文エラー, 予想外のところで T_STRING が出現した」ということで、T_STRING は「文字列」を示すPHPのトークンです。文字列を囲む 「'」 や 「"」 が対になってないのが原因です。閉じ忘れが無いか、文字列に含まれる 「'」 や 「"」 を、 「\'」 や 「\"」 にエスケープしてあるか確かめましょう。


Fatal error:

実行時に出るエラーで、これ以降の処理が実行できないほどの「致命的エラー」を表しています。このエラーが発生するとそこで強制終了します。


Fatal error: Call to undefined function mb_convert_encording() in /***/***.php on line ***
未定義の関数が呼ばれた」ということで、ここでは mb_convert_encording関数が定義されてないってことで、mb_convert_encording関数を含むライブラリがあらかじめ読み込まれてないのが原因です。
関数がPHPの組み込み関数なら該当の拡張モジュールが組み込まれてないので、マニュアルに基づいて組み込みましょう。ちなみに Windowsマシンへの mb_string(マルチバイト)モジュールの組み込みなら Apache2 + PHP5 の初期設定 に書いてます。レンタルサーバなら一度サーバ屋さんに相談してみましょう。
関数がユーザ定義関数なら、include(またはrequire)であらかじめライブラリを読み込みましょう。
もしライブラリが読み込まれているにも関わらずこのエラーが出るなら、綴りの間違いってこともあります。
良く未定義だと怒られる関数にはこんなのがあります。「mb_languagemb_convert_kana」:何れもmb_convert_encordingと同じマルチバイトライブラリの関数。「mysql_connect」:PHP5ではMysqlクライアントがデフォルトで組み込まれてません。
Fatal error: Cannot instantiate non-existent class: smarty in /***/***.php on line ***
存在しないクラスはインスタンス化できない」ってこと。この場合は "smarty" クラスが定義されてない、つまり"smarty"ライブラリがあらかじめ読み込まれてないのが原因です。
該当のクラスが "smarty" のようなPHPスクリプトのライブラリなら、include(またはrequire)であらかじめライブラリを読み込みましょう。
該当のクラスが拡張モジュールで提供されているものならマニュアルに基づいて組み込みましょう。
もしライブラリが読み込まれているにも関わらずこのエラーが出るなら、綴りの間違いってこともあります。
Fatal error: Multiple access type modifiers are not allowed in /***/***.php on line ***
アクセス修飾子を複数指定することは出来ない。PHP5 では、クラスの変数やメソッドに private, protected, public を付けてアクセス権を指定しますが、これを2つ以上指定しているのが原因です。
指定できるアクセス権はどれかひとつだけなので該当個所を見直しましょう。


Warning:

実行時に出るエラーで、以降の処理に影響を与える可能性がある「警告」です。Warning が発生しても処理は続けられますが、これ以降も関連してエラーが出る可能性が高いので、いくつも発生する場合は初めの Warning から解決すべきです。

Warning: mysql_fetch_array(): supplied argument is not a valid MySQL result resource in /***/***.php on line ***
mysql_fetch_array に 与えられた引数は有効なMySQLの結果リソースではない ってことです。mysql_fetch_array は mysql_query から得た結果リソースを引数にとりますが、これが「結果リソース」ではない、つまり mysql_query で問題が発生し false を返しているので、mysql_fetch_array で Warning が発生しているわけです。
MySQLサーバで発生したエラーは PHPでは表示されません。mysql_error関数で獲得できますので、mysql_query の直後に 「echo mysql_error();」を挿入してエラーの内容を確かめましょう。大抵は mysql_query関数に与えた SQLの構文違いです。SQL文を表示させて想定したSQL文になっているか確かめましょう。
Warning: fopen(http://***/***.log): failed to open stream: HTTP wrapper does not support writeable connections. in /***/***.php on line ***
fopen で ストリームのオープンに失敗:HTTP wrapper は書き込みによる接続を未サポートです。 となります。fopen の引数が 「http://」になっていますが、HTTP経由でファイルの書き込みはできません。サーバ内のファイルに書き込むなら、URLではなくファイルのパスを指定します。
Warning: fopen(//***/***.log): failed to open stream: Invalid argument in /***/***.php on line ***
fopen で ストリームのオープンに失敗:引数が無効です。 となります。ネットワーク上のファイルを開こうとして失敗していますのでコンピュータのアクセス権が原因です。対象のファイルやディレクトリに、ウェブサーバで読める権限(属性)を設定します。セキュリティソフトでブロックされているかもしれません。
Warning: fopen(/***/***.log): failed to open stream: No such file or directory in /***/***.php on line ***
fopen で ストリームのオープンに失敗:ファイル又はディレクトリではない。 って、要は引数で指定したファイルが存在しないのです。メッセージの (/***/***.log) が実際に引数として与えたパスですので、もし存在するはずなら、綴りが間違ってないかよーく見てみましょう。
Warning: Invalid argument supplied for foreach() in /***/***.php on line ***
間違った型の引数が与えられた とのこと。foreach では配列を引数に取るところ、配列でないものを与えているのが原因です。foreach の直前に var_dump() を挿入して引数が想定された配列か確かめましょう。
Warning: Wrong parameter count for array_key_exists() in /***/***.php on line ***
パラメータの数が違うよ とのこと。引数を2つ取る関数に対して、ひとつしか指定しなかったり、3つ指定したりするとこのようにおこられます。今一度マニュアルを見て関数の引数を確かめてください。
Warning: Cannot modify header information - headers already sent by (output started at /***/***.php:***) in /***/***.php on line ***
すでに送信しているので header を修正できない ってメッセージです。echo とか print など何か出力した後で header を送信しようとするとこう怒られます。header はあらゆる出力より先に送信しなくてはならないのがHTTPのお約束ですので出力順序を見直しましょう。たまにハマるのが PHPタグ(<?php ?>)の外のスペース。スクリプトの始めや最後に余分な改行やスペースはありませんか。改行やスペースも出力のうちです。
Warning: Division by zero in /***/***.php on line ***
ゼロ除算エラーです。「ゼロで割る」と結果が出ない演算を無限に繰り返すことになるので、プログラミングで絶対やってはならないことのひとつとされています。割り算の分母の値を確かめて見ましょう。


Notice:

プログラムの実行には影響無いけれど、これはこれでいいのかな?程度の PHP からの「通知」です。Notice が発生しても処理は続けられます。ただ、あらかじめ Notice を想定した対策が施してあればかまいませんが、バグの原因になったりセキュリティホールを生む可能性もありますので、Notice も ゼロになるようコードを書くべきです。

PHPのデフォルトのままだと error_reporting = E_ALL & ~E_NOTICE (Notice以外のエラーを表示)が設定されていて Notice が表示されることはありません。php.ini か error_reporting関数で E_ALL を指定してすると全てのエラーメッセージが表示されますので、開発環境や製作段階ではすべて表示するよう設定しましょう。

Notice: Undefined variable: email in /***/***.php on line ***
未定義の変数: $email。PHPでは変数の宣言は必要ありませんが、初期化もせずに参照しようとするとこう怒られます。Notice なので設計上問題がなければOK!と言うわけにはいきません。プログラムの中で初期化してない変数を参照しているとセキュリティホールになる場合があります。register_globals=On の環境だと、$email を外部から簡単に初期化出来てしまいます。宣言ナシでも初期は必ずしましょう。
Notice: Undefined variable: $_SESSION in /***/***.php on line ***
未定義の変数: $_SESSION。エラーの意味は上と同じですが、スーパーグローバル変数の $_SESSION の場合、あらかじめ session_start() をせずに $_SESSION を参照しようとしてるか、セッションスタートに失敗している可能性があります。
session_start() をコールした際PHPはクッキーを送信しますが、session_start() 以前に1文字でも出力しているとクッキーが送信できずに失敗します。session_start() を出力処理(echo や print等)の前に行うか、ob_start() でバッファリングを有効にしてください。
それでもという時、見落としがちなのがPHPスクリプトの初めと終わりのタグ(<?php ~ ?>)の外。この前後に不要な改行やスペースが無いか、もしあれば削除しましょう。また。PHPスクリプトの文字エンコーディングが UTF-8 のBOM付きだと、真っ先にBOMの3バイトが出力されてしまうので必ずBOM無しで保存します。
Notice: Undefined index: email in /***/***.php on line ***
未定義のインデックス: email。Undefined variable と似てますがこちらは配列の添え字(ここでは "email")が未定義ということです。これもあらかじめ初期化するか、参照前に isset か array_key_exists で値や添え字の有無を確かめてから用いましょう。

また、気付いた時に書き足していきます。



エラーメッセージの表示と非表示

ファイル名やモジュール名などが含まれるエラーメッセージは一般ユーザに見せて良い物ではありません。表示されたメッセージから用いてるアプリケーションを推測しセキュルティホールをつくといいたことを可能にしてしますので、開発(デバッグ)時以外は非表示にしておくべきです。エラーメッセージの表示と非表示を制御するには display_errorserror_reporting の2つを組み合わせて設定します。

display_errors

On(表示)と Off(非表示)のいずれかを設定します。デフォルトは On でメッセージが表示されます。これを Off にするとメッセージは一切表示されませんので、運用時は Off に設定しましょう。

display_errors の設定可能な場所は PHP_INI_ALL なので、php.ini の他 .htaccess やスクリプトからでも設定できます。但し Fatal error が出るとスクリプトの実行が途中で止まるのでスクリプトから指定しても意味がありません。実際のところ php.ini.htaccess どちらかで設定するものだと考えてかまいません。

レンタルサーバだと .htaccess を用いるのが実用的だと思いますので次のように記述します。ドキュメントルートの .htaccess には Off を設定しておき、デバッグ中のディレクトリの .htaccess だけ On にするといった具合に使い分けると良いと思います。

.htaccess

#
# PHP Directive
#
#php_flag display_errors On
php_flag display_errors Off

.htaccess でPHPの設定値を指定する場合には記述方法が2つあります。display_errors のように On 又は Off 、0 又は 1 のような2者択一の値を指定する場合は、php_flag を用いて 「php_flag 設定の名称 On/Off」 のように記述します。

また文字列などの値を指定するものについては、php_value を用いて 「php_value 設定の名称 値」 のように記述します。

PHP5.2.4以降 display_errors はエラーの出力先として 'stderr' を指定できるようになりました。もし 'stderr' を指定したい時には 「php_value display_errors stderr」 と書く必要があります。



error_reporting

表示するエラーのレベルを設定します。デフォルトでは E_ALL & ~E_NOTICE が設定してあり、Notice 以外のエラーメッセージが表示されます。

開発(デバッグ)中など全てのレベルのエラーメッセージを表示するなら、E_ALL を、一切表示しない時には 0 を指定します。

PHP5からは、コードの互換性などのメッセージを表示する値 E_STRICT が増えましたが、E_ALL に含まれてないので、これも含め全て表示させるには E_ALL | E_STRICT を指定する必要があります。

PHP5.2.6では E_STRICTE_ALL に含まれています。

error_reporting の設定可能な場所は PHP_INI_ALL なので、php.ini の他 .htaccess やスクリプトからでも設定できます。表示と非表示を制御するだけなら display_errors だけで事は足りますので、特定のスクリプトだけメッセージを表示させたい時に error_reporting関数を用いる使い方が有効です。

<?php
/**
 * Error reporting level
 */
//error_reporting(E_ALL);   // デバッグ時
error_reporting(0);   // 運用時
 

いずれも php.ini.htaccess、スクリプトの3箇所で指定出来ますが、後から指定した値で元の値が上書きされますので最も優先順位が高いのはスクリプトで指定した値となります。
php.ini.htaccess < スクリプト


error_reporting に指定する値の型は「整数」で、E_ALL や E_NOTICE はそれぞれの整数値をわかりやすく定数にしてあるだけです。.htaccesserror_reporting を設定する時は PHPの定数である E_ALL や E_NOTICE は使えませんので、それぞれの数値(マニュアル参照)を 「php_value error_reporting 8191」 のように指定します。



@ エラー演算子

関数や変数の前に 「@(エラー演算子)」 をつけるとエラーメッセージが出力されなくなります。

/**
 * fopenのエラー出力を@でストップ
 */
$file = 'access.log';
$fp = @fopen($file, 'r');

エラー出力を抑えるにはとても簡単な方法なので何にでも @ をつけたくなるかもしれませんが、エラーを抑制の為だけに @ を使うべきではありません。問題個所が発見しにくく「保守しづらいスクリプト」になるので使いどころは良く考えましょう。

fopen なら ファイルが存在するか読み込み可能かなど、あらかじめエラーの原因になりそうな事は想定できますのでそれを踏まえたコードを書きましょう。@ は結構負荷があるようですし。(@演算子のパフォーマンス

/**
 * fopenの前に検証
 */
$file = 'access.log';
if (!is_file($file) || !is_readable($file)) {
    exit();
    // 実際はここでファイルが読めない時のコード
}
$fp = fopen($file, 'r');



category: デバッグ デバッグの第一歩 正規表現は怖くない
PHP Note
ページの一番上へ
Googleグックマークに登録 Yahooグックマークに登録 livedoorクリップに登録 @niftyクリップに登録 はてなブックマークに登録 deliciousに登録 Buzzurlに登録 FC2ブックマークに登録
最近更新したNote
よく読まれている記事
Yahoo Search

最近更新された掲示板トピックス
PHPマニュアル
今日のブックマーク
PHPマニュアル関数検索
関数名を入力し検索ボタンをクリック↑