PHP的运行机制及原理
我们从未手动开启过PHP的相关进程,它是随着Apache的启动而运行的;
PHP通过SAPI和Apache相连;
PHP总共有三个模块:内核、Zend引擎、以及扩展层;
PHP内核用来处理请求、文件流、错误处理等相关操作;
Zend引擎(ZE)用以将源文件转换成机器语言,然后在虚拟机上运行它;
扩展层是一组函数、类库和流,PHP使用它们来执行一些特定的操作。比如,我们需要mysql扩展来连接MySQL数据库;
当ZE执行程序时可能会需要连接若干扩展,这时ZE将控制权交给扩展,等处理完特定任务后再返还;
最后,ZE将程序运行结果返回给PHP内核,它再将结果传送给SAPI层,最终输出到浏览器上。
php执行过程
1.扫描(scanning) ,将index.php内容变成一个个语言片段(token)
2.解析(parsing) , 将一个个语言片段变成有意义的表达式
3.编译(complication),将表达式编译成中间码(opcode)
4.执行(execution),将中间码一条一条的执行
5.输出(output buffer),将要输出的内容输出到缓冲区
学过编译原理的应该了解Lex , Lex是LEXical compiler的缩写,是个非常著名的工具,主要功能是词法分析器(scanner),描述规则采用正则表达式(regular expression)。在php源码中是 Zend/zend_language_scanner.c 去根据 Zend/zend_language_scanner.l (lex规则文件) ,来对需要执行的php文件进行词法分析 ,从而得到一个个的"词" (token),当然执行规则,也就是切分的方式还是用正则表达式做的
具体看代码 Zend/zend_language_scanner.l
分析这个返回结果我们可以发现,源码中的字符串,字符,空格,都会原样返回。每个源代码中的字符,都会出现在相应的顺序处。每一个数组的结构都是一致的,
Token ID (在Zend内部的改Token的对应码,比如,T_ECHO,T_STRING)。
具体的定义可以在zend_language_parser.h中找到
解析 parsing
词法分析后, 就需要根据一个个token去组成有意义的表达式
Parsing首先会丢弃Tokens Array中的多余的空格,然后将剩余的Tokens转换成一个一个的简单的表达式,例如上例中:
1.输出一个常量字符串
2.两个数字相加
3.上个表达式的结果存储在一个变量
4.输出这个变量
编译 complication
根据上面的表达式,我们要将之编译成中间码(opcode),编译成opcode后,zend引擎才会根据一条一条的opcode 去执行我们的功能!
我们怎么看到编译后的opcode代码呢? 可以用vld扩展或者parsekit相关函数可以看到编译后的opcode,上例中的opcode:
注意: 1+1操作完成结果要存起来,存到临时变量 ~0 将~0赋值给$c也就是!0;
执行 execution
执行的工作具体由 Zend/zend_execute.c 来完成
如果执行是某内置函数或语句,例如array_search,会调用对应的array函数.
如果调用的是扩展,例如mysqli或pdo,则会将控制权交给这些扩展,扩展执行完后将记过返回给ZE.
全部执行完后ZE将结果返回php内核,内核再返回SAPI,最后返回到服务器,通过服务器httpresponse返回到浏览器
如何让PHP的网站运行的更快?
压缩代码,去除无用的注释或空白
尽量使用PHP内置函数或扩展函数
用apc/xcache/opcache等缓存PHP的opcode
使用多线程、多进程优化程序逻辑
缓存复杂和耗时运算的结果
能异步处理的任务不要马上处理,如发邮件
依据资源情况对FastCGI配置合适的参数
Last updated