PHP如何自定义扩展二之钩子功能

接着上篇来讲PHP生命周期,看看扩展哪些钩子做哪些事,PHP生命周期大概5个阶段,模块初始化阶段PHP_module_startup,请求初始化阶段PHP_request_startup,脚本执行阶段PHP_execute_script,请求关闭阶段PHP_request_shutdown,模块关闭阶段PHP_module_shutdown,下面以cli模式介绍。

PHP_module_startup

先看看这个阶段做了什么,如果不知道PHP入口文件在哪,用gdb看看调用栈,gdb ./PHP

PHP_module_startup打断点,执行,在看下调用栈,

b PHP_module_startup
(gdb) r test.PHP
bt
PHP_module_startup (sf=0x1406460 <cli_sapi_module>, 
    additional_modules=0x0, num_additional_modules=0)
    at /www/test/PHP/PHP-7.4.3/main/main.c:2098
#1  0x00000000008bae7c in PHP_cli_startup (
    sapi_module=0x1406460 <cli_sapi_module>)
    at /www/test/PHP/PHP-7.4.3/sapi/cli/PHP_cli.c:407
#2  0x00000000008bcc80 in main (argc=2, argv=0x1425af0)
    at /www/test/PHP/PHP-7.4.3/sapi/cli/PHP_cli.c:1323

调用栈可以清楚看到执行流程,现在到/main/main.c文件看看做了哪些事情,也可以用gdb一步一步的看,这里就讲与PHP扩展有关的几个地方,这里做的初始化工作,像垃圾回收,请求初始化,注册常量,PHP.ini配置文件加载等,

先来看看怎么加载模块的

/* startup extensions statically compiled in */
    if (PHP_register_internal_extensions_func() == FAILURE) {
        PHP_printf(Unable to start builtin modules\n);
        return FAILURE;
    }

这里是加载PHP内置的模块,这里只贴出核心功能,先检查依赖

/* Check module dependencies */
    if (module->deps) {
        const zend_module_dep *dep = module->deps;

        while (dep->name) {
            if (dep->type == MODULE_DEP_CONFLICTS) {
                name_len = strlen(dep->name);
                lcname = zend_string_alloc(name_len, 0);
                zend_str_tolower_copy(ZSTR_VAL(lcname), dep->name, name_len);

                if (zend_hash_exists(&module_registry, lcname) || zend_get_extension(dep->name)) {
                    zend_string_efree(lcname);
                    /* Todo: Check version relationship */
                    zend_error(E_CORE_WARNING, Cannot load module '%s' because conflicting module '%s' is already loaded, module->name, dep->name);
                    return NULL;
                }
                zend_string_efree(lcname);
            }
            ++dep;
        }
    }
if (module->functions && zend_register_functions(NULL, module->functions, NULL, module->type)==FAILURE) {
        zend_hash_del(&module_registry, lcname);
        zend_string_release(lcname);
        EG(current_module) = NULL;
        zend_error(E_CORE_WARNING,%s: Unable to register functions, unable to load, module->name);
        return NULL;
    }

这是内置模块加载原理,现在看看ini里的扩展怎么加载

PHP_ini_register_extensions();
zend_llist_apply(&extension_lists.functions, PHP_load_PHP_extension_cb);

利用这个函数加载

PHP_load_extension(char *filename, int type, int start_Now)

这里面也执行了加载内置模块的功能

调用module->functions,进行模块功能函数注册,现在知道了为什么功能函数要写在helloworld_functions这里吧

zend_module_entry helloworld_module_entry = {
    STANDARD_MODULE_HEADER,
    helloworld,                    /* Extension name */
    helloworld_functions,            /* zend_function_entry */
    PHP_MINIT(helloworld),                            /* PHP_MINIT - Module initialization */
    NULL,                            /* PHP_MSHUTDOWN - Module shutdown */
    PHP_RINIT(helloworld),            /* PHP_RINIT - Request initialization */
    NULL,                            /* PHP_RSHUTDOWN - Request shutdown */
    PHP_MINFO(helloworld),            /* PHP_MINFO - Module info */
    PHP_HELLOWORLD_VERSION,        /* Version */
    PHP_MODULE_GLOBALS(pib),
    NULL,
    NULL,
    NULL,
    STANDARD_MODULE_PROPERTIES_EX
};

现在看看扩展的几个钩子函数

/* start Zend extensions */
    zend_startup_extensions();

这里的核心就是func(element->data)也就是执行扩展

PHP_MINIT函数

element=l->head;
    while (element) {
        next = element->next;
        if (func(element->data)) {
            DEL_LLIST_ELEMENT(element, l);
        }
        element = next;
    }

现在就知道PHP_MINIT钩子可以做很多初始化的功能,怎么注册一个自定义扩展的功能类,怎么把扩展的变量写到PHP.ini里面,怎么重写PHP内置函数

original = zend_hash_str_find_ptr(CG(function_table), var_dump, sizeof(var_dump)-1);

    if (original != NULL) {

        original->internal_function.handler = my_overwrite_var_dump;
    }

    zend_class_entry person;
    INIT_CLASS_ENTRY(person,CLASS_NAME,person_functions);
    zend_register_internal_class_ex(&person,NULL);

这里就是重写var_dump函数注册一个person类,先介绍到这里,下篇就介绍怎么把PHP代码通过词法分析语法分析生成AST,然后编译opcode指令,供zend虚拟机调用

推荐学习:《PHP教程

相关文章

统一支付是JSAPI/NATIVE/APP各种支付场景下生成支付订单,返...
统一支付是JSAPI/NATIVE/APP各种支付场景下生成支付订单,返...
前言 之前做了微信登录,所以总结一下微信授权登录并获取用户...
FastAdmin是我第一个接触的后台管理系统框架。FastAdmin是一...
之前公司需要一个内部的通讯软件,就叫我做一个。通讯软件嘛...
统一支付是JSAPI/NATIVE/APP各种支付场景下生成支付订单,返...