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

第7回 クラスの実装(前編)

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

今回から数回にわたって,第4回までに作ったcvcapture拡張モジュールを改造していきます。まずは単純なクラスを実装してみましょう。

おさらいとクラス設計

これまでのcvcapture拡張モジュールにはカメラまたは動画ファイルからリソースを作成する関数cv_create_camera_capture(),cv_create_camera_capture()と,リソースを引数にとってキャプチャした画像を保存する関数cv_create_camera_capture()があります。

これを以下のPHPコードのようなイメージでクラス化してみましょう。

リスト1 CvCaptureクラスのプロトタイプ

class CvCapture
{
    const ANY        = CV_CAP_ANY;
    /* 中略 */
    const PVAPI      = CV_CAP_PVAPI;

    private $capture;

    private function __construct($capture)
    {
        $this->capture = $capture;
    }

    public function save($filename, &$size = null)
    {
        return cv_save_capture($this->capture, $filename, $size);
    }

    static public function createCameraCapture($index = self::ANY)
    {
        $capture = cv_create_camera_capture($index);
        if (is_resource($capture)) {
            return new CvCapture($capture);
        }
        return false;
    }

    static public function createFileCapture($filename)
    {
        $capture = cv_create_file_capture($filename);
        if (is_resource($capture)) {
            return new CvCapture($capture);
        }
        return false;
    }
}

specファイル定義

リスト1のクラスをCodeGen_PECLのspecファイルで表すには,リスト2のように,ルート要素である<extension>の中に<class>要素,その中に<constant>,<property>,<function>を配置します。

リスト2 specファイルよりクラス定義を抜粋

<?xml version="1.0" encoding="UTF-8"?>
<extension name="cvcapture" version="0.3.0">
  <!-- リソースやグローバル定数・関数の定義は省略 -->
  <class name="CvCapture">
    <constant type="int" name="ANY" value="CV_CAP_ANY">autodetect</constant>
    <!-- 中略 -->
    <constant type="int" name="PVAPI" value="CV_CAP_PVAPI">PvAPI, Prosilica GigE SDK</constant>

    <property name="capture" access="private">cvcapture resource</property>

    <function access="public" static="yes" name="createCameraCapture">
      <proto>object createCameraCapture([int index])</proto>
    </function>
    <function access="public" static="yes" name="createFileCapture">
      <proto>object createFileCapture(string filename)</proto>
    </function>
    <function access="public" name="save">
      <proto>bool save(string filename[, mixed &amp;size])</proto>
    </function>
  </class>
</extension>

グローバル定数は<constants>要素の中に<constant>を配置しましたが,クラス定数の場合は<class>の中に直接<constant>を配置します。

プロパティは<property>要素で指定します。ここではname属性とaccess属性だけを使っていますが,type属性とvalue属性でデフォルト値を指定することもできます。

メソッドはグローバル関数と同じ<function>要素で指定しますが,access属性でアクセシビリティを指定できるほか,abstract="yes",final="yes",static="yes"の指定もできます。また,abstract/final属性はクラスに,static属性はプロパティにもつけられます。

なお,リスト1のプロトタイプでは便宜上コンストラクタを書いていますが,CvCaptureクラスではcreateCameraCapture()/createFileCapture()の中でコンストラクタ相当の処理もするように実装する計画なのでspecファイルにはコンストラクタを書いていません。

コンストラクタを定義したい場合は,単にメソッド名を⁠__construct⁠にするだけで,特別な指定は必要ありません※1)⁠

※1

デストラクタも同じく⁠__destruct⁠です。

ソースコードを生成する

ではこれまで通りpecl-genコマンドでソースコードを生成しましょう※2)⁠

操作1 pecl-genを実行

$ pecl-gen --dir=cvcapture-0.3.0 cvcapture-0.3.0.xml
Creating 'cvcapture' extension in 'cvcapture-0.3.0'

Your extension has been created in directory ./cvcapture-0.3.0.
See ./cvcapture-0.3.0/README and/or ./cvcapture-0.3.0/INSTALL for further instructions.

今回は動作するコードを実装する前に,生成されたソースコードcvcapture.cからCvCaptureクラスに関係する箇所を先頭から見ていきましょう。

※2

php.iniの設定によってはNoticeが出ますが,これはCodeGen_PECLのバグです。

ソースコードを読む:クラス情報

まずは59行目に以下のような記述がありました。

リスト3 zend_class_entry *(cvcapture.c:59行目)

static zend_class_entry * CvCapture_ce_ptr = NULL;

zend_class_entryはPHPのクラスを定義する構造体で,CvCapture_ce_ptrにはCvCaptureクラスの情報が格納されます。ここでいったんPHP本体のソースコードからzvalの定義を見直してみましょう。

リスト4 zval(php-5.3.x/Zend/zend.h)

/*
 * zval
 */
typedef struct _zval_struct zval;
typedef struct _zend_class_entry zend_class_entry;

typedef struct _zend_guard {
        zend_bool in_get;
        zend_bool in_set;
        zend_bool in_unset;
        zend_bool in_isset;
        zend_bool dummy; /* sizeof(zend_guard) must not be equal to sizeof(void*) */
} zend_guard;

typedef struct _zend_object {
        zend_class_entry *ce;
        HashTable *properties;
        HashTable *guards; /* protects from __get/__set ... recursion */
} zend_object;

#include "zend_object_handlers.h"

typedef union _zvalue_value {
        long lval;                                      /* long value */
        double dval;                            /* double value */
        struct {
                char *val;
                int len;
        } str;
        HashTable *ht;                          /* hash table value */
        zend_object_value obj;
} zvalue_value;

struct _zval_struct {
        /* Variable information */
        zvalue_value value;             /* value */
        zend_uint refcount__gc;
        zend_uchar type;        /* active type */
        zend_uchar is_ref__gc;
};

zval.value.obj.ceにzend_class_entry *が格納されています。つまり, zval.type = IS_OBJECT かつ zval.value.obj.ce = CvCapture_ce_ptr; な値がCvCaptureオブジェクトとなるのです。

著者プロフィール

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

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

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

コメント

コメントの記入