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