1 /*
2 +----------------------------------------------------------------------+
3 | Zend Engine |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1998-2018 Zend Technologies Ltd. (http://www.zend.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 2.00 of the Zend license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.zend.com/license/2_00.txt. |
11 | If you did not receive a copy of the Zend license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@zend.com so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Marcus Boerger <helly@php.net> |
16 +----------------------------------------------------------------------+
17 */
18
19 /* $Id$ */
20
21 #include "zend.h"
22 #include "zend_API.h"
23 #include "zend_interfaces.h"
24 #include "zend_exceptions.h"
25
26 ZEND_API zend_class_entry *zend_ce_traversable;
27 ZEND_API zend_class_entry *zend_ce_aggregate;
28 ZEND_API zend_class_entry *zend_ce_iterator;
29 ZEND_API zend_class_entry *zend_ce_arrayaccess;
30 ZEND_API zend_class_entry *zend_ce_serializable;
31
32 /* {{{ zend_call_method
33 Only returns the returned zval if retval_ptr != NULL */
zend_call_method(zval * object,zend_class_entry * obj_ce,zend_function ** fn_proxy,const char * function_name,size_t function_name_len,zval * retval_ptr,int param_count,zval * arg1,zval * arg2)34 ZEND_API zval* zend_call_method(zval *object, zend_class_entry *obj_ce, zend_function **fn_proxy, const char *function_name, size_t function_name_len, zval *retval_ptr, int param_count, zval* arg1, zval* arg2)
35 {
36 int result;
37 zend_fcall_info fci;
38 zval retval;
39 zval params[2];
40
41 if (param_count > 0) {
42 ZVAL_COPY_VALUE(¶ms[0], arg1);
43 }
44 if (param_count > 1) {
45 ZVAL_COPY_VALUE(¶ms[1], arg2);
46 }
47
48 fci.size = sizeof(fci);
49 fci.object = object ? Z_OBJ_P(object) : NULL;
50 fci.retval = retval_ptr ? retval_ptr : &retval;
51 fci.param_count = param_count;
52 fci.params = params;
53 fci.no_separation = 1;
54
55 if (!fn_proxy && !obj_ce) {
56 /* no interest in caching and no information already present that is
57 * needed later inside zend_call_function. */
58 ZVAL_STRINGL(&fci.function_name, function_name, function_name_len);
59 result = zend_call_function(&fci, NULL);
60 zval_ptr_dtor(&fci.function_name);
61 } else {
62 zend_fcall_info_cache fcic;
63 ZVAL_UNDEF(&fci.function_name); /* Unused */
64
65 fcic.initialized = 1;
66 if (!obj_ce) {
67 obj_ce = object ? Z_OBJCE_P(object) : NULL;
68 }
69 if (!fn_proxy || !*fn_proxy) {
70 HashTable *function_table = obj_ce ? &obj_ce->function_table : EG(function_table);
71 fcic.function_handler = zend_hash_str_find_ptr(
72 function_table, function_name, function_name_len);
73 if (fcic.function_handler == NULL) {
74 /* error at c-level */
75 zend_error_noreturn(E_CORE_ERROR, "Couldn't find implementation for method %s%s%s", obj_ce ? ZSTR_VAL(obj_ce->name) : "", obj_ce ? "::" : "", function_name);
76 }
77 if (fn_proxy) {
78 *fn_proxy = fcic.function_handler;
79 }
80 } else {
81 fcic.function_handler = *fn_proxy;
82 }
83
84 fcic.calling_scope = obj_ce;
85 if (object) {
86 fcic.called_scope = Z_OBJCE_P(object);
87 } else {
88 zend_class_entry *called_scope = zend_get_called_scope(EG(current_execute_data));
89
90 if (obj_ce &&
91 (!called_scope ||
92 !instanceof_function(called_scope, obj_ce))) {
93 fcic.called_scope = obj_ce;
94 } else {
95 fcic.called_scope = called_scope;
96 }
97 }
98 fcic.object = object ? Z_OBJ_P(object) : NULL;
99 result = zend_call_function(&fci, &fcic);
100 }
101 if (result == FAILURE) {
102 /* error at c-level */
103 if (!obj_ce) {
104 obj_ce = object ? Z_OBJCE_P(object) : NULL;
105 }
106 if (!EG(exception)) {
107 zend_error_noreturn(E_CORE_ERROR, "Couldn't execute method %s%s%s", obj_ce ? ZSTR_VAL(obj_ce->name) : "", obj_ce ? "::" : "", function_name);
108 }
109 }
110 /* copy arguments back, they might be changed by references */
111 if (param_count > 0 && Z_ISREF(params[0]) && !Z_ISREF_P(arg1)) {
112 ZVAL_COPY_VALUE(arg1, ¶ms[0]);
113 }
114 if (param_count > 1 && Z_ISREF(params[1]) && !Z_ISREF_P(arg2)) {
115 ZVAL_COPY_VALUE(arg2, ¶ms[1]);
116 }
117 if (!retval_ptr) {
118 zval_ptr_dtor(&retval);
119 return NULL;
120 }
121 return retval_ptr;
122 }
123 /* }}} */
124
125 /* iterator interface, c-level functions used by engine */
126
127 /* {{{ zend_user_it_new_iterator */
zend_user_it_new_iterator(zend_class_entry * ce,zval * object,zval * retval)128 ZEND_API void zend_user_it_new_iterator(zend_class_entry *ce, zval *object, zval *retval)
129 {
130 zend_call_method_with_0_params(object, ce, &ce->iterator_funcs.zf_new_iterator, "getiterator", retval);
131 }
132 /* }}} */
133
134 /* {{{ zend_user_it_invalidate_current */
zend_user_it_invalidate_current(zend_object_iterator * _iter)135 ZEND_API void zend_user_it_invalidate_current(zend_object_iterator *_iter)
136 {
137 zend_user_iterator *iter = (zend_user_iterator*)_iter;
138
139 if (!Z_ISUNDEF(iter->value)) {
140 zval_ptr_dtor(&iter->value);
141 ZVAL_UNDEF(&iter->value);
142 }
143 }
144 /* }}} */
145
146 /* {{{ zend_user_it_dtor */
zend_user_it_dtor(zend_object_iterator * _iter)147 static void zend_user_it_dtor(zend_object_iterator *_iter)
148 {
149 zend_user_iterator *iter = (zend_user_iterator*)_iter;
150 zval *object = &iter->it.data;
151
152 zend_user_it_invalidate_current(_iter);
153 zval_ptr_dtor(object);
154 }
155 /* }}} */
156
157 /* {{{ zend_user_it_valid */
zend_user_it_valid(zend_object_iterator * _iter)158 ZEND_API int zend_user_it_valid(zend_object_iterator *_iter)
159 {
160 if (_iter) {
161 zend_user_iterator *iter = (zend_user_iterator*)_iter;
162 zval *object = &iter->it.data;
163 zval more;
164 int result;
165
166 zend_call_method_with_0_params(object, iter->ce, &iter->ce->iterator_funcs.zf_valid, "valid", &more);
167 if (Z_TYPE(more) != IS_UNDEF) {
168 result = i_zend_is_true(&more);
169 zval_ptr_dtor(&more);
170 return result ? SUCCESS : FAILURE;
171 }
172 }
173 return FAILURE;
174 }
175 /* }}} */
176
177 /* {{{ zend_user_it_get_current_data */
zend_user_it_get_current_data(zend_object_iterator * _iter)178 ZEND_API zval *zend_user_it_get_current_data(zend_object_iterator *_iter)
179 {
180 zend_user_iterator *iter = (zend_user_iterator*)_iter;
181 zval *object = &iter->it.data;
182
183 if (Z_ISUNDEF(iter->value)) {
184 zend_call_method_with_0_params(object, iter->ce, &iter->ce->iterator_funcs.zf_current, "current", &iter->value);
185 }
186 return &iter->value;
187 }
188 /* }}} */
189
190 /* {{{ zend_user_it_get_current_key_default */
191 #if 0
192 static int zend_user_it_get_current_key_default(zend_object_iterator *_iter, char **str_key, uint *str_key_len, ulong *int_key)
193 {
194 *int_key = _iter->index;
195 return HASH_KEY_IS_LONG;
196 }
197 #endif
198 /* }}} */
199
200 /* {{{ zend_user_it_get_current_key */
zend_user_it_get_current_key(zend_object_iterator * _iter,zval * key)201 ZEND_API void zend_user_it_get_current_key(zend_object_iterator *_iter, zval *key)
202 {
203 zend_user_iterator *iter = (zend_user_iterator*)_iter;
204 zval *object = &iter->it.data;
205 zval retval;
206
207 zend_call_method_with_0_params(object, iter->ce, &iter->ce->iterator_funcs.zf_key, "key", &retval);
208
209 if (Z_TYPE(retval) != IS_UNDEF) {
210 ZVAL_ZVAL(key, &retval, 1, 1);
211 } else {
212 if (!EG(exception)) {
213 zend_error(E_WARNING, "Nothing returned from %s::key()", ZSTR_VAL(iter->ce->name));
214 }
215
216 ZVAL_LONG(key, 0);
217 }
218 }
219 /* }}} */
220
221 /* {{{ zend_user_it_move_forward */
zend_user_it_move_forward(zend_object_iterator * _iter)222 ZEND_API void zend_user_it_move_forward(zend_object_iterator *_iter)
223 {
224 zend_user_iterator *iter = (zend_user_iterator*)_iter;
225 zval *object = &iter->it.data;
226
227 zend_user_it_invalidate_current(_iter);
228 zend_call_method_with_0_params(object, iter->ce, &iter->ce->iterator_funcs.zf_next, "next", NULL);
229 }
230 /* }}} */
231
232 /* {{{ zend_user_it_rewind */
zend_user_it_rewind(zend_object_iterator * _iter)233 ZEND_API void zend_user_it_rewind(zend_object_iterator *_iter)
234 {
235 zend_user_iterator *iter = (zend_user_iterator*)_iter;
236 zval *object = &iter->it.data;
237
238 zend_user_it_invalidate_current(_iter);
239 zend_call_method_with_0_params(object, iter->ce, &iter->ce->iterator_funcs.zf_rewind, "rewind", NULL);
240 }
241 /* }}} */
242
243 zend_object_iterator_funcs zend_interface_iterator_funcs_iterator = {
244 zend_user_it_dtor,
245 zend_user_it_valid,
246 zend_user_it_get_current_data,
247 zend_user_it_get_current_key,
248 zend_user_it_move_forward,
249 zend_user_it_rewind,
250 zend_user_it_invalidate_current
251 };
252
253 /* {{{ zend_user_it_get_iterator */
zend_user_it_get_iterator(zend_class_entry * ce,zval * object,int by_ref)254 static zend_object_iterator *zend_user_it_get_iterator(zend_class_entry *ce, zval *object, int by_ref)
255 {
256 zend_user_iterator *iterator;
257
258 if (by_ref) {
259 zend_throw_error(NULL, "An iterator cannot be used with foreach by reference");
260 return NULL;
261 }
262
263 iterator = emalloc(sizeof(zend_user_iterator));
264
265 zend_iterator_init((zend_object_iterator*)iterator);
266
267 ZVAL_COPY(&iterator->it.data, object);
268 iterator->it.funcs = ce->iterator_funcs.funcs;
269 iterator->ce = Z_OBJCE_P(object);
270 ZVAL_UNDEF(&iterator->value);
271 return (zend_object_iterator*)iterator;
272 }
273 /* }}} */
274
275 /* {{{ zend_user_it_get_new_iterator */
zend_user_it_get_new_iterator(zend_class_entry * ce,zval * object,int by_ref)276 ZEND_API zend_object_iterator *zend_user_it_get_new_iterator(zend_class_entry *ce, zval *object, int by_ref)
277 {
278 zval iterator;
279 zend_object_iterator *new_iterator;
280 zend_class_entry *ce_it;
281
282 zend_user_it_new_iterator(ce, object, &iterator);
283 ce_it = (Z_TYPE(iterator) == IS_OBJECT) ? Z_OBJCE(iterator) : NULL;
284
285 if (!ce_it || !ce_it->get_iterator || (ce_it->get_iterator == zend_user_it_get_new_iterator && Z_OBJ(iterator) == Z_OBJ_P(object))) {
286 if (!EG(exception)) {
287 zend_throw_exception_ex(NULL, 0, "Objects returned by %s::getIterator() must be traversable or implement interface Iterator", ce ? ZSTR_VAL(ce->name) : ZSTR_VAL(Z_OBJCE_P(object)->name));
288 }
289 zval_ptr_dtor(&iterator);
290 return NULL;
291 }
292
293 new_iterator = ce_it->get_iterator(ce_it, &iterator, by_ref);
294 zval_ptr_dtor(&iterator);
295 return new_iterator;
296 }
297 /* }}} */
298
299 /* {{{ zend_implement_traversable */
zend_implement_traversable(zend_class_entry * interface,zend_class_entry * class_type)300 static int zend_implement_traversable(zend_class_entry *interface, zend_class_entry *class_type)
301 {
302 /* check that class_type is traversable at c-level or implements at least one of 'aggregate' and 'Iterator' */
303 uint32_t i;
304
305 if (class_type->get_iterator || (class_type->parent && class_type->parent->get_iterator)) {
306 return SUCCESS;
307 }
308 for (i = 0; i < class_type->num_interfaces; i++) {
309 if (class_type->interfaces[i] == zend_ce_aggregate || class_type->interfaces[i] == zend_ce_iterator) {
310 return SUCCESS;
311 }
312 }
313 zend_error_noreturn(E_CORE_ERROR, "Class %s must implement interface %s as part of either %s or %s",
314 ZSTR_VAL(class_type->name),
315 ZSTR_VAL(zend_ce_traversable->name),
316 ZSTR_VAL(zend_ce_iterator->name),
317 ZSTR_VAL(zend_ce_aggregate->name));
318 return FAILURE;
319 }
320 /* }}} */
321
322 /* {{{ zend_implement_aggregate */
zend_implement_aggregate(zend_class_entry * interface,zend_class_entry * class_type)323 static int zend_implement_aggregate(zend_class_entry *interface, zend_class_entry *class_type)
324 {
325 uint32_t i;
326 int t = -1;
327
328 if (class_type->get_iterator) {
329 if (class_type->type == ZEND_INTERNAL_CLASS) {
330 /* inheritance ensures the class has necessary userland methods */
331 return SUCCESS;
332 } else if (class_type->get_iterator != zend_user_it_get_new_iterator) {
333 /* c-level get_iterator cannot be changed (exception being only Traversable is implmented) */
334 if (class_type->num_interfaces) {
335 for (i = 0; i < class_type->num_interfaces; i++) {
336 if (class_type->interfaces[i] == zend_ce_iterator) {
337 zend_error_noreturn(E_ERROR, "Class %s cannot implement both %s and %s at the same time",
338 ZSTR_VAL(class_type->name),
339 ZSTR_VAL(interface->name),
340 ZSTR_VAL(zend_ce_iterator->name));
341 return FAILURE;
342 }
343 if (class_type->interfaces[i] == zend_ce_traversable) {
344 t = i;
345 }
346 }
347 }
348 if (t == -1) {
349 return FAILURE;
350 }
351 }
352 }
353 class_type->iterator_funcs.zf_new_iterator = NULL;
354 class_type->get_iterator = zend_user_it_get_new_iterator;
355 return SUCCESS;
356 }
357 /* }}} */
358
359 /* {{{ zend_implement_iterator */
zend_implement_iterator(zend_class_entry * interface,zend_class_entry * class_type)360 static int zend_implement_iterator(zend_class_entry *interface, zend_class_entry *class_type)
361 {
362 if (class_type->get_iterator && class_type->get_iterator != zend_user_it_get_iterator) {
363 if (class_type->type == ZEND_INTERNAL_CLASS) {
364 /* inheritance ensures the class has the necessary userland methods */
365 return SUCCESS;
366 } else {
367 /* c-level get_iterator cannot be changed */
368 if (class_type->get_iterator == zend_user_it_get_new_iterator) {
369 zend_error_noreturn(E_ERROR, "Class %s cannot implement both %s and %s at the same time",
370 ZSTR_VAL(class_type->name),
371 ZSTR_VAL(interface->name),
372 ZSTR_VAL(zend_ce_aggregate->name));
373 }
374 return FAILURE;
375 }
376 }
377 class_type->get_iterator = zend_user_it_get_iterator;
378 class_type->iterator_funcs.zf_valid = NULL;
379 class_type->iterator_funcs.zf_current = NULL;
380 class_type->iterator_funcs.zf_key = NULL;
381 class_type->iterator_funcs.zf_next = NULL;
382 class_type->iterator_funcs.zf_rewind = NULL;
383 if (!class_type->iterator_funcs.funcs) {
384 class_type->iterator_funcs.funcs = &zend_interface_iterator_funcs_iterator;
385 }
386 return SUCCESS;
387 }
388 /* }}} */
389
390 /* {{{ zend_implement_arrayaccess */
zend_implement_arrayaccess(zend_class_entry * interface,zend_class_entry * class_type)391 static int zend_implement_arrayaccess(zend_class_entry *interface, zend_class_entry *class_type)
392 {
393 #if 0
394 /* get ht from ce */
395 if (ht->read_dimension != zend_std_read_dimension
396 || ht->write_dimension != zend_std_write_dimension
397 || ht->has_dimension != zend_std_has_dimension
398 || ht->unset_dimension != zend_std_unset_dimension) {
399 return FAILURE;
400 }
401 #endif
402 return SUCCESS;
403 }
404 /* }}}*/
405
406 /* {{{ zend_user_serialize */
zend_user_serialize(zval * object,unsigned char ** buffer,size_t * buf_len,zend_serialize_data * data)407 ZEND_API int zend_user_serialize(zval *object, unsigned char **buffer, size_t *buf_len, zend_serialize_data *data)
408 {
409 zend_class_entry * ce = Z_OBJCE_P(object);
410 zval retval;
411 int result;
412
413 zend_call_method_with_0_params(object, ce, &ce->serialize_func, "serialize", &retval);
414
415
416 if (Z_TYPE(retval) == IS_UNDEF || EG(exception)) {
417 result = FAILURE;
418 } else {
419 switch(Z_TYPE(retval)) {
420 case IS_NULL:
421 /* we could also make this '*buf_len = 0' but this allows to skip variables */
422 zval_ptr_dtor(&retval);
423 return FAILURE;
424 case IS_STRING:
425 *buffer = (unsigned char*)estrndup(Z_STRVAL(retval), Z_STRLEN(retval));
426 *buf_len = Z_STRLEN(retval);
427 result = SUCCESS;
428 break;
429 default: /* failure */
430 result = FAILURE;
431 break;
432 }
433 zval_ptr_dtor(&retval);
434 }
435
436 if (result == FAILURE && !EG(exception)) {
437 zend_throw_exception_ex(NULL, 0, "%s::serialize() must return a string or NULL", ZSTR_VAL(ce->name));
438 }
439 return result;
440 }
441 /* }}} */
442
443 /* {{{ zend_user_unserialize */
zend_user_unserialize(zval * object,zend_class_entry * ce,const unsigned char * buf,size_t buf_len,zend_unserialize_data * data)444 ZEND_API int zend_user_unserialize(zval *object, zend_class_entry *ce, const unsigned char *buf, size_t buf_len, zend_unserialize_data *data)
445 {
446 zval zdata;
447
448 if (UNEXPECTED(object_init_ex(object, ce) != SUCCESS)) {
449 return FAILURE;
450 }
451
452 ZVAL_STRINGL(&zdata, (char*)buf, buf_len);
453
454 zend_call_method_with_1_params(object, ce, &ce->unserialize_func, "unserialize", NULL, &zdata);
455
456 zval_ptr_dtor(&zdata);
457
458 if (EG(exception)) {
459 return FAILURE;
460 } else {
461 return SUCCESS;
462 }
463 }
464 /* }}} */
465
zend_class_serialize_deny(zval * object,unsigned char ** buffer,size_t * buf_len,zend_serialize_data * data)466 ZEND_API int zend_class_serialize_deny(zval *object, unsigned char **buffer, size_t *buf_len, zend_serialize_data *data) /* {{{ */
467 {
468 zend_class_entry *ce = Z_OBJCE_P(object);
469 zend_throw_exception_ex(NULL, 0, "Serialization of '%s' is not allowed", ZSTR_VAL(ce->name));
470 return FAILURE;
471 }
472 /* }}} */
473
zend_class_unserialize_deny(zval * object,zend_class_entry * ce,const unsigned char * buf,size_t buf_len,zend_unserialize_data * data)474 ZEND_API int zend_class_unserialize_deny(zval *object, zend_class_entry *ce, const unsigned char *buf, size_t buf_len, zend_unserialize_data *data) /* {{{ */
475 {
476 zend_throw_exception_ex(NULL, 0, "Unserialization of '%s' is not allowed", ZSTR_VAL(ce->name));
477 return FAILURE;
478 }
479 /* }}} */
480
481 /* {{{ zend_implement_serializable */
zend_implement_serializable(zend_class_entry * interface,zend_class_entry * class_type)482 static int zend_implement_serializable(zend_class_entry *interface, zend_class_entry *class_type)
483 {
484 if (class_type->parent
485 && (class_type->parent->serialize || class_type->parent->unserialize)
486 && !instanceof_function_ex(class_type->parent, zend_ce_serializable, 1)) {
487 return FAILURE;
488 }
489 if (!class_type->serialize) {
490 class_type->serialize = zend_user_serialize;
491 }
492 if (!class_type->unserialize) {
493 class_type->unserialize = zend_user_unserialize;
494 }
495 return SUCCESS;
496 }
497 /* }}}*/
498
499 /* {{{ function tables */
500 const zend_function_entry zend_funcs_aggregate[] = {
501 ZEND_ABSTRACT_ME(iterator, getIterator, NULL)
502 ZEND_FE_END
503 };
504
505 const zend_function_entry zend_funcs_iterator[] = {
506 ZEND_ABSTRACT_ME(iterator, current, NULL)
507 ZEND_ABSTRACT_ME(iterator, next, NULL)
508 ZEND_ABSTRACT_ME(iterator, key, NULL)
509 ZEND_ABSTRACT_ME(iterator, valid, NULL)
510 ZEND_ABSTRACT_ME(iterator, rewind, NULL)
511 ZEND_FE_END
512 };
513
514 const zend_function_entry *zend_funcs_traversable = NULL;
515
516 ZEND_BEGIN_ARG_INFO_EX(arginfo_arrayaccess_offset, 0, 0, 1)
517 ZEND_ARG_INFO(0, offset)
518 ZEND_END_ARG_INFO()
519
520 ZEND_BEGIN_ARG_INFO_EX(arginfo_arrayaccess_offset_get, 0, 0, 1) /* actually this should be return by ref but atm cannot be */
521 ZEND_ARG_INFO(0, offset)
522 ZEND_END_ARG_INFO()
523
524 ZEND_BEGIN_ARG_INFO_EX(arginfo_arrayaccess_offset_value, 0, 0, 2)
525 ZEND_ARG_INFO(0, offset)
526 ZEND_ARG_INFO(0, value)
527 ZEND_END_ARG_INFO()
528
529 const zend_function_entry zend_funcs_arrayaccess[] = {
530 ZEND_ABSTRACT_ME(arrayaccess, offsetExists, arginfo_arrayaccess_offset)
531 ZEND_ABSTRACT_ME(arrayaccess, offsetGet, arginfo_arrayaccess_offset_get)
532 ZEND_ABSTRACT_ME(arrayaccess, offsetSet, arginfo_arrayaccess_offset_value)
533 ZEND_ABSTRACT_ME(arrayaccess, offsetUnset, arginfo_arrayaccess_offset)
534 ZEND_FE_END
535 };
536
537 ZEND_BEGIN_ARG_INFO(arginfo_serializable_serialize, 0)
538 ZEND_ARG_INFO(0, serialized)
539 ZEND_END_ARG_INFO()
540
541 const zend_function_entry zend_funcs_serializable[] = {
542 ZEND_ABSTRACT_ME(serializable, serialize, NULL)
543 ZEND_FENTRY(unserialize, NULL, arginfo_serializable_serialize, ZEND_ACC_PUBLIC|ZEND_ACC_ABSTRACT|ZEND_ACC_CTOR)
544 ZEND_FE_END
545 };
546 /* }}} */
547
548 /* {{{ zend_register_interfaces */
zend_register_interfaces(void)549 ZEND_API void zend_register_interfaces(void)
550 {
551 REGISTER_MAGIC_INTERFACE(traversable, Traversable);
552
553 REGISTER_MAGIC_INTERFACE(aggregate, IteratorAggregate);
554 REGISTER_MAGIC_IMPLEMENT(aggregate, traversable);
555
556 REGISTER_MAGIC_INTERFACE(iterator, Iterator);
557 REGISTER_MAGIC_IMPLEMENT(iterator, traversable);
558
559 REGISTER_MAGIC_INTERFACE(arrayaccess, ArrayAccess);
560
561 REGISTER_MAGIC_INTERFACE(serializable, Serializable);
562 }
563 /* }}} */
564
565 /*
566 * Local variables:
567 * tab-width: 4
568 * c-basic-offset: 4
569 * indent-tabs-mode: t
570 * End:
571 */
572