Maison >développement back-end >Problème PHP >Comment personnaliser la fonction de hook de l'extension PHP (2)

Comment personnaliser la fonction de hook de l'extension PHP (2)

藏色散人
藏色散人avant
2021-12-20 15:51:322121parcourir

Dans la continuité de l'article précédent parlons du cycle de vie de php et voyons quels hooks sont étendus pour faire quoi. Le cycle de vie de php comporte environ 5 étapes, l'étape d'initialisation du module php_module_startup code>, phase d'initialisation de la demande <code>php_request_startup, phase d'exécution du script php_execute_script, phase d'arrêt de la demande php_request_shutdown, phase d'arrêt du module php_module_shutdown , ce qui suit est introduit en mode cli. 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_module_startup

Jetez d'abord un œil à ce qui est fait à ce stade. Si vous ne savez pas où se trouve le fichier d'entrée php, utilisez gdb pour regarder la pile d'appels gdb ./phpcode>

Dans php_module_startup Point d'arrêt, exécutez, regardez la pile d'appels,

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);
Vous pouvez voir clairement le processus d'exécution dans la pile d'appels, allez maintenant dans le fichier /main/main.c pour voir ce qui a été fait, vous pouvez également utiliser gdb pour étape. Prenons une vue étape par étape. Voici quelques éléments liés aux extensions PHP effectuées ici, telles que le garbage collection, l'initialisation des requêtes, les constantes d'enregistrement. , chargement du fichier de configuration php.ini, etc.

Voyons d'abord comment charger les modules 🎜rrreee🎜Voici le chargement du module intégré de php. Seules les fonctions principales sont publiées ici. rrreeerrreee🎜C'est le principe du chargement du module intégré. Voyons maintenant comment charger l'extension dans ini🎜rrreeerrreee🎜Utilisez cette fonction pour charger🎜rrreee🎜 La fonction de chargement des modules intégrés est également réalisée ici. 🎜🎜Il s'appelle module->functions pour enregistrer la fonction de fonction du module. Nous savons maintenant pourquoi la fonction de fonction doit être écrite ici dans helloworld_functions🎜rrreee🎜Maintenant, regardez. l'extension Plusieurs fonctions de hook 🎜rrreee🎜Le noyau ici est func(element->data), qui est l'extension d'exécution 🎜🎜Fonction PHP_MINIT🎜rrreee🎜Sachez-le maintenant Le hook PHP_MINIT peut exécuter de nombreuses fonctions d'initialisation. Comment enregistrer une classe de fonctions étendues personnalisée, comment écrire des variables étendues dans php.ini, comment réécrire les fonctions intégrées PHP, 🎜rrreee🎜Voici la réécriture du. La fonction var_dump enregistre une classe de personne. Nous la présenterons ici en premier. Le prochain article expliquera comment générer AST via l'analyse lexicale et l'analyse syntaxique du code PHP, puis compiler les instructions d'opcode à appeler par la machine virtuelle zend. 🎜🎜Apprentissage recommandé : "🎜Tutoriel vidéo PHP🎜"🎜🎜

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer