mysqlnd
プラグイン それ自体が PHP拡張モジュール であることを覚えておくことが重要です。
次のコードは、典型的な mysqlnd
プラグインで使われる MINIT 関数の基本構造を示します。
static PHP_MINIT_FUNCTION(mysqlnd_plugin) { mysqlnd_plugin_id = mysqlnd_plugin_register(); conn_m = mysqlnd_get_conn_methods(); memcpy(org_conn_m, conn_m, sizeof(struct st_mysqlnd_conn_methods)); conn_m->query = MYSQLND_METHOD(mysqlnd_plugin_conn, query); conn_m->connect = MYSQLND_METHOD(mysqlnd_plugin_conn, connect); }
enum_func_status MYSQLND_METHOD(mysqlnd_plugin_conn, query)() { } enum_func_status MYSQLND_METHOD(mysqlnd_plugin_conn, connect)() { }
タスクの解析: C言語からユーザースペースへ
class proxy extends mysqlnd_plugin_connection { public function connect($host, ...) { .. } } mysqlnd_plugin_set_conn_proxy(new proxy());
プロセス:
PHP: ユーザーがプラグインのコールバックを登録する
PHP: ユーザーがMySQLに接続するため、任意の PHP MySQL API を呼び出す
C: ext MYSQLND_METHOD(my_conn_class,connect)( MYSQLND *conn, const char *host TSRMLS_DC) { enum_func_status ret = FAIL; zval * global_user_conn_proxy = fetch_userspace_proxy(); if (global_user_conn_proxy) { ret = MY_ZEND_CALL_METHOD_WRAPPER(global_user_conn_proxy, host, ); } else { ret = org_methods.connect(conn, host, user, passwd, passwd_len, db, db_len, port, socket, mysql_flags TSRMLS_CC); } return ret; }
ユーザースペースを呼び出す: 引数が一つの場合
MYSQLND_METHOD(my_conn_class,connect)( , const char *host, ) { if (global_user_conn_proxy) { zval* zv_host; MAKE_STD_ZVAL(zv_host); ZVAL_STRING(zv_host, host, 1); MY_ZEND_CALL_METHOD_WRAPPER(global_user_conn_proxy, zv_retval, zv_host ); zval_ptr_dtor(&zv_host); } }
ユーザースペースを呼び出す: 構造体を引数にする場合
MYSQLND_METHOD(my_conn_class, connect)( MYSQLND *conn, ) { if (global_user_conn_proxy) { zval* zv_conn; ZEND_REGISTER_RESOURCE(zv_conn, (void *)conn, le_mysqlnd_plugin_conn); MY_ZEND_CALL_METHOD_WRAPPER(global_user_conn_proxy, zv_retval, zv_conn, zv_host ); zval_ptr_dtor(&zv_conn); } }
多くの mysqlnd
のメソッドがはじめに引数としてとるのが C の "object" です。たとえば、 connect() メソッドのはじめの引数は MYSQLND
構造体 へのポインタです。MYSQNND構造体 は、mysqlnd
接続オブジェクトを表現します。
mysqlnd
接続オブジェクトへのポインタは、標準のI/Oファイルハンドルと比較できます。標準の I/Oファイルハンドルのように mysqlnd
接続オブジェクトは PHP のリソース変数タイプを使ってユーザースペースに結びついていなければならないのです。
C言語とユーザースペースを行き来する
class proxy extends mysqlnd_plugin_connection { public function connect($conn, $host, ...) { printf("Connecting to host = '%s'\n", $host); debug_print_backtrace(); return parent::connect($conn); } public function query($conn, $query) { $ret = parent::query($conn, $query); printf("Query = '%s'\n", $query); return $ret; } } mysqlnd_plugin_set_conn_proxy(new proxy());
PHP ユーザーは、オーバーライドしたメソッドの親の実装を呼び出し可能にしなければいけません。
継承することで、ユーザーが選択したメソッドだけを改善し、"pre" や "post" フックを持たせるかどうかを選択できます。
ビルトインのクラス: mysqlnd_plugin_connection::connect()
PHP_METHOD("mysqlnd_plugin_connection", connect) { zval* mysqlnd_rsrc; MYSQLND* conn; char* host; int host_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &mysqlnd_rsrc, &host, &host_len) == FAILURE) { RETURN_NULL(); } ZEND_FETCH_RESOURCE(conn, MYSQLND* conn, &mysqlnd_rsrc, -1, "Mysqlnd Connection", le_mysqlnd_plugin_conn); if (PASS == org_methods.connect(conn, host, TSRMLS_CC)) RETVAL_TRUE; else RETVAL_FALSE; }