diff --git a/src/Peachpie.Runtime/Conversions.cs b/src/Peachpie.Runtime/Conversions.cs
index e38416c286..e8cba5ac2e 100644
--- a/src/Peachpie.Runtime/Conversions.cs
+++ b/src/Peachpie.Runtime/Conversions.cs
@@ -5,7 +5,7 @@
using System.Linq;
using System.Reflection;
using System.Text;
-using System.Threading.Tasks;
+using Pchp.Core.Dynamic;
using Pchp.Core.Reflection;
using Pchp.Core.Text;
@@ -1376,6 +1376,36 @@ public static DateTime ToDateTime(this PhpValue value)
}
#endregion
+
+ #region UnwrapStruct
+
+ ///
+ /// Given is a CLR value type (not a scalar PHP type!),
+ /// this method tries to unwrap to .
+ /// No conversion is performed. The method throws an exception is the value type does not match.
+ /// If is NULL, the method returns default of .
+ ///
+ public static T UnwrapStruct(PhpValue value) where T : struct
+ {
+ switch (value.TypeCode)
+ {
+ case PhpTypeCode.Null: return default(T);
+ case PhpTypeCode.Alias: return UnwrapStruct(value.Alias.Value);
+ case PhpTypeCode.Object:
+ var obj = value.Object;
+
+ if (obj is StructBox box_t) return box_t.Value;
+ if (obj is IStructBox box) return (T)box.BoxedValue; // throws
+
+ return (T)obj; // throws if type does not match
+
+ default:
+ // throws:
+ return (T)value.ToClr();
+ }
+ }
+
+ #endregion
}
#endregion
@@ -1499,7 +1529,7 @@ public static double ToDouble(PhpValue value)
PhpTypeCode.Null => null,
_ => throw PhpException.TypeErrorException(string.Format(Resources.ErrResources.scalar_used_as_object, PhpVariable.GetTypeName(value))),
};
-
+
public static object AsResource(PhpValue value) => value.TypeCode switch
{
PhpTypeCode.Object => value.Object as PhpResource ?? throw PhpException.TypeErrorException(),
diff --git a/src/Peachpie.Runtime/Dynamic/ConvertExpression.cs b/src/Peachpie.Runtime/Dynamic/ConvertExpression.cs
index b058a19b0d..1aaa3a4505 100644
--- a/src/Peachpie.Runtime/Dynamic/ConvertExpression.cs
+++ b/src/Peachpie.Runtime/Dynamic/ConvertExpression.cs
@@ -6,8 +6,6 @@
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
-using System.Text;
-using System.Threading.Tasks;
using Pchp.Core.Reflection;
namespace Pchp.Core.Dynamic
@@ -126,9 +124,10 @@ public static Expression Bind(Expression arg, Type target, Expression ctx)
//
if (target.IsValueType == false)
{
- Debug.Assert(typeof(Nullable).IsValueType);
-
- return BindAsReferenceType(arg, target, ctx);
+ if (TryBindAsReferenceType(arg, target, ctx, out var expression))
+ {
+ return expression;
+ }
}
else
{
@@ -160,16 +159,16 @@ public static Expression Bind(Expression arg, Type target, Expression ctx)
}
}
- //// PhpValueConverter.Cast( (PhpValue ) : T
- //return Expression.Call(
- // typeof(PhpValueConverter).GetMethod("Cast", BindingFlags.Public | BindingFlags.Static).MakeGenericMethod(target),
- // BindToValue(arg));
-
- //
- return Expression.Block(
- ThrowTypeError($"{arg.Type} -> {target}"),
- BindDefault(target)
+ // Template: PhpValueConverter.Cast( PhpValue ) : T
+ return Expression.Call(
+ Cache.Operators.Cast_PhpValue_T.MakeGenericMethod(target),
+ BindToValue(arg)
);
+
+ //return Expression.Block(
+ // ThrowTypeError($"{arg.Type} -> {target}"),
+ // BindDefault(target)
+ //);
}
private static bool IsNullConstant(Expression arg)
@@ -687,13 +686,17 @@ static Expression BindAsObject(Expression expr)
return Expression.Call(Cache.Operators.PhpValue_AsObjectOrThrow, BindToValue(expr));
}
- static Expression BindAsReferenceType(Expression expr, Type target, Expression ctx)
+ ///
+ /// Specialized conversion to a referenced type.
+ ///
+ static bool TryBindAsReferenceType(Expression expr, Type target, Expression ctx, out Expression expression)
{
Debug.Assert(expr.Type != typeof(PhpAlias));
if (IsNullConstant(expr))
{
- return Expression.Constant(null, typeof(object));
+ expression = Expression.Constant(null, typeof(object));
+ return true;
}
// to System.Delegate,
@@ -702,9 +705,10 @@ static Expression BindAsReferenceType(Expression expr, Type target, Expression c
{
// Template: PhpCallableToDelegate.Get( BindAsCallable(expr), Context )
var callable = BindAsCallable(expr);
- return Expression.Call(
+ expression = Expression.Call(
typeof(PhpCallableToDelegate<>).MakeGenericType(target).GetMethod("Get"),
callable, ctx);
+ return true;
}
//// from PhpValue:
@@ -723,10 +727,9 @@ static Expression BindAsReferenceType(Expression expr, Type target, Expression c
//// just cast:
//return Expression.Convert(expr, target);
- // Template: PhpValueConverter.Cast( PhpValue )
- return Expression.Call(
- Cache.Operators.Cast_PhpValue_T.MakeGenericMethod(target),
- BindToValue(expr));
+ //
+ expression = null;
+ return false;
}
private static Expression BindAsArray(Expression expr, Type target)
@@ -913,6 +916,12 @@ public static Expression BindCost(Expression arg, Type target)
return Expression.Constant(ConversionCost.NoConversion); // if value can be converted, it would be handled above
}
+ // to System.Delegate
+ if (target.IsSubclassOf(typeof(Delegate)) && t == typeof(Closure))
+ {
+ return Expression.Constant(ConversionCost.ImplicitCast);
+ }
+
if (ReflectionUtils.IsPhpClassType(target))
{
if (!t.IsInterface && !target.IsInterface && !target.IsAssignableFrom(t) && !t.IsAssignableFrom(target))
diff --git a/src/Peachpie.Runtime/PhpValue.cs b/src/Peachpie.Runtime/PhpValue.cs
index 161a98d777..8f364408b6 100644
--- a/src/Peachpie.Runtime/PhpValue.cs
+++ b/src/Peachpie.Runtime/PhpValue.cs
@@ -308,7 +308,12 @@ public object ToClass()
return Array.ToObject();
case PhpTypeCode.Object:
- return (Object is IPhpConvertible conv) ? conv.ToClass() : Object;
+ return Object is IPhpConvertible conv
+ ? conv.ToClass()
+ //: Object is IStructBox structbox // CONSIDER: IStructBox is a regular PHP interface with conversions and methods, works well in PHP + it can have a IDynamicMetaObjectProvider
+ // ? structbox.BoxedValue // box the value type
+ : Object
+ ;
case PhpTypeCode.Alias:
return Alias.Value.ToClass();
diff --git a/src/Peachpie.Runtime/PhpValueConverter.cs b/src/Peachpie.Runtime/PhpValueConverter.cs
index aba8923d05..c76abbe20c 100644
--- a/src/Peachpie.Runtime/PhpValueConverter.cs
+++ b/src/Peachpie.Runtime/PhpValueConverter.cs
@@ -3,6 +3,7 @@
using System.Diagnostics;
using System.Reflection;
using System.Text;
+using System.Threading;
namespace Pchp.Core
{
@@ -70,6 +71,8 @@ static Delegate Create_PhpValue_to_T()
if (typeU == typeof(double)) return new Func>(x => Operators.IsSet(x) ? (double?)x.ToDouble() : null);
}
}
+
+ if (typeof(T) == typeof(CancellationToken)) return new Func(x => Convert.UnwrapStruct(x));
}
else // type.IsReferenceType
{