コンストラクタの定義
続いて、コンストラクタの実装を行います。
static zend_object_value php_pzip_file_new(zend_class_entry *ce TSRMLS_DC){ zend_object_value retval; php_pzip_file *obj; obj = (php_pzip_file *)ecalloc(1,sizeof(php_pzip_file)); // ……(1) zend_object_std_init( &obj->std, ce TSRMLS_CC); // ……(2) object_properties_init(&obj->std, ce); // ……(3) retval.handle = zend_objects_store_put(obj, (zend_objects_store_dtor_t) zend_objects_destroy_object, (zend_objects_free_object_storage_t) php_pzip_file_dtor, // ……(4) NULL TSRMLS_CC ); retval.handlers = zend_get_std_object_handlers(); // ……(5) return retval; }
(1)で構造体のメモリ確保を行います。続いて、(2)ではPHPエンジン側でも必要な全体の初期化を行います。(3)ではPHP側に参照させるためのプロパティ情報の初期化をします。今回はPHP側で参照できるプロパティを定義していませんので、あまり関係がない処理ではありますが、今後プロパティを追加した場合に必要となる処理です。
また、(4)では先ほど実装したデストラクタと一緒にオブジェクトデータをPHPエンジン側に登録します。(5)ではPHPエンジン側で使用するオブジェクト操作をするための関数を設定します。このように、クラスごとに異なる部分は、構造体の変数名程度でその他に異なる処理はありません。
コンストラクタの登録
コンストラクタを定義したら、その関数をPHP_MINIT_FUNCTIONで登録する必要があります。
PHP_MINIT_FUNCTION(pzip_file){ // : 省略 pzip_file_ce = zend_register_internal_class(&ce TSRMLS_CC); pzip_file_ce->create_object = php_pzip_file_new; // ……(1) return SUCCESS; }
そのために、(1)の行を追加する必要があります。
メソッドの定義
続いて、メソッドの定義を行います。
$thisへのアクセス
メソッドを定義する際には、必要なプロパティは$thisを通してPHPではアクセスができます。エクステンション内での$thisに相当する変数にアクセスするマクロが用意されています。
// $thisを取得する php_pzip_entry *object = (php_pzip_entry *) zend_object_store_get_object( // ……(1) getThis() TSRMLS_CC); // ……(2)
(2)のgetThis()が$thisに相当します。実際には関数の引数として渡ってくる変数を参照しています。次にこのgetThis()を引数として(1)zend_object_store_get_object()を使用して作成した構造体を取得します。
プロパティデータへの値の設定
先ほど取得した構造体に値を設定してみます。まずは、単純に与えられた引数の文字列をそのまま保存するという実装をしてみます。以下のように使われる想定です。
$entry->setFilename("hello.png");
注意すべき点が2つほどあります。実際にコードを示してみます。
PHP_METHOD(PZipEntry, setFilename){ const char *filename; int filename_len; // : (省略) // $thisを取得する php_pzip_entry *object = (php_pzip_entry *) zend_object_store_get_object(getThis() TSRMLS_CC); if(object->filename){ efree(object->filename); // ……(1) } object->filename = estrndup(filename,filename_len); // ……(2) object->filename_length = filename_len; RETURN_TRUE; }
(1)は、すでに値が設定している場合の処理です。ここでメモリを解放しないとメモリリークが発生します。もちろん、メモリの解放方法は取得時の方法にもよりますが、ここでは(2)estrndupを使用し、文字列データを新たなメモリとして取得しコピーをしています。これはfilenameという変数はこの関数内でのスコープになるので、この変数の内容をコピーして再設定するという処理が必要になります。C言語に慣れている方でしたらこのあたりはあまり注意がいらないとは思いますが、PHPに慣れていると間違いやすいので注意が必要です。
プロパティデータからの値の取得
プロパティへの値の設定方法が分かれば、ほぼ同様にして取得方法も分かると思いますが、念のために示しておきます。また、PHPからは以下のように使われる想定です。
$entry->getFilename();
このように構造体が取得出来れば、あとは通常の関数を作成する際とまったく同じ方法で実装が可能です。
PHP_METHOD(PZipEntry, getFilename){ // $thisを取得する // : 省略 if(object->filename){ RETURN_STRINGL(object->filename,object->filename_length,1); } else{ RETURN_NULL(); } }
最後に
今回はまだクラスの作成方法の流れのみでしたが、できるだけ実用的な例をイメージして紹介しました。私の個人的な好みも含みますが、やはりPHPのエクステンションを作る際にクラスが作れると、関数名の重複を避けるための長い関数名を作成する必要もありません。また、一時的なデータ保存を構造体として共有できるので、PHP側とのデータのやりとりも、手軽に少なくできます。
次回からは今回作成した例を拡張し、パスワード付きZipデータの解凍を行えるようにしていきます。外部ライブラリも使用していきますので、いよいよ実用的なエクステンションが作れるようになっていきます。