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