実例で学ぶPHP拡張モジュールの作り方

第4回 WEBカメラから画像をキャプチャ(その2)

この記事を読むのに必要な時間:およそ 6 分

ソースコード解説

生成されたソースコードのうち,リソース定義に関わる箇所の解説をします。

php_cvcapture.hではリスト4のようなリソースを開放するマクロが定義されています。Z_LVAL_Pは変数の整数としての値(long型)にアクセスするマクロです。リソースの実体はハッシュテーブルEG(regular_list)に登録されており,変数にはそのキーが整数として格納されているのです。

リスト4 リソースを開放するマクロ(php_cvcapture.h)

#define FREE_RESOURCE(resource) zend_list_delete(Z_LVAL_P(resource))

CodeGen_PECLが生成するコードではZ_LVAL_Pを使っていますが,php/Zend/zend_operators.hではリソースIDにアクセスするマクロZ_RESVAL_Pも定義されています。今のところ両者は同じもので,実装方法からして仕様が変わる可能性も極めて低いと考えられますが,それでもZ_RESVAL_Pを使った方が無難だと思います。もっとも,リソースは通常の変数と同様に,unset()やリソースが作成されたスコープを抜けて不要になった時点でも破棄されるので,cvcaptureモジュールではリソースを破棄する関数を用意しておらず,このマクロも使っていません。

次にcvcapture.cの最初のあたりを見てください。リスト5のようなコードが生成されています。

リスト5 リソース識別番号とデストラクタ(cvcapture.c)

/* {{{ Resource destructors */
int le_cvcapture; /* リソース識別番号 */
void cvcapture_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
{
  /* “CvCapture”は<resource>タグのname属性値 */
  CvCapture * resource = (CvCapture *)(rsrc->ptr);

  do {
    /* ここがspecファイルの<destruct>タグに記述したコード */
    cvReleaseCapture(&resource);
  } while (0);
}

/* }}} */

cvcapture_dtor()はリソースを格納した変数が不要になったときに呼ばれるコールバック関数です。<resource>タグのalloc属性がyesの場合は,do~whileブロックの後にefree()で開放するコードも追加されます。

le_cvcaptureはリソースの種類を識別するのに使われるグローバル変数で,モジュール初期化関数内で初期化されます。このようにPHPではいくつかの重要な値をグローバル変数として持っているため,CLIを除くZTS(マルチスレッド)モードのSAPIではdl()関数が使えないのです。

リスト6 モジュール初期化関数(cvcapture.c)

/* {{{ PHP_MINIT_FUNCTION */
PHP_MINIT_FUNCTION(cvcapture)
{
  /* (定数定義は省略) */
  le_cvcapture = zend_register_list_destructors_ex(cvcapture_dtor,
                       NULL, "cvcapture", module_number);

  /* add your stuff here */

  return SUCCESS;
}
/* }}} */

関数を実装する

ここからはcvcapture.cに関数を実装していきます。前回作成した関数に変更はありませんので,そのままコピー&ペーストしてください。

リソースを返す関数

まずはcv_create_camera_capture()から実装していきましょう。リスト7はpecl-gen直後の状態です。

リスト7 cv_create_camera_capture()関数(cvcapture.c)

/* {{{ proto resource cvcapture cv_create_camera_capture([int index])
  Start capturing frames from camera. */
PHP_FUNCTION(cv_create_camera_capture)
{
  /* 戻り値のリソースを代入する変数の宣言 */
  /* 変数名は固定。型はプロトタイプが“resource cvcapture”,リソース定義が
     <resource name="cvcapture" payload="CvCapture"/>なら,“CvCapture *” */
  CvCapture * return_res;
  long return_res_id = -1;

  /* 引数が代入される変数の宣言 */
  long index = 0;

  /* 引数をパース */
  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &index) == FAILURE) {
    return;
  }

  /* 実装されていない旨のエラーを出力 */
  php_error(E_WARNING, "cv_create_camera_capture: not yet implemented"); RETURN_FALSE;

  /* 戻り値を実体のアドレスがreturn_res,タイプがle_cvcaptureのリソースにする */
  ZEND_REGISTER_RESOURCE(return_value, return_res, le_cvcapture);
}
/* }}} cv_create_camera_capture */

戻り値をリソースにするコードは生成されているので,あとは未実装エラーを出力している箇所をcv_camera_capture()と同じキャプチャを作成する処理で置き換えれば実装完了です。cv_camera_capture()の該当箇所との違いは変数名が⁠capture⁠から自動生成された変数名⁠return_res⁠になっている点だけです。

リスト8 キャプチャを作成

return_res = cvCreateCameraCapture((int)index);
if (return_res == NULL) {
  php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot create camera capture");
  efree(fullpath);
  RETURN_FALSE;
}

cv_create_file_capture()の実装も,自動生成されたコードの未実装エラーを出力している箇所をcv_file_capture()のキャプチャを作成する処理で置き換えるだけなので,省略します。

著者プロフィール

関山隆介(せきやまりゅうすけ)

ジンガジャパン株式会社に所属。PHP拡張モジュールを作った数なら(たぶん)日本一。PHP 5.3に対応したものはPEARチャンネルGitHubで公開中。

URLhttp://d.hatena.ne.jp/rsky/