public PhpValue GetValue(PhpValue value, Context ctx, Type classContext)
        {
            var receiver = value.AsObject();

            if (receiver != null)
            {
                // CONSIDER: value = Operators.PropertyGetValue( .. )

                var t = receiver.GetPhpTypeInfo();
                var p = BinderHelpers.ResolveDeclaredProperty(t, classContext, @static: false, name: Name);

                value = p != null
                    ? p.GetValue(ctx, receiver)
                    : Operators.RuntimePropertyGetValue(ctx, t, receiver, propertyName: Name);

                return(Next.GetValue(value, ctx, classContext));
            }
            else
            {
                PhpException.VariableMisusedAsObject(value, false);
                return(PhpValue.Void);
            }
        }
        public PhpAlias GetAlias(ref PhpValue value, Context ctx, Type classContext)
        {
            var receiver = value.EnsureObject();
            var t        = receiver.GetPhpTypeInfo();

            PhpValue tmp;

            var p = BinderHelpers.ResolveDeclaredProperty(t, classContext, @static: false, Name);

            if (p != null)
            {
                switch (Next.Operation)
                {
                case RuntimeChainOperation.Property:
                    tmp = PhpValue.FromClass(p.EnsureObject(ctx, receiver));
                    break;

                case RuntimeChainOperation.ArrayItem:
                    tmp = PhpValue.Create(p.EnsureArray(ctx, receiver));
                    break;

                case RuntimeChainOperation.End:
                    return(p.EnsureAlias(ctx, receiver));

                default:
                    throw new InvalidOperationException();
                }
            }
            else
            {
                // Template: runtimeflds.Contains(key) ? runtimeflds.EnsureObject(key) : ( __get(key) ?? runtimeflds.EnsureObject(key))

                var runtimeFields = t.GetRuntimeFields(receiver);
                if (runtimeFields == null || !runtimeFields.Contains(Name))
                {
                    //
                    var __get = t.RuntimeMethods[TypeMethods.MagicMethods.__get];
                    if (__get != null)
                    {
                        // NOTE: magic methods must have public visibility, therefore the visibility check is unnecessary

                        // int subkey1 = access.Write() ? 1 : access.Unset() ? 2 : access.Isset() ? 3 : 4;
                        int subkey = Name.GetHashCode() ^ (1 << 4 /*subkey1*/);

                        using (var token = new Context.RecursionCheckToken(ctx, receiver, subkey))
                        {
                            if (!token.IsInRecursion)
                            {
                                tmp = __get.Invoke(ctx, receiver, Name);
                                return(Next.GetAlias(ref tmp, ctx, classContext));
                            }
                        }
                    }
                }

                if (runtimeFields == null)
                {
                    runtimeFields = t.EnsureRuntimeFields(receiver);
                }

                //

                switch (Next.Operation)
                {
                case RuntimeChainOperation.Property:
                    tmp = PhpValue.FromClass(runtimeFields.EnsureItemObject(Name));
                    break;

                case RuntimeChainOperation.ArrayItem:
                    tmp = PhpValue.Create(runtimeFields.EnsureItemArray(Name));
                    break;

                case RuntimeChainOperation.End:
                    return(runtimeFields.EnsureItemAlias(Name));

                default:
                    throw new InvalidOperationException();
                }
            }

            // chain:
            return(Next.GetAlias(ref tmp, ctx, classContext));
        }