diff --git a/Peachpie.sln b/Peachpie.sln index 2ff24ff158..63a40b78f7 100644 --- a/Peachpie.sln +++ b/Peachpie.sln @@ -82,6 +82,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Peachpie.NET.SdkTests", "sr EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Peachpie.App.Tests", "src\Tests\Peachpie.App.Tests\Peachpie.App.Tests.csproj", "{C8A0A533-BB62-4CB5-8861-06522DCF5EC2}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Peachpie.Library.ComDotNet", "src\Peachpie.Library.ComDotNet\Peachpie.Library.ComDotNet.csproj", "{B9DE827D-79F5-4066-80C5-7535EB66B443}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -654,6 +656,26 @@ Global {C8A0A533-BB62-4CB5-8861-06522DCF5EC2}.Release|x64.Build.0 = Release|Any CPU {C8A0A533-BB62-4CB5-8861-06522DCF5EC2}.Release|x86.ActiveCfg = Release|Any CPU {C8A0A533-BB62-4CB5-8861-06522DCF5EC2}.Release|x86.Build.0 = Release|Any CPU + {B9DE827D-79F5-4066-80C5-7535EB66B443}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B9DE827D-79F5-4066-80C5-7535EB66B443}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B9DE827D-79F5-4066-80C5-7535EB66B443}.Debug|ARM.ActiveCfg = Debug|Any CPU + {B9DE827D-79F5-4066-80C5-7535EB66B443}.Debug|ARM.Build.0 = Debug|Any CPU + {B9DE827D-79F5-4066-80C5-7535EB66B443}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {B9DE827D-79F5-4066-80C5-7535EB66B443}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {B9DE827D-79F5-4066-80C5-7535EB66B443}.Debug|x64.ActiveCfg = Debug|Any CPU + {B9DE827D-79F5-4066-80C5-7535EB66B443}.Debug|x64.Build.0 = Debug|Any CPU + {B9DE827D-79F5-4066-80C5-7535EB66B443}.Debug|x86.ActiveCfg = Debug|Any CPU + {B9DE827D-79F5-4066-80C5-7535EB66B443}.Debug|x86.Build.0 = Debug|Any CPU + {B9DE827D-79F5-4066-80C5-7535EB66B443}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B9DE827D-79F5-4066-80C5-7535EB66B443}.Release|Any CPU.Build.0 = Release|Any CPU + {B9DE827D-79F5-4066-80C5-7535EB66B443}.Release|ARM.ActiveCfg = Release|Any CPU + {B9DE827D-79F5-4066-80C5-7535EB66B443}.Release|ARM.Build.0 = Release|Any CPU + {B9DE827D-79F5-4066-80C5-7535EB66B443}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {B9DE827D-79F5-4066-80C5-7535EB66B443}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {B9DE827D-79F5-4066-80C5-7535EB66B443}.Release|x64.ActiveCfg = Release|Any CPU + {B9DE827D-79F5-4066-80C5-7535EB66B443}.Release|x64.Build.0 = Release|Any CPU + {B9DE827D-79F5-4066-80C5-7535EB66B443}.Release|x86.ActiveCfg = Release|Any CPU + {B9DE827D-79F5-4066-80C5-7535EB66B443}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -684,6 +706,7 @@ Global {8BE90675-F686-4327-8E13-BE3F7B540CF5} = {82A72490-8B53-4A22-BB14-B0C4D6C83D67} {937F85EB-7F06-4A34-8C7D-BA3BF88E71AA} = {30104149-66C2-44B1-899F-F97E9ECA3860} {C8A0A533-BB62-4CB5-8861-06522DCF5EC2} = {30104149-66C2-44B1-899F-F97E9ECA3860} + {B9DE827D-79F5-4066-80C5-7535EB66B443} = {30D6D91F-A671-4E73-A571-5614500FDE0B} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A0FC0FC5-DA92-4FD6-9841-D8ABF963E7AD} diff --git a/build/dummy/dummy.msbuildproj b/build/dummy/dummy.msbuildproj index 4f2a8cb56c..b89ff85095 100644 --- a/build/dummy/dummy.msbuildproj +++ b/build/dummy/dummy.msbuildproj @@ -12,6 +12,7 @@ + diff --git a/build/update-cache.ps1 b/build/update-cache.ps1 index b869fbecbe..ecc753f12c 100644 --- a/build/update-cache.ps1 +++ b/build/update-cache.ps1 @@ -13,7 +13,10 @@ $defaultArgs = "/p:VersionPrefix=$version,VersionSuffix=$suffix" ## Delete old nuget packages Write-Host -f green "Deleting '$version-$suffix' packages from '$packagesSource' ..." -@("Peachpie.Runtime", "Peachpie.Library", "Peachpie.Library.Scripting", "Peachpie.Library.MySql", "Peachpie.Library.MsSql", "Peachpie.Library.Graphics", "Peachpie.Library.Network", "Peachpie.Library.PDO", "Peachpie.Library.XmlDom", "Peachpie.App", "Peachpie.CodeAnalysis", "Peachpie.AspNetCore.Web", "Peachpie.AspNetCore.Mvc", "Peachpie.NET.Sdk", "Peachpie.Library.PDO.MySql", "Peachpie.Library.PDO.Sqlite", "Peachpie.Library.SqlSrv") | % { +@("Peachpie.Runtime", "Peachpie.Library", "Peachpie.Library.Scripting", "Peachpie.Library.MySql", "Peachpie.Library.MsSql", + "Peachpie.Library.Graphics", "Peachpie.Library.Network", "Peachpie.Library.PDO", "Peachpie.Library.XmlDom", "Peachpie.App", + "Peachpie.CodeAnalysis", "Peachpie.AspNetCore.Web", "Peachpie.AspNetCore.Mvc", "Peachpie.NET.Sdk", "Peachpie.Library.PDO.MySql", + "Peachpie.Library.PDO.Sqlite", "Peachpie.Library.SqlSrv", "Peachpie.Library.ComDotNet") | % { $installedFolder = "$packagesSource/$_/$version-$suffix" if (Test-Path $installedFolder) { Remove-Item -Recurse -Force $installedFolder diff --git a/src/Peachpie.Library.ComDotNet/Com.cs b/src/Peachpie.Library.ComDotNet/Com.cs new file mode 100644 index 0000000000..c4640e59ab --- /dev/null +++ b/src/Peachpie.Library.ComDotNet/Com.cs @@ -0,0 +1,121 @@ +using System; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using Pchp.Core; + +namespace Peachpie.Library.ComDotNet +{ + /// + /// COM object + /// TODO : implements VARIANT and inherit from it, which would allow returning VARIANT object from __call and __get + /// and working with it, see test case com_dotnet/com.php + /// + [PhpType(PhpTypeAttribute.InheritName), PhpExtension("com_dotnet")] + public class COM + { + #region Private members + + [PhpHidden] + internal protected object comObject = null; + + [PhpHidden] + internal protected const BindingFlags MemberAccessCom = + BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase; + + #endregion + + #region Construction + + public COM(Context ctx, string module_name) + { + __construct(ctx, module_name); + } + + [PhpFieldsOnlyCtor] + protected COM() + { + } + + public virtual void __construct(Context ctx, string module_name) + { + var comType = Type.GetTypeFromProgID(module_name); + if (comType == null) + return; + + comObject = Activator.CreateInstance(comType); + } + + public virtual void __construct(Context ctx, string module_name, string server_name) + { + var comType = Type.GetTypeFromProgID(module_name, server_name); + if (comType == null) + return; + + comObject = Activator.CreateInstance(comType); + } + + public virtual void __construct(Context ctx, string module_name, string server_name, int code_page) + { + throw new NotSupportedException(); + } + + public virtual void __construct(Context ctx, string module_name, string server_name, int code_page, string typelib) + { + throw new NotSupportedException(); + } + + #endregion + + #region Magic methods + + /// + /// Special field containing runtime fields. + /// + /// + /// The field is handled by runtime and is not intended for direct use. + /// Magic methods for property access are ignored without runtime fields. + /// + [CompilerGenerated] + internal PhpArray __peach__runtimeFields = null; + + public PhpValue __call(string name, PhpArray arguments) + { + if (comObject == null) + return PhpValue.Null; + + var parameters = arguments.GetValues().Select(v => v.ToClr()).ToArray(); + + var result = comObject.GetType().InvokeMember( + name, MemberAccessCom | BindingFlags.InvokeMethod, null, comObject, parameters); + + return PhpValue.FromClr(result); + } + + public virtual PhpValue __get(string name) + { + if (comObject == null) + return PhpValue.Null; + + var prop = comObject.GetType().InvokeMember( + name, MemberAccessCom | BindingFlags.GetProperty, null, comObject, null); + + return prop != null + ? PhpValue.FromClr(prop) + : PhpValue.Null; + } + + public virtual bool __set(string name, PhpValue value) + { + if (comObject == null) + return false; + + comObject.GetType().InvokeMember( + name, MemberAccessCom | BindingFlags.SetProperty, null, comObject, new object[1] { value.ToClr() }); + + return true; + } + + #endregion + } +} diff --git a/src/Peachpie.Library.ComDotNet/ComDotNet.cs b/src/Peachpie.Library.ComDotNet/ComDotNet.cs new file mode 100644 index 0000000000..cbb95206e7 --- /dev/null +++ b/src/Peachpie.Library.ComDotNet/ComDotNet.cs @@ -0,0 +1,21 @@ +using System; +using Pchp.Core; + +namespace Peachpie.Library.ComDotNet +{ + /// + /// COM functions + /// + [PhpExtension("com_dotnet")] + public static class ComDotNet + { + /// + /// Generate a globally unique identifier (GUID) + /// + public static PhpString com_create_guid() + { + return Guid.NewGuid().ToString("B"); + } + } + +} diff --git a/src/Peachpie.Library.ComDotNet/Peachpie.Library.ComDotNet.csproj b/src/Peachpie.Library.ComDotNet/Peachpie.Library.ComDotNet.csproj new file mode 100644 index 0000000000..7004bbccba --- /dev/null +++ b/src/Peachpie.Library.ComDotNet/Peachpie.Library.ComDotNet.csproj @@ -0,0 +1,21 @@ + + + + netstandard2.0 + Peachpie.Library.ComDotNet + Peachpie.Library.ComDotNet + peachpie;library;com;dotnet;com_dotnet + True + Peachpie PHP language library functions for COM/.Net interoperability on Windows. + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Peachpie.Library.ComDotNet/Properties/AssemblyInfo.cs b/src/Peachpie.Library.ComDotNet/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..2685374ebe --- /dev/null +++ b/src/Peachpie.Library.ComDotNet/Properties/AssemblyInfo.cs @@ -0,0 +1,11 @@ +using System.Reflection; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyProduct("Peachpie.Library.ComDotNet")] +[assembly: AssemblyTrademark("")] + +// annotates this library as a php extension, +// all its public static methods with compatible signatures will be seen as global functions to php scope +[assembly: Pchp.Core.PhpExtension("com_dotnet")] \ No newline at end of file diff --git a/src/Tests/Peachpie.ScriptTests/Peachpie.ScriptTests.csproj b/src/Tests/Peachpie.ScriptTests/Peachpie.ScriptTests.csproj index b36d9e06c2..1ce69c2127 100644 --- a/src/Tests/Peachpie.ScriptTests/Peachpie.ScriptTests.csproj +++ b/src/Tests/Peachpie.ScriptTests/Peachpie.ScriptTests.csproj @@ -20,6 +20,7 @@ + diff --git a/src/Tests/Peachpie.ScriptTests/ScriptsTest.cs b/src/Tests/Peachpie.ScriptTests/ScriptsTest.cs index 46ff3d02f5..693c86a74d 100644 --- a/src/Tests/Peachpie.ScriptTests/ScriptsTest.cs +++ b/src/Tests/Peachpie.ScriptTests/ScriptsTest.cs @@ -29,6 +29,7 @@ public class ScriptsTest typeof(Peachpie.Library.Scripting.Standard).Assembly.Location, typeof(Peachpie.Library.XmlDom.XmlDom).Assembly.Location, typeof(Peachpie.Library.Network.CURLFunctions).Assembly.Location, + typeof(Peachpie.Library.ComDotNet.COM).Assembly.Location, }; static readonly Context.IScriptingProvider _provider = Context.DefaultScriptingProvider; // use IScriptingProvider singleton diff --git a/tests/com_dotnet/com.php b/tests/com_dotnet/com.php new file mode 100644 index 0000000000..0cc26ffa30 --- /dev/null +++ b/tests/com_dotnet/com.php @@ -0,0 +1,34 @@ +GetTempName()) . PHP_EOL; + // echo $fso->GetSpecialFolder(0) . PHP_EOL; // <- Fails (COM_Object) + $drives = $fso->Drives; + echo gettype($drives).PHP_EOL; + foreach($drives as $d) { + //echo $d.PHP_EOL; // <- Fails (COM_Object) + $dO = $fso->GetDrive($d); + //echo $dO->DriveLetter.PHP_EOL; // <- Fails (COM_Object) + break; + } + + echo "testing Shell" .PHP_EOL; + $shell = new \COM('WScript.Shell'); + echo $shell->CurrentDirectory . PHP_EOL; + $shell->CurrentDirectory = "C:\\"; + echo $shell->CurrentDirectory . PHP_EOL; +} + +test(); diff --git a/tests/com_dotnet/com_create_guid.php b/tests/com_dotnet/com_create_guid.php new file mode 100644 index 0000000000..17b02cdbbb --- /dev/null +++ b/tests/com_dotnet/com_create_guid.php @@ -0,0 +1,17 @@ +