• PHP扩展的加载流程


    第一步,先完成一个最简单的扩展,只提供一个函数,hello。

    主要代码:

    ZEND_FUNCTION(hello)
    {
        php_printf("Hello World!\n");
    }

    static zend_function_entry tonic_functions[] = {
        ZEND_FE(hello,        NULL)
        { NULL, NULL, NULL }
    };

    zend_module_entry tonic_module_entry = {
    #if ZEND_MODULE_API_NO >= 20010901
         STANDARD_MODULE_HEADER,
    #endif
        "tonic"
        tonic_functions, /* Functions */
        NULL, /* MINIT */
        NULL, /* MSHUTDOWN */
        NULL, /* RINIT */
        NULL, /* RSHUTDOWN */
        NULL, /* MINFO */
    #if ZEND_MODULE_API_NO >= 20010901
        "2.1"
    #endif
        STANDARD_MODULE_PROPERTIES
    };

    #ifdef COMPILE_DL_TONIC
    ZEND_GET_MODULE(tonic)

    #endif 

      

    这几行代码,就完成了一个最简单的扩展了...


     ZEND_GET_MODULE宏展开如下:

    #define ZEND_GET_MODULE(name) \
        BEGIN_EXTERN_C()\
        ZEND_DLEXPORT zend_module_entry *get_module(void) { return &name##_module_entry; }\

        END_EXTERN_C() 

     这个宏,定义了一个名为get_module的函数,返回当前模块定义的zend_module_entry的指针。(通过这个宏可以发现,module_entry的名称,是固定格式的..不然就要自己实现get_module函数)

    再看看php加载扩展的实现(我去掉了一些老版本兼容等与分析无关的代码) :

    PHPAPI int php_load_extension(char *filename, int type, int start_now TSRMLS_DC) /* {{{ */
    {
        void *handle;
        char *libpath;
        zend_module_entry *module_entry;
        zend_module_entry *(*get_module)(void); /* 函数指针,获取module的信息 */
        int error_type;
        char *extension_dir;

        if (type == MODULE_PERSISTENT) {
            extension_dir = INI_STR("extension_dir");
        } else {
            extension_dir = PG(extension_dir);
        }

        if (type == MODULE_TEMPORARY) {
            error_type = E_WARNING;
        } else {
            error_type = E_CORE_WARNING;
        }

        /* Check if passed filename contains directory separators */
        if (strchr(filename, '/') != NULL || strchr(filename, DEFAULT_SLASH) != NULL) {
            /* Passing modules with full path is not supported for dynamically loaded extensions */
            if (type == MODULE_TEMPORARY) {
                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Temporary module name should contain only filename");
                return FAILURE;
            }
            libpath = estrdup(filename);
        } else if (extension_dir && extension_dir[0]) {
            int extension_dir_len = strlen(extension_dir);

            if (IS_SLASH(extension_dir[extension_dir_len-1])) {
                spprintf(&libpath, 0"%s%s", extension_dir, filename); /* SAFE */
            } else {
                spprintf(&libpath, 0"%s%c%s", extension_dir, DEFAULT_SLASH, filename); /* SAFE */
            }
        } else {
            return FAILURE; /* Not full path given or extension_dir is not set */
        }

        /* load dynamic symbol */
        handle = DL_LOAD(libpath); /* 加载动态链接文件 */
        if (!handle) {
            php_error_docref(NULL TSRMLS_CC, error_type, "Unable to load dynamic library '%s' - %s", libpath, GET_DL_ERROR());
            GET_DL_ERROR(); /* free the buffer storing the error */

            efree(libpath);
            return FAILURE;
        }
        efree(libpath);

        get_module = (zend_module_entry *(*)(void)) DL_FETCH_SYMBOL(handle, "get_module");/* 获取动态链接库内部实现的get_module函数的指针供下面调用  */

        /* Some OS prepend _ to symbol names while their dynamic linker
         * does not do that automatically. Thus we check manually for
         * _get_module. 
    */

        if (!get_module) {
            get_module = (zend_module_entry *(*)(void)) DL_FETCH_SYMBOL(handle, "_get_module");
        }

        if (!get_module) { /* 如果动态链接库没有实现get_module函数,或者函数原型与前面定义的不符,则不是合法的PHP扩展 */
            DL_UNLOAD(handle);
            php_error_docref(NULL TSRMLS_CC, error_type, "Invalid library (maybe not a PHP library) '%s'", filename);
            return FAILURE;
        }
        module_entry = get_module();/* 执行get_module函数,获取zend_module_entry的信息,这里是个指针 */
        
        module_entry->type = type;
        module_entry->module_number = zend_next_free_module();
        module_entry->handle = handle;

        if ((module_entry = zend_register_module_ex(module_entry TSRMLS_CC)) == NULL) {  /* 这里完成三个任务,第一判断此扩展依赖的其它扩展是否都已经正确加载,第二将扩展的module_entry添加到全局的module_registry这个HashTable中,第三,注册扩展提供的所有函数 */
            DL_UNLOAD(handle);
            return FAILURE;
        }

        if ((type == MODULE_TEMPORARY || start_now) && zend_startup_module_ex(module_entry TSRMLS_CC) == FAILURE) { /* 执行module_entry的MINIT函数 */
            DL_UNLOAD(handle);
            return FAILURE;
        }

        if ((type == MODULE_TEMPORARY || start_now) && module_entry->request_startup_func) {
            if (module_entry->request_startup_func(type, module_entry->module_number TSRMLS_CC) == FAILURE) { /* 执行module_entry的RINIT函数 */
                php_error_docref(NULL TSRMLS_CC, error_type, "Unable to initialize module '%s'", module_entry->name);
                DL_UNLOAD(handle);
                return FAILURE;
            }
        }
        return SUCCESS;

  • 相关阅读:
    2011年10月小记
    修改模拟器hosts文件
    2011年9月小记
    解决IIS7.5站点不能登录SQLEXPRESS
    EF 4.3 CodeBased Migrations
    2012年5月 小记
    Android对SD卡进行读写
    Tomcat for Eclipse
    ARR2.5 配置反向代理
    作业2浅谈数组求和java实验
  • 原文地址:https://www.cnblogs.com/bqrm/p/2721440.html
Copyright © 2020-2023  润新知