/// <summary> /// Try to load all <paramref name="providedTypes"/>. This can invoke autoloading if necessary. /// Check if they were not modified, so calling compilation unit has to be invalidated and recompiled. /// </summary> /// <param name="providedTypes">a list of provided type declarators. Can be a <B>null</B> reference.</param> /// <param name="target">The script context to be checked.</param> /// <param name="caller">Current class context.</param> /// <returns><paramref name="providedTypes"/> are loadable and match in context of <paramref name="target"/>.</returns> public static bool LoadAndMatch(List <ProvidedType> providedTypes, ScriptContext /*!*/ target, DTypeDesc caller) { Debug.Assert(target != null); if (providedTypes != null && providedTypes.Count > 0) { //// there is less declarators than we require: //if (target.DeclaredTypes.Count < providedTypes.Count) return false; // looks up each provided declarator in the target context: foreach (ProvidedType declarator in providedTypes) { //DTypeDesc decl_type; //target.DeclaredTypes.TryGetValue(declarator.Key, out decl_type); // When class is compiled in runtime, autoload is invoked on base class (if isn't already declared). // We have to call autoload on the base class also in transient assembly var decl_type = target.ResolveType(declarator.Key, null, caller, null, ResolveTypeFlags.UseAutoload); if (decl_type == null || decl_type.RealType != declarator.Value.RealType) { return(false); } } } return(true); }
/// <summary> /// Sets a property of a <see cref="DObject"/> instance according to deserialized name and value. /// </summary> /// <param name="instance">The instance being deserialized.</param> /// <param name="name">The property name formatted for serialization (see <see cref="FormatPropertyName"/>).</param> /// <param name="value">The property value.</param> /// <param name="context">Current <see cref="ScriptContext"/>.</param> public static void SetProperty(DObject /*!*/ instance, string /*!*/ name, object value, ScriptContext /*!*/ context) { // the property name might encode its visibility and "classification" -> use these // information for suitable property desc lookups PhpMemberAttributes visibility; string type_name; string property_name = ParsePropertyName(name, out type_name, out visibility); DTypeDesc declarer; if (type_name == null) { declarer = instance.TypeDesc; } else { declarer = context.ResolveType(type_name); if (declarer == null) { declarer = instance.TypeDesc; } } // try to find a suitable field handle DPropertyDesc property; if (instance.TypeDesc.GetProperty(new VariableName(property_name), declarer, out property) == PHP.Core.Reflection.GetMemberResult.OK) { if ((property.IsPrivate && declarer != property.DeclaringType)) { // if certain conditions are met, don't use the handle even if it was found // (this is to precisely mimic the PHP behavior) property = null; } } else { property = null; } if (property != null) { property.Set(instance, value); } else { // suitable CT field not found -> add it to RT fields // (note: care must be taken so that the serialize(unserialize($x)) round // trip returns $x even if user classes specified in $x are not declared) if (instance.RuntimeFields == null) { instance.RuntimeFields = new PhpArray(); } instance.RuntimeFields[name] = value; } }
/// <summary> /// Returns an unitialized instance of the specified type or <see cref="__PHP_Incomplete_Class"/>. /// </summary> /// <param name="typeName">The type name.</param> /// <param name="context">Current <see cref="ScriptContext"/>.</param> /// <returns>The newly created instance or <B>null</B> if <paramref name="typeName"/> denotes /// a primitive type.</returns> /// <remarks> /// If the <paramref name="typeName"/> denotes a CLR type, no constructor is executed. If the /// <paramref name="typeName"/> denotes a PHP type, no user constructor (e.g. <c>__construct</c>) /// is executed. /// </remarks> public static DObject GetUninitializedInstance(string /*!*/ typeName, ScriptContext /*!*/ context) { // resolve the specified type DTypeDesc type = context.ResolveType(typeName); if (type == null || type.IsAbstract) { PhpCallback callback = context.Config.Variables.DeserializationCallback; if (callback != null && !callback.IsInvalid) { callback.Invoke(typeName); type = context.ResolveType(typeName); if (type == null || type.IsAbstract) { // unserialize_callback_func failed PhpException.Throw(PhpError.Warning, CoreResources.GetString("unserialize_callback_failed", ((IPhpConvertible)callback).ToString())); } } } if (type == null || type.IsAbstract) { // type not found -> create __PHP_Incomplete_Class __PHP_Incomplete_Class pic = new __PHP_Incomplete_Class(context, false); pic.__PHP_Incomplete_Class_Name.Value = typeName; pic.__PHP_Incomplete_Class_Name.IsSet = true; return(pic); } else { // create the instance return(type.New(context) as DObject); } }
/// <summary> /// Searches for type using <see cref="ScriptContext.ResolveType"/>. If the type is found /// and has a declarator (is a user type) then the caller is made dependent on it. /// </summary> /// <returns>The type.</returns> public DTypeDesc FindAndProvideType(string name) { // finds a type - searches in script context and in libraries, may also run __autoload: DTypeDesc type = context.ResolveType(name, null, caller, null, ResolveTypeFlags.UseAutoload); // if the type is a user type then does provide the type: if (type != null && context.DeclaredTypes.ContainsKey(name)) { if (providedTypes == null) { providedTypes = new List <ProvidedType>(); } providedTypes.Add(new ProvidedType(name, type)); Debug.WriteLine("PROVIDER", "Added: {0} ({1})", name, type.RealType.FullName); } return(type); }
public virtual object isSubclassOf(ScriptContext/*!*/context, object @class) { var classname = PhpVariable.AsString(@class); if (!string.IsNullOrEmpty(classname) && this.typedesc != null) { var dtype = context.ResolveType(classname, null, null, null, ResolveTypeFlags.ThrowErrors | ResolveTypeFlags.UseAutoload); return dtype != null && this.typedesc.IsAssignableFrom(dtype); } return false; }
/// <summary> /// Sets a property of a <see cref="DObject"/> instance according to deserialized name and value. /// </summary> /// <param name="instance">The instance being deserialized.</param> /// <param name="name">The property name formatted for serialization (see <see cref="FormatPropertyName"/>).</param> /// <param name="value">The property value.</param> /// <param name="context">Current <see cref="ScriptContext"/>.</param> public static void SetProperty(DObject/*!*/ instance, string/*!*/ name, object value, ScriptContext/*!*/ context) { // the property name might encode its visibility and "classification" -> use these // information for suitable property desc lookups PhpMemberAttributes visibility; string type_name; string property_name = ParsePropertyName(name, out type_name, out visibility); DTypeDesc declarer; if (type_name == null) declarer = instance.TypeDesc; else { declarer = context.ResolveType(type_name); if (declarer == null) declarer = instance.TypeDesc; } // try to find a suitable field handle DPropertyDesc property; if (instance.TypeDesc.GetProperty(new VariableName(property_name), declarer, out property) == PHP.Core.Reflection.GetMemberResult.OK) { if ((property.IsPrivate && declarer != property.DeclaringType)) { // if certain conditions are met, don't use the handle even if it was found // (this is to precisely mimic the PHP behavior) property = null; } } else property = null; if (property != null) property.Set(instance, value); else { // suitable CT field not found -> add it to RT fields // (note: care must be taken so that the serialize(unserialize($x)) round // trip returns $x even if user classes specified in $x are not declared) if (instance.RuntimeFields == null) instance.RuntimeFields = new PhpArray(); instance.RuntimeFields[name] = value; } }
/// <summary> /// Returns an unitialized instance of the specified type or <see cref="__PHP_Incomplete_Class"/>. /// </summary> /// <param name="typeName">The type name.</param> /// <param name="context">Current <see cref="ScriptContext"/>.</param> /// <returns>The newly created instance or <B>null</B> if <paramref name="typeName"/> denotes /// a primitive type.</returns> /// <remarks> /// If the <paramref name="typeName"/> denotes a CLR type, no constructor is executed. If the /// <paramref name="typeName"/> denotes a PHP type, no user constructor (e.g. <c>__construct</c>) /// is executed. /// </remarks> public static DObject GetUninitializedInstance(string/*!*/ typeName, ScriptContext/*!*/ context) { // resolve the specified type DTypeDesc type = context.ResolveType(typeName); if (type == null || type.IsAbstract) { PhpCallback callback = context.Config.Variables.DeserializationCallback; if (callback != null && !callback.IsInvalid) { callback.Invoke(typeName); type = context.ResolveType(typeName); if (type == null || type.IsAbstract) { // unserialize_callback_func failed PhpException.Throw(PhpError.Warning, CoreResources.GetString("unserialize_callback_failed", ((IPhpConvertible)callback).ToString())); } } } if (type == null || type.IsAbstract) { // type not found -> create __PHP_Incomplete_Class __PHP_Incomplete_Class pic = new __PHP_Incomplete_Class(context, false); pic.__PHP_Incomplete_Class_Name.Value = typeName; pic.__PHP_Incomplete_Class_Name.IsSet = true; return pic; } else { // create the instance return type.New(context) as DObject; } }
/// <summary> /// Returns names and values of properties whose names have been returned by <c>__sleep</c>. /// </summary> /// <param name="instance">The instance being serialized.</param> /// <param name="sleepResult">The array returned by <c>__sleep</c>.</param> /// <param name="context">Current <see cref="ScriptContext"/>.</param> /// <returns>Name-value pairs. Names are properly formatted for serialization.</returns> /// <exception cref="PhpException">Property of the name returned from <c>__sleep</c> does not exist.</exception> /// <remarks> /// This method returns exactly <paramref name="sleepResult"/>'s <see cref="PhpHashtable.Count"/> items. /// </remarks> public static IEnumerable<KeyValuePair<string, object>> EnumerateSerializableProperties( DObject/*!*/ instance, PhpArray/*!*/ sleepResult, ScriptContext/*!*/ context) { foreach (object item in sleepResult.Values) { PhpMemberAttributes visibility; string name = PHP.Core.Convert.ObjectToString(item); string declaring_type_name; string property_name = ParsePropertyName(name, out declaring_type_name, out visibility); DTypeDesc declarer; if (declaring_type_name == null) declarer = instance.TypeDesc; else { declarer = context.ResolveType(declaring_type_name); if (declarer == null) { // property name refers to an unknown class -> value will be null yield return new KeyValuePair<string, object>(name, null); continue; } } // obtain the property desc and decorate the prop name according to its visibility and declaring class DPropertyDesc property; if (instance.TypeDesc.GetProperty(new VariableName(property_name), declarer, out property) == GetMemberResult.OK && !property.IsStatic) { if ((Enums.VisibilityEquals(visibility, property.MemberAttributes) && visibility != PhpMemberAttributes.Public) || (visibility == PhpMemberAttributes.Private && declarer != property.DeclaringType)) { // if certain conditions are met, serialize the property as null // (this is to precisely mimic the PHP behavior) yield return new KeyValuePair<string, object>(name, null); continue; } name = FormatPropertyName(property, property_name); } else property = null; // obtain the property value object val = null; if (property != null) { val = property.Get(instance); } else if (instance.RuntimeFields == null || !instance.RuntimeFields.TryGetValue(name, out val)) { // this is new in PHP 5.1 PhpException.Throw(PhpError.Notice, CoreResources.GetString("sleep_returned_bad_field", name)); } yield return new KeyValuePair<string, object>(name, val); } }
/// <summary> /// Converts a class name or class instance to <see cref="DTypeDesc"/> object. /// </summary> /// <param name="scriptContext">Current <see cref="ScriptContext"/>.</param> /// <param name="namingContext">Current <see cref="NamingContext"/>.</param> /// <param name="caller">The caller of the method to resolve visible properties properly. Can be UnknownTypeDesc.</param> /// <param name="classNameOrObject">The class name or class instance (<see cref="DObject"/>).</param> /// <param name="useAutoload"><B>True</B> iff the <c>__autoload</c> magic function should be used.</param> /// <returns>The type desc that corresponds to <paramref name="classNameOrObject"/> or <B>null</B> /// if the type could not be found or <paramref name="classNameOrObject"/> is neither a string /// nor <see cref="DObject"/>.</returns> internal static DTypeDesc ClassNameOrObjectToType(ScriptContext/*!*/ scriptContext, NamingContext namingContext, DTypeDesc caller, object classNameOrObject, bool useAutoload) { string class_name = PhpVariable.AsString(classNameOrObject); if (class_name != null) { // lookup the Type return scriptContext.ResolveType(class_name, namingContext, caller, null, (useAutoload ? ResolveTypeFlags.UseAutoload : ResolveTypeFlags.None)); } else { DObject obj = classNameOrObject as DObject; if (obj != null) return obj.TypeDesc; } return null; }
public virtual object __construct(ScriptContext context, object @class, object methodname) { string methodnameStr = PhpVariable.AsString(methodname); this.dtype = null; this.method = null; DObject dobj; if ((dobj = (@class as DObject)) != null) { this.dtype = dobj.TypeDesc; } else { var str = PhpVariable.AsString(@class); if (str != null) this.dtype = context.ResolveType(str, null, null, null, ResolveTypeFlags.UseAutoload); if (this.dtype == null) { PhpException.Throw(PhpError.Error, string.Format("Class {0} does not exist", str)); return false; } } if (this.dtype.GetMethod(new Name(methodnameStr), dtype, out this.method) == GetMemberResult.NotFound) { PhpException.Throw(PhpError.Error, string.Format("Method {0}::{1}() does not exist", dtype.MakeFullName(), methodnameStr)); return false; } return null; }
/// <summary> /// Returns names and values of properties whose names have been returned by <c>__sleep</c>. /// </summary> /// <param name="instance">The instance being serialized.</param> /// <param name="sleepResult">The array returned by <c>__sleep</c>.</param> /// <param name="context">Current <see cref="ScriptContext"/>.</param> /// <returns>Name-value pairs. Names are properly formatted for serialization.</returns> /// <exception cref="PhpException">Property of the name returned from <c>__sleep</c> does not exist.</exception> /// <remarks> /// This method returns exactly <paramref name="sleepResult"/>'s <see cref="PhpHashtable.Count"/> items. /// </remarks> public static IEnumerable <KeyValuePair <string, object> > EnumerateSerializableProperties( DObject /*!*/ instance, PhpArray /*!*/ sleepResult, ScriptContext /*!*/ context) { foreach (object item in sleepResult.Values) { PhpMemberAttributes visibility; string name = PHP.Core.Convert.ObjectToString(item); string declaring_type_name; string property_name = ParsePropertyName(name, out declaring_type_name, out visibility); DTypeDesc declarer; if (declaring_type_name == null) { declarer = instance.TypeDesc; } else { declarer = context.ResolveType(declaring_type_name); if (declarer == null) { // property name refers to an unknown class -> value will be null yield return(new KeyValuePair <string, object>(name, null)); continue; } } // obtain the property desc and decorate the prop name according to its visibility and declaring class DPropertyDesc property; if (instance.TypeDesc.GetProperty(new VariableName(property_name), declarer, out property) == GetMemberResult.OK && !property.IsStatic) { if ((Enums.VisibilityEquals(visibility, property.MemberAttributes) && visibility != PhpMemberAttributes.Public) || (visibility == PhpMemberAttributes.Private && declarer != property.DeclaringType)) { // if certain conditions are met, serialize the property as null // (this is to precisely mimic the PHP behavior) yield return(new KeyValuePair <string, object>(name, null)); continue; } name = FormatPropertyName(property, property_name); } else { property = null; } // obtain the property value object val = null; if (property != null) { val = property.Get(instance); } else if (instance.RuntimeFields == null || !instance.RuntimeFields.TryGetValue(name, out val)) { // this is new in PHP 5.1 PhpException.Throw(PhpError.Notice, CoreResources.GetString("sleep_returned_bad_field", name)); } yield return(new KeyValuePair <string, object>(name, val)); } }
/// <summary> /// Try to load all <paramref name="providedTypes"/>. This can invoke autoloading if necessary. /// Check if they were not modified, so calling compilation unit has to be invalidated and recompiled. /// </summary> /// <param name="providedTypes">a list of provided type declarators. Can be a <B>null</B> reference.</param> /// <param name="target">The script context to be checked.</param> /// <param name="caller">Current class context.</param> /// <returns><paramref name="providedTypes"/> are loadable and match in context of <paramref name="target"/>.</returns> public static bool LoadAndMatch(List<ProvidedType> providedTypes, ScriptContext/*!*/ target, DTypeDesc caller) { Debug.Assert(target != null); if (providedTypes != null && providedTypes.Count > 0) { //// there is less declarators than we require: //if (target.DeclaredTypes.Count < providedTypes.Count) return false; // looks up each provided declarator in the target context: foreach (ProvidedType declarator in providedTypes) { //DTypeDesc decl_type; //target.DeclaredTypes.TryGetValue(declarator.Key, out decl_type); // When class is compiled in runtime, autoload is invoked on base class (if isn't already declared). // We have to call autoload on the base class also in transient assembly var decl_type = target.ResolveType(declarator.Key, null, caller, null, ResolveTypeFlags.UseAutoload); if (decl_type == null || decl_type.RealType != declarator.Value.RealType) return false; } } return true; }
public virtual object __construct(ScriptContext context, object @class, object methodname) { string methodnameStr = PhpVariable.AsString(methodname); if (@class == null || string.IsNullOrEmpty(methodnameStr)) return false; this.dtype = null; this.method = null; DObject dobj; string str; if ((dobj = (@class as DObject)) != null) { this.dtype = dobj.TypeDesc; } else if ((str = PhpVariable.AsString(@class)) != null) { this.dtype = context.ResolveType(str, null, null, null, ResolveTypeFlags.UseAutoload); } if (this.dtype == null) return false; if (this.dtype.GetMethod(new Name(methodnameStr), dtype, out this.method) == GetMemberResult.NotFound) return false; return null; }
/// <summary> /// Resolves the <paramref name="typeName"/> and provides corresponding <see cref="DTypeDesc"/> or <c>null</c> reference. /// </summary> private static DTypeDesc ResolveType(ScriptContext/*!*/context, string typeName) { return context.ResolveType(typeName, null, null, null, ResolveTypeFlags.ThrowErrors | ResolveTypeFlags.UseAutoload); }
/// <summary> /// Attempts to bind this callback to its target. /// </summary> /// <param name="quiet"><B>true</B> of no errors should be thrown, <B>false</B> otherwise.</param> /// <param name="nameContext">Current <see cref="NamingContext"/> for function and class name resolution.</param> /// <param name="caller">Current class context or a <see cref="UnknownTypeDesc"/> if the class context /// should be determined ad-hoc.</param> /// <returns><B>True</B> if the callback was successfully bound, <B>false</B> if an error occured.</returns> public bool Bind(bool quiet, DTypeDesc caller, NamingContext nameContext) { if (IsInvalid) { return(false); } switch (state) { case State.UnboundFunction: { if (context == null) { context = ScriptContext.CurrentContext; } routineDesc = context.ResolveFunction(targetName, nameContext, quiet); if (routineDesc == null) { return(false); } state = State.Bound; return(true); } case State.UnboundStaticMethod: { if (context == null) { context = ScriptContext.CurrentContext; } if (caller != null && caller.IsUnknown) { callingContext = PhpStackTrace.GetClassContext(); } else { callingContext = caller; } // try to find the CLR method // find the class according to className ResolveTypeFlags flags = ResolveTypeFlags.UseAutoload; if (!quiet) { flags |= ResolveTypeFlags.ThrowErrors; } DTypeDesc type = context.ResolveType(className, nameContext, callingContext, null, flags); if (type == null) { return(false); } // find the method bool is_caller_method; lateStaticBindType = type; routineDesc = Operators.GetStaticMethodDesc(type, targetName, ref instance, callingContext, context, quiet, false, out is_caller_method); if (routineDesc == null) { return(false); } if (instance != null) { dummyInstance = true; } state = is_caller_method ? State.BoundToCaller : State.Bound; return(true); } case State.UnboundInstanceMethod: { if (caller != null && caller.IsUnknown) { callingContext = PhpStackTrace.GetClassContext(); } else { callingContext = caller; } // ask the instance for a handle to the method bool is_caller_method; routineDesc = instance.GetMethodDesc(targetName, callingContext, quiet, out is_caller_method); if (routineDesc == null) { return(false); } state = (is_caller_method ? State.BoundToCaller : State.Bound); return(true); } } return(true); }
public virtual object __construct(ScriptContext/*!*/context, object/*Traversable*/ iterator, [Optional]object classname) { this.iterator = iterator as DObject; // iterator.RealObject is Traversable ? if (this.iterator != null) { if (classname != null && classname != Arg.Default && !(this.iterator.RealObject is SPL.Iterator) // downcast only if it is not an Iterator instance ) { var downcast = context.ResolveType(PhpVariable.AsString(classname), null, this.iterator.TypeDesc, null, ResolveTypeFlags.ThrowErrors); if (downcast == null || // not found !downcast.IsAssignableFrom(this.iterator.TypeDesc) || // not base !DTypeDesc.Create(typeof(Traversable)).IsAssignableFrom(downcast)) // not Traversable { // TODO: error // zend_throw_exception(spl_ce_LogicException, "Class to downcast to not found or not base class or does not implement Traversable", 0 TSRMLS_CC); this.iterator = null; } else { //if (DTypeDesc.Create(typeof(IteratorAggregate)).IsAssignableFrom(downcast)) //{ // // {downcast} is IteratorAggregate // context.Stack.AddFrame(); // var result = this.iterator.InvokeMethod("getIterator", null, context); // if (result == null || !(result is DObject) || !(((DObject)result).RealObject is Traversable)) // { // //zend_throw_exception_ex(spl_ce_LogicException, 0 TSRMLS_CC, "%s::getIterator() must return an object that implements Traversable", ce->name); // this.iterator = null; // } // else // { // this.iterator = (DObject)result; // } //} throw new NotImplementedException(); } } } else { // TODO: error } //rewind(context); // not in PHP, performance reasons (foreach causes rewind() itself) return null; }
public virtual object __construct(ScriptContext context, object @class, object propertyname) { string propertynameStr = PhpVariable.AsString(propertyname); if (@class == null || string.IsNullOrEmpty(propertynameStr)) return false; this.dtype = null; this.property = null; DObject dobj; string str; if ((dobj = (@class as DObject)) != null) { this.dtype = dobj.TypeDesc; } else if ((str = PhpVariable.AsString(@class)) != null) { this.dtype = context.ResolveType(str, null, null, null, ResolveTypeFlags.UseAutoload); } if (this.dtype == null) return false; if (this.dtype.GetProperty(new VariableName(propertynameStr), dtype, out this.property) == GetMemberResult.NotFound) { object runtimeValue; if (dobj != null && dobj.RuntimeFields != null && dobj.RuntimeFields.TryGetValue(propertynameStr, out runtimeValue)) { // create desc of runtime field: this.property = new RuntimePhpProperty(dtype, (instance) => ((DObject)instance).GetRuntimeField(this.name, null), (instance, value) => ((DObject)instance).SetRuntimeField(this.name, value, null, null, null)); this.property.Member = new KnownRuntimeProperty(this.property, propertynameStr); } else { return false; } } return null; }