Mozo.PHP (その3)

今日の最初の方にリリースしたものはメモリリークします。
本気で使っている方はここから最新版を再度落としてください。

エラー処理 (1)

C++ の関数からエラーを出して呼び出し元に復帰したいときは、mozo::php::runtime_error を throw します。

throw php::runtime_error("something went wrong!");

これだけです。

エラー処理 (2) -- MOZO_PHP_BEGIN_CAPTURE_ERROR / MOZO_PHP_END_CAPTURE_ERROR / MOZO_PHP_LAST_ERROR

mozo::php::function で PHP の関数を C++ から呼び出したときに、track_errors=1 みたく関数のエラーメッセージをキャプチャしたい場合があります。このとき便利なのが MOZO_PHP_BEGIN_CAPTURE_ERROR、 MOZO_PHP_END_CAPTURE_ERROR と MOZO_PHP_LAST_ERROR というマクロです。

まずは、エラーをキャプチャしたいステートメントを MOZO_PHP_BEGIN_CAPTURE_ERROR と MOZO_PHP_END_CAPTURE_ERROR で囲みます。そのブロック内でエラーメッセージが出力されたら、mozo::php::error_info クラスの参照である MOZO_PHP_LAST_ERROR から、エラーの発生したファイル名、行番号とエラーメッセージの情報を取得することができます。

もしエラーメッセージが一切出力されなかった場合に MOZO_PHP_LAST_ERROR を参照すると、すべての要素が空となっています。エラーメッセージが実際に出力されたかどうかは。mozo::php::error_info::valid() メソッドの戻り値で確認できます。

MOZO_PHP_BEGIN_CAPTURE_ERROR
    // php_function がエラーを返した
    if (*php_function() == false) {
        const php::error_info& error(MOZO_PHP_LAST_ERROR);

        std::cout << error.filename() << std::endl;
        std::cout << error.line_number() << std::endl;
        std::cout << error.message() << std::endl;
    }
MOZO_PHP_END_CAPTURE_ERROR

以下に、file_get_contents() を実装した例を示します。

#include "mozo/php/module.hpp"
#include "mozo/php/function.hpp"

using namespace mozo;

class m006_module
    : public php::module,
      public php::function_container<m006_module> {
public:
    class handler
        : public php::module::handler {
    public:
        handler(m006_module* mod)
            :php::module::handler(mod) {}

        static php::value_ptr
        my_file_get_contents(::std::string filename) {
            php::function fopen("fopen"),
                          fread("fread"),
                          filesize("filesize"),
                          fclose("fclose");
            php::value_ptr $fp, $retval;

            MOZO_PHP_BEGIN_CAPTURE_ERROR
                $fp = fopen(filename, "rb");
                if (*$fp == false) {
                    throw php::runtime_error(
                        std::string("Failure in system function: \"")
                        + MOZO_PHP_LAST_ERROR.message() + "\"",
                        MOZO_PHP_LAST_ERROR.filename().c_str(),
                        MOZO_PHP_LAST_ERROR.line_number());
                }
            MOZO_PHP_END_CAPTURE_ERROR

            try {
                MOZO_PHP_BEGIN_CAPTURE_ERROR
                    $retval = fread($fp, filesize(filename));
                    if (*$retval == false) {
                        throw php::runtime_error(
                            std::string("Failure in system function: \"")
                            + MOZO_PHP_LAST_ERROR.message() + "\"",
                            MOZO_PHP_LAST_ERROR.filename().c_str(),
                            MOZO_PHP_LAST_ERROR.line_number());
                    }
                MOZO_PHP_END_CAPTURE_ERROR
            } catch (const ::std::exception&) {
                fclose($fp);
                throw;
            }            

            // finally がほしい...
            fclose($fp);

            return $retval;
        }
    };

public:
    m006_module(zend_module_entry* entry)
        : php::module(entry) {
        entry->functions =
             defun("my_file_get_contents", &handler::my_file_get_contents);
    }
};

#define MOZO_PHP_MODULE_NAME m006
#define MOZO_PHP_MODULE_CAPITALIZED_NAME M006
#define MOZO_PHP_MODULE_VERSION "0.1"
#define MOZO_PHP_MODULE_CLASS_NAME m006_module

#include "mozo/php/module_def.hpp"