2011年12月15日木曜日

もう何度も何度もやっている気がするが、今後また作るときの事を考えて、改めてここで、PHP Extension の作り方をまとめておく。

自分がPHP Extensionを作ろうと思ったときに、
「とりあえず開いて書き方ぱくるためのページ」として、
これを書いておく。解説は他のサイトを確認する。
Zend API: PHPのコアをハックする
ここはあくまで、こうやって書いたら動いたってメモ。

※ 前提条件として、PHPをソースからコンパイルしてある事とする。

ソースのパス : /usr/local/share/php-5.3.8/
extension_dir: /usr/lib64/20090626
作成するextensionの名前: mywork


1、スケルトンの作成




cd /usr/local/share/php-5.3.8/ext/
./ext_skel --extname=mywork
cd mywork


2、とりあえずコンパイル




① config.m4を修正
inl 以下をコメントアウトからはずす。
PHP_ARG_WITH(eigen, for eigen support,
Make sure that the comment is aligned:
[  --with-eigen             Include eigen support])

② コンパイル
# phpize 
# ./configure
# make
これで、./modules内に、mywork.soが作成される。
されなかったらなんか間違ってる。

③ PHPにextensionを読み込ませる。
# ln -s /usr/local/share/php-5.3.8/ext/mywork/modules/mywork.so /usr/lib64/20090626/
加えて、php.iniに「extension=mywork.so」を記入しておく。
make installしても良いが、これから何度もコンパイルするたびにmake installするのがめんどくさいから、シンボリックリンクはる。



3、引数取得・戻値返却



こんなPHP extensionを書くためにやる事。


① php_mywork.h
// 関数名を定義
PHP_FUNCTION(MYWORK);

② mywork.c
/* zend_function_entry の中に、MYWORKの行を追記 */
const zend_function_entry kakasi_functions[] = {
    PHP_FE(confirm_kakasi_compiled, NULL)       /* For testing, remove later. */
    PHP_FE(MYWORK, NULL)
    PHP_FE_END  /* Must be the last line in kakasi_functions[] */
};

~~~
PHP_FUNCTION(MYWORK){    
    // 値定義
    int   str_len;
    char *str;

    // 引数の取得
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
        "s", &str, &str_len) == FAILURE) {
        return;
    }
  // 戻り値作成(文字列)
  RETURN_STRINGL(str,str_len,0);
}
これで、引数「引数」が戻される。
longやdoubleなどの返却は、同じようなマクロを使って返却する。詳しくは、以下を参照。
Zend API: PHPのコアをハックする
配列やオブジェクトの返却はチョットややこしい。

・配列を返却
PHP_FUNCTION(MYWORK){    

~~~
  // zvalの定義
  
    zval *return_array;
    char *separated_word
    char words[1024];
    words="test,test,test,test";

  // 配列を表すzvalを作成
    MAKE_STD_ZVAL(return_array);
    if(array_init(return_array) != SUCCESS){}

    // 文字の分割 
    separated_word = strtok( words, "," );
    while( separated_word != NULL ){
        // 配列に値を追加する。
        add_next_index_stringl(return_array,separated_word,strlen(separated_word),1);
        separated_word= strtok( NULL, "," );  /* 2回目以降 */
    }

    // 配列を返却
    *return_value = *return_array;
    zval_copy_ctor(return_value);
}

・オブジェクトを返却
PHP_FUNCTION(MYWORK){    

~~~
    // 返却オブジェクトの生成
    zval *wordset;
    MAKE_STD_ZVAL(wordset);
    if(object_init(wordset) != SUCCESS){}

    // プロパティを設定
    add_property_stringl(wordset,"base",str,strlen(str), 1);
    add_property_stringl(wordset,"hira",hira,strlen(hira),1);
    add_property_stringl(wordset,"kata",kata,strlen(kata),1);
    add_property_stringl(wordset,"alph",alph,strlen(alph),1);

    // オブジェクトの返却
    *return_value = *wordset;
    zval_copy_ctor(return_value);
}



3、クラス作成



こんなPHP extensionを書くためにやる事。
member1;
$obj->member2;
$ret = $obj->method();
?>

① php_mywork.h
// 以下を追記する。
PHP_FUNCTION(mywork);
PHP_METHOD(mywork, method);
static zend_class_entry *mywork_ce;

② mywork.c
/* zend_function_entry の中に、MYWORKの行を追記 */
const zend_function_entry kakasi_functions[] = {
    PHP_FE(confirm_kakasi_compiled, NULL)       /* For testing, remove later. */
    PHP_FE(MYWORK, NULL)
    PHP_ME(MYWORK, method ,NULL)
    PHP_FE_END  /* Must be the last line in kakasi_functions[] */
};

~~~
/* PHP_MINIT_FUNCTION(mywork) の中に、MYWORKの行を追記 */
PHP_MINIT_FUNCTION(mywork)
{
    /* If you have INI entries, uncomment these lines
     REGISTER_INI_ENTRIES();
    */
    zend_class_entry ce;
    INIT_CLASS_ENTRY(ce, "", mywork_functions);
    mywork_ce = zend_register_internal_class(&ce TSRMLS_CC);

    zend_declare_property_string(mywork_ce,"member1", strlen("member1"),"初期値", ZEND_ACC_PUBLIC);
    zend_declare_property_string(mywork_ce,"member2", strlen("member2"),"初期値", ZEND_ACC_PUBLIC);

    return SUCCESS;
}

~~~
PHP_FUNCTION(mywork){
    return;
}

PHP_METHOD(mywork, method){

    // 定義
    zval *obj, *member1, *member2;

    // メンバ変数の取得
    obj = getThis();
    member1 = zend_read_property(Z_OBJCE_P(obj),obj, "member1",strlen("member1"), 1 TSRMLS_CC);
    member2 = zend_read_property(Z_OBJCE_P(obj),obj, "member2",strlen("member2"), 1 TSRMLS_CC);
    
  // 戻り値作成(文字列)
  RETURN_STRINGL(member1,strlen(member1),0);
}



番外、C++を使いたい場合



CではなくC++を使いたい場合の今までと異なる点を上げる。

① config.m4
inl コンパイラの環境変数を設定
CC=/usr/bin/gcc
CFLAGS="-g -O2 -lstdc++"
CXX=/usr/bin/g++
CXXFLAGS=-fPIC

~~~
inl 拡張子をc→ccに変更する。
PHP_NEW_EXTENSION(eigen, eigen.cc, $ext_shared)


② ファイルの拡張子
mv mywork.c mywork.cc

③ mywork.cc
// #includeを extern "C"で囲む
extern "C"{
    #include "php.h"
    #include "php_ini.h"
    #include "ext/standard/info.h"
    #include "php_eigen.h"
}



0 件のコメント:

コメントを投稿