Back to index

php5  5.3.10
zend_object_handlers.c
Go to the documentation of this file.
00001 /*
00002    +----------------------------------------------------------------------+
00003    | Zend Engine                                                          |
00004    +----------------------------------------------------------------------+
00005    | Copyright (c) 1998-2012 Zend Technologies Ltd. (http://www.zend.com) |
00006    +----------------------------------------------------------------------+
00007    | This source file is subject to version 2.00 of the Zend license,     |
00008    | that is bundled with this package in the file LICENSE, and is        |
00009    | available through the world-wide-web at the following url:           |
00010    | http://www.zend.com/license/2_00.txt.                                |
00011    | If you did not receive a copy of the Zend license and are unable to  |
00012    | obtain it through the world-wide-web, please send a note to          |
00013    | license@zend.com so we can mail you a copy immediately.              |
00014    +----------------------------------------------------------------------+
00015    | Authors: Andi Gutmans <andi@zend.com>                                |
00016    |          Zeev Suraski <zeev@zend.com>                                |
00017    +----------------------------------------------------------------------+
00018 */
00019 
00020 /* $Id: zend_object_handlers.c 321634 2012-01-01 13:15:04Z felipe $ */
00021 
00022 #include "zend.h"
00023 #include "zend_globals.h"
00024 #include "zend_variables.h"
00025 #include "zend_API.h"
00026 #include "zend_objects.h"
00027 #include "zend_objects_API.h"
00028 #include "zend_object_handlers.h"
00029 #include "zend_interfaces.h"
00030 #include "zend_closures.h"
00031 #include "zend_compile.h"
00032 
00033 #define DEBUG_OBJECT_HANDLERS 0
00034 
00035 #define Z_OBJ_P(zval_p) zend_objects_get_address(zval_p TSRMLS_CC)
00036 
00037 /*
00038   __X accessors explanation:
00039 
00040   if we have __get and property that is not part of the properties array is
00041   requested, we call __get handler. If it fails, we return uninitialized.
00042 
00043   if we have __set and property that is not part of the properties array is
00044   set, we call __set handler. If it fails, we do not change the array.
00045 
00046   for both handlers above, when we are inside __get/__set, no further calls for
00047   __get/__set for this property of this object will be made, to prevent endless
00048   recursion and enable accessors to change properties array.
00049 
00050   if we have __call and method which is not part of the class function table is
00051   called, we cal __call handler.
00052 */
00053 
00054 ZEND_API HashTable *zend_std_get_properties(zval *object TSRMLS_DC) /* {{{ */
00055 {
00056        zend_object *zobj;
00057        zobj = Z_OBJ_P(object);
00058        return zobj->properties;
00059 }
00060 /* }}} */
00061 
00062 ZEND_API HashTable *zend_std_get_debug_info(zval *object, int *is_temp TSRMLS_DC) /* {{{ */
00063 {
00064        *is_temp = 0;
00065        return zend_std_get_properties(object TSRMLS_CC);
00066 }
00067 /* }}} */
00068 
00069 static zval *zend_std_call_getter(zval *object, zval *member TSRMLS_DC) /* {{{ */
00070 {
00071        zval *retval = NULL;
00072        zend_class_entry *ce = Z_OBJCE_P(object);
00073 
00074        /* __get handler is called with one argument:
00075              property name
00076 
00077           it should return whether the call was successfull or not
00078        */
00079 
00080        SEPARATE_ARG_IF_REF(member);
00081 
00082        zend_call_method_with_1_params(&object, ce, &ce->__get, ZEND_GET_FUNC_NAME, &retval, member);
00083 
00084        zval_ptr_dtor(&member);
00085 
00086        if (retval) {
00087               Z_DELREF_P(retval);
00088        }
00089 
00090        return retval;
00091 }
00092 /* }}} */
00093 
00094 static int zend_std_call_setter(zval *object, zval *member, zval *value TSRMLS_DC) /* {{{ */
00095 {
00096        zval *retval = NULL;
00097        int result;
00098        zend_class_entry *ce = Z_OBJCE_P(object);
00099 
00100        SEPARATE_ARG_IF_REF(member);
00101        Z_ADDREF_P(value);
00102 
00103        /* __set handler is called with two arguments:
00104             property name
00105             value to be set
00106 
00107           it should return whether the call was successfull or not
00108        */
00109        zend_call_method_with_2_params(&object, ce, &ce->__set, ZEND_SET_FUNC_NAME, &retval, member, value);
00110 
00111        zval_ptr_dtor(&member);
00112        zval_ptr_dtor(&value);
00113 
00114        if (retval) {
00115               result = i_zend_is_true(retval) ? SUCCESS : FAILURE;
00116               zval_ptr_dtor(&retval);
00117               return result;
00118        } else {
00119               return FAILURE;
00120        }
00121 }
00122 /* }}} */
00123 
00124 static void zend_std_call_unsetter(zval *object, zval *member TSRMLS_DC) /* {{{ */
00125 {
00126        zend_class_entry *ce = Z_OBJCE_P(object);
00127 
00128        /* __unset handler is called with one argument:
00129              property name
00130        */
00131 
00132        SEPARATE_ARG_IF_REF(member);
00133 
00134        zend_call_method_with_1_params(&object, ce, &ce->__unset, ZEND_UNSET_FUNC_NAME, NULL, member);
00135 
00136        zval_ptr_dtor(&member);
00137 }
00138 /* }}} */
00139 
00140 static zval *zend_std_call_issetter(zval *object, zval *member TSRMLS_DC) /* {{{ */
00141 {
00142        zval *retval = NULL;
00143        zend_class_entry *ce = Z_OBJCE_P(object);
00144 
00145        /* __isset handler is called with one argument:
00146              property name
00147 
00148           it should return whether the property is set or not
00149        */
00150 
00151        SEPARATE_ARG_IF_REF(member);
00152 
00153        zend_call_method_with_1_params(&object, ce, &ce->__isset, ZEND_ISSET_FUNC_NAME, &retval, member);
00154 
00155        zval_ptr_dtor(&member);
00156 
00157        return retval;
00158 }
00159 /* }}} */
00160 
00161 static int zend_verify_property_access(zend_property_info *property_info, zend_class_entry *ce TSRMLS_DC) /* {{{ */
00162 {
00163        switch (property_info->flags & ZEND_ACC_PPP_MASK) {
00164               case ZEND_ACC_PUBLIC:
00165                      return 1;
00166               case ZEND_ACC_PROTECTED:
00167                      return zend_check_protected(property_info->ce, EG(scope));
00168               case ZEND_ACC_PRIVATE:
00169                      if ((ce==EG(scope) || property_info->ce == EG(scope)) && EG(scope)) {
00170                             return 1;
00171                      } else {
00172                             return 0;
00173                      }
00174                      break;
00175        }
00176        return 0;
00177 }
00178 /* }}} */
00179 
00180 static inline zend_bool is_derived_class(zend_class_entry *child_class, zend_class_entry *parent_class) /* {{{ */
00181 {
00182        child_class = child_class->parent;
00183        while (child_class) {
00184               if (child_class == parent_class) {
00185                      return 1;
00186               }
00187               child_class = child_class->parent;
00188        }
00189 
00190        return 0;
00191 }
00192 /* }}} */
00193 
00194 ZEND_API struct _zend_property_info *zend_get_property_info(zend_class_entry *ce, zval *member, int silent TSRMLS_DC) /* {{{ */
00195 {
00196        zend_property_info *property_info = NULL;
00197        zend_property_info *scope_property_info;
00198        zend_bool denied_access = 0;
00199        ulong h;
00200 
00201        if (Z_STRVAL_P(member)[0] == '\0') {
00202               if (!silent) {
00203                      if (Z_STRLEN_P(member) == 0) {
00204                             zend_error(E_ERROR, "Cannot access empty property");
00205                      } else {
00206                             zend_error(E_ERROR, "Cannot access property started with '\\0'");
00207                      }
00208               }
00209               return NULL;
00210        }
00211        h = zend_get_hash_value(Z_STRVAL_P(member), Z_STRLEN_P(member) + 1);
00212        if (zend_hash_quick_find(&ce->properties_info, Z_STRVAL_P(member), Z_STRLEN_P(member)+1, h, (void **) &property_info)==SUCCESS) {
00213               if(property_info->flags & ZEND_ACC_SHADOW) {
00214                      /* if it's a shadow - go to access it's private */
00215                      property_info = NULL;
00216               } else {
00217                      if (zend_verify_property_access(property_info, ce TSRMLS_CC)) {
00218                             if (property_info->flags & ZEND_ACC_CHANGED
00219                                    && !(property_info->flags & ZEND_ACC_PRIVATE)) {
00220                                    /* We still need to make sure that we're not in a context
00221                                     * where the right property is a different 'statically linked' private
00222                                     * continue checking below...
00223                                     */
00224                             } else {
00225                                    if (!silent && (property_info->flags & ZEND_ACC_STATIC)) {
00226                                           zend_error(E_STRICT, "Accessing static property %s::$%s as non static", ce->name, Z_STRVAL_P(member));
00227                                    }
00228                                    return property_info;
00229                             }
00230                      } else {
00231                             /* Try to look in the scope instead */
00232                             denied_access = 1;
00233                      }
00234               }
00235        }
00236        if (EG(scope) != ce
00237               && is_derived_class(ce, EG(scope))
00238               && EG(scope)
00239               && zend_hash_quick_find(&EG(scope)->properties_info, Z_STRVAL_P(member), Z_STRLEN_P(member)+1, h, (void **) &scope_property_info)==SUCCESS
00240               && scope_property_info->flags & ZEND_ACC_PRIVATE) {
00241               return scope_property_info;
00242        } else if (property_info) {
00243               if (denied_access) {
00244                      /* Information was available, but we were denied access.  Error out. */
00245                      if (silent) {
00246                             return NULL;
00247                      }
00248                      zend_error(E_ERROR, "Cannot access %s property %s::$%s", zend_visibility_string(property_info->flags), ce->name, Z_STRVAL_P(member));
00249               } else {
00250                      /* fall through, return property_info... */
00251               }
00252        } else {
00253               EG(std_property_info).flags = ZEND_ACC_PUBLIC;
00254               EG(std_property_info).name = Z_STRVAL_P(member);
00255               EG(std_property_info).name_length = Z_STRLEN_P(member);
00256               EG(std_property_info).h = h;
00257               EG(std_property_info).ce = ce;
00258               property_info = &EG(std_property_info);
00259        }
00260        return property_info;
00261 }
00262 /* }}} */
00263 
00264 ZEND_API int zend_check_property_access(zend_object *zobj, char *prop_info_name, int prop_info_name_len TSRMLS_DC) /* {{{ */
00265 {
00266        zend_property_info *property_info;
00267        char *class_name, *prop_name;
00268        zval member;
00269 
00270        zend_unmangle_property_name(prop_info_name, prop_info_name_len, &class_name, &prop_name);
00271        ZVAL_STRING(&member, prop_name, 0);
00272        property_info = zend_get_property_info(zobj->ce, &member, 1 TSRMLS_CC);
00273        if (!property_info) {
00274               return FAILURE;
00275        }
00276        if (class_name && class_name[0] != '*') {
00277               if (!(property_info->flags & ZEND_ACC_PRIVATE)) {
00278                      /* we we're looking for a private prop but found a non private one of the same name */
00279                      return FAILURE;
00280               } else if (strcmp(prop_info_name+1, property_info->name+1)) {
00281                      /* we we're looking for a private prop but found a private one of the same name but another class */
00282                      return FAILURE;
00283               }
00284        }
00285        return zend_verify_property_access(property_info, zobj->ce TSRMLS_CC) ? SUCCESS : FAILURE;
00286 }
00287 /* }}} */
00288 
00289 static int zend_get_property_guard(zend_object *zobj, zend_property_info *property_info, zval *member, zend_guard **pguard) /* {{{ */
00290 {
00291        zend_property_info info;
00292        zend_guard stub;
00293 
00294        if (!property_info) {
00295               property_info = &info;
00296               info.name = Z_STRVAL_P(member);
00297               info.name_length = Z_STRLEN_P(member);
00298               info.h = zend_get_hash_value(Z_STRVAL_P(member), Z_STRLEN_P(member) + 1);
00299        }
00300        if (!zobj->guards) {
00301               ALLOC_HASHTABLE(zobj->guards);
00302               zend_hash_init(zobj->guards, 0, NULL, NULL, 0);
00303        } else if (zend_hash_quick_find(zobj->guards, property_info->name, property_info->name_length+1, property_info->h, (void **) pguard) == SUCCESS) {
00304               return SUCCESS;
00305        }
00306        stub.in_get = 0;
00307        stub.in_set = 0;
00308        stub.in_unset = 0;
00309        stub.in_isset = 0;
00310        return zend_hash_quick_add(zobj->guards, property_info->name, property_info->name_length+1, property_info->h, (void**)&stub, sizeof(stub), (void**) pguard);
00311 }
00312 /* }}} */
00313 
00314 zval *zend_std_read_property(zval *object, zval *member, int type TSRMLS_DC) /* {{{ */
00315 {
00316        zend_object *zobj;
00317        zval *tmp_member = NULL;
00318        zval **retval;
00319        zval *rv = NULL;
00320        zend_property_info *property_info;
00321        int silent;
00322 
00323        silent = (type == BP_VAR_IS);
00324        zobj = Z_OBJ_P(object);
00325 
00326        if (Z_TYPE_P(member) != IS_STRING) {
00327               ALLOC_ZVAL(tmp_member);
00328               *tmp_member = *member;
00329               INIT_PZVAL(tmp_member);
00330               zval_copy_ctor(tmp_member);
00331               convert_to_string(tmp_member);
00332               member = tmp_member;
00333        }
00334 
00335 #if DEBUG_OBJECT_HANDLERS
00336        fprintf(stderr, "Read object #%d property: %s\n", Z_OBJ_HANDLE_P(object), Z_STRVAL_P(member));
00337 #endif
00338 
00339        /* make zend_get_property_info silent if we have getter - we may want to use it */
00340        property_info = zend_get_property_info(zobj->ce, member, (zobj->ce->__get != NULL) TSRMLS_CC);
00341 
00342        if (!property_info || zend_hash_quick_find(zobj->properties, property_info->name, property_info->name_length+1, property_info->h, (void **) &retval) == FAILURE) {
00343               zend_guard *guard = NULL;
00344 
00345               if (zobj->ce->__get &&
00346                   zend_get_property_guard(zobj, property_info, member, &guard) == SUCCESS &&
00347                   !guard->in_get) {
00348                      /* have getter - try with it! */
00349                      Z_ADDREF_P(object);
00350                      if (PZVAL_IS_REF(object)) {
00351                             SEPARATE_ZVAL(&object);
00352                      }
00353                      guard->in_get = 1; /* prevent circular getting */
00354                      rv = zend_std_call_getter(object, member TSRMLS_CC);
00355                      guard->in_get = 0;
00356 
00357                      if (rv) {
00358                             retval = &rv;
00359                             if (!Z_ISREF_P(rv) &&
00360                                 (type == BP_VAR_W || type == BP_VAR_RW  || type == BP_VAR_UNSET)) {
00361                                    if (Z_REFCOUNT_P(rv) > 0) {
00362                                           zval *tmp = rv;
00363 
00364                                           ALLOC_ZVAL(rv);
00365                                           *rv = *tmp;
00366                                           zval_copy_ctor(rv);
00367                                           Z_UNSET_ISREF_P(rv);
00368                                           Z_SET_REFCOUNT_P(rv, 0);
00369                                    }
00370                                    if (Z_TYPE_P(rv) != IS_OBJECT) {
00371                                           zend_error(E_NOTICE, "Indirect modification of overloaded property %s::$%s has no effect", zobj->ce->name, Z_STRVAL_P(member));
00372                                    }
00373                             }
00374                      } else {
00375                             retval = &EG(uninitialized_zval_ptr);
00376                      }
00377                      if (EXPECTED(*retval != object)) {
00378                             zval_ptr_dtor(&object);
00379                      } else {
00380                             Z_DELREF_P(object);
00381                      }
00382               } else {
00383                      if (zobj->ce->__get && guard && guard->in_get == 1) {
00384                             if (Z_STRVAL_P(member)[0] == '\0') {
00385                                    if (Z_STRLEN_P(member) == 0) {
00386                                           zend_error(E_ERROR, "Cannot access empty property");
00387                                    } else {
00388                                           zend_error(E_ERROR, "Cannot access property started with '\\0'");
00389                                    }
00390                             }
00391                      }
00392                      if (!silent) {
00393                             zend_error(E_NOTICE,"Undefined property: %s::$%s", zobj->ce->name, Z_STRVAL_P(member));
00394                      }
00395                      retval = &EG(uninitialized_zval_ptr);
00396               }
00397        }
00398        if (tmp_member) {
00399               Z_ADDREF_PP(retval);
00400               zval_ptr_dtor(&tmp_member);
00401               Z_DELREF_PP(retval);
00402        }
00403        return *retval;
00404 }
00405 /* }}} */
00406 
00407 static void zend_std_write_property(zval *object, zval *member, zval *value TSRMLS_DC) /* {{{ */
00408 {
00409        zend_object *zobj;
00410        zval *tmp_member = NULL;
00411        zval **variable_ptr;
00412        zend_property_info *property_info;
00413 
00414        zobj = Z_OBJ_P(object);
00415 
00416        if (Z_TYPE_P(member) != IS_STRING) {
00417               ALLOC_ZVAL(tmp_member);
00418               *tmp_member = *member;
00419               INIT_PZVAL(tmp_member);
00420               zval_copy_ctor(tmp_member);
00421               convert_to_string(tmp_member);
00422               member = tmp_member;
00423        }
00424 
00425        property_info = zend_get_property_info(zobj->ce, member, (zobj->ce->__set != NULL) TSRMLS_CC);
00426 
00427        if (property_info && zend_hash_quick_find(zobj->properties, property_info->name, property_info->name_length+1, property_info->h, (void **) &variable_ptr) == SUCCESS) {
00428               /* if we already have this value there, we don't actually need to do anything */
00429               if (*variable_ptr != value) {
00430                      /* if we are assigning reference, we shouldn't move it, but instead assign variable
00431                         to the same pointer */
00432                      if (PZVAL_IS_REF(*variable_ptr)) {
00433                             zval garbage = **variable_ptr; /* old value should be destroyed */
00434 
00435                             /* To check: can't *variable_ptr be some system variable like error_zval here? */
00436                             Z_TYPE_PP(variable_ptr) = Z_TYPE_P(value);
00437                             (*variable_ptr)->value = value->value;
00438                             if (Z_REFCOUNT_P(value) > 0) {
00439                                    zval_copy_ctor(*variable_ptr);
00440                             }
00441                             zval_dtor(&garbage);
00442                      } else {
00443                             zval *garbage = *variable_ptr;
00444 
00445                             /* if we assign referenced variable, we should separate it */
00446                             Z_ADDREF_P(value);
00447                             if (PZVAL_IS_REF(value)) {
00448                                    SEPARATE_ZVAL(&value);
00449                             }
00450                             *variable_ptr = value;
00451                             zval_ptr_dtor(&garbage);
00452                      }
00453               }
00454        } else {
00455               zend_guard *guard = NULL;
00456 
00457               if (zobj->ce->__set &&
00458                   zend_get_property_guard(zobj, property_info, member, &guard) == SUCCESS &&
00459                   !guard->in_set) {
00460                      Z_ADDREF_P(object);
00461                      if (PZVAL_IS_REF(object)) {
00462                             SEPARATE_ZVAL(&object);
00463                      }
00464                      guard->in_set = 1; /* prevent circular setting */
00465                      if (zend_std_call_setter(object, member, value TSRMLS_CC) != SUCCESS) {
00466                             /* for now, just ignore it - __set should take care of warnings, etc. */
00467                      }
00468                      guard->in_set = 0;
00469                      zval_ptr_dtor(&object);
00470               } else if (property_info) {
00471                      zval **foo;
00472 
00473                      /* if we assign referenced variable, we should separate it */
00474                      Z_ADDREF_P(value);
00475                      if (PZVAL_IS_REF(value)) {
00476                             SEPARATE_ZVAL(&value);
00477                      }
00478                      zend_hash_quick_update(zobj->properties, property_info->name, property_info->name_length+1, property_info->h, &value, sizeof(zval *), (void **) &foo);
00479               } else if (zobj->ce->__set && guard && guard->in_set == 1) {
00480                      if (Z_STRVAL_P(member)[0] == '\0') {
00481                             if (Z_STRLEN_P(member) == 0) {
00482                                    zend_error(E_ERROR, "Cannot access empty property");
00483                             } else {
00484                                    zend_error(E_ERROR, "Cannot access property started with '\\0'");
00485                             }
00486                      }
00487               }
00488        }
00489 
00490        if (tmp_member) {
00491               zval_ptr_dtor(&tmp_member);
00492        }
00493 }
00494 /* }}} */
00495 
00496 zval *zend_std_read_dimension(zval *object, zval *offset, int type TSRMLS_DC) /* {{{ */
00497 {
00498        zend_class_entry *ce = Z_OBJCE_P(object);
00499        zval *retval;
00500 
00501        if (instanceof_function_ex(ce, zend_ce_arrayaccess, 1 TSRMLS_CC)) {
00502               if(offset == NULL) {
00503                      /* [] construct */
00504                      ALLOC_INIT_ZVAL(offset);
00505               } else {
00506                      SEPARATE_ARG_IF_REF(offset);
00507               }
00508               zend_call_method_with_1_params(&object, ce, NULL, "offsetget", &retval, offset);
00509 
00510               zval_ptr_dtor(&offset);
00511 
00512               if (!retval) {
00513                      if (!EG(exception)) {
00514                             zend_error(E_ERROR, "Undefined offset for object of type %s used as array", ce->name);
00515                      }
00516                      return 0;
00517               }
00518 
00519               /* Undo PZVAL_LOCK() */
00520               Z_DELREF_P(retval);
00521 
00522               return retval;
00523        } else {
00524               zend_error(E_ERROR, "Cannot use object of type %s as array", ce->name);
00525               return 0;
00526        }
00527 }
00528 /* }}} */
00529 
00530 static void zend_std_write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC) /* {{{ */
00531 {
00532        zend_class_entry *ce = Z_OBJCE_P(object);
00533 
00534        if (instanceof_function_ex(ce, zend_ce_arrayaccess, 1 TSRMLS_CC)) {
00535               if (!offset) {
00536                      ALLOC_INIT_ZVAL(offset);
00537               } else {
00538                      SEPARATE_ARG_IF_REF(offset);
00539               }
00540               zend_call_method_with_2_params(&object, ce, NULL, "offsetset", NULL, offset, value);
00541               zval_ptr_dtor(&offset);
00542        } else {
00543               zend_error(E_ERROR, "Cannot use object of type %s as array", ce->name);
00544        }
00545 }
00546 /* }}} */
00547 
00548 static int zend_std_has_dimension(zval *object, zval *offset, int check_empty TSRMLS_DC) /* {{{ */
00549 {
00550        zend_class_entry *ce = Z_OBJCE_P(object);
00551        zval *retval;
00552        int result;
00553 
00554        if (instanceof_function_ex(ce, zend_ce_arrayaccess, 1 TSRMLS_CC)) {
00555               SEPARATE_ARG_IF_REF(offset);
00556               zend_call_method_with_1_params(&object, ce, NULL, "offsetexists", &retval, offset);
00557               if (retval) {
00558                      result = i_zend_is_true(retval);
00559                      zval_ptr_dtor(&retval);
00560                      if (check_empty && result && !EG(exception)) {
00561                             zend_call_method_with_1_params(&object, ce, NULL, "offsetget", &retval, offset);
00562                             if (retval) {
00563                                    result = i_zend_is_true(retval);
00564                                    zval_ptr_dtor(&retval);
00565                             }
00566                      }
00567               } else {
00568                      result = 0;
00569               }
00570               zval_ptr_dtor(&offset);
00571        } else {
00572               zend_error(E_ERROR, "Cannot use object of type %s as array", ce->name);
00573               return 0;
00574        }
00575        return result;
00576 }
00577 /* }}} */
00578 
00579 static zval **zend_std_get_property_ptr_ptr(zval *object, zval *member TSRMLS_DC) /* {{{ */
00580 {
00581        zend_object *zobj;
00582        zval tmp_member;
00583        zval **retval;
00584        zend_property_info *property_info;
00585 
00586        zobj = Z_OBJ_P(object);
00587 
00588        if (Z_TYPE_P(member) != IS_STRING) {
00589               tmp_member = *member;
00590               zval_copy_ctor(&tmp_member);
00591               convert_to_string(&tmp_member);
00592               member = &tmp_member;
00593        }
00594 
00595 #if DEBUG_OBJECT_HANDLERS
00596        fprintf(stderr, "Ptr object #%d property: %s\n", Z_OBJ_HANDLE_P(object), Z_STRVAL_P(member));
00597 #endif
00598 
00599        property_info = zend_get_property_info(zobj->ce, member, (zobj->ce->__get != NULL) TSRMLS_CC);
00600 
00601        if (!property_info || zend_hash_quick_find(zobj->properties, property_info->name, property_info->name_length+1, property_info->h, (void **) &retval) == FAILURE) {
00602               zval *new_zval;
00603               zend_guard *guard;
00604 
00605               if (!zobj->ce->__get ||
00606                      zend_get_property_guard(zobj, property_info, member, &guard) != SUCCESS ||
00607                      (property_info && guard->in_get)) {
00608                      /* we don't have access controls - will just add it */
00609                      new_zval = &EG(uninitialized_zval);
00610 
00611 /*                   zend_error(E_NOTICE, "Undefined property: %s", Z_STRVAL_P(member)); */
00612                      Z_ADDREF_P(new_zval);
00613                      zend_hash_quick_update(zobj->properties, property_info->name, property_info->name_length+1, property_info->h, &new_zval, sizeof(zval *), (void **) &retval);
00614               } else {
00615                      /* we do have getter - fail and let it try again with usual get/set */
00616                      retval = NULL;
00617               }
00618        }
00619        if (member == &tmp_member) {
00620               zval_dtor(member);
00621        }
00622        return retval;
00623 }
00624 /* }}} */
00625 
00626 static void zend_std_unset_property(zval *object, zval *member TSRMLS_DC) /* {{{ */
00627 {
00628        zend_object *zobj;
00629        zval *tmp_member = NULL;
00630        zend_property_info *property_info;
00631 
00632        zobj = Z_OBJ_P(object);
00633 
00634        if (Z_TYPE_P(member) != IS_STRING) {
00635               ALLOC_ZVAL(tmp_member);
00636               *tmp_member = *member;
00637               INIT_PZVAL(tmp_member);
00638               zval_copy_ctor(tmp_member);
00639               convert_to_string(tmp_member);
00640               member = tmp_member;
00641        }
00642 
00643        property_info = zend_get_property_info(zobj->ce, member, (zobj->ce->__unset != NULL) TSRMLS_CC);
00644 
00645        if (!property_info || zend_hash_quick_del(zobj->properties, property_info->name, property_info->name_length+1, property_info->h) == FAILURE) {
00646               zend_guard *guard = NULL;
00647 
00648               if (zobj->ce->__unset &&
00649                   zend_get_property_guard(zobj, property_info, member, &guard) == SUCCESS &&
00650                   !guard->in_unset) {
00651                      /* have unseter - try with it! */
00652                      Z_ADDREF_P(object);
00653                      if (PZVAL_IS_REF(object)) {
00654                             SEPARATE_ZVAL(&object);
00655                      }
00656                      guard->in_unset = 1; /* prevent circular unsetting */
00657                      zend_std_call_unsetter(object, member TSRMLS_CC);
00658                      guard->in_unset = 0;
00659                      zval_ptr_dtor(&object);
00660               } else if (zobj->ce->__unset && guard && guard->in_unset == 1) {
00661                      if (Z_STRVAL_P(member)[0] == '\0') {
00662                             if (Z_STRLEN_P(member) == 0) {
00663                                    zend_error(E_ERROR, "Cannot access empty property");
00664                             } else {
00665                                    zend_error(E_ERROR, "Cannot access property started with '\\0'");
00666                             }
00667                      }
00668               }
00669        }
00670 
00671        if (tmp_member) {
00672               zval_ptr_dtor(&tmp_member);
00673        }
00674 }
00675 /* }}} */
00676 
00677 static void zend_std_unset_dimension(zval *object, zval *offset TSRMLS_DC) /* {{{ */
00678 {
00679        zend_class_entry *ce = Z_OBJCE_P(object);
00680 
00681        if (instanceof_function_ex(ce, zend_ce_arrayaccess, 1 TSRMLS_CC)) {
00682               SEPARATE_ARG_IF_REF(offset);
00683               zend_call_method_with_1_params(&object, ce, NULL, "offsetunset", NULL, offset);
00684               zval_ptr_dtor(&offset);
00685        } else {
00686               zend_error(E_ERROR, "Cannot use object of type %s as array", ce->name);
00687        }
00688 }
00689 /* }}} */
00690 
00691 ZEND_API void zend_std_call_user_call(INTERNAL_FUNCTION_PARAMETERS) /* {{{ */
00692 {
00693        zend_internal_function *func = (zend_internal_function *)EG(current_execute_data)->function_state.function;
00694        zval *method_name_ptr, *method_args_ptr;
00695        zval *method_result_ptr = NULL;
00696        zend_class_entry *ce = Z_OBJCE_P(this_ptr);
00697 
00698        ALLOC_ZVAL(method_args_ptr);
00699        INIT_PZVAL(method_args_ptr);
00700        array_init_size(method_args_ptr, ZEND_NUM_ARGS());
00701 
00702        if (zend_copy_parameters_array(ZEND_NUM_ARGS(), method_args_ptr TSRMLS_CC) == FAILURE) {
00703               zval_dtor(method_args_ptr);
00704               zend_error(E_ERROR, "Cannot get arguments for __call");
00705               RETURN_FALSE;
00706        }
00707 
00708        ALLOC_ZVAL(method_name_ptr);
00709        INIT_PZVAL(method_name_ptr);
00710        ZVAL_STRING(method_name_ptr, func->function_name, 0); /* no dup - it's a copy */
00711 
00712        /* __call handler is called with two arguments:
00713           method name
00714           array of method parameters
00715 
00716        */
00717        zend_call_method_with_2_params(&this_ptr, ce, &ce->__call, ZEND_CALL_FUNC_NAME, &method_result_ptr, method_name_ptr, method_args_ptr);
00718 
00719        if (method_result_ptr) {
00720               if (Z_ISREF_P(method_result_ptr) || Z_REFCOUNT_P(method_result_ptr) > 1) {
00721                      RETVAL_ZVAL(method_result_ptr, 1, 1);
00722               } else {
00723                      RETVAL_ZVAL(method_result_ptr, 0, 1);
00724               }
00725        }
00726 
00727        /* now destruct all auxiliaries */
00728        zval_ptr_dtor(&method_args_ptr);
00729        zval_ptr_dtor(&method_name_ptr);
00730 
00731        /* destruct the function also, then - we have allocated it in get_method */
00732        efree(func);
00733 }
00734 /* }}} */
00735 
00736 /* Ensures that we're allowed to call a private method.
00737  * Returns the function address that should be called, or NULL
00738  * if no such function exists.
00739  */
00740 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) /* {{{ */
00741 {
00742        if (!ce) {
00743               return 0;
00744        }
00745 
00746        /* We may call a private function if:
00747         * 1.  The class of our object is the same as the scope, and the private
00748         *     function (EX(fbc)) has the same scope.
00749         * 2.  One of our parent classes are the same as the scope, and it contains
00750         *     a private function with the same name that has the same scope.
00751         */
00752        if (fbc->common.scope == ce && EG(scope) == ce) {
00753               /* rule #1 checks out ok, allow the function call */
00754               return fbc;
00755        }
00756 
00757 
00758        /* Check rule #2 */
00759        ce = ce->parent;
00760        while (ce) {
00761               if (ce == EG(scope)) {
00762                      if (zend_hash_find(&ce->function_table, function_name_strval, function_name_strlen+1, (void **) &fbc)==SUCCESS
00763                             && fbc->op_array.fn_flags & ZEND_ACC_PRIVATE
00764                             && fbc->common.scope == EG(scope)) {
00765                             return fbc;
00766                      }
00767                      break;
00768               }
00769               ce = ce->parent;
00770        }
00771        return NULL;
00772 }
00773 /* }}} */
00774 
00775 ZEND_API int zend_check_private(zend_function *fbc, zend_class_entry *ce, char *function_name_strval, int function_name_strlen TSRMLS_DC) /* {{{ */
00776 {
00777        return zend_check_private_int(fbc, ce, function_name_strval, function_name_strlen TSRMLS_CC) != NULL;
00778 }
00779 /* }}} */
00780 
00781 /* Ensures that we're allowed to call a protected method.
00782  */
00783 ZEND_API int zend_check_protected(zend_class_entry *ce, zend_class_entry *scope) /* {{{ */
00784 {
00785        zend_class_entry *fbc_scope = ce;
00786 
00787        /* Is the context that's calling the function, the same as one of
00788         * the function's parents?
00789         */
00790        while (fbc_scope) {
00791               if (fbc_scope==scope) {
00792                      return 1;
00793               }
00794               fbc_scope = fbc_scope->parent;
00795        }
00796 
00797        /* Is the function's scope the same as our current object context,
00798         * or any of the parents of our context?
00799         */
00800        while (scope) {
00801               if (scope==ce) {
00802                      return 1;
00803               }
00804               scope = scope->parent;
00805        }
00806        return 0;
00807 }
00808 /* }}} */
00809 
00810 static inline zend_class_entry * zend_get_function_root_class(zend_function *fbc) /* {{{ */
00811 {
00812        return fbc->common.prototype ? fbc->common.prototype->common.scope : fbc->common.scope;
00813 }
00814 /* }}} */
00815 
00816 static inline union _zend_function *zend_get_user_call_function(zend_class_entry *ce, const char *method_name, int method_len) /* {{{ */
00817 {
00818        zend_internal_function *call_user_call = emalloc(sizeof(zend_internal_function));
00819        call_user_call->type = ZEND_INTERNAL_FUNCTION;
00820        call_user_call->module = ce->module;
00821        call_user_call->handler = zend_std_call_user_call;
00822        call_user_call->arg_info = NULL;
00823        call_user_call->num_args = 0;
00824        call_user_call->scope = ce;
00825        call_user_call->fn_flags = ZEND_ACC_CALL_VIA_HANDLER;
00826        call_user_call->function_name = estrndup(method_name, method_len);
00827        call_user_call->pass_rest_by_reference = 0;
00828        call_user_call->return_reference = ZEND_RETURN_VALUE;
00829 
00830        return (union _zend_function *)call_user_call;
00831 }
00832 /* }}} */
00833 
00834 static union _zend_function *zend_std_get_method(zval **object_ptr, char *method_name, int method_len TSRMLS_DC) /* {{{ */
00835 {
00836        zend_object *zobj;
00837        zend_function *fbc;
00838        char *lc_method_name;
00839        zval *object = *object_ptr;
00840        ALLOCA_FLAG(use_heap)
00841 
00842        lc_method_name = do_alloca(method_len+1, use_heap);
00843        /* Create a zend_copy_str_tolower(dest, src, src_length); */
00844        zend_str_tolower_copy(lc_method_name, method_name, method_len);
00845 
00846        zobj = Z_OBJ_P(object);
00847        if (zend_hash_find(&zobj->ce->function_table, lc_method_name, method_len+1, (void **)&fbc) == FAILURE) {
00848               free_alloca(lc_method_name, use_heap);
00849               if (zobj->ce->__call) {
00850                      return zend_get_user_call_function(zobj->ce, method_name, method_len);
00851               } else {
00852                      return NULL;
00853               }
00854        }
00855 
00856        /* Check access level */
00857        if (fbc->op_array.fn_flags & ZEND_ACC_PRIVATE) {
00858               zend_function *updated_fbc;
00859 
00860               /* Ensure that if we're calling a private function, we're allowed to do so.
00861                * If we're not and __call() handler exists, invoke it, otherwise error out.
00862                */
00863               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);
00864               if (updated_fbc) {
00865                      fbc = updated_fbc;
00866               } else {
00867                      if (zobj->ce->__call) {
00868                             fbc = zend_get_user_call_function(zobj->ce, method_name, method_len);
00869                      } else {
00870                             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 : "");
00871                      }
00872               }
00873        } else {
00874               /* Ensure that we haven't overridden a private function and end up calling
00875                * the overriding public function...
00876                */
00877               if (EG(scope) &&
00878                   is_derived_class(fbc->common.scope, EG(scope)) &&
00879                   fbc->op_array.fn_flags & ZEND_ACC_CHANGED) {
00880                      zend_function *priv_fbc;
00881 
00882                      if (zend_hash_find(&EG(scope)->function_table, lc_method_name, method_len+1, (void **) &priv_fbc)==SUCCESS
00883                             && priv_fbc->common.fn_flags & ZEND_ACC_PRIVATE
00884                             && priv_fbc->common.scope == EG(scope)) {
00885                             fbc = priv_fbc;
00886                      }
00887               }
00888               if ((fbc->common.fn_flags & ZEND_ACC_PROTECTED)) {
00889                      /* Ensure that if we're calling a protected function, we're allowed to do so.
00890                       * If we're not and __call() handler exists, invoke it, otherwise error out.
00891                       */
00892                      if (!zend_check_protected(zend_get_function_root_class(fbc), EG(scope))) {
00893                             if (zobj->ce->__call) {
00894                                    fbc = zend_get_user_call_function(zobj->ce, method_name, method_len);
00895                             } else {
00896                                    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 : "");
00897                             }
00898                      }
00899               }
00900        }
00901 
00902        free_alloca(lc_method_name, use_heap);
00903        return fbc;
00904 }
00905 /* }}} */
00906 
00907 ZEND_API void zend_std_callstatic_user_call(INTERNAL_FUNCTION_PARAMETERS) /* {{{ */
00908 {
00909        zend_internal_function *func = (zend_internal_function *)EG(current_execute_data)->function_state.function;
00910        zval *method_name_ptr, *method_args_ptr;
00911        zval *method_result_ptr = NULL;
00912        zend_class_entry *ce = EG(scope);
00913 
00914        ALLOC_ZVAL(method_args_ptr);
00915        INIT_PZVAL(method_args_ptr);
00916        array_init_size(method_args_ptr, ZEND_NUM_ARGS());
00917 
00918        if (zend_copy_parameters_array(ZEND_NUM_ARGS(), method_args_ptr TSRMLS_CC) == FAILURE) {
00919               zval_dtor(method_args_ptr);
00920               zend_error(E_ERROR, "Cannot get arguments for " ZEND_CALLSTATIC_FUNC_NAME);
00921               RETURN_FALSE;
00922        }
00923 
00924        ALLOC_ZVAL(method_name_ptr);
00925        INIT_PZVAL(method_name_ptr);
00926        ZVAL_STRING(method_name_ptr, func->function_name, 0); /* no dup - it's a copy */
00927 
00928        /* __callStatic handler is called with two arguments:
00929           method name
00930           array of method parameters
00931        */
00932        zend_call_method_with_2_params(NULL, ce, &ce->__callstatic, ZEND_CALLSTATIC_FUNC_NAME, &method_result_ptr, method_name_ptr, method_args_ptr);
00933 
00934        if (method_result_ptr) {
00935               if (Z_ISREF_P(method_result_ptr) || Z_REFCOUNT_P(method_result_ptr) > 1) {
00936                      RETVAL_ZVAL(method_result_ptr, 1, 1);
00937               } else {
00938                      RETVAL_ZVAL(method_result_ptr, 0, 1);
00939               }
00940        }
00941 
00942        /* now destruct all auxiliaries */
00943        zval_ptr_dtor(&method_args_ptr);
00944        zval_ptr_dtor(&method_name_ptr);
00945 
00946        /* destruct the function also, then - we have allocated it in get_method */
00947        efree(func);
00948 }
00949 /* }}} */
00950 
00951 static inline union _zend_function *zend_get_user_callstatic_function(zend_class_entry *ce, const char *method_name, int method_len) /* {{{ */
00952 {
00953        zend_internal_function *callstatic_user_call = emalloc(sizeof(zend_internal_function));
00954        callstatic_user_call->type     = ZEND_INTERNAL_FUNCTION;
00955        callstatic_user_call->module   = ce->module;
00956        callstatic_user_call->handler  = zend_std_callstatic_user_call;
00957        callstatic_user_call->arg_info = NULL;
00958        callstatic_user_call->num_args = 0;
00959        callstatic_user_call->scope    = ce;
00960        callstatic_user_call->fn_flags = ZEND_ACC_STATIC | ZEND_ACC_PUBLIC | ZEND_ACC_CALL_VIA_HANDLER;
00961        callstatic_user_call->function_name = estrndup(method_name, method_len);
00962        callstatic_user_call->pass_rest_by_reference = 0;
00963        callstatic_user_call->return_reference       = ZEND_RETURN_VALUE;
00964 
00965        return (zend_function *)callstatic_user_call;
00966 }
00967 /* }}} */
00968 
00969 /* This is not (yet?) in the API, but it belongs in the built-in objects callbacks */
00970 
00971 ZEND_API zend_function *zend_std_get_static_method(zend_class_entry *ce, char *function_name_strval, int function_name_strlen TSRMLS_DC) /* {{{ */
00972 {
00973        zend_function *fbc = NULL;
00974        char *lc_class_name, *lc_function_name = NULL;
00975        
00976        lc_function_name = zend_str_tolower_dup(function_name_strval, function_name_strlen);
00977 
00978        if (function_name_strlen == ce->name_length && ce->constructor) {
00979               lc_class_name = zend_str_tolower_dup(ce->name, ce->name_length);
00980               /* Only change the method to the constructor if the constructor isn't called __construct
00981                * we check for __ so we can be binary safe for lowering, we should use ZEND_CONSTRUCTOR_FUNC_NAME
00982                */
00983               if (!memcmp(lc_class_name, lc_function_name, function_name_strlen) && memcmp(ce->constructor->common.function_name, "__", sizeof("__") - 1)) {
00984                      fbc = ce->constructor;
00985               }
00986               efree(lc_class_name);
00987        }
00988        if (!fbc && zend_hash_find(&ce->function_table, lc_function_name, function_name_strlen+1, (void **) &fbc)==FAILURE) {
00989               efree(lc_function_name);
00990 
00991               if (ce->__call &&
00992                   EG(This) &&
00993                   Z_OBJ_HT_P(EG(This))->get_class_entry &&
00994                   instanceof_function(Z_OBJCE_P(EG(This)), ce TSRMLS_CC)) {
00995                      return zend_get_user_call_function(ce, function_name_strval, function_name_strlen);
00996               } else if (ce->__callstatic) {
00997                      return zend_get_user_callstatic_function(ce, function_name_strval, function_name_strlen);
00998               } else {
00999                      return NULL;
01000               }
01001        }
01002        efree(lc_function_name);
01003 
01004 #if MBO_0
01005        /* right now this function is used for non static method lookup too */
01006        /* Is the function static */
01007        if (!(fbc->common.fn_flags & ZEND_ACC_STATIC)) {
01008               zend_error(E_ERROR, "Cannot call non static method %s::%s() without object", ZEND_FN_SCOPE_NAME(fbc), fbc->common.function_name);
01009        }
01010 #endif 
01011        if (fbc->op_array.fn_flags & ZEND_ACC_PUBLIC) {
01012               /* No further checks necessary, most common case */
01013        } else if (fbc->op_array.fn_flags & ZEND_ACC_PRIVATE) {
01014               zend_function *updated_fbc;
01015 
01016               /* Ensure that if we're calling a private function, we're allowed to do so.
01017                */
01018               updated_fbc = zend_check_private_int(fbc, EG(scope), function_name_strval, function_name_strlen TSRMLS_CC);
01019               if (updated_fbc) {
01020                      fbc = updated_fbc;
01021               } else {
01022                      if (ce->__callstatic) {
01023                             return zend_get_user_callstatic_function(ce, function_name_strval, function_name_strlen);
01024                      }
01025                      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 : "");
01026               }
01027        } else if ((fbc->common.fn_flags & ZEND_ACC_PROTECTED)) {
01028               /* Ensure that if we're calling a protected function, we're allowed to do so.
01029                */
01030               if (!zend_check_protected(zend_get_function_root_class(fbc), EG(scope))) {
01031                      if (ce->__callstatic) {
01032                             return zend_get_user_callstatic_function(ce, function_name_strval, function_name_strlen);
01033                      }
01034                      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 : "");
01035               }
01036        }
01037 
01038        return fbc;
01039 }
01040 /* }}} */
01041 
01042 ZEND_API zval **zend_std_get_static_property(zend_class_entry *ce, char *property_name, int property_name_len, zend_bool silent TSRMLS_DC) /* {{{ */
01043 {
01044        zval **retval = NULL;
01045        zend_class_entry *tmp_ce = ce;
01046        zend_property_info *property_info;
01047        zend_property_info std_property_info;
01048 
01049        if (zend_hash_find(&ce->properties_info, property_name, property_name_len+1, (void **) &property_info)==FAILURE) {
01050               std_property_info.flags = ZEND_ACC_PUBLIC;
01051               std_property_info.name = property_name;
01052               std_property_info.name_length = property_name_len;
01053               std_property_info.h = zend_get_hash_value(std_property_info.name, std_property_info.name_length+1);
01054               std_property_info.ce = ce;
01055               property_info = &std_property_info;
01056        }
01057 
01058 #if DEBUG_OBJECT_HANDLERS
01059        zend_printf("Access type for %s::%s is %s\n", ce->name, property_name, zend_visibility_string(property_info->flags));
01060 #endif
01061 
01062        if (!zend_verify_property_access(property_info, ce TSRMLS_CC)) {
01063               if (!silent) {
01064                      zend_error(E_ERROR, "Cannot access %s property %s::$%s", zend_visibility_string(property_info->flags), ce->name, property_name);
01065               }
01066               return NULL;
01067        }
01068 
01069        zend_update_class_constants(tmp_ce TSRMLS_CC);
01070 
01071        zend_hash_quick_find(CE_STATIC_MEMBERS(tmp_ce), property_info->name, property_info->name_length+1, property_info->h, (void **) &retval);
01072 
01073        if (!retval) {
01074               if (silent) {
01075                      return NULL;
01076               } else {
01077                      zend_error(E_ERROR, "Access to undeclared static property: %s::$%s", ce->name, property_name);
01078               }
01079        }
01080 
01081        return retval;
01082 }
01083 /* }}} */
01084 
01085 ZEND_API zend_bool zend_std_unset_static_property(zend_class_entry *ce, char *property_name, int property_name_len TSRMLS_DC) /* {{{ */
01086 {
01087        zend_error(E_ERROR, "Attempt to unset static property %s::$%s", ce->name, property_name);
01088        return 0;
01089 }
01090 /* }}} */
01091 
01092 ZEND_API union _zend_function *zend_std_get_constructor(zval *object TSRMLS_DC) /* {{{ */
01093 {
01094        zend_object *zobj = Z_OBJ_P(object);
01095        zend_function *constructor = zobj->ce->constructor;
01096 
01097        if (constructor) {
01098               if (constructor->op_array.fn_flags & ZEND_ACC_PUBLIC) {
01099                      /* No further checks necessary */
01100               } else if (constructor->op_array.fn_flags & ZEND_ACC_PRIVATE) {
01101                      /* Ensure that if we're calling a private function, we're allowed to do so.
01102                       */
01103                      if (constructor->common.scope != EG(scope)) {
01104                             if (EG(scope)) {
01105                                    zend_error(E_ERROR, "Call to private %s::%s() from context '%s'", constructor->common.scope->name, constructor->common.function_name, EG(scope)->name);
01106                             } else {
01107                                    zend_error(E_ERROR, "Call to private %s::%s() from invalid context", constructor->common.scope->name, constructor->common.function_name);
01108                             }
01109                      }
01110               } else if ((constructor->common.fn_flags & ZEND_ACC_PROTECTED)) {
01111                      /* Ensure that if we're calling a protected function, we're allowed to do so.
01112                       * Constructors only have prototype if they are defined by an interface but
01113                       * it is the compilers responsibility to take care of the prototype.
01114                       */
01115                      if (!zend_check_protected(zend_get_function_root_class(constructor), EG(scope))) {
01116                             if (EG(scope)) {
01117                                    zend_error(E_ERROR, "Call to protected %s::%s() from context '%s'", constructor->common.scope->name, constructor->common.function_name, EG(scope)->name);
01118                             } else {
01119                                    zend_error(E_ERROR, "Call to protected %s::%s() from invalid context", constructor->common.scope->name, constructor->common.function_name);
01120                             }
01121                      }
01122               }
01123        }
01124 
01125        return constructor;
01126 }
01127 /* }}} */
01128 
01129 int zend_compare_symbol_tables_i(HashTable *ht1, HashTable *ht2 TSRMLS_DC);
01130 
01131 static int zend_std_compare_objects(zval *o1, zval *o2 TSRMLS_DC) /* {{{ */
01132 {
01133        zend_object *zobj1, *zobj2;
01134 
01135        zobj1 = Z_OBJ_P(o1);
01136        zobj2 = Z_OBJ_P(o2);
01137 
01138        if (zobj1->ce != zobj2->ce) {
01139               return 1; /* different classes */
01140        }
01141        return zend_compare_symbol_tables_i(zobj1->properties, zobj2->properties TSRMLS_CC);
01142 }
01143 /* }}} */
01144 
01145 static int zend_std_has_property(zval *object, zval *member, int has_set_exists TSRMLS_DC) /* {{{ */
01146 {
01147        zend_object *zobj;
01148        int result;
01149        zval **value;
01150        zval *tmp_member = NULL;
01151        zend_property_info *property_info;
01152 
01153        zobj = Z_OBJ_P(object);
01154 
01155        if (Z_TYPE_P(member) != IS_STRING) {
01156               ALLOC_ZVAL(tmp_member);
01157               *tmp_member = *member;
01158               INIT_PZVAL(tmp_member);
01159               zval_copy_ctor(tmp_member);
01160               convert_to_string(tmp_member);
01161               member = tmp_member;
01162        }
01163 
01164 #if DEBUG_OBJECT_HANDLERS
01165        fprintf(stderr, "Read object #%d property: %s\n", Z_OBJ_HANDLE_P(object), Z_STRVAL_P(member));
01166 #endif
01167 
01168        property_info = zend_get_property_info(zobj->ce, member, 1 TSRMLS_CC);
01169 
01170        if (!property_info || zend_hash_quick_find(zobj->properties, property_info->name, property_info->name_length+1, property_info->h, (void **) &value) == FAILURE) {
01171               zend_guard *guard;
01172 
01173               result = 0;
01174               if ((has_set_exists != 2) &&
01175                   zobj->ce->__isset &&
01176                   zend_get_property_guard(zobj, property_info, member, &guard) == SUCCESS &&
01177                   !guard->in_isset) {
01178                      zval *rv;
01179 
01180                      /* have issetter - try with it! */
01181                      Z_ADDREF_P(object);
01182                      if (PZVAL_IS_REF(object)) {
01183                             SEPARATE_ZVAL(&object);
01184                      }
01185                      guard->in_isset = 1; /* prevent circular getting */
01186                      rv = zend_std_call_issetter(object, member TSRMLS_CC);
01187                      if (rv) {
01188                             result = zend_is_true(rv);
01189                             zval_ptr_dtor(&rv);
01190                             if (has_set_exists && result) {
01191                                    if (!EG(exception) && zobj->ce->__get && !guard->in_get) {
01192                                           guard->in_get = 1;
01193                                           rv = zend_std_call_getter(object, member TSRMLS_CC);
01194                                           guard->in_get = 0;
01195                                           if (rv) {
01196                                                  Z_ADDREF_P(rv);
01197                                                  result = i_zend_is_true(rv);
01198                                                  zval_ptr_dtor(&rv);
01199                                           } else {
01200                                                  result = 0;
01201                                           }
01202                                    } else {
01203                                           result = 0;
01204                                    }
01205                             }
01206                      }
01207                      guard->in_isset = 0;
01208                      zval_ptr_dtor(&object);
01209               }
01210        } else {
01211               switch (has_set_exists) {
01212               case 0:
01213                      result = (Z_TYPE_PP(value) != IS_NULL);
01214                      break;
01215               default:
01216                      result = zend_is_true(*value);
01217                      break;
01218               case 2:
01219                      result = 1;
01220                      break;
01221               }
01222        }
01223 
01224        if (tmp_member) {
01225               zval_ptr_dtor(&tmp_member);
01226        }
01227        return result;
01228 }
01229 /* }}} */
01230 
01231 zend_class_entry *zend_std_object_get_class(const zval *object TSRMLS_DC) /* {{{ */
01232 {
01233        zend_object *zobj;
01234        zobj = Z_OBJ_P(object);
01235 
01236        return zobj->ce;
01237 }
01238 /* }}} */
01239 
01240 int zend_std_object_get_class_name(const zval *object, char **class_name, zend_uint *class_name_len, int parent TSRMLS_DC) /* {{{ */
01241 {
01242        zend_object *zobj;
01243        zend_class_entry *ce;
01244        zobj = Z_OBJ_P(object);
01245 
01246        if (parent) {
01247               if (!zobj->ce->parent) {
01248                      return FAILURE;
01249               }
01250               ce = zobj->ce->parent;
01251        } else {
01252               ce = zobj->ce;
01253        }
01254 
01255        *class_name_len = ce->name_length;
01256        *class_name = estrndup(ce->name, ce->name_length);
01257        return SUCCESS;
01258 }
01259 /* }}} */
01260 
01261 ZEND_API int zend_std_cast_object_tostring(zval *readobj, zval *writeobj, int type TSRMLS_DC) /* {{{ */
01262 {
01263        zval *retval;
01264        zend_class_entry *ce;
01265 
01266        switch (type) {
01267               case IS_STRING:
01268                      ce = Z_OBJCE_P(readobj);
01269                      if (ce->__tostring &&
01270                             (zend_call_method_with_0_params(&readobj, ce, &ce->__tostring, "__tostring", &retval) || EG(exception))) {
01271                             if (EG(exception)) {
01272                                    if (retval) {
01273                                           zval_ptr_dtor(&retval);
01274                                    }
01275                                    zend_error(E_ERROR, "Method %s::__toString() must not throw an exception", ce->name);
01276                                    return FAILURE;
01277                             }
01278                             if (Z_TYPE_P(retval) == IS_STRING) {
01279                                    INIT_PZVAL(writeobj);
01280                                    if (readobj == writeobj) {
01281                                           zval_dtor(readobj);
01282                                    }
01283                                    ZVAL_ZVAL(writeobj, retval, 1, 1);
01284                                    if (Z_TYPE_P(writeobj) != type) {
01285                                           convert_to_explicit_type(writeobj, type);
01286                                    }
01287                                    return SUCCESS;
01288                             } else {
01289                                    zval_ptr_dtor(&retval);
01290                                    INIT_PZVAL(writeobj);
01291                                    if (readobj == writeobj) {
01292                                           zval_dtor(readobj);
01293                                    }
01294                                    ZVAL_EMPTY_STRING(writeobj);
01295                                    zend_error(E_RECOVERABLE_ERROR, "Method %s::__toString() must return a string value", ce->name);
01296                                    return SUCCESS;
01297                             }
01298                      }
01299                      return FAILURE;
01300               case IS_BOOL:
01301                      INIT_PZVAL(writeobj);
01302                      ZVAL_BOOL(writeobj, 1);
01303                      return SUCCESS;
01304               case IS_LONG:
01305                      ce = Z_OBJCE_P(readobj);
01306                      zend_error(E_NOTICE, "Object of class %s could not be converted to int", ce->name);
01307                      INIT_PZVAL(writeobj);
01308                      if (readobj == writeobj) {
01309                             zval_dtor(readobj);
01310                      }
01311                      ZVAL_LONG(writeobj, 1);
01312                      return SUCCESS;
01313               case IS_DOUBLE:
01314                      ce = Z_OBJCE_P(readobj);
01315                      zend_error(E_NOTICE, "Object of class %s could not be converted to double", ce->name);
01316                      INIT_PZVAL(writeobj);
01317                      if (readobj == writeobj) {
01318                             zval_dtor(readobj);
01319                      }
01320                      ZVAL_DOUBLE(writeobj, 1);
01321                      return SUCCESS;
01322               default:
01323                      INIT_PZVAL(writeobj);
01324                      Z_TYPE_P(writeobj) = IS_NULL;
01325                      break;
01326        }
01327        return FAILURE;
01328 }
01329 /* }}} */
01330 
01331 int zend_std_get_closure(zval *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zval **zobj_ptr TSRMLS_DC) /* {{{ */
01332 {
01333        zend_class_entry *ce;
01334        if (Z_TYPE_P(obj) != IS_OBJECT) {
01335               return FAILURE;
01336        }
01337 
01338        ce = Z_OBJCE_P(obj);
01339 
01340        if (zend_hash_find(&ce->function_table, ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME), (void**)fptr_ptr) == FAILURE) {
01341               return FAILURE;
01342        }
01343 
01344        *ce_ptr = ce;
01345        if ((*fptr_ptr)->common.fn_flags & ZEND_ACC_STATIC) {
01346               if (zobj_ptr) {
01347                      *zobj_ptr = NULL;
01348               }
01349        } else {
01350               if (zobj_ptr) {
01351                      *zobj_ptr = obj;
01352               }
01353        }
01354        return SUCCESS;
01355 }
01356 /* }}} */
01357 
01358 ZEND_API zend_object_handlers std_object_handlers = {
01359        zend_objects_store_add_ref,                      /* add_ref */
01360        zend_objects_store_del_ref,                      /* del_ref */
01361        zend_objects_clone_obj,                                 /* clone_obj */
01362 
01363        zend_std_read_property,                                 /* read_property */
01364        zend_std_write_property,                         /* write_property */
01365        zend_std_read_dimension,                         /* read_dimension */
01366        zend_std_write_dimension,                        /* write_dimension */
01367        zend_std_get_property_ptr_ptr,                   /* get_property_ptr_ptr */
01368        NULL,                                                          /* get */
01369        NULL,                                                          /* set */
01370        zend_std_has_property,                                  /* has_property */
01371        zend_std_unset_property,                         /* unset_property */
01372        zend_std_has_dimension,                                 /* has_dimension */
01373        zend_std_unset_dimension,                        /* unset_dimension */
01374        zend_std_get_properties,                         /* get_properties */
01375        zend_std_get_method,                             /* get_method */
01376        NULL,                                                          /* call_method */
01377        zend_std_get_constructor,                        /* get_constructor */
01378        zend_std_object_get_class,                       /* get_class_entry */
01379        zend_std_object_get_class_name,                  /* get_class_name */
01380        zend_std_compare_objects,                        /* compare_objects */
01381        zend_std_cast_object_tostring,                   /* cast_object */
01382        NULL,                                                          /* count_elements */
01383        NULL,                                                          /* get_debug_info */
01384        zend_std_get_closure,                                   /* get_closure */
01385 };
01386 
01387 /*
01388  * Local variables:
01389  * tab-width: 4
01390  * c-basic-offset: 4
01391  * indent-tabs-mode: t
01392  * End:
01393  */