1 /*
2 +----------------------------------------------------------------------+
3 | Zend Engine |
4 +----------------------------------------------------------------------+
5 | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 2.00 of the Zend license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.zend.com/license/2_00.txt. |
11 | If you did not receive a copy of the Zend license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@zend.com so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Ilija Tovilo <ilutov@php.net> |
16 +----------------------------------------------------------------------+
17 */
18
19 #include "zend.h"
20 #include "zend_API.h"
21 #include "zend_hash.h"
22 #include "zend_lazy_objects.h"
23 #include "zend_property_hooks.h"
24
25 typedef struct {
26 zend_object_iterator it;
27 bool by_ref;
28 bool declared_props_done;
29 zval declared_props;
30 bool dynamic_props_done;
31 uint32_t dynamic_prop_it;
32 zval current_key;
33 zval current_data;
34 } zend_hooked_object_iterator;
35
36 static zend_result zho_it_valid(zend_object_iterator *iter);
37 static void zho_it_move_forward(zend_object_iterator *iter);
38
zho_num_backed_props(zend_object * zobj)39 static uint32_t zho_num_backed_props(zend_object *zobj)
40 {
41 return zobj->ce->default_properties_count;
42 }
43
zho_build_properties_ex(zend_object * zobj,bool check_access,bool force_ptr,bool include_dynamic_props)44 static zend_array *zho_build_properties_ex(zend_object *zobj, bool check_access, bool force_ptr, bool include_dynamic_props)
45 {
46 zend_class_entry *ce = zobj->ce;
47 zend_array *properties = zend_new_array(ce->default_properties_count);
48 zend_hash_real_init_mixed(properties);
49
50 /* Build list of parents */
51 int32_t parent_count = 0;
52 for (zend_class_entry *pce = ce; pce; pce = pce->parent) {
53 parent_count++;
54 }
55 zend_class_entry **parents = emalloc(sizeof(zend_class_entry*) * parent_count);
56 int32_t i = 0;
57 for (zend_class_entry *pce = ce; pce; pce = pce->parent) {
58 parents[i++] = pce;
59 }
60
61 /* Iterate parents top to bottom */
62 i--;
63 for (; i >= 0; i--) {
64 zend_class_entry *pce = parents[i];
65
66 zend_property_info *prop_info;
67 ZEND_HASH_MAP_FOREACH_PTR(&pce->properties_info, prop_info) {
68 if (prop_info->flags & ZEND_ACC_STATIC) {
69 continue;
70 }
71 zend_string *property_name = prop_info->name;
72 /* When promoting properties from protected to public, use the unmangled name to preserve order. */
73 if (prop_info->flags & ZEND_ACC_PROTECTED) {
74 const char *tmp = zend_get_unmangled_property_name(property_name);
75 zend_string *unmangled_name = zend_string_init(tmp, strlen(tmp), false);
76 zend_property_info *child_prop_info = zend_hash_find_ptr(&ce->properties_info, unmangled_name);
77 if (child_prop_info && (child_prop_info->flags & ZEND_ACC_PUBLIC)) {
78 property_name = unmangled_name;
79 } else {
80 zend_string_release(unmangled_name);
81 }
82 }
83 if (check_access && zend_check_property_access(zobj, property_name, false) == FAILURE) {
84 goto skip_property;
85 }
86 if (prop_info->hooks || force_ptr) {
87 zend_hash_update_ptr(properties, property_name, prop_info);
88 } else {
89 if (UNEXPECTED(Z_TYPE_P(OBJ_PROP(zobj, prop_info->offset)) == IS_UNDEF)) {
90 HT_FLAGS(properties) |= HASH_FLAG_HAS_EMPTY_IND;
91 }
92 zend_hash_update_ind(properties, property_name, OBJ_PROP(zobj, prop_info->offset));
93 }
94 skip_property:
95 if (property_name != prop_info->name) {
96 zend_string_release(property_name);
97 }
98 } ZEND_HASH_FOREACH_END();
99 }
100
101 efree(parents);
102
103 if (include_dynamic_props && zobj->properties) {
104 zend_string *prop_name;
105 zval *prop_value;
106 ZEND_HASH_FOREACH_STR_KEY_VAL_FROM(zobj->properties, prop_name, prop_value, zho_num_backed_props(zobj)) {
107 Z_TRY_ADDREF_P(_zend_hash_append(properties, prop_name, prop_value));
108 } ZEND_HASH_FOREACH_END();
109 }
110
111 return properties;
112 }
113
zend_hooked_object_build_properties(zend_object * zobj)114 ZEND_API zend_array *zend_hooked_object_build_properties(zend_object *zobj)
115 {
116 if (UNEXPECTED(zend_lazy_object_must_init(zobj))) {
117 zobj = zend_lazy_object_init(zobj);
118 if (UNEXPECTED(!zobj)) {
119 return (zend_array*) &zend_empty_array;
120 }
121 }
122
123 return zho_build_properties_ex(zobj, false, false, true);
124 }
125
zho_dynamic_it_init(zend_hooked_object_iterator * hooked_iter)126 static void zho_dynamic_it_init(zend_hooked_object_iterator *hooked_iter)
127 {
128 zend_object *zobj = Z_OBJ_P(&hooked_iter->it.data);
129 zend_array *properties = zobj->handlers->get_properties(zobj);
130 hooked_iter->dynamic_props_done = false;
131 hooked_iter->dynamic_prop_it = zend_hash_iterator_add(properties, zho_num_backed_props(zobj));
132 }
133
134 static void zho_it_get_current_key(zend_object_iterator *iter, zval *key);
135
zho_declared_it_fetch_current(zend_object_iterator * iter)136 static void zho_declared_it_fetch_current(zend_object_iterator *iter)
137 {
138 zend_hooked_object_iterator *hooked_iter = (zend_hooked_object_iterator*)iter;
139 zend_object *zobj = Z_OBJ_P(&iter->data);
140 zend_array *properties = Z_ARR(hooked_iter->declared_props);
141
142 zend_property_info *prop_info = Z_PTR_P(zend_hash_get_current_data(properties));
143 if (prop_info->hooks) {
144 zend_function *get = prop_info->hooks[ZEND_PROPERTY_HOOK_GET];
145 if (!get && (prop_info->flags & ZEND_ACC_VIRTUAL)) {
146 return;
147 }
148 if (hooked_iter->by_ref
149 && (get == NULL
150 || !(get->common.fn_flags & ZEND_ACC_RETURN_REFERENCE))) {
151 zend_throw_error(NULL, "Cannot create reference to property %s::$%s",
152 ZSTR_VAL(zobj->ce->name), zend_get_unmangled_property_name(prop_info->name));
153 return;
154 }
155 zend_string *unmangled_name = prop_info->name;
156 if (ZSTR_VAL(unmangled_name)[0] == '\0') {
157 const char *tmp = zend_get_unmangled_property_name(unmangled_name);
158 unmangled_name = zend_string_init(tmp, strlen(tmp), false);
159 }
160 zval *value = zend_read_property_ex(prop_info->ce, zobj, unmangled_name, /* silent */ true, &hooked_iter->current_data);
161 if (unmangled_name != prop_info->name) {
162 zend_string_release(unmangled_name);
163 }
164 if (value == &EG(uninitialized_zval)) {
165 return;
166 } else if (value != &hooked_iter->current_data) {
167 ZVAL_COPY(&hooked_iter->current_data, value);
168 }
169 } else {
170 zval *property = OBJ_PROP(zobj, prop_info->offset);
171 ZVAL_DEINDIRECT(property);
172 if (Z_TYPE_P(property) == IS_UNDEF) {
173 return;
174 }
175 if (!hooked_iter->by_ref) {
176 ZVAL_DEREF(property);
177 } else if (Z_TYPE_P(property) != IS_REFERENCE) {
178 if (UNEXPECTED(prop_info->flags & ZEND_ACC_READONLY)) {
179 zend_throw_error(NULL,
180 "Cannot acquire reference to readonly property %s::$%s",
181 ZSTR_VAL(prop_info->ce->name), zend_get_unmangled_property_name(prop_info->name));
182 return;
183 }
184 ZVAL_MAKE_REF(property);
185 if (ZEND_TYPE_IS_SET(prop_info->type)) {
186 ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(property), prop_info);
187 }
188 }
189 ZVAL_COPY(&hooked_iter->current_data, property);
190 }
191
192 if (ZSTR_VAL(prop_info->name)[0] == '\0') {
193 const char *tmp = zend_get_unmangled_property_name(prop_info->name);
194 ZVAL_STR(&hooked_iter->current_key, zend_string_init(tmp, strlen(tmp), false));
195 } else {
196 ZVAL_STR_COPY(&hooked_iter->current_key, prop_info->name);
197 }
198 }
199
zho_dynamic_it_fetch_current(zend_object_iterator * iter)200 static void zho_dynamic_it_fetch_current(zend_object_iterator *iter)
201 {
202 zend_hooked_object_iterator *hooked_iter = (zend_hooked_object_iterator*)iter;
203 zend_array *properties = Z_OBJ(iter->data)->properties;
204 HashPosition pos = zend_hash_iterator_pos(hooked_iter->dynamic_prop_it, properties);
205
206 if (pos >= properties->nNumUsed) {
207 hooked_iter->dynamic_props_done = true;
208 return;
209 }
210
211 Bucket *bucket = properties->arData + pos;
212
213 if (UNEXPECTED(Z_TYPE(bucket->val) == IS_UNDEF)) {
214 return;
215 }
216
217 zend_object *zobj = Z_OBJ_P(&hooked_iter->it.data);
218 if (bucket->key && zend_check_property_access(zobj, bucket->key, true) != SUCCESS) {
219 return;
220 }
221
222 if (hooked_iter->by_ref && Z_TYPE(bucket->val) != IS_REFERENCE) {
223 ZVAL_MAKE_REF(&bucket->val);
224 }
225 ZVAL_COPY(&hooked_iter->current_data, &bucket->val);
226
227 if (bucket->key) {
228 ZVAL_STR_COPY(&hooked_iter->current_key, bucket->key);
229 } else {
230 ZVAL_LONG(&hooked_iter->current_key, bucket->h);
231 }
232 }
233
zho_it_fetch_current(zend_object_iterator * iter)234 static void zho_it_fetch_current(zend_object_iterator *iter)
235 {
236 zend_hooked_object_iterator *hooked_iter = (zend_hooked_object_iterator*)iter;
237 if (Z_TYPE(hooked_iter->current_data) != IS_UNDEF) {
238 return;
239 }
240
241 while (true) {
242 if (!hooked_iter->declared_props_done) {
243 zho_declared_it_fetch_current(iter);
244 } else if (!hooked_iter->dynamic_props_done) {
245 zho_dynamic_it_fetch_current(iter);
246 } else {
247 break;
248 }
249 if (Z_TYPE(hooked_iter->current_data) != IS_UNDEF || EG(exception)) {
250 break;
251 }
252 zho_it_move_forward(iter);
253 }
254 }
255
zho_it_dtor(zend_object_iterator * iter)256 static void zho_it_dtor(zend_object_iterator *iter)
257 {
258 zend_hooked_object_iterator *hooked_iter = (zend_hooked_object_iterator*)iter;
259 zval_ptr_dtor(&iter->data);
260 zval_ptr_dtor(&hooked_iter->declared_props);
261 zval_ptr_dtor_nogc(&hooked_iter->current_key);
262 zval_ptr_dtor(&hooked_iter->current_data);
263 zend_hash_iterator_del(hooked_iter->dynamic_prop_it);
264 }
265
zho_it_valid(zend_object_iterator * iter)266 static zend_result zho_it_valid(zend_object_iterator *iter)
267 {
268 zend_hooked_object_iterator *hooked_iter = (zend_hooked_object_iterator*)iter;
269 zho_it_fetch_current(iter);
270 return Z_TYPE(hooked_iter->current_data) != IS_UNDEF ? SUCCESS : FAILURE;
271 }
272
zho_it_get_current_data(zend_object_iterator * iter)273 static zval *zho_it_get_current_data(zend_object_iterator *iter)
274 {
275 zend_hooked_object_iterator *hooked_iter = (zend_hooked_object_iterator*)iter;
276 zho_it_fetch_current(iter);
277 return &hooked_iter->current_data;
278 }
279
zho_it_get_current_key(zend_object_iterator * iter,zval * key)280 static void zho_it_get_current_key(zend_object_iterator *iter, zval *key)
281 {
282 zend_hooked_object_iterator *hooked_iter = (zend_hooked_object_iterator*)iter;
283 zho_it_fetch_current(iter);
284 ZVAL_COPY(key, &hooked_iter->current_key);
285 }
286
zho_it_move_forward(zend_object_iterator * iter)287 static void zho_it_move_forward(zend_object_iterator *iter)
288 {
289 zend_hooked_object_iterator *hooked_iter = (zend_hooked_object_iterator*)iter;
290
291 zval_ptr_dtor(&hooked_iter->current_data);
292 ZVAL_UNDEF(&hooked_iter->current_data);
293 zval_ptr_dtor_nogc(&hooked_iter->current_key);
294 ZVAL_UNDEF(&hooked_iter->current_key);
295
296 if (!hooked_iter->declared_props_done) {
297 zend_array *properties = Z_ARR(hooked_iter->declared_props);
298 zend_hash_move_forward(properties);
299 if (zend_hash_has_more_elements(properties) != SUCCESS) {
300 hooked_iter->declared_props_done = true;
301 }
302 } else if (!hooked_iter->dynamic_props_done) {
303 zend_array *properties = Z_OBJ(iter->data)->properties;
304 HashPosition pos = zend_hash_iterator_pos(hooked_iter->dynamic_prop_it, properties);
305 pos++;
306 EG(ht_iterators)[hooked_iter->dynamic_prop_it].pos = pos;
307 }
308 }
309
zho_it_rewind(zend_object_iterator * iter)310 static void zho_it_rewind(zend_object_iterator *iter)
311 {
312 zend_hooked_object_iterator *hooked_iter = (zend_hooked_object_iterator*)iter;
313
314 zval_ptr_dtor(&hooked_iter->current_data);
315 ZVAL_UNDEF(&hooked_iter->current_data);
316 zval_ptr_dtor_nogc(&hooked_iter->current_key);
317 ZVAL_UNDEF(&hooked_iter->current_key);
318
319 zend_array *properties = Z_ARR(hooked_iter->declared_props);
320 zend_hash_internal_pointer_reset(properties);
321 hooked_iter->declared_props_done = !zend_hash_num_elements(properties);
322 hooked_iter->dynamic_props_done = false;
323 EG(ht_iterators)[hooked_iter->dynamic_prop_it].pos = zho_num_backed_props(Z_OBJ(iter->data));
324 }
325
zho_it_get_gc(zend_object_iterator * iter,zval ** table,int * n)326 static HashTable *zho_it_get_gc(zend_object_iterator *iter, zval **table, int *n)
327 {
328 zend_hooked_object_iterator *hooked_iter = (zend_hooked_object_iterator*)iter;
329 zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create();
330 zend_get_gc_buffer_add_zval(gc_buffer, &iter->data);
331 zend_get_gc_buffer_add_zval(gc_buffer, &hooked_iter->declared_props);
332 zend_get_gc_buffer_add_zval(gc_buffer, &hooked_iter->current_data);
333 zend_get_gc_buffer_use(gc_buffer, table, n);
334 return NULL;
335 }
336
337 static const zend_object_iterator_funcs zend_hooked_object_it_funcs = {
338 zho_it_dtor,
339 zho_it_valid,
340 zho_it_get_current_data,
341 zho_it_get_current_key,
342 zho_it_move_forward,
343 zho_it_rewind,
344 NULL,
345 zho_it_get_gc,
346 };
347
zend_hooked_object_get_iterator(zend_class_entry * ce,zval * object,int by_ref)348 ZEND_API zend_object_iterator *zend_hooked_object_get_iterator(zend_class_entry *ce, zval *object, int by_ref)
349 {
350 zend_object *zobj = Z_OBJ_P(object);
351 if (UNEXPECTED(zend_lazy_object_must_init(zobj))) {
352 zobj = zend_lazy_object_init(zobj);
353 if (UNEXPECTED(!zobj)) {
354 return NULL;
355 }
356 }
357
358 zend_hooked_object_iterator *iterator = emalloc(sizeof(zend_hooked_object_iterator));
359 zend_iterator_init(&iterator->it);
360
361 ZVAL_OBJ_COPY(&iterator->it.data, zobj);
362 iterator->it.funcs = &zend_hooked_object_it_funcs;
363 iterator->by_ref = by_ref;
364 zend_array *properties = zho_build_properties_ex(zobj, true, true, false);
365 ZVAL_ARR(&iterator->declared_props, properties);
366 iterator->declared_props_done = !zend_hash_num_elements(properties);
367 zho_dynamic_it_init(iterator);
368 ZVAL_UNDEF(&iterator->current_key);
369 ZVAL_UNDEF(&iterator->current_data);
370
371 return &iterator->it;
372 }
373