深入理解PHP之OpCode原理详解


Posted in PHP onJune 01, 2016

本文实例讲述了PHP中OpCode的原理。分享给大家供大家参考,具体如下:

OpCode是一种PHP脚本编译后的中间语言,就像Java的ByteCode,或者.NET的MSL。 此文主要基于《 Understanding OPcode》和 网络,根据个人的理解和修改,特记录下来 :

PHP代码:

<?php
  echo "Hello World";
  $a = 1 + 1;
  echo $a;
?>

PHP执行这段代码会经过如下4个步骤:

1. Scanning (Lexing) ,将PHP代码转换为语言片段(Tokens)
2. Parsing , 将Tokens转换成简单而有意义的表达式
3. Compilation , 将表达式编译成Opocdes
4. Execution , 顺次执行Opcodes,每次一条,从而实现PHP脚本的功能。

注:现在有的Cache比如:APC ,可以使得PHP缓存Opcodes ,这样,每次有请求来临的时候,就不需要重复执行前面3步,从而能大幅的提高PHP的执行速度。

首先,Zend/zend_language_scanner.c 会根据Zend/zend_language_scanner.l(Lex文件),来对输入的 PHP代码进行词法分析,从而得到一个一个的“词”,PHP4.2+开始提供了一个函数叫token_get_all ,这个函数就可以讲一段PHP代码 Scanning成Tokens;

<?php
$tokens = token_get_all('<?php
  echo "Hello World";
  $a = 1 + 1;
  echo $a;
?>');
print_r($tokens);
?>

将会得到如下结果:

Array
(
  [0] => Array
    (
      [0] => 367
      [1] => <?php
      [2] => 1
    )
  [1] => Array
    (
      [0] => 370
      [1] =>
      [2] => 2
    )
  [2] => Array
    (
      [0] => 316
      [1] => echo
      [2] => 2
    )
  [3] => Array
    (
      [0] => 370
      [1] =>
      [2] => 2
    )
  [4] => Array
    (
      [0] => 315
      [1] => "Hello World"
      [2] => 2
    )
  [5] => ;
  [6] => Array
    (
      [0] => 370
      [1] =>
      [2] => 2
    )
  [7] => Array
    (
      [0] => 309
      [1] => $a
      [2] => 3
    )
  [8] => Array
    (
      [0] => 370
      [1] =>
      [2] => 3
    )
  [9] => =
  [10] => Array
    (
      [0] => 370
      [1] =>
      [2] => 3
    )
  [11] => Array
    (
      [0] => 305
      [1] => 1
      [2] => 3
    )
  [12] => Array
    (
      [0] => 370
      [1] =>
      [2] => 3
    )
  [13] => +
  [14] => Array
    (
      [0] => 370
      [1] =>
      [2] => 3
    )
  [15] => Array
    (
      [0] => 305
      [1] => 1
      [2] => 3
    )
  [16] => ;
  [17] => Array
    (
      [0] => 370
      [1] =>
      [2] => 3
    )
  [18] => Array
    (
      [0] => 316
      [1] => echo
      [2] => 4
    )
  [19] => Array
    (
      [0] => 370
      [1] =>
      [2] => 4
    )
  [20] => Array
    (
      [0] => 309
      [1] => $a
      [2] => 4
    )
  [21] => ;
  [22] => Array
    (
      [0] => 370
      [1] =>
      [2] => 4
    )
  [23] => Array
    (
      [0] => 369
      [1] => ?>
      [2] => 5
    )
)

返回的结果, 源码中的字符串,字符,空格,都会原样返回。每个源代码中的字符,都会出现在相应的顺序处。而,其他的比如标签,操作符,语句,都会被转换成一个包含俩部分的Array: Token ID (也就是在Zend内部的改Token的对应码,比如,T_ECHO,T_STRING),和源码中的原来的内容。

接下来,就是Parsing阶段了,Parsing首先会丢弃Tokens Array中的多于的空格,然后将剩余的Tokens转换成一个一个的简单的表达式

1. echo a constant string
2. add two numbers together
3. store the result of the prior expression to a variable
4. echo a variable

然后,就改Compilation阶段了,它会把Tokens编译成一个个op_array,每个op_arrayd包含如下5个部分:

1. Opcode数字的标识,指明了每个op_array的操作类型,比如add,echo
2. 结果存放Opcode结果
3. 操作数1给Opcode的操作数
4. 操作数2
5. 扩展值1个整形用来区别被重载的操作符

比如,PHP代码会被Parsing成:

[root@localhost html]# /usr/local/php/bin/php -dvld.active=1 hello.php
Branch analysis from position: 0
Return found
filename:    /var/www/html/hello.php
function name: (null)
number of ops: 6
compiled vars: !0 = $a
line   # op              fetch     ext return operands
-------------------------------------------------------------------------------
  2   0 ECHO                           'Hello+world'
  3   1 ADD                       ~0   1, 1
     2 ASSIGN                          !0, ~0
  4   3 ECHO                           !0
  6   4 RETURN                          1
     5* ZEND_HANDLE_EXCEPTION
Hello world2

每个操作数都是由以下两个部分组成:

a) op_type : 为IS_CONST, IS_TMP_VAR, IS_VAR, IS_UNUSED, or IS_CV

b) u,一个联合体,根据op_type的不同,分别用不同的类型保存了这个操作数的值(const)或者左值(var)

而对于var来说,每个var也不一样。  IS_TMP_VAR, 顾名思义,这个是一个临时变量 ,保存一些op_array的结果,以便接下来的op_array使用,这种的操作数的u保存着一个指向变量表的一个句柄(整数),这种操作数一般用~开头,比如~0,表示变量表的0号未知的临时变量IS_VAR 这种就是我们一般意义上的变量了,他们以$开头表示IS_CV 表示ZE2.1/PHP5.1以后的编译器使用的一种cache机制,这种变量保存着被它引用的变量的地址 ,当一个变量第一次被引用的时候,就会被CV起来,以后对这个变量的引用就不需要再次去查找active符号表了,CV变量以 ! 开头表示。

$a 变量就被优化成 !0 了。

希望本文所述对大家PHP程序设计有所帮助。

PHP 相关文章推荐
PHP 加密/解密函数 dencrypt(动态密文,带压缩功能,支持中文)
Jan 30 PHP
PHP+MYSQL会员系统的登陆即权限判断实现代码
Sep 23 PHP
php下利用curl判断远程文件是否存在的实现代码
Oct 08 PHP
PHP常用函数和常见疑难问题解答
Mar 05 PHP
php过滤html中的其他网站链接的方法(域名白名单功能)
Apr 24 PHP
PHP实现的MongoDB数据库操作类分享
May 12 PHP
php连接odbc数据源并保存与查询数据的方法
Dec 24 PHP
php判断用户是否关注微信公众号
Jul 22 PHP
php解决和避免form表单重复提交的几种方法
Aug 31 PHP
深入理解Yii2.0乐观锁与悲观锁的原理与使用
Jul 26 PHP
PHP中将一个字符串部分字符用星号*替代隐藏的实现代码
Sep 08 PHP
在Laravel 的 Blade 模版中实现定义变量
Oct 14 PHP
深入理解PHP中的count函数
May 31 #PHP
Ubuntu server 11.04安装memcache及php使用memcache来存储session的方法
May 31 #PHP
php中json_encode不兼容JSON_UNESCAPED_UNICODE的解决方案
May 31 #PHP
让你的PHP7更快之Hugepage用法分析
May 31 #PHP
PHP表单数据写入MySQL数据库的代码
May 31 #PHP
PHP将页面中点击数量高的链接进行高亮显示的方法
May 30 #PHP
PHP如何实现跨域
May 30 #PHP
You might like
在IIS上安装PHP4.0正式版
2006/10/09 PHP
php下获取客户端ip地址的函数
2010/03/15 PHP
PHP基础知识介绍
2013/09/17 PHP
php stream_get_meta_data返回值
2013/09/29 PHP
php错误日志简单配置方法
2016/07/11 PHP
CheckBox 如何实现全选?
2006/06/23 Javascript
调用js时ie6和ie7,ff的区别
2009/08/19 Javascript
javascript 事件绑定问题
2011/01/01 Javascript
JS实现网页背景颜色与select框中颜色同时变化的方法
2015/02/27 Javascript
JS获得图片alt信息的方法
2015/04/01 Javascript
在JavaScript中用getMinutes()方法返回指定的分时刻
2015/06/10 Javascript
微信小程序 教程之注册页面
2016/10/17 Javascript
Javascript highcharts 饼图显示数量和百分比实例代码
2016/12/06 Javascript
vuex的使用及持久化state的方式详解
2018/01/23 Javascript
vue cli构建的项目中请求代理与项目打包问题
2018/02/26 Javascript
js实现通过开始结束控制的计时器
2019/02/25 Javascript
layui: layer.open加载窗体时出现遮罩层的解决方法
2019/09/26 Javascript
JS相册图片抖动放大展示效果的示例代码
2021/01/29 Javascript
[01:47]2018年度DOTA2最佳教练-完美盛典
2018/12/16 DOTA
python通过pil模块将raw图片转换成png图片的方法
2015/03/16 Python
Python获取文件所在目录和文件名的方法
2017/01/12 Python
Python 用Redis简单实现分布式爬虫的方法
2017/11/23 Python
详解K-means算法在Python中的实现
2017/12/05 Python
python决策树之C4.5算法详解
2017/12/20 Python
Tensorflow加载预训练模型和保存模型的实例
2018/07/27 Python
Python3.6+selenium2.53.6自动化测试_读取excel文件的方法
2019/09/06 Python
keras的siamese(孪生网络)实现案例
2020/06/12 Python
Calzedonia美国官网:意大利风格袜子、打底裤和沙滩装
2018/07/19 全球购物
创建索引时需要注意的事项
2013/05/13 面试题
AJax面试题
2014/11/25 面试题
创业者迈进成功第一步:如何写创业计划书?
2014/03/22 职场文书
社会实践评语
2014/04/28 职场文书
2014年旅游局法制宣传日活动总结
2014/11/01 职场文书
企业团队精神心得体会
2016/01/19 职场文书
2016年优秀班主任先进事迹材料
2016/02/26 职场文书
Android中的Launch Mode详情
2022/06/05 Java/Android