【懂了之后再看】PHP解释器引擎执行流
来源:互联网 发布:电商大数据应用案例 编辑:程序博客网 时间:2024/04/29 07:36
这里将介绍引擎内部执行一个PHP脚本的流程,以cli SAPI为例子来对流程中核心的部分做简单介绍,省去一些初始化及清理操作。
cli(Command Line Interface)即PHP的命令行模式,现在此SAPI是默认安装的,我们在服务器上安装完PHP之后,一般会生成一个可执行文件,假设此文件为 /usr/local/bin/php ,那么我们在SHELL下可以用以下命令来执行一个PHP脚本:
/usr/local/bin/php -f test.php
这个命令将执行当前目录下的test.php脚本,我们暂且不关心test.php具体内容,只关心一下这个执行的内部过程是怎么样的。
cli的主源代码文件在{PHPSRC}/sapi/cli/php_cli.c,整个过程就从这个文件中的 main()函数执行,整个函数比较长,主要可以分为以下几个阶段:
1:解析命令行参数
2:初始化环境
3:编译执行PHP代码
4:清理环境并返回退出
在第1个阶段中,解析-f参数为执行一个PHP文件,-f后面的test.php就是需要被执行的文件
这里我们将关注第3个阶段,如何执行test.php中的PHP代码。
最终是通过php_execute_script(&file_handle TSRMLS_CC)来执行PHP的脚本,这个函数定义在{PHPSRC}/main/main.c,原型为
- PHPAPI int php_execute_script(zend_file_handle *primary_file TSRMLS_DC)
file_handle的类型为zend_file_handle,这个是zend对文件句柄的一个封装,里面的内容就是和test.php相关的了。
php_execute_script最终是调用的zend_execute_scripts,这个函数定义在{PHPSRC}/Zend/zend.c,原型为:
- ZEND_API int zend_execute_scripts(int type TSRMLS_DC, zval **retval, int file_count, ...)
此函数具有可变参数,可以一次执行多个PHP文件,在此函数中最核心的是调用zend_compile_file和zend_execute,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.c,通过声明可以看到这个函数以zend_file_handle指针作为参 数,返回一个指向zend_op_array的指针。
zend_execute也是一个函数指针,其声明在{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语言的各种功能。
到这里主要的执行工作基本就完成。
PS:为什么要把zend_execute和zend_compile_file定义为函数指针?
在引擎初始化(zend_startup)的时候,将zend_execute指向了默认的execute,zend_compile_file指向了默 认的compile_file。我们可以在实际编译和执行之前将zend_execute和zend_compile_file重写为其他的编译和执行函 数,这样就为我们扩展引擎留下了钩子,比如一个比较有名的查看PHP的op code的扩展vld(http://www.derickrethans.nl/projects.html#vld), 此扩展就是在每次请求初始化的钩子函数(PHP_RINIT_FUNCTION)中,将zend_execute和zend_compile_file替 换成自己的vld_execute和vld_compile_file,这两个函数其实是对原始函数进行了封装,添加了输出opcode信息的附加功能, 因为引擎初始化是发生在模块请求初始化之前,而模块请求初始化又是在编译和执行之前,所以这样的覆盖能达到目的。
- 【懂了之后再看】PHP解释器引擎执行流
- PHP解释器引擎执行流程
- PHP解释器引擎执行流程
- PHP解释器引擎执行流程
- PHP内核之PHP解释器引擎执行流程
- 卖了5个月水果之后再看互联网思维
- 十年之后再看“面向对象”
- 十年之后再看“面向对象”
- 17.PHP内核探索:解释器的执行过程
- php内核探索笔记-解释器的执行过程
- PHP内核探索:解释器的执行过程
- java虚拟机字节码执行引擎之解释执行
- 深入Java虚拟机之基于栈指令的解释器执行引擎
- 有心情了再看
- 基于栈的字节码解释执行引擎
- 如何解决new了之后因为执行流跳转而导致的内存泄漏的问题
- hdu 1562 两个月之后再看搜索
- 再看《你尽力了吗》
- .NET Web开发技术简单整理
- 算法学习之排序(4)--Shell排序
- Windows下将Spring源码导入eclipse/sts
- ArcGIS Engine代码共享-工作空间(workspace)对象操作
- Git下载Spring项目源码并编译为Eclipse
- 【懂了之后再看】PHP解释器引擎执行流
- HDU(3567):八数码问题(升级版)——双BFS
- 麦森数
- B. Drazil and His Happy Friends
- 黑马程序员————IOS学习笔记 第8篇 内存管理优化
- Ubuntu下管理启动服务
- T-SQL WITH AS
- 详解SQL Server 2014内存OLTP技术架构
- 书签、工具、软件