深入理解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 相关文章推荐
数据库相关问题
Oct 09 PHP
如何在PHP中进行身份认证
Oct 09 PHP
谈PHP生成静态页面分析 模板+缓存+写文件
Aug 17 PHP
php 过滤器实现代码
Aug 09 PHP
限制ckeditor上传图片文件大小的方法
Nov 15 PHP
网站防止被刷票的一些思路与方法
Jan 08 PHP
PHP Cookei记录用户历史浏览信息的代码
Feb 03 PHP
浅谈PHP中类和对象的相关函数
Apr 26 PHP
Laravel使用消息队列需要注意的一些问题
Dec 13 PHP
PHP 布尔值的自增与自减的实现方法
May 03 PHP
php获取手机端的号码以及ip地址实例代码
Sep 12 PHP
PHP常用header头定义代码示例汇总
Aug 29 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
Zerg兵种介绍
2020/03/14 星际争霸
PHP4(windows版本)中的COM函数
2006/10/09 PHP
php md5下16位和32位的实现代码
2008/04/09 PHP
Symfony2开发之控制器用法实例分析
2016/02/05 PHP
js下获得客户端操作系统的函数代码(1:vista,2:windows7,3:2000,4:xp,5:2003,6:2008)
2011/10/31 Javascript
JS判断当前日期是否大于某个日期的实现代码
2012/09/02 Javascript
js中键盘事件实例简析
2015/01/10 Javascript
Bootstrap教程JS插件滚动监听学习笔记分享
2016/05/18 Javascript
js删除数组元素、清空数组的简单方法(必看)
2016/07/27 Javascript
Vue实现自带的过滤器实例
2017/03/09 Javascript
js实现数字递增特效【仿支付宝我的财富】
2017/05/05 Javascript
微信通过页面(H5)直接打开本地app的解决方法
2017/09/09 Javascript
详解关于react-redux中的connect用法介绍及原理解析
2017/09/11 Javascript
VUE中的无限循环代码解析
2017/09/22 Javascript
浅谈Vue.nextTick 的实现方法
2017/10/25 Javascript
浅谈ElementUI中switch回调函数change的参数问题
2018/08/24 Javascript
Vue中保存数据到磁盘文件的方法
2018/09/06 Javascript
JS实现简易贪吃蛇游戏
2020/08/24 Javascript
uniapp开发小程序实现滑动页面控制元素的显示和隐藏效果
2020/12/10 Javascript
python中实现定制类的特殊方法总结
2014/09/28 Python
Python统计分析模块statistics用法示例
2019/09/06 Python
python的列表List求均值和中位数实例
2020/03/03 Python
Python如何设置指定窗口为前台活动窗口
2020/08/12 Python
python中用Scrapy实现定时爬虫的实例讲解
2021/01/18 Python
英国家喻户晓的高街品牌:River Island
2017/11/28 全球购物
英国户外玩具儿童游乐设备网站:TP Toys(蹦床、攀爬框架、秋千、滑梯和游戏屋)
2018/04/09 全球购物
师范生教师实习自我鉴定
2013/09/27 职场文书
办公室前台岗位职责
2014/01/04 职场文书
安全检查与奖惩制度
2014/01/23 职场文书
政协会议宣传标语
2014/10/09 职场文书
2014年度安全工作总结
2014/12/04 职场文书
向女朋友道歉的话
2015/01/20 职场文书
学校施工安全责任书
2015/01/29 职场文书
物流业务员岗位职责
2015/04/03 职场文书
市场营销计划书
2019/04/24 职场文书
vue自定义右键菜单之全局实现
2022/04/09 Vue.js