• 技术文章 >后端开发 >PHP问题

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

    藏色散人藏色散人2021-12-20 16:03:55转载316

    接着上篇来讲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视频教程

    以上就是PHP如何自定义扩展(二)之钩子功能的详细内容,更多请关注php中文网其它相关文章!

    声明:本文转载于:segmentfault,如有侵犯,请联系admin@php.cn删除
    专题推荐:php 自定义扩展 钩子
    上一篇:PHP如何自定义扩展(一)之基本步骤 下一篇:PHP如何自定义扩展(三)之生命周期
    VIP课程(WEB全栈开发)

    相关文章推荐

    • 【腾讯云】年中优惠,「专享618元」优惠券!• 聊聊Laravel Carbon扩展包的作用及用法• 推荐:Laravel E-commerce电子商务扩展包• yum 怎么安装php7 扩展• PHP如何自定义扩展(一)之基本步骤
    1/1

    PHP中文网