PHP扩展开发之简单类开发
接下来我们要用扩展的形式实现以下类(演示环境:linux、php-5.5.34-src)
1 <?php 2 class Person 3 { 4 private $_name; 5 public function getName() 6 { 7 return $this -> _name; 8 } 9 public function setName($name) 10 { 11 $this -> _name = $name; 12 } 13 }
在PHP源码目录下
1 cd php-5.5.34-src 2 cd ext 3 ./ext_skel --extname=hdx //生成hdx扩展骨架 4 cd hdx
1.修改config.m4
PHP_ARG_WITH(hdx, for hdx support, [ --with-hdx Include hdx support]) //把这两行前面的dnl去掉 大概在 10-12行
2.在hdx.h添加如下
PHP_METHOD(Person, __construct);
PHP_METHOD(Person, __destruct);
PHP_METHOD(Person, getName);
PHP_METHOD(Person, setName);
3.在hdx.c下方添加相应函数
1 PHP_METHOD(Person, __construct) { 2 php_printf("__construct called."); 3 } 4 5 PHP_METHOD(Person, __destruct) { 6 php_printf("__destruct called.<br/>"); 7 } 8 9 PHP_METHOD(Person, getName) { 10 zval *self, *name; 11 self = getThis(); 12 name = zend_read_property(Z_OBJCE_P(self), self, ZEND_STRL("_name"), 0 TSRMLS_CC); 13 RETURN_STRING(Z_STRVAL_P(name), 0); 14 } 15 16 PHP_METHOD(Person, setName) { 17 char *arg = NULL; 18 int arg_len; 19 zval *value, *self; 20 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) == FAILURE) { 21 WRONG_PARAM_COUNT; 22 } 23 self = getThis(); 24 MAKE_STD_ZVAL(value); 25 ZVAL_STRINGL(value, arg, arg_len, 0); 26 SEPARATE_ZVAL_TO_MAKE_IS_REF(&value); 27 zend_update_property(Z_OBJCE_P(self), self, ZEND_STRL("_name"), value TSRMLS_CC); 28 RETURN_TRUE; 29 }
对上面的代码做一些解释:
A. 获取方法的参数信息,仍然使用zend_parse_parameters函数,与之前我们介绍过的一样;
B. 获取this指针(相对于PHP代码而言,在PHP扩展中仍然使用zval结构表示)使用getThis()函数;
C. 使用MAKE_STD_ZVAL宏申请并初始化一个zval结构,在PHP扩展中,所有的数据类型其实都是用zval结构来表示的,在本系列文章中我会单独写一篇来介绍zval。
D. 获取属性值使用zend_read_property()函数,使用zend_update_property()函数更新属性值。
4.在hdx.c 上方,初始化类:在扩展初始化函数中,注册并初始化类。
1 zend_class_entry *person_ce; 2 3 PHP_MINIT_FUNCTION(fetion_echo) 4 { 5 zend_class_entry person;
INIT_CLASS_ENTRY(person, "Person", fetion_echo_functions); 6 person_ce = zend_register_internal_class_ex(&person, NULL, NULL TSRMLS_CC); 7 8 zend_declare_property_null(person_ce, ZEND_STRL("_name"), ZEND_ACC_PRIVATE TSRMLS_CC); return SUCCESS; 9 }
使用INIT_CLASS_ENTRY宏初始化类,第二个参数指定类名,第三个参数是函数表。
5. 注册到函数:声明方法的参数,并注册到函数表中。
1 ZEND_BEGIN_ARG_INFO(arg_person_setname, 0) 2 ZEND_ARG_INFO(0, name) 3 ZEND_END_ARG_INFO() const zend_function_entry fetion_echo_functions[] = { 4 PHP_ME(Person, __construct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) 5 PHP_ME(Person, __destruct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_DTOR) 6 PHP_ME(Person, getName, NULL, ZEND_ACC_PUBLIC) 7 PHP_ME(Person, setName, arg_person_setname, ZEND_ACC_PUBLIC) 8 {NULL, NULL, NULL} /* Must be the last line in fetion_echo_functions[] */ };
类方法参数的声明与之前我们函数参数声明方式一致,在注册类方法到函数表中时使用PHP_ME宏,而不是之前使用的PHP_FE宏。
ZEND_ACC_PUBLIC:指定方法的访问修饰符
ZEND_ACC_CTOR:指定该方法为构造函数
ZEND_ACC_DTOR:指定该方法为析构函数
6.安装
1 /usr/local/php/bin/phpize 2 ./configure --with-php-config=/usr/local/php/bin/php-config 3 make && make install
然后在PHP安装目录下修改php.ini把扩展添加上
7. 运行测试:编译安装扩展后,编写一段简单的测试脚本:
1 <?php 2 $person = new Person(); 3 $person->setName("huangdongxi"); 4 echo $person->getName().'<br/>';
运行后可以看到如下输出,说明扩展工作正常:
__construct called.
huangdongxi
__destruct called.