1、PHP的运行模式. v# g2 Q2 t" i B
PHP两种运行模式是WEB模式、CLI模式。无论哪种模式,PHP工作原理都是一样的,作为一种SAPI运行。5 I0 H; q0 e5 ~/ }% R. j
1、当我们在终端敲入php这个命令的时候,它使用的是CLI。3 D P* _$ B1 {2 ]7 v- E% Z }' u$ Y
它就像一个web服务器一样来支持php完成这个请求,请求完成后再重新把控制权交给终端。 * L D _3 _, r2、当使用Apache或者别web服务器作为宿主时,当一个请求到来时,PHP会来支持完成这个请求。一般有: Q8 L. @ x3 v2 m$ s
多进程(通常编译为apache的模块来处理PHP请求)
多线程模式2 F6 L& o! B/ c# p0 `
2、一切的开始: SAPI接口$ e1 D- `- K& i- f: o
通常我们编写php Web程序都是通过Apache或者Nginx这类Web服务器来测试脚本. 或者在命令行下通过php程序来执行PHP脚本. 执行完成脚本后,服务器应答,浏览器显示应答信息,或者在命令结束后在标准输出显示内容. 我们很少关心PHP解释器在哪里. 虽然通过Web服务器和命令行程序执行脚本看起来很不一样. 实际上她们的工作是一样的. 命令行程序和Web程序类似, 命令行参数传递给要执行的脚本,相当于通过url 请求一个PHP页面. 脚本戳里完成后返回响应结果,只不过命令行响应的结果是显示在终端上. 脚本执行的开始都是通过SAPI接口进行的. ( H5 O3 E5 r) J3 w6 S 启动apache& w9 M3 x7 f/ V' s) b$ `4 W8 W9 L
当给定的SAPI启动时,例如在对/usr/local/apache/bin/apachectl start的响应中,PHP由初始化其内核子系统开始。在接近启动例程的末尾,它加载每个扩展的代码并调用其模块初始化例程(MINIT)。这使得每个扩展可以初始化内部变量、分配资源、注册资源处理器,以及向ZE注册自己的函数,以便于脚本调用这其中的函数时候ZE知道执行哪些代码。+ E1 E$ m2 H! o& n& e 请求处理初始化 @' P8 `8 z1 T# Z, F+ K) A 接下来,PHP等待SAPI层请求要处理的页面。对于CGI或CLI等SAPI,这将立刻发生且只发生一次。对于Apache、IIS或其他成熟的web服务器SAPI,每次远程用户请求页面时都将发生,因此重复很多次,也可能并发。不管请求如何产生,PHP开始于要求ZE建立脚本的运行环境,然后调用每个扩展的请求初始化 (RINIT)函数。RINIT使得扩展有机会设定特定的环境变量,根据请求分配资源,或者执行其他任务,如审核。 session扩展中有个RINIT作用的典型示例,如果启用了session.auto_start选项,RINIT将自动触发用户空间的session_start()函数以及预组装$_SESSION变量。* \* i5 O; q& r% K2 q 执行php代码 . l% ?3 w# j4 L4 W' }7 z2 C; U 一旦请求被初始化了,ZE开始接管控制权,将PHP脚本翻译成符号,最终形成操作码并逐步运行之。如任一操作码需要调用扩展的函数,ZE将会把参数绑定到该函数,并且临时交出控制权直到函数运行结束。4 y ]: n/ }4 s1 C; R- u! u 脚本结束 % s$ O& c2 s8 V$ W. D 脚本运行结束后,PHP调用每个扩展的请求关闭(RSHUTDOWN)函数以执行最后的清理工作(如将session变量存入磁盘)。接下来,ZE执行清理过程(垃圾收集)-有效地对之前的请求期间用到的每个变量执行unset()。 5 H3 Y/ H: n6 D: \. I0 @sapi关闭/ v" Y, K: C l7 i9 n& f% N
一旦完成,PHP继续等待SAPI的其他文档请求或者是关闭信号。对于CGI和CLI等SAPI,没有“下一个请求”,所以SAPI立刻开始关闭。关闭期间,PHP再次遍历每个扩展,调用其模块关闭(MSHUTDOWN)函数,并最终关闭自己的内核子系统。* R4 m9 h" N" v6 \/ m
简要的过程如下: 7 T3 ^1 ^ o! w: \+ C: o- v1. PHP是随着Apache的启动而运行的;1 h3 i) k) j( M( J8 U# f
2. PHP通过mod_php5.so模块和Apache相连(具体说来是SAPI,即服务器应用程序编程接口);- ^+ ^; b8 j6 z
3. PHP总共有三个模块:内核、Zend引擎、以及扩展层;- f5 |$ ~( Y, z1 S. s( f
4. PHP内核用来处理请求、文件流、错误处理等相关操作; ) ]9 ^2 d0 u. T3 v5. Zend引擎(ZE)用以将源文件转换成机器语言,然后在虚拟机上运行它; $ U, P- u5 U8 t6. 扩展层是一组函数、类库和流,PHP使用它们来执行一些特定的操作。比如,我们需要mysql扩展来连接MySQL数据库;! t7 D+ ^- e t3 G7 C2 u, S( r
7. 当ZE执行程序时可能会需要连接若干扩展,这时ZE将控制权交给扩展,等处理完特定任务后再返还;) f7 E! K3 c) I. x
8. 最后,ZE将程序运行结果返回给PHP内核,它再将结果传送给SAPI层,最终输出到浏览器上。' j+ T5 M. v Z0 M
3、PHP的开始和结束阶段8 z/ d3 C) V# A) G/ S# r) h H: }
开始阶段有两个过程: 1 P. F6 y C$ P9 |5 G a1 ^6 ~ 第一个过程:apache启动的过程,即在任何请求到达之前就发生。是在整个SAPI生命周期内(例如Apache启动以后的整个生命周期内或者命令行程序整个执行过程中)的开始阶段(MINIT),该阶段只进行一次.。启动Apache后,PHP解释程序也随之启动; PHP调用各个扩展(模块)的MINIT方法,从而使这些扩展切换到可用状态。看看php.ini文件里打开了哪些扩展吧; MINIT的意思是“模块初始化”。各个模块都定义了一组函数、类库等用以处理其他请求。 模块在这个阶段可以进行一些初始化工作,例如注册常量, 定义模块使用的类等等.典型的的模块回调函数MINIT方法如下: 5 s/ V9 K! ^# F9 b# K
第二个过程发生在请求阶段,当一个页面请求发生时.则在每次请求之前都会进行初始化过程(RINIT请求开始)。/ F. W7 d% j6 R5 A' M9 c; c
请求到达之后,SAPI层将控制权交给PHP层,PHP初始化本次请求执行脚本所需的环境变量,例如创建一个执行环境,包括保存php运行过程中变量名称和变量值内容的符号表. 以及当前所有的函数以及类等信息的符号表.例如是Session模块的RINIT,如果在php.ini中启用了Session 模块,那在调用该模块的RINIT时就会初始化$_SESSION变量,并将相关内容读入;然后PHP会调用所有模块RINIT函数,即“请求初始化”。在这个阶段各个模块也可以执行一些相关的操作, 模块的RINIT函数和MINIT函数类似 ,RINIT方法可以看作是一个准备过程,在程序执行之间就会自动启动。 : }0 h# l; J, U- r; l g6 w. d
结束阶段分为两个环节: 请求处理完后就进入了结束阶段, 一般脚本执行到末尾或者通过调用exit()或者die()函数,PHP都将进入结束阶段. 和开始阶段对应,结束阶段也分为两个环节,一个在请求结束后(RSHUWDOWN),一个在SAPI生命周期结束时(MSHUTDOWN).5 z7 q5 q, B+ a 第一个环节:请求处理完后结束阶段:请求处理完后就进入了结束阶段,PHP就会启动清理程序。它会按顺序调用各个模块的RSHUTDOWN方法。 RSHUTDOWN用以清除程序运行时产生的符号表,也就是对每个变量调用unset函数。典型的RSHUTDOWN方法如下: ! B q: v4 L, S