1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2014 The PHP Group                                |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.01 of the PHP 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.php.net/license/3_01.txt                                  |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Authors: Marcus Boerger <helly@php.net>                              |
16   +----------------------------------------------------------------------+
17 */
18
19/* $Id$ */
20
21#ifdef HAVE_CONFIG_H
22# include "config.h"
23#endif
24
25#include "php.h"
26#include "php_ini.h"
27#include "ext/standard/info.h"
28#include "ext/standard/php_var.h"
29#include "ext/standard/php_smart_str.h"
30#include "zend_interfaces.h"
31#include "zend_exceptions.h"
32
33#include "php_spl.h"
34#include "spl_functions.h"
35#include "spl_engine.h"
36#include "spl_iterators.h"
37#include "spl_array.h"
38#include "spl_exceptions.h"
39
40zend_object_handlers spl_handler_ArrayObject;
41PHPAPI zend_class_entry  *spl_ce_ArrayObject;
42
43zend_object_handlers spl_handler_ArrayIterator;
44PHPAPI zend_class_entry  *spl_ce_ArrayIterator;
45PHPAPI zend_class_entry  *spl_ce_RecursiveArrayIterator;
46
47#define SPL_ARRAY_STD_PROP_LIST      0x00000001
48#define SPL_ARRAY_ARRAY_AS_PROPS     0x00000002
49#define SPL_ARRAY_CHILD_ARRAYS_ONLY  0x00000004
50#define SPL_ARRAY_OVERLOADED_REWIND  0x00010000
51#define SPL_ARRAY_OVERLOADED_VALID   0x00020000
52#define SPL_ARRAY_OVERLOADED_KEY     0x00040000
53#define SPL_ARRAY_OVERLOADED_CURRENT 0x00080000
54#define SPL_ARRAY_OVERLOADED_NEXT    0x00100000
55#define SPL_ARRAY_IS_REF             0x01000000
56#define SPL_ARRAY_IS_SELF            0x02000000
57#define SPL_ARRAY_USE_OTHER          0x04000000
58#define SPL_ARRAY_INT_MASK           0xFFFF0000
59#define SPL_ARRAY_CLONE_MASK         0x0300FFFF
60
61#define SPL_ARRAY_METHOD_NO_ARG             0
62#define SPL_ARRAY_METHOD_USE_ARG            1
63#define SPL_ARRAY_METHOD_MAY_USER_ARG       2
64
65typedef struct _spl_array_object {
66    zend_object       std;
67    zval              *array;
68    zval              *retval;
69    HashPosition      pos;
70    ulong             pos_h;
71    int               ar_flags;
72    int               is_self;
73    zend_function     *fptr_offset_get;
74    zend_function     *fptr_offset_set;
75    zend_function     *fptr_offset_has;
76    zend_function     *fptr_offset_del;
77    zend_function     *fptr_count;
78    zend_class_entry* ce_get_iterator;
79    HashTable              *debug_info;
80    unsigned char          nApplyCount;
81} spl_array_object;
82
83static inline HashTable *spl_array_get_hash_table(spl_array_object* intern, int check_std_props TSRMLS_DC) { /* {{{ */
84    if ((intern->ar_flags & SPL_ARRAY_IS_SELF) != 0) {
85        if (!intern->std.properties) {
86            rebuild_object_properties(&intern->std);
87        }
88        return intern->std.properties;
89    } else if ((intern->ar_flags & SPL_ARRAY_USE_OTHER) && (check_std_props == 0 || (intern->ar_flags & SPL_ARRAY_STD_PROP_LIST) == 0) && Z_TYPE_P(intern->array) == IS_OBJECT) {
90        spl_array_object *other  = (spl_array_object*)zend_object_store_get_object(intern->array TSRMLS_CC);
91        return spl_array_get_hash_table(other, check_std_props TSRMLS_CC);
92    } else if ((intern->ar_flags & ((check_std_props ? SPL_ARRAY_STD_PROP_LIST : 0) | SPL_ARRAY_IS_SELF)) != 0) {
93        if (!intern->std.properties) {
94            rebuild_object_properties(&intern->std);
95        }
96        return intern->std.properties;
97    } else {
98        return HASH_OF(intern->array);
99    }
100} /* }}} */
101
102static void spl_array_rewind(spl_array_object *intern TSRMLS_DC);
103
104static void spl_array_update_pos(spl_array_object* intern) /* {{{ */
105{
106    Bucket *pos = intern->pos;
107    if (pos != NULL) {
108        intern->pos_h = pos->h;
109    }
110} /* }}} */
111
112static void spl_array_set_pos(spl_array_object* intern, HashPosition pos) /* {{{ */
113{
114    intern->pos = pos;
115    spl_array_update_pos(intern);
116} /* }}} */
117
118SPL_API int spl_hash_verify_pos_ex(spl_array_object * intern, HashTable * ht TSRMLS_DC) /* {{{ */
119{
120    Bucket *p;
121
122/*  IS_CONSISTENT(ht);*/
123
124/*  HASH_PROTECT_RECURSION(ht);*/
125    p = ht->arBuckets[intern->pos_h & ht->nTableMask];
126    while (p != NULL) {
127        if (p == intern->pos) {
128            return SUCCESS;
129        }
130        p = p->pNext;
131    }
132/*  HASH_UNPROTECT_RECURSION(ht); */
133    spl_array_rewind(intern TSRMLS_CC);
134    return FAILURE;
135
136} /* }}} */
137
138SPL_API int spl_hash_verify_pos(spl_array_object * intern TSRMLS_DC) /* {{{ */
139{
140    HashTable *ht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
141    return spl_hash_verify_pos_ex(intern, ht TSRMLS_CC);
142}
143/* }}} */
144
145/* {{{ spl_array_object_free_storage */
146static void spl_array_object_free_storage(void *object TSRMLS_DC)
147{
148    spl_array_object *intern = (spl_array_object *)object;
149
150    zend_object_std_dtor(&intern->std TSRMLS_CC);
151
152    zval_ptr_dtor(&intern->array);
153    zval_ptr_dtor(&intern->retval);
154
155    if (intern->debug_info != NULL) {
156        zend_hash_destroy(intern->debug_info);
157        efree(intern->debug_info);
158    }
159
160    efree(object);
161}
162/* }}} */
163
164zend_object_iterator *spl_array_get_iterator(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC);
165
166/* {{{ spl_array_object_new_ex */
167static zend_object_value spl_array_object_new_ex(zend_class_entry *class_type, spl_array_object **obj, zval *orig, int clone_orig TSRMLS_DC)
168{
169    zend_object_value retval = {0};
170    spl_array_object *intern;
171    zval *tmp;
172    zend_class_entry * parent = class_type;
173    int inherited = 0;
174
175    intern = emalloc(sizeof(spl_array_object));
176    memset(intern, 0, sizeof(spl_array_object));
177    *obj = intern;
178    ALLOC_INIT_ZVAL(intern->retval);
179
180    zend_object_std_init(&intern->std, class_type TSRMLS_CC);
181    object_properties_init(&intern->std, class_type);
182
183    intern->ar_flags = 0;
184    intern->debug_info       = NULL;
185    intern->ce_get_iterator = spl_ce_ArrayIterator;
186    if (orig) {
187        spl_array_object *other = (spl_array_object*)zend_object_store_get_object(orig TSRMLS_CC);
188
189        intern->ar_flags &= ~ SPL_ARRAY_CLONE_MASK;
190        intern->ar_flags |= (other->ar_flags & SPL_ARRAY_CLONE_MASK);
191        intern->ce_get_iterator = other->ce_get_iterator;
192        if (clone_orig) {
193            intern->array = other->array;
194            if (Z_OBJ_HT_P(orig) == &spl_handler_ArrayObject) {
195                MAKE_STD_ZVAL(intern->array);
196                array_init(intern->array);
197                zend_hash_copy(HASH_OF(intern->array), HASH_OF(other->array), (copy_ctor_func_t) zval_add_ref, &tmp, sizeof(zval*));
198            }
199            if (Z_OBJ_HT_P(orig) == &spl_handler_ArrayIterator) {
200                Z_ADDREF_P(other->array);
201            }
202        } else {
203            intern->array = orig;
204            Z_ADDREF_P(intern->array);
205            intern->ar_flags |= SPL_ARRAY_IS_REF | SPL_ARRAY_USE_OTHER;
206        }
207    } else {
208        MAKE_STD_ZVAL(intern->array);
209        array_init(intern->array);
210        intern->ar_flags &= ~SPL_ARRAY_IS_REF;
211    }
212
213    retval.handle = zend_objects_store_put(intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, (zend_objects_free_object_storage_t) spl_array_object_free_storage, NULL TSRMLS_CC);
214    while (parent) {
215        if (parent == spl_ce_ArrayIterator || parent == spl_ce_RecursiveArrayIterator) {
216            retval.handlers = &spl_handler_ArrayIterator;
217            class_type->get_iterator = spl_array_get_iterator;
218            break;
219        } else if (parent == spl_ce_ArrayObject) {
220            retval.handlers = &spl_handler_ArrayObject;
221            break;
222        }
223        parent = parent->parent;
224        inherited = 1;
225    }
226    if (!parent) { /* this must never happen */
227        php_error_docref(NULL TSRMLS_CC, E_COMPILE_ERROR, "Internal compiler error, Class is not child of ArrayObject or ArrayIterator");
228    }
229    if (inherited) {
230        zend_hash_find(&class_type->function_table, "offsetget",    sizeof("offsetget"),    (void **) &intern->fptr_offset_get);
231        if (intern->fptr_offset_get->common.scope == parent) {
232            intern->fptr_offset_get = NULL;
233        }
234        zend_hash_find(&class_type->function_table, "offsetset",    sizeof("offsetset"),    (void **) &intern->fptr_offset_set);
235        if (intern->fptr_offset_set->common.scope == parent) {
236            intern->fptr_offset_set = NULL;
237        }
238        zend_hash_find(&class_type->function_table, "offsetexists", sizeof("offsetexists"), (void **) &intern->fptr_offset_has);
239        if (intern->fptr_offset_has->common.scope == parent) {
240            intern->fptr_offset_has = NULL;
241        }
242        zend_hash_find(&class_type->function_table, "offsetunset",  sizeof("offsetunset"),  (void **) &intern->fptr_offset_del);
243        if (intern->fptr_offset_del->common.scope == parent) {
244            intern->fptr_offset_del = NULL;
245        }
246        zend_hash_find(&class_type->function_table, "count",        sizeof("count"),        (void **) &intern->fptr_count);
247        if (intern->fptr_count->common.scope == parent) {
248            intern->fptr_count = NULL;
249        }
250    }
251    /* Cache iterator functions if ArrayIterator or derived. Check current's */
252    /* cache since only current is always required */
253    if (retval.handlers == &spl_handler_ArrayIterator) {
254        if (!class_type->iterator_funcs.zf_current) {
255            zend_hash_find(&class_type->function_table, "rewind",  sizeof("rewind"),  (void **) &class_type->iterator_funcs.zf_rewind);
256            zend_hash_find(&class_type->function_table, "valid",   sizeof("valid"),   (void **) &class_type->iterator_funcs.zf_valid);
257            zend_hash_find(&class_type->function_table, "key",     sizeof("key"),     (void **) &class_type->iterator_funcs.zf_key);
258            zend_hash_find(&class_type->function_table, "current", sizeof("current"), (void **) &class_type->iterator_funcs.zf_current);
259            zend_hash_find(&class_type->function_table, "next",    sizeof("next"),    (void **) &class_type->iterator_funcs.zf_next);
260        }
261        if (inherited) {
262            if (class_type->iterator_funcs.zf_rewind->common.scope  != parent) intern->ar_flags |= SPL_ARRAY_OVERLOADED_REWIND;
263            if (class_type->iterator_funcs.zf_valid->common.scope   != parent) intern->ar_flags |= SPL_ARRAY_OVERLOADED_VALID;
264            if (class_type->iterator_funcs.zf_key->common.scope     != parent) intern->ar_flags |= SPL_ARRAY_OVERLOADED_KEY;
265            if (class_type->iterator_funcs.zf_current->common.scope != parent) intern->ar_flags |= SPL_ARRAY_OVERLOADED_CURRENT;
266            if (class_type->iterator_funcs.zf_next->common.scope    != parent) intern->ar_flags |= SPL_ARRAY_OVERLOADED_NEXT;
267        }
268    }
269
270    spl_array_rewind(intern TSRMLS_CC);
271    return retval;
272}
273/* }}} */
274
275/* {{{ spl_array_object_new */
276static zend_object_value spl_array_object_new(zend_class_entry *class_type TSRMLS_DC)
277{
278    spl_array_object *tmp;
279    return spl_array_object_new_ex(class_type, &tmp, NULL, 0 TSRMLS_CC);
280}
281/* }}} */
282
283/* {{{ spl_array_object_clone */
284static zend_object_value spl_array_object_clone(zval *zobject TSRMLS_DC)
285{
286    zend_object_value new_obj_val;
287    zend_object *old_object;
288    zend_object *new_object;
289    zend_object_handle handle = Z_OBJ_HANDLE_P(zobject);
290    spl_array_object *intern;
291
292    old_object = zend_objects_get_address(zobject TSRMLS_CC);
293    new_obj_val = spl_array_object_new_ex(old_object->ce, &intern, zobject, 1 TSRMLS_CC);
294    new_object = &intern->std;
295
296    zend_objects_clone_members(new_object, new_obj_val, old_object, handle TSRMLS_CC);
297
298    return new_obj_val;
299}
300/* }}} */
301
302static zval **spl_array_get_dimension_ptr_ptr(int check_inherited, zval *object, zval *offset, int type TSRMLS_DC) /* {{{ */
303{
304    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
305    zval **retval;
306    long index;
307    HashTable *ht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
308
309    if (!offset) {
310        return &EG(uninitialized_zval_ptr);
311    }
312
313    if ((type == BP_VAR_W || type == BP_VAR_RW) && (ht->nApplyCount > 0)) {
314        zend_error(E_WARNING, "Modification of ArrayObject during sorting is prohibited");
315        return &EG(error_zval_ptr);;
316    }
317
318    switch(Z_TYPE_P(offset)) {
319    case IS_NULL:
320        Z_STRVAL_P(offset) = "";
321        Z_STRLEN_P(offset) = 0;
322    case IS_STRING:
323        if (zend_symtable_find(ht, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, (void **) &retval) == FAILURE) {
324            switch (type) {
325                case BP_VAR_R:
326                    zend_error(E_NOTICE, "Undefined index: %s", Z_STRVAL_P(offset));
327                case BP_VAR_UNSET:
328                case BP_VAR_IS:
329                    retval = &EG(uninitialized_zval_ptr);
330                    break;
331                case BP_VAR_RW:
332                    zend_error(E_NOTICE,"Undefined index: %s", Z_STRVAL_P(offset));
333                case BP_VAR_W: {
334                    zval *value;
335                    ALLOC_INIT_ZVAL(value);
336                    zend_symtable_update(ht, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, (void**)&value, sizeof(void*), (void **)&retval);
337                }
338            }
339        }
340        return retval;
341    case IS_RESOURCE:
342        zend_error(E_STRICT, "Resource ID#%ld used as offset, casting to integer (%ld)", Z_LVAL_P(offset), Z_LVAL_P(offset));
343    case IS_DOUBLE:
344    case IS_BOOL:
345    case IS_LONG:
346        if (offset->type == IS_DOUBLE) {
347            index = (long)Z_DVAL_P(offset);
348        } else {
349            index = Z_LVAL_P(offset);
350        }
351        if (zend_hash_index_find(ht, index, (void **) &retval) == FAILURE) {
352            switch (type) {
353                case BP_VAR_R:
354                    zend_error(E_NOTICE, "Undefined offset: %ld", index);
355                case BP_VAR_UNSET:
356                case BP_VAR_IS:
357                    retval = &EG(uninitialized_zval_ptr);
358                    break;
359                case BP_VAR_RW:
360                    zend_error(E_NOTICE, "Undefined offset: %ld", index);
361                case BP_VAR_W: {
362                    zval *value;
363                    ALLOC_INIT_ZVAL(value);
364                    zend_hash_index_update(ht, index, (void**)&value, sizeof(void*), (void **)&retval);
365               }
366            }
367        }
368        return retval;
369    default:
370        zend_error(E_WARNING, "Illegal offset type");
371        return (type == BP_VAR_W || type == BP_VAR_RW) ?
372            &EG(error_zval_ptr) : &EG(uninitialized_zval_ptr);
373    }
374} /* }}} */
375
376static zval *spl_array_read_dimension_ex(int check_inherited, zval *object, zval *offset, int type TSRMLS_DC) /* {{{ */
377{
378    zval **ret;
379
380    if (check_inherited) {
381        spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
382        if (intern->fptr_offset_get) {
383            zval *rv;
384            if (!offset) {
385                ALLOC_INIT_ZVAL(offset);
386            } else {
387                SEPARATE_ARG_IF_REF(offset);
388            }
389            zend_call_method_with_1_params(&object, Z_OBJCE_P(object), &intern->fptr_offset_get, "offsetGet", &rv, offset);
390            zval_ptr_dtor(&offset);
391            if (rv) {
392                zval_ptr_dtor(&intern->retval);
393                MAKE_STD_ZVAL(intern->retval);
394                ZVAL_ZVAL(intern->retval, rv, 1, 1);
395                return intern->retval;
396            }
397            return EG(uninitialized_zval_ptr);
398        }
399    }
400    ret = spl_array_get_dimension_ptr_ptr(check_inherited, object, offset, type TSRMLS_CC);
401
402    /* When in a write context,
403     * ZE has to be fooled into thinking this is in a reference set
404     * by separating (if necessary) and returning as an is_ref=1 zval (even if refcount == 1) */
405    if ((type == BP_VAR_W || type == BP_VAR_RW || type == BP_VAR_UNSET) && !Z_ISREF_PP(ret)) {
406        if (Z_REFCOUNT_PP(ret) > 1) {
407            zval *newval;
408
409            /* Separate */
410            MAKE_STD_ZVAL(newval);
411            *newval = **ret;
412            zval_copy_ctor(newval);
413            Z_SET_REFCOUNT_P(newval, 1);
414
415            /* Replace */
416            Z_DELREF_PP(ret);
417            *ret = newval;
418        }
419
420        Z_SET_ISREF_PP(ret);
421    }
422
423    return *ret;
424} /* }}} */
425
426static zval *spl_array_read_dimension(zval *object, zval *offset, int type TSRMLS_DC) /* {{{ */
427{
428    return spl_array_read_dimension_ex(1, object, offset, type TSRMLS_CC);
429} /* }}} */
430
431static void spl_array_write_dimension_ex(int check_inherited, zval *object, zval *offset, zval *value TSRMLS_DC) /* {{{ */
432{
433    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
434    long index;
435    HashTable *ht;
436
437    if (check_inherited && intern->fptr_offset_set) {
438        if (!offset) {
439            ALLOC_INIT_ZVAL(offset);
440        } else {
441            SEPARATE_ARG_IF_REF(offset);
442        }
443        zend_call_method_with_2_params(&object, Z_OBJCE_P(object), &intern->fptr_offset_set, "offsetSet", NULL, offset, value);
444        zval_ptr_dtor(&offset);
445        return;
446    }
447
448    if (!offset) {
449        ht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
450        if (ht->nApplyCount > 0) {
451            zend_error(E_WARNING, "Modification of ArrayObject during sorting is prohibited");
452            return;
453        }
454        Z_ADDREF_P(value);
455        zend_hash_next_index_insert(ht, (void**)&value, sizeof(void*), NULL);
456        return;
457    }
458    switch(Z_TYPE_P(offset)) {
459    case IS_STRING:
460        ht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
461        if (ht->nApplyCount > 0) {
462            zend_error(E_WARNING, "Modification of ArrayObject during sorting is prohibited");
463            return;
464        }
465        Z_ADDREF_P(value);
466        zend_symtable_update(ht, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, (void**)&value, sizeof(void*), NULL);
467        return;
468    case IS_DOUBLE:
469    case IS_RESOURCE:
470    case IS_BOOL:
471    case IS_LONG:
472        ht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
473        if (ht->nApplyCount > 0) {
474            zend_error(E_WARNING, "Modification of ArrayObject during sorting is prohibited");
475            return;
476        }
477        if (offset->type == IS_DOUBLE) {
478            index = (long)Z_DVAL_P(offset);
479        } else {
480            index = Z_LVAL_P(offset);
481        }
482        Z_ADDREF_P(value);
483        zend_hash_index_update(ht, index, (void**)&value, sizeof(void*), NULL);
484        return;
485    case IS_NULL:
486        ht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
487        if (ht->nApplyCount > 0) {
488            zend_error(E_WARNING, "Modification of ArrayObject during sorting is prohibited");
489            return;
490        }
491        Z_ADDREF_P(value);
492        zend_hash_next_index_insert(ht, (void**)&value, sizeof(void*), NULL);
493        return;
494    default:
495        zend_error(E_WARNING, "Illegal offset type");
496        return;
497    }
498} /* }}} */
499
500static void spl_array_write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC) /* {{{ */
501{
502    spl_array_write_dimension_ex(1, object, offset, value TSRMLS_CC);
503} /* }}} */
504
505static void spl_array_unset_dimension_ex(int check_inherited, zval *object, zval *offset TSRMLS_DC) /* {{{ */
506{
507    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
508    long index;
509    HashTable *ht;
510
511    if (check_inherited && intern->fptr_offset_del) {
512        SEPARATE_ARG_IF_REF(offset);
513        zend_call_method_with_1_params(&object, Z_OBJCE_P(object), &intern->fptr_offset_del, "offsetUnset", NULL, offset);
514        zval_ptr_dtor(&offset);
515        return;
516    }
517
518    switch(Z_TYPE_P(offset)) {
519    case IS_STRING:
520        ht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
521        if (ht->nApplyCount > 0) {
522            zend_error(E_WARNING, "Modification of ArrayObject during sorting is prohibited");
523            return;
524        }
525        if (ht == &EG(symbol_table)) {
526            if (zend_delete_global_variable(Z_STRVAL_P(offset), Z_STRLEN_P(offset) TSRMLS_CC)) {
527                zend_error(E_NOTICE,"Undefined index: %s", Z_STRVAL_P(offset));
528            }
529        } else {
530            if (zend_symtable_del(ht, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1) == FAILURE) {
531                zend_error(E_NOTICE,"Undefined index: %s", Z_STRVAL_P(offset));
532            } else {
533                spl_array_object *obj = intern;
534
535                while (1) {
536                    if ((obj->ar_flags & SPL_ARRAY_IS_SELF) != 0) {
537                        break;
538                    } else if (Z_TYPE_P(obj->array) == IS_OBJECT) {
539                        if ((obj->ar_flags & SPL_ARRAY_USE_OTHER) == 0) {
540                            obj = (spl_array_object*)zend_object_store_get_object(obj->array TSRMLS_CC);
541                            break;
542                        } else {
543                            obj = (spl_array_object*)zend_object_store_get_object(obj->array TSRMLS_CC);
544                        }
545                    } else {
546                        obj = NULL;
547                        break;
548                    }
549                }
550                if (obj) {
551                    zend_property_info *property_info = zend_get_property_info(obj->std.ce, offset, 1 TSRMLS_CC);
552
553                    if (property_info &&
554                        (property_info->flags & ZEND_ACC_STATIC) == 0 &&
555                        property_info->offset >= 0) {
556                        obj->std.properties_table[property_info->offset] = NULL;
557                    }
558                }
559            }
560        }
561        break;
562    case IS_DOUBLE:
563    case IS_RESOURCE:
564    case IS_BOOL:
565    case IS_LONG:
566        if (offset->type == IS_DOUBLE) {
567            index = (long)Z_DVAL_P(offset);
568        } else {
569            index = Z_LVAL_P(offset);
570        }
571        ht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
572        if (ht->nApplyCount > 0) {
573            zend_error(E_WARNING, "Modification of ArrayObject during sorting is prohibited");
574            return;
575        }
576        if (zend_hash_index_del(ht, index) == FAILURE) {
577            zend_error(E_NOTICE,"Undefined offset: %ld", Z_LVAL_P(offset));
578        }
579        break;
580    default:
581        zend_error(E_WARNING, "Illegal offset type");
582        return;
583    }
584    spl_hash_verify_pos(intern TSRMLS_CC); /* call rewind on FAILURE */
585} /* }}} */
586
587static void spl_array_unset_dimension(zval *object, zval *offset TSRMLS_DC) /* {{{ */
588{
589    spl_array_unset_dimension_ex(1, object, offset TSRMLS_CC);
590} /* }}} */
591
592static int spl_array_has_dimension_ex(int check_inherited, zval *object, zval *offset, int check_empty TSRMLS_DC) /* {{{ */
593{
594    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
595    long index;
596    zval *rv, *value = NULL, **tmp;
597
598    if (check_inherited && intern->fptr_offset_has) {
599        zval *offset_tmp = offset;
600        SEPARATE_ARG_IF_REF(offset_tmp);
601        zend_call_method_with_1_params(&object, Z_OBJCE_P(object), &intern->fptr_offset_has, "offsetExists", &rv, offset_tmp);
602        zval_ptr_dtor(&offset_tmp);
603
604        if (rv && zend_is_true(rv TSRMLS_CC)) {
605            zval_ptr_dtor(&rv);
606            if (check_empty == 2) {
607                return 1;
608            } else if (intern->fptr_offset_get) {
609                value = spl_array_read_dimension_ex(1, object, offset, BP_VAR_R TSRMLS_CC);
610            }
611        } else {
612            if (rv) {
613                zval_ptr_dtor(&rv);
614            }
615            return 0;
616        }
617    }
618
619    if (!value) {
620        HashTable *ht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
621
622        switch(Z_TYPE_P(offset)) {
623            case IS_STRING:
624                if (zend_symtable_find(ht, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, (void **) &tmp) != FAILURE) {
625                    if (check_empty == 2) {
626                        return 1;
627                    }
628                } else {
629                    return 0;
630                }
631                break;
632            case IS_DOUBLE:
633            case IS_RESOURCE:
634            case IS_BOOL:
635            case IS_LONG:
636                if (offset->type == IS_DOUBLE) {
637                    index = (long)Z_DVAL_P(offset);
638                } else {
639                    index = Z_LVAL_P(offset);
640                }
641                if (zend_hash_index_find(ht, index, (void **)&tmp) != FAILURE) {
642                    if (check_empty == 2) {
643                        return 1;
644                    }
645                } else {
646                    return 0;
647                }
648                break;
649            default:
650                zend_error(E_WARNING, "Illegal offset type");
651                return 0;
652        }
653
654        if (check_inherited && intern->fptr_offset_get) {
655            value = spl_array_read_dimension_ex(1, object, offset, BP_VAR_R TSRMLS_CC);
656        } else {
657            value = *tmp;
658        }
659    }
660
661    switch (check_empty) {
662        case 0:
663            return Z_TYPE_P(value) != IS_NULL;
664        case 2:
665            return 1;
666        case 1:
667            return zend_is_true(value TSRMLS_CC);
668    }
669
670    return 0;
671} /* }}} */
672
673static int spl_array_has_dimension(zval *object, zval *offset, int check_empty TSRMLS_DC) /* {{{ */
674{
675    return spl_array_has_dimension_ex(1, object, offset, check_empty TSRMLS_CC);
676} /* }}} */
677
678/* {{{ spl_array_object_verify_pos_ex */
679static inline int spl_array_object_verify_pos_ex(spl_array_object *object, HashTable *ht, const char *msg_prefix TSRMLS_DC)
680{
681    if (!ht) {
682        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "%sArray was modified outside object and is no longer an array", msg_prefix);
683        return FAILURE;
684    }
685
686    if (object->pos && (object->ar_flags & SPL_ARRAY_IS_REF) && spl_hash_verify_pos_ex(object, ht TSRMLS_CC) == FAILURE) {
687        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "%sArray was modified outside object and internal position is no longer valid", msg_prefix);
688        return FAILURE;
689    }
690
691    return SUCCESS;
692} /* }}} */
693
694/* {{{ spl_array_object_verify_pos */
695static inline int spl_array_object_verify_pos(spl_array_object *object, HashTable *ht TSRMLS_DC)
696{
697    return spl_array_object_verify_pos_ex(object, ht, "" TSRMLS_CC);
698} /* }}} */
699
700/* {{{ proto bool ArrayObject::offsetExists(mixed $index)
701       proto bool ArrayIterator::offsetExists(mixed $index)
702   Returns whether the requested $index exists. */
703SPL_METHOD(Array, offsetExists)
704{
705    zval *index;
706    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &index) == FAILURE) {
707        return;
708    }
709    RETURN_BOOL(spl_array_has_dimension_ex(0, getThis(), index, 2 TSRMLS_CC));
710} /* }}} */
711
712/* {{{ proto mixed ArrayObject::offsetGet(mixed $index)
713       proto mixed ArrayIterator::offsetGet(mixed $index)
714   Returns the value at the specified $index. */
715SPL_METHOD(Array, offsetGet)
716{
717    zval *index, *value;
718    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &index) == FAILURE) {
719        return;
720    }
721    value = spl_array_read_dimension_ex(0, getThis(), index, BP_VAR_R TSRMLS_CC);
722    RETURN_ZVAL(value, 1, 0);
723} /* }}} */
724
725/* {{{ proto void ArrayObject::offsetSet(mixed $index, mixed $newval)
726       proto void ArrayIterator::offsetSet(mixed $index, mixed $newval)
727   Sets the value at the specified $index to $newval. */
728SPL_METHOD(Array, offsetSet)
729{
730    zval *index, *value;
731    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &index, &value) == FAILURE) {
732        return;
733    }
734    spl_array_write_dimension_ex(0, getThis(), index, value TSRMLS_CC);
735} /* }}} */
736
737void spl_array_iterator_append(zval *object, zval *append_value TSRMLS_DC) /* {{{ */
738{
739    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
740    HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
741
742    if (!aht) {
743        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array was modified outside object and is no longer an array");
744        return;
745    }
746
747    if (Z_TYPE_P(intern->array) == IS_OBJECT) {
748        php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "Cannot append properties to objects, use %s::offsetSet() instead", Z_OBJCE_P(object)->name);
749        return;
750    }
751
752    spl_array_write_dimension(object, NULL, append_value TSRMLS_CC);
753    if (!intern->pos) {
754        spl_array_set_pos(intern, aht->pListTail);
755    }
756} /* }}} */
757
758/* {{{ proto void ArrayObject::append(mixed $newval)
759       proto void ArrayIterator::append(mixed $newval)
760   Appends the value (cannot be called for objects). */
761SPL_METHOD(Array, append)
762{
763    zval *value;
764
765    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
766        return;
767    }
768    spl_array_iterator_append(getThis(), value TSRMLS_CC);
769} /* }}} */
770
771/* {{{ proto void ArrayObject::offsetUnset(mixed $index)
772       proto void ArrayIterator::offsetUnset(mixed $index)
773   Unsets the value at the specified $index. */
774SPL_METHOD(Array, offsetUnset)
775{
776    zval *index;
777    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &index) == FAILURE) {
778        return;
779    }
780    spl_array_unset_dimension_ex(0, getThis(), index TSRMLS_CC);
781} /* }}} */
782
783/* {{{ proto array ArrayObject::getArrayCopy()
784      proto array ArrayIterator::getArrayCopy()
785   Return a copy of the contained array */
786SPL_METHOD(Array, getArrayCopy)
787{
788    zval *object = getThis(), *tmp;
789    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
790
791    array_init(return_value);
792    zend_hash_copy(HASH_OF(return_value), spl_array_get_hash_table(intern, 0 TSRMLS_CC), (copy_ctor_func_t) zval_add_ref, &tmp, sizeof(zval*));
793} /* }}} */
794
795static HashTable *spl_array_get_properties(zval *object TSRMLS_DC) /* {{{ */
796{
797    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
798    HashTable *result;
799
800    if (intern->nApplyCount > 1) {
801        php_error_docref(NULL TSRMLS_CC, E_ERROR, "Nesting level too deep - recursive dependency?");
802    }
803
804    intern->nApplyCount++;
805    result = spl_array_get_hash_table(intern, 1 TSRMLS_CC);
806    intern->nApplyCount--;
807    return result;
808} /* }}} */
809
810static HashTable* spl_array_get_debug_info(zval *obj, int *is_temp TSRMLS_DC) /* {{{ */
811{
812    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(obj TSRMLS_CC);
813    zval *tmp, *storage;
814    int name_len;
815    char *zname;
816    zend_class_entry *base;
817
818    *is_temp = 0;
819
820    if (!intern->std.properties) {
821        rebuild_object_properties(&intern->std);
822    }
823
824    if (HASH_OF(intern->array) == intern->std.properties) {
825        return intern->std.properties;
826    } else {
827        if (intern->debug_info == NULL) {
828            ALLOC_HASHTABLE(intern->debug_info);
829            ZEND_INIT_SYMTABLE_EX(intern->debug_info, zend_hash_num_elements(intern->std.properties) + 1, 0);
830        }
831
832        if (intern->debug_info->nApplyCount == 0) {
833            zend_hash_clean(intern->debug_info);
834            zend_hash_copy(intern->debug_info, intern->std.properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *));
835
836            storage = intern->array;
837            zval_add_ref(&storage);
838
839            base = (Z_OBJ_HT_P(obj) == &spl_handler_ArrayIterator) ? spl_ce_ArrayIterator : spl_ce_ArrayObject;
840            zname = spl_gen_private_prop_name(base, "storage", sizeof("storage")-1, &name_len TSRMLS_CC);
841            zend_symtable_update(intern->debug_info, zname, name_len+1, &storage, sizeof(zval *), NULL);
842            efree(zname);
843        }
844
845        return intern->debug_info;
846    }
847}
848/* }}} */
849
850static zval *spl_array_read_property(zval *object, zval *member, int type, const zend_literal *key TSRMLS_DC) /* {{{ */
851{
852    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
853
854    if ((intern->ar_flags & SPL_ARRAY_ARRAY_AS_PROPS) != 0
855    && !std_object_handlers.has_property(object, member, 2, key TSRMLS_CC)) {
856        return spl_array_read_dimension(object, member, type TSRMLS_CC);
857    }
858    return std_object_handlers.read_property(object, member, type, key TSRMLS_CC);
859} /* }}} */
860
861static void spl_array_write_property(zval *object, zval *member, zval *value, const zend_literal *key TSRMLS_DC) /* {{{ */
862{
863    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
864
865    if ((intern->ar_flags & SPL_ARRAY_ARRAY_AS_PROPS) != 0
866    && !std_object_handlers.has_property(object, member, 2, key TSRMLS_CC)) {
867        spl_array_write_dimension(object, member, value TSRMLS_CC);
868        return;
869    }
870    std_object_handlers.write_property(object, member, value, key TSRMLS_CC);
871} /* }}} */
872
873static zval **spl_array_get_property_ptr_ptr(zval *object, zval *member, int type, const zend_literal *key TSRMLS_DC) /* {{{ */
874{
875    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
876
877    if ((intern->ar_flags & SPL_ARRAY_ARRAY_AS_PROPS) != 0
878    && !std_object_handlers.has_property(object, member, 2, key TSRMLS_CC)) {
879        return spl_array_get_dimension_ptr_ptr(1, object, member, type TSRMLS_CC);
880    }
881    return std_object_handlers.get_property_ptr_ptr(object, member, type, key TSRMLS_CC);
882} /* }}} */
883
884static int spl_array_has_property(zval *object, zval *member, int has_set_exists, const zend_literal *key TSRMLS_DC) /* {{{ */
885{
886    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
887
888    if ((intern->ar_flags & SPL_ARRAY_ARRAY_AS_PROPS) != 0
889    && !std_object_handlers.has_property(object, member, 2, key TSRMLS_CC)) {
890        return spl_array_has_dimension(object, member, has_set_exists TSRMLS_CC);
891    }
892    return std_object_handlers.has_property(object, member, has_set_exists, key TSRMLS_CC);
893} /* }}} */
894
895static void spl_array_unset_property(zval *object, zval *member, const zend_literal *key TSRMLS_DC) /* {{{ */
896{
897    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
898
899    if ((intern->ar_flags & SPL_ARRAY_ARRAY_AS_PROPS) != 0
900    && !std_object_handlers.has_property(object, member, 2, key TSRMLS_CC)) {
901        spl_array_unset_dimension(object, member TSRMLS_CC);
902        spl_array_rewind(intern TSRMLS_CC); /* because deletion might invalidate position */
903        return;
904    }
905    std_object_handlers.unset_property(object, member, key TSRMLS_CC);
906} /* }}} */
907
908static int spl_array_compare_objects(zval *o1, zval *o2 TSRMLS_DC) /* {{{ */
909{
910    HashTable           *ht1,
911                        *ht2;
912    spl_array_object    *intern1,
913                        *intern2;
914    int                 result  = 0;
915    zval                temp_zv;
916
917    intern1 = (spl_array_object*)zend_object_store_get_object(o1 TSRMLS_CC);
918    intern2 = (spl_array_object*)zend_object_store_get_object(o2 TSRMLS_CC);
919    ht1     = spl_array_get_hash_table(intern1, 0 TSRMLS_CC);
920    ht2     = spl_array_get_hash_table(intern2, 0 TSRMLS_CC);
921
922    zend_compare_symbol_tables(&temp_zv, ht1, ht2 TSRMLS_CC);
923    result = (int)Z_LVAL(temp_zv);
924    /* if we just compared std.properties, don't do it again */
925    if (result == 0 &&
926            !(ht1 == intern1->std.properties && ht2 == intern2->std.properties)) {
927        result = std_object_handlers.compare_objects(o1, o2 TSRMLS_CC);
928    }
929    return result;
930} /* }}} */
931
932static int spl_array_skip_protected(spl_array_object *intern, HashTable *aht TSRMLS_DC) /* {{{ */
933{
934    char *string_key;
935    uint string_length;
936    ulong num_key;
937
938    if (Z_TYPE_P(intern->array) == IS_OBJECT) {
939        do {
940            if (zend_hash_get_current_key_ex(aht, &string_key, &string_length, &num_key, 0, &intern->pos) == HASH_KEY_IS_STRING) {
941                if (!string_length || string_key[0]) {
942                    return SUCCESS;
943                }
944            } else {
945                return SUCCESS;
946            }
947            if (zend_hash_has_more_elements_ex(aht, &intern->pos) != SUCCESS) {
948                return FAILURE;
949            }
950            zend_hash_move_forward_ex(aht, &intern->pos);
951            spl_array_update_pos(intern);
952        } while (1);
953    }
954    return FAILURE;
955} /* }}} */
956
957static int spl_array_next_no_verify(spl_array_object *intern, HashTable *aht TSRMLS_DC) /* {{{ */
958{
959    zend_hash_move_forward_ex(aht, &intern->pos);
960    spl_array_update_pos(intern);
961    if (Z_TYPE_P(intern->array) == IS_OBJECT) {
962        return spl_array_skip_protected(intern, aht TSRMLS_CC);
963    } else {
964        return zend_hash_has_more_elements_ex(aht, &intern->pos);
965    }
966} /* }}} */
967
968static int spl_array_next_ex(spl_array_object *intern, HashTable *aht TSRMLS_DC) /* {{{ */
969{
970    if ((intern->ar_flags & SPL_ARRAY_IS_REF) && spl_hash_verify_pos_ex(intern, aht TSRMLS_CC) == FAILURE) {
971        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array was modified outside object and internal position is no longer valid");
972        return FAILURE;
973    }
974
975    return spl_array_next_no_verify(intern, aht TSRMLS_CC);
976} /* }}} */
977
978static int spl_array_next(spl_array_object *intern TSRMLS_DC) /* {{{ */
979{
980    HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
981
982    return spl_array_next_ex(intern, aht TSRMLS_CC);
983
984} /* }}} */
985
986/* define an overloaded iterator structure */
987typedef struct {
988    zend_user_iterator    intern;
989    spl_array_object      *object;
990} spl_array_it;
991
992static void spl_array_it_dtor(zend_object_iterator *iter TSRMLS_DC) /* {{{ */
993{
994    spl_array_it *iterator = (spl_array_it *)iter;
995
996    zend_user_it_invalidate_current(iter TSRMLS_CC);
997    zval_ptr_dtor((zval**)&iterator->intern.it.data);
998
999    efree(iterator);
1000}
1001/* }}} */
1002
1003static int spl_array_it_valid(zend_object_iterator *iter TSRMLS_DC) /* {{{ */
1004{
1005    spl_array_it       *iterator = (spl_array_it *)iter;
1006    spl_array_object   *object   = iterator->object;
1007    HashTable          *aht      = spl_array_get_hash_table(object, 0 TSRMLS_CC);
1008
1009    if (object->ar_flags & SPL_ARRAY_OVERLOADED_VALID) {
1010        return zend_user_it_valid(iter TSRMLS_CC);
1011    } else {
1012        if (spl_array_object_verify_pos_ex(object, aht, "ArrayIterator::valid(): " TSRMLS_CC) == FAILURE) {
1013            return FAILURE;
1014        }
1015
1016        return zend_hash_has_more_elements_ex(aht, &object->pos);
1017    }
1018}
1019/* }}} */
1020
1021static void spl_array_it_get_current_data(zend_object_iterator *iter, zval ***data TSRMLS_DC) /* {{{ */
1022{
1023    spl_array_it       *iterator = (spl_array_it *)iter;
1024    spl_array_object   *object   = iterator->object;
1025    HashTable          *aht      = spl_array_get_hash_table(object, 0 TSRMLS_CC);
1026
1027    if (object->ar_flags & SPL_ARRAY_OVERLOADED_CURRENT) {
1028        zend_user_it_get_current_data(iter, data TSRMLS_CC);
1029    } else {
1030        if (zend_hash_get_current_data_ex(aht, (void**)data, &object->pos) == FAILURE) {
1031            *data = NULL;
1032        }
1033    }
1034}
1035/* }}} */
1036
1037static void spl_array_it_get_current_key(zend_object_iterator *iter, zval *key TSRMLS_DC) /* {{{ */
1038{
1039    spl_array_it       *iterator = (spl_array_it *)iter;
1040    spl_array_object   *object   = iterator->object;
1041    HashTable          *aht      = spl_array_get_hash_table(object, 0 TSRMLS_CC);
1042
1043    if (object->ar_flags & SPL_ARRAY_OVERLOADED_KEY) {
1044        zend_user_it_get_current_key(iter, key TSRMLS_CC);
1045    } else {
1046        if (spl_array_object_verify_pos_ex(object, aht, "ArrayIterator::current(): " TSRMLS_CC) == FAILURE) {
1047            ZVAL_NULL(key);
1048        } else {
1049            zend_hash_get_current_key_zval_ex(aht, key, &object->pos);
1050        }
1051    }
1052}
1053/* }}} */
1054
1055static void spl_array_it_move_forward(zend_object_iterator *iter TSRMLS_DC) /* {{{ */
1056{
1057    spl_array_it       *iterator = (spl_array_it *)iter;
1058    spl_array_object   *object   = iterator->object;
1059    HashTable          *aht      = spl_array_get_hash_table(object, 0 TSRMLS_CC);
1060
1061    if (object->ar_flags & SPL_ARRAY_OVERLOADED_NEXT) {
1062        zend_user_it_move_forward(iter TSRMLS_CC);
1063    } else {
1064        zend_user_it_invalidate_current(iter TSRMLS_CC);
1065        if (!aht) {
1066            php_error_docref(NULL TSRMLS_CC, E_NOTICE, "ArrayIterator::current(): Array was modified outside object and is no longer an array");
1067            return;
1068        }
1069
1070        if ((object->ar_flags & SPL_ARRAY_IS_REF) && spl_hash_verify_pos_ex(object, aht TSRMLS_CC) == FAILURE) {
1071            php_error_docref(NULL TSRMLS_CC, E_NOTICE, "ArrayIterator::next(): Array was modified outside object and internal position is no longer valid");
1072        } else {
1073            spl_array_next_no_verify(object, aht TSRMLS_CC);
1074        }
1075    }
1076}
1077/* }}} */
1078
1079static void spl_array_rewind_ex(spl_array_object *intern, HashTable *aht TSRMLS_DC) /* {{{ */
1080{
1081
1082    zend_hash_internal_pointer_reset_ex(aht, &intern->pos);
1083    spl_array_update_pos(intern);
1084    spl_array_skip_protected(intern, aht TSRMLS_CC);
1085
1086} /* }}} */
1087
1088static void spl_array_rewind(spl_array_object *intern TSRMLS_DC) /* {{{ */
1089{
1090    HashTable          *aht      = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
1091
1092    if (!aht) {
1093        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "ArrayIterator::rewind(): Array was modified outside object and is no longer an array");
1094        return;
1095    }
1096
1097    spl_array_rewind_ex(intern, aht TSRMLS_CC);
1098}
1099/* }}} */
1100
1101static void spl_array_it_rewind(zend_object_iterator *iter TSRMLS_DC) /* {{{ */
1102{
1103    spl_array_it       *iterator = (spl_array_it *)iter;
1104    spl_array_object   *object   = iterator->object;
1105
1106    if (object->ar_flags & SPL_ARRAY_OVERLOADED_REWIND) {
1107        zend_user_it_rewind(iter TSRMLS_CC);
1108    } else {
1109        zend_user_it_invalidate_current(iter TSRMLS_CC);
1110        spl_array_rewind(object TSRMLS_CC);
1111    }
1112}
1113/* }}} */
1114
1115/* {{{ spl_array_set_array */
1116static void spl_array_set_array(zval *object, spl_array_object *intern, zval **array, long ar_flags, int just_array TSRMLS_DC) {
1117
1118    if (Z_TYPE_PP(array) == IS_ARRAY) {
1119        SEPARATE_ZVAL_IF_NOT_REF(array);
1120    }
1121
1122    if (Z_TYPE_PP(array) == IS_OBJECT && (Z_OBJ_HT_PP(array) == &spl_handler_ArrayObject || Z_OBJ_HT_PP(array) == &spl_handler_ArrayIterator)) {
1123        zval_ptr_dtor(&intern->array);
1124        if (just_array) {
1125            spl_array_object *other = (spl_array_object*)zend_object_store_get_object(*array TSRMLS_CC);
1126            ar_flags = other->ar_flags & ~SPL_ARRAY_INT_MASK;
1127        }
1128        ar_flags |= SPL_ARRAY_USE_OTHER;
1129        intern->array = *array;
1130    } else {
1131        if (Z_TYPE_PP(array) != IS_OBJECT && Z_TYPE_PP(array) != IS_ARRAY) {
1132            zend_throw_exception(spl_ce_InvalidArgumentException, "Passed variable is not an array or object, using empty array instead", 0 TSRMLS_CC);
1133            return;
1134        }
1135        zval_ptr_dtor(&intern->array);
1136        intern->array = *array;
1137    }
1138    if (object == *array) {
1139        intern->ar_flags |= SPL_ARRAY_IS_SELF;
1140        intern->ar_flags &= ~SPL_ARRAY_USE_OTHER;
1141    } else {
1142        intern->ar_flags &= ~SPL_ARRAY_IS_SELF;
1143    }
1144    intern->ar_flags |= ar_flags;
1145    Z_ADDREF_P(intern->array);
1146    if (Z_TYPE_PP(array) == IS_OBJECT) {
1147        zend_object_get_properties_t handler = Z_OBJ_HANDLER_PP(array, get_properties);
1148        if ((handler != std_object_handlers.get_properties && handler != spl_array_get_properties)
1149        || !spl_array_get_hash_table(intern, 0 TSRMLS_CC)) {
1150            zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC, "Overloaded object of type %s is not compatible with %s", Z_OBJCE_PP(array)->name, intern->std.ce->name);
1151        }
1152    }
1153
1154    spl_array_rewind(intern TSRMLS_CC);
1155}
1156/* }}} */
1157
1158/* iterator handler table */
1159zend_object_iterator_funcs spl_array_it_funcs = {
1160    spl_array_it_dtor,
1161    spl_array_it_valid,
1162    spl_array_it_get_current_data,
1163    spl_array_it_get_current_key,
1164    spl_array_it_move_forward,
1165    spl_array_it_rewind
1166};
1167
1168zend_object_iterator *spl_array_get_iterator(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC) /* {{{ */
1169{
1170    spl_array_it       *iterator;
1171    spl_array_object   *array_object = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
1172
1173    if (by_ref && (array_object->ar_flags & SPL_ARRAY_OVERLOADED_CURRENT)) {
1174        zend_error(E_ERROR, "An iterator cannot be used with foreach by reference");
1175    }
1176
1177    iterator     = emalloc(sizeof(spl_array_it));
1178
1179    Z_ADDREF_P(object);
1180    iterator->intern.it.data = (void*)object;
1181    iterator->intern.it.funcs = &spl_array_it_funcs;
1182    iterator->intern.ce = ce;
1183    iterator->intern.value = NULL;
1184    iterator->object = array_object;
1185
1186    return (zend_object_iterator*)iterator;
1187}
1188/* }}} */
1189
1190/* {{{ proto void ArrayObject::__construct(array|object ar = array() [, int flags = 0 [, string iterator_class = "ArrayIterator"]])
1191       proto void ArrayIterator::__construct(array|object ar = array() [, int flags = 0])
1192   Constructs a new array iterator from a path. */
1193SPL_METHOD(Array, __construct)
1194{
1195    zval *object = getThis();
1196    spl_array_object *intern;
1197    zval **array;
1198    long ar_flags = 0;
1199    zend_class_entry *ce_get_iterator = spl_ce_Iterator;
1200    zend_error_handling error_handling;
1201
1202    if (ZEND_NUM_ARGS() == 0) {
1203        return; /* nothing to do */
1204    }
1205
1206    zend_replace_error_handling(EH_THROW, spl_ce_InvalidArgumentException, &error_handling TSRMLS_CC);
1207
1208    intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
1209
1210    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|lC", &array, &ar_flags, &ce_get_iterator) == FAILURE) {
1211        zend_restore_error_handling(&error_handling TSRMLS_CC);
1212        return;
1213    }
1214
1215    if (ZEND_NUM_ARGS() > 2) {
1216        intern->ce_get_iterator = ce_get_iterator;
1217    }
1218
1219    ar_flags &= ~SPL_ARRAY_INT_MASK;
1220
1221    spl_array_set_array(object, intern, array, ar_flags, ZEND_NUM_ARGS() == 1 TSRMLS_CC);
1222
1223    zend_restore_error_handling(&error_handling TSRMLS_CC);
1224
1225}
1226 /* }}} */
1227
1228/* {{{ proto void ArrayObject::setIteratorClass(string iterator_class)
1229   Set the class used in getIterator. */
1230SPL_METHOD(Array, setIteratorClass)
1231{
1232    zval *object = getThis();
1233    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
1234    zend_class_entry * ce_get_iterator = spl_ce_Iterator;
1235
1236    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "C", &ce_get_iterator) == FAILURE) {
1237        return;
1238    }
1239
1240    intern->ce_get_iterator = ce_get_iterator;
1241}
1242/* }}} */
1243
1244/* {{{ proto string ArrayObject::getIteratorClass()
1245   Get the class used in getIterator. */
1246SPL_METHOD(Array, getIteratorClass)
1247{
1248    zval *object = getThis();
1249    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
1250
1251    if (zend_parse_parameters_none() == FAILURE) {
1252        return;
1253    }
1254
1255    RETURN_STRING(intern->ce_get_iterator->name, 1);
1256}
1257/* }}} */
1258
1259/* {{{ proto int ArrayObject::getFlags()
1260   Get flags */
1261SPL_METHOD(Array, getFlags)
1262{
1263    zval *object = getThis();
1264    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
1265
1266    if (zend_parse_parameters_none() == FAILURE) {
1267        return;
1268    }
1269
1270    RETURN_LONG(intern->ar_flags & ~SPL_ARRAY_INT_MASK);
1271}
1272/* }}} */
1273
1274/* {{{ proto void ArrayObject::setFlags(int flags)
1275   Set flags */
1276SPL_METHOD(Array, setFlags)
1277{
1278    zval *object = getThis();
1279    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
1280    long ar_flags = 0;
1281
1282    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &ar_flags) == FAILURE) {
1283        return;
1284    }
1285
1286    intern->ar_flags = (intern->ar_flags & SPL_ARRAY_INT_MASK) | (ar_flags & ~SPL_ARRAY_INT_MASK);
1287}
1288/* }}} */
1289
1290/* {{{ proto Array|Object ArrayObject::exchangeArray(Array|Object ar = array())
1291   Replace the referenced array or object with a new one and return the old one (right now copy - to be changed) */
1292SPL_METHOD(Array, exchangeArray)
1293{
1294    zval *object = getThis(), *tmp, **array;
1295    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
1296
1297    array_init(return_value);
1298    zend_hash_copy(HASH_OF(return_value), spl_array_get_hash_table(intern, 0 TSRMLS_CC), (copy_ctor_func_t) zval_add_ref, &tmp, sizeof(zval*));
1299
1300    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &array) == FAILURE) {
1301        return;
1302    }
1303
1304    spl_array_set_array(object, intern, array, 0L, 1 TSRMLS_CC);
1305
1306}
1307/* }}} */
1308
1309/* {{{ proto ArrayIterator ArrayObject::getIterator()
1310   Create a new iterator from a ArrayObject instance */
1311SPL_METHOD(Array, getIterator)
1312{
1313    zval *object = getThis();
1314    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
1315    spl_array_object *iterator;
1316    HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
1317
1318    if (zend_parse_parameters_none() == FAILURE) {
1319        return;
1320    }
1321
1322    if (!aht) {
1323        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array was modified outside object and is no longer an array");
1324        return;
1325    }
1326
1327    return_value->type = IS_OBJECT;
1328    return_value->value.obj = spl_array_object_new_ex(intern->ce_get_iterator, &iterator, object, 0 TSRMLS_CC);
1329    Z_SET_REFCOUNT_P(return_value, 1);
1330    Z_SET_ISREF_P(return_value);
1331}
1332/* }}} */
1333
1334/* {{{ proto void ArrayIterator::rewind()
1335   Rewind array back to the start */
1336SPL_METHOD(Array, rewind)
1337{
1338    zval *object = getThis();
1339    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
1340
1341    if (zend_parse_parameters_none() == FAILURE) {
1342        return;
1343    }
1344
1345    spl_array_rewind(intern TSRMLS_CC);
1346}
1347/* }}} */
1348
1349/* {{{ proto void ArrayIterator::seek(int $position)
1350   Seek to position. */
1351SPL_METHOD(Array, seek)
1352{
1353    long opos, position;
1354    zval *object = getThis();
1355    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
1356    HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
1357    int result;
1358
1359    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &position) == FAILURE) {
1360        return;
1361    }
1362
1363    if (!aht) {
1364        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array was modified outside object and is no longer an array");
1365        return;
1366    }
1367
1368    opos = position;
1369
1370    if (position >= 0) { /* negative values are not supported */
1371        spl_array_rewind(intern TSRMLS_CC);
1372        result = SUCCESS;
1373
1374        while (position-- > 0 && (result = spl_array_next(intern TSRMLS_CC)) == SUCCESS);
1375
1376        if (result == SUCCESS && zend_hash_has_more_elements_ex(aht, &intern->pos) == SUCCESS) {
1377            return; /* ok */
1378        }
1379    }
1380    zend_throw_exception_ex(spl_ce_OutOfBoundsException, 0 TSRMLS_CC, "Seek position %ld is out of range", opos);
1381} /* }}} */
1382
1383int static spl_array_object_count_elements_helper(spl_array_object *intern, long *count TSRMLS_DC) /* {{{ */
1384{
1385    HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
1386    HashPosition pos;
1387
1388    if (!aht) {
1389        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array was modified outside object and is no longer an array");
1390        *count = 0;
1391        return FAILURE;
1392    }
1393
1394    if (Z_TYPE_P(intern->array) == IS_OBJECT) {
1395        /* We need to store the 'pos' since we'll modify it in the functions
1396         * we're going to call and which do not support 'pos' as parameter. */
1397        pos = intern->pos;
1398        *count = 0;
1399        spl_array_rewind(intern TSRMLS_CC);
1400        while(intern->pos && spl_array_next(intern TSRMLS_CC) == SUCCESS) {
1401            (*count)++;
1402        }
1403        spl_array_set_pos(intern, pos);
1404        return SUCCESS;
1405    } else {
1406        *count = zend_hash_num_elements(aht);
1407        return SUCCESS;
1408    }
1409} /* }}} */
1410
1411int spl_array_object_count_elements(zval *object, long *count TSRMLS_DC) /* {{{ */
1412{
1413    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
1414
1415    if (intern->fptr_count) {
1416        zval *rv;
1417        zend_call_method_with_0_params(&object, intern->std.ce, &intern->fptr_count, "count", &rv);
1418        if (rv) {
1419            zval_ptr_dtor(&intern->retval);
1420            MAKE_STD_ZVAL(intern->retval);
1421            ZVAL_ZVAL(intern->retval, rv, 1, 1);
1422            convert_to_long(intern->retval);
1423            *count = (long) Z_LVAL_P(intern->retval);
1424            return SUCCESS;
1425        }
1426        *count = 0;
1427        return FAILURE;
1428    }
1429    return spl_array_object_count_elements_helper(intern, count TSRMLS_CC);
1430} /* }}} */
1431
1432/* {{{ proto int ArrayObject::count()
1433       proto int ArrayIterator::count()
1434   Return the number of elements in the Iterator. */
1435SPL_METHOD(Array, count)
1436{
1437    long count;
1438    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
1439
1440    if (zend_parse_parameters_none() == FAILURE) {
1441        return;
1442    }
1443
1444    spl_array_object_count_elements_helper(intern, &count TSRMLS_CC);
1445
1446    RETURN_LONG(count);
1447} /* }}} */
1448
1449static void spl_array_method(INTERNAL_FUNCTION_PARAMETERS, char *fname, int fname_len, int use_arg) /* {{{ */
1450{
1451    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
1452    HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
1453    zval *tmp, *arg = NULL;
1454    zval *retval_ptr = NULL;
1455
1456    MAKE_STD_ZVAL(tmp);
1457    Z_TYPE_P(tmp) = IS_ARRAY;
1458    Z_ARRVAL_P(tmp) = aht;
1459
1460    if (!use_arg) {
1461        aht->nApplyCount++;
1462        zend_call_method(NULL, NULL, NULL, fname, fname_len, &retval_ptr, 1, tmp, NULL TSRMLS_CC);
1463        aht->nApplyCount--;
1464    } else if (use_arg == SPL_ARRAY_METHOD_MAY_USER_ARG) {
1465        if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "|z", &arg) == FAILURE) {
1466            Z_TYPE_P(tmp) = IS_NULL;
1467            zval_ptr_dtor(&tmp);
1468            zend_throw_exception(spl_ce_BadMethodCallException, "Function expects one argument at most", 0 TSRMLS_CC);
1469            return;
1470        }
1471        aht->nApplyCount++;
1472        zend_call_method(NULL, NULL, NULL, fname, fname_len, &retval_ptr, arg? 2 : 1, tmp, arg TSRMLS_CC);
1473        aht->nApplyCount--;
1474    } else {
1475        if (ZEND_NUM_ARGS() != 1 || zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "z", &arg) == FAILURE) {
1476            Z_TYPE_P(tmp) = IS_NULL;
1477            zval_ptr_dtor(&tmp);
1478            zend_throw_exception(spl_ce_BadMethodCallException, "Function expects exactly one argument", 0 TSRMLS_CC);
1479            return;
1480        }
1481        aht->nApplyCount++;
1482        zend_call_method(NULL, NULL, NULL, fname, fname_len, &retval_ptr, 2, tmp, arg TSRMLS_CC);
1483        aht->nApplyCount--;
1484    }
1485    Z_TYPE_P(tmp) = IS_NULL; /* we want to destroy the zval, not the hashtable */
1486    zval_ptr_dtor(&tmp);
1487    if (retval_ptr) {
1488        COPY_PZVAL_TO_ZVAL(*return_value, retval_ptr);
1489    }
1490} /* }}} */
1491
1492#define SPL_ARRAY_METHOD(cname, fname, use_arg) \
1493SPL_METHOD(cname, fname) \
1494{ \
1495    spl_array_method(INTERNAL_FUNCTION_PARAM_PASSTHRU, #fname, sizeof(#fname)-1, use_arg); \
1496}
1497
1498/* {{{ proto int ArrayObject::asort([int $sort_flags = SORT_REGULAR ])
1499       proto int ArrayIterator::asort([int $sort_flags = SORT_REGULAR ])
1500   Sort the entries by values. */
1501SPL_ARRAY_METHOD(Array, asort, SPL_ARRAY_METHOD_MAY_USER_ARG) /* }}} */
1502
1503/* {{{ proto int ArrayObject::ksort([int $sort_flags = SORT_REGULAR ])
1504       proto int ArrayIterator::ksort([int $sort_flags = SORT_REGULAR ])
1505   Sort the entries by key. */
1506SPL_ARRAY_METHOD(Array, ksort, SPL_ARRAY_METHOD_MAY_USER_ARG) /* }}} */
1507
1508/* {{{ proto int ArrayObject::uasort(callback cmp_function)
1509       proto int ArrayIterator::uasort(callback cmp_function)
1510   Sort the entries by values user defined function. */
1511SPL_ARRAY_METHOD(Array, uasort, SPL_ARRAY_METHOD_USE_ARG) /* }}} */
1512
1513/* {{{ proto int ArrayObject::uksort(callback cmp_function)
1514       proto int ArrayIterator::uksort(callback cmp_function)
1515   Sort the entries by key using user defined function. */
1516SPL_ARRAY_METHOD(Array, uksort, SPL_ARRAY_METHOD_USE_ARG) /* }}} */
1517
1518/* {{{ proto int ArrayObject::natsort()
1519       proto int ArrayIterator::natsort()
1520   Sort the entries by values using "natural order" algorithm. */
1521SPL_ARRAY_METHOD(Array, natsort, SPL_ARRAY_METHOD_NO_ARG) /* }}} */
1522
1523/* {{{ proto int ArrayObject::natcasesort()
1524       proto int ArrayIterator::natcasesort()
1525   Sort the entries by key using case insensitive "natural order" algorithm. */
1526SPL_ARRAY_METHOD(Array, natcasesort, SPL_ARRAY_METHOD_NO_ARG) /* }}} */
1527
1528/* {{{ proto mixed|NULL ArrayIterator::current()
1529   Return current array entry */
1530SPL_METHOD(Array, current)
1531{
1532    zval *object = getThis();
1533    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
1534    zval **entry;
1535    HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
1536
1537    if (zend_parse_parameters_none() == FAILURE) {
1538        return;
1539    }
1540
1541    if (spl_array_object_verify_pos(intern, aht TSRMLS_CC) == FAILURE) {
1542        return;
1543    }
1544
1545    if (zend_hash_get_current_data_ex(aht, (void **) &entry, &intern->pos) == FAILURE) {
1546        return;
1547    }
1548    RETVAL_ZVAL(*entry, 1, 0);
1549}
1550/* }}} */
1551
1552/* {{{ proto mixed|NULL ArrayIterator::key()
1553   Return current array key */
1554SPL_METHOD(Array, key)
1555{
1556    if (zend_parse_parameters_none() == FAILURE) {
1557        return;
1558    }
1559
1560    spl_array_iterator_key(getThis(), return_value TSRMLS_CC);
1561} /* }}} */
1562
1563void spl_array_iterator_key(zval *object, zval *return_value TSRMLS_DC) /* {{{ */
1564{
1565    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
1566    HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
1567
1568    if (spl_array_object_verify_pos(intern, aht TSRMLS_CC) == FAILURE) {
1569        return;
1570    }
1571
1572    zend_hash_get_current_key_zval_ex(aht, return_value, &intern->pos);
1573}
1574/* }}} */
1575
1576/* {{{ proto void ArrayIterator::next()
1577   Move to next entry */
1578SPL_METHOD(Array, next)
1579{
1580    zval *object = getThis();
1581    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
1582    HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
1583
1584    if (zend_parse_parameters_none() == FAILURE) {
1585        return;
1586    }
1587
1588    if (spl_array_object_verify_pos(intern, aht TSRMLS_CC) == FAILURE) {
1589        return;
1590    }
1591
1592    spl_array_next_no_verify(intern, aht TSRMLS_CC);
1593}
1594/* }}} */
1595
1596/* {{{ proto bool ArrayIterator::valid()
1597   Check whether array contains more entries */
1598SPL_METHOD(Array, valid)
1599{
1600    zval *object = getThis();
1601    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
1602    HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
1603
1604    if (zend_parse_parameters_none() == FAILURE) {
1605        return;
1606    }
1607
1608    if (spl_array_object_verify_pos(intern, aht TSRMLS_CC) == FAILURE) {
1609        RETURN_FALSE;
1610    } else {
1611        RETURN_BOOL(zend_hash_has_more_elements_ex(aht, &intern->pos) == SUCCESS);
1612    }
1613}
1614/* }}} */
1615
1616/* {{{ proto bool RecursiveArrayIterator::hasChildren()
1617   Check whether current element has children (e.g. is an array) */
1618SPL_METHOD(Array, hasChildren)
1619{
1620    zval *object = getThis(), **entry;
1621    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
1622    HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
1623
1624    if (zend_parse_parameters_none() == FAILURE) {
1625        return;
1626    }
1627
1628    if (spl_array_object_verify_pos(intern, aht TSRMLS_CC) == FAILURE) {
1629        RETURN_FALSE;
1630    }
1631
1632    if (zend_hash_get_current_data_ex(aht, (void **) &entry, &intern->pos) == FAILURE) {
1633        RETURN_FALSE;
1634    }
1635
1636    RETURN_BOOL(Z_TYPE_PP(entry) == IS_ARRAY || (Z_TYPE_PP(entry) == IS_OBJECT && (intern->ar_flags & SPL_ARRAY_CHILD_ARRAYS_ONLY) == 0));
1637}
1638/* }}} */
1639
1640/* {{{ proto object RecursiveArrayIterator::getChildren()
1641   Create a sub iterator for the current element (same class as $this) */
1642SPL_METHOD(Array, getChildren)
1643{
1644    zval *object = getThis(), **entry, *flags;
1645    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
1646    HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
1647
1648    if (zend_parse_parameters_none() == FAILURE) {
1649        return;
1650    }
1651
1652    if (spl_array_object_verify_pos(intern, aht TSRMLS_CC) == FAILURE) {
1653        return;
1654    }
1655
1656    if (zend_hash_get_current_data_ex(aht, (void **) &entry, &intern->pos) == FAILURE) {
1657        return;
1658    }
1659
1660    if (Z_TYPE_PP(entry) == IS_OBJECT) {
1661        if ((intern->ar_flags & SPL_ARRAY_CHILD_ARRAYS_ONLY) != 0) {
1662            return;
1663        }
1664        if (instanceof_function(Z_OBJCE_PP(entry), Z_OBJCE_P(getThis()) TSRMLS_CC)) {
1665            RETURN_ZVAL(*entry, 1, 0);
1666        }
1667    }
1668
1669    MAKE_STD_ZVAL(flags);
1670    ZVAL_LONG(flags, SPL_ARRAY_USE_OTHER | intern->ar_flags);
1671    spl_instantiate_arg_ex2(Z_OBJCE_P(getThis()), &return_value, 0, *entry, flags TSRMLS_CC);
1672    zval_ptr_dtor(&flags);
1673}
1674/* }}} */
1675
1676/* {{{ proto string ArrayObject::serialize()
1677   Serialize the object */
1678SPL_METHOD(Array, serialize)
1679{
1680    zval *object = getThis();
1681    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
1682    HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
1683    zval members, *pmembers;
1684    php_serialize_data_t var_hash;
1685    smart_str buf = {0};
1686    zval *flags;
1687
1688    if (zend_parse_parameters_none() == FAILURE) {
1689        return;
1690    }
1691
1692    if (!aht) {
1693        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array was modified outside object and is no longer an array");
1694        return;
1695    }
1696
1697    PHP_VAR_SERIALIZE_INIT(var_hash);
1698
1699    MAKE_STD_ZVAL(flags);
1700    ZVAL_LONG(flags, (intern->ar_flags & SPL_ARRAY_CLONE_MASK));
1701
1702    /* storage */
1703    smart_str_appendl(&buf, "x:", 2);
1704    php_var_serialize(&buf, &flags, &var_hash TSRMLS_CC);
1705    zval_ptr_dtor(&flags);
1706
1707    if (!(intern->ar_flags & SPL_ARRAY_IS_SELF)) {
1708        php_var_serialize(&buf, &intern->array, &var_hash TSRMLS_CC);
1709        smart_str_appendc(&buf, ';');
1710    }
1711
1712    /* members */
1713    smart_str_appendl(&buf, "m:", 2);
1714    INIT_PZVAL(&members);
1715    if (!intern->std.properties) {
1716        rebuild_object_properties(&intern->std);
1717    }
1718    Z_ARRVAL(members) = intern->std.properties;
1719    Z_TYPE(members) = IS_ARRAY;
1720    pmembers = &members;
1721    php_var_serialize(&buf, &pmembers, &var_hash TSRMLS_CC); /* finishes the string */
1722
1723    /* done */
1724    PHP_VAR_SERIALIZE_DESTROY(var_hash);
1725
1726    if (buf.c) {
1727        RETURN_STRINGL(buf.c, buf.len, 0);
1728    }
1729
1730    RETURN_NULL();
1731} /* }}} */
1732
1733/* {{{ proto void ArrayObject::unserialize(string serialized)
1734 * unserialize the object
1735 */
1736SPL_METHOD(Array, unserialize)
1737{
1738    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
1739
1740    char *buf;
1741    int buf_len;
1742    const unsigned char *p, *s;
1743    php_unserialize_data_t var_hash;
1744    zval *pmembers, *pflags = NULL;
1745    long flags;
1746
1747    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &buf, &buf_len) == FAILURE) {
1748        return;
1749    }
1750
1751    if (buf_len == 0) {
1752        zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Empty serialized string cannot be empty");
1753        return;
1754    }
1755
1756    /* storage */
1757    s = p = (const unsigned char*)buf;
1758    PHP_VAR_UNSERIALIZE_INIT(var_hash);
1759
1760    if (*p!= 'x' || *++p != ':') {
1761        goto outexcept;
1762    }
1763    ++p;
1764
1765    ALLOC_INIT_ZVAL(pflags);
1766    if (!php_var_unserialize(&pflags, &p, s + buf_len, &var_hash TSRMLS_CC) || Z_TYPE_P(pflags) != IS_LONG) {
1767        zval_ptr_dtor(&pflags);
1768        goto outexcept;
1769    }
1770
1771    --p; /* for ';' */
1772    flags = Z_LVAL_P(pflags);
1773    zval_ptr_dtor(&pflags);
1774    /* flags needs to be verified and we also need to verify whether the next
1775     * thing we get is ';'. After that we require an 'm' or somethign else
1776     * where 'm' stands for members and anything else should be an array. If
1777     * neither 'a' or 'm' follows we have an error. */
1778
1779    if (*p != ';') {
1780        goto outexcept;
1781    }
1782    ++p;
1783
1784    if (*p!='m') {
1785        if (*p!='a' && *p!='O' && *p!='C' && *p!='r') {
1786            goto outexcept;
1787        }
1788        intern->ar_flags &= ~SPL_ARRAY_CLONE_MASK;
1789        intern->ar_flags |= flags & SPL_ARRAY_CLONE_MASK;
1790        zval_ptr_dtor(&intern->array);
1791        ALLOC_INIT_ZVAL(intern->array);
1792        if (!php_var_unserialize(&intern->array, &p, s + buf_len, &var_hash TSRMLS_CC)) {
1793            goto outexcept;
1794        }
1795    }
1796    if (*p != ';') {
1797        goto outexcept;
1798    }
1799    ++p;
1800
1801    /* members */
1802    if (*p!= 'm' || *++p != ':') {
1803        goto outexcept;
1804    }
1805    ++p;
1806
1807    ALLOC_INIT_ZVAL(pmembers);
1808    if (!php_var_unserialize(&pmembers, &p, s + buf_len, &var_hash TSRMLS_CC)) {
1809        zval_ptr_dtor(&pmembers);
1810        goto outexcept;
1811    }
1812
1813    /* copy members */
1814    if (!intern->std.properties) {
1815        rebuild_object_properties(&intern->std);
1816    }
1817    zend_hash_copy(intern->std.properties, Z_ARRVAL_P(pmembers), (copy_ctor_func_t) zval_add_ref, (void *) NULL, sizeof(zval *));
1818    zval_ptr_dtor(&pmembers);
1819
1820    /* done reading $serialized */
1821
1822    PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
1823    return;
1824
1825outexcept:
1826    PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
1827    zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Error at offset %ld of %d bytes", (long)((char*)p - buf), buf_len);
1828    return;
1829
1830} /* }}} */
1831
1832/* {{{ arginfo and function tbale */
1833ZEND_BEGIN_ARG_INFO(arginfo_array___construct, 0)
1834    ZEND_ARG_INFO(0, array)
1835ZEND_END_ARG_INFO()
1836
1837ZEND_BEGIN_ARG_INFO_EX(arginfo_array_offsetGet, 0, 0, 1)
1838    ZEND_ARG_INFO(0, index)
1839ZEND_END_ARG_INFO()
1840
1841ZEND_BEGIN_ARG_INFO_EX(arginfo_array_offsetSet, 0, 0, 2)
1842    ZEND_ARG_INFO(0, index)
1843    ZEND_ARG_INFO(0, newval)
1844ZEND_END_ARG_INFO()
1845
1846ZEND_BEGIN_ARG_INFO(arginfo_array_append, 0)
1847    ZEND_ARG_INFO(0, value)
1848ZEND_END_ARG_INFO()
1849
1850ZEND_BEGIN_ARG_INFO(arginfo_array_seek, 0)
1851    ZEND_ARG_INFO(0, position)
1852ZEND_END_ARG_INFO()
1853
1854ZEND_BEGIN_ARG_INFO(arginfo_array_exchangeArray, 0)
1855    ZEND_ARG_INFO(0, array)
1856ZEND_END_ARG_INFO()
1857
1858ZEND_BEGIN_ARG_INFO(arginfo_array_setFlags, 0)
1859    ZEND_ARG_INFO(0, flags)
1860ZEND_END_ARG_INFO()
1861
1862ZEND_BEGIN_ARG_INFO(arginfo_array_setIteratorClass, 0)
1863    ZEND_ARG_INFO(0, iteratorClass)
1864ZEND_END_ARG_INFO()
1865
1866ZEND_BEGIN_ARG_INFO(arginfo_array_uXsort, 0)
1867    ZEND_ARG_INFO(0, cmp_function)
1868ZEND_END_ARG_INFO();
1869
1870ZEND_BEGIN_ARG_INFO(arginfo_array_unserialize, 0)
1871    ZEND_ARG_INFO(0, serialized)
1872ZEND_END_ARG_INFO();
1873
1874ZEND_BEGIN_ARG_INFO(arginfo_array_void, 0)
1875ZEND_END_ARG_INFO()
1876
1877static const zend_function_entry spl_funcs_ArrayObject[] = {
1878    SPL_ME(Array, __construct,      arginfo_array___construct,      ZEND_ACC_PUBLIC)
1879    SPL_ME(Array, offsetExists,     arginfo_array_offsetGet,        ZEND_ACC_PUBLIC)
1880    SPL_ME(Array, offsetGet,        arginfo_array_offsetGet,        ZEND_ACC_PUBLIC)
1881    SPL_ME(Array, offsetSet,        arginfo_array_offsetSet,        ZEND_ACC_PUBLIC)
1882    SPL_ME(Array, offsetUnset,      arginfo_array_offsetGet,        ZEND_ACC_PUBLIC)
1883    SPL_ME(Array, append,           arginfo_array_append,           ZEND_ACC_PUBLIC)
1884    SPL_ME(Array, getArrayCopy,     arginfo_array_void,             ZEND_ACC_PUBLIC)
1885    SPL_ME(Array, count,            arginfo_array_void,             ZEND_ACC_PUBLIC)
1886    SPL_ME(Array, getFlags,         arginfo_array_void,             ZEND_ACC_PUBLIC)
1887    SPL_ME(Array, setFlags,         arginfo_array_setFlags,         ZEND_ACC_PUBLIC)
1888    SPL_ME(Array, asort,            arginfo_array_void,             ZEND_ACC_PUBLIC)
1889    SPL_ME(Array, ksort,            arginfo_array_void,             ZEND_ACC_PUBLIC)
1890    SPL_ME(Array, uasort,           arginfo_array_uXsort,           ZEND_ACC_PUBLIC)
1891    SPL_ME(Array, uksort,           arginfo_array_uXsort,           ZEND_ACC_PUBLIC)
1892    SPL_ME(Array, natsort,          arginfo_array_void,             ZEND_ACC_PUBLIC)
1893    SPL_ME(Array, natcasesort,      arginfo_array_void,             ZEND_ACC_PUBLIC)
1894    SPL_ME(Array, unserialize,      arginfo_array_unserialize,      ZEND_ACC_PUBLIC)
1895    SPL_ME(Array, serialize,        arginfo_array_void,             ZEND_ACC_PUBLIC)
1896    /* ArrayObject specific */
1897    SPL_ME(Array, getIterator,      arginfo_array_void,             ZEND_ACC_PUBLIC)
1898    SPL_ME(Array, exchangeArray,    arginfo_array_exchangeArray,    ZEND_ACC_PUBLIC)
1899    SPL_ME(Array, setIteratorClass, arginfo_array_setIteratorClass, ZEND_ACC_PUBLIC)
1900    SPL_ME(Array, getIteratorClass, arginfo_array_void,             ZEND_ACC_PUBLIC)
1901    PHP_FE_END
1902};
1903
1904static const zend_function_entry spl_funcs_ArrayIterator[] = {
1905    SPL_ME(Array, __construct,      arginfo_array___construct,      ZEND_ACC_PUBLIC)
1906    SPL_ME(Array, offsetExists,     arginfo_array_offsetGet,        ZEND_ACC_PUBLIC)
1907    SPL_ME(Array, offsetGet,        arginfo_array_offsetGet,        ZEND_ACC_PUBLIC)
1908    SPL_ME(Array, offsetSet,        arginfo_array_offsetSet,        ZEND_ACC_PUBLIC)
1909    SPL_ME(Array, offsetUnset,      arginfo_array_offsetGet,        ZEND_ACC_PUBLIC)
1910    SPL_ME(Array, append,           arginfo_array_append,           ZEND_ACC_PUBLIC)
1911    SPL_ME(Array, getArrayCopy,     arginfo_array_void,             ZEND_ACC_PUBLIC)
1912    SPL_ME(Array, count,            arginfo_array_void,             ZEND_ACC_PUBLIC)
1913    SPL_ME(Array, getFlags,         arginfo_array_void,             ZEND_ACC_PUBLIC)
1914    SPL_ME(Array, setFlags,         arginfo_array_setFlags,         ZEND_ACC_PUBLIC)
1915    SPL_ME(Array, asort,            arginfo_array_void,             ZEND_ACC_PUBLIC)
1916    SPL_ME(Array, ksort,            arginfo_array_void,             ZEND_ACC_PUBLIC)
1917    SPL_ME(Array, uasort,           arginfo_array_uXsort,           ZEND_ACC_PUBLIC)
1918    SPL_ME(Array, uksort,           arginfo_array_uXsort,           ZEND_ACC_PUBLIC)
1919    SPL_ME(Array, natsort,          arginfo_array_void,             ZEND_ACC_PUBLIC)
1920    SPL_ME(Array, natcasesort,      arginfo_array_void,             ZEND_ACC_PUBLIC)
1921    SPL_ME(Array, unserialize,      arginfo_array_unserialize,      ZEND_ACC_PUBLIC)
1922    SPL_ME(Array, serialize,        arginfo_array_void,             ZEND_ACC_PUBLIC)
1923    /* ArrayIterator specific */
1924    SPL_ME(Array, rewind,           arginfo_array_void,             ZEND_ACC_PUBLIC)
1925    SPL_ME(Array, current,          arginfo_array_void,             ZEND_ACC_PUBLIC)
1926    SPL_ME(Array, key,              arginfo_array_void,             ZEND_ACC_PUBLIC)
1927    SPL_ME(Array, next,             arginfo_array_void,             ZEND_ACC_PUBLIC)
1928    SPL_ME(Array, valid,            arginfo_array_void,             ZEND_ACC_PUBLIC)
1929    SPL_ME(Array, seek,             arginfo_array_seek,             ZEND_ACC_PUBLIC)
1930    PHP_FE_END
1931};
1932
1933static const zend_function_entry spl_funcs_RecursiveArrayIterator[] = {
1934    SPL_ME(Array, hasChildren,   arginfo_array_void, ZEND_ACC_PUBLIC)
1935    SPL_ME(Array, getChildren,   arginfo_array_void, ZEND_ACC_PUBLIC)
1936    PHP_FE_END
1937};
1938/* }}} */
1939
1940/* {{{ PHP_MINIT_FUNCTION(spl_array) */
1941PHP_MINIT_FUNCTION(spl_array)
1942{
1943    REGISTER_SPL_STD_CLASS_EX(ArrayObject, spl_array_object_new, spl_funcs_ArrayObject);
1944    REGISTER_SPL_IMPLEMENTS(ArrayObject, Aggregate);
1945    REGISTER_SPL_IMPLEMENTS(ArrayObject, ArrayAccess);
1946    REGISTER_SPL_IMPLEMENTS(ArrayObject, Serializable);
1947    REGISTER_SPL_IMPLEMENTS(ArrayObject, Countable);
1948    memcpy(&spl_handler_ArrayObject, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
1949
1950    spl_handler_ArrayObject.clone_obj = spl_array_object_clone;
1951    spl_handler_ArrayObject.read_dimension = spl_array_read_dimension;
1952    spl_handler_ArrayObject.write_dimension = spl_array_write_dimension;
1953    spl_handler_ArrayObject.unset_dimension = spl_array_unset_dimension;
1954    spl_handler_ArrayObject.has_dimension = spl_array_has_dimension;
1955    spl_handler_ArrayObject.count_elements = spl_array_object_count_elements;
1956
1957    spl_handler_ArrayObject.get_properties = spl_array_get_properties;
1958    spl_handler_ArrayObject.get_debug_info = spl_array_get_debug_info;
1959    spl_handler_ArrayObject.read_property = spl_array_read_property;
1960    spl_handler_ArrayObject.write_property = spl_array_write_property;
1961    spl_handler_ArrayObject.get_property_ptr_ptr = spl_array_get_property_ptr_ptr;
1962    spl_handler_ArrayObject.has_property = spl_array_has_property;
1963    spl_handler_ArrayObject.unset_property = spl_array_unset_property;
1964
1965    spl_handler_ArrayObject.compare_objects = spl_array_compare_objects;
1966
1967    REGISTER_SPL_STD_CLASS_EX(ArrayIterator, spl_array_object_new, spl_funcs_ArrayIterator);
1968    REGISTER_SPL_IMPLEMENTS(ArrayIterator, Iterator);
1969    REGISTER_SPL_IMPLEMENTS(ArrayIterator, ArrayAccess);
1970    REGISTER_SPL_IMPLEMENTS(ArrayIterator, SeekableIterator);
1971    REGISTER_SPL_IMPLEMENTS(ArrayIterator, Serializable);
1972    REGISTER_SPL_IMPLEMENTS(ArrayIterator, Countable);
1973    memcpy(&spl_handler_ArrayIterator, &spl_handler_ArrayObject, sizeof(zend_object_handlers));
1974    spl_ce_ArrayIterator->get_iterator = spl_array_get_iterator;
1975
1976    REGISTER_SPL_SUB_CLASS_EX(RecursiveArrayIterator, ArrayIterator, spl_array_object_new, spl_funcs_RecursiveArrayIterator);
1977    REGISTER_SPL_IMPLEMENTS(RecursiveArrayIterator, RecursiveIterator);
1978    spl_ce_RecursiveArrayIterator->get_iterator = spl_array_get_iterator;
1979
1980    REGISTER_SPL_CLASS_CONST_LONG(ArrayObject,   "STD_PROP_LIST",    SPL_ARRAY_STD_PROP_LIST);
1981    REGISTER_SPL_CLASS_CONST_LONG(ArrayObject,   "ARRAY_AS_PROPS",   SPL_ARRAY_ARRAY_AS_PROPS);
1982
1983    REGISTER_SPL_CLASS_CONST_LONG(ArrayIterator, "STD_PROP_LIST",    SPL_ARRAY_STD_PROP_LIST);
1984    REGISTER_SPL_CLASS_CONST_LONG(ArrayIterator, "ARRAY_AS_PROPS",   SPL_ARRAY_ARRAY_AS_PROPS);
1985
1986    REGISTER_SPL_CLASS_CONST_LONG(RecursiveArrayIterator, "CHILD_ARRAYS_ONLY", SPL_ARRAY_CHILD_ARRAYS_ONLY);
1987
1988    return SUCCESS;
1989}
1990/* }}} */
1991
1992/*
1993 * Local variables:
1994 * tab-width: 4
1995 * c-basic-offset: 4
1996 * End:
1997 * vim600: fdm=marker
1998 * vim: noet sw=4 ts=4
1999 */
2000