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
54ZEND_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
62ZEND_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
69static 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
94static 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
124static 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
140static 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
161static 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
180static 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
194ZEND_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
264ZEND_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
289static 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
324zval *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
417static 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
508zval *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
542static 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
560static 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
591static 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
638static 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
689static 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
703ZEND_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 */
752static 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
787ZEND_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 */
795ZEND_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
822static 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
828static 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
846static 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
919ZEND_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
963static 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
983ZEND_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
1054ZEND_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
1097ZEND_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
1104ZEND_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
1141int zend_compare_symbol_tables_i(HashTable *ht1, HashTable *ht2 TSRMLS_DC);
1142
1143static 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
1157static 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
1243zend_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
1252int 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
1273ZEND_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
1344int 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
1371ZEND_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