zend_std_read_property

coder_study發表於2019-11-06
//讀取屬性
zval *zend_std_read_property(zval *object, zval *member, int type, void **cache_slot, zval *rv) /* {{{ */
{
    zend_object *zobj;
    zval tmp_member, tmp_object;
    zval *retval;
    uint32_t property_offset;
    uint32_t *guard = NULL;

    zobj = Z_OBJ_P(object);

    ZVAL_UNDEF(&tmp_member);
    if (UNEXPECTED(Z_TYPE_P(member) != IS_STRING)) {
        ZVAL_STR(&tmp_member, zval_get_string(member));
        member = &tmp_member;
        cache_slot = NULL;
    }

#if DEBUG_OBJECT_HANDLERS
    fprintf(stderr, "Read object #%d property: %s\n", Z_OBJ_HANDLE_P(object), Z_STRVAL_P(member));
#endif

    /* make zend_get_property_info silent if we have getter - we may want to use it */

    //根據屬性名在zend_class.zend_property_info中查詢zend_property_info,得到屬性值在zend_object中的儲存offset
    //zend_get_property_offset()會對屬性的可見性(public、private、protected)進行驗證  ********
    property_offset = zend_get_property_offset(zobj->ce, Z_STR_P(member), (type == BP_VAR_IS) || (zobj->ce->__get != NULL), cache_slot);

    if (EXPECTED(property_offset != ZEND_WRONG_PROPERTY_OFFSET)) {
        if (EXPECTED(property_offset != ZEND_DYNAMIC_PROPERTY_OFFSET)) {
            //普通屬性,直接根據offset取到屬性值:((zval*)((char*)(zobj) + offset))
            retval = OBJ_PROP(zobj, property_offset);
            if (EXPECTED(Z_TYPE_P(retval) != IS_UNDEF)) {
                goto exit;
            }
        } else if (EXPECTED(zobj->properties != NULL)) {
             //動態屬性的情況,沒有在類中顯式定義的屬性
            retval = zend_hash_find(zobj->properties, Z_STR_P(member));
            if (EXPECTED(retval)) goto exit;
        }
    } else if (UNEXPECTED(EG(exception))) {
        retval = &EG(uninitialized_zval);
        goto exit;
    }

    ZVAL_UNDEF(&tmp_object);

    /* magic isset */

    //沒有找到屬性
    //呼叫魔術方法:__isset()
    if ((type == BP_VAR_IS) && zobj->ce->__isset) {
        zval tmp_result;
        guard = zend_get_property_guard(zobj, Z_STR_P(member));

        if (!((*guard) & IN_ISSET)) { //
            if (Z_TYPE(tmp_member) == IS_UNDEF) {
                ZVAL_COPY(&tmp_member, member);
                member = &tmp_member;
            }
            ZVAL_COPY(&tmp_object, object);
            ZVAL_UNDEF(&tmp_result);

            *guard |= IN_ISSET;
            zend_std_call_issetter(&tmp_object, member, &tmp_result);
            *guard &= ~IN_ISSET;

            if (!zend_is_true(&tmp_result)) {
                retval = &EG(uninitialized_zval);
                zval_ptr_dtor(&tmp_object);
                zval_ptr_dtor(&tmp_result);
                goto exit;
            }

            zval_ptr_dtor(&tmp_result);
        }
    }

    /* magic get */
    if (zobj->ce->__get) {
          //判斷get的變數名是否已經在__get()中
        if (guard == NULL) {
            guard = zend_get_property_guard(zobj, Z_STR_P(member));
        }
        if (!((*guard) & IN_GET)) {
            /* have getter - try with it! */
            if (Z_TYPE(tmp_object) == IS_UNDEF) {
                ZVAL_COPY(&tmp_object, object);
            }
            *guard |= IN_GET; /* prevent circular getting */
            zend_std_call_getter(&tmp_object, member, rv);
            *guard &= ~IN_GET;

            if (Z_TYPE_P(rv) != IS_UNDEF) {
                retval = rv;
                if (!Z_ISREF_P(rv) &&
                    (type == BP_VAR_W || type == BP_VAR_RW  || type == BP_VAR_UNSET)) {
                    SEPARATE_ZVAL(rv);
                    if (UNEXPECTED(Z_TYPE_P(rv) != IS_OBJECT)) {
                        zend_error(E_NOTICE, "Indirect modification of overloaded property %s::$%s has no effect", ZSTR_VAL(zobj->ce->name), Z_STRVAL_P(member));
                    }
                }
            } else {
                retval = &EG(uninitialized_zval);
            }
            zval_ptr_dtor(&tmp_object);
            goto exit;
        } else if (Z_STRVAL_P(member)[0] == '\0' && Z_STRLEN_P(member) != 0) {
            zval_ptr_dtor(&tmp_object);
            zend_throw_error(NULL, "Cannot access property started with '\\0'");
            retval = &EG(uninitialized_zval);
            goto exit;
        }
    }

    zval_ptr_dtor(&tmp_object);

    if ((type != BP_VAR_IS)) {
        zend_error(E_NOTICE,"Undefined property: %s::$%s", ZSTR_VAL(zobj->ce->name), Z_STRVAL_P(member));
    }
    retval = &EG(uninitialized_zval);

exit:
    if (UNEXPECTED(Z_REFCOUNTED(tmp_member))) {
        zval_ptr_dtor(&tmp_member);
    }

    return retval;
}

zend_get_property_offset

//獲取偏移量
static zend_always_inline uint32_t zend_get_property_offset(zend_class_entry *ce, zend_string *member, int silent, void **cache_slot) /* {{{ */
{
    zval *zv;
    zend_property_info *property_info = NULL;
    uint32_t flags;
    zend_class_entry *scope;

    if (cache_slot && EXPECTED(ce == CACHED_PTR_EX(cache_slot))) {
        return (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1);
    }

    if (UNEXPECTED(ZSTR_VAL(member)[0] == '\0' && ZSTR_LEN(member) != 0)) {
        if (!silent) {
            zend_throw_error(NULL, "Cannot access property started with '\\0'");
        }
        return ZEND_WRONG_PROPERTY_OFFSET;
    }

    //普通屬性不存在
    if (UNEXPECTED(zend_hash_num_elements(&ce->properties_info) == 0)) {
        goto exit_dynamic; //動態的
    }

    //
    zv = zend_hash_find(&ce->properties_info, member); // 共有屬性到這一步
    if (EXPECTED(zv != NULL)) {
        property_info = (zend_property_info*)Z_PTR_P(zv); //獲取到屬性資訊
        flags = property_info->flags; //獲取屬性的標識 public protected private
        //判斷是否是私有屬性
        if (UNEXPECTED((flags & ZEND_ACC_SHADOW) != 0)) {
            /* if it's a shadow - go to access it's private */
            //父類的私有屬性
            property_info = NULL;
        } else {
            if (EXPECTED(zend_verify_property_access(property_info, ce) != 0)) {
                if (UNEXPECTED(!(flags & ZEND_ACC_CHANGED))
                    || UNEXPECTED((flags & ZEND_ACC_PRIVATE))) {
                    if (UNEXPECTED((flags & ZEND_ACC_STATIC) != 0)) {
                        if (!silent) {
                            //靜態屬性的讀取方式不正確
                            zend_error(E_NOTICE, "Accessing static property %s::$%s as non static", ZSTR_VAL(ce->name), ZSTR_VAL(member));
                        }
                        return ZEND_DYNAMIC_PROPERTY_OFFSET;
                    }
                    //公共屬性到這一步了
                    goto exit;
                }
            } else {
                /* Try to look in the scope instead */
                property_info = ZEND_WRONG_PROPERTY_INFO;
            }
        }
    }

    if (EG(fake_scope)) {
        scope = EG(fake_scope);
    } else {
        scope = zend_get_executed_scope();
    }

    if (scope != ce
        && scope
        && is_derived_class(ce, scope)
        && (zv = zend_hash_find(&scope->properties_info, member)) != NULL
        && ((zend_property_info*)Z_PTR_P(zv))->flags & ZEND_ACC_PRIVATE) {

        property_info = (zend_property_info*)Z_PTR_P(zv); 
        if (UNEXPECTED((property_info->flags & ZEND_ACC_STATIC) != 0)) {

            return ZEND_DYNAMIC_PROPERTY_OFFSET;
        }

    } else if (UNEXPECTED(property_info == NULL)) {
exit_dynamic:
        if (cache_slot) {
            CACHE_POLYMORPHIC_PTR_EX(cache_slot, ce, (void*)(intptr_t)ZEND_DYNAMIC_PROPERTY_OFFSET);
        }
        return ZEND_DYNAMIC_PROPERTY_OFFSET;
    } else if (UNEXPECTED(property_info == ZEND_WRONG_PROPERTY_INFO)) {
        /* Information was available, but we were denied access.  Error out. */
        if (!silent) {
            zend_throw_error(NULL, "Cannot access %s property %s::$%s", zend_visibility_string(flags), ZSTR_VAL(ce->name), ZSTR_VAL(member));
        }
        return ZEND_WRONG_PROPERTY_OFFSET;
    }

exit:
    if (cache_slot) {
        CACHE_POLYMORPHIC_PTR_EX(cache_slot, ce, (void*)(intptr_t)property_info->offset);
    }
    return property_info->offset;
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結