preface
在之前的文章中,我们已经实现了一些 object handlers 来将我们的 ArrayBuffer 整合到 php 中。但是美中不足的是,我们的 ArrayBufferView 并不支持迭代器操作。也就是它不能像 php 中的数组那样使用foreach
来遍历。
那么,我们接下来就来看看迭代器在内核中是如何实现的,并且给我们的 ArrayBufferView 也增加一个迭代器。
get_iterator handler
内核中的迭代器跟用户端的IteratorAggregate
接口功能是一样的。一个具有迭代功能的类都有一个get_iterator
处理器,它会返回一个zend_object_iterator *
类型的结构,该结构定义如下(位于 phpsrc/Zend/zend_iterators.h 中):
1struct _zend_object_iterator {
2 void *data;
3 zend_object_iterator_funcs *funcs;
4 ulong index; /* private to fe_reset/fe_fetch opcodes */
5};
其中的index
成员就是内核中用以实现foreach
的,它的值会在每次迭代后增加。funcs
成员包含了不同的迭代操作:
1typedef struct _zend_object_iterator_funcs {
2 /* release all resources associated with this iterator instance */
3 void (*dtor)(zend_object_iterator *iter TSRMLS_DC);
4
5 /* check for end of iteration (FAILURE or SUCCESS if data is valid) */
6 int (*valid)(zend_object_iterator *iter TSRMLS_DC);
7
8 /* fetch the item data for the current element */
9 void (*get_current_data)(zend_object_iterator *iter, zval ***data TSRMLS_DC);
10
11 /* fetch the key for the current element (optional, may be NULL). The key
12 * should be written into the provided zval* using the ZVAL_* macros. If
13 * this handler is not provided auto-incrementing integer keys will be
14 * used. */
15 void (*get_current_key)(zend_object_iterator *iter, zval *key TSRMLS_DC);
16
17 /* step forwards to next element */
18 void (*move_forward)(zend_object_iterator *iter TSRMLS_DC);
19
20 /* rewind to start of data (optional, may be NULL) */
21 void (*rewind)(zend_object_iterator *iter TSRMLS_DC);
22
23 /* invalidate current value/key (optional, may be NULL) */
24 void (*invalidate_current)(zend_object_iterator *iter TSRMLS_DC);
25} zend_object_iterator_funcs;
这些处理器跟Iterator
接口中的抽象方法类似,只不过名字不同罢了。唯一没有对应用户端接口的是invalidate_current
,它可以被用来销毁当前的key/value
。
然而这个操作几乎不会被用到,通常foreach
也不会取调用它。
最后一个成员data
可以用来存放一些自定义的数据。通常类似于zend_object
的操作,我们需要对它的结构进行扩展。
为了给 ArrayBufferView 添加迭代器,我们需要保存一些信息:首先我们需要有一个 buffer view 对象的引用,我们可以用data
来存放该引用。其次我们还需要存储buffer_view_object
,
这样可以避免我们在每种迭代操作中都要获取它。最后我们还需要存放当前元素的offset
和当前元素的zval *
。
1typedef struct _buffer_view_iterator {
2 zend_object_iterator intern;
3 buffer_view_object *view;
4 size_t offset;
5 zval *current;
6} buffer_view_iterator;
下面我们来声明一个zend_object_iterator_funcs
结构体:
1static zend_object_iterator_funcs linger_buffer_view_iterator_funcs = {
2 linger_buffer_view_iterator_dtor,
3 linger_buffer_view_iterator_valid,
4 linger_buffer_view_iterator_get_current_data,
5 linger_buffer_view_iterator_get_current_key,
6 linger_buffer_view_iterator_move_forward,
7 linger_buffer_view_iterator_rewind
8};
接下来我们来实现get_iterator
handler。这个处理器接受一个class entry
,一个object
,还有一个标记迭代是否为引用,然后返回
zend_object_iterator *
。我们要做的就是创建一个 iterator,并对其中的元素做初始化:
1zend_object_iterator *linger_buffer_view_get_iterator(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC)
2{
3 buffer_view_iterator *iterator;
4 if (by_ref) {
5 zend_throw_exception(NULL, "Cannot interate buffer view by refererce", 0 TSRMLS_CC);
6 return NULL;
7 }
8 iterator = emalloc(sizeof(buffer_view_iterator));
9 iterator->intern.funcs = &linger_buffer_view_iterator_funcs;
10 iterator->intern.data = object;
11 Z_ADDREF_P(object);
12
13 iterator->view = zend_object_store_get_object(object TSRMLS_CC);
14 iterator->offset = 0;
15 iterator->current = NULL;
16 return (zend_object_iterator *) iterator;
17}
然后我们需要调整注册 buffer view class 的宏函数
1#define REGISTER_ARRAY_BUFFER_VIEW_CLASS(class_name, type) \
2 do { \
3 INIT_CLASS_ENTRY(ce, #class_name, linger_array_buffer_view_methods); \
4 type##_array_ce = zend_register_internal_class(&ce TSRMLS_CC); \
5 type##_array_ce->create_object = linger_array_buffer_view_create_object; \
6 type##_array_ce->get_iterator = linger_buffer_view_get_iterator; \
7 type##_array_ce->iterator_funcs.funcs = &linger_buffer_view_iterator_funcs; \
8 zend_class_implements(type##_array_ce TSRMLS_CC, 1, zend_ce_traversable); \
9 } while (0)
Iterator functions
1static void linger_buffer_view_iterator_dtor(zend_object_iterator *intern TSRMLS_DC)
2{
3 buffer_view_iterator *iterator = (buffer_view_iterator *) intern;
4 if (iterator->current) {
5 zval_ptr_dtor(&iterator->current);
6 }
7 zval_ptr_dtor((zval **) &intern->data);
8 efree(iterator);
9}
10
11static int linger_buffer_view_iterator_valid(zend_object_iterator *intern TSRMLS_DC)
12{
13 buffer_view_iterator *iterator = (buffer_view_iterator *)intern;
14 return iterator->offset < iterator->view->length ? SUCCESS : FAILURE;
15}
16
17static void linger_buffer_view_iterator_get_current_data(zend_object_iterator *intern, zval ***data TSRMLS_DC)
18{
19 buffer_view_iterator *iterator = (buffer_view_iterator *)intern;
20 if (iterator->current) {
21 zval_ptr_dtor(&iterator->current);
22 }
23
24 if (iterator->offset < iterator->view->length) {
25 iterator->current = linger_buffer_view_offset_get(iterator->view, iterator->offset);
26 *data = &iterator->current;
27 } else {
28 *data = NULL;
29 }
30}
31
32static void linger_buffer_view_iterator_get_current_key(zend_object_iterator *intern, zval *key TSRMLS_DC)
33{
34 buffer_view_iterator *iterator = (buffer_view_iterator *)intern;
35 ZVAL_LONG(key, iterator->offset);
36}
37
38static void linger_buffer_view_iterator_move_forward(zend_object_iterator *intern TSRMLS_DC)
39{
40 buffer_view_iterator *iterator = (buffer_view_iterator *)intern;
41 iterator->offset++;
42}
43
44static void linger_buffer_view_iterator_rewind(zend_object_iterator *intern TSRMLS_DC)
45{
46 buffer_view_iterator *iterator = (buffer_view_iterator *) iterator;
47 iterator->offset = 0;
48 iterator->current = NULL;
49}
代码依然平淡无奇,所以没什么好解释的。
至此我们就完成了 ArrayBufferView 类的迭代器操作。完整代码可以访问:https://github.com/iliubang/php-ArrayBuffer.git
评论