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