如何使用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 相关文章推荐
smtp邮件发送一例
Oct 09 PHP
dedecms防止FCK乱格式化你的代码的修改方法
Mar 17 PHP
php chr() ord()中文截取乱码问题解决方法
Sep 08 PHP
一步一步学习PHP(8) php 数组
Mar 05 PHP
php set_time_limit()函数的使用详解
Jun 05 PHP
在PHP中使用X-SendFile头让文件下载更快
Jun 01 PHP
PHP学习笔记之php文件操作
Jun 03 PHP
PHP7.1新功能之Nullable Type用法分析
Sep 26 PHP
ThinkPHP中Widget扩展的两种写法及调用方法详解
May 04 PHP
利用php操作memcache缓存的基础方法示例
Aug 02 PHP
PHP中的自动加载操作实现方法详解
Aug 06 PHP
详解thinkphp的Auth类认证
May 28 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简单实现上一页下一页功能示例
2016/09/14 PHP
Thinkphp5.0 框架实现控制器向视图view赋值及视图view取值操作示例
2019/10/12 PHP
JavaScript运行时库属性一览表
2014/03/14 Javascript
jQuery插件MixItUp实现动画过滤和排序
2015/04/12 Javascript
js简单实现竖向tab选项卡的方法
2015/05/04 Javascript
jquery实现初次打开有动画效果的网页TAB切换代码
2015/09/06 Javascript
jQuery each函数源码分析
2016/05/25 Javascript
JavaScript判断是否是微信浏览器
2016/06/13 Javascript
jquery实现焦点轮播效果
2017/02/23 Javascript
JavaScript中的遍历详解(多种遍历)
2017/04/07 Javascript
webpack配置sass模块的加载的方法
2017/07/30 Javascript
jQuery超简单遮罩层实现方法示例
2018/09/06 jQuery
JS实现的合并两个有序链表算法示例
2019/02/25 Javascript
Vue 引入AMap高德地图的实现代码
2019/04/29 Javascript
javascript数组元素删除方法delete和splice解析
2019/12/09 Javascript
微信小程序背景音乐开发详解
2019/12/12 Javascript
JavaScript eval()函数定义及使用方法详解
2020/07/07 Javascript
Python函数嵌套实例
2014/09/23 Python
Python参数解析模块sys、getopt、argparse使用与对比分析
2019/04/02 Python
django 快速启动数据库客户端程序的方法示例
2019/08/16 Python
简单瞅瞅Python vars()内置函数的实现
2019/09/27 Python
Pycharm中如何关掉python console
2020/10/27 Python
使用CSS3实现多列布局与多背景的技巧
2016/02/29 HTML / CSS
html5理解head_动力节点Java学院整理
2017/07/13 HTML / CSS
Html5游戏开发之乒乓Ping Pong游戏示例(三)
2013/01/21 HTML / CSS
HTML5实现签到 功能
2018/10/09 HTML / CSS
芬兰灯具网上商店:Nettilamppu.fi
2018/06/30 全球购物
天逸系统(武汉)有限公司Java笔试题
2015/12/29 面试题
Overload和Override的区别
2012/09/02 面试题
《小石潭记》教学反思
2014/02/13 职场文书
企业宣传方案
2014/03/04 职场文书
农村面貌改造提升实施方案
2014/03/18 职场文书
房产买卖委托公证书
2014/04/04 职场文书
2014年小学教师工作自我评价
2014/09/22 职场文书
Nginx配置之实现多台服务器负载均衡
2021/08/02 Servers
Java 实战项目之家居购物商城系统详解流程
2021/11/11 Java/Android