@@ -21,50 +21,49 @@ internal class PhpSerializer {
2121
2222 public PhpSerializer ( PhpSerializiationOptions options = null ) {
2323 this . _options = options ?? PhpSerializiationOptions . DefaultOptions ;
24-
2524 this . _seenObjects = new ( ) ;
2625 }
2726
2827 public string Serialize ( object input ) {
2928 switch ( input ) {
30- case Enum enumValue : {
31- if ( this . _options . NumericEnums ) {
32- return $ "i:{ enumValue . GetNumericString ( ) } ;";
33- } else {
34- return this . Serialize ( enumValue . ToString ( ) ) ;
35- }
36- }
37- case long longValue : {
38- return $ "i:{ longValue } ;";
39- }
40- case int integerValue : {
41- return $ "i:{ integerValue } ;";
42- }
43- case double floatValue : {
44- if ( double . IsPositiveInfinity ( floatValue ) ) {
45- return $ "d:INF;";
46- }
47- if ( double . IsNegativeInfinity ( floatValue ) ) {
48- return $ "d:-INF;";
49- }
50- if ( double . IsNaN ( floatValue ) ) {
51- return $ "d:NAN;";
52- }
53- return $ "d:{ floatValue . ToString ( CultureInfo . InvariantCulture ) } ;";
54- }
55- case string stringValue : {
56- // Use the UTF8 byte count, because that's what the PHP implementation does:
57- return $ "s:{ ASCIIEncoding . UTF8 . GetByteCount ( stringValue ) } :\" { stringValue } \" ;";
29+ case Enum enumValue :
30+ if ( this . _options . NumericEnums ) {
31+ return $ "i:{ enumValue . GetNumericString ( ) } ;";
32+ } else {
33+ return this . Serialize ( enumValue . ToString ( ) ) ;
5834 }
59- case bool boolValue : {
60- return boolValue ? "b:1;" : "b:0;" ;
35+
36+ case long longValue :
37+ return string . Concat ( "i:" , longValue . ToString ( CultureInfo . InvariantCulture ) , ";" ) ;
38+
39+ case int integerValue :
40+ return string . Concat ( "i:" , integerValue . ToString ( CultureInfo . InvariantCulture ) , ";" ) ;
41+
42+ case double floatValue :
43+ if ( double . IsPositiveInfinity ( floatValue ) ) {
44+ return $ "d:INF;";
6145 }
62- case null : {
63- return "N ;";
46+ if ( double . IsNegativeInfinity ( floatValue ) ) {
47+ return $ "d:-INF ;";
6448 }
65- default : {
66- return this . SerializeComplex ( input ) ;
49+ if ( double . IsNaN ( floatValue ) ) {
50+ return $ "d:NAN;" ;
6751 }
52+ return string . Concat ( "d:" , floatValue . ToString ( CultureInfo . InvariantCulture ) , ";" ) ;
53+
54+ case string stringValue :
55+ // Use the UTF8 byte count, because that's what the PHP implementation does:
56+ string length = Encoding . UTF8 . GetByteCount ( stringValue ) . ToString ( CultureInfo . InvariantCulture ) ;
57+ return string . Concat ( "s:" , length , ":\" " , stringValue , "\" ;" ) ;
58+
59+ case bool boolValue :
60+ return boolValue ? "b:1;" : "b:0;" ;
61+
62+ case null :
63+ return "N;" ;
64+
65+ default :
66+ return this . SerializeComplex ( input ) ;
6867 }
6968 }
7069
@@ -77,59 +76,42 @@ private string SerializeComplex(object input) {
7776 }
7877 this . _seenObjects . Add ( input ) ;
7978
80- StringBuilder output = new StringBuilder ( ) ;
8179 switch ( input ) {
8280 case PhpDynamicObject dynamicObject : {
83- var inputType = input . GetType ( ) ;
8481 var className = dynamicObject . GetClassName ( ) ?? "stdClass" ;
85- IEnumerable < string > memberNames = dynamicObject . GetDynamicMemberNames ( ) ;
86-
87- output . Append ( "O:" )
88- . Append ( className . Length )
89- . Append ( ":\" " )
90- . Append ( className )
91- . Append ( "\" :" )
92- . Append ( memberNames . Count ( ) )
93- . Append ( ":{" ) ;
94-
95- foreach ( string memberName in memberNames ) {
96- output . Append ( this . Serialize ( memberName ) )
97- . Append ( this . Serialize ( dynamicObject . GetMember ( memberName ) ) ) ;
82+ ICollection < string > memberNames = dynamicObject . GetDynamicMemberNames ( ) ;
83+ string preamble = $ "O:{ className . Length } :\" { className } \" :{ memberNames . Count } :{{";
84+ string [ ] entryStrings = new string [ memberNames . Count * 2 ] ;
85+ int entryIndex = 0 ;
86+ foreach ( var memberName in memberNames ) {
87+ entryStrings [ entryIndex ] = this . Serialize ( memberName ) ;
88+ entryStrings [ entryIndex + 1 ] = this . Serialize ( dynamicObject . GetMember ( memberName ) ) ;
89+ entryIndex += 2 ;
9890 }
99- output . Append ( '}' ) ;
100- return output . ToString ( ) ;
91+ return string . Concat ( preamble , string . Concat ( entryStrings ) , "}" ) ;
10192 }
10293 case ExpandoObject expando : {
10394 var dictionary = ( IDictionary < string , object > ) expando ;
104- var inputType = input . GetType ( ) ;
105- output . Append ( "O:" )
106- . Append ( "stdClass" . Length )
107- . Append ( ":\" " )
108- . Append ( "stdClass" )
109- . Append ( "\" :" )
110- . Append ( dictionary . Keys . Count )
111- . Append ( ":{" ) ;
112-
113- foreach ( var keyValue in dictionary ) {
114- output . Append ( this . Serialize ( keyValue . Key ) )
115- . Append ( this . Serialize ( keyValue . Value ) ) ;
95+ string preamble = $ "O:8:\" stdClass\" :{ dictionary . Keys . Count } :{{";
96+
97+ string [ ] entryStrings = new string [ dictionary . Count * 2 ] ;
98+ int entryIndex = 0 ;
99+ foreach ( var entry in dictionary ) {
100+ entryStrings [ entryIndex ] = this . Serialize ( entry . Key ) ;
101+ entryStrings [ entryIndex + 1 ] = this . Serialize ( entry . Value ) ;
102+ entryIndex += 2 ;
116103 }
117- output . Append ( '}' ) ;
118- return output . ToString ( ) ;
104+ return string . Concat ( preamble , string . Concat ( entryStrings ) , "}" ) ;
119105 }
120106 case IDynamicMetaObjectProvider :
121107 throw new NotSupportedException (
122108 "Serialization support for dynamic objects is limited to PhpSerializerNET.PhpDynamicObject and System.Dynamic.ExpandoObject in this version."
123109 ) ;
124110 case IDictionary dictionary : {
111+ string preamble ;
125112 if ( input is IPhpObject phpObject ) {
126- output . Append ( "O:" ) ;
127- output . Append ( phpObject . GetClassName ( ) . Length ) ;
128- output . Append ( ":\" " ) ;
129- output . Append ( phpObject . GetClassName ( ) ) ;
130- output . Append ( "\" :" ) ;
131- output . Append ( dictionary . Count ) ;
132- output . Append ( ":{" ) ;
113+ string className = phpObject . GetClassName ( ) ;
114+ preamble = $ "O:{ className . Length } :\" { className } \" :{ dictionary . Count } :{{";
133115 } else {
134116 var dictionaryType = dictionary . GetType ( ) ;
135117 if ( dictionaryType . GenericTypeArguments . Length > 0 ) {
@@ -138,28 +120,31 @@ private string SerializeComplex(object input) {
138120 throw new Exception ( $ "Can not serialize into associative array with key type { keyType . FullName } ") ;
139121 }
140122 }
141-
142- output . Append ( $ "a:{ dictionary . Count } :") ;
143- output . Append ( '{' ) ;
123+ preamble = $ "a:{ dictionary . Count } :{{";
144124 }
145125
126+ string [ ] entryStrings = new string [ dictionary . Count * 2 ] ;
127+ int entryIndex = 0 ;
146128 foreach ( DictionaryEntry entry in dictionary ) {
147- output . Append ( $ "{ this . Serialize ( entry . Key ) } { this . Serialize ( entry . Value ) } ") ;
129+ entryStrings [ entryIndex ] = this . Serialize ( entry . Key ) ;
130+ entryStrings [ entryIndex + 1 ] = this . Serialize ( entry . Value ) ;
131+ entryIndex += 2 ;
148132 }
149- output . Append ( '}' ) ;
150- return output . ToString ( ) ;
133+ return string . Concat ( preamble , string . Concat ( entryStrings ) , "}" ) ;
151134 }
152- case IList collection : {
153- output . Append ( $ "a:{ collection . Count } :") ;
154- output . Append ( '{' ) ;
155- for ( int i = 0 ; i < collection . Count ; i ++ ) {
156- output . Append ( this . Serialize ( i ) ) ;
157- output . Append ( this . Serialize ( collection [ i ] ) ) ;
158- }
159- output . Append ( '}' ) ;
160- return output . ToString ( ) ;
135+ case IList collection :
136+ string [ ] itemStrings = new string [ collection . Count * 2 ] ;
137+ for ( int i = 0 ; i < itemStrings . Length ; i += 2 ) {
138+ itemStrings [ i ] = string . Concat ( "i:" , ( i / 2 ) . ToString ( CultureInfo . InvariantCulture ) , ";" ) ;
139+ itemStrings [ i + 1 ] = this . Serialize ( collection [ i / 2 ] ) ;
161140 }
141+ return string . Concat (
142+ $ "a:{ collection . Count } :{{",
143+ string . Concat ( itemStrings ) ,
144+ "}"
145+ ) ;
162146 default : {
147+ StringBuilder output = new StringBuilder ( ) ;
163148 var inputType = input . GetType ( ) ;
164149
165150 if ( typeof ( IPhpObject ) . IsAssignableFrom ( inputType ) || inputType . GetCustomAttribute < PhpClass > ( ) != null ) {
@@ -190,7 +175,6 @@ private string SerializeComplex(object input) {
190175 }
191176 }
192177 }
193-
194178 output . Append ( $ "a:{ members . Count } :") ;
195179 output . Append ( '{' ) ;
196180 foreach ( var member in members ) {
0 commit comments