おぎろぐはてブロ

なんだかんだエンジニアになって10年以上

php_error / エラー出力関数たち

PHP Extensionで、使うPHP Coreのエラー出力関数について。いっぱいあって迷うのだけれど、整理。

種類と利用されてる数

5.1.4でext下のモジュールでの利用数は以下のとおり。

  43 php_error
2328 php_error_docref
  12 php_error_docref1
  16 php_error_docref2
  44 zend_error
   4 php_verror
それぞれの概要

出力先は、iniファイルで指定したerror_log、指定が無ければ、SAPI側のlog_messageになります。(sapi_module_structのsapi_error)

  • php_error
    • マクロで実体はzend_errorだけれど、zend_errorを直接使わずに、こっち使いましょう
  • php_error_docref[012]*
    • 関数内だと関数名や、ドキュメントへのリンクをつけたりしてくれる便利版
  • zend_error
    • ユーザ定義エラーハンドラ(PHPスクリプトレベル)へのコールバック
    • zend_error_cbに設定された、エラーハンドラへのコールバック (php_error_cbを呼んでる)
      • php_error_cbで、エラーレベルを付けたり、ファイル名、行数などを付加している
  • php_verror
    • php_error_docref[012]*から最終的に呼び出される関数。php_error_docrefの方で事足りるなら呼ばないほうがいいかも
    • で、文字列をフォーマットして、php_error()に渡します

数を見ても分かるように、php_error_docref()を使うことが多いです。

php_error_docref()

/* php_error_docrefはphp_error_docref0のエイリアス */
PHPAPI void php_error_docref(const char *docref TSRMLS_DC, int type,
                             const char *format, ...)
PHPAPI void php_error_docref1(const char *docref TSRMLS_DC, 
                              const char *param1, 
                              int type, const char *format, ...)
PHPAPI void php_error_docref2(const char *docref TSRMLS_DC,
                              const char *param1, const char *param2, 
                              int type, const char *format, ...)
引数
  • const char *docref: ドキュメントの場所
  • int type: エラーレベル (E_NOTICE, E_WARNING, E_ERRORとか)
  • const char *param1, *param2: 追加表示のパラメータ
  • const char *format, ... : sprintfする文字列
使い方を、CODING_STANDARDSから。

コードの実行中にエラーや警告を報告する際は、php_error_docref()の関数群を利用しましょう。
php_error_docref()は、以下のように関数名を自動で付加します。

php_error(E_WHATEVER, "%s(): Desc.", get_active_function_name(TSRMLS_C));

php_error_docref()の1つ目の引数(const char *docref)は、NULLもしくはエラーの詳細が説明されてあるページのURLを指定します。
実行中の関数から、自動でURLを生成するのであれば、NULLを渡します。

php_error_docref(NULL TSRMLS_CC, E_WHATEVER, "Desc.");

実際、php_error_docrefを呼んでいる殆どは、docrefにNULLを渡しています。
php.netにリファレンスがある関数でないと、リンクが存在しない方に向いてしまうので、リファレンスを明示したいのであれば、フルURLをdocrefに積むようにするべきでしょう。とはいえ、出てくるURLをクリックしたことないですね。。
URLを指定する際は、http://から開始してください

php_error_docref("http://externalsite.tld/page.ext#error"
                     TSRMLS_CC, E_WHATEVER, "Desc.");

もしくは、拡張子無しのマニュアルページの名前を指定することもできます。(オプションで#から始まるターゲットアンカーを指定できます)
名前を省略し、docrefを#から始まる文字列(#hoge)にすると、NULLの場合と同じくURLが生成され、それにアンカーとして#hogeが付加されます。

php_error_docref("function.ext-func#error" TSRMLS_CC, E_WHATEVER, "Desc.");
php_error_docref("#error" TSRMLS_CC, E_WHATEVER, "Desc.");

名前は、_ を - に変換したものとなることに注意してください。ext_func()だと、ext-funcとなります。

php_error_docref1(), php_error_docref2()

もし、関数名のあとに重要なパラメータを付けたいときは、php_error_docref1(), php_error_docref2()を利用します。
例として、ファイル関数が開いたファイル名を表示する場合。

php_error_docref1("function.fopen" TSRMLS_CC, filename,
                  E_WHATEVER, "Desc.");

php_error_docref2("function.fopen" TSRMLS_CC, filename, openmode,
                  E_WHATEVER, "Desc.");

"関数名(param1,param2) format" と展開されます。

spprintf(), vspprintf()

typoじゃないです。php_verrorの中で使われたりする、snprintf() / vsnprintf() に似た関数です。
非持続的なメモリを中で確保してくれます。

int spprintf( char **pbuf, size_t max_len, const char *format, ...);
int vspprintf(char **pbuf, size_t max_len, const char *format, va_list ap);

例。

char *buf = NULL;
spprintf(&buf, 0, "function.%s", function);

/* 忘れずにefree */
if (buf) {
    efree(buf);
}