xref: /php-src/Zend/zend_property_hooks.c (revision 17d46bb3)
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 
39 // FIXME: This should probably be stored on zend_class_entry somewhere (e.g. through num_virtual_props).
zho_num_backed_props(zend_object * zobj)40 static uint32_t zho_num_backed_props(zend_object *zobj)
41 {
42 	zend_property_info *prop_info;
43 	int backed_property_count = 0;
44 	ZEND_HASH_MAP_FOREACH_PTR(&zobj->ce->properties_info, prop_info) {
45 		if (!(prop_info->flags & (ZEND_ACC_STATIC|ZEND_ACC_VIRTUAL))) {
46 			backed_property_count++;
47 		}
48 	} ZEND_HASH_FOREACH_END();
49 	return backed_property_count;
50 }
51 
zho_build_properties_ex(zend_object * zobj,bool check_access,bool include_dynamic_props)52 static zend_array *zho_build_properties_ex(zend_object *zobj, bool check_access, bool include_dynamic_props)
53 {
54 	if (UNEXPECTED(zend_lazy_object_must_init(zobj))) {
55 		zobj = zend_lazy_object_init(zobj);
56 		if (UNEXPECTED(!zobj)) {
57 			return zend_new_array(0);
58 		}
59 	}
60 
61 	zend_class_entry *ce = zobj->ce;
62 	zend_array *properties = zend_new_array(ce->default_properties_count);
63 	zend_hash_real_init_mixed(properties);
64 
65 	zend_property_info *prop_info;
66 	ZEND_HASH_MAP_FOREACH_PTR(&ce->properties_info, prop_info) {
67 		if (prop_info->flags & ZEND_ACC_STATIC) {
68 			continue;
69 		}
70 		if (check_access && zend_check_property_access(zobj, prop_info->name, false) == FAILURE) {
71 			continue;
72 		}
73 		if (prop_info->hooks) {
74 			_zend_hash_append_ptr(properties, prop_info->name, prop_info);
75 		} else {
76 			if (UNEXPECTED(Z_TYPE_P(OBJ_PROP(zobj, prop_info->offset)) == IS_UNDEF)) {
77 				HT_FLAGS(properties) |= HASH_FLAG_HAS_EMPTY_IND;
78 			}
79 			_zend_hash_append_ind(properties, prop_info->name, OBJ_PROP(zobj, prop_info->offset));
80 		}
81 	} ZEND_HASH_FOREACH_END();
82 
83 	if (include_dynamic_props && zobj->properties) {
84 		zend_string *prop_name;
85 		zval *prop_value;
86 		ZEND_HASH_FOREACH_STR_KEY_VAL_FROM(zobj->properties, prop_name, prop_value, zho_num_backed_props(zobj)) {
87 			Z_TRY_ADDREF_P(_zend_hash_append(properties, prop_name, prop_value));
88 		} ZEND_HASH_FOREACH_END();
89 	}
90 
91 	return properties;
92 }
93 
zend_hooked_object_build_properties(zend_object * zobj)94 ZEND_API zend_array *zend_hooked_object_build_properties(zend_object *zobj)
95 {
96 	return zho_build_properties_ex(zobj, false, true);
97 }
98 
zho_dynamic_it_init(zend_hooked_object_iterator * hooked_iter)99 static bool zho_dynamic_it_init(zend_hooked_object_iterator *hooked_iter)
100 {
101 	if (hooked_iter->dynamic_prop_it != (uint32_t) -1) {
102 		return true;
103 	}
104 
105 	zend_object *zobj = Z_OBJ_P(&hooked_iter->it.data);
106 	if (!zobj->properties || zho_num_backed_props(zobj) == zobj->properties->nNumUsed) {
107 		hooked_iter->dynamic_props_done = true;
108 		return false;
109 	}
110 
111 	hooked_iter->dynamic_prop_it = zend_hash_iterator_add(zobj->properties, zho_num_backed_props(zobj));
112 	return true;
113 }
114 
115 static void zho_it_get_current_key(zend_object_iterator *iter, zval *key);
116 
zho_declared_it_fetch_current(zend_object_iterator * iter)117 static void zho_declared_it_fetch_current(zend_object_iterator *iter)
118 {
119 	zend_hooked_object_iterator *hooked_iter = (zend_hooked_object_iterator*)iter;
120 	zend_object *zobj = Z_OBJ_P(&iter->data);
121 	zend_array *properties = Z_ARR(hooked_iter->declared_props);
122 
123 	zval *property = zend_hash_get_current_data(properties);
124 	if (Z_TYPE_P(property) == IS_PTR) {
125 		zend_property_info *prop_info = Z_PTR_P(property);
126 		zend_function *get = prop_info->hooks[ZEND_PROPERTY_HOOK_GET];
127 		if (!get && (prop_info->flags & ZEND_ACC_VIRTUAL)) {
128 			return;
129 		}
130 		if (hooked_iter->by_ref
131 		 && (get == NULL
132 		  || !(get->common.fn_flags & ZEND_ACC_RETURN_REFERENCE))) {
133 			zend_throw_error(NULL, "Cannot create reference to property %s::$%s",
134 				ZSTR_VAL(zobj->ce->name), zend_get_unmangled_property_name(prop_info->name));
135 			return;
136 		}
137 		zval *value = zend_read_property_ex(prop_info->ce, zobj, prop_info->name, /* silent */ true, &hooked_iter->current_data);
138 		if (value == &EG(uninitialized_zval)) {
139 			return;
140 		} else if (value != &hooked_iter->current_data) {
141 			ZVAL_COPY(&hooked_iter->current_data, value);
142 		}
143 	} else {
144 		ZVAL_DEINDIRECT(property);
145 		if (Z_TYPE_P(property) == IS_UNDEF) {
146 			return;
147 		}
148 		if (!hooked_iter->by_ref) {
149 			ZVAL_DEREF(property);
150 		} else if (Z_TYPE_P(property) != IS_REFERENCE) {
151 			ZVAL_MAKE_REF(property);
152 
153 			zend_property_info *prop_info = zend_get_property_info_for_slot(zobj, property);
154 			if (ZEND_TYPE_IS_SET(prop_info->type)) {
155 				ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(property), prop_info);
156 			}
157 		}
158 		ZVAL_COPY(&hooked_iter->current_data, property);
159 	}
160 	zend_hash_get_current_key_zval(properties, &hooked_iter->current_key);
161 }
162 
zho_dynamic_it_fetch_current(zend_object_iterator * iter)163 static void zho_dynamic_it_fetch_current(zend_object_iterator *iter)
164 {
165 	zend_hooked_object_iterator *hooked_iter = (zend_hooked_object_iterator*)iter;
166 	ZEND_ASSERT(hooked_iter->dynamic_prop_it != (uint32_t) -1);
167 
168 	zend_array *properties = Z_OBJ(iter->data)->properties;
169 	HashPosition pos = zend_hash_iterator_pos(hooked_iter->dynamic_prop_it, properties);
170 
171 	Bucket *bucket = properties->arData + pos;
172 	if (hooked_iter->by_ref && Z_TYPE(bucket->val) != IS_REFERENCE) {
173 		ZEND_ASSERT(Z_TYPE(bucket->val) != IS_UNDEF);
174 		ZVAL_MAKE_REF(&bucket->val);
175 	}
176 	ZVAL_COPY(&hooked_iter->current_data, &bucket->val);
177 
178 	if (bucket->key) {
179 		ZVAL_STR_COPY(&hooked_iter->current_key, bucket->key);
180 	} else {
181 		ZVAL_LONG(&hooked_iter->current_key, bucket->h);
182 	}
183 }
184 
zho_it_fetch_current(zend_object_iterator * iter)185 static void zho_it_fetch_current(zend_object_iterator *iter)
186 {
187 	zend_hooked_object_iterator *hooked_iter = (zend_hooked_object_iterator*)iter;
188 	if (Z_TYPE(hooked_iter->current_data) != IS_UNDEF) {
189 		return;
190 	}
191 
192 	while (true) {
193 		if (!hooked_iter->declared_props_done) {
194 			zho_declared_it_fetch_current(iter);
195 		} else if (!hooked_iter->dynamic_props_done && zho_dynamic_it_init(hooked_iter)) {
196 			zho_dynamic_it_fetch_current(iter);
197 		} else {
198 			break;
199 		}
200 		if (Z_TYPE(hooked_iter->current_data) != IS_UNDEF || EG(exception)) {
201 			break;
202 		}
203 		zho_it_move_forward(iter);
204 	}
205 }
206 
zho_it_dtor(zend_object_iterator * iter)207 static void zho_it_dtor(zend_object_iterator *iter)
208 {
209 	zend_hooked_object_iterator *hooked_iter = (zend_hooked_object_iterator*)iter;
210 	zval_ptr_dtor(&iter->data);
211 	zval_ptr_dtor(&hooked_iter->declared_props);
212 	zval_ptr_dtor_nogc(&hooked_iter->current_key);
213 	zval_ptr_dtor(&hooked_iter->current_data);
214 	if (hooked_iter->dynamic_prop_it != (uint32_t) -1) {
215 		zend_hash_iterator_del(hooked_iter->dynamic_prop_it);
216 	}
217 }
218 
zho_it_valid(zend_object_iterator * iter)219 static zend_result zho_it_valid(zend_object_iterator *iter)
220 {
221 	zend_hooked_object_iterator *hooked_iter = (zend_hooked_object_iterator*)iter;
222 	zho_it_fetch_current(iter);
223 	return Z_TYPE(hooked_iter->current_data) != IS_UNDEF ? SUCCESS : FAILURE;
224 }
225 
zho_it_get_current_data(zend_object_iterator * iter)226 static zval *zho_it_get_current_data(zend_object_iterator *iter)
227 {
228 	zend_hooked_object_iterator *hooked_iter = (zend_hooked_object_iterator*)iter;
229 	zho_it_fetch_current(iter);
230 	return &hooked_iter->current_data;
231 }
232 
zho_it_get_current_key(zend_object_iterator * iter,zval * key)233 static void zho_it_get_current_key(zend_object_iterator *iter, zval *key)
234 {
235 	zend_hooked_object_iterator *hooked_iter = (zend_hooked_object_iterator*)iter;
236 	zho_it_fetch_current(iter);
237 	ZVAL_COPY(key, &hooked_iter->current_key);
238 }
239 
zho_it_move_forward(zend_object_iterator * iter)240 static void zho_it_move_forward(zend_object_iterator *iter)
241 {
242 	zend_hooked_object_iterator *hooked_iter = (zend_hooked_object_iterator*)iter;
243 
244 	zval_ptr_dtor(&hooked_iter->current_data);
245 	ZVAL_UNDEF(&hooked_iter->current_data);
246 	zval_ptr_dtor_nogc(&hooked_iter->current_key);
247 	ZVAL_UNDEF(&hooked_iter->current_key);
248 
249 	if (!hooked_iter->declared_props_done) {
250 		zend_array *properties = Z_ARR(hooked_iter->declared_props);
251 		zend_hash_move_forward(properties);
252 		if (zend_hash_has_more_elements(properties) != SUCCESS) {
253 			hooked_iter->declared_props_done = true;
254 		}
255 	} else if (!hooked_iter->dynamic_props_done && zho_dynamic_it_init(hooked_iter)) {
256 		zend_array *properties = Z_OBJ(iter->data)->properties;
257 		HashPosition pos = zend_hash_iterator_pos(hooked_iter->dynamic_prop_it, properties);
258 		pos++;
259 		EG(ht_iterators)[hooked_iter->dynamic_prop_it].pos = pos;
260 		if (pos >= properties->nNumUsed) {
261 			hooked_iter->dynamic_props_done = true;
262 		}
263 	}
264 }
265 
zho_it_rewind(zend_object_iterator * iter)266 static void zho_it_rewind(zend_object_iterator *iter)
267 {
268 	zend_hooked_object_iterator *hooked_iter = (zend_hooked_object_iterator*)iter;
269 
270 	zval_ptr_dtor(&hooked_iter->current_data);
271 	ZVAL_UNDEF(&hooked_iter->current_data);
272 	zval_ptr_dtor_nogc(&hooked_iter->current_key);
273 	ZVAL_UNDEF(&hooked_iter->current_key);
274 
275 	hooked_iter->declared_props_done = false;
276 	zend_array *properties = Z_ARR(hooked_iter->declared_props);
277 	zend_hash_internal_pointer_reset(properties);
278 	hooked_iter->dynamic_props_done = false;
279 	if (hooked_iter->dynamic_prop_it != (uint32_t) -1) {
280 		EG(ht_iterators)[hooked_iter->dynamic_prop_it].pos = zho_num_backed_props(Z_OBJ(iter->data));
281 	}
282 }
283 
zho_it_get_gc(zend_object_iterator * iter,zval ** table,int * n)284 static HashTable *zho_it_get_gc(zend_object_iterator *iter, zval **table, int *n)
285 {
286 	zend_hooked_object_iterator *hooked_iter = (zend_hooked_object_iterator*)iter;
287 	zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create();
288 	zend_get_gc_buffer_add_zval(gc_buffer, &iter->data);
289 	zend_get_gc_buffer_add_zval(gc_buffer, &hooked_iter->declared_props);
290 	zend_get_gc_buffer_add_zval(gc_buffer, &hooked_iter->current_data);
291 	zend_get_gc_buffer_use(gc_buffer, table, n);
292 	return NULL;
293 }
294 
295 static const zend_object_iterator_funcs zend_hooked_object_it_funcs = {
296 	zho_it_dtor,
297 	zho_it_valid,
298 	zho_it_get_current_data,
299 	zho_it_get_current_key,
300 	zho_it_move_forward,
301 	zho_it_rewind,
302 	NULL,
303 	zho_it_get_gc,
304 };
305 
zend_hooked_object_get_iterator(zend_class_entry * ce,zval * object,int by_ref)306 ZEND_API zend_object_iterator *zend_hooked_object_get_iterator(zend_class_entry *ce, zval *object, int by_ref)
307 {
308 	zend_hooked_object_iterator *iterator = emalloc(sizeof(zend_hooked_object_iterator));
309 	zend_iterator_init(&iterator->it);
310 
311 	ZVAL_OBJ_COPY(&iterator->it.data, Z_OBJ_P(object));
312 	iterator->it.funcs = &zend_hooked_object_it_funcs;
313 	iterator->by_ref = by_ref;
314 	iterator->declared_props_done = false;
315 	zend_array *properties = zho_build_properties_ex(Z_OBJ_P(object), true, false);
316 	ZVAL_ARR(&iterator->declared_props, properties);
317 	iterator->dynamic_props_done = false;
318 	iterator->dynamic_prop_it = (uint32_t) -1;
319 	ZVAL_UNDEF(&iterator->current_key);
320 	ZVAL_UNDEF(&iterator->current_data);
321 
322 	return &iterator->it;
323 }
324