目录
catalogue
1. SAPI接口
Relevant Link:
2. PHP CLI模式解释执行脚本流程
Copyright (c) 2016 LittleHann All rights reserved
首页 后端开发 php教程 PHP解释器引擎执行流程

PHP解释器引擎执行流程

Jun 23, 2016 pm 01:17 PM

catalogue

1. SAPI接口2. PHP CLI模式解释执行脚本流程3. PHP Zend Complile/Execute函数接口化(Hook Call架构基础)
登录后复制

1. SAPI接口

PHP的SAPI层实现上层接口的封装,使得PHP可以用在很多种模式场景下(例如apache、ningx、cgi、fastcgi、cli),以以cli SAPI为例子学习PHP解释器引擎是如何处理PHP用户态源代码文件的Cli(Command Line Interface)即PHP的命令行模式,现在此SAPI是默认安装的,我们在服务器上安装完PHP之后,一般会生成一个可执行文件

脚本执行的开始都是以SAPI接口实现开始的。只是不同的SAPI接口实现会完成他们特定的工作, 例如Apache的mod_php SAPI实现需要初始化从Apache获取的一些信息,在输出内容是将内容返回给Apache, 其他的SAPI实现也类似

0x1: sapi_module_struct

要定义个SAPI,首先要定义个sapi_module_structPHP-SRC/sapi/cli/php_cli.c

/* {{{ sapi_module_struct cli_sapi_module */static sapi_module_struct cli_sapi_module = {    "cli",                            /* name php_info()的时候被使用 */    "Command Line Interface",        /* pretty name */    php_cli_startup,                /* startup */    php_module_shutdown_wrapper,    /* shutdown */    NULL,                            /* activate */    sapi_cli_deactivate,            /* deactivate */    sapi_cli_ub_write,                /* unbuffered write */    sapi_cli_flush,                    /* flush */    NULL,                            /* get uid */    NULL,                            /* getenv */    php_error,                        /* error handler */    sapi_cli_header_handler,        /* header handler */    sapi_cli_send_headers,            /* send headers handler */    sapi_cli_send_header,            /* send header handler */    NULL,                            /* read POST data */    sapi_cli_read_cookies,          /* read Cookies */    sapi_cli_register_variables,    /* register server variables */    sapi_cli_log_message,            /* Log message */    NULL,                            /* Get request time */    NULL,                            /* Child terminate */        STANDARD_SAPI_MODULE_PROPERTIES};/* }}} */
登录后复制

这个结构,包含了一些常量,比如name, 这个会在我们调用php_info()的时候被使用。一些初始化,收尾函数,以及一些函数指针,用来告诉Zend,如何获取,和输出数据,我们在下面的流程介绍中就会逐个涉及到其中的字段

http://www.nowamagic.net/librarys/veda/detail/1285
登录后复制

2. PHP CLI模式解释执行脚本流程

0x1: Process Startup

主进程main在进行一些必要的初始化工作后,就进入SAPI的逻辑流程, 初始化的一些环境变量,这将在整个SAPI生命周期中发生作用

0x2: MINIT

进入特定的SAPI模式之后,PHP调用各个扩展的MINIT方法 \php-5.6.17\sapi\cli\php_cli.c

int main(int argc, char *argv[]){    ..    sapi_module_struct *sapi_module = &cli_sapi_module;    ..    sapi_module->ini_defaults = sapi_cli_ini_defaults;    sapi_module->php_ini_path_override = ini_path_override;    sapi_module->phpinfo_as_text = 1;    sapi_module->php_ini_ignore_cwd = 1;    sapi_startup(sapi_module);    sapi_started = 1;    ..
登录后复制

php_cli_startup

static int php_cli_startup(sapi_module_struct *sapi_module) /* {{{ */{    if (php_module_startup(sapi_module, NULL, 0)==FAILURE) {        return FAILURE;    }    return SUCCESS;}
登录后复制

PHP调用各个扩展的MINIT方法,从而使这些扩展切换到可用状态

/* {{{ php_module_startup */int php_module_startup(sapi_module_struct *sf, zend_module_entry *additional_modules, uint num_additional_modules){    ..    zend_module_entry *module;    ..    module_shutdown = 0;    module_startup = 1;    sapi_initialize_empty_request(TSRMLS_C);    sapi_activate(TSRMLS_C);    ..    /* start additional PHP extensions */    php_register_extensions_bc(additional_modules, num_additional_modules TSRMLS_CC);    /* load and startup extensions compiled as shared objects (aka DLLs)       as requested by php.ini entries       theese are loaded after initialization of internal extensions       as extensions *might* rely on things from ext/standard       which is always an internal extension and to be initialized       ahead of all other internals     */    php_ini_register_extensions(TSRMLS_C);    zend_startup_modules(TSRMLS_C);    /* start Zend extensions */    zend_startup_extensions();    ..
登录后复制

MINIT的意思是"模块初始化"。各个模块都定义了一组函数、类库等用以处理其他请求一个典型的MINIT方法如下

PHP_MINIT_FUNCTION(extension_name){ /* Initialize functions, classes etc */ }
登录后复制

0x3: RINIT

当一个页面请求发生时,SAPI层将控制权交给PHP层。于是PHP设置了用于回复本次请求所需的环境变量。同时,它还建立一个变量表,用来存放执行过程 中产生的变量名和值。PHP调用各个模块的RINIT方法,即"请求初始化"

一个经典的例子是Session模块的RINIT,如果在php.ini中 启用了Session模块,那在调用该模块的RINIT时就会初始化$_SESSION变量,并将相关内容读入

RINIT方法可以看作是一个准备过程, 在程序执行之前就会自动启动。一个典型的RINIT方法如下

PHP_RINIT_FUNCTION(extension_name) { /* Initialize session variables,pre-populate variables, redefine global variables etc */ }
登录后复制

PHP会在每个request的时候,处理一些初始化,资源分配的事务。这部分就是activate字段要定义的,从上面的结构我们可以看出,从上面cli对应的cli_sapi_module结构体来看,对于CGI来说,它并没有提供初始化处理句柄。对于mod_php来说,那就不同了,他要在apache的pool中注册资源析构函数,申请空间, 初始化环境变量,等等

0x4: SCRIPT

PHP通过php_execute_script(&file_handle TSRMLS_CC)来执行PHP的脚本 \php-5.6.17\main\main.c

/* {{{ php_execute_script */PHPAPI int php_execute_script(zend_file_handle *primary_file TSRMLS_DC){    //file_handle的类型为zend_file_handle,这个是zend对文件句柄的一个封装,里面的内容和待执行脚本相关    zend_file_handle *prepend_file_p, *append_file_p;    zend_file_handle prepend_file = {0}, append_file = {0};    ..    //php_execute_script最终是调用的zend_execute_scripts    retval = (zend_execute_scripts(ZEND_REQUIRE TSRMLS_CC, NULL, 3, prepend_file_p, primary_file, append_file_p) == SUCCESS);    ..
登录后复制

php_execute_script最终是调用的zend_execute_scripts{PHPSRC}/Zend/zend.c

//此函数具有可变参数,可以一次执行多个PHP文件ZEND_API int zend_execute_scripts(int type TSRMLS_DC, zval **retval, int file_count, ...) /* {{{ */{    ..    EG(active_op_array) = zend_compile_file(file_handle, type TSRMLS_CC);    ..    if (EG(active_op_array))     {        EG(return_value_ptr_ptr) = retval ? retval : NULL;        zend_execute(EG(active_op_array) TSRMLS_CC);        ..
登录后复制

1. compile编译过程

zend_compile_file是一个函数指针,其声明在{PHPSRC}/Zend/zend_compile.c中

ZEND_API zend_op_array *(*zend_compile_file)(zend_file_handle *file_handle, int type TSRMLS_DC);  
登录后复制

在引擎初始化的时候,会将compile_file函数的地址赋值给zend_compile_file,compile_file函数定义在{PHPSRC}/Zend/zend_language_scanner.l

//函数以zend_file_handle指针作为参数,返回一个指向zend_op_array的指针ZEND_API zend_op_array *compile_file(zend_file_handle *file_handle, int type TSRMLS_DC){    ..    //Lex词法解析过程    ..
登录后复制

2. execute执行过程(逐条执行opcode)

zend_execute也是一个函数指针(利用compile过程得到的opcode array),其声明在{PHPSRC}/Zend/zend_execute.c

ZEND_API extern void (*zend_execute)(zend_op_array *op_array TSRMLS_DC);  
登录后复制

在引擎初始化的时候,会将execute函数的地址赋值给zend_execute,execute的定义在{PHPSRC}/Zend/zend_vm_execute.h

//zend_execute以一个指向zend_op_array结构的指针作为参数,这个指针即前面zend_compile_file的返回值,zend_execute就开始执行op_array中的op code,在执行op code的过程中,就实现了PHP语言的各种功能ZEND_API void zend_execute(zend_op_array *op_array TSRMLS_DC){    if (EG(exception)) {        return;    }     zend_execute_ex(i_create_execute_data_from_op_array(op_array, 0 TSRMLS_CC) TSRMLS_CC);}
登录后复制

0x5: RSHUTDOWN

一旦页面执行完毕(无论是执行到了文件末尾还是用exit或die函数中止),PHP就会启动清理程序。它会按顺序调用各个模块的RSHUTDOWN方法。 RSHUTDOWN用以清除程序运行时产生的符号表,也就是对每个变量调用unset函数

PHP_RSHUTDOWN_FUNCTION(extension_name) { /* Do memory management, unset all variables used in the last PHP call etc */ }
登录后复制

0x6: MSHUTDOWN

最后,所有的请求都已处理完毕,SAPI也准备关闭了,PHP开始执行第二步:PHP调用每个扩展的MSHUTDOWN方法,这是各个模块最后一次释放内存的机会

PHP_MSHUTDOWN_FUNCTION(extension_name) { /* Free handlers and persistent memory etc */ }
登录后复制

/main/main.c

/* {{{ php_module_shutdown_wrapper */int php_module_shutdown_wrapper(sapi_module_struct *sapi_globals){    TSRMLS_FETCH();    php_module_shutdown(TSRMLS_C);    return SUCCESS;}
登录后复制
http://www.nowamagic.net/librarys/veda/detail/1286http://www.nowamagic.net/librarys/veda/detail/1322http://www.nowamagic.net/librarys/veda/detail/1323http://www.nowamagic.net/librarys/veda/detail/1332http://blog.csdn.net/phpkernel/article/details/5716342http://www.nowamagic.net/librarys/veda/detail/1287http://www.nowamagic.net/librarys/veda/detail/1289
登录后复制

3. PHP Zend Complile/Execute函数接口化(Hook Call架构基础)

PHP内核在设计架构实现的时候,除了提供了扩展机制,还在Zend的两个关键流程(compile、execute)提供了Hook机制,PHP扩展开发人员可以Hook劫持Zend的编译/解释执行流程,在Zend编译执行之前先执行自定义的代码逻辑,然后再交还控制权给Zend。在引擎初始化(zend_startup)的时候

1. end_execute指向了默认的execute2. zend_compile_file指向了默认的compile_file
登录后复制

我们可以在实际编译和执行之前(RINIT阶段中)将zend_execute和zend_compile_file重写为其他的编译和执行函数,这样就为我们扩展引擎留下了钩子,比如一个比较有名的查看PHP的op code的扩展vld,此扩展就是在每次请求初始化的钩子函数(PHP_RINIT_FUNCTION)中,将zend_execute和zend_compile_file替换成自己的vld_execute和vld_compile_file,这两个函数其实是对原始函数进行了封装,添加了输出opcode信息的附加功能,因为引擎初始化是发生在模块请求初始化之前,而模块请求初始化又是在编译和执行之前,所以这样的覆盖能达到目的

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热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.能量晶体解释及其做什么(黄色晶体)
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解锁Myrise中的所有内容
3 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

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

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

在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的实用应用和潜在的触摸

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

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

框架安全功能:防止漏洞。 框架安全功能:防止漏洞。 Mar 28, 2025 pm 05:11 PM

文章讨论了框架中的基本安全功能,以防止漏洞,包括输入验证,身份验证和常规更新。

See all articles