今回から数回にわたって、
おさらいとクラス設計
これまでのcvcapture拡張モジュールにはカメラまたは動画ファイルからリソースを作成する関数cv_
これを以下のPHPコードのようなイメージでクラス化してみましょう。
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_
<?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 &size])</proto>
</function>
</class>
</extension>
グローバル定数は<constants>要素の中に<constant>を配置しましたが、
プロパティは<property>要素で指定します。ここではname属性とaccess属性だけを使っていますが、
メソッドはグローバル関数と同じ<function>要素で指定しますが、
なお、
コンストラクタを定義したい場合は、
ソースコードを生成する
ではこれまで通り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.
今回は動作するコードを実装する前に、
ソースコードを読む:クラス情報
まずは59行目に以下のような記述がありました。
static zend_class_entry * CvCapture_ce_ptr = NULL;
zend_
/*
* 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.
ソースコードを読む:メソッドのひな形
さらに読み進めましょう。クラスエントリー宣言のすぐ下ではメソッドのひな形が生成されています。
PHP_METHOD(CvCapture, createCameraCapture)
{
zend_class_entry * _this_ce;
zval * _this_zval = NULL;
long index = 0;
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|l", &_this_zval, CvCapture_ce_ptr, &index) == FAILURE) {
return;
}
_this_ce = Z_OBJCE_P(_this_zval);
php_error(E_WARNING, "createCameraCapture: not yet implemented"); RETURN_FALSE;
object_init(return_value)
}
関数はPHP_
ここで出てきたgetThis()はZend/
関数の場合は引数の取得にzend_
これはdate拡張モジュールの各関数のように、
さらにobject_
PHP_METHOD(CvCapture, createCameraCapture)
{
long index = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &index) == FAILURE) {
return;
}
php_error(E_WARNING, "createCameraCapture: not yet implemented"); RETURN_FALSE;
object_init_ex(return_value, CvCapture_ce_ptr);
}
object_
まだカメラに接続してリソースをプロパティに代入する処理が書かれていませんが、
ソースコードを読む:メソッド情報
その他のメソッドのひな形の後、
static zend_function_entry CvCapture_methods[] = {
PHP_ME(CvCapture, createCameraCapture, CvCapture__createCameraCapture_args, /**/ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
PHP_ME(CvCapture, createFileCapture, CvCapture__createFileCapture_args, /**/ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
PHP_ME(CvCapture, save, CvCapture__save_args, /**/ZEND_ACC_PUBLIC)
{ NULL, NULL, NULL }
};
PHP_
ここで配列なのに区切りのカンマが無いことに気付かれた方は鋭いです。PHP_
最後の { NULL, NULL, NULL } はこれ以上エントリがないことを示す終端記号で、
ソースコードを読む:クラス登録
関数情報の次はいよいよ実際にクラスを有効にする処理です。
static void class_init_CvCapture(void)
{
zend_class_entry ce;
INIT_CLASS_ENTRY(ce, "CvCapture", CvCapture_methods);
CvCapture_ce_ptr = zend_register_internal_class(&ce);
/* {{{ Property registration */
zend_declare_property_null(CvCapture_ce_ptr,
"capture", 7,
ZEND_ACC_PRIVATE TSRMLS_DC);
/* }}} Property registration */
/* {{{ Constant registration */
do {
zval *tmp, *val;
zend_declare_class_constant_long(CvCapture_ce_ptr, "ANY", 3, CV_CAP_ANY TSRMLS_CC );
/* 中略 */
zend_declare_class_constant_long(CvCapture_ce_ptr, "PVAPI", 5, CV_CAP_PVAPI TSRMLS_CC );
} while(0);
/* } Constant registration */
}
まずINIT_
この関数はもう少し下に書かれているPHP_
ビルドしてみよう
CvCaptureクラスに関係するコードの解説はこれで終わりです。メソッド未実装の状態ですが、
$ phpize $ ./configure $ make
大抵の環境ではそのまま通って操作3のように出力されるのですが、
Build complete. Don't forget to run 'make test'.
/../cvcapture.c: In function ‘class_init_CvCapture’: /../cvcapture.c:142: error: too few arguments to function ‘zend_register_internal_class’ /../cvcapture.c:148: error: expected expression before ‘void’ /../cvcapture.c:157: error: ‘tsrm_ls’ undeclared (first use in this function) /../cvcapture.c:157: error: (Each undeclared identifier is reported only once /../cvcapture.c:157: error: for each function it appears in.) make: *** [cvcapture.lo] Error 1
これもやはりCodeGen_
--- cvcapture.c.orig
+++ cvcapture.c
@@ -134,18 +134,18 @@
/* }}} Methods */
-static void class_init_CvCapture(void)
+static void class_init_CvCapture(TSRMLS_D)
{
zend_class_entry ce;
INIT_CLASS_ENTRY(ce, "CvCapture", CvCapture_methods);
- CvCapture_ce_ptr = zend_register_internal_class(&ce);
+ CvCapture_ce_ptr = zend_register_internal_class(&ce TSRMLS_CC);
/* {{{ Property registration */
zend_declare_property_null(CvCapture_ce_ptr,
"capture", 7,
- ZEND_ACC_PRIVATE TSRMLS_DC);
+ ZEND_ACC_PRIVATE TSRMLS_CC);
/* }}} Property registration */
@@ -238,7 +238,7 @@
REGISTER_LONG_CONSTANT("CV_CAP_PVAPI", CV_CAP_PVAPI, CONST_PERSISTENT | CONST_CS);
le_cvcapture = zend_register_list_destructors_ex(cvcapture_dtor,
NULL, "cvcapture", module_number);
- class_init_CvCapture();
+ class_init_CvCapture(TSRMLS_C);
/* add your stuff here */
これでスレッドセーフなPHP向けにもコンパイルできるようになります。
まとめと次回予告
今回はPHPのクラスを定義するためのspecファイルの書き方と、
肝心のメソッドが未実装のままですので、
また、
今回までで通常の拡張モジュール作成に必要なspecファイルの書き方は大体説明できたので、
サンプルファイルのダウンロード