1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Author: Antony Dovgal <tony@daylessday.org> |
14 | Etienne Kneuss <colder@php.net> |
15 +----------------------------------------------------------------------+
16 */
17
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21
22 #include "php.h"
23 #include "php_ini.h"
24 #include "ext/standard/info.h"
25 #include "zend_exceptions.h"
26
27 #include "php_spl.h"
28 #include "spl_fixedarray_arginfo.h"
29 #include "spl_functions.h"
30 #include "spl_engine.h"
31 #include "spl_fixedarray.h"
32 #include "spl_exceptions.h"
33 #include "spl_iterators.h"
34 #include "ext/json/php_json.h"
35
36 zend_object_handlers spl_handler_SplFixedArray;
37 PHPAPI zend_class_entry *spl_ce_SplFixedArray;
38
39 #ifdef COMPILE_DL_SPL_FIXEDARRAY
40 ZEND_GET_MODULE(spl_fixedarray)
41 #endif
42
43 /* Check if the object is an instance of a subclass of SplFixedArray that overrides method's implementation.
44 * Expect subclassing SplFixedArray to be rare and check that first. */
45 #define HAS_FIXEDARRAY_ARRAYACCESS_OVERRIDE(object, method) UNEXPECTED((object)->ce != spl_ce_SplFixedArray && (object)->ce->arrayaccess_funcs_ptr->method->common.scope != spl_ce_SplFixedArray)
46
47 typedef struct _spl_fixedarray {
48 zend_long size;
49 /* It is possible to resize this, so this can't be combined with the object */
50 zval *elements;
51 /* If positive, it's a resize within a resize and the value gives the desired size. If -1, it's not. */
52 zend_long cached_resize;
53 } spl_fixedarray;
54
55 typedef struct _spl_fixedarray_object {
56 spl_fixedarray array;
57 zend_function *fptr_count;
58 zend_object std;
59 } spl_fixedarray_object;
60
61 typedef struct _spl_fixedarray_it {
62 zend_object_iterator intern;
63 zend_long current;
64 } spl_fixedarray_it;
65
spl_fixed_array_from_obj(zend_object * obj)66 static spl_fixedarray_object *spl_fixed_array_from_obj(zend_object *obj)
67 {
68 return (spl_fixedarray_object*)((char*)(obj) - XtOffsetOf(spl_fixedarray_object, std));
69 }
70
71 #define Z_SPLFIXEDARRAY_P(zv) spl_fixed_array_from_obj(Z_OBJ_P((zv)))
72
73 /* Helps enforce the invariants in debug mode:
74 * - if size == 0, then elements == NULL
75 * - if size > 0, then elements != NULL
76 * - size is not less than 0
77 */
spl_fixedarray_empty(spl_fixedarray * array)78 static bool spl_fixedarray_empty(spl_fixedarray *array)
79 {
80 if (array->elements) {
81 ZEND_ASSERT(array->size > 0);
82 return false;
83 }
84 ZEND_ASSERT(array->size == 0);
85 return true;
86 }
87
spl_fixedarray_default_ctor(spl_fixedarray * array)88 static void spl_fixedarray_default_ctor(spl_fixedarray *array)
89 {
90 array->size = 0;
91 array->elements = NULL;
92 array->cached_resize = -1;
93 }
94
95 /* Initializes the range [from, to) to null. Does not dtor existing elements. */
spl_fixedarray_init_elems(spl_fixedarray * array,zend_long from,zend_long to)96 static void spl_fixedarray_init_elems(spl_fixedarray *array, zend_long from, zend_long to)
97 {
98 ZEND_ASSERT(from <= to);
99 zval *begin = array->elements + from, *end = array->elements + to;
100
101 while (begin != end) {
102 ZVAL_NULL(begin++);
103 }
104 }
105
spl_fixedarray_init_non_empty_struct(spl_fixedarray * array,zend_long size)106 static void spl_fixedarray_init_non_empty_struct(spl_fixedarray *array, zend_long size)
107 {
108 array->size = 0; /* reset size in case ecalloc() fails */
109 array->elements = size ? safe_emalloc(size, sizeof(zval), 0) : NULL;
110 array->size = size;
111 array->cached_resize = -1;
112 }
113
spl_fixedarray_init(spl_fixedarray * array,zend_long size)114 static void spl_fixedarray_init(spl_fixedarray *array, zend_long size)
115 {
116 if (size > 0) {
117 spl_fixedarray_init_non_empty_struct(array, size);
118 spl_fixedarray_init_elems(array, 0, size);
119 } else {
120 spl_fixedarray_default_ctor(array);
121 }
122 }
123
124 /* Copies the range [begin, end) into the fixedarray, beginning at `offset`.
125 * Does not dtor the existing elements.
126 */
spl_fixedarray_copy_range(spl_fixedarray * array,zend_long offset,zval * begin,zval * end)127 static void spl_fixedarray_copy_range(spl_fixedarray *array, zend_long offset, zval *begin, zval *end)
128 {
129 ZEND_ASSERT(offset >= 0);
130 ZEND_ASSERT(array->size - offset >= end - begin);
131
132 zval *to = &array->elements[offset];
133 while (begin != end) {
134 ZVAL_COPY(to++, begin++);
135 }
136 }
137
spl_fixedarray_copy_ctor(spl_fixedarray * to,spl_fixedarray * from)138 static void spl_fixedarray_copy_ctor(spl_fixedarray *to, spl_fixedarray *from)
139 {
140 zend_long size = from->size;
141 spl_fixedarray_init(to, size);
142 if (size != 0) {
143 zval *begin = from->elements, *end = from->elements + size;
144 spl_fixedarray_copy_range(to, 0, begin, end);
145 }
146 }
147
148 /* Destructs the elements in the range [from, to).
149 * Caller is expected to bounds check.
150 */
spl_fixedarray_dtor_range(spl_fixedarray * array,zend_long from,zend_long to)151 static void spl_fixedarray_dtor_range(spl_fixedarray *array, zend_long from, zend_long to)
152 {
153 array->size = from;
154 zval *begin = array->elements + from, *end = array->elements + to;
155 while (begin != end) {
156 zval_ptr_dtor(begin++);
157 }
158 }
159
160 /* Destructs and frees contents but not the array itself.
161 * If you want to re-use the array then you need to re-initialize it.
162 */
spl_fixedarray_dtor(spl_fixedarray * array)163 static void spl_fixedarray_dtor(spl_fixedarray *array)
164 {
165 if (!spl_fixedarray_empty(array)) {
166 zval *begin = array->elements, *end = array->elements + array->size;
167 array->elements = NULL;
168 array->size = 0;
169 while (begin != end) {
170 zval_ptr_dtor(--end);
171 }
172 efree(begin);
173 }
174 }
175
spl_fixedarray_resize(spl_fixedarray * array,zend_long size)176 static void spl_fixedarray_resize(spl_fixedarray *array, zend_long size)
177 {
178 if (size == array->size) {
179 /* nothing to do */
180 return;
181 }
182
183 /* first initialization */
184 if (array->size == 0) {
185 spl_fixedarray_init(array, size);
186 return;
187 }
188
189 if (UNEXPECTED(array->cached_resize >= 0)) {
190 /* We're already resizing, so just remember the desired size.
191 * The resize will happen later. */
192 array->cached_resize = size;
193 return;
194 }
195 array->cached_resize = size;
196
197 /* clearing the array */
198 if (size == 0) {
199 spl_fixedarray_dtor(array);
200 array->elements = NULL;
201 array->size = 0;
202 } else if (size > array->size) {
203 array->elements = safe_erealloc(array->elements, size, sizeof(zval), 0);
204 spl_fixedarray_init_elems(array, array->size, size);
205 array->size = size;
206 } else { /* size < array->size */
207 /* Size set in spl_fixedarray_dtor_range() */
208 spl_fixedarray_dtor_range(array, size, array->size);
209 array->elements = erealloc(array->elements, sizeof(zval) * size);
210 }
211
212 /* If resized within the destructor, take the last resize command and perform it */
213 zend_long cached_resize = array->cached_resize;
214 array->cached_resize = -1;
215 if (cached_resize != size) {
216 spl_fixedarray_resize(array, cached_resize);
217 }
218 }
219
spl_fixedarray_object_get_gc(zend_object * obj,zval ** table,int * n)220 static HashTable* spl_fixedarray_object_get_gc(zend_object *obj, zval **table, int *n)
221 {
222 spl_fixedarray_object *intern = spl_fixed_array_from_obj(obj);
223 HashTable *ht = zend_std_get_properties(obj);
224
225 *table = intern->array.elements;
226 *n = (int)intern->array.size;
227
228 return ht;
229 }
230
spl_fixedarray_object_get_properties_for(zend_object * obj,zend_prop_purpose purpose)231 static HashTable* spl_fixedarray_object_get_properties_for(zend_object *obj, zend_prop_purpose purpose)
232 {
233 /* This has __serialize, so the purpose is not ZEND_PROP_PURPOSE_SERIALIZE, which would expect a non-null return value */
234 ZEND_ASSERT(purpose != ZEND_PROP_PURPOSE_SERIALIZE);
235
236 const spl_fixedarray_object *intern = spl_fixed_array_from_obj(obj);
237 /*
238 * SplFixedArray can be subclassed or have dynamic properties (With or without AllowDynamicProperties in subclasses).
239 * Instances of subclasses with declared properties may have properties but not yet have a property table.
240 */
241 HashTable *source_properties = obj->properties ? obj->properties : (obj->ce->default_properties_count ? zend_std_get_properties(obj) : NULL);
242
243 const zend_long size = intern->array.size;
244 if (size == 0 && (!source_properties || !zend_hash_num_elements(source_properties))) {
245 return NULL;
246 }
247 zval *const elements = intern->array.elements;
248 HashTable *ht = zend_new_array(size);
249
250 for (zend_long i = 0; i < size; i++) {
251 Z_TRY_ADDREF_P(&elements[i]);
252 zend_hash_next_index_insert(ht, &elements[i]);
253 }
254 if (source_properties && zend_hash_num_elements(source_properties) > 0) {
255 zend_long nkey;
256 zend_string *skey;
257 zval *value;
258 ZEND_HASH_MAP_FOREACH_KEY_VAL_IND(source_properties, nkey, skey, value) {
259 Z_TRY_ADDREF_P(value);
260 if (skey) {
261 zend_hash_add_new(ht, skey, value);
262 } else {
263 zend_hash_index_update(ht, nkey, value);
264 }
265 } ZEND_HASH_FOREACH_END();
266 }
267
268 return ht;
269 }
270
spl_fixedarray_object_free_storage(zend_object * object)271 static void spl_fixedarray_object_free_storage(zend_object *object)
272 {
273 spl_fixedarray_object *intern = spl_fixed_array_from_obj(object);
274 spl_fixedarray_dtor(&intern->array);
275 zend_object_std_dtor(&intern->std);
276 }
277
spl_fixedarray_object_new_ex(zend_class_entry * class_type,zend_object * orig,bool clone_orig)278 static zend_object *spl_fixedarray_object_new_ex(zend_class_entry *class_type, zend_object *orig, bool clone_orig)
279 {
280 spl_fixedarray_object *intern;
281 zend_class_entry *parent = class_type;
282 bool inherited = false;
283
284 intern = zend_object_alloc(sizeof(spl_fixedarray_object), parent);
285
286 zend_object_std_init(&intern->std, class_type);
287 object_properties_init(&intern->std, class_type);
288
289 if (orig && clone_orig) {
290 spl_fixedarray_object *other = spl_fixed_array_from_obj(orig);
291 spl_fixedarray_copy_ctor(&intern->array, &other->array);
292 }
293
294 while (parent) {
295 if (parent == spl_ce_SplFixedArray) {
296 break;
297 }
298
299 parent = parent->parent;
300 inherited = true;
301 }
302
303 ZEND_ASSERT(parent);
304
305 if (UNEXPECTED(inherited)) {
306 /* Find count() method */
307 zend_function *fptr_count = zend_hash_find_ptr(&class_type->function_table, ZSTR_KNOWN(ZEND_STR_COUNT));
308 if (fptr_count->common.scope == parent) {
309 fptr_count = NULL;
310 }
311 intern->fptr_count = fptr_count;
312 }
313
314 return &intern->std;
315 }
316
spl_fixedarray_new(zend_class_entry * class_type)317 static zend_object *spl_fixedarray_new(zend_class_entry *class_type)
318 {
319 return spl_fixedarray_object_new_ex(class_type, NULL, 0);
320 }
321
spl_fixedarray_object_clone(zend_object * old_object)322 static zend_object *spl_fixedarray_object_clone(zend_object *old_object)
323 {
324 zend_object *new_object = spl_fixedarray_object_new_ex(old_object->ce, old_object, 1);
325
326 zend_objects_clone_members(new_object, old_object);
327
328 return new_object;
329 }
330
spl_offset_convert_to_long(zval * offset)331 static zend_long spl_offset_convert_to_long(zval *offset) /* {{{ */
332 {
333 try_again:
334 switch (Z_TYPE_P(offset)) {
335 case IS_STRING: {
336 zend_ulong index;
337 if (ZEND_HANDLE_NUMERIC(Z_STR_P(offset), index)) {
338 return (zend_long) index;
339 }
340 break;
341 }
342 case IS_DOUBLE:
343 return zend_dval_to_lval_safe(Z_DVAL_P(offset));
344 case IS_LONG:
345 return Z_LVAL_P(offset);
346 case IS_FALSE:
347 return 0;
348 case IS_TRUE:
349 return 1;
350 case IS_REFERENCE:
351 offset = Z_REFVAL_P(offset);
352 goto try_again;
353 case IS_RESOURCE:
354 zend_use_resource_as_offset(offset);
355 return Z_RES_HANDLE_P(offset);
356 }
357
358 /* Use SplFixedArray name from the CE */
359 zend_illegal_container_offset(spl_ce_SplFixedArray->name, offset, BP_VAR_R);
360 return 0;
361 }
362
spl_fixedarray_object_read_dimension_helper(spl_fixedarray_object * intern,zval * offset)363 static zval *spl_fixedarray_object_read_dimension_helper(spl_fixedarray_object *intern, zval *offset)
364 {
365 zend_long index;
366
367 /* we have to return NULL on error here to avoid memleak because of
368 * ZE duplicating uninitialized_zval_ptr */
369 if (!offset) {
370 zend_throw_error(NULL, "[] operator not supported for SplFixedArray");
371 return NULL;
372 }
373
374 index = spl_offset_convert_to_long(offset);
375 if (EG(exception)) {
376 return NULL;
377 }
378
379 if (index < 0 || index >= intern->array.size) {
380 // TODO Change error message and use OutOfBound SPL Exception?
381 zend_throw_exception(spl_ce_RuntimeException, "Index invalid or out of range", 0);
382 return NULL;
383 } else {
384 return &intern->array.elements[index];
385 }
386 }
387
388 static int spl_fixedarray_object_has_dimension(zend_object *object, zval *offset, int check_empty);
389
spl_fixedarray_object_read_dimension(zend_object * object,zval * offset,int type,zval * rv)390 static zval *spl_fixedarray_object_read_dimension(zend_object *object, zval *offset, int type, zval *rv)
391 {
392 if (type == BP_VAR_IS && !spl_fixedarray_object_has_dimension(object, offset, 0)) {
393 return &EG(uninitialized_zval);
394 }
395
396 if (HAS_FIXEDARRAY_ARRAYACCESS_OVERRIDE(object, zf_offsetget)) {
397 zval tmp;
398 if (!offset) {
399 ZVAL_NULL(&tmp);
400 offset = &tmp;
401 }
402 zend_call_known_instance_method_with_1_params(object->ce->arrayaccess_funcs_ptr->zf_offsetget, object, rv, offset);
403 if (!Z_ISUNDEF_P(rv)) {
404 return rv;
405 }
406 return &EG(uninitialized_zval);
407 }
408
409 spl_fixedarray_object *intern = spl_fixed_array_from_obj(object);
410 return spl_fixedarray_object_read_dimension_helper(intern, offset);
411 }
412
spl_fixedarray_object_write_dimension_helper(spl_fixedarray_object * intern,zval * offset,zval * value)413 static void spl_fixedarray_object_write_dimension_helper(spl_fixedarray_object *intern, zval *offset, zval *value)
414 {
415 zend_long index;
416
417 if (!offset) {
418 /* '$array[] = value' syntax is not supported */
419 zend_throw_error(NULL, "[] operator not supported for SplFixedArray");
420 return;
421 }
422
423 index = spl_offset_convert_to_long(offset);
424 if (EG(exception)) {
425 return;
426 }
427
428 if (index < 0 || index >= intern->array.size) {
429 // TODO Change error message and use OutOfBound SPL Exception?
430 zend_throw_exception(spl_ce_RuntimeException, "Index invalid or out of range", 0);
431 return;
432 } else {
433 /* Fix #81429 */
434 zval *ptr = &(intern->array.elements[index]);
435 zval tmp;
436 ZVAL_COPY_VALUE(&tmp, ptr);
437 ZVAL_COPY_DEREF(ptr, value);
438 zval_ptr_dtor(&tmp);
439 }
440 }
441
spl_fixedarray_object_write_dimension(zend_object * object,zval * offset,zval * value)442 static void spl_fixedarray_object_write_dimension(zend_object *object, zval *offset, zval *value)
443 {
444 if (HAS_FIXEDARRAY_ARRAYACCESS_OVERRIDE(object, zf_offsetset)) {
445 zval tmp;
446
447 if (!offset) {
448 ZVAL_NULL(&tmp);
449 offset = &tmp;
450 }
451 zend_call_known_instance_method_with_2_params(object->ce->arrayaccess_funcs_ptr->zf_offsetset, object, NULL, offset, value);
452 return;
453 }
454
455 spl_fixedarray_object *intern = spl_fixed_array_from_obj(object);
456 spl_fixedarray_object_write_dimension_helper(intern, offset, value);
457 }
458
spl_fixedarray_object_unset_dimension_helper(spl_fixedarray_object * intern,zval * offset)459 static void spl_fixedarray_object_unset_dimension_helper(spl_fixedarray_object *intern, zval *offset)
460 {
461 zend_long index;
462
463 index = spl_offset_convert_to_long(offset);
464 if (EG(exception)) {
465 return;
466 }
467
468 if (index < 0 || index >= intern->array.size) {
469 // TODO Change error message and use OutOfBound SPL Exception?
470 zend_throw_exception(spl_ce_RuntimeException, "Index invalid or out of range", 0);
471 return;
472 } else {
473 zval_ptr_dtor(&(intern->array.elements[index]));
474 ZVAL_NULL(&intern->array.elements[index]);
475 }
476 }
477
spl_fixedarray_object_unset_dimension(zend_object * object,zval * offset)478 static void spl_fixedarray_object_unset_dimension(zend_object *object, zval *offset)
479 {
480 if (UNEXPECTED(HAS_FIXEDARRAY_ARRAYACCESS_OVERRIDE(object, zf_offsetunset))) {
481 zend_call_known_instance_method_with_1_params(object->ce->arrayaccess_funcs_ptr->zf_offsetunset, object, NULL, offset);
482 return;
483 }
484
485 spl_fixedarray_object *intern = spl_fixed_array_from_obj(object);
486 spl_fixedarray_object_unset_dimension_helper(intern, offset);
487 }
488
spl_fixedarray_object_has_dimension_helper(spl_fixedarray_object * intern,zval * offset,bool check_empty)489 static bool spl_fixedarray_object_has_dimension_helper(spl_fixedarray_object *intern, zval *offset, bool check_empty)
490 {
491 zend_long index;
492
493 index = spl_offset_convert_to_long(offset);
494 if (EG(exception)) {
495 return false;
496 }
497
498 if (index < 0 || index >= intern->array.size) {
499 return false;
500 }
501
502 if (check_empty) {
503 return zend_is_true(&intern->array.elements[index]);
504 }
505
506 return Z_TYPE(intern->array.elements[index]) != IS_NULL;
507 }
508
spl_fixedarray_object_has_dimension(zend_object * object,zval * offset,int check_empty)509 static int spl_fixedarray_object_has_dimension(zend_object *object, zval *offset, int check_empty)
510 {
511 if (HAS_FIXEDARRAY_ARRAYACCESS_OVERRIDE(object, zf_offsetexists)) {
512 zval rv;
513
514 zend_call_known_instance_method_with_1_params(object->ce->arrayaccess_funcs_ptr->zf_offsetexists, object, &rv, offset);
515 bool result = zend_is_true(&rv);
516 zval_ptr_dtor(&rv);
517 return result;
518 }
519
520 spl_fixedarray_object *intern = spl_fixed_array_from_obj(object);
521
522 return spl_fixedarray_object_has_dimension_helper(intern, offset, check_empty);
523 }
524
spl_fixedarray_object_count_elements(zend_object * object,zend_long * count)525 static zend_result spl_fixedarray_object_count_elements(zend_object *object, zend_long *count)
526 {
527 spl_fixedarray_object *intern;
528
529 intern = spl_fixed_array_from_obj(object);
530 if (UNEXPECTED(intern->fptr_count)) {
531 zval rv;
532 zend_call_known_instance_method_with_0_params(intern->fptr_count, object, &rv);
533 if (!Z_ISUNDEF(rv)) {
534 *count = zval_get_long(&rv);
535 zval_ptr_dtor(&rv);
536 } else {
537 *count = 0;
538 }
539 } else {
540 *count = intern->array.size;
541 }
542 return SUCCESS;
543 }
544
PHP_METHOD(SplFixedArray,__construct)545 PHP_METHOD(SplFixedArray, __construct)
546 {
547 zval *object = ZEND_THIS;
548 spl_fixedarray_object *intern;
549 zend_long size = 0;
550
551 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &size) == FAILURE) {
552 RETURN_THROWS();
553 }
554
555 if (size < 0) {
556 zend_argument_value_error(1, "must be greater than or equal to 0");
557 RETURN_THROWS();
558 }
559
560 intern = Z_SPLFIXEDARRAY_P(object);
561
562 if (!spl_fixedarray_empty(&intern->array)) {
563 /* called __construct() twice, bail out */
564 return;
565 }
566
567 spl_fixedarray_init(&intern->array, size);
568 }
569
PHP_METHOD(SplFixedArray,__wakeup)570 PHP_METHOD(SplFixedArray, __wakeup)
571 {
572 spl_fixedarray_object *intern = Z_SPLFIXEDARRAY_P(ZEND_THIS);
573 HashTable *intern_ht = zend_std_get_properties(Z_OBJ_P(ZEND_THIS));
574 zval *data;
575
576 if (zend_parse_parameters_none() == FAILURE) {
577 RETURN_THROWS();
578 }
579
580 if (intern->array.size == 0) {
581 int index = 0;
582 int size = zend_hash_num_elements(intern_ht);
583
584 spl_fixedarray_init(&intern->array, size);
585
586 ZEND_HASH_FOREACH_VAL(intern_ht, data) {
587 ZVAL_COPY(&intern->array.elements[index], data);
588 index++;
589 } ZEND_HASH_FOREACH_END();
590
591 /* Remove the unserialised properties, since we now have the elements
592 * within the spl_fixedarray_object structure. */
593 zend_hash_clean(intern_ht);
594 }
595 }
596
PHP_METHOD(SplFixedArray,__serialize)597 PHP_METHOD(SplFixedArray, __serialize)
598 {
599 spl_fixedarray_object *intern = Z_SPLFIXEDARRAY_P(ZEND_THIS);
600 zval *current;
601 zend_string *key;
602
603 if (zend_parse_parameters_none() == FAILURE) {
604 RETURN_THROWS();
605 }
606
607 HashTable *ht = zend_std_get_properties(&intern->std);
608 uint32_t num_properties = zend_hash_num_elements(ht);
609 array_init_size(return_value, intern->array.size + num_properties);
610
611 /* elements */
612 for (zend_long i = 0; i < intern->array.size; i++) {
613 current = &intern->array.elements[i];
614 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), current);
615 Z_TRY_ADDREF_P(current);
616 }
617
618 /* members */
619 ZEND_HASH_FOREACH_STR_KEY_VAL_IND(ht, key, current) {
620 /* If the properties table was already rebuild, it will also contain the
621 * array elements. The array elements are already added in the above loop.
622 * We can detect array elements by the fact that their key == NULL. */
623 if (key != NULL) {
624 zend_hash_add_new(Z_ARRVAL_P(return_value), key, current);
625 Z_TRY_ADDREF_P(current);
626 }
627 } ZEND_HASH_FOREACH_END();
628 }
629
PHP_METHOD(SplFixedArray,__unserialize)630 PHP_METHOD(SplFixedArray, __unserialize)
631 {
632 spl_fixedarray_object *intern = Z_SPLFIXEDARRAY_P(ZEND_THIS);
633 HashTable *data;
634 zval members_zv, *elem;
635 zend_string *key;
636 zend_long size;
637
638 if (zend_parse_parameters(ZEND_NUM_ARGS(), "h", &data) == FAILURE) {
639 RETURN_THROWS();
640 }
641
642 if (intern->array.size == 0) {
643 size = zend_hash_num_elements(data);
644 spl_fixedarray_init_non_empty_struct(&intern->array, size);
645 if (!size) {
646 return;
647 }
648 array_init(&members_zv);
649
650 intern->array.size = 0;
651 ZEND_HASH_FOREACH_STR_KEY_VAL(data, key, elem) {
652 if (key == NULL) {
653 ZVAL_COPY(&intern->array.elements[intern->array.size], elem);
654 intern->array.size++;
655 } else {
656 Z_TRY_ADDREF_P(elem);
657 zend_hash_add(Z_ARRVAL(members_zv), key, elem);
658 }
659 } ZEND_HASH_FOREACH_END();
660
661 if (intern->array.size != size) {
662 if (intern->array.size) {
663 intern->array.elements = erealloc(intern->array.elements, sizeof(zval) * intern->array.size);
664 } else {
665 efree(intern->array.elements);
666 intern->array.elements = NULL;
667 }
668 }
669
670 object_properties_load(&intern->std, Z_ARRVAL(members_zv));
671 zval_ptr_dtor(&members_zv);
672 }
673 }
674
PHP_METHOD(SplFixedArray,count)675 PHP_METHOD(SplFixedArray, count)
676 {
677 zval *object = ZEND_THIS;
678 spl_fixedarray_object *intern;
679
680 if (zend_parse_parameters_none() == FAILURE) {
681 RETURN_THROWS();
682 }
683
684 intern = Z_SPLFIXEDARRAY_P(object);
685 RETURN_LONG(intern->array.size);
686 }
687
PHP_METHOD(SplFixedArray,toArray)688 PHP_METHOD(SplFixedArray, toArray)
689 {
690 spl_fixedarray_object *intern;
691
692 if (zend_parse_parameters_none() == FAILURE) {
693 RETURN_THROWS();
694 }
695
696 intern = Z_SPLFIXEDARRAY_P(ZEND_THIS);
697
698 if (!spl_fixedarray_empty(&intern->array)) {
699 array_init(return_value);
700 for (zend_long i = 0; i < intern->array.size; i++) {
701 zend_hash_index_update(Z_ARRVAL_P(return_value), i, &intern->array.elements[i]);
702 Z_TRY_ADDREF(intern->array.elements[i]);
703 }
704 } else {
705 RETURN_EMPTY_ARRAY();
706 }
707 }
708
PHP_METHOD(SplFixedArray,fromArray)709 PHP_METHOD(SplFixedArray, fromArray)
710 {
711 zval *data;
712 spl_fixedarray array;
713 spl_fixedarray_object *intern;
714 int num;
715 bool save_indexes = 1;
716
717 if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|b", &data, &save_indexes) == FAILURE) {
718 RETURN_THROWS();
719 }
720
721 num = zend_hash_num_elements(Z_ARRVAL_P(data));
722
723 if (num > 0 && save_indexes) {
724 zval *element;
725 zend_string *str_index;
726 zend_ulong num_index, max_index = 0;
727 zend_long tmp;
728
729 ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(data), num_index, str_index) {
730 if (str_index != NULL || (zend_long)num_index < 0) {
731 zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0, "array must contain only positive integer keys");
732 RETURN_THROWS();
733 }
734
735 if (num_index > max_index) {
736 max_index = num_index;
737 }
738 } ZEND_HASH_FOREACH_END();
739
740 tmp = max_index + 1;
741 if (tmp <= 0) {
742 zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0, "integer overflow detected");
743 RETURN_THROWS();
744 }
745 spl_fixedarray_init(&array, tmp);
746
747 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(data), num_index, str_index, element) {
748 ZVAL_COPY_DEREF(&array.elements[num_index], element);
749 } ZEND_HASH_FOREACH_END();
750
751 } else if (num > 0 && !save_indexes) {
752 zval *element;
753 zend_long i = 0;
754
755 spl_fixedarray_init(&array, num);
756
757 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(data), element) {
758 ZVAL_COPY_DEREF(&array.elements[i], element);
759 i++;
760 } ZEND_HASH_FOREACH_END();
761 } else {
762 spl_fixedarray_init(&array, 0);
763 }
764
765 object_init_ex(return_value, spl_ce_SplFixedArray);
766
767 intern = Z_SPLFIXEDARRAY_P(return_value);
768 intern->array = array;
769 }
770
PHP_METHOD(SplFixedArray,getSize)771 PHP_METHOD(SplFixedArray, getSize)
772 {
773 zval *object = ZEND_THIS;
774 spl_fixedarray_object *intern;
775
776 if (zend_parse_parameters_none() == FAILURE) {
777 RETURN_THROWS();
778 }
779
780 intern = Z_SPLFIXEDARRAY_P(object);
781 RETURN_LONG(intern->array.size);
782 }
783
PHP_METHOD(SplFixedArray,setSize)784 PHP_METHOD(SplFixedArray, setSize)
785 {
786 zval *object = ZEND_THIS;
787 spl_fixedarray_object *intern;
788 zend_long size;
789
790 if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &size) == FAILURE) {
791 RETURN_THROWS();
792 }
793
794 if (size < 0) {
795 zend_argument_value_error(1, "must be greater than or equal to 0");
796 RETURN_THROWS();
797 }
798
799 intern = Z_SPLFIXEDARRAY_P(object);
800
801 spl_fixedarray_resize(&intern->array, size);
802 RETURN_TRUE;
803 }
804
805 /* Returns whether the requested $index exists. */
PHP_METHOD(SplFixedArray,offsetExists)806 PHP_METHOD(SplFixedArray, offsetExists)
807 {
808 zval *zindex;
809 spl_fixedarray_object *intern;
810
811 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zindex) == FAILURE) {
812 RETURN_THROWS();
813 }
814
815 intern = Z_SPLFIXEDARRAY_P(ZEND_THIS);
816
817 RETURN_BOOL(spl_fixedarray_object_has_dimension_helper(intern, zindex, 0));
818 }
819
820 /* Returns the value at the specified $index. */
PHP_METHOD(SplFixedArray,offsetGet)821 PHP_METHOD(SplFixedArray, offsetGet)
822 {
823 zval *zindex, *value;
824 spl_fixedarray_object *intern;
825
826 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zindex) == FAILURE) {
827 RETURN_THROWS();
828 }
829
830 intern = Z_SPLFIXEDARRAY_P(ZEND_THIS);
831 value = spl_fixedarray_object_read_dimension_helper(intern, zindex);
832
833 if (value) {
834 RETURN_COPY_DEREF(value);
835 } else {
836 RETURN_NULL();
837 }
838 }
839
840 /* Sets the value at the specified $index to $newval. */
PHP_METHOD(SplFixedArray,offsetSet)841 PHP_METHOD(SplFixedArray, offsetSet)
842 {
843 zval *zindex, *value;
844 spl_fixedarray_object *intern;
845
846 if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &zindex, &value) == FAILURE) {
847 RETURN_THROWS();
848 }
849
850 intern = Z_SPLFIXEDARRAY_P(ZEND_THIS);
851 spl_fixedarray_object_write_dimension_helper(intern, zindex, value);
852
853 }
854
855 /* Unsets the value at the specified $index. */
PHP_METHOD(SplFixedArray,offsetUnset)856 PHP_METHOD(SplFixedArray, offsetUnset)
857 {
858 zval *zindex;
859 spl_fixedarray_object *intern;
860
861 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zindex) == FAILURE) {
862 RETURN_THROWS();
863 }
864
865 intern = Z_SPLFIXEDARRAY_P(ZEND_THIS);
866 spl_fixedarray_object_unset_dimension_helper(intern, zindex);
867
868 }
869
870 /* Create a new iterator from a SplFixedArray instance. */
PHP_METHOD(SplFixedArray,getIterator)871 PHP_METHOD(SplFixedArray, getIterator)
872 {
873 if (zend_parse_parameters_none() == FAILURE) {
874 RETURN_THROWS();
875 }
876
877 zend_create_internal_iterator_zval(return_value, ZEND_THIS);
878 }
879
PHP_METHOD(SplFixedArray,jsonSerialize)880 PHP_METHOD(SplFixedArray, jsonSerialize)
881 {
882 ZEND_PARSE_PARAMETERS_NONE();
883
884 spl_fixedarray_object *intern = Z_SPLFIXEDARRAY_P(ZEND_THIS);
885 array_init_size(return_value, intern->array.size);
886 for (zend_long i = 0; i < intern->array.size; i++) {
887 zend_hash_next_index_insert_new(Z_ARR_P(return_value), &intern->array.elements[i]);
888 Z_TRY_ADDREF(intern->array.elements[i]);
889 }
890 }
891
spl_fixedarray_it_dtor(zend_object_iterator * iter)892 static void spl_fixedarray_it_dtor(zend_object_iterator *iter)
893 {
894 zval_ptr_dtor(&iter->data);
895 }
896
spl_fixedarray_it_rewind(zend_object_iterator * iter)897 static void spl_fixedarray_it_rewind(zend_object_iterator *iter)
898 {
899 ((spl_fixedarray_it*)iter)->current = 0;
900 }
901
spl_fixedarray_it_valid(zend_object_iterator * iter)902 static int spl_fixedarray_it_valid(zend_object_iterator *iter)
903 {
904 spl_fixedarray_it *iterator = (spl_fixedarray_it*)iter;
905 spl_fixedarray_object *object = Z_SPLFIXEDARRAY_P(&iter->data);
906
907 if (iterator->current >= 0 && iterator->current < object->array.size) {
908 return SUCCESS;
909 }
910
911 return FAILURE;
912 }
913
spl_fixedarray_it_get_current_data(zend_object_iterator * iter)914 static zval *spl_fixedarray_it_get_current_data(zend_object_iterator *iter)
915 {
916 zval zindex, *data;
917 spl_fixedarray_it *iterator = (spl_fixedarray_it*)iter;
918 spl_fixedarray_object *object = Z_SPLFIXEDARRAY_P(&iter->data);
919
920 ZVAL_LONG(&zindex, iterator->current);
921 data = spl_fixedarray_object_read_dimension_helper(object, &zindex);
922
923 if (data == NULL) {
924 data = &EG(uninitialized_zval);
925 }
926 return data;
927 }
928
spl_fixedarray_it_get_current_key(zend_object_iterator * iter,zval * key)929 static void spl_fixedarray_it_get_current_key(zend_object_iterator *iter, zval *key)
930 {
931 ZVAL_LONG(key, ((spl_fixedarray_it*)iter)->current);
932 }
933
spl_fixedarray_it_move_forward(zend_object_iterator * iter)934 static void spl_fixedarray_it_move_forward(zend_object_iterator *iter)
935 {
936 ((spl_fixedarray_it*)iter)->current++;
937 }
938
939 /* iterator handler table */
940 static const zend_object_iterator_funcs spl_fixedarray_it_funcs = {
941 spl_fixedarray_it_dtor,
942 spl_fixedarray_it_valid,
943 spl_fixedarray_it_get_current_data,
944 spl_fixedarray_it_get_current_key,
945 spl_fixedarray_it_move_forward,
946 spl_fixedarray_it_rewind,
947 NULL,
948 NULL, /* get_gc */
949 };
950
spl_fixedarray_get_iterator(zend_class_entry * ce,zval * object,int by_ref)951 static zend_object_iterator *spl_fixedarray_get_iterator(zend_class_entry *ce, zval *object, int by_ref)
952 {
953 spl_fixedarray_it *iterator;
954
955 if (by_ref) {
956 zend_throw_error(NULL, "An iterator cannot be used with foreach by reference");
957 return NULL;
958 }
959
960 iterator = emalloc(sizeof(spl_fixedarray_it));
961
962 zend_iterator_init((zend_object_iterator*)iterator);
963
964 ZVAL_OBJ_COPY(&iterator->intern.data, Z_OBJ_P(object));
965 iterator->intern.funcs = &spl_fixedarray_it_funcs;
966
967 return &iterator->intern;
968 }
969
PHP_MINIT_FUNCTION(spl_fixedarray)970 PHP_MINIT_FUNCTION(spl_fixedarray)
971 {
972 spl_ce_SplFixedArray = register_class_SplFixedArray(
973 zend_ce_aggregate, zend_ce_arrayaccess, zend_ce_countable, php_json_serializable_ce);
974 spl_ce_SplFixedArray->create_object = spl_fixedarray_new;
975 spl_ce_SplFixedArray->default_object_handlers = &spl_handler_SplFixedArray;
976 spl_ce_SplFixedArray->get_iterator = spl_fixedarray_get_iterator;
977
978 memcpy(&spl_handler_SplFixedArray, &std_object_handlers, sizeof(zend_object_handlers));
979
980 spl_handler_SplFixedArray.offset = XtOffsetOf(spl_fixedarray_object, std);
981 spl_handler_SplFixedArray.clone_obj = spl_fixedarray_object_clone;
982 spl_handler_SplFixedArray.read_dimension = spl_fixedarray_object_read_dimension;
983 spl_handler_SplFixedArray.write_dimension = spl_fixedarray_object_write_dimension;
984 spl_handler_SplFixedArray.unset_dimension = spl_fixedarray_object_unset_dimension;
985 spl_handler_SplFixedArray.has_dimension = spl_fixedarray_object_has_dimension;
986 spl_handler_SplFixedArray.count_elements = spl_fixedarray_object_count_elements;
987 spl_handler_SplFixedArray.get_properties_for = spl_fixedarray_object_get_properties_for;
988 spl_handler_SplFixedArray.get_gc = spl_fixedarray_object_get_gc;
989 spl_handler_SplFixedArray.free_obj = spl_fixedarray_object_free_storage;
990
991 return SUCCESS;
992 }
993