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