3.1.1 PHP模块的启动过程
一个PHP脚本文件在Zend Engine(虚拟机)中的执行,主要分为以下几个过程:
- 词法分析 - Scanner:Zend Engine调用词法分析器(Lex生成,源码见:https://github.com/php/php-src/blob/master/Zend/zend_language_scanner.l ),将待执行的php脚本源文件,去掉空格、注释,分割成许多个的token。
- 语法分析 - Parser:Zend Engine调用语法分析器(Yacc生成,源码见:https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y ),将token转换为许多个op code,它是PHP的中间语言。
- 执行:Zend Engine调用zend_excutor来执行op code,输出结果。
而在PHP脚本执行之前,PHP解释器需要接受来自web服务器(如Apache)的脚本执行请求;且在PHP脚本执行完成之后,PHP解释器需要将执行结果发送(响应)至web服务器,这个过程是通过SAPI(Server abstarct API)接口实现的。
SAPI
SAPI,Server abstract API(A API is A Contract),即面向Server的接口,它是一种机制,控制着PHP/Zend Engine内部与外部服务器『世界』的交互。
实际上,php脚本执行的开始都是以实现SAPI接口为开始的,只是不同类型的SAPI,实现不同类型的模块任务。即只要使用PHP就会用到它,即使是在CLI模式,也被认为是一种SAPI,只是在实现的时候只是某些函数接口是空的(无需实现),如send header请求。下面是PHP官网实现的SAPI类型:
- apache2handler:实现了Aapache的mod_php模块,即PHP解释器作为Apache服务器的一个内置模块(Apache配置http.conf:LoadModule php7_module libexec/apache2/libphp7.so),让Apache本身支持解释执行PHP文件,无需每一个请求都要启动mod_php模块,而是每一次请求,服务器均发起的一个Apache进程(启动Apache进程时默认启动PHP模块)。
- cgi:Common Gateway Interface,通用网关接口,它定义了PHP与web服务器的交互方式。Web服务器充当了应用程序(CGI程序/脚本)的网关。它定义了一套规范,用于描述web server与应用程序之间的通信过程。如通过哪种机制传递数据([env var->请求元数据] + [stdin(默认)->请求体] + [响应->stdout(默认)](CGI接口的输入与输出-简单理想情况:client request -> stdin->cgi->stdout->web server)),数据的格式又是怎样的,异常如何处理等等,具体可详见RFC3875。目前PHP7官方实现的是fastcgi模式。
- cli:命令行模式执行PHP。如*nix操作系统环境下,输入'/usr/loca/php/bin/php'命令。
- embed:实现了嵌入式系统下执行PHP。
- fpm:fastcgi process manager,它是PHP FastCGI的有一种实现,特别适用于高负载的站点。
- litespeed:是一款商业版的兼容Apache的web服务器软件(也有对应的开源版本- OpenLiteSpeed)。litespeed官方号称是高性能Apache的替换(drop-in replacement)方案。
- phpdbg:PHP调试器也作为一种SAPI方式实现。它可以完全控制代码环境而不对代码的功能和性能产生影响。
以上各种PHP的运行交互模式,可根据具体业务需求选择配置最合适的SAPI类型。
PHP Startup
php脚本的执行始于SAPI(接受执行请求),终于SAPI(返回执行结果),其整个生命周期的执行流程可大致如下图所示:
PHP是以模块的组织形式扩展功能的,每一个加入PHP扩展的模块均需要进行PHP Core阶段的四个子过程的流转:
- MINIT:Module Initialization,模块初始化阶段。PHP将搜索php.ini配置文件中enable的模块,然后分别调用这些模块的MINIT方法,做一些模块初始化的工作,如注册常量、定义模块使用的类等,该阶段在整个SAPI生命周期内只执行一次。
- RINIT:Request Initialization,客户端请求初始化阶段。当请求到达时,程序的控制权已由SAPI层转移至PHP Core层。该阶段发生在请求脚本执行之前,PHP Core将做三件事情:1)创建一个脚本可执行环境(用来执行客户端发起的php请求);2)创建一张符号表(用来存储执行该请求脚本期间需要的变量);3)调用模块的RINIT方法,如初始化模块的某些全局变量等。
- RSHUTDOWM:Request Shutdown,Request请求结束阶段,该阶段发生在请求脚本执行完成之后,PHP Core将调用每个模块的RSHUTDOWN方法,做一些清理工作。如清除模块的全局变量,销毁符号表等。
- MSHUTDOWM:Module Shutdown,卸载模块阶段,当SAPI生命周期结束时(如web服务器异常退出或命令行脚本执行完成)将执行MSHUTDOWM方法,做一些模块清理工作。如注销注册过的handler,释放申请过的内存空间等。
References
1、http://www.laruence.com/2008/08/11/147.html
2、http://laruence-wordpress.stor.sinaapp.com/uploads/the-php-life-cycle.pdf
3、http://stackoverflow.com/questions/9948008/what-is-sapi-and-when-would-you-use-it
4、http://www.php-internals.com/book/?p=chapt02/02-01-php-life-cycle-and-zend-engine
5、http://www.laruence.com/2008/08/12/180.html
6、https://github.com/php/php-src/tree/master/sapi
7、http://stackoverflow.com/questions/2712825/what-is-mod-php
8、https://www.ietf.org/rfc/rfc3875.txt
9、http://php.net/manual/en/install.fpm.php
10、https://segmentfault.com/q/1010000000256516
11、http://www.php-internals.com/book/?p=chapt02/02-01-php-life-cycle-and-zend-engine
12、http://abhinavsingh.com/how-does-php-echos-a-hello-world-behind-the-scene/
13、http://www.slideshare.net/ericzhangcn/php-and-zend-internal-i