1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2013 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;
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, **tmp;
597
598    if (check_inherited && intern->fptr_offset_has) {
599        SEPARATE_ARG_IF_REF(offset);
600        zend_call_method_with_1_params(&object, Z_OBJCE_P(object), &intern->fptr_offset_has, "offsetExists", &rv, offset);
601        zval_ptr_dtor(&offset);
602        if (rv && zend_is_true(rv)) {
603            zval_ptr_dtor(&rv);
604            return 1;
605        }
606        if (rv) {
607            zval_ptr_dtor(&rv);
608        }
609        return 0;
610    }
611
612    switch(Z_TYPE_P(offset)) {
613        case IS_STRING:
614            {
615                HashTable *ht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
616                if (zend_symtable_find(ht, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, (void **) &tmp) != FAILURE) {
617                    switch (check_empty) {
618                        case 0:
619                            return Z_TYPE_PP(tmp) != IS_NULL;
620                        case 2:
621                            return 1;
622                        default:
623                            return zend_is_true(*tmp);
624                    }
625                }
626            }
627            return 0;
628        case IS_DOUBLE:
629        case IS_RESOURCE:
630        case IS_BOOL:
631        case IS_LONG:
632            {
633                HashTable *ht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
634                if (offset->type == IS_DOUBLE) {
635                    index = (long)Z_DVAL_P(offset);
636                } else {
637                    index = Z_LVAL_P(offset);
638                }
639                if (zend_hash_index_find(ht, index, (void **)&tmp) != FAILURE) {
640                    switch (check_empty) {
641                        case 0:
642                            return Z_TYPE_PP(tmp) != IS_NULL;
643                        case 2:
644                            return 1;
645                        default:
646                            return zend_is_true(*tmp);
647                    }
648                }
649                return 0;
650            }
651        default:
652            zend_error(E_WARNING, "Illegal offset type");
653    }
654    return 0;
655} /* }}} */
656
657static int spl_array_has_dimension(zval *object, zval *offset, int check_empty TSRMLS_DC) /* {{{ */
658{
659    return spl_array_has_dimension_ex(1, object, offset, check_empty TSRMLS_CC);
660} /* }}} */
661
662/* {{{ spl_array_object_verify_pos_ex */
663static inline int spl_array_object_verify_pos_ex(spl_array_object *object, HashTable *ht, const char *msg_prefix TSRMLS_DC)
664{
665    if (!ht) {
666        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "%sArray was modified outside object and is no longer an array", msg_prefix);
667        return FAILURE;
668    }
669
670    if (object->pos && (object->ar_flags & SPL_ARRAY_IS_REF) && spl_hash_verify_pos_ex(object, ht TSRMLS_CC) == FAILURE) {
671        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "%sArray was modified outside object and internal position is no longer valid", msg_prefix);
672        return FAILURE;
673    }
674
675    return SUCCESS;
676} /* }}} */
677
678/* {{{ spl_array_object_verify_pos */
679static inline int spl_array_object_verify_pos(spl_array_object *object, HashTable *ht TSRMLS_DC)
680{
681    return spl_array_object_verify_pos_ex(object, ht, "" TSRMLS_CC);
682} /* }}} */
683
684/* {{{ proto bool ArrayObject::offsetExists(mixed $index)
685       proto bool ArrayIterator::offsetExists(mixed $index)
686   Returns whether the requested $index exists. */
687SPL_METHOD(Array, offsetExists)
688{
689    zval *index;
690    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &index) == FAILURE) {
691        return;
692    }
693    RETURN_BOOL(spl_array_has_dimension_ex(0, getThis(), index, 2 TSRMLS_CC));
694} /* }}} */
695
696/* {{{ proto mixed ArrayObject::offsetGet(mixed $index)
697       proto mixed ArrayIterator::offsetGet(mixed $index)
698   Returns the value at the specified $index. */
699SPL_METHOD(Array, offsetGet)
700{
701    zval *index, *value;
702    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &index) == FAILURE) {
703        return;
704    }
705    value = spl_array_read_dimension_ex(0, getThis(), index, BP_VAR_R TSRMLS_CC);
706    RETURN_ZVAL(value, 1, 0);
707} /* }}} */
708
709/* {{{ proto void ArrayObject::offsetSet(mixed $index, mixed $newval)
710       proto void ArrayIterator::offsetSet(mixed $index, mixed $newval)
711   Sets the value at the specified $index to $newval. */
712SPL_METHOD(Array, offsetSet)
713{
714    zval *index, *value;
715    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &index, &value) == FAILURE) {
716        return;
717    }
718    spl_array_write_dimension_ex(0, getThis(), index, value TSRMLS_CC);
719} /* }}} */
720
721void spl_array_iterator_append(zval *object, zval *append_value TSRMLS_DC) /* {{{ */
722{
723    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
724    HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
725
726    if (!aht) {
727        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array was modified outside object and is no longer an array");
728        return;
729    }
730
731    if (Z_TYPE_P(intern->array) == IS_OBJECT) {
732        php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "Cannot append properties to objects, use %s::offsetSet() instead", Z_OBJCE_P(object)->name);
733        return;
734    }
735
736    spl_array_write_dimension(object, NULL, append_value TSRMLS_CC);
737    if (!intern->pos) {
738        spl_array_set_pos(intern, aht->pListTail);
739    }
740} /* }}} */
741
742/* {{{ proto void ArrayObject::append(mixed $newval)
743       proto void ArrayIterator::append(mixed $newval)
744   Appends the value (cannot be called for objects). */
745SPL_METHOD(Array, append)
746{
747    zval *value;
748
749    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
750        return;
751    }
752    spl_array_iterator_append(getThis(), value TSRMLS_CC);
753} /* }}} */
754
755/* {{{ proto void ArrayObject::offsetUnset(mixed $index)
756       proto void ArrayIterator::offsetUnset(mixed $index)
757   Unsets the value at the specified $index. */
758SPL_METHOD(Array, offsetUnset)
759{
760    zval *index;
761    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &index) == FAILURE) {
762        return;
763    }
764    spl_array_unset_dimension_ex(0, getThis(), index TSRMLS_CC);
765} /* }}} */
766
767/* {{{ proto array ArrayObject::getArrayCopy()
768      proto array ArrayIterator::getArrayCopy()
769   Return a copy of the contained array */
770SPL_METHOD(Array, getArrayCopy)
771{
772    zval *object = getThis(), *tmp;
773    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
774
775    array_init(return_value);
776    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*));
777} /* }}} */
778
779static HashTable *spl_array_get_properties(zval *object TSRMLS_DC) /* {{{ */
780{
781    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
782    HashTable *result;
783
784    if (intern->nApplyCount > 1) {
785        php_error_docref(NULL TSRMLS_CC, E_ERROR, "Nesting level too deep - recursive dependency?");
786    }
787
788    intern->nApplyCount++;
789    result = spl_array_get_hash_table(intern, 1 TSRMLS_CC);
790    intern->nApplyCount--;
791    return result;
792} /* }}} */
793
794static HashTable* spl_array_get_debug_info(zval *obj, int *is_temp TSRMLS_DC) /* {{{ */
795{
796    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(obj TSRMLS_CC);
797    zval *tmp, *storage;
798    int name_len;
799    char *zname;
800    zend_class_entry *base;
801
802    *is_temp = 0;
803
804    if (!intern->std.properties) {
805        rebuild_object_properties(&intern->std);
806    }
807
808    if (HASH_OF(intern->array) == intern->std.properties) {
809        return intern->std.properties;
810    } else {
811        if (intern->debug_info == NULL) {
812            ALLOC_HASHTABLE(intern->debug_info);
813            ZEND_INIT_SYMTABLE_EX(intern->debug_info, zend_hash_num_elements(intern->std.properties) + 1, 0);
814        }
815
816        if (intern->debug_info->nApplyCount == 0) {
817            zend_hash_clean(intern->debug_info);
818            zend_hash_copy(intern->debug_info, intern->std.properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *));
819
820            storage = intern->array;
821            zval_add_ref(&storage);
822
823            base = (Z_OBJ_HT_P(obj) == &spl_handler_ArrayIterator) ? spl_ce_ArrayIterator : spl_ce_ArrayObject;
824            zname = spl_gen_private_prop_name(base, "storage", sizeof("storage")-1, &name_len TSRMLS_CC);
825            zend_symtable_update(intern->debug_info, zname, name_len+1, &storage, sizeof(zval *), NULL);
826            efree(zname);
827        }
828
829        return intern->debug_info;
830    }
831}
832/* }}} */
833
834static zval *spl_array_read_property(zval *object, zval *member, int type, const zend_literal *key TSRMLS_DC) /* {{{ */
835{
836    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
837
838    if ((intern->ar_flags & SPL_ARRAY_ARRAY_AS_PROPS) != 0
839    && !std_object_handlers.has_property(object, member, 2, key TSRMLS_CC)) {
840        return spl_array_read_dimension(object, member, type TSRMLS_CC);
841    }
842    return std_object_handlers.read_property(object, member, type, key TSRMLS_CC);
843} /* }}} */
844
845static void spl_array_write_property(zval *object, zval *member, zval *value, const zend_literal *key TSRMLS_DC) /* {{{ */
846{
847    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
848
849    if ((intern->ar_flags & SPL_ARRAY_ARRAY_AS_PROPS) != 0
850    && !std_object_handlers.has_property(object, member, 2, key TSRMLS_CC)) {
851        spl_array_write_dimension(object, member, value TSRMLS_CC);
852        return;
853    }
854    std_object_handlers.write_property(object, member, value, key TSRMLS_CC);
855} /* }}} */
856
857static zval **spl_array_get_property_ptr_ptr(zval *object, zval *member, int type, const zend_literal *key TSRMLS_DC) /* {{{ */
858{
859    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
860
861    if ((intern->ar_flags & SPL_ARRAY_ARRAY_AS_PROPS) != 0
862    && !std_object_handlers.has_property(object, member, 2, key TSRMLS_CC)) {
863        return spl_array_get_dimension_ptr_ptr(1, object, member, type TSRMLS_CC);
864    }
865    return std_object_handlers.get_property_ptr_ptr(object, member, type, key TSRMLS_CC);
866} /* }}} */
867
868static int spl_array_has_property(zval *object, zval *member, int has_set_exists, const zend_literal *key TSRMLS_DC) /* {{{ */
869{
870    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
871
872    if ((intern->ar_flags & SPL_ARRAY_ARRAY_AS_PROPS) != 0
873    && !std_object_handlers.has_property(object, member, 2, key TSRMLS_CC)) {
874        return spl_array_has_dimension(object, member, has_set_exists TSRMLS_CC);
875    }
876    return std_object_handlers.has_property(object, member, has_set_exists, key TSRMLS_CC);
877} /* }}} */
878
879static void spl_array_unset_property(zval *object, zval *member, const zend_literal *key TSRMLS_DC) /* {{{ */
880{
881    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
882
883    if ((intern->ar_flags & SPL_ARRAY_ARRAY_AS_PROPS) != 0
884    && !std_object_handlers.has_property(object, member, 2, key TSRMLS_CC)) {
885        spl_array_unset_dimension(object, member TSRMLS_CC);
886        spl_array_rewind(intern TSRMLS_CC); /* because deletion might invalidate position */
887        return;
888    }
889    std_object_handlers.unset_property(object, member, key TSRMLS_CC);
890} /* }}} */
891
892static int spl_array_compare_objects(zval *o1, zval *o2 TSRMLS_DC) /* {{{ */
893{
894    HashTable           *ht1,
895                        *ht2;
896    spl_array_object    *intern1,
897                        *intern2;
898    int                 result  = 0;
899    zval                temp_zv;
900
901    intern1 = (spl_array_object*)zend_object_store_get_object(o1 TSRMLS_CC);
902    intern2 = (spl_array_object*)zend_object_store_get_object(o2 TSRMLS_CC);
903    ht1     = spl_array_get_hash_table(intern1, 0 TSRMLS_CC);
904    ht2     = spl_array_get_hash_table(intern2, 0 TSRMLS_CC);
905
906    zend_compare_symbol_tables(&temp_zv, ht1, ht2 TSRMLS_CC);
907    result = (int)Z_LVAL(temp_zv);
908    /* if we just compared std.properties, don't do it again */
909    if (result == 0 &&
910            !(ht1 == intern1->std.properties && ht2 == intern2->std.properties)) {
911        result = std_object_handlers.compare_objects(o1, o2 TSRMLS_CC);
912    }
913    return result;
914} /* }}} */
915
916static int spl_array_skip_protected(spl_array_object *intern, HashTable *aht TSRMLS_DC) /* {{{ */
917{
918    char *string_key;
919    uint string_length;
920    ulong num_key;
921
922    if (Z_TYPE_P(intern->array) == IS_OBJECT) {
923        do {
924            if (zend_hash_get_current_key_ex(aht, &string_key, &string_length, &num_key, 0, &intern->pos) == HASH_KEY_IS_STRING) {
925                if (!string_length || string_key[0]) {
926                    return SUCCESS;
927                }
928            } else {
929                return SUCCESS;
930            }
931            if (zend_hash_has_more_elements_ex(aht, &intern->pos) != SUCCESS) {
932                return FAILURE;
933            }
934            zend_hash_move_forward_ex(aht, &intern->pos);
935            spl_array_update_pos(intern);
936        } while (1);
937    }
938    return FAILURE;
939} /* }}} */
940
941static int spl_array_next_no_verify(spl_array_object *intern, HashTable *aht TSRMLS_DC) /* {{{ */
942{
943    zend_hash_move_forward_ex(aht, &intern->pos);
944    spl_array_update_pos(intern);
945    if (Z_TYPE_P(intern->array) == IS_OBJECT) {
946        return spl_array_skip_protected(intern, aht TSRMLS_CC);
947    } else {
948        return zend_hash_has_more_elements_ex(aht, &intern->pos);
949    }
950} /* }}} */
951
952static int spl_array_next_ex(spl_array_object *intern, HashTable *aht TSRMLS_DC) /* {{{ */
953{
954    if ((intern->ar_flags & SPL_ARRAY_IS_REF) && spl_hash_verify_pos_ex(intern, aht TSRMLS_CC) == FAILURE) {
955        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array was modified outside object and internal position is no longer valid");
956        return FAILURE;
957    }
958
959    return spl_array_next_no_verify(intern, aht TSRMLS_CC);
960} /* }}} */
961
962static int spl_array_next(spl_array_object *intern TSRMLS_DC) /* {{{ */
963{
964    HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
965
966    return spl_array_next_ex(intern, aht TSRMLS_CC);
967
968} /* }}} */
969
970/* define an overloaded iterator structure */
971typedef struct {
972    zend_user_iterator    intern;
973    spl_array_object      *object;
974} spl_array_it;
975
976static void spl_array_it_dtor(zend_object_iterator *iter TSRMLS_DC) /* {{{ */
977{
978    spl_array_it *iterator = (spl_array_it *)iter;
979
980    zend_user_it_invalidate_current(iter TSRMLS_CC);
981    zval_ptr_dtor((zval**)&iterator->intern.it.data);
982
983    efree(iterator);
984}
985/* }}} */
986
987static int spl_array_it_valid(zend_object_iterator *iter TSRMLS_DC) /* {{{ */
988{
989    spl_array_it       *iterator = (spl_array_it *)iter;
990    spl_array_object   *object   = iterator->object;
991    HashTable          *aht      = spl_array_get_hash_table(object, 0 TSRMLS_CC);
992
993    if (object->ar_flags & SPL_ARRAY_OVERLOADED_VALID) {
994        return zend_user_it_valid(iter TSRMLS_CC);
995    } else {
996        if (spl_array_object_verify_pos_ex(object, aht, "ArrayIterator::valid(): " TSRMLS_CC) == FAILURE) {
997            return FAILURE;
998        }
999
1000        return zend_hash_has_more_elements_ex(aht, &object->pos);
1001    }
1002}
1003/* }}} */
1004
1005static void spl_array_it_get_current_data(zend_object_iterator *iter, zval ***data TSRMLS_DC) /* {{{ */
1006{
1007    spl_array_it       *iterator = (spl_array_it *)iter;
1008    spl_array_object   *object   = iterator->object;
1009    HashTable          *aht      = spl_array_get_hash_table(object, 0 TSRMLS_CC);
1010
1011    if (object->ar_flags & SPL_ARRAY_OVERLOADED_CURRENT) {
1012        zend_user_it_get_current_data(iter, data TSRMLS_CC);
1013    } else {
1014        if (zend_hash_get_current_data_ex(aht, (void**)data, &object->pos) == FAILURE) {
1015            *data = NULL;
1016        }
1017    }
1018}
1019/* }}} */
1020
1021static void spl_array_it_get_current_key(zend_object_iterator *iter, zval *key 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_KEY) {
1028        zend_user_it_get_current_key(iter, key TSRMLS_CC);
1029    } else {
1030        if (spl_array_object_verify_pos_ex(object, aht, "ArrayIterator::current(): " TSRMLS_CC) == FAILURE) {
1031            ZVAL_NULL(key);
1032        } else {
1033            zend_hash_get_current_key_zval_ex(aht, key, &object->pos);
1034        }
1035    }
1036}
1037/* }}} */
1038
1039static void spl_array_it_move_forward(zend_object_iterator *iter TSRMLS_DC) /* {{{ */
1040{
1041    spl_array_it       *iterator = (spl_array_it *)iter;
1042    spl_array_object   *object   = iterator->object;
1043    HashTable          *aht      = spl_array_get_hash_table(object, 0 TSRMLS_CC);
1044
1045    if (object->ar_flags & SPL_ARRAY_OVERLOADED_NEXT) {
1046        zend_user_it_move_forward(iter TSRMLS_CC);
1047    } else {
1048        zend_user_it_invalidate_current(iter TSRMLS_CC);
1049        if (!aht) {
1050            php_error_docref(NULL TSRMLS_CC, E_NOTICE, "ArrayIterator::current(): Array was modified outside object and is no longer an array");
1051            return;
1052        }
1053
1054        if ((object->ar_flags & SPL_ARRAY_IS_REF) && spl_hash_verify_pos_ex(object, aht TSRMLS_CC) == FAILURE) {
1055            php_error_docref(NULL TSRMLS_CC, E_NOTICE, "ArrayIterator::next(): Array was modified outside object and internal position is no longer valid");
1056        } else {
1057            spl_array_next_no_verify(object, aht TSRMLS_CC);
1058        }
1059    }
1060}
1061/* }}} */
1062
1063static void spl_array_rewind_ex(spl_array_object *intern, HashTable *aht TSRMLS_DC) /* {{{ */
1064{
1065
1066    zend_hash_internal_pointer_reset_ex(aht, &intern->pos);
1067    spl_array_update_pos(intern);
1068    spl_array_skip_protected(intern, aht TSRMLS_CC);
1069
1070} /* }}} */
1071
1072static void spl_array_rewind(spl_array_object *intern TSRMLS_DC) /* {{{ */
1073{
1074    HashTable          *aht      = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
1075
1076    if (!aht) {
1077        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "ArrayIterator::rewind(): Array was modified outside object and is no longer an array");
1078        return;
1079    }
1080
1081    spl_array_rewind_ex(intern, aht TSRMLS_CC);
1082}
1083/* }}} */
1084
1085static void spl_array_it_rewind(zend_object_iterator *iter TSRMLS_DC) /* {{{ */
1086{
1087    spl_array_it       *iterator = (spl_array_it *)iter;
1088    spl_array_object   *object   = iterator->object;
1089
1090    if (object->ar_flags & SPL_ARRAY_OVERLOADED_REWIND) {
1091        zend_user_it_rewind(iter TSRMLS_CC);
1092    } else {
1093        zend_user_it_invalidate_current(iter TSRMLS_CC);
1094        spl_array_rewind(object TSRMLS_CC);
1095    }
1096}
1097/* }}} */
1098
1099/* {{{ spl_array_set_array */
1100static void spl_array_set_array(zval *object, spl_array_object *intern, zval **array, long ar_flags, int just_array TSRMLS_DC) {
1101
1102    if (Z_TYPE_PP(array) == IS_ARRAY) {
1103        SEPARATE_ZVAL_IF_NOT_REF(array);
1104    }
1105
1106    if (Z_TYPE_PP(array) == IS_OBJECT && (Z_OBJ_HT_PP(array) == &spl_handler_ArrayObject || Z_OBJ_HT_PP(array) == &spl_handler_ArrayIterator)) {
1107        zval_ptr_dtor(&intern->array);
1108        if (just_array) {
1109            spl_array_object *other = (spl_array_object*)zend_object_store_get_object(*array TSRMLS_CC);
1110            ar_flags = other->ar_flags & ~SPL_ARRAY_INT_MASK;
1111        }
1112        ar_flags |= SPL_ARRAY_USE_OTHER;
1113        intern->array = *array;
1114    } else {
1115        if (Z_TYPE_PP(array) != IS_OBJECT && Z_TYPE_PP(array) != IS_ARRAY) {
1116            zend_throw_exception(spl_ce_InvalidArgumentException, "Passed variable is not an array or object, using empty array instead", 0 TSRMLS_CC);
1117            return;
1118        }
1119        zval_ptr_dtor(&intern->array);
1120        intern->array = *array;
1121    }
1122    if (object == *array) {
1123        intern->ar_flags |= SPL_ARRAY_IS_SELF;
1124        intern->ar_flags &= ~SPL_ARRAY_USE_OTHER;
1125    } else {
1126        intern->ar_flags &= ~SPL_ARRAY_IS_SELF;
1127    }
1128    intern->ar_flags |= ar_flags;
1129    Z_ADDREF_P(intern->array);
1130    if (Z_TYPE_PP(array) == IS_OBJECT) {
1131        zend_object_get_properties_t handler = Z_OBJ_HANDLER_PP(array, get_properties);
1132        if ((handler != std_object_handlers.get_properties && handler != spl_array_get_properties)
1133        || !spl_array_get_hash_table(intern, 0 TSRMLS_CC)) {
1134            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);
1135        }
1136    }
1137
1138    spl_array_rewind(intern TSRMLS_CC);
1139}
1140/* }}} */
1141
1142/* iterator handler table */
1143zend_object_iterator_funcs spl_array_it_funcs = {
1144    spl_array_it_dtor,
1145    spl_array_it_valid,
1146    spl_array_it_get_current_data,
1147    spl_array_it_get_current_key,
1148    spl_array_it_move_forward,
1149    spl_array_it_rewind
1150};
1151
1152zend_object_iterator *spl_array_get_iterator(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC) /* {{{ */
1153{
1154    spl_array_it       *iterator;
1155    spl_array_object   *array_object = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
1156
1157    if (by_ref && (array_object->ar_flags & SPL_ARRAY_OVERLOADED_CURRENT)) {
1158        zend_error(E_ERROR, "An iterator cannot be used with foreach by reference");
1159    }
1160
1161    iterator     = emalloc(sizeof(spl_array_it));
1162
1163    Z_ADDREF_P(object);
1164    iterator->intern.it.data = (void*)object;
1165    iterator->intern.it.funcs = &spl_array_it_funcs;
1166    iterator->intern.ce = ce;
1167    iterator->intern.value = NULL;
1168    iterator->object = array_object;
1169
1170    return (zend_object_iterator*)iterator;
1171}
1172/* }}} */
1173
1174/* {{{ proto void ArrayObject::__construct(array|object ar = array() [, int flags = 0 [, string iterator_class = "ArrayIterator"]])
1175       proto void ArrayIterator::__construct(array|object ar = array() [, int flags = 0])
1176   Constructs a new array iterator from a path. */
1177SPL_METHOD(Array, __construct)
1178{
1179    zval *object = getThis();
1180    spl_array_object *intern;
1181    zval **array;
1182    long ar_flags = 0;
1183    zend_class_entry *ce_get_iterator = spl_ce_Iterator;
1184    zend_error_handling error_handling;
1185
1186    if (ZEND_NUM_ARGS() == 0) {
1187        return; /* nothing to do */
1188    }
1189
1190    zend_replace_error_handling(EH_THROW, spl_ce_InvalidArgumentException, &error_handling TSRMLS_CC);
1191
1192    intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
1193
1194    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|lC", &array, &ar_flags, &ce_get_iterator) == FAILURE) {
1195        zend_restore_error_handling(&error_handling TSRMLS_CC);
1196        return;
1197    }
1198
1199    if (ZEND_NUM_ARGS() > 2) {
1200        intern->ce_get_iterator = ce_get_iterator;
1201    }
1202
1203    ar_flags &= ~SPL_ARRAY_INT_MASK;
1204
1205    spl_array_set_array(object, intern, array, ar_flags, ZEND_NUM_ARGS() == 1 TSRMLS_CC);
1206
1207    zend_restore_error_handling(&error_handling TSRMLS_CC);
1208
1209}
1210 /* }}} */
1211
1212/* {{{ proto void ArrayObject::setIteratorClass(string iterator_class)
1213   Set the class used in getIterator. */
1214SPL_METHOD(Array, setIteratorClass)
1215{
1216    zval *object = getThis();
1217    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
1218    zend_class_entry * ce_get_iterator = spl_ce_Iterator;
1219
1220    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "C", &ce_get_iterator) == FAILURE) {
1221        return;
1222    }
1223
1224    intern->ce_get_iterator = ce_get_iterator;
1225}
1226/* }}} */
1227
1228/* {{{ proto string ArrayObject::getIteratorClass()
1229   Get the class used in getIterator. */
1230SPL_METHOD(Array, getIteratorClass)
1231{
1232    zval *object = getThis();
1233    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
1234
1235    if (zend_parse_parameters_none() == FAILURE) {
1236        return;
1237    }
1238
1239    RETURN_STRING(intern->ce_get_iterator->name, 1);
1240}
1241/* }}} */
1242
1243/* {{{ proto int ArrayObject::getFlags()
1244   Get flags */
1245SPL_METHOD(Array, getFlags)
1246{
1247    zval *object = getThis();
1248    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
1249
1250    if (zend_parse_parameters_none() == FAILURE) {
1251        return;
1252    }
1253
1254    RETURN_LONG(intern->ar_flags & ~SPL_ARRAY_INT_MASK);
1255}
1256/* }}} */
1257
1258/* {{{ proto void ArrayObject::setFlags(int flags)
1259   Set flags */
1260SPL_METHOD(Array, setFlags)
1261{
1262    zval *object = getThis();
1263    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
1264    long ar_flags = 0;
1265
1266    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &ar_flags) == FAILURE) {
1267        return;
1268    }
1269
1270    intern->ar_flags = (intern->ar_flags & SPL_ARRAY_INT_MASK) | (ar_flags & ~SPL_ARRAY_INT_MASK);
1271}
1272/* }}} */
1273
1274/* {{{ proto Array|Object ArrayObject::exchangeArray(Array|Object ar = array())
1275   Replace the referenced array or object with a new one and return the old one (right now copy - to be changed) */
1276SPL_METHOD(Array, exchangeArray)
1277{
1278    zval *object = getThis(), *tmp, **array;
1279    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
1280
1281    array_init(return_value);
1282    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*));
1283
1284    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &array) == FAILURE) {
1285        return;
1286    }
1287
1288    spl_array_set_array(object, intern, array, 0L, 1 TSRMLS_CC);
1289
1290}
1291/* }}} */
1292
1293/* {{{ proto ArrayIterator ArrayObject::getIterator()
1294   Create a new iterator from a ArrayObject instance */
1295SPL_METHOD(Array, getIterator)
1296{
1297    zval *object = getThis();
1298    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
1299    spl_array_object *iterator;
1300    HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
1301
1302    if (zend_parse_parameters_none() == FAILURE) {
1303        return;
1304    }
1305
1306    if (!aht) {
1307        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array was modified outside object and is no longer an array");
1308        return;
1309    }
1310
1311    return_value->type = IS_OBJECT;
1312    return_value->value.obj = spl_array_object_new_ex(intern->ce_get_iterator, &iterator, object, 0 TSRMLS_CC);
1313    Z_SET_REFCOUNT_P(return_value, 1);
1314    Z_SET_ISREF_P(return_value);
1315}
1316/* }}} */
1317
1318/* {{{ proto void ArrayIterator::rewind()
1319   Rewind array back to the start */
1320SPL_METHOD(Array, rewind)
1321{
1322    zval *object = getThis();
1323    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
1324
1325    if (zend_parse_parameters_none() == FAILURE) {
1326        return;
1327    }
1328
1329    spl_array_rewind(intern TSRMLS_CC);
1330}
1331/* }}} */
1332
1333/* {{{ proto void ArrayIterator::seek(int $position)
1334   Seek to position. */
1335SPL_METHOD(Array, seek)
1336{
1337    long opos, position;
1338    zval *object = getThis();
1339    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
1340    HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
1341    int result;
1342
1343    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &position) == FAILURE) {
1344        return;
1345    }
1346
1347    if (!aht) {
1348        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array was modified outside object and is no longer an array");
1349        return;
1350    }
1351
1352    opos = position;
1353
1354    if (position >= 0) { /* negative values are not supported */
1355        spl_array_rewind(intern TSRMLS_CC);
1356        result = SUCCESS;
1357
1358        while (position-- > 0 && (result = spl_array_next(intern TSRMLS_CC)) == SUCCESS);
1359
1360        if (result == SUCCESS && zend_hash_has_more_elements_ex(aht, &intern->pos) == SUCCESS) {
1361            return; /* ok */
1362        }
1363    }
1364    zend_throw_exception_ex(spl_ce_OutOfBoundsException, 0 TSRMLS_CC, "Seek position %ld is out of range", opos);
1365} /* }}} */
1366
1367int static spl_array_object_count_elements_helper(spl_array_object *intern, long *count TSRMLS_DC) /* {{{ */
1368{
1369    HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
1370    HashPosition pos;
1371
1372    if (!aht) {
1373        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array was modified outside object and is no longer an array");
1374        *count = 0;
1375        return FAILURE;
1376    }
1377
1378    if (Z_TYPE_P(intern->array) == IS_OBJECT) {
1379        /* We need to store the 'pos' since we'll modify it in the functions
1380         * we're going to call and which do not support 'pos' as parameter. */
1381        pos = intern->pos;
1382        *count = 0;
1383        spl_array_rewind(intern TSRMLS_CC);
1384        while(intern->pos && spl_array_next(intern TSRMLS_CC) == SUCCESS) {
1385            (*count)++;
1386        }
1387        spl_array_set_pos(intern, pos);
1388        return SUCCESS;
1389    } else {
1390        *count = zend_hash_num_elements(aht);
1391        return SUCCESS;
1392    }
1393} /* }}} */
1394
1395int spl_array_object_count_elements(zval *object, long *count TSRMLS_DC) /* {{{ */
1396{
1397    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
1398
1399    if (intern->fptr_count) {
1400        zval *rv;
1401        zend_call_method_with_0_params(&object, intern->std.ce, &intern->fptr_count, "count", &rv);
1402        if (rv) {
1403            zval_ptr_dtor(&intern->retval);
1404            MAKE_STD_ZVAL(intern->retval);
1405            ZVAL_ZVAL(intern->retval, rv, 1, 1);
1406            convert_to_long(intern->retval);
1407            *count = (long) Z_LVAL_P(intern->retval);
1408            return SUCCESS;
1409        }
1410        *count = 0;
1411        return FAILURE;
1412    }
1413    return spl_array_object_count_elements_helper(intern, count TSRMLS_CC);
1414} /* }}} */
1415
1416/* {{{ proto int ArrayObject::count()
1417       proto int ArrayIterator::count()
1418   Return the number of elements in the Iterator. */
1419SPL_METHOD(Array, count)
1420{
1421    long count;
1422    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
1423
1424    if (zend_parse_parameters_none() == FAILURE) {
1425        return;
1426    }
1427
1428    spl_array_object_count_elements_helper(intern, &count TSRMLS_CC);
1429
1430    RETURN_LONG(count);
1431} /* }}} */
1432
1433static void spl_array_method(INTERNAL_FUNCTION_PARAMETERS, char *fname, int fname_len, int use_arg) /* {{{ */
1434{
1435    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
1436    HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
1437    zval *tmp, *arg = NULL;
1438    zval *retval_ptr = NULL;
1439
1440    MAKE_STD_ZVAL(tmp);
1441    Z_TYPE_P(tmp) = IS_ARRAY;
1442    Z_ARRVAL_P(tmp) = aht;
1443
1444    if (!use_arg) {
1445        aht->nApplyCount++;
1446        zend_call_method(NULL, NULL, NULL, fname, fname_len, &retval_ptr, 1, tmp, NULL TSRMLS_CC);
1447        aht->nApplyCount--;
1448    } else if (use_arg == SPL_ARRAY_METHOD_MAY_USER_ARG) {
1449        if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "|z", &arg) == FAILURE) {
1450            Z_TYPE_P(tmp) = IS_NULL;
1451            zval_ptr_dtor(&tmp);
1452            zend_throw_exception(spl_ce_BadMethodCallException, "Function expects one argument at most", 0 TSRMLS_CC);
1453            return;
1454        }
1455        aht->nApplyCount++;
1456        zend_call_method(NULL, NULL, NULL, fname, fname_len, &retval_ptr, arg? 2 : 1, tmp, arg TSRMLS_CC);
1457        aht->nApplyCount--;
1458    } else {
1459        if (ZEND_NUM_ARGS() != 1 || zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "z", &arg) == FAILURE) {
1460            Z_TYPE_P(tmp) = IS_NULL;
1461            zval_ptr_dtor(&tmp);
1462            zend_throw_exception(spl_ce_BadMethodCallException, "Function expects exactly one argument", 0 TSRMLS_CC);
1463            return;
1464        }
1465        aht->nApplyCount++;
1466        zend_call_method(NULL, NULL, NULL, fname, fname_len, &retval_ptr, 2, tmp, arg TSRMLS_CC);
1467        aht->nApplyCount--;
1468    }
1469    Z_TYPE_P(tmp) = IS_NULL; /* we want to destroy the zval, not the hashtable */
1470    zval_ptr_dtor(&tmp);
1471    if (retval_ptr) {
1472        COPY_PZVAL_TO_ZVAL(*return_value, retval_ptr);
1473    }
1474} /* }}} */
1475
1476#define SPL_ARRAY_METHOD(cname, fname, use_arg) \
1477SPL_METHOD(cname, fname) \
1478{ \
1479    spl_array_method(INTERNAL_FUNCTION_PARAM_PASSTHRU, #fname, sizeof(#fname)-1, use_arg); \
1480}
1481
1482/* {{{ proto int ArrayObject::asort([int $sort_flags = SORT_REGULAR ])
1483       proto int ArrayIterator::asort([int $sort_flags = SORT_REGULAR ])
1484   Sort the entries by values. */
1485SPL_ARRAY_METHOD(Array, asort, SPL_ARRAY_METHOD_MAY_USER_ARG) /* }}} */
1486
1487/* {{{ proto int ArrayObject::ksort([int $sort_flags = SORT_REGULAR ])
1488       proto int ArrayIterator::ksort([int $sort_flags = SORT_REGULAR ])
1489   Sort the entries by key. */
1490SPL_ARRAY_METHOD(Array, ksort, SPL_ARRAY_METHOD_MAY_USER_ARG) /* }}} */
1491
1492/* {{{ proto int ArrayObject::uasort(callback cmp_function)
1493       proto int ArrayIterator::uasort(callback cmp_function)
1494   Sort the entries by values user defined function. */
1495SPL_ARRAY_METHOD(Array, uasort, SPL_ARRAY_METHOD_USE_ARG) /* }}} */
1496
1497/* {{{ proto int ArrayObject::uksort(callback cmp_function)
1498       proto int ArrayIterator::uksort(callback cmp_function)
1499   Sort the entries by key using user defined function. */
1500SPL_ARRAY_METHOD(Array, uksort, SPL_ARRAY_METHOD_USE_ARG) /* }}} */
1501
1502/* {{{ proto int ArrayObject::natsort()
1503       proto int ArrayIterator::natsort()
1504   Sort the entries by values using "natural order" algorithm. */
1505SPL_ARRAY_METHOD(Array, natsort, SPL_ARRAY_METHOD_NO_ARG) /* }}} */
1506
1507/* {{{ proto int ArrayObject::natcasesort()
1508       proto int ArrayIterator::natcasesort()
1509   Sort the entries by key using case insensitive "natural order" algorithm. */
1510SPL_ARRAY_METHOD(Array, natcasesort, SPL_ARRAY_METHOD_NO_ARG) /* }}} */
1511
1512/* {{{ proto mixed|NULL ArrayIterator::current()
1513   Return current array entry */
1514SPL_METHOD(Array, current)
1515{
1516    zval *object = getThis();
1517    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
1518    zval **entry;
1519    HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
1520
1521    if (zend_parse_parameters_none() == FAILURE) {
1522        return;
1523    }
1524
1525    if (spl_array_object_verify_pos(intern, aht TSRMLS_CC) == FAILURE) {
1526        return;
1527    }
1528
1529    if (zend_hash_get_current_data_ex(aht, (void **) &entry, &intern->pos) == FAILURE) {
1530        return;
1531    }
1532    RETVAL_ZVAL(*entry, 1, 0);
1533}
1534/* }}} */
1535
1536/* {{{ proto mixed|NULL ArrayIterator::key()
1537   Return current array key */
1538SPL_METHOD(Array, key)
1539{
1540    if (zend_parse_parameters_none() == FAILURE) {
1541        return;
1542    }
1543
1544    spl_array_iterator_key(getThis(), return_value TSRMLS_CC);
1545} /* }}} */
1546
1547void spl_array_iterator_key(zval *object, zval *return_value TSRMLS_DC) /* {{{ */
1548{
1549    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
1550    HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
1551
1552    if (spl_array_object_verify_pos(intern, aht TSRMLS_CC) == FAILURE) {
1553        return;
1554    }
1555
1556    zend_hash_get_current_key_zval_ex(aht, return_value, &intern->pos);
1557}
1558/* }}} */
1559
1560/* {{{ proto void ArrayIterator::next()
1561   Move to next entry */
1562SPL_METHOD(Array, next)
1563{
1564    zval *object = getThis();
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 (zend_parse_parameters_none() == FAILURE) {
1569        return;
1570    }
1571
1572    if (spl_array_object_verify_pos(intern, aht TSRMLS_CC) == FAILURE) {
1573        return;
1574    }
1575
1576    spl_array_next_no_verify(intern, aht TSRMLS_CC);
1577}
1578/* }}} */
1579
1580/* {{{ proto bool ArrayIterator::valid()
1581   Check whether array contains more entries */
1582SPL_METHOD(Array, valid)
1583{
1584    zval *object = getThis();
1585    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
1586    HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
1587
1588    if (zend_parse_parameters_none() == FAILURE) {
1589        return;
1590    }
1591
1592    if (spl_array_object_verify_pos(intern, aht TSRMLS_CC) == FAILURE) {
1593        RETURN_FALSE;
1594    } else {
1595        RETURN_BOOL(zend_hash_has_more_elements_ex(aht, &intern->pos) == SUCCESS);
1596    }
1597}
1598/* }}} */
1599
1600/* {{{ proto bool RecursiveArrayIterator::hasChildren()
1601   Check whether current element has children (e.g. is an array) */
1602SPL_METHOD(Array, hasChildren)
1603{
1604    zval *object = getThis(), **entry;
1605    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
1606    HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
1607
1608    if (zend_parse_parameters_none() == FAILURE) {
1609        return;
1610    }
1611
1612    if (spl_array_object_verify_pos(intern, aht TSRMLS_CC) == FAILURE) {
1613        RETURN_FALSE;
1614    }
1615
1616    if (zend_hash_get_current_data_ex(aht, (void **) &entry, &intern->pos) == FAILURE) {
1617        RETURN_FALSE;
1618    }
1619
1620    RETURN_BOOL(Z_TYPE_PP(entry) == IS_ARRAY || (Z_TYPE_PP(entry) == IS_OBJECT && (intern->ar_flags & SPL_ARRAY_CHILD_ARRAYS_ONLY) == 0));
1621}
1622/* }}} */
1623
1624/* {{{ proto object RecursiveArrayIterator::getChildren()
1625   Create a sub iterator for the current element (same class as $this) */
1626SPL_METHOD(Array, getChildren)
1627{
1628    zval *object = getThis(), **entry, *flags;
1629    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
1630    HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
1631
1632    if (zend_parse_parameters_none() == FAILURE) {
1633        return;
1634    }
1635
1636    if (spl_array_object_verify_pos(intern, aht TSRMLS_CC) == FAILURE) {
1637        return;
1638    }
1639
1640    if (zend_hash_get_current_data_ex(aht, (void **) &entry, &intern->pos) == FAILURE) {
1641        return;
1642    }
1643
1644    if (Z_TYPE_PP(entry) == IS_OBJECT) {
1645        if ((intern->ar_flags & SPL_ARRAY_CHILD_ARRAYS_ONLY) != 0) {
1646            return;
1647        }
1648        if (instanceof_function(Z_OBJCE_PP(entry), Z_OBJCE_P(getThis()) TSRMLS_CC)) {
1649            RETURN_ZVAL(*entry, 0, 0);
1650        }
1651    }
1652
1653    MAKE_STD_ZVAL(flags);
1654    ZVAL_LONG(flags, SPL_ARRAY_USE_OTHER | intern->ar_flags);
1655    spl_instantiate_arg_ex2(Z_OBJCE_P(getThis()), &return_value, 0, *entry, flags TSRMLS_CC);
1656    zval_ptr_dtor(&flags);
1657}
1658/* }}} */
1659
1660/* {{{ proto string ArrayObject::serialize()
1661   Serialize the object */
1662SPL_METHOD(Array, serialize)
1663{
1664    zval *object = getThis();
1665    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
1666    HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
1667    zval members, *pmembers;
1668    php_serialize_data_t var_hash;
1669    smart_str buf = {0};
1670    zval *flags;
1671
1672    if (zend_parse_parameters_none() == FAILURE) {
1673        return;
1674    }
1675
1676    if (!aht) {
1677        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array was modified outside object and is no longer an array");
1678        return;
1679    }
1680
1681    PHP_VAR_SERIALIZE_INIT(var_hash);
1682
1683    MAKE_STD_ZVAL(flags);
1684    ZVAL_LONG(flags, (intern->ar_flags & SPL_ARRAY_CLONE_MASK));
1685
1686    /* storage */
1687    smart_str_appendl(&buf, "x:", 2);
1688    php_var_serialize(&buf, &flags, &var_hash TSRMLS_CC);
1689    zval_ptr_dtor(&flags);
1690
1691    if (!(intern->ar_flags & SPL_ARRAY_IS_SELF)) {
1692        php_var_serialize(&buf, &intern->array, &var_hash TSRMLS_CC);
1693        smart_str_appendc(&buf, ';');
1694    }
1695
1696    /* members */
1697    smart_str_appendl(&buf, "m:", 2);
1698    INIT_PZVAL(&members);
1699    if (!intern->std.properties) {
1700        rebuild_object_properties(&intern->std);
1701    }
1702    Z_ARRVAL(members) = intern->std.properties;
1703    Z_TYPE(members) = IS_ARRAY;
1704    pmembers = &members;
1705    php_var_serialize(&buf, &pmembers, &var_hash TSRMLS_CC); /* finishes the string */
1706
1707    /* done */
1708    PHP_VAR_SERIALIZE_DESTROY(var_hash);
1709
1710    if (buf.c) {
1711        RETURN_STRINGL(buf.c, buf.len, 0);
1712    }
1713
1714    RETURN_NULL();
1715} /* }}} */
1716
1717/* {{{ proto void ArrayObject::unserialize(string serialized)
1718 * unserialize the object
1719 */
1720SPL_METHOD(Array, unserialize)
1721{
1722    spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
1723
1724    char *buf;
1725    int buf_len;
1726    const unsigned char *p, *s;
1727    php_unserialize_data_t var_hash;
1728    zval *pmembers, *pflags = NULL;
1729    long flags;
1730
1731    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &buf, &buf_len) == FAILURE) {
1732        return;
1733    }
1734
1735    if (buf_len == 0) {
1736        zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Empty serialized string cannot be empty");
1737        return;
1738    }
1739
1740    /* storage */
1741    s = p = (const unsigned char*)buf;
1742    PHP_VAR_UNSERIALIZE_INIT(var_hash);
1743
1744    if (*p!= 'x' || *++p != ':') {
1745        goto outexcept;
1746    }
1747    ++p;
1748
1749    ALLOC_INIT_ZVAL(pflags);
1750    if (!php_var_unserialize(&pflags, &p, s + buf_len, &var_hash TSRMLS_CC) || Z_TYPE_P(pflags) != IS_LONG) {
1751        zval_ptr_dtor(&pflags);
1752        goto outexcept;
1753    }
1754
1755    --p; /* for ';' */
1756    flags = Z_LVAL_P(pflags);
1757    zval_ptr_dtor(&pflags);
1758    /* flags needs to be verified and we also need to verify whether the next
1759     * thing we get is ';'. After that we require an 'm' or somethign else
1760     * where 'm' stands for members and anything else should be an array. If
1761     * neither 'a' or 'm' follows we have an error. */
1762
1763    if (*p != ';') {
1764        goto outexcept;
1765    }
1766    ++p;
1767
1768    if (*p!='m') {
1769        if (*p!='a' && *p!='O' && *p!='C') {
1770            goto outexcept;
1771        }
1772        intern->ar_flags &= ~SPL_ARRAY_CLONE_MASK;
1773        intern->ar_flags |= flags & SPL_ARRAY_CLONE_MASK;
1774        zval_ptr_dtor(&intern->array);
1775        ALLOC_INIT_ZVAL(intern->array);
1776        if (!php_var_unserialize(&intern->array, &p, s + buf_len, &var_hash TSRMLS_CC)) {
1777            goto outexcept;
1778        }
1779    }
1780    if (*p != ';') {
1781        goto outexcept;
1782    }
1783    ++p;
1784
1785    /* members */
1786    if (*p!= 'm' || *++p != ':') {
1787        goto outexcept;
1788    }
1789    ++p;
1790
1791    ALLOC_INIT_ZVAL(pmembers);
1792    if (!php_var_unserialize(&pmembers, &p, s + buf_len, &var_hash TSRMLS_CC)) {
1793        zval_ptr_dtor(&pmembers);
1794        goto outexcept;
1795    }
1796
1797    /* copy members */
1798    if (!intern->std.properties) {
1799        rebuild_object_properties(&intern->std);
1800    }
1801    zend_hash_copy(intern->std.properties, Z_ARRVAL_P(pmembers), (copy_ctor_func_t) zval_add_ref, (void *) NULL, sizeof(zval *));
1802    zval_ptr_dtor(&pmembers);
1803
1804    /* done reading $serialized */
1805
1806    PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
1807    return;
1808
1809outexcept:
1810    PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
1811    zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Error at offset %ld of %d bytes", (long)((char*)p - buf), buf_len);
1812    return;
1813
1814} /* }}} */
1815
1816/* {{{ arginfo and function tbale */
1817ZEND_BEGIN_ARG_INFO(arginfo_array___construct, 0)
1818    ZEND_ARG_INFO(0, array)
1819ZEND_END_ARG_INFO()
1820
1821ZEND_BEGIN_ARG_INFO_EX(arginfo_array_offsetGet, 0, 0, 1)
1822    ZEND_ARG_INFO(0, index)
1823ZEND_END_ARG_INFO()
1824
1825ZEND_BEGIN_ARG_INFO_EX(arginfo_array_offsetSet, 0, 0, 2)
1826    ZEND_ARG_INFO(0, index)
1827    ZEND_ARG_INFO(0, newval)
1828ZEND_END_ARG_INFO()
1829
1830ZEND_BEGIN_ARG_INFO(arginfo_array_append, 0)
1831    ZEND_ARG_INFO(0, value)
1832ZEND_END_ARG_INFO()
1833
1834ZEND_BEGIN_ARG_INFO(arginfo_array_seek, 0)
1835    ZEND_ARG_INFO(0, position)
1836ZEND_END_ARG_INFO()
1837
1838ZEND_BEGIN_ARG_INFO(arginfo_array_exchangeArray, 0)
1839    ZEND_ARG_INFO(0, array)
1840ZEND_END_ARG_INFO()
1841
1842ZEND_BEGIN_ARG_INFO(arginfo_array_setFlags, 0)
1843    ZEND_ARG_INFO(0, flags)
1844ZEND_END_ARG_INFO()
1845
1846ZEND_BEGIN_ARG_INFO(arginfo_array_setIteratorClass, 0)
1847    ZEND_ARG_INFO(0, iteratorClass)
1848ZEND_END_ARG_INFO()
1849
1850ZEND_BEGIN_ARG_INFO(arginfo_array_uXsort, 0)
1851    ZEND_ARG_INFO(0, cmp_function)
1852ZEND_END_ARG_INFO();
1853
1854ZEND_BEGIN_ARG_INFO(arginfo_array_unserialize, 0)
1855    ZEND_ARG_INFO(0, serialized)
1856ZEND_END_ARG_INFO();
1857
1858ZEND_BEGIN_ARG_INFO(arginfo_array_void, 0)
1859ZEND_END_ARG_INFO()
1860
1861static const zend_function_entry spl_funcs_ArrayObject[] = {
1862    SPL_ME(Array, __construct,      arginfo_array___construct,      ZEND_ACC_PUBLIC)
1863    SPL_ME(Array, offsetExists,     arginfo_array_offsetGet,        ZEND_ACC_PUBLIC)
1864    SPL_ME(Array, offsetGet,        arginfo_array_offsetGet,        ZEND_ACC_PUBLIC)
1865    SPL_ME(Array, offsetSet,        arginfo_array_offsetSet,        ZEND_ACC_PUBLIC)
1866    SPL_ME(Array, offsetUnset,      arginfo_array_offsetGet,        ZEND_ACC_PUBLIC)
1867    SPL_ME(Array, append,           arginfo_array_append,           ZEND_ACC_PUBLIC)
1868    SPL_ME(Array, getArrayCopy,     arginfo_array_void,             ZEND_ACC_PUBLIC)
1869    SPL_ME(Array, count,            arginfo_array_void,             ZEND_ACC_PUBLIC)
1870    SPL_ME(Array, getFlags,         arginfo_array_void,             ZEND_ACC_PUBLIC)
1871    SPL_ME(Array, setFlags,         arginfo_array_setFlags,         ZEND_ACC_PUBLIC)
1872    SPL_ME(Array, asort,            arginfo_array_void,             ZEND_ACC_PUBLIC)
1873    SPL_ME(Array, ksort,            arginfo_array_void,             ZEND_ACC_PUBLIC)
1874    SPL_ME(Array, uasort,           arginfo_array_uXsort,           ZEND_ACC_PUBLIC)
1875    SPL_ME(Array, uksort,           arginfo_array_uXsort,           ZEND_ACC_PUBLIC)
1876    SPL_ME(Array, natsort,          arginfo_array_void,             ZEND_ACC_PUBLIC)
1877    SPL_ME(Array, natcasesort,      arginfo_array_void,             ZEND_ACC_PUBLIC)
1878    SPL_ME(Array, unserialize,      arginfo_array_unserialize,      ZEND_ACC_PUBLIC)
1879    SPL_ME(Array, serialize,        arginfo_array_void,             ZEND_ACC_PUBLIC)
1880    /* ArrayObject specific */
1881    SPL_ME(Array, getIterator,      arginfo_array_void,             ZEND_ACC_PUBLIC)
1882    SPL_ME(Array, exchangeArray,    arginfo_array_exchangeArray,    ZEND_ACC_PUBLIC)
1883    SPL_ME(Array, setIteratorClass, arginfo_array_setIteratorClass, ZEND_ACC_PUBLIC)
1884    SPL_ME(Array, getIteratorClass, arginfo_array_void,             ZEND_ACC_PUBLIC)
1885    PHP_FE_END
1886};
1887
1888static const zend_function_entry spl_funcs_ArrayIterator[] = {
1889    SPL_ME(Array, __construct,      arginfo_array___construct,      ZEND_ACC_PUBLIC)
1890    SPL_ME(Array, offsetExists,     arginfo_array_offsetGet,        ZEND_ACC_PUBLIC)
1891    SPL_ME(Array, offsetGet,        arginfo_array_offsetGet,        ZEND_ACC_PUBLIC)
1892    SPL_ME(Array, offsetSet,        arginfo_array_offsetSet,        ZEND_ACC_PUBLIC)
1893    SPL_ME(Array, offsetUnset,      arginfo_array_offsetGet,        ZEND_ACC_PUBLIC)
1894    SPL_ME(Array, append,           arginfo_array_append,           ZEND_ACC_PUBLIC)
1895    SPL_ME(Array, getArrayCopy,     arginfo_array_void,             ZEND_ACC_PUBLIC)
1896    SPL_ME(Array, count,            arginfo_array_void,             ZEND_ACC_PUBLIC)
1897    SPL_ME(Array, getFlags,         arginfo_array_void,             ZEND_ACC_PUBLIC)
1898    SPL_ME(Array, setFlags,         arginfo_array_setFlags,         ZEND_ACC_PUBLIC)
1899    SPL_ME(Array, asort,            arginfo_array_void,             ZEND_ACC_PUBLIC)
1900    SPL_ME(Array, ksort,            arginfo_array_void,             ZEND_ACC_PUBLIC)
1901    SPL_ME(Array, uasort,           arginfo_array_uXsort,           ZEND_ACC_PUBLIC)
1902    SPL_ME(Array, uksort,           arginfo_array_uXsort,           ZEND_ACC_PUBLIC)
1903    SPL_ME(Array, natsort,          arginfo_array_void,             ZEND_ACC_PUBLIC)
1904    SPL_ME(Array, natcasesort,      arginfo_array_void,             ZEND_ACC_PUBLIC)
1905    SPL_ME(Array, unserialize,      arginfo_array_unserialize,      ZEND_ACC_PUBLIC)
1906    SPL_ME(Array, serialize,        arginfo_array_void,             ZEND_ACC_PUBLIC)
1907    /* ArrayIterator specific */
1908    SPL_ME(Array, rewind,           arginfo_array_void,             ZEND_ACC_PUBLIC)
1909    SPL_ME(Array, current,          arginfo_array_void,             ZEND_ACC_PUBLIC)
1910    SPL_ME(Array, key,              arginfo_array_void,             ZEND_ACC_PUBLIC)
1911    SPL_ME(Array, next,             arginfo_array_void,             ZEND_ACC_PUBLIC)
1912    SPL_ME(Array, valid,            arginfo_array_void,             ZEND_ACC_PUBLIC)
1913    SPL_ME(Array, seek,             arginfo_array_seek,             ZEND_ACC_PUBLIC)
1914    PHP_FE_END
1915};
1916
1917static const zend_function_entry spl_funcs_RecursiveArrayIterator[] = {
1918    SPL_ME(Array, hasChildren,   arginfo_array_void, ZEND_ACC_PUBLIC)
1919    SPL_ME(Array, getChildren,   arginfo_array_void, ZEND_ACC_PUBLIC)
1920    PHP_FE_END
1921};
1922/* }}} */
1923
1924/* {{{ PHP_MINIT_FUNCTION(spl_array) */
1925PHP_MINIT_FUNCTION(spl_array)
1926{
1927    REGISTER_SPL_STD_CLASS_EX(ArrayObject, spl_array_object_new, spl_funcs_ArrayObject);
1928    REGISTER_SPL_IMPLEMENTS(ArrayObject, Aggregate);
1929    REGISTER_SPL_IMPLEMENTS(ArrayObject, ArrayAccess);
1930    REGISTER_SPL_IMPLEMENTS(ArrayObject, Serializable);
1931    REGISTER_SPL_IMPLEMENTS(ArrayObject, Countable);
1932    memcpy(&spl_handler_ArrayObject, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
1933
1934    spl_handler_ArrayObject.clone_obj = spl_array_object_clone;
1935    spl_handler_ArrayObject.read_dimension = spl_array_read_dimension;
1936    spl_handler_ArrayObject.write_dimension = spl_array_write_dimension;
1937    spl_handler_ArrayObject.unset_dimension = spl_array_unset_dimension;
1938    spl_handler_ArrayObject.has_dimension = spl_array_has_dimension;
1939    spl_handler_ArrayObject.count_elements = spl_array_object_count_elements;
1940
1941    spl_handler_ArrayObject.get_properties = spl_array_get_properties;
1942    spl_handler_ArrayObject.get_debug_info = spl_array_get_debug_info;
1943    spl_handler_ArrayObject.read_property = spl_array_read_property;
1944    spl_handler_ArrayObject.write_property = spl_array_write_property;
1945    spl_handler_ArrayObject.get_property_ptr_ptr = spl_array_get_property_ptr_ptr;
1946    spl_handler_ArrayObject.has_property = spl_array_has_property;
1947    spl_handler_ArrayObject.unset_property = spl_array_unset_property;
1948
1949    spl_handler_ArrayObject.compare_objects = spl_array_compare_objects;
1950
1951    REGISTER_SPL_STD_CLASS_EX(ArrayIterator, spl_array_object_new, spl_funcs_ArrayIterator);
1952    REGISTER_SPL_IMPLEMENTS(ArrayIterator, Iterator);
1953    REGISTER_SPL_IMPLEMENTS(ArrayIterator, ArrayAccess);
1954    REGISTER_SPL_IMPLEMENTS(ArrayIterator, SeekableIterator);
1955    REGISTER_SPL_IMPLEMENTS(ArrayIterator, Serializable);
1956    REGISTER_SPL_IMPLEMENTS(ArrayIterator, Countable);
1957    memcpy(&spl_handler_ArrayIterator, &spl_handler_ArrayObject, sizeof(zend_object_handlers));
1958    spl_ce_ArrayIterator->get_iterator = spl_array_get_iterator;
1959
1960    REGISTER_SPL_SUB_CLASS_EX(RecursiveArrayIterator, ArrayIterator, spl_array_object_new, spl_funcs_RecursiveArrayIterator);
1961    REGISTER_SPL_IMPLEMENTS(RecursiveArrayIterator, RecursiveIterator);
1962    spl_ce_RecursiveArrayIterator->get_iterator = spl_array_get_iterator;
1963
1964    REGISTER_SPL_CLASS_CONST_LONG(ArrayObject,   "STD_PROP_LIST",    SPL_ARRAY_STD_PROP_LIST);
1965    REGISTER_SPL_CLASS_CONST_LONG(ArrayObject,   "ARRAY_AS_PROPS",   SPL_ARRAY_ARRAY_AS_PROPS);
1966
1967    REGISTER_SPL_CLASS_CONST_LONG(ArrayIterator, "STD_PROP_LIST",    SPL_ARRAY_STD_PROP_LIST);
1968    REGISTER_SPL_CLASS_CONST_LONG(ArrayIterator, "ARRAY_AS_PROPS",   SPL_ARRAY_ARRAY_AS_PROPS);
1969
1970    REGISTER_SPL_CLASS_CONST_LONG(RecursiveArrayIterator, "CHILD_ARRAYS_ONLY", SPL_ARRAY_CHILD_ARRAYS_ONLY);
1971
1972    return SUCCESS;
1973}
1974/* }}} */
1975
1976/*
1977 * Local variables:
1978 * tab-width: 4
1979 * c-basic-offset: 4
1980 * End:
1981 * vim600: fdm=marker
1982 * vim: noet sw=4 ts=4
1983 */
1984