PHP扩展封装Protobuf反序列化方法(c++)
2009-09-01 10:08
876 查看
据说google的protobuf效率很高,我们决定尝试一下,使用到php项目中,作为反序列化的协议,调用.net的soa接口。
由于protobuf的源码只有java、c++、Python,我选用了c++作为开发语言,进行PHP扩展的开发。
首先,注意到与用c做PHP扩展的几点不同,主要集中在config.m4中:
PHP_ARG_ENABLE(protophp, whether to enable protophp support,
[ --enable-protophp Enable protophp support])
if test "$PHP_PROTOPHP" != "no"; then
PHP_REQUIRE_CXX()
PHP_ADD_LIBRARY(stdc++, "", EXTRA_LDFLAGS)
PHP_ADD_LIBRARY(protobuf, "", EXTRA_LDFLAGS)
AC_DEFINE(HAVE_PROTOPHPLIB,1,[ ])
CPPFILE="protophp.cpp addressbook.pb.cpp"
PHP_NEW_EXTENSION(protophp, $CPPFILE, $ext_shared)
fi
其中PHP_REQUIRE_CXX()指明了使用c++作为开发语言,所以也就是用g++作为编译器。
PHP_ADD_LIBRARY将c++标准库和protobuf的库包含进来。
同时,由于根据protobuf协议,针对每个data类,都需要有一个.h和.cpp文件,所以CPPFILE不但要包含我们的PHP扩展源码,也要包含对应的data类代码。
另外,我们将.c文件重命名为.cpp文件。这里,可以看到我们的PHP扩展模块名为:PROTOPHP。
然后,在protophp.cpp中,调用protobuf的parse方法:
tutorial::AddressBook address_book;
address_book.ParseFromString(str);
这时,address_book对象已经生成,但是要使其能被php代码使用,还要针对每个类,写一个object to array的方法。
或许还有别的方法?
那么,这时我们看到,如果使用这种方法,每当添加 一个新的data类,我们需要修改以下4处:
1、添加class.h文件
2、添加class.cpp 文件
3、在config.m4的CPPFILE中添加class.cpp
4、在protophp.cpp开头include <class.h>
应该还有别的更简便的方法,我继续学习。
------------------------------------------------------------
继续更改,希望能够通过字符串类名动态生成c++的类,但是由于C++不支持这样的动态编译,所以需要折中修改。
首先,在PHP扩展中维持一个全局数组:
#include "addressbook.pb.h"
// 类名与相应初始函数的映射
typedef void (*cyFactoryParser_Ptr)(string protostr, zval** return_value);
typedef struct {
char * class_name; // 类名
cyFactoryParser_Ptr parser; // 对象初始化+转化为数组
} TclassNameToFunc;
static TclassNameToFunc classInfo[] = { // 如果有新增类,修改这里
{"AddressBook", tutorial::AddressBook::cyparser}
};
其中由某个类的字符串类名,映射到具体的处理函数。该处理函数需要在每次添加新的data类的时候,添加到该全局数组中。也就是说,除了通过protoc工具根据proto文件生成c++的.h和.cc文件之外,还要在.h或者.cc文件中添加自己的parser函数。
从而,在PHP扩展的函数主题部分,其实就很简单了:
// 寻找合适的处理函数
for(classInfoIdx=0; classInfoIdx < classInfoLen; ++classInfoIdx){
cla_func = classInfo[classInfoIdx];
if (!strcmp(cla_func.class_name, class_name)){
break;
}
}
// 没找到
if (classInfoIdx == classInfoLen){
cerr << "can't find!" << endl;
RETURN_FALSE;
}
// 初始化返回值:数组
array_init(return_value);
// 处理
cla_func.parser(str, &return_value);
这里,为了能够直接将C++的对象,变成返回值返回给php代码,所以需要在parser函数的定义处 include "php.h"文件。
但是这里,就出现了一个error:
/usr/local/include/google/protobuf/descriptor.pb.h:1165:64: error: macro "add_method" requires 3 arguments, but only 1 given
/usr/local/include/google/protobuf/descriptor.pb.h:3316:86: error: macro "add_method" requires 3 arguments, but only 1 given
经过排查protobuf和php的源码,发现Zend里有一个宏也叫add_method,所以冲突了。修改protobuf的函数名称为cyadd_method,然后重新编译protobuf,重新make,就ok了。
由于protobuf的源码只有java、c++、Python,我选用了c++作为开发语言,进行PHP扩展的开发。
首先,注意到与用c做PHP扩展的几点不同,主要集中在config.m4中:
PHP_ARG_ENABLE(protophp, whether to enable protophp support,
[ --enable-protophp Enable protophp support])
if test "$PHP_PROTOPHP" != "no"; then
PHP_REQUIRE_CXX()
PHP_ADD_LIBRARY(stdc++, "", EXTRA_LDFLAGS)
PHP_ADD_LIBRARY(protobuf, "", EXTRA_LDFLAGS)
AC_DEFINE(HAVE_PROTOPHPLIB,1,[ ])
CPPFILE="protophp.cpp addressbook.pb.cpp"
PHP_NEW_EXTENSION(protophp, $CPPFILE, $ext_shared)
fi
其中PHP_REQUIRE_CXX()指明了使用c++作为开发语言,所以也就是用g++作为编译器。
PHP_ADD_LIBRARY将c++标准库和protobuf的库包含进来。
同时,由于根据protobuf协议,针对每个data类,都需要有一个.h和.cpp文件,所以CPPFILE不但要包含我们的PHP扩展源码,也要包含对应的data类代码。
另外,我们将.c文件重命名为.cpp文件。这里,可以看到我们的PHP扩展模块名为:PROTOPHP。
然后,在protophp.cpp中,调用protobuf的parse方法:
tutorial::AddressBook address_book;
address_book.ParseFromString(str);
这时,address_book对象已经生成,但是要使其能被php代码使用,还要针对每个类,写一个object to array的方法。
或许还有别的方法?
那么,这时我们看到,如果使用这种方法,每当添加 一个新的data类,我们需要修改以下4处:
1、添加class.h文件
2、添加class.cpp 文件
3、在config.m4的CPPFILE中添加class.cpp
4、在protophp.cpp开头include <class.h>
应该还有别的更简便的方法,我继续学习。
------------------------------------------------------------
继续更改,希望能够通过字符串类名动态生成c++的类,但是由于C++不支持这样的动态编译,所以需要折中修改。
首先,在PHP扩展中维持一个全局数组:
#include "addressbook.pb.h"
// 类名与相应初始函数的映射
typedef void (*cyFactoryParser_Ptr)(string protostr, zval** return_value);
typedef struct {
char * class_name; // 类名
cyFactoryParser_Ptr parser; // 对象初始化+转化为数组
} TclassNameToFunc;
static TclassNameToFunc classInfo[] = { // 如果有新增类,修改这里
{"AddressBook", tutorial::AddressBook::cyparser}
};
其中由某个类的字符串类名,映射到具体的处理函数。该处理函数需要在每次添加新的data类的时候,添加到该全局数组中。也就是说,除了通过protoc工具根据proto文件生成c++的.h和.cc文件之外,还要在.h或者.cc文件中添加自己的parser函数。
从而,在PHP扩展的函数主题部分,其实就很简单了:
// 寻找合适的处理函数
for(classInfoIdx=0; classInfoIdx < classInfoLen; ++classInfoIdx){
cla_func = classInfo[classInfoIdx];
if (!strcmp(cla_func.class_name, class_name)){
break;
}
}
// 没找到
if (classInfoIdx == classInfoLen){
cerr << "can't find!" << endl;
RETURN_FALSE;
}
// 初始化返回值:数组
array_init(return_value);
// 处理
cla_func.parser(str, &return_value);
这里,为了能够直接将C++的对象,变成返回值返回给php代码,所以需要在parser函数的定义处 include "php.h"文件。
但是这里,就出现了一个error:
/usr/local/include/google/protobuf/descriptor.pb.h:1165:64: error: macro "add_method" requires 3 arguments, but only 1 given
/usr/local/include/google/protobuf/descriptor.pb.h:3316:86: error: macro "add_method" requires 3 arguments, but only 1 given
经过排查protobuf和php的源码,发现Zend里有一个宏也叫add_method,所以冲突了。修改protobuf的函数名称为cyadd_method,然后重新编译protobuf,重新make,就ok了。
相关文章推荐
- (转)C++ 版本的protobuf 动态序列化方法
- c/c++编写protobuf 对应的php扩展
- C++序列化方法 参考google protobuf
- 我对C++ string和length方法的一个长期误解------从protobuf序列化说起(没处理好会引起数据丢失、反序列化失败哦!)
- Raspberrypi Linux: 为php安装protobuf扩展
- protobuf使用方法(c++为例)
- proto-gen AS3版本的protobuf extension反序列化错误解决方法
- 我对C++ string和length方法的一个长期误解------从protobuf序列化说起(没处理好会引起数据丢失、反序列化失败哦!)
- 最常用的两种C++序列化方案的使用心得(protobuf和boost serialization)
- php安装protobuf 扩展
- 【protobuf 001】 最常用的两种C++序列化方案的使用心得(protobuf和boost serialization)
- 最常用的两种C++序列化方案的使用心得(protobuf和boost serialization)
- 最常用的两种C++序列化方案的使用心得(protobuf和boost serialization)
- 常用的C++序列化方案(protobuf和boost serialization)
- 最常用的两种C++序列化方案的使用心得(protobuf和boost serialization)
- c#序列化与反序列化通用方法, 使用protobuf-net实现
- 最常用的两种C++序列化方案的使用心得(protobuf和boost serialization)
- 【protocol buffers】java解析protoBuf custom option自定义扩展文件具体方法
- 最常用的两种C++序列化方案的使用心得(protobuf和boost serialization)