diff --git a/Source/ClassLibrary/DateTime.CLR/DateTime.cs b/Source/ClassLibrary/DateTime.CLR/DateTime.cs index 3028d6e2..788fa44c 100644 --- a/Source/ClassLibrary/DateTime.CLR/DateTime.cs +++ b/Source/ClassLibrary/DateTime.CLR/DateTime.cs @@ -611,7 +611,7 @@ public static string FormatDate(string format) /// Nuber of seconds since 1970 specifying a date. /// Formatted string. [ImplementsFunction("date")] - public static string FormatDate(string format, int timestamp) + public static string FormatDate(string format, long timestamp) { return FormatDate(format, DateTimeUtils.UnixTimeStampToUtc(timestamp), PhpTimeZone.CurrentTimeZone); } @@ -634,7 +634,7 @@ public static string FormatUtcDate(string format) /// Nuber of seconds since 1970 specifying a date. /// Formatted string. [ImplementsFunction("gmdate")] - public static string FormatUtcDate(string format, int timestamp) + public static string FormatUtcDate(string format, long timestamp) { return FormatDate(format, DateTimeUtils.UnixTimeStampToUtc(timestamp), DateTimeUtils.UtcTimeZone); } @@ -645,7 +645,7 @@ public static string FormatUtcDate(string format, int timestamp) /// Format definition for output. /// Part of the date, e.g. month or hours. [ImplementsFunction("idate")] - public static int GetDatePart(string format) + public static long GetDatePart(string format) { if (format == null || format.Length != 1) PhpException.InvalidArgument("format"); @@ -660,7 +660,7 @@ public static int GetDatePart(string format) /// Nuber of seconds since 1970 specifying a date. /// Part of the date, e.g. month or hours. [ImplementsFunction("idate")] - public static int GetDatePart(string format, int timestamp) + public static long GetDatePart(string format, long timestamp) { if (format == null || format.Length != 1) PhpException.InvalidArgument("format"); @@ -668,7 +668,7 @@ public static int GetDatePart(string format, int timestamp) return GetDatePart(format[0], DateTimeUtils.UnixTimeStampToUtc(timestamp), PhpTimeZone.CurrentTimeZone); } - private static int GetDatePart(char format, DateTime utc, TimeZoneInfo/*!*/ zone) + private static long GetDatePart(char format, DateTime utc, TimeZoneInfo/*!*/ zone) { DateTime local = TimeZoneInfo.ConvertTimeFromUtc(utc, zone);// zone.ToLocalTime(utc); @@ -1035,7 +1035,7 @@ private static void GetIsoWeekAndYear(DateTime dt, out int week, out int year) private static int GetSwatchBeat(DateTime utc) { - int seconds = DateTimeUtils.UtcToUnixTimeStamp(utc); + long seconds = DateTimeUtils.UtcToUnixTimeStamp(utc); int beat = (int)(((seconds - (seconds - ((seconds % 86400) + 3600))) * 10) / 864) % 1000; return (beat < 0) ? beat + 1000 : beat; } @@ -1080,7 +1080,7 @@ public static string FormatTime(string format) /// Number of seconds since 1970 representing the time to format. /// Formatted string representing date and time. [ImplementsFunction("strftime")] - public static string FormatTime(string format, int timestamp) + public static string FormatTime(string format, long timestamp) { return FormatTime(format, DateTimeUtils.UnixTimeStampToUtc(timestamp), PhpTimeZone.CurrentTimeZone); } @@ -1103,7 +1103,7 @@ public static string FormatUtcTime(string format) /// Number of seconds since 1970 representing the time to format. /// Formatted string representing date and time. [ImplementsFunction("gmstrftime")] - public static string FormatUtcTime(string format, int timestamp) + public static string FormatUtcTime(string format, long timestamp) { return FormatTime(format, DateTimeUtils.UnixTimeStampToUtc(timestamp), DateTimeUtils.UtcTimeZone); } @@ -1349,56 +1349,56 @@ private static string FormatTime(string format, DateTime utc, TimeZoneInfo/*!*/ #region gmmktime [ImplementsFunction("gmmktime")] - public static int MakeUtcTime() + public static long MakeUtcTime() { DateTime utc_now = DateTime.UtcNow; return MakeUtcTime(utc_now.Hour, utc_now.Minute, utc_now.Second, utc_now.Month, utc_now.Day, utc_now.Year); } [ImplementsFunction("gmmktime")] - public static int MakeUtcTime(int hour) + public static long MakeUtcTime(int hour) { DateTime utc_now = DateTime.UtcNow; return MakeUtcTime(hour, utc_now.Minute, utc_now.Second, utc_now.Month, utc_now.Day, utc_now.Year); } [ImplementsFunction("gmmktime")] - public static int MakeUtcTime(int hour, int minute) + public static long MakeUtcTime(int hour, int minute) { DateTime utc_now = DateTime.UtcNow; return MakeUtcTime(hour, minute, utc_now.Second, utc_now.Month, utc_now.Day, utc_now.Year); } [ImplementsFunction("gmmktime")] - public static int MakeUtcTime(int hour, int minute, int second) + public static long MakeUtcTime(int hour, int minute, int second) { DateTime utc_now = DateTime.UtcNow; return MakeUtcTime(hour, minute, second, utc_now.Month, utc_now.Day, utc_now.Year); } [ImplementsFunction("gmmktime")] - public static int MakeUtcTime(int hour, int minute, int second, int month) + public static long MakeUtcTime(int hour, int minute, int second, int month) { DateTime utc_now = DateTime.UtcNow; return MakeUtcTime(hour, minute, second, month, utc_now.Day, utc_now.Year); } [ImplementsFunction("gmmktime")] - public static int MakeUtcTime(int hour, int minute, int second, int month, int day) + public static long MakeUtcTime(int hour, int minute, int second, int month, int day) { DateTime utc_now = DateTime.UtcNow; return MakeUtcTime(hour, minute, second, month, day, utc_now.Year); } [ImplementsFunction("gmmktime")] - public static int MakeUtcTime(int hour, int minute, int second, int month, int day, int year, int dummy) + public static long MakeUtcTime(int hour, int minute, int second, int month, int day, int year, int dummy) { // According to PHP manual daylight savings time parameter ignored return MakeUtcTime(hour, minute, second, month, day, year); } [ImplementsFunction("gmmktime")] - public static int MakeUtcTime(int hour, int minute, int second, int month, int day, int year) + public static long MakeUtcTime(int hour, int minute, int second, int month, int day, int year) { return DateTimeUtils.UtcToUnixTimeStamp(MakeDateTime(hour, minute, second, month, day, year)); } @@ -1412,7 +1412,7 @@ public static int MakeUtcTime(int hour, int minute, int second, int month, int d /// /// Unix timestamp. [ImplementsFunction("mktime")] - public static int MakeTime() + public static long MakeTime() { DateTime now = Now; return MakeTime(now.Hour, now.Minute, now.Second, now.Month, now.Day, now.Year, -1); @@ -1425,7 +1425,7 @@ public static int MakeTime() /// The hour. /// Unix timestamp. [ImplementsFunction("mktime")] - public static int MakeTime(int hour) + public static long MakeTime(int hour) { DateTime now = Now; return MakeTime(hour, now.Minute, now.Second, now.Month, now.Day, now.Year, -1); @@ -1439,7 +1439,7 @@ public static int MakeTime(int hour) /// The minute. /// Unix timestamp. [ImplementsFunction("mktime")] - public static int MakeTime(int hour, int minute) + public static long MakeTime(int hour, int minute) { DateTime now = Now; return MakeTime(hour, minute, now.Second, now.Month, now.Day, now.Year, -1); @@ -1454,7 +1454,7 @@ public static int MakeTime(int hour, int minute) /// The second. /// Unix timestamp. [ImplementsFunction("mktime")] - public static int MakeTime(int hour, int minute, int second) + public static long MakeTime(int hour, int minute, int second) { DateTime now = Now; return MakeTime(hour, minute, second, now.Month, now.Day, now.Year, -1); @@ -1470,7 +1470,7 @@ public static int MakeTime(int hour, int minute, int second) /// The month. /// Unix timestamp. [ImplementsFunction("mktime")] - public static int MakeTime(int hour, int minute, int second, int month) + public static long MakeTime(int hour, int minute, int second, int month) { DateTime now = Now; return MakeTime(hour, minute, second, month, now.Day, now.Year, -1); @@ -1487,7 +1487,7 @@ public static int MakeTime(int hour, int minute, int second, int month) /// The day. /// Unix timestamp. [ImplementsFunction("mktime")] - public static int MakeTime(int hour, int minute, int second, int month, int day) + public static long MakeTime(int hour, int minute, int second, int month, int day) { DateTime now = Now; return MakeTime(hour, minute, second, month, day, now.Year, -1); @@ -1504,7 +1504,7 @@ public static int MakeTime(int hour, int minute, int second, int month, int day) /// The year. /// Unix timestamp. [ImplementsFunction("mktime")] - public static int MakeTime(int hour, int minute, int second, int month, int day, int year) + public static long MakeTime(int hour, int minute, int second, int month, int day, int year) { return MakeTime(hour, minute, second, month, day, year, -1); } @@ -1521,7 +1521,7 @@ public static int MakeTime(int hour, int minute, int second, int month, int day, /// Daylight savings time. /// Unix timestamp. [ImplementsFunction("mktime")] - public static int MakeTime(int hour, int minute, int second, int month, int day, int year, int daylightSaving) + public static long MakeTime(int hour, int minute, int second, int month, int day, int year, int daylightSaving) { var zone = PhpTimeZone.CurrentTimeZone; DateTime local = MakeDateTime(hour, minute, second, month, day, year); @@ -1662,7 +1662,7 @@ public static PhpArray GetDate() /// Number of seconds since 1970. /// Associative array with date information. [ImplementsFunction("getdate")] - public static PhpArray GetDate(int timestamp) + public static PhpArray GetDate(long timestamp) { return GetDate(DateTimeUtils.UnixTimeStampToUtc(timestamp)); } @@ -1816,7 +1816,7 @@ public static PhpArray GetLocalTime() /// Number of seconds since 1970. /// Array containing values specifying the date and time. [ImplementsFunction("localtime")] - public static PhpArray GetLocalTime(int timestamp) + public static PhpArray GetLocalTime(long timestamp) { return GetLocalTime(DateTimeUtils.UnixTimeStampToUtc(timestamp), false); } @@ -1846,7 +1846,7 @@ public static PhpArray GetLocalTime(int timestamp) /// /// [ImplementsFunction("localtime")] - public static PhpArray GetLocalTime(int timestamp, bool returnAssociative) + public static PhpArray GetLocalTime(long timestamp, bool returnAssociative) { return GetLocalTime(DateTimeUtils.UnixTimeStampToUtc(timestamp), returnAssociative); } @@ -1985,13 +1985,13 @@ public static object StringToTime(string time) /// Timestamp (seconds from 1970) to which is the new timestamp counted. /// Number of seconds since 1/1/1970 or -1 on failure. [ImplementsFunction("strtotime")] - public static object StringToTime(string time, int start) + public static object StringToTime(string time, long start) { return StringToTime(time, DateTimeUtils.UnixTimeStampToUtc(start)); } /// - /// Implementation of function. + /// Implementation of function. /// private static object StringToTime(string time, DateTime startUtc) { @@ -2000,7 +2000,7 @@ private static object StringToTime(string time, DateTime startUtc) if (time.Length == 0) return false; string error = null; - int result = StrToTime.DateInfo.Parse(time, startUtc, out error); + long result = StrToTime.DateInfo.Parse(time, startUtc, out error); if (error != null) { PhpException.Throw(PhpError.Warning, error); @@ -2019,7 +2019,7 @@ private static object StringToTime(string time, DateTime startUtc) /// /// Number of seconds since 1970. [ImplementsFunction("time")] - public static int Time() + public static long Time() { return DateTimeUtils.UtcToUnixTimeStamp(DateTime.UtcNow); } @@ -2039,79 +2039,79 @@ public enum TimeFormats } [ImplementsFunction("date_sunrise")] - public static object GetSunriseTime(int timestamp) + public static object GetSunriseTime(long timestamp) { return GetSunTime(timestamp, TimeFormats.String, Double.NaN, Double.NaN, Double.NaN, Double.NaN, true); } [ImplementsFunction("date_sunrise")] - public static object GetSunriseTime(int timestamp, TimeFormats format) + public static object GetSunriseTime(long timestamp, TimeFormats format) { return GetSunTime(timestamp, format, Double.NaN, Double.NaN, Double.NaN, Double.NaN, true); } - [ImplementsFunction("date_sunrise")] - public static object GetSunriseTime(int timestamp, TimeFormats format, double latitude) + [ImplementsFunction("date_sunrise")] + public static object GetSunriseTime(long timestamp, TimeFormats format, double latitude) { return GetSunTime(timestamp, format, latitude, Double.NaN, Double.NaN, Double.NaN, true); } - [ImplementsFunction("date_sunrise")] - public static object GetSunriseTime(int timestamp, TimeFormats format, double latitude, double longitude) + [ImplementsFunction("date_sunrise")] + public static object GetSunriseTime(long timestamp, TimeFormats format, double latitude, double longitude) { return GetSunTime(timestamp, format, latitude, longitude, Double.NaN, Double.NaN, true); } - [ImplementsFunction("date_sunrise")] - public static object GetSunriseTime(int timestamp, TimeFormats format, double latitude, double longitude, double zenith) + [ImplementsFunction("date_sunrise")] + public static object GetSunriseTime(long timestamp, TimeFormats format, double latitude, double longitude, double zenith) { return GetSunTime(timestamp, format, latitude, longitude, zenith, Double.NaN, true); } - [ImplementsFunction("date_sunrise")] - public static object GetSunriseTime(int timestamp, TimeFormats format, double latitude, double longitude, double zenith, double offset) + [ImplementsFunction("date_sunrise")] + public static object GetSunriseTime(long timestamp, TimeFormats format, double latitude, double longitude, double zenith, double offset) { return GetSunTime(timestamp, format, latitude, longitude, zenith, offset, true); } - [ImplementsFunction("date_sunset")] - public static object GetSunsetTime(int timestamp) + [ImplementsFunction("date_sunset")] + public static object GetSunsetTime(long timestamp) { return GetSunTime(timestamp, TimeFormats.String, Double.NaN, Double.NaN, Double.NaN, Double.NaN, false); } - [ImplementsFunction("date_sunset")] - public static object GetSunsetTime(int timestamp, TimeFormats format) + [ImplementsFunction("date_sunset")] + public static object GetSunsetTime(long timestamp, TimeFormats format) { return GetSunTime(timestamp, format, Double.NaN, Double.NaN, Double.NaN, Double.NaN, false); } - [ImplementsFunction("date_sunset")] - public static object GetSunsetTime(int timestamp, TimeFormats format, double latitude) + [ImplementsFunction("date_sunset")] + public static object GetSunsetTime(long timestamp, TimeFormats format, double latitude) { return GetSunTime(timestamp, format, latitude, Double.NaN, Double.NaN, Double.NaN, false); } - [ImplementsFunction("date_sunset")] - public static object GetSunsetTime(int timestamp, TimeFormats format, double latitude, double longitude) + [ImplementsFunction("date_sunset")] + public static object GetSunsetTime(long timestamp, TimeFormats format, double latitude, double longitude) { return GetSunTime(timestamp, format, latitude, longitude, Double.NaN, Double.NaN, false); } - [ImplementsFunction("date_sunset")] - public static object GetSunsetTime(int timestamp, TimeFormats format, double latitude, double longitude, double zenith) + [ImplementsFunction("date_sunset")] + public static object GetSunsetTime(long timestamp, TimeFormats format, double latitude, double longitude, double zenith) { return GetSunTime(timestamp, format, latitude, longitude, zenith, Double.NaN, false); } - [ImplementsFunction("date_sunset")] - public static object GetSunsetTime(int timestamp, TimeFormats format, double latitude, double longitude, double zenith, double offset) + [ImplementsFunction("date_sunset")] + public static object GetSunsetTime(long timestamp, TimeFormats format, double latitude, double longitude, double zenith, double offset) { return GetSunTime(timestamp, format, latitude, longitude, zenith, offset, false); - } - - - public static object GetSunTime(int timestamp, TimeFormats format, double latitude, double longitude, double zenith, double offset, bool getSunrise) + } + + + public static object GetSunTime(long timestamp, TimeFormats format, double latitude, double longitude, double zenith, double offset, bool getSunrise) { var zone = PhpTimeZone.CurrentTimeZone; DateTime utc = DateTimeUtils.UnixTimeStampToUtc(timestamp); @@ -2263,11 +2263,11 @@ private static double CalculateSunTime(int day, double latitude, double longitud struct StringToTimeCase { public string String; - public int StartTime; + public long StartTime; public string Result; public TimeZoneInfo[] Zones; - public StringToTimeCase(string str, int start, string result, TimeZoneInfo[] zones) + public StringToTimeCase(string str, long start, string result, TimeZoneInfo[] zones) { this.String = str; this.StartTime = start; diff --git a/Source/ClassLibrary/DateTime.CLR/DateTimeParsing.cs b/Source/ClassLibrary/DateTime.CLR/DateTimeParsing.cs index af0ecb6e..ab16aa04 100644 --- a/Source/ClassLibrary/DateTime.CLR/DateTimeParsing.cs +++ b/Source/ClassLibrary/DateTime.CLR/DateTimeParsing.cs @@ -116,7 +116,7 @@ public struct Relative #region Parse - public static int Parse(string/*!*/ str, DateTime utcStart, out string error) + public static long Parse(string/*!*/ str, DateTime utcStart, out string error) { Debug.Assert(str != null); @@ -141,7 +141,7 @@ public static int Parse(string/*!*/ str, DateTime utcStart, out string error) #region GetUnixTimeStamp - private int GetUnixTimeStamp(DateTime utcStart, out string error) + private long GetUnixTimeStamp(DateTime utcStart, out string error) { var zone = PhpTimeZone.CurrentTimeZone; DateTime start = TimeZoneInfo.ConvertTimeFromUtc(utcStart, zone);// zone.ToLocalTime(utcStart); diff --git a/Source/ClassLibrary/FileSystem.CLR.cs b/Source/ClassLibrary/FileSystem.CLR.cs index 79ae0f88..9b83b24a 100644 --- a/Source/ClassLibrary/FileSystem.CLR.cs +++ b/Source/ClassLibrary/FileSystem.CLR.cs @@ -55,6 +55,8 @@ private static void Clear() statCacheUrl = null; } + private static char[] invalidPathChars = Path.GetInvalidPathChars(); + #endregion #region Stat Basics (BuildStatArray, StatInternal, lstat, stat, fstat, clearstatcache; exists, touch) @@ -510,11 +512,11 @@ public static string GetType(string path) /// The file access time or -1 in case of failure. [ImplementsFunction("fileatime")] [return: CastToFalse] - public static int GetAccessTime(string path) + public static long GetAccessTime(string path) { bool ok = StatInternal(path, false); if (!ok) return -1; - return unchecked((int)statCache.st_atime); + return statCache.st_atime; } /// @@ -666,7 +668,7 @@ public static bool IsDirectory(string path) { StreamWrapper wrapper; - if (!string.IsNullOrEmpty(path) && StatInternalCheck(ref path, false, out wrapper)) // do not throw warning if path is null or empty + if (!string.IsNullOrEmpty(path) && path.IndexOfAny(invalidPathChars) < 0 && StatInternalCheck(ref path, false, out wrapper)) // do not throw warning if path is null or empty { string url; if (StatInternalTryCache(path, out url)) @@ -693,7 +695,7 @@ public static bool IsDirectory(string path) [ImplementsFunction("is_executable")] public static bool IsExecutable(string path) { - bool ok = StatInternal(path, false); + bool ok = !string.IsNullOrEmpty(path) && path.IndexOfAny(invalidPathChars) < 0 && StatInternal(path, false); if (!ok) return false; return ((FileModeFlags)statCache.st_mode & FileModeFlags.Execute) > 0; } @@ -708,7 +710,7 @@ public static bool IsFile(string path) { StreamWrapper wrapper; - if (StatInternalCheck(ref path, false, out wrapper)) + if (!string.IsNullOrEmpty(path) && path.IndexOfAny(invalidPathChars) < 0 && StatInternalCheck(ref path, false, out wrapper)) { string url; if (StatInternalTryCache(path, out url)) @@ -744,7 +746,7 @@ public static bool IsLink(string path) [ImplementsFunction("is_readable")] public static bool IsReadable(string path) { - bool ok = StatInternal(path, false); + bool ok = !string.IsNullOrEmpty(path) && path.IndexOfAny(invalidPathChars) < 0 && StatInternal(path, false); if (!ok) return false; return ((FileModeFlags)statCache.st_mode & FileModeFlags.Read) > 0; } @@ -769,7 +771,7 @@ public static bool IsWriteable(string path) [ImplementsFunction("is_writable")] public static bool IsWritable(string path) { - bool ok = StatInternal(path, false); + bool ok = !string.IsNullOrEmpty(path) && path.IndexOfAny(invalidPathChars) < 0 && StatInternal(path, false); if (!ok) return false; return ((FileModeFlags)statCache.st_mode & FileModeFlags.Write) > 0; } diff --git a/Source/ClassLibrary/Miscellaneous.cs b/Source/ClassLibrary/Miscellaneous.cs index f84a3fc0..b0f84c86 100644 --- a/Source/ClassLibrary/Miscellaneous.cs +++ b/Source/ClassLibrary/Miscellaneous.cs @@ -378,7 +378,7 @@ public static int GetCurrentProcessId() /// /// The UNIX timestamp or -1 on error. [ImplementsFunction("getlastmod")] - public static int GetLastModification() + public static long GetLastModification() { try { diff --git a/Source/ClassLibrary/Output.CLR.cs b/Source/ClassLibrary/Output.CLR.cs index c1b7701c..9ff55084 100644 --- a/Source/ClassLibrary/Output.CLR.cs +++ b/Source/ClassLibrary/Output.CLR.cs @@ -332,7 +332,22 @@ public static PhpArray GetStatus(bool full) public static void FlushHttpBuffers() { HttpContext http_context = HttpContext.Current; - if (http_context != null) http_context.Response.Flush(); + if (http_context != null) + { + try + { + http_context.Response.Flush(); + } + catch (HttpException) + { + var context = RequestContext.CurrentContext; + if (context != null && !context.TrackClientDisconnection) + { + return; + } + throw; + } + } } /// diff --git a/Source/ClassLibrary/Session.CLR.cs b/Source/ClassLibrary/Session.CLR.cs index b7425d0d..467f52cc 100644 --- a/Source/ClassLibrary/Session.CLR.cs +++ b/Source/ClassLibrary/Session.CLR.cs @@ -213,7 +213,7 @@ protected override void Collect(string savePath, string sid, int lifetime) { if (dir == null) return; - int threshold = DateTimeUtils.UtcToUnixTimeStamp(DateTime.Now.ToUniversalTime().AddSeconds(-lifetime)); + long threshold = DateTimeUtils.UtcToUnixTimeStamp(DateTime.Now.ToUniversalTime().AddSeconds(-lifetime)); string file_name; while ((file_name = PhpDirectory.Read(dir)) != null) @@ -221,7 +221,7 @@ protected override void Collect(string savePath, string sid, int lifetime) if (file_name.Length >= FilePrefix.Length && file_name.Substring(0, FilePrefix.Length) == FilePrefix) { string full_path = Path.Combine(savePath, file_name); - int time = PhpFile.GetAccessTime(full_path); + long time = PhpFile.GetAccessTime(full_path); if (time < threshold) { diff --git a/Source/ClassLibrary/Web.cs b/Source/ClassLibrary/Web.cs index 8bdac944..468c6665 100644 --- a/Source/ClassLibrary/Web.cs +++ b/Source/ClassLibrary/Web.cs @@ -378,7 +378,7 @@ public static string RawUrlEncode(string str) [ImplementsFunction("urldecode")] public static string UrlDecode(string str) { - return HttpUtility.UrlDecode(str); + return HttpUtility.UrlDecode(str, Configuration.Application.Globalization.PageEncoding); } /// @@ -387,7 +387,7 @@ public static string UrlDecode(string str) [ImplementsFunction("urlencode")] public static string UrlEncode(string str) { - return UpperCaseEncodedChars(HttpUtility.UrlEncode(str)); + return UpperCaseEncodedChars(HttpUtility.UrlEncode(str, Configuration.Application.Globalization.PageEncoding)); } private static string UpperCaseEncodedChars(string encoded) diff --git a/Source/Core/Compiler/AppCompiler.CLR.cs b/Source/Core/Compiler/AppCompiler.CLR.cs index 64f230a3..9f26a874 100644 --- a/Source/Core/Compiler/AppCompiler.CLR.cs +++ b/Source/Core/Compiler/AppCompiler.CLR.cs @@ -1324,7 +1324,7 @@ public static bool IsPureUnit(string/*!*/ value) } PhpAssemblyBuilder assembly_builder = PhpAssemblyBuilder.Create(applicationContext, kind, ps.Pure, ps.OutPath, - ps.DocPath, entry_point_file, ps.Version, ps.Key, ps.Icon, resource_files, config.Compiler.Debug, ps.Force32Bit); + ps.DocPath, entry_point_file, ps.Version, ps.Key, ps.Icon, resource_files, config.Compiler.DebugMode, ps.Force32Bit); assembly_builder.IsMTA = ps.IsMTA; diff --git a/Source/Core/Compiler/CodeGenerator/CodeGenerator.cs b/Source/Core/Compiler/CodeGenerator/CodeGenerator.cs index 7f010ca1..1129537e 100644 --- a/Source/Core/Compiler/CodeGenerator/CodeGenerator.cs +++ b/Source/Core/Compiler/CodeGenerator/CodeGenerator.cs @@ -2950,7 +2950,7 @@ internal void EmitPhpException(ILEmitter/*!*/ il, MethodInfo/*!*/ method) /// Real last column of the point. internal void MarkSequencePoint(int startLine, int startColumn, int endLine, int endColumn) { - if (context.Config.Compiler.Debug) + if (context.Config.Compiler.DebugMode != DebugMode.None) { // ignores #pragma inside the code span: ISymbolDocumentWriter symbol_writer = sourceUnit.GetMappedSymbolDocumentWriter(startLine); @@ -2971,7 +2971,7 @@ internal void MarkSequencePoint(int startLine, int startColumn, int endLine, int } } - /// + /// /// Marks a sequence point (see ) using position of given . /// /// Expression which position is used to mark sequence point. diff --git a/Source/Core/Compiler/WebServerManagers.CLR.cs b/Source/Core/Compiler/WebServerManagers.CLR.cs index 52384f70..eae44548 100644 --- a/Source/Core/Compiler/WebServerManagers.CLR.cs +++ b/Source/Core/Compiler/WebServerManagers.CLR.cs @@ -381,7 +381,7 @@ public IPhpModuleBuilder DefineModuleBuilder(CompilationUnitBase/*!*/ compiledUn // creates a script assembly builder: SingleScriptAssemblyBuilder builder = new SingleScriptAssemblyBuilder(applicationContext, - name, outDir, name.Name + AssemblyExt, AssemblyKinds.WebPage, context.Config.Compiler.Debug, false, context.SaveOnlyAssembly, null); + name, outDir, name.Name + AssemblyExt, AssemblyKinds.WebPage, context.Config.Compiler.DebugMode, false, context.SaveOnlyAssembly, null); return builder.DefineScript(unit); } diff --git a/Source/Core/Configuration.CLR.cs b/Source/Core/Configuration.CLR.cs index 230c0a9c..c1d03d2c 100644 --- a/Source/Core/Configuration.CLR.cs +++ b/Source/Core/Configuration.CLR.cs @@ -851,7 +851,15 @@ internal int HashCode hashcode += mapping.Pattern.ToString().GetHashCode(); hashcode += mapping.Replacement.GetHashCode(); } - hashcode += debug ? 897987897 : 12; + if (debug) + { + hashcode += 897987897; + } + else + { + hashcode += 12; + hashcode += genegatePdbInRelease ? 564456654 : 35; + } } dirty = false; @@ -873,6 +881,7 @@ internal CompilerSection() dirty = true; hashcode = 0; debug = true; + genegatePdbInRelease = true; disabledWarnings = WarningGroups.DeferredToRuntime | WarningGroups.CompilerStrict; disabledWarningNumbers = ArrayUtils.EmptyIntegers; @@ -979,6 +988,10 @@ public bool Parse(string name, string value, XmlNode node) debug = (value == "true"); return true; + case "GeneratePdbInRelease": + genegatePdbInRelease = (value == "true"); + return true; + case "WatchSourceChanges": { // applicable only in run-time: diff --git a/Source/Core/Configuration.cs b/Source/Core/Configuration.cs index b40685da..7b3ef405 100644 --- a/Source/Core/Configuration.cs +++ b/Source/Core/Configuration.cs @@ -16,6 +16,12 @@ namespace PHP.Core { + public enum DebugMode + { + None, + Pdb, + Full + } #region Language Features Enum /// @@ -222,7 +228,14 @@ public sealed partial class OutputControlSection : IPhpConfigurationSection internal OutputControlSection DeepCopy() { - return (OutputControlSection)MemberwiseClone(); + return new OutputControlSection() + { + charSet = charSet, + contentType = contentType, + implicitFlush = implicitFlush, + outputBuffering = outputBuffering, + outputHandler = outputHandler == null ? null : outputHandler.DeepCopy() + }; } } @@ -337,7 +350,12 @@ private static string AbsolutizeLogFile(string value, System.Xml.XmlNode/*!*/nod /// internal ErrorControlSection DeepCopy() { - return (ErrorControlSection)this.MemberwiseClone(); + var copy = (ErrorControlSection)MemberwiseClone(); + if (UserExceptionHandler != null) + copy.UserExceptionHandler = UserExceptionHandler.DeepCopy(); + if (UserHandler != null) + copy.UserHandler = UserHandler.DeepCopy(); + return copy; } } @@ -422,7 +440,10 @@ public sealed partial class AssertionSection : IPhpConfigurationSection /// internal AssertionSection DeepCopy() { - return (AssertionSection)this.MemberwiseClone(); + var copy = (AssertionSection) MemberwiseClone(); + if (Callback != null) + copy.Callback = Callback.DeepCopy(); + return copy; } } @@ -503,7 +524,12 @@ public static bool ValidateRegisteringOrder(string value) /// internal VariablesSection DeepCopy() { - return (VariablesSection)this.MemberwiseClone(); + var copy = (VariablesSection) MemberwiseClone(); + if (DeserializationCallback != null) + { + copy.DeserializationCallback = DeserializationCallback.DeepCopy(); + } + return copy; } } @@ -787,7 +813,34 @@ public bool Debug } internal bool debug; - #endregion + public bool GenegatePdbInRelease { + get { return genegatePdbInRelease; } + set + { + genegatePdbInRelease = value; +#if !SILVERLIGHT + dirty = true; +#endif + } + } + internal bool genegatePdbInRelease; + + public DebugMode DebugMode + { + get + { + DebugMode result; + if (debug) + result = DebugMode.Full; + else if (genegatePdbInRelease) + result = DebugMode.Pdb; + else + result = DebugMode.None; + return result; + } + } + + #endregion #region Inclusions diff --git a/Source/Core/Emit/AssemblyBuilders.CLR.cs b/Source/Core/Emit/AssemblyBuilders.CLR.cs index 7eda1953..b963607a 100644 --- a/Source/Core/Emit/AssemblyBuilders.CLR.cs +++ b/Source/Core/Emit/AssemblyBuilders.CLR.cs @@ -47,8 +47,8 @@ public bool IsExecutable /// /// Whether the assembly contains debug information. /// - public bool Debuggable { get { return debuggable; } } - private readonly bool debuggable; + public DebugMode Debuggable { get { return debuggable; } } + private readonly DebugMode debuggable; /// /// Whether saved assembly should be executed as 32-bit process on 64-bit environments. @@ -91,7 +91,7 @@ public bool IsExecutable #region Construction protected PhpAssemblyBuilder(PhpAssembly/*!*/ assembly, AssemblyName assemblyName, string moduleName, - string directory, string fileName, AssemblyKinds kind, ICollection resources, bool debug, + string directory, string fileName, AssemblyKinds kind, ICollection resources, DebugMode debug, bool force32bit, bool saveOnlyAssembly, Win32IconResource icon) : base(assembly) { @@ -109,7 +109,7 @@ public bool IsExecutable #else AssemblyBuilder assembly_builder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, saveOnlyAssembly ? AssemblyBuilderAccess.Save : AssemblyBuilderAccess.RunAndSave, directory); - ModuleBuilder module_builder = assembly_builder.DefineDynamicModule(moduleName, fileName, debug); + ModuleBuilder module_builder = assembly_builder.DefineDynamicModule(moduleName, fileName, debug != DebugMode.None); #endif DefineGlobalType(module_builder); assembly.WriteUp(module_builder, Path.Combine(directory, fileName)); // TODO: Combine can be avoided (pass path instead of directory + fileName) @@ -186,7 +186,7 @@ public override void EmitCustomAttribute(CustomAttributeBuilder/*!*/ builder, AS public static PhpAssemblyBuilder/*!*/ Create(ApplicationContext/*!*/ applicationContext, AssemblyKinds kind, bool pure, FullPath outPath, FullPath docPath, PhpSourceFile entryPoint, Version version, - StrongNameKeyPair key, Win32IconResource icon, ICollection resources, bool debug, bool force32bit) + StrongNameKeyPair key, Win32IconResource icon, ICollection resources, DebugMode debug, bool force32bit) { string out_dir = Path.GetDirectoryName(outPath); string out_file = Path.GetFileName(outPath); @@ -222,7 +222,7 @@ public void Save() BakeGlobals(); // annotates the assembly with Debuggable attribute: - if (debuggable) + if (debuggable == DebugMode.Full) { builder.SetCustomAttribute(new CustomAttributeBuilder(Constructors.Debuggable, new object[] { true, true })); @@ -377,7 +377,7 @@ private MethodInfo CreateEntryPoint() RealAssemblyBuilder.SetEntryPoint(entry_method, Enums.ToPEFileKind(kind)); // user entry point can be defined only on module which is in debug mode: - if (debuggable) + if (debuggable == DebugMode.Full) ReflectionUtils.SetUserEntryPoint(RealModuleBuilder, GetUserEntryPointMethod()); return entry_method; @@ -401,7 +401,7 @@ public sealed class PureAssemblyBuilder : PhpAssemblyBuilder public override bool IsPure { get { return true; } } public PureAssemblyBuilder(ApplicationContext/*!*/ applicationContext, AssemblyName assemblyName, - string directory, string fileName, AssemblyKinds kind, ICollection resources, bool debug, bool force32bit, Win32IconResource icon) + string directory, string fileName, AssemblyKinds kind, ICollection resources, DebugMode debug, bool force32bit, Win32IconResource icon) : base(new PureAssembly(applicationContext), assemblyName, PureAssembly.ModuleName, directory, fileName, kind, resources, debug, force32bit, false, icon) { @@ -503,7 +503,7 @@ public abstract class ScriptAssemblyBuilder : PhpAssemblyBuilder public ScriptAssembly/*!*/ ScriptAssembly { get { return (ScriptAssembly)assembly; } } protected ScriptAssemblyBuilder(ScriptAssembly/*!*/ assembly, AssemblyName assemblyName, string directory, - string fileName, AssemblyKinds kind, ICollection resources, bool debug, + string fileName, AssemblyKinds kind, ICollection resources, DebugMode debug, bool force32bit, bool saveOnlyAssembly, Win32IconResource icon) : base(assembly, assemblyName, ScriptAssembly.RealModuleName, directory, fileName, kind,resources, debug, force32bit, saveOnlyAssembly, icon) { @@ -635,7 +635,7 @@ internal class SingleScriptAssemblyBuilder : ScriptAssemblyBuilder /// Icon resource or a null reference. /// Resources to embed public SingleScriptAssemblyBuilder(ApplicationContext/*!*/ applicationContext, AssemblyName assemblyName, string directory, string fileName, - AssemblyKinds kind, ICollection resources, bool debug, bool force32bit, bool saveOnlyAssembly, Win32IconResource icon) + AssemblyKinds kind, ICollection resources, DebugMode debug, bool force32bit, bool saveOnlyAssembly, Win32IconResource icon) : base(new SingleScriptAssembly(applicationContext), assemblyName, directory, fileName, kind, resources, debug, force32bit, saveOnlyAssembly, icon) { } @@ -652,7 +652,7 @@ internal class SingleScriptAssemblyBuilder : ScriptAssemblyBuilder /// Whether to not load the assembly into memory. /// Icon resource or a null reference. public SingleScriptAssemblyBuilder(ApplicationContext/*!*/ applicationContext, AssemblyName assemblyName, string directory, string fileName, - AssemblyKinds kind, bool debug, bool force32bit, bool saveOnlyAssembly, Win32IconResource icon) + AssemblyKinds kind, DebugMode debug, bool force32bit, bool saveOnlyAssembly, Win32IconResource icon) : base(new SingleScriptAssembly(applicationContext), assemblyName, directory, fileName, kind, null, debug, force32bit, saveOnlyAssembly, icon) { } @@ -723,7 +723,7 @@ internal class MultiScriptAssemblyBuilder : ScriptAssemblyBuilder /// Resources to embed public MultiScriptAssemblyBuilder(ApplicationContext/*!*/ applicationContext, AssemblyName assemblyName, string directory, string fileName, AssemblyKinds kind, ICollection resources, - bool debug, bool force32bit, Win32IconResource icon, PhpSourceFile entryPoint) + DebugMode debug, bool force32bit, Win32IconResource icon, PhpSourceFile entryPoint) : base(new MultiScriptAssembly(applicationContext), assemblyName, directory, fileName, kind, resources, debug, force32bit, false, icon) { this.entryPoint = entryPoint; diff --git a/Source/Core/Emit/AssemblyBuilders.cs b/Source/Core/Emit/AssemblyBuilders.cs index 7a98f570..b70e82ba 100644 --- a/Source/Core/Emit/AssemblyBuilders.cs +++ b/Source/Core/Emit/AssemblyBuilders.cs @@ -194,7 +194,7 @@ public sealed class TransientAssemblyBuilder : PhpAssemblyBuilderBase private object initializationMutex = new object(); private volatile bool initialized = false; - private bool debuggable = false; + private DebugMode debuggable = DebugMode.None; #endregion @@ -230,7 +230,7 @@ public TransientAssemblyBuilder(ApplicationContext/*!*/ applicationContext) return result; } - private void InitializeRealAssembly(bool debuggable) + private void InitializeRealAssembly(DebugMode debuggable) { if (!initialized) { @@ -243,7 +243,7 @@ private void InitializeRealAssembly(bool debuggable) // TODO: do we need sync? AssemblyBuilder assembly_builder = AppDomain.CurrentDomain.DefineDynamicAssembly (assembly_name, AssemblyBuilderAccess.Run); - ModuleBuilder module_builder = assembly_builder.DefineDynamicModule(TransientAssembly.RealModuleName, debuggable); + ModuleBuilder module_builder = assembly_builder.DefineDynamicModule(TransientAssembly.RealModuleName, debuggable!=DebugMode.None); assembly.WriteUp(module_builder, null); @@ -258,7 +258,7 @@ private void InitializeRealAssembly(bool debuggable) public TransientAssembly TransientAssembly { get { return (TransientAssembly)assembly; } } - public TransientModuleBuilder/*!*/ DefineModule(TransientCompilationUnit/*!*/ compilationUnit, bool debuggable, + public TransientModuleBuilder/*!*/ DefineModule(TransientCompilationUnit/*!*/ compilationUnit, DebugMode debuggable, int containerId, EvalKinds kind, string sourcePath) { InitializeRealAssembly(debuggable); diff --git a/Source/Core/Errors.cs b/Source/Core/Errors.cs index 813d4ba3..f582ea72 100644 --- a/Source/Core/Errors.cs +++ b/Source/Core/Errors.cs @@ -216,7 +216,7 @@ public static void ArgumentNull(string argument) /// The name of the argument. public static void ReferenceNull(string argument) { - Throw(PhpError.Error, CoreResources.GetString("reference_null", argument)); + Throw(PhpError.Warning, CoreResources.GetString("reference_null", argument)); } /// @@ -585,12 +585,12 @@ public static void PropertyTypeMismatch(string/*!*/ className, string/*!*/ prope [ThreadStatic] internal static Action ThrowCallbackOverride = null; - /// - /// Reports a PHP error. - /// - /// The error type - /// The error message. - public static void Throw(PhpError error, string message) + /// + /// Reports a PHP error. + /// + /// The error type + /// The error message. + public static void Throw(PhpError error, string message) { if (ThrowCallbackOverride != null) { @@ -607,7 +607,7 @@ public static void Throw(PhpError error, string message) // determines whether the error will be reported and whether it is handleable: bool is_error_reported = ((PhpErrorSet)error & config.ErrorControl.ReportErrors) != 0 && !context.ErrorReportingDisabled; - bool is_error_handleable = ((PhpErrorSet)error & PhpErrorSet.Handleable & (PhpErrorSet)config.ErrorControl.UserHandlerErrors) != 0; + bool is_error_handleable = ((PhpErrorSet)error & PhpErrorSet.Handleable & (PhpErrorSet)config.ErrorControl.UserHandlerErrors) != 0; bool is_error_fatal = ((PhpErrorSet)error & PhpErrorSet.Fatal) != 0; bool do_report = true; diff --git a/Source/Core/HttpHeaders.CLR.cs b/Source/Core/HttpHeaders.CLR.cs index 422a4d09..16e86424 100644 --- a/Source/Core/HttpHeaders.CLR.cs +++ b/Source/Core/HttpHeaders.CLR.cs @@ -435,8 +435,13 @@ private class IntegratedPipelineHeaders : HttpHeaders public IntegratedPipelineHeaders() :base(false) { - if (httpContext != null) - httpContext.Response.Headers["X-Powered-By"] = PoweredByHeader; + try + { + if (httpContext != null) + httpContext.Response.Headers["X-Powered-By"] = PoweredByHeader; + } + catch(HttpException) // integrated pipeline initialization mode + {} } #endregion diff --git a/Source/Core/PhpBytes.cs b/Source/Core/PhpBytes.cs index 7b068304..93eb7a88 100644 --- a/Source/Core/PhpBytes.cs +++ b/Source/Core/PhpBytes.cs @@ -195,35 +195,35 @@ internal void Unshare() #region DebugView, DumpTo - /// - /// Dumps internal data, escapes non-ASCII characters. - /// - /// Output to dump to. - private void DumpTo(System.IO.TextWriter/*!*/output) - { - Debug.Assert(output != null); - - const string hex_digs = "0123456789abcdef"; - char[] patch = new char[4] { '\\', 'x', '0', '0' }; - - foreach (byte b in ReadonlyData) - { - // printable characters are outputted normally - if (b < 0x7f) - { - output.Write((char)b); - } - else - { - patch[2] = hex_digs[(b & 0xf0) >> 4]; - patch[3] = hex_digs[(b & 0x0f)]; - - output.Write(patch); - } - } - } - - private string DebugView() + /// + /// Dumps internal data, escapes non-ASCII characters. + /// + /// Output to dump to. + private void DumpTo(System.IO.TextWriter /*!*/ output) + { + Debug.Assert(output != null); + + const string hex_digs = "0123456789abcdef"; + char[] patch = new char[4] {'\\', 'x', '0', '0'}; + + foreach (byte b in ReadonlyData) + { + // printable characters are outputted normally + if (b < 0x7f) + { + output.Write((char) b); + } + else + { + patch[2] = hex_digs[(b & 0xf0) >> 4]; + patch[3] = hex_digs[(b & 0x0f)]; + + output.Write(patch); + } + } + } + + private string DebugView() { var output = new System.IO.StringWriter(); const string hex_digs = "0123456789ABCDEF"; @@ -421,9 +421,7 @@ string IPhpConvertible.ToString(bool throwOnError, out bool success) /// The output text stream. public void Print(System.IO.TextWriter output) { - output.Write("\""); - DumpTo(output); - output.WriteLine("\""); + output.Write(ToString()); } /// diff --git a/Source/Core/PhpCallback.cs b/Source/Core/PhpCallback.cs index 97fabf6c..0d9ae472 100644 --- a/Source/Core/PhpCallback.cs +++ b/Source/Core/PhpCallback.cs @@ -583,7 +583,14 @@ string IPhpConvertible.ToString() } } - /// + public PhpCallback DeepCopy() + { + Debug.Assert(!IsBound); //bounded callbacks are not supported + + return (PhpCallback)MemberwiseClone(); + } + + /// /// Converts instance to its string representation according to PHP conversion algorithm. /// /// Indicates whether conversion was successful. diff --git a/Source/Core/Reflection/CompilationUnits.cs b/Source/Core/Reflection/CompilationUnits.cs index 6d9af36a..079f437c 100644 --- a/Source/Core/Reflection/CompilationUnits.cs +++ b/Source/Core/Reflection/CompilationUnits.cs @@ -315,7 +315,7 @@ public override DConstant GetVisibleConstant(QualifiedName qualifiedName, ref st // .. but we don't know whether it will be dynamic in advance! this.assembly_builder = scriptContext.ApplicationContext.TransientAssemblyBuilder; - this.module_builder = assembly_builder.DefineModule(this, context.Config.Compiler.Debug, + this.module_builder = assembly_builder.DefineModule(this, context.Config.Compiler.DebugMode, descriptor.ContainingTransientModuleId, kind, descriptor.ContainingSourcePath); this.module = module_builder; this.evalKind = kind; diff --git a/Source/Core/ScriptContext.CLR.cs b/Source/Core/ScriptContext.CLR.cs index 19c72201..4b8d82dc 100644 --- a/Source/Core/ScriptContext.CLR.cs +++ b/Source/Core/ScriptContext.CLR.cs @@ -266,7 +266,7 @@ public static void RunApplication(Delegate/*!*/ mainRoutine, string relativeSour output = Stream.Null; context.OutputStream = output; - context.Output = new StreamWriter(output); + context.Output = new StreamWriter(output, Configuration.Application.Globalization.PageEncoding); return context; } @@ -338,12 +338,11 @@ public static ScriptContext CurrentContext try { return ((ScriptContext)CallContext.GetData(callContextSlotName)) ?? CreateDefaultScriptContext(); // on Mono, .GetData must be used (GetLogicalData is not implemented) - } + } catch (InvalidCastException) { throw new InvalidCallContextDataException(callContextSlotName); } - //return result.AttachToHttpApplication(); } set @@ -352,10 +351,28 @@ public static ScriptContext CurrentContext CallContext.FreeNamedDataSlot(callContextSlotName); else CallContext.SetData(callContextSlotName, value); // on Mono, .SetData must be used (SetLogicalData is not implemented) - } + } } /// + + public static ScriptContext CurrentContextOrNull + { + [Emitted] + get + { + try + { + return (ScriptContext)CallContext.GetData(callContextSlotName); // on Mono, .GetData must be used (GetLogicalData is not implemented) + } + catch (InvalidCastException) + { + throw new InvalidCallContextDataException(callContextSlotName); + }; + } + } + + /// /// Initialize new ScriptContext and store it into the LogicalCallContext. /// /// Newly created ScriptContext. diff --git a/Source/Core/ScriptContext.cs b/Source/Core/ScriptContext.cs index a5c1a1d0..b57479ed 100644 --- a/Source/Core/ScriptContext.cs +++ b/Source/Core/ScriptContext.cs @@ -12,6 +12,7 @@ using System; using System.IO; +using System.Runtime.ExceptionServices; using System.Text; using System.Threading; using System.Reflection; @@ -462,6 +463,34 @@ public string[] SplAutoloadExtensions #region Construction + static ScriptContext() + { + AppDomain.CurrentDomain.FirstChanceException+=CurrentDomainOnFirstChanceException; + } + + private static void CurrentDomainOnFirstChanceException(object sender, FirstChanceExceptionEventArgs args) + { + if (args.Exception is ThreadAbortException) + { + try + { + var context = CurrentContextOrNull; + if (context == null || !context.ExecutionTimedOut) + return; + bool old_throw = context.ThrowExceptionOnError; + context.ThrowExceptionOnError = false; + + PhpException.Throw( + PhpError.Error, + CoreResources.GetString("execution_timed_out", context.config.RequestControl.ExecutionTimeout) + new StackTrace()); + + context.ThrowExceptionOnError = old_throw; + } + catch + { } + } + } + /// /// Creates an instance of initialized with dummy streams and /// a copy of the default local configuration. @@ -2160,7 +2189,9 @@ public void ApplyExecutionTimeout(int seconds) catch (ThreadAbortException) { if (!executionTimedOut) throw; - ThreadAbortedDueToTimeout(); +#if !SILVERLIGHT + Thread.ResetAbort(); +#endif } catch (PhpException) { @@ -2265,7 +2296,7 @@ private object GuardedMain(object/*!*/ mainRoutine) /// /// Flushes all remaining data from output buffers. /// - internal object FinalizeBufferedOutput(object _) + internal object FinalizeOutput(object _) { // flushes output, applies user defined output filter, and disables buffering: if (bufferedOutput != null) @@ -2274,33 +2305,17 @@ internal object FinalizeBufferedOutput(object _) // redirects sinks: IsOutputBuffered = false; - return null; - } - - /// - /// Called when the execution has been timed out. - /// - private void ThreadAbortedDueToTimeout() - { - Debug.Assert(executionTimedOut); - -#if !SILVERLIGHT - Thread.ResetAbort(); -#endif - - bool old_throw = ThrowExceptionOnError; - ThrowExceptionOnError = false; + // flush unbuffered output + output.Flush(); - PhpException.Throw(PhpError.Error, CoreResources.GetString("execution_timed_out", - config.RequestControl.ExecutionTimeout)); - - ThrowExceptionOnError = old_throw; + return null; } // GENERICS: Lambda private void TimedOut(object/*!*/ thread) { executionTimedOut = true; + ((Thread)thread).Abort(); } @@ -2913,7 +2928,7 @@ void IDisposable.Dispose() { this.GuardedCall(this.ProcessShutdownCallbacks, null, false); this.GuardedCall(this.FinalizePhpObjects, null, false); - this.GuardedCall(this.FinalizeBufferedOutput, null, false); + this.GuardedCall(this.FinalizeOutput, null, false); // additional disposal action if (this.TryDispose != null) diff --git a/Source/Core/Utils.cs b/Source/Core/Utils.cs index 0b9d2bc2..12903a93 100644 --- a/Source/Core/Utils.cs +++ b/Source/Core/Utils.cs @@ -3825,16 +3825,16 @@ public static class DateTimeUtils /// /// Time. /// Unix timestamp. - public static int UtcToUnixTimeStamp(DateTime dt) + public static long UtcToUnixTimeStamp(DateTime dt) { double seconds = (dt - UtcStartOfUnixEpoch).TotalSeconds; - if (seconds < Int32.MinValue) - return Int32.MinValue; - if (seconds > Int32.MaxValue) - return Int32.MaxValue; + if (seconds < Int64.MinValue) + return Int64.MinValue; + if (seconds > Int64.MaxValue) + return Int64.MaxValue; - return (int)seconds; + return (long)seconds; } /// @@ -3842,7 +3842,7 @@ public static int UtcToUnixTimeStamp(DateTime dt) /// /// UNIX timestamp /// structure representing UTC time. - public static DateTime UnixTimeStampToUtc(int timestamp) + public static DateTime UnixTimeStampToUtc(long timestamp) { return UtcStartOfUnixEpoch + TimeSpan.FromSeconds(timestamp); } diff --git a/Source/Extensions/Curl/CurlHttp.cs b/Source/Extensions/Curl/CurlHttp.cs index 31305171..5f653a0f 100644 --- a/Source/Extensions/Curl/CurlHttp.cs +++ b/Source/Extensions/Curl/CurlHttp.cs @@ -3,6 +3,7 @@ using System.IO; using System.Linq; using System.Net; +using System.Net.Security; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text; @@ -74,6 +75,11 @@ internal override object Execute(PhpCurlResource curl, ref CURLcode result) for (; ; ) { request = (HttpWebRequest)HttpWebRequest.Create(uri); + if (!string.IsNullOrWhiteSpace(uri.UserInfo)) + { + var arr = uri.UserInfo.Split(new[] {':'}, 2); + request.Credentials = new NetworkCredential(arr[0], arr[1]); + } Curl_HttpReq httpreq = (redirectAttempts == 0) || keepVerb ? setRequestMethod(data) : Curl_HttpReq.GET; setTimeOut(data); @@ -95,6 +101,18 @@ internal override object Execute(PhpCurlResource curl, ref CURLcode result) setCredentials(data); setCookies(data); +#if NET45 + request.ServerCertificateValidationCallback = (sender, certificate, chain, errors) => + { + if (data.Ssl.VerifyPeer && (errors & SslPolicyErrors.RemoteCertificateChainErrors) == SslPolicyErrors.RemoteCertificateChainErrors) + return false; + if (data.Ssl.VerifyHost > 0 && (errors & SslPolicyErrors.RemoteCertificateNameMismatch) == SslPolicyErrors.RemoteCertificateNameMismatch) + // VerifyHost==1 removed in curl + return false; + return true; + }; +#endif + //ssl.VerifyPeer && ssl.VerifyHost == 2 is supported by default .NET // other values are currently unsupported diff --git a/Source/Extensions/Curl/HttpFormDataUploader.cs b/Source/Extensions/Curl/HttpFormDataUploader.cs index 49166cd5..e25d95da 100644 --- a/Source/Extensions/Curl/HttpFormDataUploader.cs +++ b/Source/Extensions/Curl/HttpFormDataUploader.cs @@ -7,6 +7,7 @@ using System.IO; using System.Threading; using System.Security; +using Convert = PHP.Core.Convert; namespace PHP.Library.Curl { @@ -113,7 +114,7 @@ private void AddFormData(object data) { //currentData.Type = FormType.FORM_CONTENT; - PhpBytes bytes = PhpVariable.AsBytes(data); + PhpBytes bytes = Convert.ObjectToPhpBytes(data); currentData.Data = bytes.ReadonlyData; NextDataIsFooter(); diff --git a/Source/Extensions/Curl/UserDefined.cs b/Source/Extensions/Curl/UserDefined.cs index 98864fc2..5366d946 100644 --- a/Source/Extensions/Curl/UserDefined.cs +++ b/Source/Extensions/Curl/UserDefined.cs @@ -959,10 +959,10 @@ public CURLcode SetOption(CurlOption option, object value) /* * Enable peer SSL verifying. */ - +#if !NET45 if (((bool)value) == false) PhpException.ArgumentValueNotSupported("value", false); - +#endif ssl.VerifyPeer = (bool)value; break; @@ -972,9 +972,10 @@ public CURLcode SetOption(CurlOption option, object value) * Enable verification of the CN contained in the peer certificate */ +#if !NET45 if (((int)value) != 2) PhpException.ArgumentValueNotSupported("value", (int)value); - +#endif ssl.VerifyHost = (int)value; break; @@ -1099,6 +1100,10 @@ public CURLcode SetOption(CurlOption option, object value) httpreq = Curl_HttpReq.POST_FORM; opt_no_body = false; /* this is implied */ break; + case CurlOption.CURLOPT_NOSIGNAL: + case CurlOption.CURLOPT_SSLVERSION: + // this options can be safely ignored + break; #region UNSUPPORTED OPTIONS diff --git a/Source/Extensions/Gd2/PhpGd.cs b/Source/Extensions/Gd2/PhpGd.cs index 97af6a8d..62f72750 100644 --- a/Source/Extensions/Gd2/PhpGd.cs +++ b/Source/Extensions/Gd2/PhpGd.cs @@ -714,12 +714,24 @@ public static bool imagecolormatch(PhpResource im1, PhpResource im2) /// /// Get the index of the specified color or its closest possible alternative /// - [ImplementsFunction("imagecolorresolve", FunctionImplOptions.NotSupported)] + [ImplementsFunction("imagecolorresolve")] public static int imagecolorresolve(PhpResource im, int red, int green, int blue) { - //TODO: (Maros) Used in non-truecolor images (palette images). - //PhpException.FunctionNotSupported(PhpError.Warning); - return -1; + var im1 = (PhpGdImageResource) im; + var minValue = int.MaxValue; + var color = Color.FromArgb(red, green, blue); + int minIndex = -1; + for (int i = 0; i < im1.Image.Palette.Entries.Length; i++) + { + var curColor = im1.Image.Palette.Entries[i]; + var value = Math.Abs(curColor.A - color.A) + Math.Abs(curColor.B - color.B) + Math.Abs(curColor.G - color.G) + Math.Abs(curColor.R - color.R); + if (value < minValue) + { + value = minValue; + minIndex = i; + } + } + return minIndex; } #endregion @@ -758,11 +770,26 @@ public static void imagecolorset(PhpResource im, int col, int red, int green, in /// /// Get the colors for an index /// - [ImplementsFunction("imagecolorsforindex", FunctionImplOptions.NotSupported)] + [ImplementsFunction("imagecolorsforindex")] public static PhpArray imagecolorsforindex(PhpResource im, int col) { - //PhpException.FunctionNotSupported(PhpError.Warning); - return null; + var im1 = (PhpGdImageResource) im; + var arr = new PhpArray(); + var entries = im1.Image.Palette.Entries; + Color color; + if (entries.Length > 0) + { + color = entries[col]; + } + else + { + color = Color.FromArgb(col); + } + arr["red"] = (int)color.R; + arr["green"] = (int)color.G; + arr["blue"] = (int)color.B; + arr["alpha"] = (int)color.A; + return arr; } #endregion @@ -3308,8 +3335,16 @@ private static PhpResource CreateGdImageFrom(string filename, ImageFormat format Bitmap image = LoadBitmap(filename, format); if (image == null) return null; - - return new PhpGdImageResource(image); + + var result = new PhpGdImageResource(image); + var color = image.Palette.Entries.Where(a => a.A < 255).Take(1).ToArray(); + if (color.Length > 0) + { + result.transparentColor = color[0]; + result.IsTransparentColSet = true; + result.SaveAlpha = true; + } + return result; } /// diff --git a/Source/Extensions/Gd2/PhpImage.cs b/Source/Extensions/Gd2/PhpImage.cs index de10babd..af10fe2b 100644 --- a/Source/Extensions/Gd2/PhpImage.cs +++ b/Source/Extensions/Gd2/PhpImage.cs @@ -767,8 +767,19 @@ private static bool SkipVariable(Stream stream) return false; } length = length - 2; - - stream.Seek(length, SeekOrigin.Current); + if (stream.CanSeek) + stream.Seek(length, SeekOrigin.Current); + else + { + var buffer = new byte[Math.Min(4*1024, length)]; + do + { + var count = stream.Read(buffer, 0, Math.Min(length, buffer.Length)); + length -= count; + if (count == 0) + return false; + } while (length > 0); + } return true; } diff --git a/Source/Extensions/Soap/CodeConstants.cs b/Source/Extensions/Soap/CodeConstants.cs index 3d76dc4d..c4803fb9 100644 --- a/Source/Extensions/Soap/CodeConstants.cs +++ b/Source/Extensions/Soap/CodeConstants.cs @@ -14,8 +14,11 @@ internal struct CodeConstants internal const string END = "End"; internal const string CODENAMESPACE = "PHP.Library.Soap.DynamicProxy"; + internal const string CODENAMESPACESERVER = "PHP.Library.Soap.Server"; internal const string DEFAULTBASETYPE = "System.Web.Services.Protocols.SoapHttpClientProtocol"; + internal const string DEFAULTSERVERBASETYPE = "System.Web.Services.WebService"; internal const string CUSTOMBASETYPE = "PHP.Library.Soap.SoapHttpClientProtocolExtended"; + internal const string CUSTOMSERVERBASETYPE = "PHP.Library.Soap.WebServiceExtended"; internal const string LIBTEMPDIR = "DynamicProxyTempDir"; internal const string TEMPDLLEXTENSION = "_soapclient_tmp.dll"; diff --git a/Source/Extensions/Soap/DynamicWebServiceProxy.cs b/Source/Extensions/Soap/DynamicWebServiceProxy.cs index 1bbf6d39..9150b6db 100644 --- a/Source/Extensions/Soap/DynamicWebServiceProxy.cs +++ b/Source/Extensions/Soap/DynamicWebServiceProxy.cs @@ -2,16 +2,20 @@ using System.CodeDom; using System.CodeDom.Compiler; using System.Collections; +using System.Collections.Generic; using System.Data; using System.Globalization; using System.IO; +using System.Linq; using System.Net; using System.Reflection; -using System.Security.Cryptography.X509Certificates; +using System.Runtime.Serialization.Formatters.Binary; +using System.Security.Cryptography.X509Certificates; using System.Text; using System.Web; using System.Web.Services.Description; using System.Web.Services.Discovery; +using System.Web.Services.Protocols; using System.Xml; using System.Xml.Schema; using System.Xml.Serialization; @@ -27,7 +31,7 @@ internal class DynamicWebServiceProxy private Assembly ass; private object proxyInstance; private string wsdl; - private string protocolName = "Soap"; + private string protocolName = null; private string proxySource; private ServiceDescriptionImporter sdi; private XmlSchemas schemas; @@ -36,6 +40,7 @@ internal class DynamicWebServiceProxy private ArrayList outParams = new ArrayList(); private ServiceCache serviceCache; private readonly X509Certificate2 certificate; + private SoapVersion? version; /// /// Creates a new instance. @@ -44,12 +49,18 @@ internal class DynamicWebServiceProxy /// Enables access to SOAP messages /// Type of caching to be used /// Certificate to use. - internal DynamicWebServiceProxy(string wsdlLocation, bool enableMessageAccess = false, WsdlCache wsdlCache = WsdlCache.Both, X509Certificate2 certificate = null) + /// + internal DynamicWebServiceProxy(string wsdlLocation, bool enableMessageAccess = false, WsdlCache wsdlCache = WsdlCache.Both, X509Certificate2 certificate = null, SoapVersion? version = null) { this.wsdl = wsdlLocation; this.enableMessageAccess = enableMessageAccess; this.serviceCache = new ServiceCache(wsdlLocation, wsdlCache, new ServiceCache.CacheMissEvent(BuildAssemblyFromWsdl)); this.certificate = certificate; + this.version = version; + if (this.version == SoapVersion.SOAP_1_2) + protocolName = "Soap12"; + else if (this.version == SoapVersion.SOAP_1_1) + protocolName = "Soap"; BuildProxy(); } @@ -60,7 +71,7 @@ internal DynamicWebServiceProxy(string wsdlLocation, bool enableMessageAccess = public object InvokeCall(string methodName ,PhpArray parameters) { var soapProxy = (SoapHttpClientProtocolExtended)proxyInstance; - MethodInfo mi = soapProxy.GetType().GetMethod(methodName); + var mi = WsdlHelper.GetMethodBySoapName(methodName, soapProxy.GetType()); bool wrappedArgs = true; @@ -79,13 +90,14 @@ public object InvokeCall(string methodName ,PhpArray parameters) object[] transformedParameters = paramBinder.BindParams(mi, parameters, wrappedArgs); - object[] resArray = soapProxy.Invoke(methodName, transformedParameters); + object[] resArray = soapProxy.Invoke(mi.Name, transformedParameters); if (resArray[0] != null) { + var returnName = WsdlHelper.GetParameterSoapName(mi.ReturnParameter); resArray[0] = ResultBinder.BindResult( resArray[0], - mi.Name, + returnName, wrappedArgs); } @@ -101,6 +113,7 @@ public object InvokeCall(string methodName ,PhpArray parameters) return resArray[0]; } + #region Async invoke (not supported now) ///// @@ -213,7 +226,7 @@ public string Wsdl /// Gets or sets the name of the protocol. /// /// - public Protocol ProtocolName + public Protocol? ProtocolName { get { @@ -225,6 +238,10 @@ public Protocol ProtocolName return Protocol.HttpPost; case "Soap": return Protocol.HttpSoap; + case "Soap12": + return Protocol.HttpSoap12; + case null: + return null; default: return Protocol.HttpSoap; } @@ -242,6 +259,12 @@ public Protocol ProtocolName case Protocol.HttpSoap: protocolName = "Soap"; break; + case Protocol.HttpSoap12: + protocolName = "Soap12"; + break; + case null: + protocolName = null; + break; } } } @@ -279,7 +302,9 @@ private Assembly BuildAssemblyFromWsdl(string absoluteWsdlLocation, string wsdlC // WSDL service description importer CodeNamespace cns = new CodeNamespace(CodeConstants.CODENAMESPACE); + CodeNamespace cnsServer = new CodeNamespace(CodeConstants.CODENAMESPACESERVER);//TODO: may be split assembly or merge namespaces sdi = new ServiceDescriptionImporter(); + sdi.CodeGenerationOptions = CodeGenerationOptions.None; //sdi.AddServiceDescription(sd, null, null); // check for optional imports in the root WSDL @@ -287,10 +312,17 @@ private Assembly BuildAssemblyFromWsdl(string absoluteWsdlLocation, string wsdlC sdi.ProtocolName = protocolName; sdi.Import(cns, null); + sdi = new ServiceDescriptionImporter(); + CheckForImports(absoluteWsdlLocation); + sdi.CodeGenerationOptions = CodeGenerationOptions.None; + sdi.ProtocolName = protocolName; + sdi.Style = ServiceDescriptionImportStyle.Server; + sdi.Import(cnsServer, null); // change the base class // get all available Service classes - not only the default one ArrayList newCtr = new ArrayList(); + Dictionary bodies = new Dictionary(); foreach (CodeTypeDeclaration ctDecl in cns.Types) { @@ -307,9 +339,40 @@ private Assembly BuildAssemblyFromWsdl(string absoluteWsdlLocation, string wsdlC { cns.Types.Remove(ctDecl); ctDecl.BaseTypes[0] = new CodeTypeReference(CodeConstants.CUSTOMBASETYPE); + foreach (var member in ctDecl.Members.OfType()) + { + bodies[member.Name] = member.Statements; + } cns.Types.Add(ctDecl); } + newCtr.Clear(); + foreach (CodeTypeDeclaration ctDecl in cnsServer.Types) + { + if (ctDecl.BaseTypes.Count > 0) + { + if (ctDecl.BaseTypes[0].BaseType == CodeConstants.DEFAULTSERVERBASETYPE) + { + newCtr.Add(ctDecl); + } + } + } + foreach (CodeTypeDeclaration ctDecl in newCtr) + { + cnsServer.Types.Remove(ctDecl); + ctDecl.BaseTypes[0] = new CodeTypeReference(CodeConstants.CUSTOMSERVERBASETYPE); + ctDecl.TypeAttributes = ctDecl.TypeAttributes ^ TypeAttributes.Abstract; + foreach (var member in ctDecl.Members.OfType().Where(a=>(a.Attributes&MemberAttributes.ScopeMask)==MemberAttributes.Abstract).ToArray()) + { + ctDecl.Members.Remove(member); + member.Attributes = member.Attributes ^ MemberAttributes.Abstract; + member.Statements.AddRange(bodies[member.Name]); + ctDecl.Members.Add(member); + } + cnsServer.Types.Add(ctDecl); + } + + // source code generation CSharpCodeProvider cscp = new CSharpCodeProvider(); StringBuilder srcStringBuilder = new StringBuilder(); @@ -332,8 +395,10 @@ private Assembly BuildAssemblyFromWsdl(string absoluteWsdlLocation, string wsdlC } } } - - cscp.GenerateCodeFromNamespace(cns, sw, null); + var unit = new CodeCompileUnit(); + unit.Namespaces.Add(cns); + unit.Namespaces.Add(cnsServer); + cscp.GenerateCodeFromCompileUnit(unit, sw, null); proxySource = srcStringBuilder.ToString(); sw.Close(); @@ -402,7 +467,12 @@ private object CreateProxyInstance() /// private void ResetInternalState() { - protocolName = "Soap"; + if (version == null) + protocolName = null; + else if (version == SoapVersion.SOAP_1_2) + protocolName = "Soap12"; + else + protocolName = "Soap"; sdi = null; } @@ -463,19 +533,18 @@ private void CheckForImports(string baseWSDLUrl) /// Gets the SOAP request. /// /// - public string SoapRequest + public PhpBytes SoapRequest { get { if (enableMessageAccess && pipelineProperlyConfigured) { - PropertyInfo propInfo = proxyInstance.GetType().GetProperty("SoapRequestString"); - object result = propInfo.GetValue(proxyInstance, null); + byte[] result = ((SoapHttpClientProtocolExtended)proxyInstance).SoapRequest; - return (string)result; + return result == null ? PhpBytes.Empty : new PhpBytes(result); } else - return String.Empty; + return PhpBytes.Empty; } } @@ -483,19 +552,42 @@ public string SoapRequest /// Gets the SOAP response. /// /// - public string SoapResponse + public PhpBytes SoapResponse { get { if (enableMessageAccess && pipelineProperlyConfigured) { - PropertyInfo propInfo = proxyInstance.GetType().GetProperty("SoapResponseString"); - object result = propInfo.GetValue(proxyInstance, null); + byte[] result = ((SoapHttpClientProtocolExtended)proxyInstance).SoapResponse; - return (string)result; + return result == null ? PhpBytes.Empty : new PhpBytes(result); } else - return String.Empty; + return PhpBytes.Empty; + } + } + + public string RequestHeaders + { + get + { + if (enableMessageAccess && pipelineProperlyConfigured) + { + return ((SoapHttpClientProtocolExtended) proxyInstance).RequestHeaders; + } + return string.Empty; + } + } + + public string ResponseHeaders + { + get + { + if (enableMessageAccess && pipelineProperlyConfigured) + { + return ((SoapHttpClientProtocolExtended) proxyInstance).ResponseHeaders; + } + return string.Empty; } } @@ -619,5 +711,19 @@ public ArrayList OutParameters { get { return outParams; } } + + public string Location + { + get + { + var soapProxy = (SoapHttpClientProtocolExtended)proxyInstance; + return soapProxy.Url; + } + set + { + var soapProxy = (SoapHttpClientProtocolExtended)proxyInstance; + soapProxy.Url = value; + } + } } } diff --git a/Source/Extensions/Soap/Enums.cs b/Source/Extensions/Soap/Enums.cs index 1870c0c3..31f82f61 100644 --- a/Source/Extensions/Soap/Enums.cs +++ b/Source/Extensions/Soap/Enums.cs @@ -44,6 +44,15 @@ internal enum Protocol { HttpGet, HttpPost, - HttpSoap + HttpSoap, + HttpSoap12 + } + + public enum SoapVersion + { + [ImplementsConstant("SOAP_1_1")] + SOAP_1_1 = 1, + [ImplementsConstant("SOAP_1_2")] + SOAP_1_2 = 2 } } diff --git a/Source/Extensions/Soap/Extension.Soap.csproj b/Source/Extensions/Soap/Extension.Soap.csproj index 95ebcd64..7bea0cdb 100644 --- a/Source/Extensions/Soap/Extension.Soap.csproj +++ b/Source/Extensions/Soap/Extension.Soap.csproj @@ -68,6 +68,12 @@ + + + + + Component + True True diff --git a/Source/Extensions/Soap/ParameterBinder.cs b/Source/Extensions/Soap/ParameterBinder.cs index b25f706b..19f80c8c 100644 --- a/Source/Extensions/Soap/ParameterBinder.cs +++ b/Source/Extensions/Soap/ParameterBinder.cs @@ -6,6 +6,7 @@ using System.Collections; using System.Reflection; using PHP.Core.Reflection; +using Convert = PHP.Core.Convert; namespace PHP.Library.Soap { @@ -92,11 +93,15 @@ public object[] BindParams(MethodInfo mi, PhpArray parameters, bool wrappedArgs) { if (SetSpecifiedParameter(resultParams, pi)) continue; - - if (parameters.TryGetValue(pi.Name, out value)) + value = null; + if (parameters == null || parameters.TryGetValue(pi.Name, out value)) { resultParams.Add(Bind(value, pi.ParameterType)); } + else + { + resultParams.Add(pi.ParameterType.IsValueType ? Activator.CreateInstance(pi.ParameterType) : null); + } } } @@ -113,11 +118,17 @@ public object[] BindParams(MethodInfo mi, PhpArray parameters, bool wrappedArgs) private object Bind(object graph, Type targetType) { if (graph == null) - return null; + { + if(!targetType.IsValueType) + return null; + return Activator.CreateInstance(targetType); + } // unwrap Nullable<> if (targetType.IsGenericType && targetType.GetGenericTypeDefinition() == typeof(Nullable<>)) targetType = targetType.GetGenericArguments()[0]; + if (targetType.IsByRef) + targetType = targetType.GetElementType(); switch (Type.GetTypeCode(graph.GetType())) { @@ -218,6 +229,9 @@ private object BindPrimitiveType(object obj, Type targetType) if (targetType.IsValueType) lastPrimitive = true; + if (targetType == typeof (string)) + obj = Encoding.UTF8.GetString(Convert.ObjectToPhpBytes(obj).ReadonlyData); + return PHP.Core.ConvertToClr.ObjectToType(obj, targetType); } @@ -273,7 +287,10 @@ private object BindObject(DObject obj, Type targetType) private object BindArray(PhpArray array, Type targetType) { if (targetType.IsArray) + { + array = (PhpArray) (array.ContainsKey("item") ? array["item"] : array); // TODO: ??? return BindArrayToArray(array, targetType); + } else return BindArrayToObject(array, targetType); } @@ -340,10 +357,12 @@ private object BindArrayToArray(PhpArray array, Type targetType) { Debug.Assert(targetType.IsArray); + var count = array == null ? 0 : array.Count; + Type elementType = targetType.GetElementType(); - Array vals = Array.CreateInstance(elementType, array.Count); + Array vals = Array.CreateInstance(elementType, count); - for (int i = 0; i < array.Count; ++i) + for (int i = 0; i < count; ++i) { vals.SetValue(Bind(array[i], elementType), i); } @@ -355,5 +374,41 @@ private object BindArrayToArray(PhpArray array, Type targetType) #endregion + + public object[] BindServerResult(MethodInfo mi, PhpArray result, bool wrappedArgs) + { + var resultParams = new List(); + var parameterInfos = new List(); + parameterInfos.Add(mi.ReturnParameter); + parameterInfos.AddRange(mi.GetParameters().Where(a=>a.IsOut)); + object value; + + + if (!wrappedArgs) + { + //TODO: make sure: When arguments are not wrapped soap method parameter is only one + Debug.Assert(parameterInfos.Count == 1); + + resultParams.Add(Bind(result, parameterInfos[0].ParameterType)); + + } + else + { + foreach (var pi in parameterInfos) + { + if (SetSpecifiedParameter(resultParams, pi)) + continue; + var name = WsdlHelper.GetParameterSoapName(pi); + if (result.TryGetValue(name, out value)) + { + resultParams.Add(Bind(value, pi.ParameterType)); + } + } + } + + lastPrimitive = false; + + return resultParams.ToArray(); + } } } diff --git a/Source/Extensions/Soap/ResultBinder.cs b/Source/Extensions/Soap/ResultBinder.cs index 721d9541..35a2e4af 100644 --- a/Source/Extensions/Soap/ResultBinder.cs +++ b/Source/Extensions/Soap/ResultBinder.cs @@ -37,10 +37,27 @@ internal static object BindResult(object graph, string functionName, bool wrapRe object res = Bind(graph); if (wrapResult) - return WrapToStdClass(res, functionName + "Result"); + return WrapToStdClass(res, functionName); else return res; } + + internal static object BindServerParameters(object[] parameters, MethodInfo mi) + { + var runtimeFields = new PhpArray(); + //I can also just return CLR type and wrap it with PHP.Core.Reflection.ClrObject.WrapDynamic + int i = 0; + foreach (var p in mi.GetParameters().Where(a => !a.IsOut)) + { + var name = WsdlHelper.GetParameterSoapName(p); + var value = Bind(parameters[i++]); + runtimeFields[name] = value; + } + return new stdClass() + { + RuntimeFields = runtimeFields + }; + } private static object BindEnum(object obj, Type type) { @@ -75,9 +92,18 @@ private static object BindObject(object obj, Type type) if (specified) { - value = Bind(field.GetValue(obj), field); + var fieldValue = field.GetValue(obj); + value = Bind(fieldValue, field); if (value != null) - runtimeFields.Add(field.Name, value); + { + if (fieldValue != null && fieldValue.GetType().IsArray && ((Array) fieldValue).Length > 0) + fieldValue = ((Array) fieldValue).GetValue(0); + var attr = Attribute.GetCustomAttributes(field, typeof(XmlElementAttribute)).Cast().FirstOrDefault(a => a.Type == null || a.Type.IsInstanceOfType(fieldValue)); + var name = field.Name; + if (attr != null && !string.IsNullOrWhiteSpace(attr.ElementName)) + name = attr.ElementName; + runtimeFields.Add(name, value); + } } } @@ -92,29 +118,28 @@ private static string GetArrayItemTypeName(Type type, FieldInfo fi) if (fi == null) return "item"; - object[] attr = fi.GetCustomAttributes(false); + XmlArrayItemAttribute[] attr = (XmlArrayItemAttribute[])fi.GetCustomAttributes(typeof(XmlArrayItemAttribute), false); for (int i = 0; i < attr.Length; ++i) { - if (attr[i].GetType() == typeof(XmlArrayItemAttribute)) - { - XmlArrayItemAttribute arrayItemAttr = (XmlArrayItemAttribute)attr[i]; - if (!String.IsNullOrEmpty(arrayItemAttr.ElementName)) - return arrayItemAttr.ElementName; - else - return type.GetElementType().Name; - } - else if (attr[i].GetType() == typeof(XmlArrayAttribute)) - { + XmlArrayItemAttribute arrayItemAttr = (XmlArrayItemAttribute) attr[i]; + if (!String.IsNullOrEmpty(arrayItemAttr.ElementName)) + return arrayItemAttr.ElementName; + else return type.GetElementType().Name; - } } + XmlArrayAttribute attr1 = (XmlArrayAttribute)fi.GetCustomAttribute(typeof(XmlArrayAttribute), false); + if (attr1 != null) + return type.GetElementType().Name; return null; } private static object BindArray(object obj, Type type, FieldInfo targetFieldInfo) { + if (type == typeof(byte[])) + return new PhpBytes((byte[])obj); + Array array = (Array)obj; object res; string elementName; @@ -176,8 +201,8 @@ private static object Bind(object graph, FieldInfo targetFieldInfo = null) if (type.IsEnum) return BindEnum(graph, type); - - + if (type == typeof (string)) + graph = Encoding.Default.GetString(Encoding.UTF8.GetBytes((string) graph)); return PHP.Core.Convert.ClrLiteralToPhpLiteral(graph); case TypeCode.DateTime: @@ -187,7 +212,7 @@ private static object Bind(object graph, FieldInfo targetFieldInfo = null) if (dt.TimeOfDay == TimeSpan.Zero) return dt.ToString("yyyy-MM-dd"); - return dt.ToString("yyyy-MM-ddTHH:mm:ss.fffffffzzz"); + return dt.ToString("yyyy-MM-ddTHH:mm:ss.FFFFFFFzzz"); case TypeCode.Object: { diff --git a/Source/Extensions/Soap/SoapClient.cs b/Source/Extensions/Soap/SoapClient.cs index b8a8238e..29412812 100644 --- a/Source/Extensions/Soap/SoapClient.cs +++ b/Source/Extensions/Soap/SoapClient.cs @@ -20,6 +20,13 @@ public partial class SoapClient : PhpObject { private DynamicWebServiceProxy wsp = null; private bool exceptions = true; + private SoapVersion version = SoapVersion.SOAP_1_1; + + [PhpVisible, ImplementsMethod] + public object __soapCall(string function_name, PhpArray arguments) + { + return __call(function_name, arguments); + } /// /// Calls a SOAP function @@ -33,9 +40,9 @@ public object __call(string function_name, PhpArray arguments/*, PhpArray option { var item = arguments.GetArrayItem(0, true); - if (item != null && item.GetType() == typeof(PhpArray)) + if ((item != null && item.GetType() == typeof (PhpArray)) || item == null) { - PhpArray arr = (PhpArray)item; + PhpArray arr = (PhpArray) item; return wsp.InvokeCall(function_name, arr); } } @@ -75,7 +82,7 @@ public PhpArray __getFunctions() /// Returns last SOAP request /// [PhpVisible, ImplementsMethod] - public string __getLastRequest() + public PhpBytes __getLastRequest() { try { @@ -95,8 +102,15 @@ public string __getLastRequest() [PhpVisible, ImplementsMethod] public string __getLastRequestHeaders() { - PhpException.FunctionNotSupported(PhpError.Warning); - return null; + try + { + return wsp.RequestHeaders; + } + catch (Exception exception) + { + SoapFault.Throw(ScriptContext.CurrentContext, "SOAP-ERROR", exception.Message, exceptions); + return null; + } } @@ -104,7 +118,7 @@ public string __getLastRequestHeaders() /// Returns last SOAP response /// [PhpVisible, ImplementsMethod] - public object __getLastResponse() + public PhpBytes __getLastResponse() { try { @@ -124,8 +138,15 @@ public object __getLastResponse() [PhpVisible, ImplementsMethod] public string __getLastResponseHeaders() { - PhpException.FunctionNotSupported(PhpError.Warning); - return null; + try + { + return wsp.ResponseHeaders; + } + catch (Exception exception) + { + SoapFault.Throw(ScriptContext.CurrentContext, "SOAP-ERROR", exception.Message, exceptions); + return null; + } } @@ -157,8 +178,9 @@ public void __setCookie(string name, string value) [PhpVisible, ImplementsMethod] public string __setLocation(string new_location) { - PhpException.FunctionNotSupported(PhpError.Warning); - return null; + var result = wsp.Location; + wsp.Location = new_location; + return result; } @@ -217,6 +239,11 @@ public void __construct(string wsdl, [Optional] PhpArray options) exceptions = PHP.Core.Convert.ObjectToBoolean(value); } + if (options.TryGetValue("soap_version", out value)) + { + version = (SoapVersion) PHP.Core.Convert.ObjectToInteger(value); + } + // certificate: string pass = null; @@ -229,13 +256,13 @@ public void __construct(string wsdl, [Optional] PhpArray options) { var cert = Core.Convert.ObjectToString(value); if (cert != null) - certificate = new X509Certificate2(cert, pass); + certificate = new X509Certificate2(cert, pass, X509KeyStorageFlags.MachineKeySet); } } try { - wsp = new DynamicWebServiceProxy(wsdl, enableMessageAccess, wsdlCache, certificate); + wsp = new DynamicWebServiceProxy(wsdl, enableMessageAccess, wsdlCache, certificate, version); } catch (Exception exception) { diff --git a/Source/Extensions/Soap/SoapClient.dynamic.cs b/Source/Extensions/Soap/SoapClient.dynamic.cs index 95251671..62ea3e82 100644 --- a/Source/Extensions/Soap/SoapClient.dynamic.cs +++ b/Source/Extensions/Soap/SoapClient.dynamic.cs @@ -9,27 +9,57 @@ using System.Text; using PHP.Core; using System.Runtime.InteropServices; +using System.Security.Cryptography.X509Certificates; +using System.IO; namespace PHP.Library.Soap { [Serializable()] public partial class SoapClient { - /// protected SoapClient(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context) { } - /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public SoapClient(ScriptContext context, bool newInstance) : base(context, newInstance) { } - /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public SoapClient(ScriptContext context, DTypeDesc caller) : this(context, true) { this.InvokeConstructor(context, caller); } - /// + [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] + public object __soapCall(ScriptContext __context, object function_name, object arguments) + { + + string tmp1 = PhpVariable.AsString(function_name); + if (tmp1 == null) + + { + PhpException.InvalidImplicitCast(function_name, "string", "__soapCall"); + return null; + } + + PhpArray tmp2 = arguments as PhpArray; + if (tmp2 == null) + + { + PhpException.InvalidImplicitCast(arguments, "PhpArray", "__soapCall"); + return null; + } + return __soapCall(tmp1, tmp2); + } + [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] + public static object __soapCall(object instance, PhpStack stack) + { + stack.CalleeName = "__soapCall"; + + object arg1 = stack.PeekValue(1); + + object arg2 = stack.PeekValue(2); + stack.RemoveFrame(); + return ((SoapClient)instance).__soapCall(stack.Context, arg1, arg2); + } [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public object __call(ScriptContext __context, object function_name, object arguments) { @@ -51,7 +81,6 @@ public object __call(ScriptContext __context, object function_name, object argum } return __call(tmp1, tmp2); } - /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public static object __call(object instance, PhpStack stack) { @@ -63,13 +92,11 @@ public static object __call(object instance, PhpStack stack) stack.RemoveFrame(); return ((SoapClient)instance).__call(stack.Context, arg1, arg2); } - /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public object __doRequest(ScriptContext __context) { return __doRequest(); } - /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public static object __doRequest(object instance, PhpStack stack) { @@ -77,13 +104,11 @@ public static object __doRequest(object instance, PhpStack stack) stack.RemoveFrame(); return ((SoapClient)instance).__doRequest(stack.Context); } - /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public object __getFunctions(ScriptContext __context) { return __getFunctions(); } - /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public static object __getFunctions(object instance, PhpStack stack) { @@ -91,13 +116,11 @@ public static object __getFunctions(object instance, PhpStack stack) stack.RemoveFrame(); return ((SoapClient)instance).__getFunctions(stack.Context); } - /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public object __getLastRequest(ScriptContext __context) { return __getLastRequest(); } - /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public static object __getLastRequest(object instance, PhpStack stack) { @@ -105,13 +128,11 @@ public static object __getLastRequest(object instance, PhpStack stack) stack.RemoveFrame(); return ((SoapClient)instance).__getLastRequest(stack.Context); } - /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public object __getLastRequestHeaders(ScriptContext __context) { return __getLastRequestHeaders(); } - /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public static object __getLastRequestHeaders(object instance, PhpStack stack) { @@ -119,13 +140,11 @@ public static object __getLastRequestHeaders(object instance, PhpStack stack) stack.RemoveFrame(); return ((SoapClient)instance).__getLastRequestHeaders(stack.Context); } - /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public object __getLastResponse(ScriptContext __context) { return __getLastResponse(); } - /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public static object __getLastResponse(object instance, PhpStack stack) { @@ -133,13 +152,11 @@ public static object __getLastResponse(object instance, PhpStack stack) stack.RemoveFrame(); return ((SoapClient)instance).__getLastResponse(stack.Context); } - /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public object __getLastResponseHeaders(ScriptContext __context) { return __getLastResponseHeaders(); } - /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public static object __getLastResponseHeaders(object instance, PhpStack stack) { @@ -147,13 +164,11 @@ public static object __getLastResponseHeaders(object instance, PhpStack stack) stack.RemoveFrame(); return ((SoapClient)instance).__getLastResponseHeaders(stack.Context); } - /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public object __getTypes(ScriptContext __context) { return __getTypes(); } - /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public static object __getTypes(object instance, PhpStack stack) { @@ -161,7 +176,6 @@ public static object __getTypes(object instance, PhpStack stack) stack.RemoveFrame(); return ((SoapClient)instance).__getTypes(stack.Context); } - /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public object __setCookie(ScriptContext __context, object name, object value) { @@ -184,7 +198,6 @@ public object __setCookie(ScriptContext __context, object name, object value) __setCookie(tmp1, tmp2); return null; } - /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public static object __setCookie(object instance, PhpStack stack) { @@ -196,7 +209,6 @@ public static object __setCookie(object instance, PhpStack stack) stack.RemoveFrame(); return ((SoapClient)instance).__setCookie(stack.Context, arg1, arg2); } - /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public object __setLocation(ScriptContext __context, object new_location) { @@ -210,7 +222,6 @@ public object __setLocation(ScriptContext __context, object new_location) } return __setLocation(tmp1); } - /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public static object __setLocation(object instance, PhpStack stack) { @@ -220,7 +231,6 @@ public static object __setLocation(object instance, PhpStack stack) stack.RemoveFrame(); return ((SoapClient)instance).__setLocation(stack.Context, arg1); } - /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public object __setSoapHeaders(ScriptContext __context, object SoapHeaders) { @@ -235,7 +245,6 @@ public object __setSoapHeaders(ScriptContext __context, object SoapHeaders) __setSoapHeaders(tmp1); return null; } - /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public static object __setSoapHeaders(object instance, PhpStack stack) { @@ -245,7 +254,6 @@ public static object __setSoapHeaders(object instance, PhpStack stack) stack.RemoveFrame(); return ((SoapClient)instance).__setSoapHeaders(stack.Context, arg1); } - /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public object __construct(ScriptContext __context, object wsdl, [System.Runtime.InteropServices.OptionalAttribute()] object options) @@ -274,7 +282,6 @@ public static object __setSoapHeaders(object instance, PhpStack stack) __construct(tmp1, tmp2); return null; } - /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public static object __construct(object instance, PhpStack stack) { @@ -286,9 +293,9 @@ public static object __construct(object instance, PhpStack stack) stack.RemoveFrame(); return ((SoapClient)instance).__construct(stack.Context, arg1, arg2); } - /// private static void __PopulateTypeDesc(PhpTypeDesc desc) { + desc.AddMethod("__soapCall", PhpMemberAttributes.Public, new RoutineDelegate(SoapClient.__soapCall)); desc.AddMethod("__call", PhpMemberAttributes.Public, new RoutineDelegate(SoapClient.__call)); desc.AddMethod("__doRequest", PhpMemberAttributes.Public, new RoutineDelegate(SoapClient.__doRequest)); desc.AddMethod("__getFunctions", PhpMemberAttributes.Public, new RoutineDelegate(SoapClient.__getFunctions)); diff --git a/Source/Extensions/Soap/SoapHttpClientProtocolExtended.cs b/Source/Extensions/Soap/SoapHttpClientProtocolExtended.cs index cebac89f..c48fd111 100644 --- a/Source/Extensions/Soap/SoapHttpClientProtocolExtended.cs +++ b/Source/Extensions/Soap/SoapHttpClientProtocolExtended.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net; using System.Text; +using System.Web; using System.Web.Services.Protocols; namespace PHP.Library.Soap @@ -14,6 +16,8 @@ public class SoapHttpClientProtocolExtended : SoapHttpClientProtocol { private byte[] m_SoapRequestMsg; private byte[] m_SoapResponseMsg; + private WebRequest lastRequest; + private WebResponse lastResponse; /// /// Creates a new instance. @@ -110,5 +114,39 @@ public new object[] Invoke(string methodName, object[] parameters) { return base.Invoke(methodName, parameters); } + + protected override WebRequest GetWebRequest(Uri uri) + { + return lastRequest = base.GetWebRequest(uri); + } + + protected override WebResponse GetWebResponse(WebRequest request) + { + return lastResponse = base.GetWebResponse(request); + } + + public string RequestHeaders + { + get + { + var req = (HttpWebRequest) lastRequest; + var sb = new StringBuilder(); + sb.AppendFormat("{0} {1} HTTP/{2}{3}", req.Method, req.RequestUri.PathAndQuery, req.ProtocolVersion.ToString(2), Environment.NewLine); + sb.Append(req.Headers); + return sb.ToString(); + } + } + + public string ResponseHeaders + { + get + { + var res = ((HttpWebResponse) lastResponse); + var sb = new StringBuilder(); + sb.AppendFormat("HTTP/{0} {1} {2}{3}", res.ProtocolVersion.ToString(2), (int)res.StatusCode, res.StatusDescription, Environment.NewLine); + sb.Append(res.Headers); + return sb.ToString(); + } + } } } diff --git a/Source/Extensions/Soap/SoapMessageAccessClientExtension.cs b/Source/Extensions/Soap/SoapMessageAccessClientExtension.cs index 08c0bb8f..4adf68d7 100644 --- a/Source/Extensions/Soap/SoapMessageAccessClientExtension.cs +++ b/Source/Extensions/Soap/SoapMessageAccessClientExtension.cs @@ -198,7 +198,12 @@ protected virtual void Dispose(bool disposing) // Set large fields to null if (oldStream != null) { - oldStream.Close(); + try + { + oldStream.Close(); + } + catch(InvalidOperationException) + {} oldStream = null; } diff --git a/Source/Extensions/Soap/SoapServer.cs b/Source/Extensions/Soap/SoapServer.cs new file mode 100644 index 00000000..c15aa630 --- /dev/null +++ b/Source/Extensions/Soap/SoapServer.cs @@ -0,0 +1,54 @@ +using System; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Web; +using System.Web.Services.Protocols; +using PHP.Core; +using PHP.Core.Reflection; + +namespace PHP.Library.Soap +{ + [ImplementsType()] + public partial class SoapServer : PhpObject + { + private DynamicWebServiceProxy wsp; + private DTypeDesc type; + + public SoapServer(string wsdl) : this(wsdl, null) + { + } + + public SoapServer(string wsdl, PhpArray options) : base(ScriptContext.CurrentContext, true) + { + + } + + [PhpVisible, ImplementsMethod] + public void __construct(string wsdl, [Optional] PhpArray options) + { + wsp = new DynamicWebServiceProxy(wsdl); + } + + [PhpVisible, ImplementsMethod] + public void setClass(string class_name) + { + var context = ScriptContext.CurrentContext; + type = context.DeclaredTypes[class_name]; + } + + private static MethodInfo coreGetHandler = typeof(WebServiceHandlerFactory).GetMethod("CoreGetHandler", + BindingFlags.NonPublic | BindingFlags.Instance); + [PhpVisible, ImplementsMethod] + public void handle() + { + var clrType = wsp.ProxyAssembly.GetTypes().First(a => a.BaseType == typeof (WebServiceExtended)); + var factory = new WebServiceHandlerFactory(); + var context = HttpContext.Current; + context.Items["SoapServerType"] = type; + context.Items["SoapServerDynamicWebServiceProxy"] = wsp; + var handler = (IHttpHandler) coreGetHandler.Invoke(factory, new object[] {clrType, context, context.Request, context.Response}); + handler.ProcessRequest(context); + } + } +} \ No newline at end of file diff --git a/Source/Extensions/Soap/WebServiceExtended.cs b/Source/Extensions/Soap/WebServiceExtended.cs new file mode 100644 index 00000000..b435ba7e --- /dev/null +++ b/Source/Extensions/Soap/WebServiceExtended.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Web.Services; +using PHP.Core; +using PHP.Core.Reflection; +using Convert = PHP.Core.Convert; + +namespace PHP.Library.Soap +{ + public class WebServiceExtended : WebService + { + protected object[] Invoke(string name, params object[] parameters) + { + var type = (DTypeDesc) Context.Items["SoapServerType"]; + var method = type.GetMethod(new Name(name)); + var clrMethod = WsdlHelper.GetMethodBySoapName(name, GetType()); + var p = ResultBinder.BindServerParameters(parameters, clrMethod); + var context = ScriptContext.CurrentContext; + context.Stack.AddFrame(p); + var result = Convert.ObjectToPhpArray(method.Invoke(null, context.Stack)); + var binder = new ParameterBinder(); + var results = binder.BindServerResult(clrMethod, result, true); + return results; + } + } +} diff --git a/Source/Extensions/Soap/WsdlHelper.cs b/Source/Extensions/Soap/WsdlHelper.cs index dd03f427..b45b3e55 100644 --- a/Source/Extensions/Soap/WsdlHelper.cs +++ b/Source/Extensions/Soap/WsdlHelper.cs @@ -1,9 +1,12 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Text; using System.IO; using System.Net; +using System.Web.Services.Protocols; +using System.Xml.Serialization; using PHP.Core; namespace PHP.Library.Soap @@ -38,5 +41,31 @@ internal static string GetWsdlContent(string source, out string fullPath) return wsdlSourceValue; } + internal static MethodInfo GetMethodBySoapName(string methodName, Type type) + { + MethodInfo mi = type.GetMethod(methodName); + if (mi == null) + { + foreach (var methodInfo in type.GetMethods()) + { + var at = (SoapDocumentMethodAttribute)methodInfo.GetCustomAttributes(typeof(SoapDocumentMethodAttribute)).SingleOrDefault(); + if (at != null && at.RequestElementName == methodName) + { + mi = methodInfo; + break; + } + } + } + return mi; + } + + internal static string GetParameterSoapName(ParameterInfo parameter) + { + var name = parameter.Name ?? parameter.Member.Name + "Result"; + var at = (XmlElementAttribute)parameter.GetCustomAttribute(typeof(XmlElementAttribute)); + if (at != null && !string.IsNullOrWhiteSpace(at.ElementName)) + name = at.ElementName; + return name; + } } } diff --git a/Source/Extensions/Xml/PhpXmlParser.cs b/Source/Extensions/Xml/PhpXmlParser.cs index 738095f9..2ea01e0a 100644 --- a/Source/Extensions/Xml/PhpXmlParser.cs +++ b/Source/Extensions/Xml/PhpXmlParser.cs @@ -369,7 +369,7 @@ public static int Parse(NamingContext/*!*/ namingContext, DTypeDesc caller, PhpR /// with operators such as ===. /// [ImplementsFunction("xml_parse_into_struct", FunctionImplOptions.NeedsClassContext | FunctionImplOptions.NeedsNamingContext)] - public static int ParseIntoStruct(NamingContext/*!*/ namingContext, DTypeDesc caller, PhpResource parser, string data, PhpReference values) + public static int ParseIntoStruct(NamingContext/*!*/ namingContext, DTypeDesc caller, PhpResource parser, object data, PhpReference values) { return ParseIntoStruct(namingContext, caller, parser, data, values, null); } @@ -392,7 +392,7 @@ public static int ParseIntoStruct(NamingContext/*!*/ namingContext, DTypeDesc ca /// with operators such as ===. /// [ImplementsFunction("xml_parse_into_struct", FunctionImplOptions.NeedsClassContext | FunctionImplOptions.NeedsNamingContext)] - public static int ParseIntoStruct(NamingContext/*!*/ namingContext, DTypeDesc caller, PhpResource parser, string data, PhpReference values, PhpReference index) + public static int ParseIntoStruct(NamingContext/*!*/ namingContext, DTypeDesc caller, PhpResource parser, object data, PhpReference values, PhpReference index) { if (values == null) { diff --git a/Source/Extensions/Xml/XmlParserResource.cs b/Source/Extensions/Xml/XmlParserResource.cs index 67caf270..0f8a3109 100644 --- a/Source/Extensions/Xml/XmlParserResource.cs +++ b/Source/Extensions/Xml/XmlParserResource.cs @@ -6,6 +6,7 @@ using System.Xml; using System.IO; using PHP.Core.Reflection; +using Convert = PHP.Core.Convert; namespace PHP.Library.Xml { @@ -223,15 +224,25 @@ public bool Parse(DTypeDesc caller, NamingContext context, string input, bool is } } - public bool ParseIntoStruct(DTypeDesc caller, NamingContext context, string input, PhpArray values, PhpArray indices) + public bool ParseIntoStruct(DTypeDesc caller, NamingContext context, object input, PhpArray values, PhpArray indices) { return ParseInternal(caller, context, input, values, indices); } - private bool ParseInternal(DTypeDesc caller, NamingContext context, string xml, PhpArray values, PhpArray indices) + private bool ParseInternal(DTypeDesc caller, NamingContext context, object xml, PhpArray values, PhpArray indices) { - StringReader stringReader = new StringReader(xml); - XmlTextReader reader = new XmlTextReader(stringReader); + Stream stream; + PhpBytes phpBytes; + byte[] bytes; + if ((phpBytes = xml as PhpBytes) != null) + stream = new MemoryStream(phpBytes.ReadonlyData, false); + else if ((bytes = xml as byte[]) != null) + stream = new MemoryStream(bytes); + else + stream = new RecodingStream(Convert.ObjectToString(xml), Configuration.Application.Globalization.PageEncoding); + XmlTextReader reader = new XmlTextReader(stream); + reader.DtdProcessing = DtdProcessing.Ignore; + Stack elementStack = new Stack(); TextRecord textChunk = null; @@ -242,6 +253,7 @@ private bool ParseInternal(DTypeDesc caller, NamingContext context, string xml, _endNamespaceDeclHandler.BindOrBiteMyLegsOff(caller, context); _characterDataHandler.BindOrBiteMyLegsOff(caller, context); _processingInstructionHandler.BindOrBiteMyLegsOff(caller, context); + int depth = 1; while (reader.ReadState == ReadState.Initial || reader.ReadState == ReadState.Interactive) { @@ -282,7 +294,7 @@ private bool ParseInternal(DTypeDesc caller, NamingContext context, string xml, case ReadState.Interactive: //debug step, that prints out the current state of the parser (pretty printed) //Debug_ParseStep(reader); - ParseStep(reader, elementStack, ref textChunk, values, indices); + ParseStep(reader, elementStack, ref textChunk, values, indices, ref depth); break; } @@ -293,7 +305,7 @@ private bool ParseInternal(DTypeDesc caller, NamingContext context, string xml, return true; } - private void ParseStep(XmlReader reader, Stack elementStack, ref TextRecord textChunk, PhpArray values, PhpArray indices) + private void ParseStep(XmlTextReader reader, Stack elementStack, ref TextRecord textChunk, PhpArray values, PhpArray indices, ref int depth) { string elementName; bool emptyElement; @@ -302,7 +314,7 @@ private void ParseStep(XmlReader reader, Stack elementStack, ref switch (reader.NodeType) { case XmlNodeType.Element: - elementName = reader.Name; + elementName = _enableCaseFolding ? reader.Name.ToUpperInvariant() : reader.Name; emptyElement = reader.IsEmptyElement; PhpArray attributeArray = new PhpArray(); @@ -326,8 +338,7 @@ private void ParseStep(XmlReader reader, Stack elementStack, ref continue; } - - attributeArray.Add(_enableCaseFolding ? reader.Name.ToUpperInvariant() : reader.Name, reader.Value); + attributeArray.Add(_enableCaseFolding ? reader.Name.ToUpperInvariant() : reader.Name, new PhpBytes(reader.Encoding.GetBytes(reader.Value))); } while (reader.MoveToNextAttribute()); } @@ -337,7 +348,7 @@ private void ParseStep(XmlReader reader, Stack elementStack, ref { currentElementRecord = elementStack.Peek(); - UpdateValueAndIndexArrays(currentElementRecord, ref textChunk, values, indices, true); + UpdateValueAndIndexArrays(reader, currentElementRecord, ref textChunk, values, indices, true); if (currentElementRecord.State == ElementState.Beginning) currentElementRecord.State = ElementState.Interior; @@ -347,10 +358,12 @@ private void ParseStep(XmlReader reader, Stack elementStack, ref elementStack.Push( new ElementRecord() { ElementName = elementName, - Level = reader.Depth, + Level = depth, State = ElementState.Beginning, Attributes = (PhpArray)attributeArray.DeepCopy() }); + if (!reader.IsEmptyElement) + depth++; if (_startElementHandler.Callback != null) _startElementHandler.Invoke(this, _enableCaseFolding ? elementName.ToUpperInvariant() : elementName, attributeArray); @@ -374,12 +387,14 @@ private void ParseStep(XmlReader reader, Stack elementStack, ref // pop the top element record currentElementRecord = elementStack.Pop(); - UpdateValueAndIndexArrays(currentElementRecord, ref textChunk, values, indices, false); + UpdateValueAndIndexArrays(reader, currentElementRecord, ref textChunk, values, indices, false); if (_endElementHandler.Callback != null) _endElementHandler.Invoke(this, _enableCaseFolding ? elementName.ToUpperInvariant() : elementName); else if (_defaultHandler.Callback != null) _defaultHandler.Invoke(this, ""); + if (!reader.IsEmptyElement) + depth--; break; @@ -411,51 +426,55 @@ private void ParseStep(XmlReader reader, Stack elementStack, ref } } - private void UpdateValueAndIndexArrays(ElementRecord elementRecord, ref TextRecord textRecord, PhpArray values, PhpArray indices, bool middle) + private void UpdateValueAndIndexArrays(XmlTextReader reader, ElementRecord elementRecord, ref TextRecord textRecord, PhpArray values, PhpArray indices, bool middle) { - // if we have no valid data in the middle, just end - if (middle && textRecord == null) - return; - - if (!middle && elementRecord.State == ElementState.Interior) - UpdateValueAndIndexArrays(elementRecord, ref textRecord, values, indices, true); - if (values != null) { PhpArray arrayRecord = new PhpArray(); arrayRecord.Add("tag", elementRecord.ElementName); - arrayRecord.Add("level", elementRecord.Level); if (elementRecord.State == ElementState.Beginning) arrayRecord.Add("type", middle ? "open" : "complete"); else arrayRecord.Add("type", middle ? "cdata" : "close"); + arrayRecord.Add("level", elementRecord.Level); - if (textRecord != null) - arrayRecord.Add("value", textRecord.Text); + if (textRecord != null && !string.IsNullOrWhiteSpace(textRecord.Text)) + arrayRecord.Add("value", new PhpBytes(reader.Encoding.GetBytes(textRecord.Text))); - if (elementRecord.State == ElementState.Beginning && elementRecord.Attributes.Count != 0) - arrayRecord.Add("attributes", elementRecord.Attributes); + if ((string)arrayRecord["type"] != "cdata" || arrayRecord.ContainsKey("value")) + { - values.Add(arrayRecord); + if (elementRecord.State == ElementState.Beginning && elementRecord.Attributes.Count != 0) + arrayRecord.Add("attributes", elementRecord.Attributes); - if (indices != null) - { - PhpArray elementIndices; + values.Add(arrayRecord); - if (!indices.ContainsKey(elementRecord.ElementName)) + if (indices != null) { - elementIndices = new PhpArray(); - indices.Add(elementRecord.ElementName, elementIndices); - } - else - elementIndices = (PhpArray)indices[elementRecord.ElementName]; + PhpArray elementIndices; - // add the max index (last inserted value) - elementIndices.Add(values.MaxIntegerKey); + if (!indices.ContainsKey(elementRecord.ElementName)) + { + elementIndices = new PhpArray(); + indices.Add(elementRecord.ElementName, elementIndices); + } + else + elementIndices = (PhpArray) indices[elementRecord.ElementName]; + + // add the max index (last inserted value) + elementIndices.Add(values.MaxIntegerKey); + } } } + // if we have no valid data in the middle, just end + if (middle && textRecord == null) + return; + + if (!middle && elementRecord.State == ElementState.Interior) + UpdateValueAndIndexArrays(reader, elementRecord, ref textRecord, values, indices, true); + textRecord = null; } @@ -477,5 +496,76 @@ public XmlParserResource(Encoding outputEncoding, bool processNamespaces, string _enableCaseFolding = true; _enableSkipWhitespace = false; } + + private class RecodingStream : Stream + { + private char[] data; + private Encoder encoder; + private int charsPosition; + + public RecodingStream(string data, Encoding encoding) + { + this.data = data.ToCharArray(); + encoder = encoding.GetEncoder(); + } + + public override void Flush() + { + encoder.Reset(); + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (charsPosition >= data.Length) + return 0; + int charsUsed; + int bytesUsed; + bool completed; + encoder.Convert(data, charsPosition, data.Length - charsPosition, buffer, offset, count, false, out charsUsed, out bytesUsed, out completed); + charsPosition += charsUsed; + return bytesUsed; + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } + + public override bool CanRead + { + get { return true; } + } + + public override bool CanSeek + { + get { return false; } + } + + public override bool CanWrite + { + get { return false; } + } + + public override long Length + { + get { throw new NotSupportedException(); } + } + + public override long Position + { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + } + } } } diff --git a/Source/Extensions/Zlib/PhpZlib.cs b/Source/Extensions/Zlib/PhpZlib.cs index 227713f0..301c1290 100644 --- a/Source/Extensions/Zlib/PhpZlib.cs +++ b/Source/Extensions/Zlib/PhpZlib.cs @@ -155,6 +155,7 @@ public static PhpBytes GzCompress(PhpBytes data, int level) PhpException.Throw(PhpError.Warning, String.Format("compression level ({0}) must be within -1..9", level)); return null; } + if (data == null) data = PhpBytes.Empty; int length_bound = data.Length + (data.Length / PHP_ZLIB_MODIFIER) + 15 + 1; diff --git a/Source/Extensions/iconv/PhpNetIconv.cs b/Source/Extensions/iconv/PhpNetIconv.cs index d649df52..549cf532 100644 --- a/Source/Extensions/iconv/PhpNetIconv.cs +++ b/Source/Extensions/iconv/PhpNetIconv.cs @@ -364,6 +364,7 @@ public static object iconv_substr(object/*string*/str, int offset, int length /* [return: CastToFalse] public static PhpBytes iconv(string in_charset, string out_charset, object str) { + PhpBytes result = null; // check args if (str == null) { @@ -400,46 +401,48 @@ public static PhpBytes iconv(string in_charset, string out_charset, object str) else out_encoding.EncoderFallback = new StopEncoderFallback(out_result); // throw notice and discard all remaining characters - try + // + var in_encoding = ResolveEncoding(in_charset); + if (in_encoding == null) + { + PhpException.Throw(PhpError.Notice, string.Format(Strings.wrong_charset, in_charset, in_charset, out_charset)); + result = null; + } + else if (str.GetType() == typeof(PhpBytes)) { - // - if (str.GetType() == typeof(PhpBytes)) + // resolve in_charset + if (in_charset == null) + { + PhpException.ArgumentNull("in_charset"); + result = null; + } + else { - // resolve in_charset - if (in_charset == null) - { - PhpException.ArgumentNull("in_charset"); - return null; - } - var in_encoding = ResolveEncoding(in_charset); - if (in_encoding == null) - { - PhpException.Throw(PhpError.Notice, string.Format(Strings.wrong_charset, in_charset, in_charset, out_charset)); - return null; - } - // TODO: in_encoding.Clone() ensures it is NOT readOnly, then set DecoderFallback to catch invalid byte sequences // convert to - return new PhpBytes(out_encoding.GetBytes(in_encoding.GetString(((PhpBytes)str).ReadonlyData))); - } - - if (str.GetType() == typeof(string) || (str = Core.Convert.ObjectToString(str)) != null) - { - // convert UTF16 to - return new PhpBytes(out_encoding.GetBytes((string)str)); + result = new PhpBytes(out_encoding.GetBytes(in_encoding.GetString(((PhpBytes) str).ReadonlyData))); } } - finally + else if (str.GetType() == typeof(string) || (str = Core.Convert.ObjectToString(str)) != null) { - if (out_result.firstFallbackCharIndex >= 0) + // convert UTF16 to + var pageEncoding = Configuration.Application.Globalization.PageEncoding; + if (in_encoding.Equals(pageEncoding)) + result = new PhpBytes(out_encoding.GetBytes((string) str)); + else + result = new PhpBytes(Encoding.Convert(in_encoding, out_encoding, pageEncoding.GetBytes((string)str))); + } + if (out_result.firstFallbackCharIndex >= 0) + { + // Notice: iconv(): Detected an illegal character in input string + PHP.Core.PhpException.Throw(Core.PhpError.Notice, Strings.illegal_character); + if (!transliterate && !discard_ilseq) { - // Notice: iconv(): Detected an illegal character in input string - PHP.Core.PhpException.Throw(Core.PhpError.Notice, Strings.illegal_character); + result = null; } } - - return null; + return result; } //ob_iconv_handler — Convert character encoding as output buffer handler diff --git a/Source/Extensions/mbstring/MbString.cs b/Source/Extensions/mbstring/MbString.cs index edb9e014..e64411ea 100644 --- a/Source/Extensions/mbstring/MbString.cs +++ b/Source/Extensions/mbstring/MbString.cs @@ -173,7 +173,11 @@ public enum CaseConstants // .NET encodings foreach (var encoding in Encoding.GetEncodings()) - enc[encoding.Name] = encoding.GetEncoding(); + { + var e = encoding.GetEncoding(); + enc[encoding.Name] = e; + enc["cp" + encoding.CodePage] = e; + } _encodings = enc; }