如何使用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 和 MySQL 基础教程(四)
Oct 09 PHP
php preg_match_all结合str_replace替换内容中所有img
Oct 11 PHP
PHP生成excel时单元格内换行问题的解决方法
Aug 26 PHP
php curl的深入解析
Jun 02 PHP
php类的扩展和继承用法实例
Jun 20 PHP
PHP中static关键字以及与self关键字的区别
Jul 01 PHP
如何通过Linux命令行使用和运行PHP脚本
Jul 29 PHP
PHP实现的蚂蚁爬杆路径算法代码
Dec 03 PHP
PHP生成和获取XML格式数据的方法
Mar 04 PHP
Laravel 5.5 的自定义验证对象/类示例代码详解
Aug 29 PHP
PHP数组常用函数实例小结
Aug 20 PHP
Laravel实现通过blade模板引擎渲染视图
Oct 25 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中count获取多维数组长度的方法
2014/11/03 PHP
学习php设计模式 php实现门面模式(Facade)
2015/12/07 PHP
php类的自动加载操作实例详解
2016/09/28 PHP
php下载远程大文件(获取远程文件大小)的实例
2017/06/17 PHP
浅谈PHP封装CURL
2019/03/06 PHP
解决laravel资源加载路径设置的问题
2019/10/14 PHP
IE中直接运行显示当前网页中的图片 推荐
2006/08/31 Javascript
jquery判断单个复选框是否被选中的代码
2009/09/03 Javascript
javascript中将Object转换为String函数代码 (json str)
2012/04/29 Javascript
laytpl 精致巧妙的JavaScript模板引擎
2014/08/29 Javascript
jQuery中first()方法用法实例
2015/01/06 Javascript
jQuery validate插件submitHandler提交导致死循环解决方法
2016/01/21 Javascript
jQuery利用sort对DOM元素进行排序操作
2016/11/07 Javascript
Angular.js指令学习中一些重要属性的用法教程
2017/05/24 Javascript
javascript、php关键字搜索函数的使用方法
2018/05/29 Javascript
JavaScript中七种流行的开源机器学习框架
2018/10/11 Javascript
微信小程序实现简易table表格
2020/06/19 Javascript
vue-cli和v-charts实现可视化图表过程解析
2019/10/08 Javascript
解决vscode进行vue格式化,会自动补分号和双引号的问题
2020/10/26 Javascript
python字符串替换示例
2014/04/24 Python
python模拟Django框架实例
2016/05/17 Python
使用Python对MySQL数据操作
2017/04/06 Python
python递归打印某个目录的内容(实例讲解)
2017/08/30 Python
Python反射的用法实例分析
2018/02/11 Python
python使用sqlite3时游标使用方法
2018/03/13 Python
用Python批量把文件复制到另一个文件夹的实现方法
2019/08/16 Python
python设置代理和添加镜像源的方法
2020/02/14 Python
Python根据字典的值查询出对应的键的方法
2020/09/30 Python
解决PyCharm无法使用lxml库的问题(图解)
2020/12/22 Python
中国最大隐形眼镜网上商城:视客眼镜网
2016/10/30 全球购物
中国旅游网站:途牛旅游网
2019/09/29 全球购物
XML文档面试题
2015/08/05 面试题
JS原生实现轮播图的几种方法
2021/03/23 Javascript
体育教师工作总结的自我评价
2013/10/10 职场文书
国际语言毕业生求职信
2014/07/08 职场文书
Mysql MVCC机制原理详解
2021/04/20 MySQL