如何使用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代码优化及php相关问题总结
Oct 09 PHP
基于PHP与XML的PDF文档生成技术
Oct 09 PHP
php中导出数据到excel时数字变为科学计数的解决方法
Feb 03 PHP
PHP开发工具ZendStudio下Xdebug工具使用说明详解
Nov 11 PHP
PHP根据传入参数合并多个JS和CSS文件的简单实现
Jun 13 PHP
php中使用array_filter()函数过滤空数组的实现代码
Aug 19 PHP
php的XML文件解释类应用实例
Sep 22 PHP
19个Android常用工具类汇总
Dec 30 PHP
PHP获取当前日期和时间及格式化方法参数
May 11 PHP
thinkphp如何获取客户端IP
Nov 03 PHP
编写PHP程序检查字符串中的中文字符个数的实例分享
Mar 17 PHP
PHP正则表达式匹配替换与分割功能实例浅析
Feb 04 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
《猛禽小队》:DC宇宙的又一超级大烂片
2020/04/09 欧美动漫
关于使用coreseek并为其做分页的介绍
2013/06/21 PHP
关于ob_get_contents(),ob_end_clean(),ob_start(),的具体用法详解
2013/06/24 PHP
Thinkphp模板中截取字符串函数简介
2014/06/17 PHP
PHP和javascript常用正则表达式及用法实例
2014/07/01 PHP
9个比较实用的php代码片段
2016/03/15 PHP
PHP使用finfo_file()函数检测上传图片类型的实现方法
2017/04/18 PHP
iis6手工创建网站后无法运行php脚本的解决方法
2017/06/08 PHP
FireFox中textNode分片的问题
2007/04/10 Javascript
javascript教程之不完整的继承(js原型链)
2014/01/13 Javascript
js实现带按钮的上下滚动效果
2015/05/12 Javascript
详谈JS中实现种子随机数及作用
2016/07/19 Javascript
微信小程序 Storage API实例详解
2016/10/02 Javascript
微信小程序 实战实例开发流程详细介绍
2017/01/05 Javascript
js+html5实现半透明遮罩层弹框效果
2020/08/24 Javascript
vue路由 遍历生成复数router-link的例子
2019/10/30 Javascript
JS实现打字游戏
2019/12/17 Javascript
js实现微信聊天界面
2020/08/09 Javascript
如何利用nodejs实现命令行游戏
2020/11/24 NodeJs
[05:37]DOTA2-DPC中国联赛 正赛 Elephant vs iG 选手采访
2021/03/11 DOTA
Python 模拟登陆的两种实现方法
2017/08/10 Python
使用Python快速制作可视化报表的方法
2019/02/03 Python
python读取目录下所有的jpg文件,并显示第一张图片的示例
2019/06/13 Python
python数据挖掘需要学的内容
2019/06/23 Python
python文件转为exe文件的方法及用法详解
2019/07/08 Python
Django结合ajax进行页面实时更新的例子
2019/08/12 Python
Python爬虫 批量爬取下载抖音视频代码实例
2019/08/16 Python
python3 实现调用串口功能
2019/12/26 Python
Python如何生成xml文件
2020/06/04 Python
详解使用HTML5 Canvas创建动态粒子网格动画
2016/12/14 HTML / CSS
AmazeUI 点击元素显示全屏的实现
2020/08/25 HTML / CSS
情人节寄语大全
2014/04/11 职场文书
党员弘扬焦裕禄精神思想汇报
2014/09/10 职场文书
个人思想政治总结
2015/03/05 职场文书
婚礼双方父亲致辞
2015/07/27 职场文书
大学入学感言
2015/08/01 职场文书