如何使用PHP Embed SAPI实现Opcodes查看器


Posted in PHP onNovember 10, 2015

PHP提供了一个Embed SAPI,也就是说,PHP容许你在C/C++语言中调用PHP/ZE提供的函数。本文就通过基于Embed SAPI实现一个PHP的opcodes查看器。

首先,下载PHP源码以供编译, 我现在使用的是PHP5.3 alpha2

进入源码目录:

 ./configure --enable-embed --with-config-file-scan-dir=/etc/php.d --with-mysql  --with-config-file-path=/etc/
 ./make
 ./make install

最后,记得要将生成的libphp5.so复制到运行时库的目录,我直接拷贝到了/lib/, 否则会在运行你自己的embed程序的时候报错:

./embed: error while loading shared libraries: libphp5.so: cannot open shared object file: No such file or directory

如果你对PHP的SAPI还不熟悉的话,我建议你看看我的这篇文章:深入理解Zend SAPIs(Zend SAPI Internals)
这个时候,你就可以在你的C代码中,嵌入PHP脚本解析器了, 我的例子:

#include "sapi/embed/php_embed.h"
int main(int argc, char * argv[]){
 PHP_EMBED_START_BLOCK(argc,argv);
 char * script = " print 'Hello World!';";
 zend_eval_string(script, NULL,
          "Simple Hello World App" TSRMLS_CC);
 PHP_EMBED_END_BLOCK();
 return 0;
}

然后就是要指明include path了,一个简单的Makefile

CC = gcc
CFLAGS = -I/usr/local/include/php/ \
   -I/usr/local/include/php/main \
   -I/usr/local/include/php/Zend \
   -I/usr/local/include/php/TSRM \
   -Wall -g
LDFLAGS = -lstdc++ -L/usr/local/lib -lphp5
ALL:
 $(CC) -o embed embed.cpp $(CFLAGS) $(LDFLAGS)

编译成功以后, 运行,我们可以看到, stdout输出 Hello World!

基于这个,我们就可以很容易的实现一个类似于vld的Opcodes dumper:
首先我们定义opcode的转换函数(全部的opcodes可以查看Zend/zend_vm_opcodes.h);

char *opname(zend_uchar opcode){
 switch(opcode) {
  case ZEND_NOP: return "ZEND_NOP"; break;
  case ZEND_ADD: return "ZEND_ADD"; break;
  case ZEND_SUB: return "ZEND_SUB"; break;
  case ZEND_MUL: return "ZEND_MUL"; break;
  case ZEND_DIV: return "ZEND_DIV"; break;
  case ZEND_MOD: return "ZEND_MOD"; break;
  case ZEND_SL: return "ZEND_SL"; break;
  case ZEND_SR: return "ZEND_SR"; break;
  case ZEND_CONCAT: return "ZEND_CONCAT"; break;
  case ZEND_BW_OR: return "ZEND_BW_OR"; break;
  case ZEND_BW_AND: return "ZEND_BW_AND"; break;
  case ZEND_BW_XOR: return "ZEND_BW_XOR"; break;
  case ZEND_BW_NOT: return "ZEND_BW_NOT"; break;
  /*...省略 ....*/
  default : return "UNKNOW"; break;

然后定义zval和znode的输出函数:

char *format_zval(zval *z)
{
 static char buffer[BUFFER_LEN];
 int len;
 switch(z->type) {
  case IS_NULL:
   return "NULL";
  case IS_LONG:
  case IS_BOOL:
   snprintf(buffer, BUFFER_LEN, "%d", z->value.lval);
   return buffer;
  case IS_DOUBLE:
   snprintf(buffer, BUFFER_LEN, "%f", z->value.dval);
   return buffer;
  case IS_STRING:
   snprintf(buffer, BUFFER_LEN, "\"%s\"", z->value.str.val);
   return buffer;
  case IS_ARRAY:
  case IS_OBJECT:
  case IS_RESOURCE:
  case IS_CONSTANT:
  case IS_CONSTANT_ARRAY:
   return "";
  default:
   return "unknown";
 }
}
char * format_znode(znode *n){
 static char buffer[BUFFER_LEN];
 switch (n->op_type) {
  case IS_CONST:
   return format_zval(&n->u.constant);
   break;
  case IS_VAR:
   snprintf(buffer, BUFFER_LEN, "$%d", n->u.var/sizeof(temp_variable));
   return buffer;
   break;
  case IS_TMP_VAR:
   snprintf(buffer, BUFFER_LEN, "~%d", n->u.var/sizeof(temp_variable));
   return buffer;
   break;
  default:
   return "";
   break;
 }
}

 然后定义op_array的输出函数:

void dump_op(zend_op *op, int num){
 printf("%5d %5d %30s %040s %040s %040s\n", num, op->lineno,
   opname(op->opcode),
   format_znode(&op->op1),
   format_znode(&op->op2),
   format_znode(&op->result)) ;
}
void dump_op_array(zend_op_array *op_array){
 if(op_array) {
  int i;
  printf("%5s %5s %30s %040s %040s %040s\n", "opnum", "line", "opcode", "op1", "op2", "result");
  for(i = 0; i < op_array->last; i++) {
   dump_op(&op_array->opcodes[i], i);
  }
 }
}

最后,就是程序的主函数了:

int main(int argc, char **argv){
 zend_op_array *op_array;
 zend_file_handle file_handle;
 if(argc != 2) {
  printf("usage: op_dumper <script>\n");
  return 1;
 }
 PHP_EMBED_START_BLOCK(argc,argv);
 printf("Script: %s\n", argv[1]);
 file_handle.filename = argv[1];
 file_handle.free_filename = 0;
 file_handle.type = ZEND_HANDLE_FILENAME;
 file_handle.opened_path = NULL;
 op_array = zend_compile_file(&file_handle, ZEND_INCLUDE TSRMLS_CC);
 if(!op_array) {
  printf("Error parsing script: %s\n", file_handle.filename);
  return 1;
 }
 dump_op_array(op_array);
 PHP_EMBED_END_BLOCK();
 return 0;
}

编译,运行测试脚本(sample.php):

sample.php:

   echo "laruence";

命令:

./opcodes_dumper  sample.php

得到输出结果(如果你对下面的结果很迷惑,那么建议你再看看我的这篇文章:深入理解PHP原理之Opcodes):

Script: sample.php

opnum   line                         opcode                                      op1                                      op2                                   result
    0      2                      ZEND_ECHO                               "laruence"
    1      4                    ZEND_RETURN                                        1

呵呵,怎么样,是不是很好玩呢?

PHP 相关文章推荐
php循环语句 for()与foreach()用法区别介绍
Sep 05 PHP
php 生成短网址原理及代码
Jan 23 PHP
PHP写的资源下载防盗链类分享
May 12 PHP
为PHP5.4开启Zend OPCode缓存
Dec 26 PHP
php堆排序实现原理与应用方法
Jan 03 PHP
PHP获取QQ达人QQ信息的方法
Mar 05 PHP
支持中文、字母、数字的PHP验证码
May 04 PHP
php使用标签替换的方式生成静态页面
May 21 PHP
Joomla实现组件中弹出一个模式(modal)窗口的方法
May 04 PHP
基于PHP后台的Android新闻浏览客户端
May 23 PHP
php微信高级接口群发 多客服
Jun 23 PHP
PHP面向对象程序设计之构造方法和析构方法详解
Jun 13 PHP
深入理解PHP内核(二)之SAPI探究
Nov 10 #PHP
深入理解PHP内核(一)
Nov 10 #PHP
在PHP中使用FastCGI解析漏洞及修复方案
Nov 10 #PHP
PHP中使用GD库绘制折线图 折线统计图的绘制方法
Nov 09 #PHP
再推荐十款免费的php开发工具
Nov 09 #PHP
php开发工具有哪五款
Nov 09 #PHP
PHP编程开发怎么提高编程效率 提高PHP编程技术
Nov 09 #PHP
You might like
攻克CakePHP系列二 表单数据显示
2008/10/22 PHP
Yii2框架使用计划任务的方法
2016/05/25 PHP
PHP数据库表操作的封装类及用法实例详解
2016/07/12 PHP
监控 url fragment变化的js代码
2010/04/19 Javascript
基于JavaScript 数据类型之Boolean类型分析介绍
2013/04/19 Javascript
深入理解javascript严格模式(Strict Mode)
2014/11/28 Javascript
javascript使用prototype完成单继承
2014/12/24 Javascript
jQuery实现的动态伸缩导航菜单实例
2015/05/07 Javascript
easyUI实现类似搜索框关键词自动提示功能示例代码
2016/12/27 Javascript
jQuery实现遮罩层登录对话框
2016/12/29 Javascript
基本DOM节点操作
2017/01/17 Javascript
javascript 秒表计时器实现代码
2017/03/09 Javascript
详解angular ui-grid之过滤器设置
2017/06/07 Javascript
vue+iview写个弹框的示例代码
2017/12/05 Javascript
vue加载完成后的回调函数方法
2018/09/07 Javascript
如何安装控制器JavaScript生成插件详解
2018/10/21 Javascript
Vue基础配置讲解
2019/11/29 Javascript
js实现小球在页面规定的区域运动
2020/06/16 Javascript
如何手动实现一个 JavaScript 模块执行器
2020/10/16 Javascript
uni-app使用countdown插件实现倒计时
2020/11/01 Javascript
[01:09:24]Ti4开幕式
2014/07/19 DOTA
[45:15]Optic vs VP 2018国际邀请赛淘汰赛BO3 第一场 8.24
2018/08/25 DOTA
python实现360皮肤按钮控件示例
2014/02/21 Python
Python基于回溯法子集树模板解决取物搭配问题实例
2017/09/02 Python
教你使用python实现微信每天给女朋友说晚安
2018/03/23 Python
提升Python程序性能的7个习惯
2019/04/14 Python
社区版pycharm创建django项目的方法(pycharm的newproject左侧没有项目选项)
2020/09/23 Python
HTML5 实现图片上传预处理功能
2020/02/06 HTML / CSS
大学班级学风建设方案
2014/05/01 职场文书
教师专业自荐信
2014/05/31 职场文书
机电系毕业生求职信
2014/07/11 职场文书
招商引资工作汇报材料
2014/10/28 职场文书
职业生涯规划书之大学四年
2019/08/07 职场文书
2019年圣诞节祝福语集锦
2019/12/25 职场文书
MySQL索引是啥?不懂就问
2021/07/21 MySQL
Go微服务项目配置文件的定义和读取示例详解
2022/06/21 Golang