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