如何Hook PDO类方法实现自定义函数优先执行并解决查找NULL问题
解决PDO方法Hook的问题
核心问题:Hook时机不对
你之前的代码返回NULL,本质是扩展加载顺序导致的:如果你的扩展比PDO先完成初始化(MINIT阶段),此时PDO类还未被注册到Zend引擎的类表CG(class_table)中,自然找不到目标类和方法。
正确实现步骤
1. 定义全局变量保存原方法处理器
需要全局保存PDO::exec的原handler,避免自定义逻辑执行后无法调用原方法:
static zend_function_handler original_pdo_exec_handler = NULL;
2. 实现自定义Hook处理器
编写你的统计逻辑,执行完成后调用原处理器:
ZEND_METHOD(PDO, exec) { // 1. 收集SQL执行统计(示例逻辑) zval *sql_param = NULL; if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &sql_param) == SUCCESS) { if (Z_TYPE_P(sql_param) == IS_STRING) { // 这里可以将SQL、执行时间等信息写入统计结构 // 比如:record_pdo_stat(Z_STRVAL_P(sql_param), getThis()); } } // 2. 调用原PDO::exec方法 if (original_pdo_exec_handler != NULL) { original_pdo_exec_handler( getThis(), getCalledMethod(), getThis(), ZEND_NUM_ARGS(), getArguments(), return_value, return_value_ptr, this_ptr ); } }
3. 延迟Hook到所有扩展加载完成后
使用zend_register_post_startup_callback,确保PDO扩展已经初始化完成再执行Hook:
static void hook_pdo_methods(void) { // 查找PDO类条目 zend_class_entry *pdo_ce = zend_hash_str_find_ptr(CG(class_table), "PDO", sizeof("PDO") - 1); if (!pdo_ce) { php_error(E_WARNING, "无法找到PDO类,Hook失败"); return; } // 查找PDO::exec方法 zend_function *pdo_exec_func = zend_hash_str_find_ptr(&pdo_ce->function_table, "exec", sizeof("exec") - 1); if (!pdo_exec_func) { php_error(E_WARNING, "无法找到PDO::exec方法"); return; } // 保存原处理器并替换为自定义逻辑 original_pdo_exec_handler = pdo_exec_func->internal_function.handler; pdo_exec_func->internal_function.handler = ZEND_METHOD_NAME(PDO, exec); } // 在你的扩展MINIT阶段注册回调 PHP_MINIT_FUNCTION(your_extension_name) { zend_register_post_startup_callback(hook_pdo_methods); return SUCCESS; }
关于符号Hook的疑问
PHP内部方法不会暴露到动态符号表中,无法像普通C函数那样通过dlsym直接获取地址,必须通过Zend引擎提供的哈希表接口(如zend_hash_str_find_ptr)查找类和方法的结构,这也是New Relic、Datadog等APM扩展的标准做法。
内容的提问来源于stack exchange,提问作者김성훈
相关产品推荐
相关产品推荐

