xref: /PHP-5.3/Zend/zend_object_handlers.c (revision 33b104c7)
1 /*
2    +----------------------------------------------------------------------+
3    | Zend Engine                                                          |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1998-2013 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    +----------------------------------------------------------------------+
18 */
19 
20 /* $Id$ */
21 
22 #include "zend.h"
23 #include "zend_globals.h"
24 #include "zend_variables.h"
25 #include "zend_API.h"
26 #include "zend_objects.h"
27 #include "zend_objects_API.h"
28 #include "zend_object_handlers.h"
29 #include "zend_interfaces.h"
30 #include "zend_closures.h"
31 #include "zend_compile.h"
32 
33 #define DEBUG_OBJECT_HANDLERS 0
34 
35 #define Z_OBJ_P(zval_p) zend_objects_get_address(zval_p TSRMLS_CC)
36 
37 /*
38   __X accessors explanation:
39 
40   if we have __get and property that is not part of the properties array is
41   requested, we call __get handler. If it fails, we return uninitialized.
42 
43   if we have __set and property that is not part of the properties array is
44   set, we call __set handler. If it fails, we do not change the array.
45 
46   for both handlers above, when we are inside __get/__set, no further calls for
47   __get/__set for this property of this object will be made, to prevent endless
48   recursion and enable accessors to change properties array.
49 
50   if we have __call and method which is not part of the class function table is
51   called, we cal __call handler.
52 */
53 
zend_std_get_properties(zval * object TSRMLS_DC)54 ZEND_API HashTable *zend_std_get_properties(zval *object TSRMLS_DC) /* {{{ */
55 {
56 	zend_object *zobj;
57 	zobj = Z_OBJ_P(object);
58 	return zobj->properties;
59 }
60 /* }}} */
61 
zend_std_get_debug_info(zval * object,int * is_temp TSRMLS_DC)62 ZEND_API HashTable *zend_std_get_debug_info(zval *object, int *is_temp TSRMLS_DC) /* {{{ */
63 {
64 	*is_temp = 0;
65 	return zend_std_get_properties(object TSRMLS_CC);
66 }
67 /* }}} */
68 
zend_std_call_getter(zval * object,zval * member TSRMLS_DC)69 static zval *zend_std_call_getter(zval *object, zval *member TSRMLS_DC) /* {{{ */
70 {
71 	zval *retval = NULL;
72 	zend_class_entry *ce = Z_OBJCE_P(object);
73 
74 	/* __get handler is called with one argument:
75 	      property name
76 
77 	   it should return whether the call was successfull or not
78 	*/
79 
80 	SEPARATE_ARG_IF_REF(member);
81 
82 	zend_call_method_with_1_params(&object, ce, &ce->__get, ZEND_GET_FUNC_NAME, &retval, member);
83 
84 	zval_ptr_dtor(&member);
85 
86 	if (retval) {
87 		Z_DELREF_P(retval);
88 	}
89 
90 	return retval;
91 }
92 /* }}} */
93 
zend_std_call_setter(zval * object,zval * member,zval * value TSRMLS_DC)94 static int zend_std_call_setter(zval *object, zval *member, zval *value TSRMLS_DC) /* {{{ */
95 {
96 	zval *retval = NULL;
97 	int result;
98 	zend_class_entry *ce = Z_OBJCE_P(object);
99 
100 	SEPARATE_ARG_IF_REF(member);
101 	Z_ADDREF_P(value);
102 
103 	/* __set handler is called with two arguments:
104 	     property name
105 	     value to be set
106 
107 	   it should return whether the call was successfull or not
108 	*/
109 	zend_call_method_with_2_params(&object, ce, &ce->__set, ZEND_SET_FUNC_NAME, &retval, member, value);
110 
111 	zval_ptr_dtor(&member);
112 	zval_ptr_dtor(&value);
113 
114 	if (retval) {
115 		result = i_zend_is_true(retval) ? SUCCESS : FAILURE;
116 		zval_ptr_dtor(&retval);
117 		return result;
118 	} else {
119 		return FAILURE;
120 	}
121 }
122 /* }}} */
123 
zend_std_call_unsetter(zval * object,zval * member TSRMLS_DC)124 static void zend_std_call_unsetter(zval *object, zval *member TSRMLS_DC) /* {{{ */
125 {
126 	zend_class_entry *ce = Z_OBJCE_P(object);
127 
128 	/* __unset handler is called with one argument:
129 	      property name
130 	*/
131 
132 	SEPARATE_ARG_IF_REF(member);
133 
134 	zend_call_method_with_1_params(&object, ce, &ce->__unset, ZEND_UNSET_FUNC_NAME, NULL, member);
135 
136 	zval_ptr_dtor(&member);
137 }
138 /* }}} */
139 
zend_std_call_issetter(zval * object,zval * member TSRMLS_DC)140 static zval *zend_std_call_issetter(zval *object, zval *member TSRMLS_DC) /* {{{ */
141 {
142 	zval *retval = NULL;
143 	zend_class_entry *ce = Z_OBJCE_P(object);
144 
145 	/* __isset handler is called with one argument:
146 	      property name
147 
148 	   it should return whether the property is set or not
149 	*/
150 
151 	SEPARATE_ARG_IF_REF(member);
152 
153 	zend_call_method_with_1_params(&object, ce, &ce->__isset, ZEND_ISSET_FUNC_NAME, &retval, member);
154 
155 	zval_ptr_dtor(&member);
156 
157 	return retval;
158 }
159 /* }}} */
160 
zend_verify_property_access(zend_property_info * property_info,zend_class_entry * ce TSRMLS_DC)161 static int zend_verify_property_access(zend_property_info *property_info, zend_class_entry *ce TSRMLS_DC) /* {{{ */
162 {
163 	switch (property_info->flags & ZEND_ACC_PPP_MASK) {
164 		case ZEND_ACC_PUBLIC:
165 			return 1;
166 		case ZEND_ACC_PROTECTED:
167 			return zend_check_protected(property_info->ce, EG(scope));
168 		case ZEND_ACC_PRIVATE:
169 			if ((ce==EG(scope) || property_info->ce == EG(scope)) && EG(scope)) {
170 				return 1;
171 			} else {
172 				return 0;
173 			}
174 			break;
175 	}
176 	return 0;
177 }
178 /* }}} */
179 
is_derived_class(zend_class_entry * child_class,zend_class_entry * parent_class)180 static inline zend_bool is_derived_class(zend_class_entry *child_class, zend_class_entry *parent_class) /* {{{ */
181 {
182 	child_class = child_class->parent;
183 	while (child_class) {
184 		if (child_class == parent_class) {
185 			return 1;
186 		}
187 		child_class = child_class->parent;
188 	}
189 
190 	return 0;
191 }
192 /* }}} */
193 
zend_get_property_info(zend_class_entry * ce,zval * member,int silent TSRMLS_DC)194 ZEND_API struct _zend_property_info *zend_get_property_info(zend_class_entry *ce, zval *member, int silent TSRMLS_DC) /* {{{ */
195 {
196 	zend_property_info *property_info = NULL;
197 	zend_property_info *scope_property_info;
198 	zend_bool denied_access = 0;
199 	ulong h;
200 
201 	if (Z_STRVAL_P(member)[0] == '\0') {
202 		if (!silent) {
203 			if (Z_STRLEN_P(member) == 0) {
204 				zend_error(E_ERROR, "Cannot access empty property");
205 			} else {
206 				zend_error(E_ERROR, "Cannot access property started with '\\0'");
207 			}
208 		}
209 		return NULL;
210 	}
211 	h = zend_get_hash_value(Z_STRVAL_P(member), Z_STRLEN_P(member) + 1);
212 	if (zend_hash_quick_find(&ce->properties_info, Z_STRVAL_P(member), Z_STRLEN_P(member)+1, h, (void **) &property_info)==SUCCESS) {
213 		if(property_info->flags & ZEND_ACC_SHADOW) {
214 			/* if it's a shadow - go to access it's private */
215 			property_info = NULL;
216 		} else {
217 			if (zend_verify_property_access(property_info, ce TSRMLS_CC)) {
218 				if (property_info->flags & ZEND_ACC_CHANGED
219 					&& !(property_info->flags & ZEND_ACC_PRIVATE)) {
220 					/* We still need to make sure that we're not in a context
221 					 * where the right property is a different 'statically linked' private
222 					 * continue checking below...
223 					 */
224 				} else {
225 					if (!silent && (property_info->flags & ZEND_ACC_STATIC)) {
226 						zend_error(E_STRICT, "Accessing static property %s::$%s as non static", ce->name, Z_STRVAL_P(member));
227 					}
228 					return property_info;
229 				}
230 			} else {
231 				/* Try to look in the scope instead */
232 				denied_access = 1;
233 			}
234 		}
235 	}
236 	if (EG(scope) != ce
237 		&& is_derived_class(ce, EG(scope))
238 		&& EG(scope)
239 		&& zend_hash_quick_find(&EG(scope)->properties_info, Z_STRVAL_P(member), Z_STRLEN_P(member)+1, h, (void **) &scope_property_info)==SUCCESS
240 		&& scope_property_info->flags & ZEND_ACC_PRIVATE) {
241 		return scope_property_info;
242 	} else if (property_info) {
243 		if (denied_access) {
244 			/* Information was available, but we were denied access.  Error out. */
245 			if (silent) {
246 				return NULL;
247 			}
248 			zend_error(E_ERROR, "Cannot access %s property %s::$%s", zend_visibility_string(property_info->flags), ce->name, Z_STRVAL_P(member));
249 		} else {
250 			/* fall through, return property_info... */
251 		}
252 	} else {
253 		EG(std_property_info).flags = ZEND_ACC_PUBLIC;
254 		EG(std_property_info).name = Z_STRVAL_P(member);
255 		EG(std_property_info).name_length = Z_STRLEN_P(member);
256 		EG(std_property_info).h = h;
257 		EG(std_property_info).ce = ce;
258 		property_info = &EG(std_property_info);
259 	}
260 	return property_info;
261 }
262 /* }}} */
263 
zend_check_property_access(zend_object * zobj,char * prop_info_name,int prop_info_name_len TSRMLS_DC)264 ZEND_API int zend_check_property_access(zend_object *zobj, char *prop_info_name, int prop_info_name_len TSRMLS_DC) /* {{{ */
265 {
266 	zend_property_info *property_info;
267 	char *class_name, *prop_name;
268 	zval member;
269 
270 	zend_unmangle_property_name(prop_info_name, prop_info_name_len, &class_name, &prop_name);
271 	ZVAL_STRING(&member, prop_name, 0);
272 	property_info = zend_get_property_info(zobj->ce, &member, 1 TSRMLS_CC);
273 	if (!property_info) {
274 		return FAILURE;
275 	}
276 	if (class_name && class_name[0] != '*') {
277 		if (!(property_info->flags & ZEND_ACC_PRIVATE)) {
278 			/* we we're looking for a private prop but found a non private one of the same name */
279 			return FAILURE;
280 		} else if (strcmp(prop_info_name+1, property_info->name+1)) {
281 			/* we we're looking for a private prop but found a private one of the same name but another class */
282 			return FAILURE;
283 		}
284 	}
285 	return zend_verify_property_access(property_info, zobj->ce TSRMLS_CC) ? SUCCESS : FAILURE;
286 }
287 /* }}} */
288 
zend_get_property_guard(zend_object * zobj,zend_property_info * property_info,zval * member,zend_guard ** pguard)289 static int zend_get_property_guard(zend_object *zobj, zend_property_info *property_info, zval *member, zend_guard **pguard) /* {{{ */
290 {
291 	zend_property_info info;
292 	zend_guard stub;
293 
294 	if (!property_info) {
295 		property_info = &info;
296 		info.name = Z_STRVAL_P(member);
297 		info.name_length = Z_STRLEN_P(member);
298 		info.h = zend_get_hash_value(Z_STRVAL_P(member), Z_STRLEN_P(member) + 1);
299 	} else if(property_info->name[0] == '\0'){
300 		const char *class_name = NULL, *prop_name = NULL;
301 		zend_unmangle_property_name(property_info->name, property_info->name_length, &class_name, &prop_name);
302 		if(class_name) {
303 			/* use unmangled name for protected properties */
304 			info.name = prop_name;
305 			info.name_length = strlen(prop_name);
306 			info.h = zend_get_hash_value(info.name, info.name_length+1);
307 			property_info = &info;
308 		}
309 	}
310 	if (!zobj->guards) {
311 		ALLOC_HASHTABLE(zobj->guards);
312 		zend_hash_init(zobj->guards, 0, NULL, NULL, 0);
313 	} else if (zend_hash_quick_find(zobj->guards, property_info->name, property_info->name_length+1, property_info->h, (void **) pguard) == SUCCESS) {
314 		return SUCCESS;
315 	}
316 	stub.in_get = 0;
317 	stub.in_set = 0;
318 	stub.in_unset = 0;
319 	stub.in_isset = 0;
320 	return zend_hash_quick_add(zobj->guards, property_info->name, property_info->name_length+1, property_info->h, (void**)&stub, sizeof(stub), (void**) pguard);
321 }
322 /* }}} */
323 
zend_std_read_property(zval * object,zval * member,int type TSRMLS_DC)324 zval *zend_std_read_property(zval *object, zval *member, int type TSRMLS_DC) /* {{{ */
325 {
326 	zend_object *zobj;
327 	zval *tmp_member = NULL;
328 	zval **retval;
329 	zval *rv = NULL;
330 	zend_property_info *property_info;
331 	int silent;
332 
333 	silent = (type == BP_VAR_IS);
334 	zobj = Z_OBJ_P(object);
335 
336  	if (Z_TYPE_P(member) != IS_STRING) {
337  		ALLOC_ZVAL(tmp_member);
338 		*tmp_member = *member;
339 		INIT_PZVAL(tmp_member);
340 		zval_copy_ctor(tmp_member);
341 		convert_to_string(tmp_member);
342 		member = tmp_member;
343 	}
344 
345 #if DEBUG_OBJECT_HANDLERS
346 	fprintf(stderr, "Read object #%d property: %s\n", Z_OBJ_HANDLE_P(object), Z_STRVAL_P(member));
347 #endif
348 
349 	/* make zend_get_property_info silent if we have getter - we may want to use it */
350 	property_info = zend_get_property_info(zobj->ce, member, (zobj->ce->__get != NULL) TSRMLS_CC);
351 
352 	if (!property_info || zend_hash_quick_find(zobj->properties, property_info->name, property_info->name_length+1, property_info->h, (void **) &retval) == FAILURE) {
353 		zend_guard *guard = NULL;
354 
355 		if (zobj->ce->__get &&
356 		    zend_get_property_guard(zobj, property_info, member, &guard) == SUCCESS &&
357 		    !guard->in_get) {
358 			/* have getter - try with it! */
359 			Z_ADDREF_P(object);
360 			if (PZVAL_IS_REF(object)) {
361 				SEPARATE_ZVAL(&object);
362 			}
363 			guard->in_get = 1; /* prevent circular getting */
364 			rv = zend_std_call_getter(object, member TSRMLS_CC);
365 			guard->in_get = 0;
366 
367 			if (rv) {
368 				retval = &rv;
369 				if (!Z_ISREF_P(rv) &&
370 				    (type == BP_VAR_W || type == BP_VAR_RW  || type == BP_VAR_UNSET)) {
371 					if (Z_REFCOUNT_P(rv) > 0) {
372 						zval *tmp = rv;
373 
374 						ALLOC_ZVAL(rv);
375 						*rv = *tmp;
376 						zval_copy_ctor(rv);
377 						Z_UNSET_ISREF_P(rv);
378 						Z_SET_REFCOUNT_P(rv, 0);
379 					}
380 					if (Z_TYPE_P(rv) != IS_OBJECT) {
381 						zend_error(E_NOTICE, "Indirect modification of overloaded property %s::$%s has no effect", zobj->ce->name, Z_STRVAL_P(member));
382 					}
383 				}
384 			} else {
385 				retval = &EG(uninitialized_zval_ptr);
386 			}
387 			if (EXPECTED(*retval != object)) {
388 				zval_ptr_dtor(&object);
389 			} else {
390 				Z_DELREF_P(object);
391 			}
392 		} else {
393 			if (zobj->ce->__get && guard && guard->in_get == 1) {
394 				if (Z_STRVAL_P(member)[0] == '\0') {
395 					if (Z_STRLEN_P(member) == 0) {
396 						zend_error(E_ERROR, "Cannot access empty property");
397 					} else {
398 						zend_error(E_ERROR, "Cannot access property started with '\\0'");
399 					}
400 				}
401 			}
402 			if (!silent) {
403 				zend_error(E_NOTICE,"Undefined property: %s::$%s", zobj->ce->name, Z_STRVAL_P(member));
404 			}
405 			retval = &EG(uninitialized_zval_ptr);
406 		}
407 	}
408 	if (tmp_member) {
409 		Z_ADDREF_PP(retval);
410 		zval_ptr_dtor(&tmp_member);
411 		Z_DELREF_PP(retval);
412 	}
413 	return *retval;
414 }
415 /* }}} */
416 
zend_std_write_property(zval * object,zval * member,zval * value TSRMLS_DC)417 static void zend_std_write_property(zval *object, zval *member, zval *value TSRMLS_DC) /* {{{ */
418 {
419 	zend_object *zobj;
420 	zval *tmp_member = NULL;
421 	zval **variable_ptr;
422 	zend_property_info *property_info;
423 
424 	zobj = Z_OBJ_P(object);
425 
426  	if (Z_TYPE_P(member) != IS_STRING) {
427  		ALLOC_ZVAL(tmp_member);
428 		*tmp_member = *member;
429 		INIT_PZVAL(tmp_member);
430 		zval_copy_ctor(tmp_member);
431 		convert_to_string(tmp_member);
432 		member = tmp_member;
433 	}
434 
435 	property_info = zend_get_property_info(zobj->ce, member, (zobj->ce->__set != NULL) TSRMLS_CC);
436 
437 	if (property_info && zend_hash_quick_find(zobj->properties, property_info->name, property_info->name_length+1, property_info->h, (void **) &variable_ptr) == SUCCESS) {
438 		/* if we already have this value there, we don't actually need to do anything */
439 		if (*variable_ptr != value) {
440 			/* if we are assigning reference, we shouldn't move it, but instead assign variable
441 			   to the same pointer */
442 			if (PZVAL_IS_REF(*variable_ptr)) {
443 				zval garbage = **variable_ptr; /* old value should be destroyed */
444 
445 				/* To check: can't *variable_ptr be some system variable like error_zval here? */
446 				Z_TYPE_PP(variable_ptr) = Z_TYPE_P(value);
447 				(*variable_ptr)->value = value->value;
448 				if (Z_REFCOUNT_P(value) > 0) {
449 					zval_copy_ctor(*variable_ptr);
450 				} else {
451 					efree(value);
452 				}
453 				zval_dtor(&garbage);
454 			} else {
455 				zval *garbage = *variable_ptr;
456 
457 				/* if we assign referenced variable, we should separate it */
458 				Z_ADDREF_P(value);
459 				if (PZVAL_IS_REF(value)) {
460 					SEPARATE_ZVAL(&value);
461 				}
462 				*variable_ptr = value;
463 				zval_ptr_dtor(&garbage);
464 			}
465 		}
466 	} else {
467 		zend_guard *guard = NULL;
468 
469 		if (zobj->ce->__set &&
470 		    zend_get_property_guard(zobj, property_info, member, &guard) == SUCCESS &&
471 		    !guard->in_set) {
472 			Z_ADDREF_P(object);
473 			if (PZVAL_IS_REF(object)) {
474 				SEPARATE_ZVAL(&object);
475 			}
476 			guard->in_set = 1; /* prevent circular setting */
477 			if (zend_std_call_setter(object, member, value TSRMLS_CC) != SUCCESS) {
478 				/* for now, just ignore it - __set should take care of warnings, etc. */
479 			}
480 			guard->in_set = 0;
481 			zval_ptr_dtor(&object);
482 		} else if (property_info) {
483 			zval **foo;
484 
485 			/* if we assign referenced variable, we should separate it */
486 			Z_ADDREF_P(value);
487 			if (PZVAL_IS_REF(value)) {
488 				SEPARATE_ZVAL(&value);
489 			}
490 			zend_hash_quick_update(zobj->properties, property_info->name, property_info->name_length+1, property_info->h, &value, sizeof(zval *), (void **) &foo);
491 		} else if (zobj->ce->__set && guard && guard->in_set == 1) {
492 			if (Z_STRVAL_P(member)[0] == '\0') {
493 				if (Z_STRLEN_P(member) == 0) {
494 					zend_error(E_ERROR, "Cannot access empty property");
495 				} else {
496 					zend_error(E_ERROR, "Cannot access property started with '\\0'");
497 				}
498 			}
499 		}
500 	}
501 
502 	if (tmp_member) {
503 		zval_ptr_dtor(&tmp_member);
504 	}
505 }
506 /* }}} */
507 
zend_std_read_dimension(zval * object,zval * offset,int type TSRMLS_DC)508 zval *zend_std_read_dimension(zval *object, zval *offset, int type TSRMLS_DC) /* {{{ */
509 {
510 	zend_class_entry *ce = Z_OBJCE_P(object);
511 	zval *retval;
512 
513 	if (instanceof_function_ex(ce, zend_ce_arrayaccess, 1 TSRMLS_CC)) {
514 		if(offset == NULL) {
515 			/* [] construct */
516 			ALLOC_INIT_ZVAL(offset);
517 		} else {
518 			SEPARATE_ARG_IF_REF(offset);
519 		}
520 		zend_call_method_with_1_params(&object, ce, NULL, "offsetget", &retval, offset);
521 
522 		zval_ptr_dtor(&offset);
523 
524 		if (!retval) {
525 			if (!EG(exception)) {
526 				zend_error(E_ERROR, "Undefined offset for object of type %s used as array", ce->name);
527 			}
528 			return 0;
529 		}
530 
531 		/* Undo PZVAL_LOCK() */
532 		Z_DELREF_P(retval);
533 
534 		return retval;
535 	} else {
536 		zend_error(E_ERROR, "Cannot use object of type %s as array", ce->name);
537 		return 0;
538 	}
539 }
540 /* }}} */
541 
zend_std_write_dimension(zval * object,zval * offset,zval * value TSRMLS_DC)542 static void zend_std_write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC) /* {{{ */
543 {
544 	zend_class_entry *ce = Z_OBJCE_P(object);
545 
546 	if (instanceof_function_ex(ce, zend_ce_arrayaccess, 1 TSRMLS_CC)) {
547 		if (!offset) {
548 			ALLOC_INIT_ZVAL(offset);
549 		} else {
550 			SEPARATE_ARG_IF_REF(offset);
551 		}
552 		zend_call_method_with_2_params(&object, ce, NULL, "offsetset", NULL, offset, value);
553 		zval_ptr_dtor(&offset);
554 	} else {
555 		zend_error(E_ERROR, "Cannot use object of type %s as array", ce->name);
556 	}
557 }
558 /* }}} */
559 
zend_std_has_dimension(zval * object,zval * offset,int check_empty TSRMLS_DC)560 static int zend_std_has_dimension(zval *object, zval *offset, int check_empty TSRMLS_DC) /* {{{ */
561 {
562 	zend_class_entry *ce = Z_OBJCE_P(object);
563 	zval *retval;
564 	int result;
565 
566 	if (instanceof_function_ex(ce, zend_ce_arrayaccess, 1 TSRMLS_CC)) {
567 		SEPARATE_ARG_IF_REF(offset);
568 		zend_call_method_with_1_params(&object, ce, NULL, "offsetexists", &retval, offset);
569 		if (retval) {
570 			result = i_zend_is_true(retval);
571 			zval_ptr_dtor(&retval);
572 			if (check_empty && result && !EG(exception)) {
573 				zend_call_method_with_1_params(&object, ce, NULL, "offsetget", &retval, offset);
574 				if (retval) {
575 					result = i_zend_is_true(retval);
576 					zval_ptr_dtor(&retval);
577 				}
578 			}
579 		} else {
580 			result = 0;
581 		}
582 		zval_ptr_dtor(&offset);
583 	} else {
584 		zend_error(E_ERROR, "Cannot use object of type %s as array", ce->name);
585 		return 0;
586 	}
587 	return result;
588 }
589 /* }}} */
590 
zend_std_get_property_ptr_ptr(zval * object,zval * member TSRMLS_DC)591 static zval **zend_std_get_property_ptr_ptr(zval *object, zval *member TSRMLS_DC) /* {{{ */
592 {
593 	zend_object *zobj;
594 	zval tmp_member;
595 	zval **retval;
596 	zend_property_info *property_info;
597 
598 	zobj = Z_OBJ_P(object);
599 
600  	if (Z_TYPE_P(member) != IS_STRING) {
601 		tmp_member = *member;
602 		zval_copy_ctor(&tmp_member);
603 		convert_to_string(&tmp_member);
604 		member = &tmp_member;
605 	}
606 
607 #if DEBUG_OBJECT_HANDLERS
608 	fprintf(stderr, "Ptr object #%d property: %s\n", Z_OBJ_HANDLE_P(object), Z_STRVAL_P(member));
609 #endif
610 
611 	property_info = zend_get_property_info(zobj->ce, member, (zobj->ce->__get != NULL) TSRMLS_CC);
612 
613 	if (!property_info || zend_hash_quick_find(zobj->properties, property_info->name, property_info->name_length+1, property_info->h, (void **) &retval) == FAILURE) {
614 		zval *new_zval;
615 		zend_guard *guard;
616 
617 		if (!zobj->ce->__get ||
618 			zend_get_property_guard(zobj, property_info, member, &guard) != SUCCESS ||
619 			(property_info && guard->in_get)) {
620 			/* we don't have access controls - will just add it */
621 			new_zval = &EG(uninitialized_zval);
622 
623 /* 			zend_error(E_NOTICE, "Undefined property: %s", Z_STRVAL_P(member)); */
624 			Z_ADDREF_P(new_zval);
625 			zend_hash_quick_update(zobj->properties, property_info->name, property_info->name_length+1, property_info->h, &new_zval, sizeof(zval *), (void **) &retval);
626 		} else {
627 			/* we do have getter - fail and let it try again with usual get/set */
628 			retval = NULL;
629 		}
630 	}
631 	if (member == &tmp_member) {
632 		zval_dtor(member);
633 	}
634 	return retval;
635 }
636 /* }}} */
637 
zend_std_unset_property(zval * object,zval * member TSRMLS_DC)638 static void zend_std_unset_property(zval *object, zval *member TSRMLS_DC) /* {{{ */
639 {
640 	zend_object *zobj;
641 	zval *tmp_member = NULL;
642 	zend_property_info *property_info;
643 
644 	zobj = Z_OBJ_P(object);
645 
646  	if (Z_TYPE_P(member) != IS_STRING) {
647  		ALLOC_ZVAL(tmp_member);
648 		*tmp_member = *member;
649 		INIT_PZVAL(tmp_member);
650 		zval_copy_ctor(tmp_member);
651 		convert_to_string(tmp_member);
652 		member = tmp_member;
653 	}
654 
655 	property_info = zend_get_property_info(zobj->ce, member, (zobj->ce->__unset != NULL) TSRMLS_CC);
656 
657 	if (!property_info || zend_hash_quick_del(zobj->properties, property_info->name, property_info->name_length+1, property_info->h) == FAILURE) {
658 		zend_guard *guard = NULL;
659 
660 		if (zobj->ce->__unset &&
661 		    zend_get_property_guard(zobj, property_info, member, &guard) == SUCCESS &&
662 		    !guard->in_unset) {
663 			/* have unseter - try with it! */
664 			Z_ADDREF_P(object);
665 			if (PZVAL_IS_REF(object)) {
666 				SEPARATE_ZVAL(&object);
667 			}
668 			guard->in_unset = 1; /* prevent circular unsetting */
669 			zend_std_call_unsetter(object, member TSRMLS_CC);
670 			guard->in_unset = 0;
671 			zval_ptr_dtor(&object);
672 		} else if (zobj->ce->__unset && guard && guard->in_unset == 1) {
673 			if (Z_STRVAL_P(member)[0] == '\0') {
674 				if (Z_STRLEN_P(member) == 0) {
675 					zend_error(E_ERROR, "Cannot access empty property");
676 				} else {
677 					zend_error(E_ERROR, "Cannot access property started with '\\0'");
678 				}
679 			}
680 		}
681 	}
682 
683 	if (tmp_member) {
684 		zval_ptr_dtor(&tmp_member);
685 	}
686 }
687 /* }}} */
688 
zend_std_unset_dimension(zval * object,zval * offset TSRMLS_DC)689 static void zend_std_unset_dimension(zval *object, zval *offset TSRMLS_DC) /* {{{ */
690 {
691 	zend_class_entry *ce = Z_OBJCE_P(object);
692 
693 	if (instanceof_function_ex(ce, zend_ce_arrayaccess, 1 TSRMLS_CC)) {
694 		SEPARATE_ARG_IF_REF(offset);
695 		zend_call_method_with_1_params(&object, ce, NULL, "offsetunset", NULL, offset);
696 		zval_ptr_dtor(&offset);
697 	} else {
698 		zend_error(E_ERROR, "Cannot use object of type %s as array", ce->name);
699 	}
700 }
701 /* }}} */
702 
zend_std_call_user_call(INTERNAL_FUNCTION_PARAMETERS)703 ZEND_API void zend_std_call_user_call(INTERNAL_FUNCTION_PARAMETERS) /* {{{ */
704 {
705 	zend_internal_function *func = (zend_internal_function *)EG(current_execute_data)->function_state.function;
706 	zval *method_name_ptr, *method_args_ptr;
707 	zval *method_result_ptr = NULL;
708 	zend_class_entry *ce = Z_OBJCE_P(this_ptr);
709 
710 	ALLOC_ZVAL(method_args_ptr);
711 	INIT_PZVAL(method_args_ptr);
712 	array_init_size(method_args_ptr, ZEND_NUM_ARGS());
713 
714 	if (zend_copy_parameters_array(ZEND_NUM_ARGS(), method_args_ptr TSRMLS_CC) == FAILURE) {
715 		zval_dtor(method_args_ptr);
716 		zend_error(E_ERROR, "Cannot get arguments for __call");
717 		RETURN_FALSE;
718 	}
719 
720 	ALLOC_ZVAL(method_name_ptr);
721 	INIT_PZVAL(method_name_ptr);
722 	ZVAL_STRING(method_name_ptr, func->function_name, 0); /* no dup - it's a copy */
723 
724 	/* __call handler is called with two arguments:
725 	   method name
726 	   array of method parameters
727 
728 	*/
729 	zend_call_method_with_2_params(&this_ptr, ce, &ce->__call, ZEND_CALL_FUNC_NAME, &method_result_ptr, method_name_ptr, method_args_ptr);
730 
731 	if (method_result_ptr) {
732 		if (Z_ISREF_P(method_result_ptr) || Z_REFCOUNT_P(method_result_ptr) > 1) {
733 			RETVAL_ZVAL(method_result_ptr, 1, 1);
734 		} else {
735 			RETVAL_ZVAL(method_result_ptr, 0, 1);
736 		}
737 	}
738 
739 	/* now destruct all auxiliaries */
740 	zval_ptr_dtor(&method_args_ptr);
741 	zval_ptr_dtor(&method_name_ptr);
742 
743 	/* destruct the function also, then - we have allocated it in get_method */
744 	efree(func);
745 }
746 /* }}} */
747 
748 /* Ensures that we're allowed to call a private method.
749  * Returns the function address that should be called, or NULL
750  * if no such function exists.
751  */
zend_check_private_int(zend_function * fbc,zend_class_entry * ce,char * function_name_strval,int function_name_strlen TSRMLS_DC)752 static inline zend_function *zend_check_private_int(zend_function *fbc, zend_class_entry *ce, char *function_name_strval, int function_name_strlen TSRMLS_DC) /* {{{ */
753 {
754 	if (!ce) {
755 		return 0;
756 	}
757 
758 	/* We may call a private function if:
759 	 * 1.  The class of our object is the same as the scope, and the private
760 	 *     function (EX(fbc)) has the same scope.
761 	 * 2.  One of our parent classes are the same as the scope, and it contains
762 	 *     a private function with the same name that has the same scope.
763 	 */
764 	if (fbc->common.scope == ce && EG(scope) == ce) {
765 		/* rule #1 checks out ok, allow the function call */
766 		return fbc;
767 	}
768 
769 
770 	/* Check rule #2 */
771 	ce = ce->parent;
772 	while (ce) {
773 		if (ce == EG(scope)) {
774 			if (zend_hash_find(&ce->function_table, function_name_strval, function_name_strlen+1, (void **) &fbc)==SUCCESS
775 				&& fbc->op_array.fn_flags & ZEND_ACC_PRIVATE
776 				&& fbc->common.scope == EG(scope)) {
777 				return fbc;
778 			}
779 			break;
780 		}
781 		ce = ce->parent;
782 	}
783 	return NULL;
784 }
785 /* }}} */
786 
zend_check_private(zend_function * fbc,zend_class_entry * ce,char * function_name_strval,int function_name_strlen TSRMLS_DC)787 ZEND_API int zend_check_private(zend_function *fbc, zend_class_entry *ce, char *function_name_strval, int function_name_strlen TSRMLS_DC) /* {{{ */
788 {
789 	return zend_check_private_int(fbc, ce, function_name_strval, function_name_strlen TSRMLS_CC) != NULL;
790 }
791 /* }}} */
792 
793 /* Ensures that we're allowed to call a protected method.
794  */
zend_check_protected(zend_class_entry * ce,zend_class_entry * scope)795 ZEND_API int zend_check_protected(zend_class_entry *ce, zend_class_entry *scope) /* {{{ */
796 {
797 	zend_class_entry *fbc_scope = ce;
798 
799 	/* Is the context that's calling the function, the same as one of
800 	 * the function's parents?
801 	 */
802 	while (fbc_scope) {
803 		if (fbc_scope==scope) {
804 			return 1;
805 		}
806 		fbc_scope = fbc_scope->parent;
807 	}
808 
809 	/* Is the function's scope the same as our current object context,
810 	 * or any of the parents of our context?
811 	 */
812 	while (scope) {
813 		if (scope==ce) {
814 			return 1;
815 		}
816 		scope = scope->parent;
817 	}
818 	return 0;
819 }
820 /* }}} */
821 
zend_get_function_root_class(zend_function * fbc)822 static inline zend_class_entry * zend_get_function_root_class(zend_function *fbc) /* {{{ */
823 {
824 	return fbc->common.prototype ? fbc->common.prototype->common.scope : fbc->common.scope;
825 }
826 /* }}} */
827 
zend_get_user_call_function(zend_class_entry * ce,const char * method_name,int method_len)828 static inline union _zend_function *zend_get_user_call_function(zend_class_entry *ce, const char *method_name, int method_len) /* {{{ */
829 {
830 	zend_internal_function *call_user_call = emalloc(sizeof(zend_internal_function));
831 	call_user_call->type = ZEND_INTERNAL_FUNCTION;
832 	call_user_call->module = ce->module;
833 	call_user_call->handler = zend_std_call_user_call;
834 	call_user_call->arg_info = NULL;
835 	call_user_call->num_args = 0;
836 	call_user_call->scope = ce;
837 	call_user_call->fn_flags = ZEND_ACC_CALL_VIA_HANDLER;
838 	call_user_call->function_name = estrndup(method_name, method_len);
839 	call_user_call->pass_rest_by_reference = 0;
840 	call_user_call->return_reference = ZEND_RETURN_VALUE;
841 
842 	return (union _zend_function *)call_user_call;
843 }
844 /* }}} */
845 
zend_std_get_method(zval ** object_ptr,char * method_name,int method_len TSRMLS_DC)846 static union _zend_function *zend_std_get_method(zval **object_ptr, char *method_name, int method_len TSRMLS_DC) /* {{{ */
847 {
848 	zend_object *zobj;
849 	zend_function *fbc;
850 	char *lc_method_name;
851 	zval *object = *object_ptr;
852 	ALLOCA_FLAG(use_heap)
853 
854 	lc_method_name = do_alloca(method_len+1, use_heap);
855 	/* Create a zend_copy_str_tolower(dest, src, src_length); */
856 	zend_str_tolower_copy(lc_method_name, method_name, method_len);
857 
858 	zobj = Z_OBJ_P(object);
859 	if (zend_hash_find(&zobj->ce->function_table, lc_method_name, method_len+1, (void **)&fbc) == FAILURE) {
860 		free_alloca(lc_method_name, use_heap);
861 		if (zobj->ce->__call) {
862 			return zend_get_user_call_function(zobj->ce, method_name, method_len);
863 		} else {
864 			return NULL;
865 		}
866 	}
867 
868 	/* Check access level */
869 	if (fbc->op_array.fn_flags & ZEND_ACC_PRIVATE) {
870 		zend_function *updated_fbc;
871 
872 		/* Ensure that if we're calling a private function, we're allowed to do so.
873 		 * If we're not and __call() handler exists, invoke it, otherwise error out.
874 		 */
875 		updated_fbc = zend_check_private_int(fbc, Z_OBJ_HANDLER_P(object, get_class_entry)(object TSRMLS_CC), lc_method_name, method_len TSRMLS_CC);
876 		if (updated_fbc) {
877 			fbc = updated_fbc;
878 		} else {
879 			if (zobj->ce->__call) {
880 				fbc = zend_get_user_call_function(zobj->ce, method_name, method_len);
881 			} else {
882 				zend_error(E_ERROR, "Call to %s method %s::%s() from context '%s'", zend_visibility_string(fbc->common.fn_flags), ZEND_FN_SCOPE_NAME(fbc), method_name, EG(scope) ? EG(scope)->name : "");
883 			}
884 		}
885 	} else {
886 		/* Ensure that we haven't overridden a private function and end up calling
887 		 * the overriding public function...
888 		 */
889 		if (EG(scope) &&
890 		    is_derived_class(fbc->common.scope, EG(scope)) &&
891 		    fbc->op_array.fn_flags & ZEND_ACC_CHANGED) {
892 			zend_function *priv_fbc;
893 
894 			if (zend_hash_find(&EG(scope)->function_table, lc_method_name, method_len+1, (void **) &priv_fbc)==SUCCESS
895 				&& priv_fbc->common.fn_flags & ZEND_ACC_PRIVATE
896 				&& priv_fbc->common.scope == EG(scope)) {
897 				fbc = priv_fbc;
898 			}
899 		}
900 		if ((fbc->common.fn_flags & ZEND_ACC_PROTECTED)) {
901 			/* Ensure that if we're calling a protected function, we're allowed to do so.
902 			 * If we're not and __call() handler exists, invoke it, otherwise error out.
903 			 */
904 			if (!zend_check_protected(zend_get_function_root_class(fbc), EG(scope))) {
905 				if (zobj->ce->__call) {
906 					fbc = zend_get_user_call_function(zobj->ce, method_name, method_len);
907 				} else {
908 					zend_error(E_ERROR, "Call to %s method %s::%s() from context '%s'", zend_visibility_string(fbc->common.fn_flags), ZEND_FN_SCOPE_NAME(fbc), method_name, EG(scope) ? EG(scope)->name : "");
909 				}
910 			}
911 		}
912 	}
913 
914 	free_alloca(lc_method_name, use_heap);
915 	return fbc;
916 }
917 /* }}} */
918 
zend_std_callstatic_user_call(INTERNAL_FUNCTION_PARAMETERS)919 ZEND_API void zend_std_callstatic_user_call(INTERNAL_FUNCTION_PARAMETERS) /* {{{ */
920 {
921 	zend_internal_function *func = (zend_internal_function *)EG(current_execute_data)->function_state.function;
922 	zval *method_name_ptr, *method_args_ptr;
923 	zval *method_result_ptr = NULL;
924 	zend_class_entry *ce = EG(scope);
925 
926 	ALLOC_ZVAL(method_args_ptr);
927 	INIT_PZVAL(method_args_ptr);
928 	array_init_size(method_args_ptr, ZEND_NUM_ARGS());
929 
930 	if (zend_copy_parameters_array(ZEND_NUM_ARGS(), method_args_ptr TSRMLS_CC) == FAILURE) {
931 		zval_dtor(method_args_ptr);
932 		zend_error(E_ERROR, "Cannot get arguments for " ZEND_CALLSTATIC_FUNC_NAME);
933 		RETURN_FALSE;
934 	}
935 
936 	ALLOC_ZVAL(method_name_ptr);
937 	INIT_PZVAL(method_name_ptr);
938 	ZVAL_STRING(method_name_ptr, func->function_name, 0); /* no dup - it's a copy */
939 
940 	/* __callStatic handler is called with two arguments:
941 	   method name
942 	   array of method parameters
943 	*/
944 	zend_call_method_with_2_params(NULL, ce, &ce->__callstatic, ZEND_CALLSTATIC_FUNC_NAME, &method_result_ptr, method_name_ptr, method_args_ptr);
945 
946 	if (method_result_ptr) {
947 		if (Z_ISREF_P(method_result_ptr) || Z_REFCOUNT_P(method_result_ptr) > 1) {
948 			RETVAL_ZVAL(method_result_ptr, 1, 1);
949 		} else {
950 			RETVAL_ZVAL(method_result_ptr, 0, 1);
951 		}
952 	}
953 
954 	/* now destruct all auxiliaries */
955 	zval_ptr_dtor(&method_args_ptr);
956 	zval_ptr_dtor(&method_name_ptr);
957 
958 	/* destruct the function also, then - we have allocated it in get_method */
959 	efree(func);
960 }
961 /* }}} */
962 
zend_get_user_callstatic_function(zend_class_entry * ce,const char * method_name,int method_len)963 static inline union _zend_function *zend_get_user_callstatic_function(zend_class_entry *ce, const char *method_name, int method_len) /* {{{ */
964 {
965 	zend_internal_function *callstatic_user_call = emalloc(sizeof(zend_internal_function));
966 	callstatic_user_call->type     = ZEND_INTERNAL_FUNCTION;
967 	callstatic_user_call->module   = ce->module;
968 	callstatic_user_call->handler  = zend_std_callstatic_user_call;
969 	callstatic_user_call->arg_info = NULL;
970 	callstatic_user_call->num_args = 0;
971 	callstatic_user_call->scope    = ce;
972 	callstatic_user_call->fn_flags = ZEND_ACC_STATIC | ZEND_ACC_PUBLIC | ZEND_ACC_CALL_VIA_HANDLER;
973 	callstatic_user_call->function_name = estrndup(method_name, method_len);
974 	callstatic_user_call->pass_rest_by_reference = 0;
975 	callstatic_user_call->return_reference       = ZEND_RETURN_VALUE;
976 
977 	return (zend_function *)callstatic_user_call;
978 }
979 /* }}} */
980 
981 /* This is not (yet?) in the API, but it belongs in the built-in objects callbacks */
982 
zend_std_get_static_method(zend_class_entry * ce,char * function_name_strval,int function_name_strlen TSRMLS_DC)983 ZEND_API zend_function *zend_std_get_static_method(zend_class_entry *ce, char *function_name_strval, int function_name_strlen TSRMLS_DC) /* {{{ */
984 {
985 	zend_function *fbc = NULL;
986 	char *lc_class_name, *lc_function_name = NULL;
987 
988 	lc_function_name = zend_str_tolower_dup(function_name_strval, function_name_strlen);
989 
990 	if (function_name_strlen == ce->name_length && ce->constructor) {
991 		lc_class_name = zend_str_tolower_dup(ce->name, ce->name_length);
992 		/* Only change the method to the constructor if the constructor isn't called __construct
993 		 * we check for __ so we can be binary safe for lowering, we should use ZEND_CONSTRUCTOR_FUNC_NAME
994 		 */
995 		if (!memcmp(lc_class_name, lc_function_name, function_name_strlen) && memcmp(ce->constructor->common.function_name, "__", sizeof("__") - 1)) {
996 			fbc = ce->constructor;
997 		}
998 		efree(lc_class_name);
999 	}
1000 	if (!fbc && zend_hash_find(&ce->function_table, lc_function_name, function_name_strlen+1, (void **) &fbc)==FAILURE) {
1001 		efree(lc_function_name);
1002 
1003 		if (ce->__call &&
1004 		    EG(This) &&
1005 		    Z_OBJ_HT_P(EG(This))->get_class_entry &&
1006 		    instanceof_function(Z_OBJCE_P(EG(This)), ce TSRMLS_CC)) {
1007 			return zend_get_user_call_function(ce, function_name_strval, function_name_strlen);
1008 		} else if (ce->__callstatic) {
1009 			return zend_get_user_callstatic_function(ce, function_name_strval, function_name_strlen);
1010 		} else {
1011 	   		return NULL;
1012 		}
1013 	}
1014 	efree(lc_function_name);
1015 
1016 #if MBO_0
1017 	/* right now this function is used for non static method lookup too */
1018 	/* Is the function static */
1019 	if (!(fbc->common.fn_flags & ZEND_ACC_STATIC)) {
1020 		zend_error(E_ERROR, "Cannot call non static method %s::%s() without object", ZEND_FN_SCOPE_NAME(fbc), fbc->common.function_name);
1021 	}
1022 #endif
1023 	if (fbc->op_array.fn_flags & ZEND_ACC_PUBLIC) {
1024 		/* No further checks necessary, most common case */
1025 	} else if (fbc->op_array.fn_flags & ZEND_ACC_PRIVATE) {
1026 		zend_function *updated_fbc;
1027 
1028 		/* Ensure that if we're calling a private function, we're allowed to do so.
1029 		 */
1030 		updated_fbc = zend_check_private_int(fbc, EG(scope), function_name_strval, function_name_strlen TSRMLS_CC);
1031 		if (updated_fbc) {
1032 			fbc = updated_fbc;
1033 		} else {
1034 			if (ce->__callstatic) {
1035 				return zend_get_user_callstatic_function(ce, function_name_strval, function_name_strlen);
1036 			}
1037 			zend_error(E_ERROR, "Call to %s method %s::%s() from context '%s'", zend_visibility_string(fbc->common.fn_flags), ZEND_FN_SCOPE_NAME(fbc), function_name_strval, EG(scope) ? EG(scope)->name : "");
1038 		}
1039 	} else if ((fbc->common.fn_flags & ZEND_ACC_PROTECTED)) {
1040 		/* Ensure that if we're calling a protected function, we're allowed to do so.
1041 		 */
1042 		if (!zend_check_protected(zend_get_function_root_class(fbc), EG(scope))) {
1043 			if (ce->__callstatic) {
1044 				return zend_get_user_callstatic_function(ce, function_name_strval, function_name_strlen);
1045 			}
1046 			zend_error(E_ERROR, "Call to %s method %s::%s() from context '%s'", zend_visibility_string(fbc->common.fn_flags), ZEND_FN_SCOPE_NAME(fbc), function_name_strval, EG(scope) ? EG(scope)->name : "");
1047 		}
1048 	}
1049 
1050 	return fbc;
1051 }
1052 /* }}} */
1053 
zend_std_get_static_property(zend_class_entry * ce,char * property_name,int property_name_len,zend_bool silent TSRMLS_DC)1054 ZEND_API zval **zend_std_get_static_property(zend_class_entry *ce, char *property_name, int property_name_len, zend_bool silent TSRMLS_DC) /* {{{ */
1055 {
1056 	zval **retval = NULL;
1057 	zend_class_entry *tmp_ce = ce;
1058 	zend_property_info *property_info;
1059 	zend_property_info std_property_info;
1060 
1061 	if (zend_hash_find(&ce->properties_info, property_name, property_name_len+1, (void **) &property_info)==FAILURE) {
1062 		std_property_info.flags = ZEND_ACC_PUBLIC;
1063 		std_property_info.name = property_name;
1064 		std_property_info.name_length = property_name_len;
1065 		std_property_info.h = zend_get_hash_value(std_property_info.name, std_property_info.name_length+1);
1066 		std_property_info.ce = ce;
1067 		property_info = &std_property_info;
1068 	}
1069 
1070 #if DEBUG_OBJECT_HANDLERS
1071 	zend_printf("Access type for %s::%s is %s\n", ce->name, property_name, zend_visibility_string(property_info->flags));
1072 #endif
1073 
1074 	if (!zend_verify_property_access(property_info, ce TSRMLS_CC)) {
1075 		if (!silent) {
1076 			zend_error(E_ERROR, "Cannot access %s property %s::$%s", zend_visibility_string(property_info->flags), ce->name, property_name);
1077 		}
1078 		return NULL;
1079 	}
1080 
1081 	zend_update_class_constants(tmp_ce TSRMLS_CC);
1082 
1083 	zend_hash_quick_find(CE_STATIC_MEMBERS(tmp_ce), property_info->name, property_info->name_length+1, property_info->h, (void **) &retval);
1084 
1085 	if (!retval) {
1086 		if (silent) {
1087 			return NULL;
1088 		} else {
1089 			zend_error(E_ERROR, "Access to undeclared static property: %s::$%s", ce->name, property_name);
1090 		}
1091 	}
1092 
1093 	return retval;
1094 }
1095 /* }}} */
1096 
zend_std_unset_static_property(zend_class_entry * ce,char * property_name,int property_name_len TSRMLS_DC)1097 ZEND_API zend_bool zend_std_unset_static_property(zend_class_entry *ce, char *property_name, int property_name_len TSRMLS_DC) /* {{{ */
1098 {
1099 	zend_error(E_ERROR, "Attempt to unset static property %s::$%s", ce->name, property_name);
1100 	return 0;
1101 }
1102 /* }}} */
1103 
zend_std_get_constructor(zval * object TSRMLS_DC)1104 ZEND_API union _zend_function *zend_std_get_constructor(zval *object TSRMLS_DC) /* {{{ */
1105 {
1106 	zend_object *zobj = Z_OBJ_P(object);
1107 	zend_function *constructor = zobj->ce->constructor;
1108 
1109 	if (constructor) {
1110 		if (constructor->op_array.fn_flags & ZEND_ACC_PUBLIC) {
1111 			/* No further checks necessary */
1112 		} else if (constructor->op_array.fn_flags & ZEND_ACC_PRIVATE) {
1113 			/* Ensure that if we're calling a private function, we're allowed to do so.
1114 			 */
1115 			if (constructor->common.scope != EG(scope)) {
1116 				if (EG(scope)) {
1117 					zend_error(E_ERROR, "Call to private %s::%s() from context '%s'", constructor->common.scope->name, constructor->common.function_name, EG(scope)->name);
1118 				} else {
1119 					zend_error(E_ERROR, "Call to private %s::%s() from invalid context", constructor->common.scope->name, constructor->common.function_name);
1120 				}
1121 			}
1122 		} else if ((constructor->common.fn_flags & ZEND_ACC_PROTECTED)) {
1123 			/* Ensure that if we're calling a protected function, we're allowed to do so.
1124 			 * Constructors only have prototype if they are defined by an interface but
1125 			 * it is the compilers responsibility to take care of the prototype.
1126 			 */
1127 			if (!zend_check_protected(zend_get_function_root_class(constructor), EG(scope))) {
1128 				if (EG(scope)) {
1129 					zend_error(E_ERROR, "Call to protected %s::%s() from context '%s'", constructor->common.scope->name, constructor->common.function_name, EG(scope)->name);
1130 				} else {
1131 					zend_error(E_ERROR, "Call to protected %s::%s() from invalid context", constructor->common.scope->name, constructor->common.function_name);
1132 				}
1133 			}
1134 		}
1135 	}
1136 
1137 	return constructor;
1138 }
1139 /* }}} */
1140 
1141 int zend_compare_symbol_tables_i(HashTable *ht1, HashTable *ht2 TSRMLS_DC);
1142 
zend_std_compare_objects(zval * o1,zval * o2 TSRMLS_DC)1143 static int zend_std_compare_objects(zval *o1, zval *o2 TSRMLS_DC) /* {{{ */
1144 {
1145 	zend_object *zobj1, *zobj2;
1146 
1147 	zobj1 = Z_OBJ_P(o1);
1148 	zobj2 = Z_OBJ_P(o2);
1149 
1150 	if (zobj1->ce != zobj2->ce) {
1151 		return 1; /* different classes */
1152 	}
1153 	return zend_compare_symbol_tables_i(zobj1->properties, zobj2->properties TSRMLS_CC);
1154 }
1155 /* }}} */
1156 
zend_std_has_property(zval * object,zval * member,int has_set_exists TSRMLS_DC)1157 static int zend_std_has_property(zval *object, zval *member, int has_set_exists TSRMLS_DC) /* {{{ */
1158 {
1159 	zend_object *zobj;
1160 	int result;
1161 	zval **value;
1162 	zval *tmp_member = NULL;
1163 	zend_property_info *property_info;
1164 
1165 	zobj = Z_OBJ_P(object);
1166 
1167 	if (Z_TYPE_P(member) != IS_STRING) {
1168 		ALLOC_ZVAL(tmp_member);
1169 		*tmp_member = *member;
1170 		INIT_PZVAL(tmp_member);
1171 		zval_copy_ctor(tmp_member);
1172 		convert_to_string(tmp_member);
1173 		member = tmp_member;
1174 	}
1175 
1176 #if DEBUG_OBJECT_HANDLERS
1177 	fprintf(stderr, "Read object #%d property: %s\n", Z_OBJ_HANDLE_P(object), Z_STRVAL_P(member));
1178 #endif
1179 
1180 	property_info = zend_get_property_info(zobj->ce, member, 1 TSRMLS_CC);
1181 
1182 	if (!property_info || zend_hash_quick_find(zobj->properties, property_info->name, property_info->name_length+1, property_info->h, (void **) &value) == FAILURE) {
1183 		zend_guard *guard;
1184 
1185 		result = 0;
1186 		if ((has_set_exists != 2) &&
1187 		    zobj->ce->__isset &&
1188 		    zend_get_property_guard(zobj, property_info, member, &guard) == SUCCESS &&
1189 		    !guard->in_isset) {
1190 			zval *rv;
1191 
1192 			/* have issetter - try with it! */
1193 			Z_ADDREF_P(object);
1194 			if (PZVAL_IS_REF(object)) {
1195 				SEPARATE_ZVAL(&object);
1196 			}
1197 			guard->in_isset = 1; /* prevent circular getting */
1198 			rv = zend_std_call_issetter(object, member TSRMLS_CC);
1199 			if (rv) {
1200 				result = zend_is_true(rv);
1201 				zval_ptr_dtor(&rv);
1202 				if (has_set_exists && result) {
1203 					if (!EG(exception) && zobj->ce->__get && !guard->in_get) {
1204 						guard->in_get = 1;
1205 						rv = zend_std_call_getter(object, member TSRMLS_CC);
1206 						guard->in_get = 0;
1207 						if (rv) {
1208 							Z_ADDREF_P(rv);
1209 							result = i_zend_is_true(rv);
1210 							zval_ptr_dtor(&rv);
1211 						} else {
1212 							result = 0;
1213 						}
1214 					} else {
1215 						result = 0;
1216 					}
1217 				}
1218 			}
1219 			guard->in_isset = 0;
1220 			zval_ptr_dtor(&object);
1221 		}
1222 	} else {
1223 		switch (has_set_exists) {
1224 		case 0:
1225 			result = (Z_TYPE_PP(value) != IS_NULL);
1226 			break;
1227 		default:
1228 			result = zend_is_true(*value);
1229 			break;
1230 		case 2:
1231 			result = 1;
1232 			break;
1233 		}
1234 	}
1235 
1236 	if (tmp_member) {
1237 		zval_ptr_dtor(&tmp_member);
1238 	}
1239 	return result;
1240 }
1241 /* }}} */
1242 
zend_std_object_get_class(const zval * object TSRMLS_DC)1243 zend_class_entry *zend_std_object_get_class(const zval *object TSRMLS_DC) /* {{{ */
1244 {
1245 	zend_object *zobj;
1246 	zobj = Z_OBJ_P(object);
1247 
1248 	return zobj->ce;
1249 }
1250 /* }}} */
1251 
zend_std_object_get_class_name(const zval * object,char ** class_name,zend_uint * class_name_len,int parent TSRMLS_DC)1252 int zend_std_object_get_class_name(const zval *object, char **class_name, zend_uint *class_name_len, int parent TSRMLS_DC) /* {{{ */
1253 {
1254 	zend_object *zobj;
1255 	zend_class_entry *ce;
1256 	zobj = Z_OBJ_P(object);
1257 
1258 	if (parent) {
1259 		if (!zobj->ce->parent) {
1260 			return FAILURE;
1261 		}
1262 		ce = zobj->ce->parent;
1263 	} else {
1264 		ce = zobj->ce;
1265 	}
1266 
1267 	*class_name_len = ce->name_length;
1268 	*class_name = estrndup(ce->name, ce->name_length);
1269 	return SUCCESS;
1270 }
1271 /* }}} */
1272 
zend_std_cast_object_tostring(zval * readobj,zval * writeobj,int type TSRMLS_DC)1273 ZEND_API int zend_std_cast_object_tostring(zval *readobj, zval *writeobj, int type TSRMLS_DC) /* {{{ */
1274 {
1275 	zval *retval;
1276 	zend_class_entry *ce;
1277 
1278 	switch (type) {
1279 		case IS_STRING:
1280 			ce = Z_OBJCE_P(readobj);
1281 			if (ce->__tostring &&
1282 				(zend_call_method_with_0_params(&readobj, ce, &ce->__tostring, "__tostring", &retval) || EG(exception))) {
1283 				if (EG(exception)) {
1284 					if (retval) {
1285 						zval_ptr_dtor(&retval);
1286 					}
1287 					EG(exception) = NULL;
1288 					zend_error(E_ERROR, "Method %s::__toString() must not throw an exception", ce->name);
1289 					return FAILURE;
1290 				}
1291 				if (Z_TYPE_P(retval) == IS_STRING) {
1292 					INIT_PZVAL(writeobj);
1293 					if (readobj == writeobj) {
1294 						zval_dtor(readobj);
1295 					}
1296 					ZVAL_ZVAL(writeobj, retval, 1, 1);
1297 					if (Z_TYPE_P(writeobj) != type) {
1298 						convert_to_explicit_type(writeobj, type);
1299 					}
1300 					return SUCCESS;
1301 				} else {
1302 					zval_ptr_dtor(&retval);
1303 					INIT_PZVAL(writeobj);
1304 					if (readobj == writeobj) {
1305 						zval_dtor(readobj);
1306 					}
1307 					ZVAL_EMPTY_STRING(writeobj);
1308 					zend_error(E_RECOVERABLE_ERROR, "Method %s::__toString() must return a string value", ce->name);
1309 					return SUCCESS;
1310 				}
1311 			}
1312 			return FAILURE;
1313 		case IS_BOOL:
1314 			INIT_PZVAL(writeobj);
1315 			ZVAL_BOOL(writeobj, 1);
1316 			return SUCCESS;
1317 		case IS_LONG:
1318 			ce = Z_OBJCE_P(readobj);
1319 			zend_error(E_NOTICE, "Object of class %s could not be converted to int", ce->name);
1320 			INIT_PZVAL(writeobj);
1321 			if (readobj == writeobj) {
1322 				zval_dtor(readobj);
1323 			}
1324 			ZVAL_LONG(writeobj, 1);
1325 			return SUCCESS;
1326 		case IS_DOUBLE:
1327 			ce = Z_OBJCE_P(readobj);
1328 			zend_error(E_NOTICE, "Object of class %s could not be converted to double", ce->name);
1329 			INIT_PZVAL(writeobj);
1330 			if (readobj == writeobj) {
1331 				zval_dtor(readobj);
1332 			}
1333 			ZVAL_DOUBLE(writeobj, 1);
1334 			return SUCCESS;
1335 		default:
1336 			INIT_PZVAL(writeobj);
1337 			Z_TYPE_P(writeobj) = IS_NULL;
1338 			break;
1339 	}
1340 	return FAILURE;
1341 }
1342 /* }}} */
1343 
zend_std_get_closure(zval * obj,zend_class_entry ** ce_ptr,zend_function ** fptr_ptr,zval ** zobj_ptr TSRMLS_DC)1344 int zend_std_get_closure(zval *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zval **zobj_ptr TSRMLS_DC) /* {{{ */
1345 {
1346 	zend_class_entry *ce;
1347 	if (Z_TYPE_P(obj) != IS_OBJECT) {
1348 		return FAILURE;
1349 	}
1350 
1351 	ce = Z_OBJCE_P(obj);
1352 
1353 	if (zend_hash_find(&ce->function_table, ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME), (void**)fptr_ptr) == FAILURE) {
1354 		return FAILURE;
1355 	}
1356 
1357 	*ce_ptr = ce;
1358 	if ((*fptr_ptr)->common.fn_flags & ZEND_ACC_STATIC) {
1359 		if (zobj_ptr) {
1360 			*zobj_ptr = NULL;
1361 		}
1362 	} else {
1363 		if (zobj_ptr) {
1364 			*zobj_ptr = obj;
1365 		}
1366 	}
1367 	return SUCCESS;
1368 }
1369 /* }}} */
1370 
1371 ZEND_API zend_object_handlers std_object_handlers = {
1372 	zend_objects_store_add_ref,				/* add_ref */
1373 	zend_objects_store_del_ref,				/* del_ref */
1374 	zend_objects_clone_obj,					/* clone_obj */
1375 
1376 	zend_std_read_property,					/* read_property */
1377 	zend_std_write_property,				/* write_property */
1378 	zend_std_read_dimension,				/* read_dimension */
1379 	zend_std_write_dimension,				/* write_dimension */
1380 	zend_std_get_property_ptr_ptr,			/* get_property_ptr_ptr */
1381 	NULL,									/* get */
1382 	NULL,									/* set */
1383 	zend_std_has_property,					/* has_property */
1384 	zend_std_unset_property,				/* unset_property */
1385 	zend_std_has_dimension,					/* has_dimension */
1386 	zend_std_unset_dimension,				/* unset_dimension */
1387 	zend_std_get_properties,				/* get_properties */
1388 	zend_std_get_method,					/* get_method */
1389 	NULL,									/* call_method */
1390 	zend_std_get_constructor,				/* get_constructor */
1391 	zend_std_object_get_class,				/* get_class_entry */
1392 	zend_std_object_get_class_name,			/* get_class_name */
1393 	zend_std_compare_objects,				/* compare_objects */
1394 	zend_std_cast_object_tostring,			/* cast_object */
1395 	NULL,									/* count_elements */
1396 	NULL,									/* get_debug_info */
1397 	zend_std_get_closure,					/* get_closure */
1398 };
1399 
1400 /*
1401  * Local variables:
1402  * tab-width: 4
1403  * c-basic-offset: 4
1404  * indent-tabs-mode: t
1405  * End:
1406  */
1407