搜索
首页后端开发php教程深入理解PHP原理之扩展载入过程

why xdebug extension must be loaded as a zend extension?

what is zend extension and what are the differents between regular php extension and zend extension?

let’s start from that the extension loading process.

PHP是可以被扩展的, PHP的核心引擎Zend Engine也是可以被扩展的, 如果你也对Apache Module的编写也有所了解的话, 那么, 你就会对如下的结构很熟悉了:

struct _zend_extension {        char *name;        char *version;        char *author;        char *URL;        char *copyright;        startup_func_t startup;        shutdown_func_t shutdown;        activate_func_t activate;        deactivate_func_t deactivate;        message_handler_func_t message_handler;        op_array_handler_func_t op_array_handler;        statement_handler_func_t statement_handler;        fcall_begin_handler_func_t fcall_begin_handler;        fcall_end_handler_func_t fcall_end_handler;        op_array_ctor_func_t op_array_ctor;        op_array_dtor_func_t op_array_dtor;        int (*api_no_check)(int api_no);        void *reserved2;        void *reserved3;        void *reserved4;        void *reserved5;        void *reserved6;        void *reserved7;        void *reserved8;        DL_HANDLE handle;        int resource_number;};

然后, 让我们对比下PHP extension的module entry:

struct _zend_module_entry {          unsigned short size;          unsigned int zend_api;          unsigned char zend_debug;          unsigned char zts;          struct _zend_ini_entry *ini_entry;          struct _zend_module_dep *deps;          char *name;          struct _zend_function_entry *functions;          int (*module_startup_func)(INIT_FUNC_ARGS);          int (*module_shutdown_func)(SHUTDOWN_FUNC_ARGS);          int (*request_startup_func)(INIT_FUNC_ARGS);          int (*request_shutdown_func)(SHUTDOWN_FUNC_ARGS);          void (*info_func)(ZEND_MODULE_INFO_FUNC_ARGS);          char *version;          size_t globals_size;     #ifdef ZTS          ts_rsrc_id* globals_id_ptr;     #else          void* globals_ptr;     #endif          void (*globals_ctor)(void *global TSRMLS_DC);          void (*globals_dtor)(void *global TSRMLS_DC);          int (*post_deactivate_func)(void);          int module_started;          unsigned char type;          void *handle;          int module_number;     };

上面的结构, 可以结合我之前的文章用C/C++扩展你的PHP来帮助理解.

恩,回到主题:既然Xdebug要以Zend Extension方式加载, 那么它必然有基于Zend Extension的需求, 会是什么呢?

恩, 我们知道Xdebug有profile PHP的功能, 对, 就是statement_handler:
the statement handler callback inserts an additional opcode at the end of every statement in a script in which the callback is called. One of the primary uses for this sort of callback is to implement per-line profiling, stepping debuggers, or code-coverage utilities.

并且,因为Xdebug也提供了给用户脚本使用的函数, 所以, 它也会有部分PHP extension的实现, 并且由于它要以ZendExt方式载入的原因,所以它必须自己实现本身PHPExt部分的载入过程.

最后, 将PHP Extension的载入过程罗列如下(我会慢慢加上注释), 当然, 如果你等不及想知道, 也欢迎你直接在我的博客风雪之隅留言探讨.

以apache/mod_php5.c为例

1. 在mod_php5.c中,定义了Apache模块结构:

module MODULE_VAR_EXPORT php5_module =     {          STANDARD_MODULE_STUFF,          php_init_handler,           /* initializer */          php_create_dir,             /* per-directory config creator */          php_merge_dir,              /* dir merger */          NULL,                       /* per-server config creator */          NULL,                       /* merge server config */          php_commands,               /* command table */          php_handlers,               /* handlers */          NULL,                       /* filename translation */          NULL,                       /* check_user_id */          NULL,                       /* check auth */          NULL,                       /* check access */          NULL,                       /* type_checker */          NULL,                       /* fixups */          NULL                        /* logger */     #if MODULE_MAGIC_NUMBER >= 19970103          , NULL                      /* header parser */     #endif     #if MODULE_MAGIC_NUMBER >= 19970719          , NULL                      /* child_init */     #endif     #if MODULE_MAGIC_NUMBER >= 19970728          , php_child_exit_handler        /* child_exit */     #endif     #if MODULE_MAGIC_NUMBER >= 19970902          , NULL                      /* post read-request */     #endif     };/* }}} */

可见, 最开始被调用的将会是php_init_handler,

static void php_init_handler(server_rec *s, pool *p){    register_cleanup(p, NULL, (void (*)(void *))apache_php_module_shutdown_wrapper, (void (*)(void *))php_module_shutdown_for_exec);    if (!apache_php_initialized) {        apache_php_initialized = 1;#ifdef ZTS        tsrm_startup(1, 1, 0, NULL);#endif        sapi_startup(&apache_sapi_module);        php_apache_startup(&apache_sapi_module);    }#if MODULE_MAGIC_NUMBER >= 19980527    {        TSRMLS_FETCH();        if (PG(expose_php)) {            ap_add_version_component("PHP/" PHP_VERSION);        }    }#endif}

这里, 调用了sapi_startup, 这部分是初始化php的apache sapi,
然后是调用,php_apache_startup:

static int php_apache_startup(sapi_module_struct *sapi_module){    if (php_module_startup(sapi_module, &apache_module_entry, 1) == FAILURE) {        return FAILURE;    } else {        return SUCCESS;    }}

这个时候,调用了php_module_startup, 其中有:

/* this will read in php.ini, set up the configuration parameters,       load zend extensions and register php function extensions       to be loaded later */    if (php_init_config(TSRMLS_C) == FAILURE) {        return FAILURE;    }

调用了php_init_config, 这部分读取所有的php.ini和关联的ini文件, 然后对于每一条配置指令调用:

.... if (sapi_module.ini_entries) {        zend_parse_ini_string(sapi_module.ini_entries, 1, php_config_ini_parser_cb, &extension_lists);    }然后在php_config_ini_parser_cb中:               if (!strcasecmp(Z_STRVAL_P(arg1), "extension")) { /* load function module */                    zval copy;                     copy = *arg2;                    zval_copy_ctor(&copy);                    copy.refcount = 0;                    zend_llist_add_element(&extension_lists.functions, &copy);                } else if (!strcasecmp(Z_STRVAL_P(arg1), ZEND_EXTENSION_TOKEN)) { /* load Zend extension */                    char *extension_name = estrndup(Z_STRVAL_P(arg2), Z_STRLEN_P(arg2));                     zend_llist_add_element(&extension_lists.engine, &extension_name);                } else {                    zend_hash_update(&configuration_hash, Z_STRVAL_P(arg1), Z_STRLEN_P(arg1) + 1, arg2, sizeof(zval), (void **) &entry);                    Z_STRVAL_P(entry) = zend_strndup(Z_STRVAL_P(entry), Z_STRLEN_P(entry));                }

这里记录下来所有要载入的php extension和zend extension,
然后, 让我们回到php_module_startup, 后面有调用到了
php_ini_register_extensions(TSRMLS_C);

void php_ini_register_extensions(TSRMLS_D){    zend_llist_apply(&extension_lists.engine, php_load_zend_extension_cb TSRMLS_CC);    zend_llist_apply(&extension_lists.functions, php_load_function_extension_cb TSRMLS_CC);     zend_llist_destroy(&extension_lists.engine);    zend_llist_destroy(&extension_lists.functions);}

我们可以看到, 对于每一个扩展记录, 都调用了一个回叫函数, 我们这里只看php_load_function_extension_cb:

static void php_load_function_extension_cb(void *arg TSRMLS_DC){    zval *extension = (zval *) arg;    zval zval;     php_dl(extension, MODULE_PERSISTENT, &zval, 0 TSRMLS_CC);}

最后, 就是核心的载入逻辑了:

void php_dl(zval *file, int type, zval *return_value, int start_now TSRMLS_DC){        void *handle;        char *libpath;        zend_module_entry *module_entry;        zend_module_entry *(*get_module)(void);        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;        }         if (extension_dir && extension_dir[0]){                int extension_dir_len = strlen(extension_dir);                 if (type == MODULE_TEMPORARY) {                        if (strchr(Z_STRVAL_P(file), '/') != NULL || strchr(Z_STRVAL_P(file), DEFAULT_SLASH) != NULL) {                                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Temporary module name should contain only filename");                                RETURN_FALSE;                        }                }                 if (IS_SLASH(extension_dir[extension_dir_len-1])) {                        spprintf(&libpath, 0, "%s%s", extension_dir, Z_STRVAL_P(file));                } else {                        spprintf(&libpath, 0, "%s%c%s", extension_dir, DEFAULT_SLASH, Z_STRVAL_P(file));                }        } else {                libpath = estrndup(Z_STRVAL_P(file), Z_STRLEN_P(file));        }         /* 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_FALSE;        }         efree(libpath);         get_module = (zend_module_entry *(*)(void)) DL_FETCH_SYMBOL(handle, "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) {                DL_UNLOAD(handle);                php_error_docref(NULL TSRMLS_CC, error_type, "Invalid library (maybe not a PHP library) '%s' ", Z_STRVAL_P(file));                RETURN_FALSE;        }        module_entry = get_module();        if ((module_entry->zend_debug != ZEND_DEBUG) || (module_entry->zts != USING_ZTS)                || (module_entry->zend_api != ZEND_MODULE_API_NO)) {                /* Check for pre-4.1.0 module which has a slightly different module_entry structure :(  */                        struct pre_4_1_0_module_entry {                                  char *name;                                  zend_function_entry *functions;                                  int (*module_startup_func)(INIT_FUNC_ARGS);                                  int (*module_shutdown_func)(SHUTDOWN_FUNC_ARGS);                                  int (*request_startup_func)(INIT_FUNC_ARGS);                                  int (*request_shutdown_func)(SHUTDOWN_FUNC_ARGS);                                  void (*info_func)(ZEND_MODULE_INFO_FUNC_ARGS);                                  int (*global_startup_func)(void);                                  int (*global_shutdown_func)(void);                                  int globals_id;                                  int module_started;                                  unsigned char type;                                  void *handle;                                  int module_number;                                  unsigned char zend_debug;                                  unsigned char zts;                                  unsigned int zend_api;                        };                         char *name;                        int zend_api;                        unsigned char zend_debug, zts;                         if ((((struct pre_4_1_0_module_entry *)module_entry)->zend_api > 20000000) &&                                (((struct pre_4_1_0_module_entry *)module_entry)->zend_api < 20010901)                        ) {                                name       = ((struct pre_4_1_0_module_entry *)module_entry)->name;                                zend_api   = ((struct pre_4_1_0_module_entry *)module_entry)->zend_api;                                zend_debug = ((struct pre_4_1_0_module_entry *)module_entry)->zend_debug;                                zts        = ((struct pre_4_1_0_module_entry *)module_entry)->zts;                        } else {                                name       = module_entry->name;                                zend_api   = module_entry->zend_api;                                zend_debug = module_entry->zend_debug;                                zts        = module_entry->zts;                        }                         php_error_docref(NULL TSRMLS_CC, error_type,                                          "%s: Unable to initialize module\n"                                          "Module compiled with module API=%d, debug=%d, thread-safety=%d\n"                                          "PHP    compiled with module API=%d, debug=%d, thread-safety=%d\n"                                          "These options need to match\n",                                          name, zend_api, zend_debug, zts,                                          ZEND_MODULE_API_NO, ZEND_DEBUG, USING_ZTS);                        DL_UNLOAD(handle);                        RETURN_FALSE;        }        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) {                DL_UNLOAD(handle);                RETURN_FALSE;        }         if ((type == MODULE_TEMPORARY || start_now) && zend_startup_module_ex(module_entry TSRMLS_CC) == FAILURE) {                DL_UNLOAD(handle);                RETURN_FALSE;        }         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) {                        php_error_docref(NULL TSRMLS_CC, error_type, "Unable to initialize module '%s'", module_entry->name);                        DL_UNLOAD(handle);                        RETURN_FALSE;                }        }        RETURN_TRUE;}

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
在Laravel中使用Flash会话数据在Laravel中使用Flash会话数据Mar 12, 2025 pm 05:08 PM

Laravel使用其直观的闪存方法简化了处理临时会话数据。这非常适合在您的应用程序中显示简短的消息,警报或通知。 默认情况下,数据仅针对后续请求: $请求 -

php中的卷曲:如何在REST API中使用PHP卷曲扩展php中的卷曲:如何在REST API中使用PHP卷曲扩展Mar 14, 2025 am 11:42 AM

PHP客户端URL(curl)扩展是开发人员的强大工具,可以与远程服务器和REST API无缝交互。通过利用Libcurl(备受尊敬的多协议文件传输库),PHP curl促进了有效的执行

简化的HTTP响应在Laravel测试中模拟了简化的HTTP响应在Laravel测试中模拟了Mar 12, 2025 pm 05:09 PM

Laravel 提供简洁的 HTTP 响应模拟语法,简化了 HTTP 交互测试。这种方法显着减少了代码冗余,同时使您的测试模拟更直观。 基本实现提供了多种响应类型快捷方式: use Illuminate\Support\Facades\Http; Http::fake([ 'google.com' => 'Hello World', 'github.com' => ['foo' => 'bar'], 'forge.laravel.com' =>

在Codecanyon上的12个最佳PHP聊天脚本在Codecanyon上的12个最佳PHP聊天脚本Mar 13, 2025 pm 12:08 PM

您是否想为客户最紧迫的问题提供实时的即时解决方案? 实时聊天使您可以与客户进行实时对话,并立即解决他们的问题。它允许您为您的自定义提供更快的服务

解释PHP中晚期静态结合的概念。解释PHP中晚期静态结合的概念。Mar 21, 2025 pm 01:33 PM

文章讨论了PHP 5.3中引入的PHP中的晚期静态结合(LSB),从而允许静态方法的运行时分辨率调用以获得更灵活的继承。 LSB的实用应用和潜在的触摸

如何注册和使用Laravel服务提供商如何注册和使用Laravel服务提供商Mar 07, 2025 am 01:18 AM

Laravel的服务容器和服务提供商是其架构的基础。 本文探讨了服务容器,详细信息服务提供商创建,注册,并通过示例演示了实际用法。 我们将从OVE开始

PHP记录:PHP日志分析的最佳实践PHP记录:PHP日志分析的最佳实践Mar 10, 2025 pm 02:32 PM

PHP日志记录对于监视和调试Web应用程序以及捕获关键事件,错误和运行时行为至关重要。它为系统性能提供了宝贵的见解,有助于识别问题并支持更快的故障排除

自定义/扩展框架:如何添加自定义功能。自定义/扩展框架:如何添加自定义功能。Mar 28, 2025 pm 05:12 PM

本文讨论了将自定义功能添加到框架上,专注于理解体系结构,识别扩展点以及集成和调试的最佳实践。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
2 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
2 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
2 周前By尊渡假赌尊渡假赌尊渡假赌

热工具

EditPlus 中文破解版

EditPlus 中文破解版

体积小,语法高亮,不支持代码提示功能

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

功能强大的PHP集成开发环境

安全考试浏览器

安全考试浏览器

Safe Exam Browser是一个安全的浏览器环境,用于安全地进行在线考试。该软件将任何计算机变成一个安全的工作站。它控制对任何实用工具的访问,并防止学生使用未经授权的资源。

Dreamweaver Mac版

Dreamweaver Mac版

视觉化网页开发工具

VSCode Windows 64位 下载

VSCode Windows 64位 下载

微软推出的免费、功能强大的一款IDE编辑器