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