Press "Enter" to skip to content

编写提供对象给PHP使用的Module

    还是那个关于开发安全签名的PHP模块, 今天将它包装成一个PHP的CLASS,也同样,网上的资料少之甚少,于是我想将经验写一篇,关于如何在Extension Module中创建一个可以被PHP访问的对象的文章。 和大家分享。
首先,让我们达成几个共识:
1.我的模块叫做getCookie. 使用C++编写,源文件是getCookie.cc,现在我要给它添加一个可以为PHP使用的对象;
2.DOC_ROOT 是你的扩展开发目录,比如我的扩展名字叫getCookie,那么目录就在/home/y/share/php/getCookie下;
3.我要添加的类叫做XCSecure,为什么要叫XC,因为是我名字的缩写:)
4.我的XCSecure完成一个功能,构造这个对象的时候,传入一个字符串, 返回一个以这个字符串为key,和cookie经过MD5后的字符床;
5.我假设你已经对基本的如何在linux下开发一个PHP Module很熟悉了,如果不熟悉 ,那么你可以去在网上查阅《深入PHP内核》,翻译的很不错;
现在,我们已经有一定的共识了,那么开始吧:
    在PHP中,模块和PHP脚本交换数据,都是通过Zval的,我们要使用自己的C++类,就必须把我们的C++类注册为一个资源,然后每次NEW的时候,就注册一个资源实例,然后记录它的句柄到Zval中。
接下来,我们一步一步来做把。
1. 首先你要在Module中定义你自己的C++ Class。 如下:
XCSecure.h

#ifndef _XCSECURE_H_
#define _XCSECURE_H_
#include
using namespace std;
class XCSecure{
private:
string _sec;
public:
XCSecure(char * s);
~XCSecure();
char * genSec();
};
#endif

然后在XCSecure.cc中定义类函数,并在getCookie.cc中也包含XCSecure.h;
2. 修改config.m4,
PHP_NEW_EXTENSION(my_module, 'my_module.cc' 'XCSecure.cc', $ext_shared);
此处要注意的是,'my_module.cc'和'XCSecure.cc'之间使用空格分割;
然后在DOC_ROOT下运行phpize,使得为我们生成configure;
然后在DOC_ROOT下运行./configure 使得为我们生成Makefile;
到这里,我们的前期工作就做好了, 你现在也可以运行make ; make install了,但只会生成一个空的module,接下来就可以填充完善它了。
3. 修改getCookie.cc,为容纳XCSecure做一些工作:
首先,我们要定义一个全局的zend_class_entry * ;
static zend_class_entry * php_xcsecure_ptr;
    然后,我们要定义一个全局句柄(INT型), 我们要把我们的C++类注册成一个PHP中的资源(Resource), 在PHP中,资源是个很宽泛的概念,比如,链接Mysql的句柄,打开一个文件的句柄等等。
static int de_xcsecure;
4. 定义我们XCSecure的成员函数:

function_entry php5_secure_method[] = {
PHP_FALIAS(XCSecure, XCSecure_new, NULL)
PHP_FALIAS(genSec, XCSecure_genSec, NULL)
{NULL, NULL, NULL}
};

当然,你还需要自己定义这些函数;
5. 我选择在PHP_MINIT阶段来初始化我的类;

PHP_MINIT_FUNCTION(getCookie){
zend_class_entry php5_secure_entry;
de_php5_secure = zend_register_list_destructors_ex(_de_php5_secure, NULL, "Signature Generate Type", module_number); //为我们的C++类的对象创建析构函数,并获得它的资源类型句柄
INIT_CLASS_ENTRY(php5_secure_entry, "XCSecure", php5_secure_method);
php5_secure_entry_ptr = zend_register_internal_class(&php5_secure_entry);//注册我们的类,这样在PHP脚本中就可以使用了。
REGISTER_LONG_CONSTANT("XCSECURE_LOAD", 1, CONST_CS|CONST_PERSISTENT);
return SUCCESS;
}

    首先我们创建了一个zend_class_entry, 根据变量所表达出来的意思 ,这是一个zend中的对于一个对象的操作句柄类型
    然后,我们注册了我们这个对象的清理函数, 这个是因为,当在PHP脚本中unset我们的变量的时候,zend内核必须要知道如何清理我们的对象,释放我们占用的内存;
然后,我们初始化了我们的类的申明,
INIT_CLASS_ENTRY(php5_secure_entry, "XCSecure", php5_secure_method);
参数2是我们的类型名字,在PHP中var_dump的时候 ,就会显示出来,如:
object(XCSecure)#1 (0) { }
参数3是我们已经定义过的我们的类的成员函数;
然后我们向zend内核注册了我们的类,
并将它返回的指针付给了我们的全局变量(这个我自己另有用处)
zend_register_internal_class(&php5_secure_entry);
这个时候,我们的对象就算注册完成了,现在已经可以在脚本中使用了;
$xcSecure = new XCSecure('laruence');
6. 当在PHP脚本中new一个我们的对象的时候,Zend就会自动调用我们的构造函数,接下来,完成我们的构造函数:

PHP_FUNCTION(XCSecure_new){
XCSecure * instance;
char* s;
int len = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &s, &len) == FAILURE) {
RETURN_FALSE;
}
instance = new XCSecure(s);
zval * res_id;
MAKE_STD_ZVAL(res_id);
int id = ZEND_REGISTER_RESOURCE(NULL, instance, le_xc_secure);
ZVAL_LONG(res_id, id);
zend_hash_update(Z_OBJPROP_P(getThis()), Hash_Key, sizeof(Hash_Key), &res_id, sizeof(res_id), NULL);
}

    首 先我们取得用户 new XCSecure($para)的参数,它是个字符串,然后,new了一个C++对象, 并注册它为一个资源实例,注意ZEND_REGISTER_RESOURCE的最后一个参数,是我们当时定义我们的资源析构函数的时候 ,返回的资源类型句柄。
然后我们把注册资源实例以后返回的资源句柄,id 保存在一个Zval中。
    当用户在PHP中 创建一个我们的类的实例的时候,Zend会调用一个zend_objects_new来创建一个标准的Zend对象,并把它存入 zend_objects_store, 它是一个Bucket的数组,然后把我们的对象在数组中的索引(Zend中称为handler),存入一个zend_object_value结构,然后 把这个zend_object_value存入一个zval结构,之后,zend会再调用我们的构造函数,并且this指针指向这个zval, 所以,在我们的构造函数中,就可以通过getThis(),来获得这个zval,当然,也可以直接使用this_ptr;
    注册完资源 后,构造函数就把得到的资源句柄(其实也是一个list的索引),存入this指向的zval的object的properties属性中(这是一个哈希 表),以后当用户通过我们的对象调用类函数的时候,我们就可以通过this获得这个对象,然后再通过对象的属性中的资源句柄,获得我们的C++对象。比 如:

PHP_FUNCTION(XCSecure_genSec){
zval ** rsc;
XCSecure * secure;
if(zend_hash_find(Z_OBJPROP_P(getThis()), Hash_Key, sizeof(Hash_Key), (void **)&rsc) == SUCCESS){
secure = (XCSecure *)zend_fetch_resource(NULL, Z_LVAL_PP(rsc), NULL, NULL, 1, le_xc_secure);
}
ZVAL_STRING(return_value, secure->genSec(), 1);
}

    首先我们通过getThis(),取得this指针, 然后通过Z_OBJPROP_P宏,来取得this指向的zval(一个对象object)的对象的propertis属性,然后再通过zend_hash_find取得构造函数的时候保存的资源句柄。
    之后,通过zend_fetch_resource取得构造函数创建的C++对象,之后你就可以象在C++中一样,随便使用这个C++对象了

7. 最后,我定义了个整型常量,这个也是另有它用;现在也可以在脚本中,访问这个常量了 ,其中的CONST_SC表明我们的这个常量是大小写敏感的,CONST_PERSISTENT顾名思义了。。
恩,到现在,我们的对象就已经加入到我们的模块中了,你现在就可以简单的make ; make install;然后来测试了 :)

11 Comments

  1. N品ブランドスーパーコピー通販専門店
    ルイヴィトン、シャネル、グッチ、
    ロレックス、バレンシアガ、エルメス、
    コーチ、ブラダ、クロエ大激売中
    ブランド腕時計、バッグ、財布、小物専売店
    ◆主要取扱商品 バッグ、財布、腕時計、ベルト!
    ◆全国送料一律無料
    ◆信用第一、良い品質、低価格は 私達の勝ち残りの切り札です。
    ◆当社の商品は絶対の自信が御座います。
    おすすめ人気ブランド腕時計, 最高等級時計大量入荷!
    ◆N品質シリアル付きも有り 付属品完備!
    ◆新品種類がそろっています。 品質がよい、価格が低い、実物写真!
    営業時間:年中無休
    ホームページ上でのご注文は24時間受け付けております
    エルメスバッグコピー品 http://www.kopi356.com/category-63-b0.html

  2. グッチ時計偽物
    グッチ時計偽物 August 28, 2017

    人気ブランドコピー
    2017年春夏新作が入荷!
    早くも2017年最新作が入荷激安屋は最高級のブランド スーパーコピー代引き(N品)専門店です!
    高品質のブランド コピー、ブランド コピー 代引商品や情報が満載しています。
    全部の商品は最高な素材と優れた技術で造られて、正規と比べて、品質が同じです!
    当店の商品はすべて最高品質のN品でございます、
    製造工場直営ですので他社に比べて大変お安く、最低価格を提示しております。
    皆様方のご注文をお待ちしております。
    グッチ時計偽物 http://www.nawane111.com

  3. apollotime
    apollotime April 22, 2015

    如何引用PHP扩展类?

  4. yufeng
    yufeng June 19, 2011

    哗,08年,看来我还是太懒了。
    学习。

Comments are closed.