xref: /PHP-7.2/Zend/zend_object_handlers.c (revision 358379be)
1 /*
2    +----------------------------------------------------------------------+
3    | Zend Engine                                                          |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1998-2018 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: Andi Gutmans <andi@zend.com>                                |
16    |          Zeev Suraski <zeev@zend.com>                                |
17    |          Dmitry Stogov <dmitry@zend.com>                             |
18    +----------------------------------------------------------------------+
19 */
20 
21 /* $Id$ */
22 
23 #include "zend.h"
24 #include "zend_globals.h"
25 #include "zend_variables.h"
26 #include "zend_API.h"
27 #include "zend_objects.h"
28 #include "zend_objects_API.h"
29 #include "zend_object_handlers.h"
30 #include "zend_interfaces.h"
31 #include "zend_exceptions.h"
32 #include "zend_closures.h"
33 #include "zend_compile.h"
34 #include "zend_hash.h"
35 
36 #define DEBUG_OBJECT_HANDLERS 0
37 
38 /* guard flags */
39 #define IN_GET		(1<<0)
40 #define IN_SET		(1<<1)
41 #define IN_UNSET	(1<<2)
42 #define IN_ISSET	(1<<3)
43 
44 #define Z_OBJ_PROTECT_RECURSION(zval_p) \
45 	do { \
46 		if (Z_OBJ_APPLY_COUNT_P(zval_p) >= 3) { \
47 			zend_error_noreturn(E_ERROR, "Nesting level too deep - recursive dependency?"); \
48 		} \
49 		Z_OBJ_INC_APPLY_COUNT_P(zval_p); \
50 	} while (0)
51 
52 
53 #define Z_OBJ_UNPROTECT_RECURSION(zval_p) \
54 	Z_OBJ_DEC_APPLY_COUNT_P(zval_p)
55 
56 /*
57   __X accessors explanation:
58 
59   if we have __get and property that is not part of the properties array is
60   requested, we call __get handler. If it fails, we return uninitialized.
61 
62   if we have __set and property that is not part of the properties array is
63   set, we call __set handler. If it fails, we do not change the array.
64 
65   for both handlers above, when we are inside __get/__set, no further calls for
66   __get/__set for this property of this object will be made, to prevent endless
67   recursion and enable accessors to change properties array.
68 
69   if we have __call and method which is not part of the class function table is
70   called, we cal __call handler.
71 */
72 
rebuild_object_properties(zend_object * zobj)73 ZEND_API void rebuild_object_properties(zend_object *zobj) /* {{{ */
74 {
75 	if (!zobj->properties) {
76 		zend_property_info *prop_info;
77 		zend_class_entry *ce = zobj->ce;
78 
79 		ALLOC_HASHTABLE(zobj->properties);
80 		zend_hash_init(zobj->properties, ce->default_properties_count, NULL, ZVAL_PTR_DTOR, 0);
81 		if (ce->default_properties_count) {
82 			zend_hash_real_init(zobj->properties, 0);
83 			zobj->properties->nInternalPointer = 0;
84 			ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop_info) {
85 				if (/*prop_info->ce == ce &&*/
86 				    (prop_info->flags & ZEND_ACC_STATIC) == 0) {
87 
88 					if (UNEXPECTED(Z_TYPE_P(OBJ_PROP(zobj, prop_info->offset)) == IS_UNDEF)) {
89 						zobj->properties->u.v.flags |= HASH_FLAG_HAS_EMPTY_IND;
90 					}
91 
92 					_zend_hash_append_ind(zobj->properties, prop_info->name,
93 						OBJ_PROP(zobj, prop_info->offset));
94 				}
95 			} ZEND_HASH_FOREACH_END();
96 			while (ce->parent && ce->parent->default_properties_count) {
97 				ce = ce->parent;
98 				ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop_info) {
99 					if (prop_info->ce == ce &&
100 					    (prop_info->flags & ZEND_ACC_STATIC) == 0 &&
101 					    (prop_info->flags & ZEND_ACC_PRIVATE) != 0) {
102 						zval zv;
103 
104 						if (UNEXPECTED(Z_TYPE_P(OBJ_PROP(zobj, prop_info->offset)) == IS_UNDEF)) {
105 							zobj->properties->u.v.flags |= HASH_FLAG_HAS_EMPTY_IND;
106 						}
107 
108 						ZVAL_INDIRECT(&zv, OBJ_PROP(zobj, prop_info->offset));
109 						zend_hash_add(zobj->properties, prop_info->name, &zv);
110 					}
111 				} ZEND_HASH_FOREACH_END();
112 			}
113 		}
114 	}
115 }
116 /* }}} */
117 
zend_std_get_properties(zval * object)118 ZEND_API HashTable *zend_std_get_properties(zval *object) /* {{{ */
119 {
120 	zend_object *zobj;
121 	zobj = Z_OBJ_P(object);
122 	if (!zobj->properties) {
123 		rebuild_object_properties(zobj);
124 	}
125 	return zobj->properties;
126 }
127 /* }}} */
128 
zend_std_get_gc(zval * object,zval ** table,int * n)129 ZEND_API HashTable *zend_std_get_gc(zval *object, zval **table, int *n) /* {{{ */
130 {
131 	if (Z_OBJ_HANDLER_P(object, get_properties) != zend_std_get_properties) {
132 		*table = NULL;
133 		*n = 0;
134 		return Z_OBJ_HANDLER_P(object, get_properties)(object);
135 	} else {
136 		zend_object *zobj = Z_OBJ_P(object);
137 
138 		if (zobj->properties) {
139 			*table = NULL;
140 			*n = 0;
141 			if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)
142 			 && EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
143 				GC_REFCOUNT(zobj->properties)--;
144 				zobj->properties = zend_array_dup(zobj->properties);
145 			}
146 			return zobj->properties;
147 		} else {
148 			*table = zobj->properties_table;
149 			*n = zobj->ce->default_properties_count;
150 			return NULL;
151 		}
152 	}
153 }
154 /* }}} */
155 
zend_std_get_debug_info(zval * object,int * is_temp)156 ZEND_API HashTable *zend_std_get_debug_info(zval *object, int *is_temp) /* {{{ */
157 {
158 	zend_class_entry *ce = Z_OBJCE_P(object);
159 	zval retval;
160 	HashTable *ht;
161 
162 	if (!ce->__debugInfo) {
163 		*is_temp = 0;
164 		return Z_OBJ_HANDLER_P(object, get_properties)
165 			? Z_OBJ_HANDLER_P(object, get_properties)(object)
166 			: NULL;
167 	}
168 
169 	zend_call_method_with_0_params(object, ce, &ce->__debugInfo, ZEND_DEBUGINFO_FUNC_NAME, &retval);
170 	if (Z_TYPE(retval) == IS_ARRAY) {
171 		if (!Z_REFCOUNTED(retval)) {
172 			*is_temp = 1;
173 			return zend_array_dup(Z_ARRVAL(retval));
174 		} else if (Z_REFCOUNT(retval) <= 1) {
175 			*is_temp = 1;
176 			ht = Z_ARR(retval);
177 			return ht;
178 		} else {
179 			*is_temp = 0;
180 			zval_ptr_dtor(&retval);
181 			return Z_ARRVAL(retval);
182 		}
183 	} else if (Z_TYPE(retval) == IS_NULL) {
184 		*is_temp = 1;
185 		ALLOC_HASHTABLE(ht);
186 		zend_hash_init(ht, 0, NULL, ZVAL_PTR_DTOR, 0);
187 		return ht;
188 	}
189 
190 	zend_error_noreturn(E_ERROR, ZEND_DEBUGINFO_FUNC_NAME "() must return an array");
191 
192 	return NULL; /* Compilers are dumb and don't understand that noreturn means that the function does NOT need a return value... */
193 }
194 /* }}} */
195 
zend_std_call_getter(zval * object,zval * member,zval * retval)196 static void zend_std_call_getter(zval *object, zval *member, zval *retval) /* {{{ */
197 {
198 	zend_class_entry *ce = Z_OBJCE_P(object);
199 	zend_class_entry *orig_fake_scope = EG(fake_scope);
200 
201 	EG(fake_scope) = NULL;
202 
203 	/* __get handler is called with one argument:
204 	      property name
205 
206 	   it should return whether the call was successful or not
207 	*/
208 	zend_call_method_with_1_params(object, ce, &ce->__get, ZEND_GET_FUNC_NAME, retval, member);
209 
210 	EG(fake_scope) = orig_fake_scope;
211 }
212 /* }}} */
213 
zend_std_call_setter(zval * object,zval * member,zval * value)214 static void zend_std_call_setter(zval *object, zval *member, zval *value) /* {{{ */
215 {
216 	zend_class_entry *ce = Z_OBJCE_P(object);
217 	zend_class_entry *orig_fake_scope = EG(fake_scope);
218 
219 	EG(fake_scope) = NULL;
220 
221 	/* __set handler is called with two arguments:
222 	     property name
223 	     value to be set
224 	*/
225 	zend_call_method_with_2_params(object, ce, &ce->__set, ZEND_SET_FUNC_NAME, NULL, member, value);
226 
227 	EG(fake_scope) = orig_fake_scope;
228 }
229 /* }}} */
230 
zend_std_call_unsetter(zval * object,zval * member)231 static void zend_std_call_unsetter(zval *object, zval *member) /* {{{ */
232 {
233 	zend_class_entry *ce = Z_OBJCE_P(object);
234 	zend_class_entry *orig_fake_scope = EG(fake_scope);
235 
236 	EG(fake_scope) = NULL;
237 
238 	/* __unset handler is called with one argument:
239 	      property name
240 	*/
241 
242 	if (Z_REFCOUNTED_P(member)) Z_ADDREF_P(member);
243 
244 	zend_call_method_with_1_params(object, ce, &ce->__unset, ZEND_UNSET_FUNC_NAME, NULL, member);
245 
246 	zval_ptr_dtor(member);
247 
248 	EG(fake_scope) = orig_fake_scope;
249 }
250 /* }}} */
251 
zend_std_call_issetter(zval * object,zval * member,zval * retval)252 static void zend_std_call_issetter(zval *object, zval *member, zval *retval) /* {{{ */
253 {
254 	zend_class_entry *ce = Z_OBJCE_P(object);
255 	zend_class_entry *orig_fake_scope = EG(fake_scope);
256 
257 	EG(fake_scope) = NULL;
258 
259 	/* __isset handler is called with one argument:
260 	      property name
261 
262 	   it should return whether the property is set or not
263 	*/
264 
265 	if (Z_REFCOUNTED_P(member)) Z_ADDREF_P(member);
266 
267 	zend_call_method_with_1_params(object, ce, &ce->__isset, ZEND_ISSET_FUNC_NAME, retval, member);
268 
269 	zval_ptr_dtor(member);
270 
271 	EG(fake_scope) = orig_fake_scope;
272 }
273 /* }}} */
274 
zend_verify_property_access(zend_property_info * property_info,zend_class_entry * ce)275 static zend_always_inline int zend_verify_property_access(zend_property_info *property_info, zend_class_entry *ce) /* {{{ */
276 {
277 	zend_class_entry *scope;
278 
279 	if (property_info->flags & ZEND_ACC_PUBLIC) {
280 		return 1;
281 	} else if (property_info->flags & ZEND_ACC_PRIVATE) {
282 		if (EG(fake_scope)) {
283 			scope = EG(fake_scope);
284 		} else {
285 			scope = zend_get_executed_scope();
286 		}
287 		return (ce == scope || property_info->ce == scope);
288 	} else if (property_info->flags & ZEND_ACC_PROTECTED) {
289 		if (EG(fake_scope)) {
290 			scope = EG(fake_scope);
291 		} else {
292 			scope = zend_get_executed_scope();
293 		}
294 		return zend_check_protected(property_info->ce, scope);
295 	}
296 	return 0;
297 }
298 /* }}} */
299 
is_derived_class(zend_class_entry * child_class,zend_class_entry * parent_class)300 static zend_always_inline zend_bool is_derived_class(zend_class_entry *child_class, zend_class_entry *parent_class) /* {{{ */
301 {
302 	child_class = child_class->parent;
303 	while (child_class) {
304 		if (child_class == parent_class) {
305 			return 1;
306 		}
307 		child_class = child_class->parent;
308 	}
309 
310 	return 0;
311 }
312 /* }}} */
313 
zend_get_property_offset(zend_class_entry * ce,zend_string * member,int silent,void ** cache_slot)314 static zend_always_inline uint32_t zend_get_property_offset(zend_class_entry *ce, zend_string *member, int silent, void **cache_slot) /* {{{ */
315 {
316 	zval *zv;
317 	zend_property_info *property_info = NULL;
318 	uint32_t flags;
319 	zend_class_entry *scope;
320 
321 	if (cache_slot && EXPECTED(ce == CACHED_PTR_EX(cache_slot))) {
322 		return (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1);
323 	}
324 
325 	if (UNEXPECTED(ZSTR_VAL(member)[0] == '\0' && ZSTR_LEN(member) != 0)) {
326 		if (!silent) {
327 			zend_throw_error(NULL, "Cannot access property started with '\\0'");
328 		}
329 		return ZEND_WRONG_PROPERTY_OFFSET;
330 	}
331 
332 	if (UNEXPECTED(zend_hash_num_elements(&ce->properties_info) == 0)) {
333 		goto exit_dynamic;
334 	}
335 
336 	zv = zend_hash_find(&ce->properties_info, member);
337 	if (EXPECTED(zv != NULL)) {
338 		property_info = (zend_property_info*)Z_PTR_P(zv);
339 		flags = property_info->flags;
340 		if (UNEXPECTED((flags & ZEND_ACC_SHADOW) != 0)) {
341 			/* if it's a shadow - go to access it's private */
342 			property_info = NULL;
343 		} else {
344 			if (EXPECTED(zend_verify_property_access(property_info, ce) != 0)) {
345 				if (UNEXPECTED(!(flags & ZEND_ACC_CHANGED))
346 					|| UNEXPECTED((flags & ZEND_ACC_PRIVATE))) {
347 					if (UNEXPECTED((flags & ZEND_ACC_STATIC) != 0)) {
348 						if (!silent) {
349 							zend_error(E_NOTICE, "Accessing static property %s::$%s as non static", ZSTR_VAL(ce->name), ZSTR_VAL(member));
350 						}
351 						return ZEND_DYNAMIC_PROPERTY_OFFSET;
352 					}
353 					goto exit;
354 				}
355 			} else {
356 				/* Try to look in the scope instead */
357 				property_info = ZEND_WRONG_PROPERTY_INFO;
358 			}
359 		}
360 	}
361 
362 	if (EG(fake_scope)) {
363 		scope = EG(fake_scope);
364 	} else {
365 		scope = zend_get_executed_scope();
366 	}
367 
368 	if (scope != ce
369 		&& scope
370 		&& is_derived_class(ce, scope)
371 		&& (zv = zend_hash_find(&scope->properties_info, member)) != NULL
372 		&& ((zend_property_info*)Z_PTR_P(zv))->flags & ZEND_ACC_PRIVATE) {
373 		property_info = (zend_property_info*)Z_PTR_P(zv);
374 		if (UNEXPECTED((property_info->flags & ZEND_ACC_STATIC) != 0)) {
375 			return ZEND_DYNAMIC_PROPERTY_OFFSET;
376 		}
377 	} else if (UNEXPECTED(property_info == NULL)) {
378 exit_dynamic:
379 		if (cache_slot) {
380 			CACHE_POLYMORPHIC_PTR_EX(cache_slot, ce, (void*)(intptr_t)ZEND_DYNAMIC_PROPERTY_OFFSET);
381 		}
382 		return ZEND_DYNAMIC_PROPERTY_OFFSET;
383 	} else if (UNEXPECTED(property_info == ZEND_WRONG_PROPERTY_INFO)) {
384 		/* Information was available, but we were denied access.  Error out. */
385 		if (!silent) {
386 			zend_throw_error(NULL, "Cannot access %s property %s::$%s", zend_visibility_string(flags), ZSTR_VAL(ce->name), ZSTR_VAL(member));
387 		}
388 		return ZEND_WRONG_PROPERTY_OFFSET;
389 	}
390 
391 exit:
392 	if (cache_slot) {
393 		CACHE_POLYMORPHIC_PTR_EX(cache_slot, ce, (void*)(intptr_t)property_info->offset);
394 	}
395 	return property_info->offset;
396 }
397 /* }}} */
398 
zend_get_property_info(zend_class_entry * ce,zend_string * member,int silent)399 ZEND_API zend_property_info *zend_get_property_info(zend_class_entry *ce, zend_string *member, int silent) /* {{{ */
400 {
401 	zval *zv;
402 	zend_property_info *property_info = NULL;
403 	uint32_t flags;
404 	zend_class_entry *scope;
405 
406 	if (UNEXPECTED(ZSTR_VAL(member)[0] == '\0' && ZSTR_LEN(member) != 0)) {
407 		if (!silent) {
408 			zend_throw_error(NULL, "Cannot access property started with '\\0'");
409 		}
410 		return ZEND_WRONG_PROPERTY_INFO;
411 	}
412 
413 	if (UNEXPECTED(zend_hash_num_elements(&ce->properties_info) == 0)) {
414 		goto exit_dynamic;
415 	}
416 
417 	zv = zend_hash_find(&ce->properties_info, member);
418 	if (EXPECTED(zv != NULL)) {
419 		property_info = (zend_property_info*)Z_PTR_P(zv);
420 		flags = property_info->flags;
421 		if (UNEXPECTED((flags & ZEND_ACC_SHADOW) != 0)) {
422 			/* if it's a shadow - go to access it's private */
423 			property_info = NULL;
424 		} else {
425 			if (EXPECTED(zend_verify_property_access(property_info, ce) != 0)) {
426 				if (UNEXPECTED(!(flags & ZEND_ACC_CHANGED))
427 					|| UNEXPECTED((flags & ZEND_ACC_PRIVATE))) {
428 					if (UNEXPECTED((flags & ZEND_ACC_STATIC) != 0)) {
429 						if (!silent) {
430 							zend_error(E_NOTICE, "Accessing static property %s::$%s as non static", ZSTR_VAL(ce->name), ZSTR_VAL(member));
431 						}
432 					}
433 					goto exit;
434 				}
435 			} else {
436 				/* Try to look in the scope instead */
437 				property_info = ZEND_WRONG_PROPERTY_INFO;
438 			}
439 		}
440 	}
441 
442 	if (EG(fake_scope)) {
443 		scope = EG(fake_scope);
444 	} else {
445 		scope = zend_get_executed_scope();
446 	}
447 
448 	if (scope != ce
449 		&& scope
450 		&& is_derived_class(ce, scope)
451 		&& (zv = zend_hash_find(&scope->properties_info, member)) != NULL
452 		&& ((zend_property_info*)Z_PTR_P(zv))->flags & ZEND_ACC_PRIVATE) {
453 		property_info = (zend_property_info*)Z_PTR_P(zv);
454 	} else if (UNEXPECTED(property_info == NULL)) {
455 exit_dynamic:
456 		return NULL;
457 	} else if (UNEXPECTED(property_info == ZEND_WRONG_PROPERTY_INFO)) {
458 		/* Information was available, but we were denied access.  Error out. */
459 		if (!silent) {
460 			zend_throw_error(NULL, "Cannot access %s property %s::$%s", zend_visibility_string(flags), ZSTR_VAL(ce->name), ZSTR_VAL(member));
461 		}
462 		return ZEND_WRONG_PROPERTY_INFO;
463 	}
464 
465 exit:
466 	return property_info;
467 }
468 /* }}} */
469 
zend_check_property_access(zend_object * zobj,zend_string * prop_info_name)470 ZEND_API int zend_check_property_access(zend_object *zobj, zend_string *prop_info_name) /* {{{ */
471 {
472 	zend_property_info *property_info;
473 	const char *class_name = NULL;
474 	const char *prop_name;
475 	zend_string *member;
476 	size_t prop_name_len;
477 
478 	if (ZSTR_VAL(prop_info_name)[0] == 0) {
479 		zend_unmangle_property_name_ex(prop_info_name, &class_name, &prop_name, &prop_name_len);
480 		member = zend_string_init(prop_name, prop_name_len, 0);
481 	} else {
482 		member = zend_string_copy(prop_info_name);
483 	}
484 	property_info = zend_get_property_info(zobj->ce, member, 1);
485 	zend_string_release(member);
486 	if (property_info == NULL) {
487 		/* undefined public property */
488 		if (class_name && class_name[0] != '*') {
489 			/* we we're looking for a private prop */
490 			return FAILURE;
491 		}
492 		return SUCCESS;
493 	} else if (property_info == ZEND_WRONG_PROPERTY_INFO) {
494 		return FAILURE;
495 	}
496 	if (class_name && class_name[0] != '*') {
497 		if (!(property_info->flags & ZEND_ACC_PRIVATE)) {
498 			/* we we're looking for a private prop but found a non private one of the same name */
499 			return FAILURE;
500 		} else if (strcmp(ZSTR_VAL(prop_info_name)+1, ZSTR_VAL(property_info->name)+1)) {
501 			/* we we're looking for a private prop but found a private one of the same name but another class */
502 			return FAILURE;
503 		}
504 	}
505 	return zend_verify_property_access(property_info, zobj->ce) ? SUCCESS : FAILURE;
506 }
507 /* }}} */
508 
zend_property_guard_dtor(zval * el)509 static void zend_property_guard_dtor(zval *el) /* {{{ */ {
510 	uint32_t *ptr = (uint32_t*)Z_PTR_P(el);
511 	if (EXPECTED(!(((zend_uintptr_t)ptr) & 1))) {
512 		efree_size(ptr, sizeof(uint32_t));
513 	}
514 }
515 /* }}} */
516 
zend_get_property_guard(zend_object * zobj,zend_string * member)517 ZEND_API uint32_t *zend_get_property_guard(zend_object *zobj, zend_string *member) /* {{{ */
518 {
519 	HashTable *guards;
520 	zval *zv;
521 	uint32_t *ptr;
522 
523 	ZEND_ASSERT(GC_FLAGS(zobj) & IS_OBJ_USE_GUARDS);
524 	zv = zobj->properties_table + zobj->ce->default_properties_count;
525 	if (EXPECTED(Z_TYPE_P(zv) == IS_STRING)) {
526 		zend_string *str = Z_STR_P(zv);
527 		if (EXPECTED(str == member) ||
528 		     /* hash values are always pred-calculated here */
529 		    (EXPECTED(ZSTR_H(str) == ZSTR_H(member)) &&
530 		     EXPECTED(ZSTR_LEN(str) == ZSTR_LEN(member)) &&
531 		     EXPECTED(memcmp(ZSTR_VAL(str), ZSTR_VAL(member), ZSTR_LEN(member)) == 0))) {
532 			return &zv->u2.property_guard;
533 		} else if (EXPECTED(zv->u2.property_guard == 0)) {
534 			zend_string_release(Z_STR_P(zv));
535 			ZVAL_STR_COPY(zv, member);
536 			return &zv->u2.property_guard;
537 		} else {
538 			ALLOC_HASHTABLE(guards);
539 			zend_hash_init(guards, 8, NULL, zend_property_guard_dtor, 0);
540 			/* mark pointer as "special" using low bit */
541 			zend_hash_add_new_ptr(guards, str,
542 				(void*)(((zend_uintptr_t)&zv->u2.property_guard) | 1));
543 			zend_string_release(Z_STR_P(zv));
544 			ZVAL_ARR(zv, guards);
545 		}
546 	} else if (EXPECTED(Z_TYPE_P(zv) == IS_ARRAY)) {
547 		guards = Z_ARRVAL_P(zv);
548 		ZEND_ASSERT(guards != NULL);
549 		zv = zend_hash_find(guards, member);
550 		if (zv != NULL) {
551 			return (uint32_t*)(((zend_uintptr_t)Z_PTR_P(zv)) & ~1);
552 		}
553 	} else {
554 		ZEND_ASSERT(Z_TYPE_P(zv) == IS_UNDEF);
555 		GC_FLAGS(zobj) |= IS_OBJ_HAS_GUARDS;
556 		ZVAL_STR_COPY(zv, member);
557 		zv->u2.property_guard = 0;
558 		return &zv->u2.property_guard;
559 	}
560 	/* we have to allocate uint32_t separately because ht->arData may be reallocated */
561 	ptr = (uint32_t*)emalloc(sizeof(uint32_t));
562 	*ptr = 0;
563 	return (uint32_t*)zend_hash_add_new_ptr(guards, member, ptr);
564 }
565 /* }}} */
566 
zend_std_read_property(zval * object,zval * member,int type,void ** cache_slot,zval * rv)567 zval *zend_std_read_property(zval *object, zval *member, int type, void **cache_slot, zval *rv) /* {{{ */
568 {
569 	zend_object *zobj;
570 	zval tmp_member, tmp_object;
571 	zval *retval;
572 	uint32_t property_offset;
573 	uint32_t *guard = NULL;
574 
575 	zobj = Z_OBJ_P(object);
576 
577 	ZVAL_UNDEF(&tmp_member);
578 	if (UNEXPECTED(Z_TYPE_P(member) != IS_STRING)) {
579 		ZVAL_STR(&tmp_member, zval_get_string(member));
580 		member = &tmp_member;
581 		cache_slot = NULL;
582 	}
583 
584 #if DEBUG_OBJECT_HANDLERS
585 	fprintf(stderr, "Read object #%d property: %s\n", Z_OBJ_HANDLE_P(object), Z_STRVAL_P(member));
586 #endif
587 
588 	/* make zend_get_property_info silent if we have getter - we may want to use it */
589 	property_offset = zend_get_property_offset(zobj->ce, Z_STR_P(member), (type == BP_VAR_IS) || (zobj->ce->__get != NULL), cache_slot);
590 
591 	if (EXPECTED(property_offset != ZEND_WRONG_PROPERTY_OFFSET)) {
592 		if (EXPECTED(property_offset != ZEND_DYNAMIC_PROPERTY_OFFSET)) {
593 			retval = OBJ_PROP(zobj, property_offset);
594 			if (EXPECTED(Z_TYPE_P(retval) != IS_UNDEF)) {
595 				goto exit;
596 			}
597 		} else if (EXPECTED(zobj->properties != NULL)) {
598 			retval = zend_hash_find(zobj->properties, Z_STR_P(member));
599 			if (EXPECTED(retval)) goto exit;
600 		}
601 	} else if (UNEXPECTED(EG(exception))) {
602 		retval = &EG(uninitialized_zval);
603 		goto exit;
604 	}
605 
606 	ZVAL_UNDEF(&tmp_object);
607 
608 	/* magic isset */
609 	if ((type == BP_VAR_IS) && zobj->ce->__isset) {
610 		zval tmp_result;
611 		guard = zend_get_property_guard(zobj, Z_STR_P(member));
612 
613 		if (!((*guard) & IN_ISSET)) {
614 			if (Z_TYPE(tmp_member) == IS_UNDEF) {
615 				ZVAL_COPY(&tmp_member, member);
616 				member = &tmp_member;
617 			}
618 			ZVAL_COPY(&tmp_object, object);
619 			ZVAL_UNDEF(&tmp_result);
620 
621 			*guard |= IN_ISSET;
622 			zend_std_call_issetter(&tmp_object, member, &tmp_result);
623 			*guard &= ~IN_ISSET;
624 
625 			if (!zend_is_true(&tmp_result)) {
626 				retval = &EG(uninitialized_zval);
627 				zval_ptr_dtor(&tmp_object);
628 				zval_ptr_dtor(&tmp_result);
629 				goto exit;
630 			}
631 
632 			zval_ptr_dtor(&tmp_result);
633 		}
634 	}
635 
636 	/* magic get */
637 	if (zobj->ce->__get) {
638 		if (guard == NULL) {
639 			guard = zend_get_property_guard(zobj, Z_STR_P(member));
640 		}
641 		if (!((*guard) & IN_GET)) {
642 			/* have getter - try with it! */
643 			if (Z_TYPE(tmp_object) == IS_UNDEF) {
644 				ZVAL_COPY(&tmp_object, object);
645 			}
646 			*guard |= IN_GET; /* prevent circular getting */
647 			zend_std_call_getter(&tmp_object, member, rv);
648 			*guard &= ~IN_GET;
649 
650 			if (Z_TYPE_P(rv) != IS_UNDEF) {
651 				retval = rv;
652 				if (!Z_ISREF_P(rv) &&
653 				    (type == BP_VAR_W || type == BP_VAR_RW  || type == BP_VAR_UNSET)) {
654 					SEPARATE_ZVAL(rv);
655 					if (UNEXPECTED(Z_TYPE_P(rv) != IS_OBJECT)) {
656 						zend_error(E_NOTICE, "Indirect modification of overloaded property %s::$%s has no effect", ZSTR_VAL(zobj->ce->name), Z_STRVAL_P(member));
657 					}
658 				}
659 			} else {
660 				retval = &EG(uninitialized_zval);
661 			}
662 			zval_ptr_dtor(&tmp_object);
663 			goto exit;
664 		} else if (Z_STRVAL_P(member)[0] == '\0' && Z_STRLEN_P(member) != 0) {
665 			zval_ptr_dtor(&tmp_object);
666 			zend_throw_error(NULL, "Cannot access property started with '\\0'");
667 			retval = &EG(uninitialized_zval);
668 			goto exit;
669 		}
670 	}
671 
672 	zval_ptr_dtor(&tmp_object);
673 
674 	if ((type != BP_VAR_IS)) {
675 		zend_error(E_NOTICE,"Undefined property: %s::$%s", ZSTR_VAL(zobj->ce->name), Z_STRVAL_P(member));
676 	}
677 	retval = &EG(uninitialized_zval);
678 
679 exit:
680 	if (UNEXPECTED(Z_REFCOUNTED(tmp_member))) {
681 		zval_ptr_dtor(&tmp_member);
682 	}
683 
684 	return retval;
685 }
686 /* }}} */
687 
zend_std_write_property(zval * object,zval * member,zval * value,void ** cache_slot)688 ZEND_API void zend_std_write_property(zval *object, zval *member, zval *value, void **cache_slot) /* {{{ */
689 {
690 	zend_object *zobj;
691 	zval tmp_member;
692 	zval *variable_ptr;
693 	uint32_t property_offset;
694 
695 	zobj = Z_OBJ_P(object);
696 
697 	ZVAL_UNDEF(&tmp_member);
698  	if (UNEXPECTED(Z_TYPE_P(member) != IS_STRING)) {
699 		ZVAL_STR(&tmp_member, zval_get_string(member));
700 		member = &tmp_member;
701 		cache_slot = NULL;
702 	}
703 
704 	property_offset = zend_get_property_offset(zobj->ce, Z_STR_P(member), (zobj->ce->__set != NULL), cache_slot);
705 
706 	if (EXPECTED(property_offset != ZEND_WRONG_PROPERTY_OFFSET)) {
707 		if (EXPECTED(property_offset != ZEND_DYNAMIC_PROPERTY_OFFSET)) {
708 			variable_ptr = OBJ_PROP(zobj, property_offset);
709 			if (Z_TYPE_P(variable_ptr) != IS_UNDEF) {
710 				goto found;
711 			}
712 		} else if (EXPECTED(zobj->properties != NULL)) {
713 			if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
714 				if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
715 					GC_REFCOUNT(zobj->properties)--;
716 				}
717 				zobj->properties = zend_array_dup(zobj->properties);
718 			}
719 			if ((variable_ptr = zend_hash_find(zobj->properties, Z_STR_P(member))) != NULL) {
720 found:
721 				zend_assign_to_variable(variable_ptr, value, IS_CV);
722 				goto exit;
723 			}
724 		}
725 	} else if (UNEXPECTED(EG(exception))) {
726 		goto exit;
727 	}
728 
729 	/* magic set */
730 	if (zobj->ce->__set) {
731 		uint32_t *guard = zend_get_property_guard(zobj, Z_STR_P(member));
732 
733 	    if (!((*guard) & IN_SET)) {
734 			zval tmp_object;
735 
736 			ZVAL_COPY(&tmp_object, object);
737 			(*guard) |= IN_SET; /* prevent circular setting */
738 			zend_std_call_setter(&tmp_object, member, value);
739 			(*guard) &= ~IN_SET;
740 			zval_ptr_dtor(&tmp_object);
741 		} else if (EXPECTED(property_offset != ZEND_WRONG_PROPERTY_OFFSET)) {
742 			goto write_std_property;
743 		} else {
744 			if (Z_STRVAL_P(member)[0] == '\0' && Z_STRLEN_P(member) != 0) {
745 				zend_throw_error(NULL, "Cannot access property started with '\\0'");
746 				goto exit;
747 			}
748 		}
749 	} else if (EXPECTED(property_offset != ZEND_WRONG_PROPERTY_OFFSET)) {
750 		zval tmp;
751 
752 write_std_property:
753 		if (Z_REFCOUNTED_P(value)) {
754 			if (Z_ISREF_P(value)) {
755 				/* if we assign referenced variable, we should separate it */
756 				ZVAL_COPY(&tmp, Z_REFVAL_P(value));
757 				value = &tmp;
758 			} else {
759 				Z_ADDREF_P(value);
760 			}
761 		}
762 		if (EXPECTED(property_offset != ZEND_DYNAMIC_PROPERTY_OFFSET)) {
763 			ZVAL_COPY_VALUE(OBJ_PROP(zobj, property_offset), value);
764 		} else {
765 			if (!zobj->properties) {
766 				rebuild_object_properties(zobj);
767 			}
768 			zend_hash_add_new(zobj->properties, Z_STR_P(member), value);
769 		}
770 	}
771 
772 exit:
773 	if (UNEXPECTED(Z_REFCOUNTED(tmp_member))) {
774 		zval_ptr_dtor(&tmp_member);
775 	}
776 }
777 /* }}} */
778 
zend_std_read_dimension(zval * object,zval * offset,int type,zval * rv)779 zval *zend_std_read_dimension(zval *object, zval *offset, int type, zval *rv) /* {{{ */
780 {
781 	zend_class_entry *ce = Z_OBJCE_P(object);
782 	zval tmp_offset, tmp_object;
783 
784 	if (EXPECTED(instanceof_function_ex(ce, zend_ce_arrayaccess, 1) != 0)) {
785 		if (offset == NULL) {
786 			/* [] construct */
787 			ZVAL_NULL(&tmp_offset);
788 		} else {
789 			ZVAL_DEREF(offset);
790 			ZVAL_COPY(&tmp_offset, offset);
791 		}
792 
793 		ZVAL_COPY(&tmp_object, object);
794 		if (type == BP_VAR_IS) {
795 			zend_call_method_with_1_params(&tmp_object, ce, NULL, "offsetexists", rv, &tmp_offset);
796 			if (UNEXPECTED(Z_ISUNDEF_P(rv))) {
797 				zval_ptr_dtor(&tmp_object);
798 				zval_ptr_dtor(&tmp_offset);
799 				return NULL;
800 			}
801 			if (!i_zend_is_true(rv)) {
802 				zval_ptr_dtor(&tmp_object);
803 				zval_ptr_dtor(&tmp_offset);
804 				zval_ptr_dtor(rv);
805 				return &EG(uninitialized_zval);
806 			}
807 			zval_ptr_dtor(rv);
808 		}
809 
810 		zend_call_method_with_1_params(&tmp_object, ce, NULL, "offsetget", rv, &tmp_offset);
811 
812 		zval_ptr_dtor(&tmp_object);
813 		zval_ptr_dtor(&tmp_offset);
814 
815 		if (UNEXPECTED(Z_TYPE_P(rv) == IS_UNDEF)) {
816 			if (UNEXPECTED(!EG(exception))) {
817 				zend_throw_error(NULL, "Undefined offset for object of type %s used as array", ZSTR_VAL(ce->name));
818 			}
819 			return NULL;
820 		}
821 		return rv;
822 	} else {
823 		zend_throw_error(NULL, "Cannot use object of type %s as array", ZSTR_VAL(ce->name));
824 		return NULL;
825 	}
826 }
827 /* }}} */
828 
zend_std_write_dimension(zval * object,zval * offset,zval * value)829 static void zend_std_write_dimension(zval *object, zval *offset, zval *value) /* {{{ */
830 {
831 	zend_class_entry *ce = Z_OBJCE_P(object);
832 	zval tmp_offset, tmp_object;
833 
834 	if (EXPECTED(instanceof_function_ex(ce, zend_ce_arrayaccess, 1) != 0)) {
835 		if (!offset) {
836 			ZVAL_NULL(&tmp_offset);
837 		} else {
838 			ZVAL_DEREF(offset);
839 			ZVAL_COPY(&tmp_offset, offset);
840 		}
841 		ZVAL_COPY(&tmp_object, object);
842 		zend_call_method_with_2_params(&tmp_object, ce, NULL, "offsetset", NULL, &tmp_offset, value);
843 		zval_ptr_dtor(&tmp_object);
844 		zval_ptr_dtor(&tmp_offset);
845 	} else {
846 		zend_throw_error(NULL, "Cannot use object of type %s as array", ZSTR_VAL(ce->name));
847 	}
848 }
849 /* }}} */
850 
zend_std_has_dimension(zval * object,zval * offset,int check_empty)851 static int zend_std_has_dimension(zval *object, zval *offset, int check_empty) /* {{{ */
852 {
853 	zend_class_entry *ce = Z_OBJCE_P(object);
854 	zval retval, tmp_offset, tmp_object;
855 	int result;
856 
857 	if (EXPECTED(instanceof_function_ex(ce, zend_ce_arrayaccess, 1) != 0)) {
858 		ZVAL_DEREF(offset);
859 		ZVAL_COPY(&tmp_offset, offset);
860 		ZVAL_COPY(&tmp_object, object);
861 		zend_call_method_with_1_params(&tmp_object, ce, NULL, "offsetexists", &retval, &tmp_offset);
862 		if (EXPECTED(Z_TYPE(retval) != IS_UNDEF)) {
863 			result = i_zend_is_true(&retval);
864 			zval_ptr_dtor(&retval);
865 			if (check_empty && result && EXPECTED(!EG(exception))) {
866 				zend_call_method_with_1_params(&tmp_object, ce, NULL, "offsetget", &retval, &tmp_offset);
867 				if (EXPECTED(Z_TYPE(retval) != IS_UNDEF)) {
868 					result = i_zend_is_true(&retval);
869 					zval_ptr_dtor(&retval);
870 				}
871 			}
872 		} else {
873 			result = 0;
874 		}
875 		zval_ptr_dtor(&tmp_object);
876 		zval_ptr_dtor(&tmp_offset);
877 	} else {
878 		zend_throw_error(NULL, "Cannot use object of type %s as array", ZSTR_VAL(ce->name));
879 		return 0;
880 	}
881 	return result;
882 }
883 /* }}} */
884 
zend_std_get_property_ptr_ptr(zval * object,zval * member,int type,void ** cache_slot)885 static zval *zend_std_get_property_ptr_ptr(zval *object, zval *member, int type, void **cache_slot) /* {{{ */
886 {
887 	zend_object *zobj;
888 	zend_string *name;
889 	zval *retval = NULL;
890 	uint32_t property_offset;
891 
892 	zobj = Z_OBJ_P(object);
893 	if (EXPECTED(Z_TYPE_P(member) == IS_STRING)) {
894 		name = Z_STR_P(member);
895 	} else {
896 		name = zval_get_string(member);
897 	}
898 
899 #if DEBUG_OBJECT_HANDLERS
900 	fprintf(stderr, "Ptr object #%d property: %s\n", Z_OBJ_HANDLE_P(object), ZSTR_VAL(name));
901 #endif
902 
903 	property_offset = zend_get_property_offset(zobj->ce, name, (zobj->ce->__get != NULL), cache_slot);
904 
905 	if (EXPECTED(property_offset != ZEND_WRONG_PROPERTY_OFFSET)) {
906 		if (EXPECTED(property_offset != ZEND_DYNAMIC_PROPERTY_OFFSET)) {
907 			retval = OBJ_PROP(zobj, property_offset);
908 			if (UNEXPECTED(Z_TYPE_P(retval) == IS_UNDEF)) {
909 				if (EXPECTED(!zobj->ce->__get) ||
910 				    UNEXPECTED((*zend_get_property_guard(zobj, name)) & IN_GET)) {
911 					ZVAL_NULL(retval);
912 					/* Notice is thrown after creation of the property, to avoid EG(std_property_info)
913 					 * being overwritten in an error handler. */
914 					if (UNEXPECTED(type == BP_VAR_RW || type == BP_VAR_R)) {
915 						zend_error(E_NOTICE, "Undefined property: %s::$%s", ZSTR_VAL(zobj->ce->name), ZSTR_VAL(name));
916 					}
917 				} else {
918 					/* we do have getter - fail and let it try again with usual get/set */
919 					retval = NULL;
920 				}
921 			}
922 		} else {
923 			if (EXPECTED(zobj->properties)) {
924 				if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
925 					if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
926 						GC_REFCOUNT(zobj->properties)--;
927 					}
928 					zobj->properties = zend_array_dup(zobj->properties);
929 				}
930 			    if (EXPECTED((retval = zend_hash_find(zobj->properties, name)) != NULL)) {
931 					if (UNEXPECTED(Z_TYPE_P(member) != IS_STRING)) {
932 						zend_string_release(name);
933 					}
934 					return retval;
935 			    }
936 			}
937 			if (EXPECTED(!zobj->ce->__get) ||
938 			    UNEXPECTED((*zend_get_property_guard(zobj, name)) & IN_GET)) {
939 				if (UNEXPECTED(!zobj->properties)) {
940 					rebuild_object_properties(zobj);
941 				}
942 				retval = zend_hash_update(zobj->properties, name, &EG(uninitialized_zval));
943 				/* Notice is thrown after creation of the property, to avoid EG(std_property_info)
944 				 * being overwritten in an error handler. */
945 				if (UNEXPECTED(type == BP_VAR_RW || type == BP_VAR_R)) {
946 					zend_error(E_NOTICE, "Undefined property: %s::$%s", ZSTR_VAL(zobj->ce->name), ZSTR_VAL(name));
947 				}
948 			}
949 		}
950 	}
951 
952 	if (UNEXPECTED(Z_TYPE_P(member) != IS_STRING)) {
953 		zend_string_release(name);
954 	}
955 	return retval;
956 }
957 /* }}} */
958 
zend_std_unset_property(zval * object,zval * member,void ** cache_slot)959 static void zend_std_unset_property(zval *object, zval *member, void **cache_slot) /* {{{ */
960 {
961 	zend_object *zobj;
962 	zval tmp_member;
963 	uint32_t property_offset;
964 
965 	zobj = Z_OBJ_P(object);
966 
967 	ZVAL_UNDEF(&tmp_member);
968  	if (UNEXPECTED(Z_TYPE_P(member) != IS_STRING)) {
969 		ZVAL_STR(&tmp_member, zval_get_string(member));
970 		member = &tmp_member;
971 		cache_slot = NULL;
972 	}
973 
974 	property_offset = zend_get_property_offset(zobj->ce, Z_STR_P(member), (zobj->ce->__unset != NULL), cache_slot);
975 
976 	if (EXPECTED(property_offset != ZEND_WRONG_PROPERTY_OFFSET)) {
977 		if (EXPECTED(property_offset != ZEND_DYNAMIC_PROPERTY_OFFSET)) {
978 			zval *slot = OBJ_PROP(zobj, property_offset);
979 
980 			if (Z_TYPE_P(slot) != IS_UNDEF) {
981 				zval_ptr_dtor(slot);
982 				ZVAL_UNDEF(slot);
983 				if (zobj->properties) {
984 					zobj->properties->u.v.flags |= HASH_FLAG_HAS_EMPTY_IND;
985 				}
986 				goto exit;
987 			}
988 		} else if (EXPECTED(zobj->properties != NULL)) {
989 			if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
990 				if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
991 					GC_REFCOUNT(zobj->properties)--;
992 				}
993 				zobj->properties = zend_array_dup(zobj->properties);
994 			}
995 			if (EXPECTED(zend_hash_del(zobj->properties, Z_STR_P(member)) != FAILURE)) {
996 				goto exit;
997 			}
998 		}
999 	} else if (UNEXPECTED(EG(exception))) {
1000 		goto exit;
1001 	}
1002 
1003 	/* magic unset */
1004 	if (zobj->ce->__unset) {
1005 		uint32_t *guard = zend_get_property_guard(zobj, Z_STR_P(member));
1006 		if (!((*guard) & IN_UNSET)) {
1007 			zval tmp_object;
1008 
1009 			/* have unseter - try with it! */
1010 			ZVAL_COPY(&tmp_object, object);
1011 			(*guard) |= IN_UNSET; /* prevent circular unsetting */
1012 			zend_std_call_unsetter(&tmp_object, member);
1013 			(*guard) &= ~IN_UNSET;
1014 			zval_ptr_dtor(&tmp_object);
1015 		} else {
1016 			if (Z_STRVAL_P(member)[0] == '\0' && Z_STRLEN_P(member) != 0) {
1017 				zend_throw_error(NULL, "Cannot access property started with '\\0'");
1018 				goto exit;
1019 			}
1020 		}
1021 	}
1022 
1023 exit:
1024 	if (UNEXPECTED(Z_REFCOUNTED(tmp_member))) {
1025 		zval_ptr_dtor(&tmp_member);
1026 	}
1027 }
1028 /* }}} */
1029 
zend_std_unset_dimension(zval * object,zval * offset)1030 static void zend_std_unset_dimension(zval *object, zval *offset) /* {{{ */
1031 {
1032 	zend_class_entry *ce = Z_OBJCE_P(object);
1033 	zval tmp_offset, tmp_object;
1034 
1035 	if (instanceof_function_ex(ce, zend_ce_arrayaccess, 1)) {
1036 		ZVAL_DEREF(offset);
1037 		ZVAL_COPY(&tmp_offset, offset);
1038 		ZVAL_COPY(&tmp_object, object);
1039 		zend_call_method_with_1_params(&tmp_object, ce, NULL, "offsetunset", NULL, &tmp_offset);
1040 		zval_ptr_dtor(&tmp_object);
1041 		zval_ptr_dtor(&tmp_offset);
1042 	} else {
1043 		zend_throw_error(NULL, "Cannot use object of type %s as array", ZSTR_VAL(ce->name));
1044 	}
1045 }
1046 /* }}} */
1047 
1048 /* Ensures that we're allowed to call a private method.
1049  * Returns the function address that should be called, or NULL
1050  * if no such function exists.
1051  */
zend_check_private_int(zend_function * fbc,zend_class_entry * ce,zend_string * function_name)1052 static inline zend_function *zend_check_private_int(zend_function *fbc, zend_class_entry *ce, zend_string *function_name) /* {{{ */
1053 {
1054     zval *func;
1055     zend_class_entry *scope;
1056 
1057 	if (!ce) {
1058 		return 0;
1059 	}
1060 
1061 	/* We may call a private function if:
1062 	 * 1.  The class of our object is the same as the scope, and the private
1063 	 *     function (EX(fbc)) has the same scope.
1064 	 * 2.  One of our parent classes are the same as the scope, and it contains
1065 	 *     a private function with the same name that has the same scope.
1066 	 */
1067 	scope = zend_get_executed_scope();
1068 	if (fbc->common.scope == ce && scope == ce) {
1069 		/* rule #1 checks out ok, allow the function call */
1070 		return fbc;
1071 	}
1072 
1073 
1074 	/* Check rule #2 */
1075 	ce = ce->parent;
1076 	while (ce) {
1077 		if (ce == scope) {
1078 			if ((func = zend_hash_find(&ce->function_table, function_name))) {
1079 				fbc = Z_FUNC_P(func);
1080 				if (fbc->common.fn_flags & ZEND_ACC_PRIVATE
1081 					&& fbc->common.scope == scope) {
1082 					return fbc;
1083 				}
1084 			}
1085 			break;
1086 		}
1087 		ce = ce->parent;
1088 	}
1089 	return NULL;
1090 }
1091 /* }}} */
1092 
zend_check_private(zend_function * fbc,zend_class_entry * ce,zend_string * function_name)1093 ZEND_API int zend_check_private(zend_function *fbc, zend_class_entry *ce, zend_string *function_name) /* {{{ */
1094 {
1095 	return zend_check_private_int(fbc, ce, function_name) != NULL;
1096 }
1097 /* }}} */
1098 
1099 /* Ensures that we're allowed to call a protected method.
1100  */
zend_check_protected(zend_class_entry * ce,zend_class_entry * scope)1101 ZEND_API int zend_check_protected(zend_class_entry *ce, zend_class_entry *scope) /* {{{ */
1102 {
1103 	zend_class_entry *fbc_scope = ce;
1104 
1105 	/* Is the context that's calling the function, the same as one of
1106 	 * the function's parents?
1107 	 */
1108 	while (fbc_scope) {
1109 		if (fbc_scope==scope) {
1110 			return 1;
1111 		}
1112 		fbc_scope = fbc_scope->parent;
1113 	}
1114 
1115 	/* Is the function's scope the same as our current object context,
1116 	 * or any of the parents of our context?
1117 	 */
1118 	while (scope) {
1119 		if (scope==ce) {
1120 			return 1;
1121 		}
1122 		scope = scope->parent;
1123 	}
1124 	return 0;
1125 }
1126 /* }}} */
1127 
zend_get_call_trampoline_func(zend_class_entry * ce,zend_string * method_name,int is_static)1128 ZEND_API zend_function *zend_get_call_trampoline_func(zend_class_entry *ce, zend_string *method_name, int is_static) /* {{{ */
1129 {
1130 	size_t mname_len;
1131 	zend_op_array *func;
1132 	zend_function *fbc = is_static ? ce->__callstatic : ce->__call;
1133 
1134 	ZEND_ASSERT(fbc);
1135 
1136 	if (EXPECTED(EG(trampoline).common.function_name == NULL)) {
1137 		func = &EG(trampoline).op_array;
1138 	} else {
1139 		func = ecalloc(1, sizeof(zend_op_array));
1140 	}
1141 
1142 	func->type = ZEND_USER_FUNCTION;
1143 	func->arg_flags[0] = 0;
1144 	func->arg_flags[1] = 0;
1145 	func->arg_flags[2] = 0;
1146 	func->fn_flags = ZEND_ACC_CALL_VIA_TRAMPOLINE | ZEND_ACC_PUBLIC;
1147 	if (is_static) {
1148 		func->fn_flags |= ZEND_ACC_STATIC;
1149 	}
1150 	func->opcodes = &EG(call_trampoline_op);
1151 
1152 	func->prototype = fbc;
1153 	func->scope = fbc->common.scope;
1154 	/* reserve space for arguments, local and temorary variables */
1155 	func->T = (fbc->type == ZEND_USER_FUNCTION)? MAX(fbc->op_array.last_var + fbc->op_array.T, 2) : 2;
1156 	func->filename = (fbc->type == ZEND_USER_FUNCTION)? fbc->op_array.filename : ZSTR_EMPTY_ALLOC();
1157 	func->line_start = (fbc->type == ZEND_USER_FUNCTION)? fbc->op_array.line_start : 0;
1158 	func->line_end = (fbc->type == ZEND_USER_FUNCTION)? fbc->op_array.line_end : 0;
1159 
1160 	//??? keep compatibility for "\0" characters
1161 	//??? see: Zend/tests/bug46238.phpt
1162 	if (UNEXPECTED((mname_len = strlen(ZSTR_VAL(method_name))) != ZSTR_LEN(method_name))) {
1163 		func->function_name = zend_string_init(ZSTR_VAL(method_name), mname_len, 0);
1164 	} else {
1165 		func->function_name = zend_string_copy(method_name);
1166 	}
1167 
1168 	return (zend_function*)func;
1169 }
1170 /* }}} */
1171 
zend_get_user_call_function(zend_class_entry * ce,zend_string * method_name)1172 static zend_always_inline zend_function *zend_get_user_call_function(zend_class_entry *ce, zend_string *method_name) /* {{{ */
1173 {
1174 	return zend_get_call_trampoline_func(ce, method_name, 0);
1175 }
1176 /* }}} */
1177 
zend_std_get_method(zend_object ** obj_ptr,zend_string * method_name,const zval * key)1178 static union _zend_function *zend_std_get_method(zend_object **obj_ptr, zend_string *method_name, const zval *key) /* {{{ */
1179 {
1180 	zend_object *zobj = *obj_ptr;
1181 	zval *func;
1182 	zend_function *fbc;
1183 	zend_string *lc_method_name;
1184 	zend_class_entry *scope = NULL;
1185 	ALLOCA_FLAG(use_heap);
1186 
1187 	if (EXPECTED(key != NULL)) {
1188 		lc_method_name = Z_STR_P(key);
1189 #ifdef ZEND_ALLOCA_MAX_SIZE
1190 		use_heap = 0;
1191 #endif
1192 	} else {
1193 		ZSTR_ALLOCA_ALLOC(lc_method_name, ZSTR_LEN(method_name), use_heap);
1194 		zend_str_tolower_copy(ZSTR_VAL(lc_method_name), ZSTR_VAL(method_name), ZSTR_LEN(method_name));
1195 	}
1196 
1197 	if (UNEXPECTED((func = zend_hash_find(&zobj->ce->function_table, lc_method_name)) == NULL)) {
1198 		if (UNEXPECTED(!key)) {
1199 			ZSTR_ALLOCA_FREE(lc_method_name, use_heap);
1200 		}
1201 		if (zobj->ce->__call) {
1202 			return zend_get_user_call_function(zobj->ce, method_name);
1203 		} else {
1204 			return NULL;
1205 		}
1206 	}
1207 
1208 	fbc = Z_FUNC_P(func);
1209 	/* Check access level */
1210 	if (fbc->op_array.fn_flags & ZEND_ACC_PRIVATE) {
1211 		zend_function *updated_fbc;
1212 
1213 		/* Ensure that if we're calling a private function, we're allowed to do so.
1214 		 * If we're not and __call() handler exists, invoke it, otherwise error out.
1215 		 */
1216 		updated_fbc = zend_check_private_int(fbc, zobj->ce, lc_method_name);
1217 		if (EXPECTED(updated_fbc != NULL)) {
1218 			fbc = updated_fbc;
1219 		} else {
1220 			if (zobj->ce->__call) {
1221 				fbc = zend_get_user_call_function(zobj->ce, method_name);
1222 			} else {
1223 				scope = zend_get_executed_scope();
1224 				zend_throw_error(NULL, "Call to %s method %s::%s() from context '%s'", zend_visibility_string(fbc->common.fn_flags), ZEND_FN_SCOPE_NAME(fbc), ZSTR_VAL(method_name), scope ? ZSTR_VAL(scope->name) : "");
1225 				fbc = NULL;
1226 			}
1227 		}
1228 	} else {
1229 		/* Ensure that we haven't overridden a private function and end up calling
1230 		 * the overriding public function...
1231 		 */
1232 		if (fbc->op_array.fn_flags & (ZEND_ACC_CHANGED|ZEND_ACC_PROTECTED)) {
1233 			scope = zend_get_executed_scope();
1234 		}
1235 		if (fbc->op_array.fn_flags & ZEND_ACC_CHANGED) {
1236 			if (scope && is_derived_class(fbc->common.scope, scope)) {
1237 				if ((func = zend_hash_find(&scope->function_table, lc_method_name)) != NULL) {
1238 					zend_function *priv_fbc = Z_FUNC_P(func);
1239 					if (priv_fbc->common.fn_flags & ZEND_ACC_PRIVATE
1240 						&& priv_fbc->common.scope == scope) {
1241 						fbc = priv_fbc;
1242 					}
1243 				}
1244 			}
1245 		}
1246 		if (fbc->common.fn_flags & ZEND_ACC_PROTECTED) {
1247 			/* Ensure that if we're calling a protected function, we're allowed to do so.
1248 			 * If we're not and __call() handler exists, invoke it, otherwise error out.
1249 			 */
1250 			if (UNEXPECTED(!zend_check_protected(zend_get_function_root_class(fbc), scope))) {
1251 				if (zobj->ce->__call) {
1252 					fbc = zend_get_user_call_function(zobj->ce, method_name);
1253 				} else {
1254 					zend_throw_error(NULL, "Call to %s method %s::%s() from context '%s'", zend_visibility_string(fbc->common.fn_flags), ZEND_FN_SCOPE_NAME(fbc), ZSTR_VAL(method_name), scope ? ZSTR_VAL(scope->name) : "");
1255 					fbc = NULL;
1256 				}
1257 			}
1258 		}
1259 	}
1260 
1261 	if (UNEXPECTED(!key)) {
1262 		ZSTR_ALLOCA_FREE(lc_method_name, use_heap);
1263 	}
1264 	return fbc;
1265 }
1266 /* }}} */
1267 
zend_get_user_callstatic_function(zend_class_entry * ce,zend_string * method_name)1268 static zend_always_inline zend_function *zend_get_user_callstatic_function(zend_class_entry *ce, zend_string *method_name) /* {{{ */
1269 {
1270 	return zend_get_call_trampoline_func(ce, method_name, 1);
1271 }
1272 /* }}} */
1273 
zend_std_get_static_method(zend_class_entry * ce,zend_string * function_name,const zval * key)1274 ZEND_API zend_function *zend_std_get_static_method(zend_class_entry *ce, zend_string *function_name, const zval *key) /* {{{ */
1275 {
1276 	zend_function *fbc = NULL;
1277 	zend_string *lc_function_name;
1278 	zend_object *object;
1279 	zend_class_entry *scope;
1280 
1281 	if (EXPECTED(key != NULL)) {
1282 		lc_function_name = Z_STR_P(key);
1283 	} else {
1284 		lc_function_name = zend_string_tolower(function_name);
1285 	}
1286 
1287 	do {
1288 		zval *func = zend_hash_find(&ce->function_table, lc_function_name);
1289 		if (EXPECTED(func != NULL)) {
1290 			fbc = Z_FUNC_P(func);
1291 		} else if (ce->constructor
1292 			&& ZSTR_LEN(lc_function_name) == ZSTR_LEN(ce->name)
1293 			&& zend_binary_strncasecmp(ZSTR_VAL(lc_function_name), ZSTR_LEN(lc_function_name), ZSTR_VAL(ce->name), ZSTR_LEN(lc_function_name), ZSTR_LEN(lc_function_name)) == 0
1294 			/* Only change the method to the constructor if the constructor isn't called __construct
1295 			 * we check for __ so we can be binary safe for lowering, we should use ZEND_CONSTRUCTOR_FUNC_NAME
1296 			 */
1297 			&& (ZSTR_VAL(ce->constructor->common.function_name)[0] != '_'
1298 				|| ZSTR_VAL(ce->constructor->common.function_name)[1] != '_')) {
1299 			fbc = ce->constructor;
1300 		} else {
1301 			if (UNEXPECTED(!key)) {
1302 				zend_string_release(lc_function_name);
1303 			}
1304 			if (ce->__call &&
1305 				(object = zend_get_this_object(EG(current_execute_data))) != NULL &&
1306 			    instanceof_function(object->ce, ce)) {
1307 				/* Call the top-level defined __call().
1308 				 * see: tests/classes/__call_004.phpt  */
1309 
1310 				zend_class_entry *call_ce = object->ce;
1311 
1312 				while (!call_ce->__call) {
1313 					call_ce = call_ce->parent;
1314 				}
1315 				return zend_get_user_call_function(call_ce, function_name);
1316 			} else if (ce->__callstatic) {
1317 				return zend_get_user_callstatic_function(ce, function_name);
1318 			} else {
1319 	   			return NULL;
1320 			}
1321 		}
1322 	} while (0);
1323 
1324 #if MBO_0
1325 	/* right now this function is used for non static method lookup too */
1326 	/* Is the function static */
1327 	if (UNEXPECTED(!(fbc->common.fn_flags & ZEND_ACC_STATIC))) {
1328 		zend_error_noreturn(E_ERROR, "Cannot call non static method %s::%s() without object", ZEND_FN_SCOPE_NAME(fbc), ZSTR_VAL(fbc->common.function_name));
1329 	}
1330 #endif
1331 	if (fbc->op_array.fn_flags & ZEND_ACC_PUBLIC) {
1332 		/* No further checks necessary, most common case */
1333 	} else if (fbc->op_array.fn_flags & ZEND_ACC_PRIVATE) {
1334 		zend_function *updated_fbc;
1335 
1336 		/* Ensure that if we're calling a private function, we're allowed to do so.
1337 		 */
1338 		scope = zend_get_executed_scope();
1339 		updated_fbc = zend_check_private_int(fbc, scope, lc_function_name);
1340 		if (EXPECTED(updated_fbc != NULL)) {
1341 			fbc = updated_fbc;
1342 		} else {
1343 			if (ce->__callstatic) {
1344 				fbc = zend_get_user_callstatic_function(ce, function_name);
1345 			} else {
1346 				zend_throw_error(NULL, "Call to %s method %s::%s() from context '%s'", zend_visibility_string(fbc->common.fn_flags), ZEND_FN_SCOPE_NAME(fbc), ZSTR_VAL(function_name), scope ? ZSTR_VAL(scope->name) : "");
1347 				fbc = NULL;
1348 			}
1349 		}
1350 	} else if ((fbc->common.fn_flags & ZEND_ACC_PROTECTED)) {
1351 		/* Ensure that if we're calling a protected function, we're allowed to do so.
1352 		 */
1353 		scope = zend_get_executed_scope();
1354 		if (UNEXPECTED(!zend_check_protected(zend_get_function_root_class(fbc), scope))) {
1355 			if (ce->__callstatic) {
1356 				fbc = zend_get_user_callstatic_function(ce, function_name);
1357 			} else {
1358 				zend_throw_error(NULL, "Call to %s method %s::%s() from context '%s'", zend_visibility_string(fbc->common.fn_flags), ZEND_FN_SCOPE_NAME(fbc), ZSTR_VAL(function_name), scope ? ZSTR_VAL(scope->name) : "");
1359 				fbc = NULL;
1360 			}
1361 		}
1362 	}
1363 
1364 	if (UNEXPECTED(!key)) {
1365 		zend_string_release(lc_function_name);
1366 	}
1367 
1368 	return fbc;
1369 }
1370 /* }}} */
1371 
zend_std_get_static_property(zend_class_entry * ce,zend_string * property_name,zend_bool silent)1372 ZEND_API zval *zend_std_get_static_property(zend_class_entry *ce, zend_string *property_name, zend_bool silent) /* {{{ */
1373 {
1374 	zend_property_info *property_info = zend_hash_find_ptr(&ce->properties_info, property_name);
1375 	zval *ret;
1376 
1377 	if (UNEXPECTED(property_info == NULL)) {
1378 		goto undeclared_property;
1379 	}
1380 
1381 	if (UNEXPECTED(!zend_verify_property_access(property_info, ce))) {
1382 		if (!silent) {
1383 			zend_throw_error(NULL, "Cannot access %s property %s::$%s", zend_visibility_string(property_info->flags), ZSTR_VAL(ce->name), ZSTR_VAL(property_name));
1384 		}
1385 		return NULL;
1386 	}
1387 
1388 	if (UNEXPECTED((property_info->flags & ZEND_ACC_STATIC) == 0)) {
1389 		goto undeclared_property;
1390 	}
1391 
1392 	if (UNEXPECTED(!(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED))) {
1393 		if (UNEXPECTED(zend_update_class_constants(ce)) != SUCCESS) {
1394 			return NULL;
1395 		}
1396 	}
1397 	ret = CE_STATIC_MEMBERS(ce) + property_info->offset;
1398 
1399 	/* check if static properties were destoyed */
1400 	if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
1401 undeclared_property:
1402 		if (!silent) {
1403 			zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(property_name));
1404 		}
1405 		ret = NULL;
1406 	}
1407 
1408 	return ret;
1409 }
1410 /* }}} */
1411 
zend_std_unset_static_property(zend_class_entry * ce,zend_string * property_name)1412 ZEND_API ZEND_COLD zend_bool zend_std_unset_static_property(zend_class_entry *ce, zend_string *property_name) /* {{{ */
1413 {
1414 	zend_throw_error(NULL, "Attempt to unset static property %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(property_name));
1415 	return 0;
1416 }
1417 /* }}} */
1418 
zend_std_get_constructor(zend_object * zobj)1419 ZEND_API union _zend_function *zend_std_get_constructor(zend_object *zobj) /* {{{ */
1420 {
1421 	zend_function *constructor = zobj->ce->constructor;
1422 	zend_class_entry *scope;
1423 
1424 	if (constructor) {
1425 		if (constructor->op_array.fn_flags & ZEND_ACC_PUBLIC) {
1426 			/* No further checks necessary */
1427 		} else if (constructor->op_array.fn_flags & ZEND_ACC_PRIVATE) {
1428 			/* Ensure that if we're calling a private function, we're allowed to do so.
1429 			 */
1430 			if (EG(fake_scope)) {
1431 				scope = EG(fake_scope);
1432 			} else {
1433 				scope = zend_get_executed_scope();
1434 			}
1435 			if (UNEXPECTED(constructor->common.scope != scope)) {
1436 				if (scope) {
1437 					zend_throw_error(NULL, "Call to private %s::%s() from context '%s'", ZSTR_VAL(constructor->common.scope->name), ZSTR_VAL(constructor->common.function_name), ZSTR_VAL(scope->name));
1438 					constructor = NULL;
1439 				} else {
1440 					zend_throw_error(NULL, "Call to private %s::%s() from invalid context", ZSTR_VAL(constructor->common.scope->name), ZSTR_VAL(constructor->common.function_name));
1441 					constructor = NULL;
1442 				}
1443 			}
1444 		} else if ((constructor->common.fn_flags & ZEND_ACC_PROTECTED)) {
1445 			/* Ensure that if we're calling a protected function, we're allowed to do so.
1446 			 * Constructors only have prototype if they are defined by an interface but
1447 			 * it is the compilers responsibility to take care of the prototype.
1448 			 */
1449 			if (EG(fake_scope)) {
1450 				scope = EG(fake_scope);
1451 			} else {
1452 				scope = zend_get_executed_scope();
1453 			}
1454 			if (UNEXPECTED(!zend_check_protected(zend_get_function_root_class(constructor), scope))) {
1455 				if (scope) {
1456 					zend_throw_error(NULL, "Call to protected %s::%s() from context '%s'", ZSTR_VAL(constructor->common.scope->name), ZSTR_VAL(constructor->common.function_name), ZSTR_VAL(scope->name));
1457 					constructor = NULL;
1458 				} else {
1459 					zend_throw_error(NULL, "Call to protected %s::%s() from invalid context", ZSTR_VAL(constructor->common.scope->name), ZSTR_VAL(constructor->common.function_name));
1460 					constructor = NULL;
1461 				}
1462 			}
1463 		}
1464 	}
1465 
1466 	return constructor;
1467 }
1468 /* }}} */
1469 
zend_std_compare_objects(zval * o1,zval * o2)1470 static int zend_std_compare_objects(zval *o1, zval *o2) /* {{{ */
1471 {
1472 	zend_object *zobj1, *zobj2;
1473 
1474 	zobj1 = Z_OBJ_P(o1);
1475 	zobj2 = Z_OBJ_P(o2);
1476 
1477 	if (zobj1->ce != zobj2->ce) {
1478 		return 1; /* different classes */
1479 	}
1480 	if (!zobj1->properties && !zobj2->properties) {
1481 		zval *p1, *p2, *end;
1482 
1483 		if (!zobj1->ce->default_properties_count) {
1484 			return 0;
1485 		}
1486 		p1 = zobj1->properties_table;
1487 		p2 = zobj2->properties_table;
1488 		end = p1 + zobj1->ce->default_properties_count;
1489 		Z_OBJ_PROTECT_RECURSION(o1);
1490 		Z_OBJ_PROTECT_RECURSION(o2);
1491 		do {
1492 			if (Z_TYPE_P(p1) != IS_UNDEF) {
1493 				if (Z_TYPE_P(p2) != IS_UNDEF) {
1494 					zval result;
1495 
1496 					if (compare_function(&result, p1, p2)==FAILURE) {
1497 						Z_OBJ_UNPROTECT_RECURSION(o1);
1498 						Z_OBJ_UNPROTECT_RECURSION(o2);
1499 						return 1;
1500 					}
1501 					if (Z_LVAL(result) != 0) {
1502 						Z_OBJ_UNPROTECT_RECURSION(o1);
1503 						Z_OBJ_UNPROTECT_RECURSION(o2);
1504 						return Z_LVAL(result);
1505 					}
1506 				} else {
1507 					Z_OBJ_UNPROTECT_RECURSION(o1);
1508 					Z_OBJ_UNPROTECT_RECURSION(o2);
1509 					return 1;
1510 				}
1511 			} else {
1512 				if (Z_TYPE_P(p2) != IS_UNDEF) {
1513 					Z_OBJ_UNPROTECT_RECURSION(o1);
1514 					Z_OBJ_UNPROTECT_RECURSION(o2);
1515 					return 1;
1516 				}
1517 			}
1518 			p1++;
1519 			p2++;
1520 		} while (p1 != end);
1521 		Z_OBJ_UNPROTECT_RECURSION(o1);
1522 		Z_OBJ_UNPROTECT_RECURSION(o2);
1523 		return 0;
1524 	} else {
1525 		if (!zobj1->properties) {
1526 			rebuild_object_properties(zobj1);
1527 		}
1528 		if (!zobj2->properties) {
1529 			rebuild_object_properties(zobj2);
1530 		}
1531 		return zend_compare_symbol_tables(zobj1->properties, zobj2->properties);
1532 	}
1533 }
1534 /* }}} */
1535 
zend_std_has_property(zval * object,zval * member,int has_set_exists,void ** cache_slot)1536 static int zend_std_has_property(zval *object, zval *member, int has_set_exists, void **cache_slot) /* {{{ */
1537 {
1538 	zend_object *zobj;
1539 	int result;
1540 	zval *value = NULL;
1541 	zval tmp_member;
1542 	uint32_t property_offset;
1543 
1544 	zobj = Z_OBJ_P(object);
1545 
1546 	ZVAL_UNDEF(&tmp_member);
1547 	if (UNEXPECTED(Z_TYPE_P(member) != IS_STRING)) {
1548 		ZVAL_STR(&tmp_member, zval_get_string(member));
1549 		member = &tmp_member;
1550 		cache_slot = NULL;
1551 	}
1552 
1553 	property_offset = zend_get_property_offset(zobj->ce, Z_STR_P(member), 1, cache_slot);
1554 
1555 	if (EXPECTED(property_offset != ZEND_WRONG_PROPERTY_OFFSET)) {
1556 		if (EXPECTED(property_offset != ZEND_DYNAMIC_PROPERTY_OFFSET)) {
1557 			value = OBJ_PROP(zobj, property_offset);
1558 			if (Z_TYPE_P(value) != IS_UNDEF) {
1559 				goto found;
1560 			}
1561 		} else if (EXPECTED(zobj->properties != NULL) &&
1562 		           (value = zend_hash_find(zobj->properties, Z_STR_P(member))) != NULL) {
1563 found:
1564 			switch (has_set_exists) {
1565 				case 0:
1566 					ZVAL_DEREF(value);
1567 					result = (Z_TYPE_P(value) != IS_NULL);
1568 					break;
1569 				default:
1570 					result = zend_is_true(value);
1571 					break;
1572 				case 2:
1573 					result = 1;
1574 					break;
1575 			}
1576 			goto exit;
1577 		}
1578 	} else if (UNEXPECTED(EG(exception))) {
1579 		result = 0;
1580 		goto exit;
1581 	}
1582 
1583 	result = 0;
1584 	if ((has_set_exists != 2) && zobj->ce->__isset) {
1585 		uint32_t *guard = zend_get_property_guard(zobj, Z_STR_P(member));
1586 
1587 		if (!((*guard) & IN_ISSET)) {
1588 			zval rv;
1589 			zval tmp_object;
1590 
1591 			/* have issetter - try with it! */
1592 			if (Z_TYPE(tmp_member) == IS_UNDEF) {
1593 				ZVAL_COPY(&tmp_member, member);
1594 				member = &tmp_member;
1595 			}
1596 			ZVAL_COPY(&tmp_object, object);
1597 			(*guard) |= IN_ISSET; /* prevent circular getting */
1598 			zend_std_call_issetter(&tmp_object, member, &rv);
1599 			if (Z_TYPE(rv) != IS_UNDEF) {
1600 				result = zend_is_true(&rv);
1601 				zval_ptr_dtor(&rv);
1602 				if (has_set_exists && result) {
1603 					if (EXPECTED(!EG(exception)) && zobj->ce->__get && !((*guard) & IN_GET)) {
1604 						(*guard) |= IN_GET;
1605 						zend_std_call_getter(&tmp_object, member, &rv);
1606 						(*guard) &= ~IN_GET;
1607 						if (Z_TYPE(rv) != IS_UNDEF) {
1608 							result = i_zend_is_true(&rv);
1609 							zval_ptr_dtor(&rv);
1610 						} else {
1611 							result = 0;
1612 						}
1613 					} else {
1614 						result = 0;
1615 					}
1616 				}
1617 			}
1618 			(*guard) &= ~IN_ISSET;
1619 			zval_ptr_dtor(&tmp_object);
1620 		}
1621 	}
1622 
1623 exit:
1624 	if (UNEXPECTED(Z_REFCOUNTED(tmp_member))) {
1625 		zval_ptr_dtor(&tmp_member);
1626 	}
1627 	return result;
1628 }
1629 /* }}} */
1630 
zend_std_object_get_class_name(const zend_object * zobj)1631 zend_string *zend_std_object_get_class_name(const zend_object *zobj) /* {{{ */
1632 {
1633 	return zend_string_copy(zobj->ce->name);
1634 }
1635 /* }}} */
1636 
zend_std_cast_object_tostring(zval * readobj,zval * writeobj,int type)1637 ZEND_API int zend_std_cast_object_tostring(zval *readobj, zval *writeobj, int type) /* {{{ */
1638 {
1639 	zval retval;
1640 	zend_class_entry *ce;
1641 
1642 	switch (type) {
1643 		case IS_STRING:
1644 			ce = Z_OBJCE_P(readobj);
1645 			if (ce->__tostring &&
1646 				(zend_call_method_with_0_params(readobj, ce, &ce->__tostring, "__tostring", &retval) || EG(exception))) {
1647 				if (UNEXPECTED(EG(exception) != NULL)) {
1648 					zval *msg, ex, rv;
1649 					zval_ptr_dtor(&retval);
1650 					ZVAL_OBJ(&ex, EG(exception));
1651 					EG(exception) = NULL;
1652 					msg = zend_read_property(Z_OBJCE(ex), &ex, "message", sizeof("message") - 1, 1, &rv);
1653 					if (UNEXPECTED(Z_TYPE_P(msg) != IS_STRING)) {
1654 						ZVAL_EMPTY_STRING(&rv);
1655 						msg = &rv;
1656 					}
1657 					zend_error_noreturn(E_ERROR,
1658 							"Method %s::__toString() must not throw an exception, caught %s: %s",
1659 							ZSTR_VAL(ce->name), ZSTR_VAL(Z_OBJCE(ex)->name), Z_STRVAL_P(msg));
1660 					return FAILURE;
1661 				}
1662 				if (EXPECTED(Z_TYPE(retval) == IS_STRING)) {
1663 					if (readobj == writeobj) {
1664 						zval_ptr_dtor(readobj);
1665 					}
1666 					ZVAL_COPY_VALUE(writeobj, &retval);
1667 					return SUCCESS;
1668 				} else {
1669 					zval_ptr_dtor(&retval);
1670 					if (readobj == writeobj) {
1671 						zval_ptr_dtor(readobj);
1672 					}
1673 					ZVAL_EMPTY_STRING(writeobj);
1674 					zend_error(E_RECOVERABLE_ERROR, "Method %s::__toString() must return a string value", ZSTR_VAL(ce->name));
1675 					return SUCCESS;
1676 				}
1677 			}
1678 			return FAILURE;
1679 		case _IS_BOOL:
1680 			ZVAL_BOOL(writeobj, 1);
1681 			return SUCCESS;
1682 		case IS_LONG:
1683 			ce = Z_OBJCE_P(readobj);
1684 			zend_error(E_NOTICE, "Object of class %s could not be converted to int", ZSTR_VAL(ce->name));
1685 			if (readobj == writeobj) {
1686 				zval_dtor(readobj);
1687 			}
1688 			ZVAL_LONG(writeobj, 1);
1689 			return SUCCESS;
1690 		case IS_DOUBLE:
1691 			ce = Z_OBJCE_P(readobj);
1692 			zend_error(E_NOTICE, "Object of class %s could not be converted to float", ZSTR_VAL(ce->name));
1693 			if (readobj == writeobj) {
1694 				zval_dtor(readobj);
1695 			}
1696 			ZVAL_DOUBLE(writeobj, 1);
1697 			return SUCCESS;
1698 		default:
1699 			ZVAL_NULL(writeobj);
1700 			break;
1701 	}
1702 	return FAILURE;
1703 }
1704 /* }}} */
1705 
zend_std_get_closure(zval * obj,zend_class_entry ** ce_ptr,zend_function ** fptr_ptr,zend_object ** obj_ptr)1706 int zend_std_get_closure(zval *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr) /* {{{ */
1707 {
1708 	zval *func;
1709 	zend_class_entry *ce;
1710 
1711 	if (Z_TYPE_P(obj) != IS_OBJECT) {
1712 		return FAILURE;
1713 	}
1714 
1715 	ce = Z_OBJCE_P(obj);
1716 
1717 	if ((func = zend_hash_find(&ce->function_table, ZSTR_KNOWN(ZEND_STR_MAGIC_INVOKE))) == NULL) {
1718 		return FAILURE;
1719 	}
1720 	*fptr_ptr = Z_FUNC_P(func);
1721 
1722 	*ce_ptr = ce;
1723 	if ((*fptr_ptr)->common.fn_flags & ZEND_ACC_STATIC) {
1724 		if (obj_ptr) {
1725 			*obj_ptr = NULL;
1726 		}
1727 	} else {
1728 		if (obj_ptr) {
1729 			*obj_ptr = Z_OBJ_P(obj);
1730 		}
1731 	}
1732 	return SUCCESS;
1733 }
1734 /* }}} */
1735 
1736 ZEND_API zend_object_handlers std_object_handlers = {
1737 	0,										/* offset */
1738 
1739 	zend_object_std_dtor,					/* free_obj */
1740 	zend_objects_destroy_object,			/* dtor_obj */
1741 	zend_objects_clone_obj,					/* clone_obj */
1742 
1743 	zend_std_read_property,					/* read_property */
1744 	zend_std_write_property,				/* write_property */
1745 	zend_std_read_dimension,				/* read_dimension */
1746 	zend_std_write_dimension,				/* write_dimension */
1747 	zend_std_get_property_ptr_ptr,			/* get_property_ptr_ptr */
1748 	NULL,									/* get */
1749 	NULL,									/* set */
1750 	zend_std_has_property,					/* has_property */
1751 	zend_std_unset_property,				/* unset_property */
1752 	zend_std_has_dimension,					/* has_dimension */
1753 	zend_std_unset_dimension,				/* unset_dimension */
1754 	zend_std_get_properties,				/* get_properties */
1755 	zend_std_get_method,					/* get_method */
1756 	NULL,									/* call_method */
1757 	zend_std_get_constructor,				/* get_constructor */
1758 	zend_std_object_get_class_name,			/* get_class_name */
1759 	zend_std_compare_objects,				/* compare_objects */
1760 	zend_std_cast_object_tostring,			/* cast_object */
1761 	NULL,									/* count_elements */
1762 	zend_std_get_debug_info,				/* get_debug_info */
1763 	zend_std_get_closure,					/* get_closure */
1764 	zend_std_get_gc,						/* get_gc */
1765 	NULL,									/* do_operation */
1766 	NULL,									/* compare */
1767 };
1768 
1769 /*
1770  * Local variables:
1771  * tab-width: 4
1772  * c-basic-offset: 4
1773  * indent-tabs-mode: t
1774  * End:
1775  * vim600: sw=4 ts=4 fdm=marker
1776  * vim<600: sw=4 ts=4
1777  */
1778