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