>  기사  >  백엔드 개발  >  PHP 확장 기능을 사용자 정의하는 방법(2) 후크 기능

PHP 확장 기능을 사용자 정의하는 방법(2) 후크 기능

藏色散人
藏色散人앞으로
2021-12-20 15:51:322082검색

이전 기사에 이어 PHP 수명 주기에 대해 이야기하고 어떤 후크가 무엇을 수행하기 위해 확장되는지 살펴보겠습니다. PHP 수명 주기는 모듈 초기화 단계 와 같이 약 5단계로 구성됩니다. >php_module_startup code>, 요청 초기화 단계 php_request_startup, 스크립트 실행 단계 php_execute_script, 요청 종료 단계 php_request_shutdown, 모듈 종료 단계 php_module_shutdown , 다음은 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

먼저 이 단계에서 수행되는 작업을 살펴보세요. PHP 항목 파일이 어디에 있는지 모르면 gdb를 사용하여 gdb ./php를 살펴보세요. code>

php_module_startup에서 중단점, 실행, 콜 스택 살펴보기,

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);
콜 스택에서 실행 과정을 명확하게 볼 수 있으니 이제 /main/main.c 파일로 이동하세요. 수행된 작업을 보려면 gdb를 사용하여 단계별로 살펴보겠습니다. 다음은 가비지 수집, 요청 초기화, 등록 상수와 같은 PHP 확장과 관련된 몇 가지 작업입니다. , php.ini 구성 파일 로드 등

먼저 모듈을 로드하는 방법을 살펴보겠습니다. 🎜rrreee🎜여기에는 php에 내장된 모듈이 로드됩니다. 먼저 종속성을 확인하세요. rrreeerrreee🎜이것이 내장 모듈을 로드하는 원리입니다. 이제 ini에서 확장을 로드하는 방법을 살펴보겠습니다.🎜rrreeerrreee🎜이 함수를 사용하여 로드🎜rrreee🎜 내장 모듈을 로드하는 기능도 여기서 수행됩니다. 🎜🎜모듈 함수 함수를 등록하는 것을 module->functions라고 합니다. 이제 여기 helloworld_functions에 함수 함수를 작성해야 하는 이유를 알 수 있습니다.🎜rrreee🎜이제 보세요. 확장 여러 후크 함수 🎜rrreee🎜여기서 핵심은 실행 확장인 func(element->data)입니다. 🎜🎜PHP_MINIT 함수🎜rrreee🎜지금 알아보세요 PHP_MINIT 후크는 다양한 초기화 기능을 수행할 수 있습니다. 사용자 정의 확장 함수 클래스를 등록하는 방법, php.ini에 확장 변수를 작성하는 방법, PHP 내장 함수를 다시 작성하는 방법, 🎜rrreee🎜여기에 다시 작성하는 내용이 있습니다. var_dump 함수는 person 클래스를 등록하는데, 여기서 먼저 소개하겠습니다. 다음 글에서는 PHP 코드의 어휘 분석과 구문 분석을 통해 AST를 생성한 후 zend 가상 머신이 호출할 opcode 명령어를 컴파일하는 방법을 소개하겠습니다. 🎜🎜추천 학습: "🎜PHP 비디오 튜토리얼🎜"🎜🎜

위 내용은 PHP 확장 기능을 사용자 정의하는 방법(2) 후크 기능의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 segmentfault.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제