PHP扩展开发:简单类实现
话说之前有写过如何在vs2008下开发PHP扩展,今天我们来实现一个简单类。
对于一个类,一般包括类定义,声明成员变量,声明成员函数,定义类常量,
对于类继承,重载,接口实现等不在这里说明。
首先我们看下这个简单类的PHP实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <?php /** * 简单类示例的PHP实现 2010-11-03 sz * @author phppan.p#gmail.com http://www.phppan.com * 哥学社成员(http://www.blog-brother.com/) * @package test */ class Phppan { private $_name; CONST URL = 'http://www.phppan.com'; public function __construct() { } public function getName() { return $this->_name; } public function setName($name) { $this->_name = $name; } } |
我们同样使用在之前创建的martin扩展
下面说明其实现过程:
1、声明类的成员函数
在php_martin.h文件中声明类的成员函数,如下所示代码:
1 2 3 4 5 | PHP_METHOD(phppan, __construct); PHP_METHOD(phppan, __destruct); PHP_METHOD(phppan, getName); PHP_METHOD(phppan, setName); |
2、定义类的成员函数
在martin.c文件中给出这4个函数的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | /** {{{ */ PHP_METHOD(phppan, __construct) { } /* }}} */ /** {{{ */ PHP_METHOD(phppan, __destruct) { } /* }}} */ /** {{{ */ PHP_METHOD(phppan, getName) { zval *self, *name; self = getThis(); name = zend_read_property(Z_OBJCE_P(self), self, ZEND_STRL("_name"), 0 TSRMLS_CC); RETURN_STRING(Z_STRVAL_P(name), 0); } /* }}} */ /** {{{ */ PHP_METHOD(phppan, setName) { char *arg = NULL; int arg_len; zval *value, *self; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) == FAILURE) { WRONG_PARAM_COUNT; } self = getThis(); MAKE_STD_ZVAL(value); ZVAL_STRING(value, arg, arg_len, 0); SEPARATE_ZVAL_TO_MAKE_IS_REF(&value); zend_update_property(Z_OBJCE_P(self), self, ZEND_STRL("_name"), value TSRMLS_CC); RETURN_TRUE; } /* }}} */ |
3、注册这些函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /* {{{ martin_functions[] * * Every user visible function must have an entry in martin_functions[]. */ const zend_function_entry martin_functions[] = { PHP_FE(martin_echo, NULL) PHP_ME(phppan, __construct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) PHP_ME(phppan, __destruct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_DTOR) PHP_ME(phppan, getName, NULL, ZEND_ACC_PUBLIC) PHP_ME(phppan, setName, setName_args, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} /* Must be the last line in martin_functions[] */ }; /* }}} */ |
4、在扩展模块初始化时初始化类,并声明成员变量和常量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | zend_class_entry *phppan_ce; /* 类方法的参数 */ ZEND_BEGIN_ARG_INFO(setName_args, 0) ZEND_ARG_INFO(0, name) ZEND_END_ARG_INFO() /* {{{ PHP_MINIT_FUNCTION */ PHP_MINIT_FUNCTION(martin) { /* If you have INI entries, uncomment these lines REGISTER_INI_ENTRIES(); */ zend_class_entry phppan; INIT_CLASS_ENTRY(phppan, "Phppan", martin_functions); phppan_ce = zend_register_internal_class_ex(&phppan, NULL, NULL TSRMLS_CC); /* 声明常量URL */ zend_declare_class_constant_stringl(phppan_ce, ZEND_STRL("URL"), ZEND_STRL("http://www.phppan.com") TSRMLS_CC); /* 声明私有成员变量 _name */ zend_declare_property_null(phppan_ce, ZEND_STRL("_name"), ZEND_ACC_PRIVATE TSRMLS_CC); return SUCCESS; } /* }}} */ |
以上为所有代码
在这些代码里面有一些东西需要说明一下:
1、类方法的定义与php的单个函数的定义一样,使用zend_function_entry结构数组,不同的是单个方法使用PHP_FE宏,
类方法的定义使用PHP_ME宏,在h文件中使用ZEND_METHOD声明,普通的函数使用ZEND_FUNCTION声明。
PHP_ME宏
定义在php.h中
#define PHP_ME ZEND_ME
#define ZEND_ME(classname, name, arg_info, flags) ZEND_FENTRY(name, ZEND_MN(classname##_##name), arg_info, flags)
2、在注册类时把该结构体作为参数交给相关的类注册方法即可
ZEND_BEGIN_ARG_INFO(setName_args, 0)
ZEND_ARG_INFO(0, name)
ZEND_END_ARG_INFO()
3、取this变量使用getThis();
4、使用zend_read_property读取类成员变量,返回的是zval 指针类型
5、使用zend_update_property更新类成员变量。
6、初始化类使用INIT_CLASS_ENTRY宏
7、注册类使用zend_register_internal_class_ex函数
function registration failed duplicate name.问题的解决方法:
这个由于我们在写这个简单类时偷了一下懒,将martin_functions作为模块的函数也将它作为了类的方法。
解决方法是替换上面的martin_functions数组,增加phppan_functions,并且在注册时使用phppan_functions,在模块的functions字段使用martin_functions
/* {{{ martin_functions[] * * Every user visible function must have an entry in martin_functions[]. */ const zend_function_entry martin_functions[] = { {NULL, NULL, NULL} /* Must be the last line in martin_functions[] */ }; const zend_function_entry phppan_functions[] = { PHP_ME(phppan, __construct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) PHP_ME(phppan, __destruct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_DTOR) PHP_ME(phppan, getName, NULL, ZEND_ACC_PUBLIC) PHP_ME(phppan, setName, setName_args, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} /* Must be the last line in martin_functions[] */ }; /* }}} */ INIT_CLASS_ENTRY(phppan, "Phppan", phppan_functions); |
动作这么快. 我才hello world呢..
已经hello world过了,hoho
Pingback引用通告: Thinking In LAMP Blog » Blog Archive » PHP每月通讯(2010年12月)
学习小胖同学的读书笔记,更好的帮助我理解PHP
你好,我对两个C++类进行了扩展,当中都添加了__construct和__destruct方法。在同时加载这两个扩展的时候,提示函数重定义,无法注册的问题。function registration failed duplicate name.使得后一个扩展无法加载。请问我应该怎么解决呢?
这个由于我们在写这个简单类时偷了一下懒,将martin_functions作为模块的函数也将它作为了类的方法。
解决方法是替换上面的martin_functions数组,增加phppan_functions,并且在注册时使用phppan_functions,在模块的functions字段使用martin_functions,具体内容见原文的最后。
代码片段:
2、定义类的成员函数
line 43 ZVAL_STRING(value, arg, arg_len, 0);
多了一个参数
感谢指正,这是我的问题,不过这个代码应该也是可以编译通过的。
在PHP中,三个参数的定义是:#define ZVAL_STRING(z, s, duplicate)
四个参数的定义是:#define ZVAL_STRINGL(z, s, l, duplicate)
哎呦我去, function registration failed duplicate name.折腾了好久的问题,在这儿找到答案了
虽然按照你说的方法做了, 但我仍没明白啥意思, 除了换个名之外