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