7.9.5开始构建一个mysqlnd插件

版权1997 - 2021 PHP文档组。10bet官方网站

重要的是要记住,一个mysqlnd插件本身就是一个PHP扩展。

下面的代码显示了使用MINIT函数的基本结构的典型mysqlnd插件:

/ * my_php_mysqlnd_plugin。c * /静态PHP_MINIT_FUNCTION (mysqlnd_plugin){/ *全局、ini条目、资源类插件* / * / / *注册mysqlnd 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 - >查询= MYSQLND_METHOD (mysqlnd_plugin_conn、查询);conn_m - >连接= MYSQLND_METHOD (mysqlnd_plugin_conn、连接);}
/ * my_mysqlnd_plugin。c * / enum_func_status MYSQLND_METHOD (mysqlnd_plugin_conn、查询)(/ *……{/ * * /)……* /}enum_func_status MYSQLND_METHOD (mysqlnd_plugin_conn连接)(/ *……{/ * * /)……* /}

任务分析:从C到用户空间

类代理扩展mysqlnd_plugin_connection{公共函数连接(主机、美元…){…}}mysqlnd_plugin_set_conn_proxy(新代理());

过程:

  1. PHP:用户注册插件回调

  2. PHP: MySQL用户调用任何PHP API连接到MySQL

  3. C: ext / * mysql * mysqlnd方法的调用

  4. C: mysqlnd ext / mysqlnd_plugin结束

  5. C: ext / mysqlnd_plugin

    1. 调用用户空间回调

    2. 或原创的mysqlnd方法,如果用户空间回调没有设置

你需要执行以下:

  1. 写一个类“mysqlnd_plugin_connection”C

  2. 通过“mysqlnd_plugin_set_conn_proxy接受并注册代理对象()

  3. 调用用户空间代理方法从C(优化- zend_interfaces.h)

用户空间对象方法可以被使用call_user_function ()或者你可以操作水平接近Zend引擎和使用zend_call_method ()

优化:使用zend_call_method从C调用方法

下面的代码片段显示了原型的zend_call_method函数,从zend_interfaces.h

ZEND_API zval * zend_call_method (zval * * object_pp zend_class_entry * obj_ce zend_function * * fn_proxy, char * function_name, int function_name_len, zval * * retval_ptr_ptr, int param_count, zval * __arg1、zval *最长TSRMLS_DC);

Zend API只支持两个参数。你可能需要更多的,例如:

enum_func_status (* func_mysqlnd_conn__connect) (MYSQLND *康涅狄格州,const char *主机,const char *用户,const char * passwd, unsigned int passwd_len, const char * db, unsigned int db_len, unsigned int港口,const char *插座,unsigned int mysql_flags TSRMLS_DC);

为了解决这个问题,你需要复制zend_call_method ()和为额外添加一个设备参数。为此,您可以创建一个组MY_ZEND_CALL_METHOD_WRAPPER宏。

调用PHP用户空间

这个代码片段显示了从C调用用户空间函数的优化方法:

/ * my_mysqlnd_plugin。c * / MYSQLND_METHOD (my_conn_class、连接)(MYSQLND *康涅狄格州,const char *主机/ *……* / TSRMLS_DC) {enum_func_status ret =失败;zval * global_user_conn_proxy = fetch_userspace_proxy ();如果(global_user_conn_proxy){/ *调用用户空间代理* / ret = MY_ZEND_CALL_METHOD_WRAPPER (global_user_conn_proxy、主机/ *…* /);其他}{/ *或原始mysqlnd方法=什么也不做,是透明* / ret = org_methods。连接(康涅狄格州、主机、用户密码、passwd_len db, db_len,港口,插座,mysql_flags TSRMLS_CC);}返回受潮湿腐烂;}

调用用户空间:简单的参数

/ * my_mysqlnd_plugin。c * / MYSQLND_METHOD (my_conn_class连接)(/ *……* /,const char *主机,/ * * /){/ *…如果(global_user_conn_proxy) {/ * / *……* / zval * zv_host;MAKE_STD_ZVAL (zv_host);ZVAL_STRING (zv_host主机1);MY_ZEND_CALL_METHOD_WRAPPER (global_user_conn_proxy zv_retval、zv_host / *…* /);zval_ptr_dtor (&zv_host);/ *…… */ } /* ... */ }

调用用户空间:结构作为参数

/ * my_mysqlnd_plugin。c * / MYSQLND_METHOD (my_conn_class、连接)(康涅狄格州MYSQLND * / *…* /) {/ *……如果(global_user_conn_proxy) {/ * / *……* / zval * zv_conn;ZEND_REGISTER_RESOURCE (zv_conn (void *)康涅狄格州,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“对象”。例如,connect()方法的第一个参数是一个指针MYSQLND。struct MYSQLND代表mysqlnd连接对象。

mysqlnd连接对象指针可以比作一个标准I / O文件句柄。像一个标准I / O文件句柄mysqlnd连接对象应当与用户空间使用PHP资源变量类型。

从C到用户空间

类代理扩展mysqlnd_plugin_connection{公共函数连接(康涅狄格州,主机,美元…){/ *”前“钩* / printf("连接到主机= ' % s ' \ n”,主持人美元);debug_print_backtrace ();返回父:连接(康涅狄格州美元);}公共函数查询(康涅狄格州,查询美元){/ *”后“钩* / ret =父:美元:查询(康涅狄格州、查询);printf("查询= ' % s ' \ n ",查询美元);返回ret美元;}}mysqlnd_plugin_set_conn_proxy(新代理());

PHP用户必须能够调用重写的父实现方法。

由于子类化只可以优化选择方法,你可以选择“前”或“后”挂钩。

内置的类:mysqlnd_plugin_connection:: connect ()

/ * my_mysqlnd_plugin_classes。c * / PHP_METHOD (“mysqlnd_plugin_connection”,连接){/ *……简化!…* / zval * mysqlnd_rsrc;康涅狄格州MYSQLND *;char *主机;int host_len;如果(zend_parse_parameters (ZEND_NUM_ARGS () TSRMLS_CC,“rs”, &mysqlnd_rsrc,主机,&host_len) = =失败){RETURN_NULL ();}ZEND_FETCH_RESOURCE(康涅狄格州MYSQLND *康涅狄格州,&mysqlnd_rsrc, 1,“MYSQLND连接”,le_mysqlnd_plugin_conn); if (PASS == org_methods.connect(conn, host, /* simplified! */ TSRMLS_CC)) RETVAL_TRUE; else RETVAL_FALSE; }