如何使用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 相关文章推荐
自动跳转中英文页面
Oct 09 PHP
php中看实例学正则表达式
Dec 25 PHP
php后退一页表单内容保存实现方法
Jun 17 PHP
Windows下的PHP安装文件线程安全和非线程安全的区别
Apr 23 PHP
对PHP PDO的一些认识小结
Jan 23 PHP
PHP中使用file_get_contents post数据代码例子
Feb 13 PHP
Smarty模板引擎缓存机制详解
May 23 PHP
PHP针对字符串开头和结尾的判断方法
Jul 11 PHP
浅谈php中urlencode与rawurlencode的区别
Sep 05 PHP
php中this关键字用法分析
Dec 07 PHP
form自动提交实例讲解
Jul 10 PHP
PHP排序算法之简单选择排序(Simple Selection Sort)实例分析
Apr 20 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
PHP 输出缓存详解
2009/06/20 PHP
php生成的html meta和link标记在body标签里 顶部有个空行
2010/05/18 PHP
php异常处理技术,顶级异常处理器
2012/06/13 PHP
php中的单引号、双引号和转义字符详解
2017/02/16 PHP
laravel model模型定义实现开启自动管理时间created_at,updated_at
2019/10/17 PHP
超简单的jquery的AJAX用法
2010/05/10 Javascript
javascript事件函数中获得事件源的两种不错方法
2014/03/17 Javascript
jquery分页插件jpaginate在IE中不兼容问题
2014/04/22 Javascript
js实现百度联盟中一款不错的图片切换效果完整实例
2015/03/04 Javascript
JavaScript获取各大浏览器信息图示
2015/11/20 Javascript
D3.js实现直方图的方法详解
2016/09/25 Javascript
利用Query+bootstrap和js两种方式实现日期选择器
2017/01/10 Javascript
JavaScript函数节流和函数去抖知识点学习
2018/07/31 Javascript
angularjs请求数据的方法示例
2019/08/06 Javascript
js实现数据导出为EXCEL(支持大量数据导出)
2020/03/31 Javascript
[00:37]2016完美“圣”典风云人物:AMS宣传片
2016/12/06 DOTA
[51:36]Optic vs Newbee 2018国际邀请赛小组赛BO2 第一场 8.17
2018/08/18 DOTA
python3使用PyMysql连接mysql数据库实例
2017/02/07 Python
Python获取系统所有进程PID及进程名称的方法示例
2018/05/24 Python
Django中使用Celery的方法示例
2018/11/29 Python
在Pycharm中修改文件默认打开方式的方法
2019/01/17 Python
Python设计模式之代理模式实例详解
2019/01/19 Python
python使用递归的方式建立二叉树
2019/07/03 Python
Python操作远程服务器 paramiko模块详细介绍
2019/08/07 Python
Python操作列表常用方法实例小结【创建、遍历、统计、切片等】
2019/10/25 Python
python GUI库图形界面开发之PyQt5选项卡控件QTabWidget详细使用方法与实例
2020/03/01 Python
合作经营协议书
2014/04/17 职场文书
个人工作主要事迹
2014/05/08 职场文书
小学生关于梦想的演讲稿
2014/08/22 职场文书
2014年采购部工作总结
2014/11/20 职场文书
辞职信如何写
2015/02/27 职场文书
幼儿园重阳节活动总结
2015/05/05 职场文书
2016年“5.12”护士节慰问信
2015/11/30 职场文书
ObjectMapper 如何忽略字段大小写
2021/06/29 Java/Android
Windows环境下实现批量执行Sql文件
2021/10/05 SQL Server
使用Redis做预定库存缓存功能
2022/04/02 Redis