なぜ xdebug 拡張機能を zend 拡張機能としてロードする必要があるのですか?
zend 拡張機能とは何ですか? 通常の php 拡張機能と zend 拡張機能の違いは何ですか?
拡張機能の読み込みから始めましょう。
PHP は拡張でき、PHP のコア エンジンである Zend Engine も拡張できます。Apache モジュールの作成についても知識がある場合は、次の構造に精通しているでしょう:
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 拡張機能のモジュール エントリを比較してみましょう:
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 にはプロファイル PHP 関数があることはわかっています。そうです、それは、statement_handler:
ステートメント ハンドラー コールバックは、すべてのステートメントの最後に追加のオペコードを挿入します。この種のコールバックの主な用途の 1 つは、行ごとのプロファイリング、ステッピング デバッガー、またはコード カバレッジ ユーティリティを実装することです。また、Xdebug には
も用意されています。ユーザー スクリプトの関数であるため、PHP 拡張機能の一部の実装も含まれます。また、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 が呼び出されます。この部分は Apache を初期化するためのものです。 php 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.refcount = 0; zend_llist_add_element(&extension_lists.functions, ©); } 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 拡張機能と zend 拡張機能をここに記録します。
次に、後で呼び出される 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);}
最後に、コアの読み込みロジックがあります:
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);}