@@ -26,17 +26,31 @@ internal PhpDeserializer(Span<PhpToken> tokens, ReadOnlySpan<byte> input, PhpDes
2626 }
2727
2828 internal object Deserialize ( ) {
29- return this . DeserializeToken ( ) ;
29+ return this . Next ( ) ;
3030 }
3131
3232 internal object Deserialize ( Type targetType ) {
33- return this . DeserializeToken ( targetType ) ;
33+ return this . Next ( targetType ) ;
3434 }
3535
36- private object DeserializeToken ( ) {
37- var token = this . _tokens [ this . _currentToken ] ;
38- this . _currentToken ++ ;
36+ private object DeserializeToken ( in PhpToken token ) {
3937 switch ( token . Type ) {
38+ case PhpDataType . Reference :
39+ for ( int i = 0 ; i < this . _tokens . Length ; i ++ ) {
40+ if ( this . _tokens [ i ] . Reference == token . Reference ) {
41+ // This is a hack because this whole class was never designed with references in mind.
42+ // Because of that we're always assuming that this._currentToken points to the token
43+ // that needs to be deserialized next when filling arrays and objects.
44+ // Going back in the token-list therefore does not work if we don't also reset the
45+ // currentToken position and restore it after.
46+ var tokenPosition = this . _currentToken ;
47+ this . _currentToken = i + 1 ;
48+ var reference = this . DeserializeToken ( this . _tokens [ i ] ) ;
49+ this . _currentToken = tokenPosition ;
50+ return reference ;
51+ }
52+ }
53+ throw new DeserializationException ( "Can not resolve reference" ) ;
4054 case PhpDataType . Boolean :
4155 return token . Value . GetBool ( this . _input ) ;
4256 case PhpDataType . Integer :
@@ -49,7 +63,6 @@ private object DeserializeToken() {
4963 return token . Value . GetBool ( this . _input ) ;
5064 }
5165 }
52-
5366 return this . GetString ( token ) ;
5467 case PhpDataType . Array :
5568 return this . MakeCollection ( token ) ;
@@ -60,11 +73,30 @@ private object DeserializeToken() {
6073 return null ;
6174 }
6275 }
63-
64- private object DeserializeToken ( Type targetType ) {
76+ private object Next ( ) {
6577 var token = this . _tokens [ this . _currentToken ] ;
6678 this . _currentToken ++ ;
79+ return this . DeserializeToken ( token ) ;
80+ }
81+
82+ private object DeserializeToken ( Type targetType , in PhpToken token ) {
6783 switch ( token . Type ) {
84+ case PhpDataType . Reference :
85+ for ( int i = 0 ; i < this . _tokens . Length ; i ++ ) {
86+ if ( this . _tokens [ i ] . Reference == token . Reference ) {
87+ // This is a hack because this whole class was never designed with references in mind.
88+ // Because of that we're always assuming that this._currentToken points to the token
89+ // that needs to be deserialized next when filling arrays and objects.
90+ // Going back in the token-list therefore does not work if we don't also reset the
91+ // currentToken position and restore it after.
92+ var tokenPosition = this . _currentToken ;
93+ this . _currentToken = i + 1 ;
94+ var reference = this . DeserializeToken ( targetType , this . _tokens [ i ] ) ;
95+ this . _currentToken = tokenPosition ;
96+ return reference ;
97+ }
98+ }
99+ throw new DeserializationException ( "Can not resolve reference" ) ;
68100 case PhpDataType . Boolean :
69101 return this . DeserializeBoolean ( targetType , token ) ;
70102 case PhpDataType . Integer :
@@ -110,15 +142,21 @@ private object DeserializeToken(Type targetType) {
110142 }
111143 }
112144
145+ private object Next ( Type targetType ) {
146+ var token = this . _tokens [ this . _currentToken ] ;
147+ this . _currentToken ++ ;
148+ return DeserializeToken ( targetType , token ) ;
149+ }
150+
113151 private object DeserializeInteger ( Type targetType , in PhpToken token ) {
114152 return Type . GetTypeCode ( targetType ) switch {
115- TypeCode . Int16 => short . Parse ( token . Value . GetSlice ( this . _input ) , CultureInfo . InvariantCulture ) ,
116- TypeCode . Int32 => int . Parse ( token . Value . GetSlice ( this . _input ) , CultureInfo . InvariantCulture ) ,
117- TypeCode . Int64 => long . Parse ( token . Value . GetSlice ( this . _input ) , CultureInfo . InvariantCulture ) ,
118- TypeCode . UInt16 => ushort . Parse ( token . Value . GetSlice ( this . _input ) , CultureInfo . InvariantCulture ) ,
119- TypeCode . UInt32 => uint . Parse ( token . Value . GetSlice ( this . _input ) , CultureInfo . InvariantCulture ) ,
120- TypeCode . UInt64 => ulong . Parse ( token . Value . GetSlice ( this . _input ) , CultureInfo . InvariantCulture ) ,
121- TypeCode . SByte => sbyte . Parse ( token . Value . GetSlice ( this . _input ) , CultureInfo . InvariantCulture ) ,
153+ TypeCode . Int16 => short . Parse ( token . Value . GetSlice ( in this . _input ) , CultureInfo . InvariantCulture ) ,
154+ TypeCode . Int32 => int . Parse ( token . Value . GetSlice ( in this . _input ) , CultureInfo . InvariantCulture ) ,
155+ TypeCode . Int64 => long . Parse ( token . Value . GetSlice ( in this . _input ) , CultureInfo . InvariantCulture ) ,
156+ TypeCode . UInt16 => ushort . Parse ( token . Value . GetSlice ( in this . _input ) , CultureInfo . InvariantCulture ) ,
157+ TypeCode . UInt32 => uint . Parse ( token . Value . GetSlice ( in this . _input ) , CultureInfo . InvariantCulture ) ,
158+ TypeCode . UInt64 => ulong . Parse ( token . Value . GetSlice ( in this . _input ) , CultureInfo . InvariantCulture ) ,
159+ TypeCode . SByte => sbyte . Parse ( token . Value . GetSlice ( in this . _input ) , CultureInfo . InvariantCulture ) ,
122160 _ => this . DeserializeTokenFromSimpleType ( targetType , token . Type , this . GetString ( token ) , token . Position ) ,
123161 } ;
124162 }
@@ -251,15 +289,15 @@ private object MakeClass(in PhpToken token) {
251289 var result = new PhpDynamicObject ( token . Length , typeName ) ;
252290 result . SetClassName ( typeName ) ;
253291 for ( int i = 0 ; i < token . Length ; i ++ ) {
254- result . TryAdd ( ( string ) this . DeserializeToken ( ) , this . DeserializeToken ( ) ) ;
292+ result . TryAdd ( ( string ) this . Next ( ) , this . Next ( ) ) ;
255293 }
256294
257295 return result ;
258296 } else if ( this . _options . StdClass == StdClassOption . Dictionary ) {
259297 var result = new PhpObjectDictionary ( token . Length , typeName ) ;
260298 result . SetClassName ( typeName ) ;
261299 for ( int i = 0 ; i < token . Length ; i ++ ) {
262- result . TryAdd ( ( string ) this . DeserializeToken ( ) , this . DeserializeToken ( ) ) ;
300+ result . TryAdd ( ( string ) this . Next ( ) , this . Next ( ) ) ;
263301 }
264302
265303 return result ;
@@ -272,7 +310,7 @@ private object MakeClass(in PhpToken token) {
272310 // go back one because we're basically re-entering the object-token from the top.
273311 // If we don't decrement the pointer, we'd start with the first child token instead of the object token.
274312 this . _currentToken -- ;
275- var constructedObject = this . DeserializeToken ( targetType ) ;
313+ var constructedObject = this . Next ( targetType ) ;
276314 if ( constructedObject is IPhpObject phpObject and not PhpDateTime ) {
277315 phpObject . SetClassName ( typeName ) ;
278316 }
@@ -303,7 +341,7 @@ private object MakeStruct(Type targetType, in PhpToken token) {
303341 if ( fields [ fieldName ] != null ) {
304342 var field = fields [ fieldName ] ;
305343 try {
306- field . SetValue ( result , this . DeserializeToken ( field . FieldType ) ) ;
344+ field . SetValue ( result , this . Next ( field . FieldType ) ) ;
307345 } catch ( Exception exception ) {
308346 var valueToken = this . _tokens [ this . _currentToken ] ;
309347 throw new DeserializationException (
@@ -353,7 +391,7 @@ private object MakeObject(Type targetType, in PhpToken token) {
353391 // null if PhpIgnore'd
354392 try {
355393 property . SetValue (
356- result , this . DeserializeToken ( property . PropertyType )
394+ result , this . Next ( property . PropertyType )
357395 ) ;
358396 } catch ( Exception exception ) {
359397 var valueToken = this . _tokens [ this . _currentToken - 1 ] ;
@@ -378,12 +416,12 @@ private object MakeArray(Type targetType, in PhpToken token) {
378416 if ( elementType == typeof ( object ) ) {
379417 for ( int i = 0 ; i < token . Length ; i ++ ) {
380418 this . _currentToken ++ ;
381- result . SetValue ( this . DeserializeToken ( ) , i ) ;
419+ result . SetValue ( this . Next ( ) , i ) ;
382420 }
383421 } else {
384422 for ( int i = 0 ; i < token . Length ; i ++ ) {
385423 this . _currentToken ++ ;
386- result . SetValue ( this . DeserializeToken ( elementType ) , i ) ;
424+ result . SetValue ( this . Next ( elementType ) , i ) ;
387425 }
388426 }
389427
@@ -425,12 +463,12 @@ private object MakeList(Type targetType, in PhpToken token) {
425463 if ( itemType == typeof ( object ) ) {
426464 for ( int i = 0 ; i < token . Length ; i ++ ) {
427465 this . _currentToken ++ ;
428- result . Add ( this . DeserializeToken ( ) ) ;
466+ result . Add ( this . Next ( ) ) ;
429467 }
430468 } else {
431469 for ( int i = 0 ; i < token . Length ; i ++ ) {
432470 this . _currentToken ++ ;
433- result . Add ( this . DeserializeToken ( itemType ) ) ;
471+ result . Add ( this . Next ( itemType ) ) ;
434472 }
435473 }
436474 return result ;
@@ -444,7 +482,7 @@ private object MakeDictionary(Type targetType, in PhpToken token) {
444482
445483 if ( ! targetType . GenericTypeArguments . Any ( ) ) {
446484 for ( int i = 0 ; i < token . Length ; i ++ ) {
447- result . Add ( this . DeserializeToken ( ) , this . DeserializeToken ( ) ) ;
485+ result . Add ( this . Next ( ) , this . Next ( ) ) ;
448486 }
449487 return result ;
450488 }
@@ -455,11 +493,11 @@ private object MakeDictionary(Type targetType, in PhpToken token) {
455493 for ( int i = 0 ; i < token . Length ; i ++ ) {
456494 result . Add (
457495 keyType == typeof ( object )
458- ? this . DeserializeToken ( )
459- : this . DeserializeToken ( keyType ) ,
496+ ? this . Next ( )
497+ : this . Next ( keyType ) ,
460498 valueType == typeof ( object )
461- ? this . DeserializeToken ( )
462- : this . DeserializeToken ( valueType )
499+ ? this . Next ( )
500+ : this . Next ( valueType )
463501 ) ;
464502 }
465503 return result ;
@@ -498,14 +536,14 @@ private object MakeCollection(in PhpToken token) {
498536 if ( ! isList || ( this . _options . UseLists == ListOptions . Default && ! consecutive ) ) {
499537 var result = new Dictionary < object , object > ( token . Length ) ;
500538 for ( int i = 0 ; i < token . Length ; i ++ ) {
501- result . Add ( this . DeserializeToken ( ) , this . DeserializeToken ( ) ) ;
539+ result . Add ( this . Next ( ) , this . Next ( ) ) ;
502540 }
503541 return result ;
504542 } else {
505543 var result = new List < object > ( token . Length ) ;
506544 for ( int i = 0 ; i < token . Length ; i ++ ) {
507545 this . _currentToken ++ ;
508- result . Add ( this . DeserializeToken ( ) ) ;
546+ result . Add ( this . Next ( ) ) ;
509547 }
510548 return result ;
511549 }
0 commit comments