内容简介:[ PHP 内核与扩展开发系列] 类与面向对象:访问对象实例的属性和方法
上一章节里,我们看了一下如何在 PHP 扩展里定义类与接口,这一章节我们将学习一下如何在 PHP 扩展中操作类的实例 ——对象。PHP语言中的面向对象其实是分为三个部分来实现的:class、object、refrence。class 就是我们所说的类,可以直观的理解为前面章节中所描述的 zend_class_entry。object 就是类的实际对象。每一个 zval 并不直接包含具体的object,而是通过一个索引(refrence)与其联系。也就是说,每个 class 都有很多个 object 实例,并把他们统一放在一个数组里,每个 zval 只要记住自己相应的 key 就行了。如此一来,我们在传递 zval 时,实际上传递的是一个索引,而不是内存中具体的对象数据。
调用对象方法
为了操作一个对象,我们需要先获取这个对象的实例,而这肯定会涉及调用对象的构造方法。有关如何在扩展中调用 PHP 的函数与对象的方法这里不展开描述了。首先我们先了解下一个 object 在 PHP内核中到底是如何实现的:
typedef struct _zend_object_value { zend_object_handle handle; zend_object_handlers *handlers; } zend_object_value; // 这里我们再回顾一下 zval 的值 value 的结构。 typedef union _zvalue_value { long lval; /* long value */ double dval; /* double value */ struct { char *val; int len; } str; HashTable *ht; /* hash table value */ zend_object_value obj; } zvalue_value;
如果我们有一个 zval *tmp
,那么可以通过 tmp->value.obj
来访问到最终保存对象实例的 zend_object_value 结构体,它包含两个成员:
-
zend_object_handle handle
:最终实现是一个unsigned int
值,Zend 会把每个对象放进数组里,这个handle
就是此实例的索引。所以我们在把对象当作参数传递时,只不过传递的是handle
罢了,这样对性能有利,同时也是对象的引用机制的原理。 -
zend_object_handlers *handlers
:这个里面是一组函数指针,我们可以通过它来对对象进行一些操作,比如:添加引用、获取属性等。此结构体在Zend/zend_object_handlers.h
里定义。
下面我给出这个类的 PHP 语言实现:
<?php class baby { public function __construct() { echo "a new baby!\n"; } public function hello() { echo "hello world!\n"; } } function test_call() { $obj = new baby(); $obj->hello(); }
然后我们在扩展中实现以上 test_call
函数:
zend_class_entry *baby_ce; int academy_call_user_function(zval** retval, zval* obj, char* function_name, char* paras, ...){ //用于接收参数 short int paras_count = 0; zval*** parameters = NULL; long long_tmp; char *string_tmp; zval *zval_tmp; double dou_tmp; int i; //仅与调用有关的变量 int fun_re, retval_is_null = 0; HashTable *function_table; //接收参数 paras_count = strlen(paras); if(paras_count > 0) { parameters = (zval***)emalloc(sizeof(zval**) * paras_count); va_list ap; va_start(ap,paras); for(i=0; i<paras_count; i++) { parameters[i] = (zval**)emalloc(sizeof(zval*)); switch(paras[i]) { case 's': MAKE_STD_ZVAL(*parameters[i]); string_tmp = va_arg(ap, char*); long_tmp = va_arg(ap, long); ZVAL_STRINGL(*parameters[i], string_tmp, long_tmp, 1); break; case 'l': MAKE_STD_ZVAL(*parameters[i]); long_tmp = va_arg(ap, long); ZVAL_LONG(*parameters[i], long_tmp); break; case 'd': MAKE_STD_ZVAL(*parameters[i]); dou_tmp = va_arg(ap, double); ZVAL_DOUBLE(*parameters[i], dou_tmp); break; case 'n': MAKE_STD_ZVAL(*parameters[i]); ZVAL_NULL(*parameters[i]); break; case 'z': zval_tmp = va_arg(ap, zval*); *parameters[i] = zval_tmp; break; case 'b': MAKE_STD_ZVAL(*parameters[i]); ZVAL_BOOL(*parameters[i], (int)va_arg(ap, int)); break; default: zend_error(E_ERROR, "Unsupported type:%c in walu_call_user_function", paras[i]); return 0; } } va_end(ap); } //构造参数执行call_user_function_ex zval *_function_name; MAKE_STD_ZVAL(_function_name); ZVAL_STRINGL(_function_name, function_name, strlen(function_name), 1); if (retval == NULL) { retval_is_null = 1; retval = (zval**)emalloc(sizeof(zval*)); } //开始函数调用 if (obj) { function_table = &Z_OBJCE_P(obj)->function_table; } else { function_table = (CG(function_table)); } zend_fcall_info fci; fci.size = sizeof(fci); fci.function_table = function_table; fci.object_ptr = obj ? obj : NULL; fci.function_name = _function_name; fci.retval_ptr_ptr = retval; fci.param_count = paras_count; fci.params = parameters; fci.no_separation = 1; fci.symbol_table = NULL; fun_re = zend_call_function(&fci, NULL TSRMLS_CC); //函数调用结束 if (retval_is_null == 1) { zval_ptr_dtor(retval); efree(retval); } //free掉parameter及其里面的每个元素zval**,及每个元素zval**对应的zval* //对于传进来的zval,不进行free,由参数调用者自行free zval_ptr_dtor(&_function_name); if(paras_count > 0) { for (i = 0; i < paras_count; i++) { if (paras[i] != 'z') { zval_ptr_dtor(parameters[i]); } efree(parameters[i]); } efree(parameters); } return fun_re; } ZEND_FUNCTION(test_call) { zval *obj; MAKE_STD_ZVAL(obj); object_init_ex(obj, baby_ce); //如果确认此类没有构造函数就不用调用了。 academy_call_user_function(NULL, obj, "__construct", ""); academy_call_user_function(NULL, obj, "hello", ""); zval_ptr_dtor(&obj); return; } ZEND_METHOD(baby, __construct) { printf("a new baby!\n"); } ZEND_METHOD(baby, hello) { printf("hello world!\n"); } static zend_function_entry baby_method[]={ ZEND_ME(baby, __construct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) ZEND_ME(baby, hello, NULL, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; ZEND_MINIT_FUNCTION(test) { zend_class_entry ce; INIT_CLASS_ENTRY(ce, "baby", baby_method); baby_ce = zend_register_internal_class(&ce TSRMLS_CC); return SUCCESS; }
重新编译,执行命令查看是否生效:
php -r "test_call();"
访问对象属性
我们已经看了下如何操作一个对象的方法,下面主要描述与对象属性有关的东西。有关如何对它进行定义的操作我们已经在前面章节描述过了,这里不再叙述,只讲对其的操作。
读取对象的属性
ZEND_API zval *zend_read_property(zend_class_entry *scope, zval *object, char *name, int name_length, zend_bool silent TSRMLS_DC); ZEND_API zval *zend_read_static_property(zend_class_entry *scope, char *name, int name_length, zend_bool silent TSRMLS_DC);
zend_read_property
函数用于读取对象的属性,而 zend_read_static_property
则用于读取静态属性。可以看出,静态属性是直接保存在类上的,与具体的对象无关。 silent
参数等于 0
表示如果属性不存在,则抛出一个 notice 错误;等于 1
表示如果属性不存在不报错。
如果所查的属性不存在,那么此函数将返回 IS_NULL
类型的 zval。
更新对象的属性
ZEND_API void zend_update_property(zend_class_entry *scope, zval *object, char *name, int name_length, zval *value TSRMLS_DC); ZEND_API int zend_update_static_property(zend_class_entry *scope, char *name, int name_length, zval *value TSRMLS_DC);
zend_update_property
用来更新对象的属性, zend_update_static_property
用来更新类的静态属性。如果对象或者类中没有相关的属性,函数将自动的添加上。
读写对象与类属性的实例
我们先看个 PHP 实现的例子:
class baby { public $age; public static $area; public function __construct($age, $area) { $this->age = $age; self::$area = $area; var_dump($this->age, self::$area); } }
对应在 PHP 扩展中的实现:
ZEND_METHOD(baby, __construct) { zval *age, *area; zend_class_entry *ce; ce = Z_OBJCE_P(getThis()); if( zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &age, &area) == FAILURE ) { printf("Error\n"); RETURN_NULL(); } zend_update_property(ce, getThis(), "age", sizeof("age")-1, age TSRMLS_CC); zend_update_static_property(ce, "area", sizeof("area")-1, area TSRMLS_CC); age = NULL; area = NULL; age = zend_read_property(ce, getThis(), "age", sizeof("age")-1, 0 TSRMLS_DC); php_var_dump(&age, 1 TSRMLS_CC); area = zend_read_static_property(ce, "area", sizeof("area")-1, 0 TSRMLS_DC); php_var_dump(&area, 1 TSRMLS_CC); }
一些其它的快捷函数
更新对象与类的属性函数大全:
ZEND_API void zend_update_property_null(zend_class_entry *scope, zval *object, char *name, int name_length TSRMLS_DC); ZEND_API void zend_update_property_bool(zend_class_entry *scope, zval *object, char *name, int name_length, long value TSRMLS_DC); ZEND_API void zend_update_property_long(zend_class_entry *scope, zval *object, char *name, int name_length, long value TSRMLS_DC); ZEND_API void zend_update_property_double(zend_class_entry *scope, zval *object, char *name, int name_length, double value TSRMLS_DC); ZEND_API void zend_update_property_string(zend_class_entry *scope, zval *object, char *name, int name_length, const char *value TSRMLS_DC); ZEND_API void zend_update_property_stringl(zend_class_entry *scope, zval *object, char *name, int name_length, const char *value, int value_length TSRMLS_DC); ZEND_API int zend_update_static_property_null(zend_class_entry *scope, char *name, int name_length TSRMLS_DC); ZEND_API int zend_update_static_property_bool(zend_class_entry *scope, char *name, int name_length, long value TSRMLS_DC); ZEND_API int zend_update_static_property_long(zend_class_entry *scope, char *name, int name_length, long value TSRMLS_DC); ZEND_API int zend_update_static_property_double(zend_class_entry *scope, char *name, int name_length, double value TSRMLS_DC); ZEND_API int zend_update_static_property_string(zend_class_entry *scope, char *name, int name_length, const char *value TSRMLS_DC); ZEND_API int zend_update_static_property_stringl(zend_class_entry *scope, char *name, int name_length, const char *value, int value_length TSRMLS_DC);
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- PHP内核介绍及扩展开发指南―类和对象
- [ PHP 内核与扩展开发系列] 类与面向对象:如何定义一个类
- [ PHP 内核与扩展开发系列] 类与面向对象:接口实现与类的继承
- 内核必须懂(六): 使用kgdb调试内核
- Linux内核如何替换内核函数并调用原始函数
- Linux内核工程师是怎么步入内核殿堂的?
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
学习JavaScript数据结构与算法(第2版)
[巴西] Loiane Groner / 邓 钢、孙晓博、吴 双、陈 迪、袁 源 / 人民邮电出版社 / 2017-9 / 49.00元
本书首先介绍了JavaScript 语言的基础知识以及ES6 和ES7 中引入的新功能,接下来讨论了数组、栈、队列、链表、集合、字典、散列表、树、图等数据结构,之后探讨了各种排序和搜索算法,包括冒泡排序、选择排序、插入排序、归并排序、快速排序、堆排序、计数排序、桶排序、基数排序、顺序搜索、二分搜索,然后介绍了动态规划和贪心算法等常用的高级算法以及函数式编程,最后还介绍了如何计算算法的复杂度。一起来看看 《学习JavaScript数据结构与算法(第2版)》 这本书的介绍吧!
Base64 编码/解码
Base64 编码/解码
HEX HSV 转换工具
HEX HSV 互换工具