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