4
4
"errors"
5
5
"reflect"
6
6
"strconv"
7
+ "fmt"
7
8
)
8
9
9
10
// The internal consume functions work as the parser/lexer when reading
@@ -90,7 +91,7 @@ func consumeStringRealPart(data []byte, offset int) (string, int, error) {
90
91
s := DecodePHPString (data )
91
92
92
93
// The +2 is to skip over the final '";'
93
- return s [offset : offset + length ], offset + length + 2 , nil
94
+ return s [offset : offset + length ], offset + length + 2 , nil
94
95
}
95
96
96
97
func consumeNil (data []byte , offset int ) (interface {}, int , error ) {
@@ -109,24 +110,23 @@ func consumeBool(data []byte, offset int) (bool, int, error) {
109
110
return data [offset + 2 ] == '1' , offset + 4 , nil
110
111
}
111
112
112
- func consumeObject (data []byte , offset int , v interface {}) (int , error ) {
113
- if ! checkType (data , 'O' , offset ) {
114
- return - 1 , errors .New ("not an object" )
115
- }
113
+ func consumeObjectAsMap (data []byte , offset int ) (
114
+ map [interface {}]interface {}, int , error ) {
115
+ result := map [interface {}]interface {}{}
116
116
117
117
// Read the class name. The class name follows the same format as a
118
118
// string. We could just ignore the length and hope that no class name
119
119
// ever had a non-ascii characters in it, but this is safer - and
120
120
// probably easier.
121
121
_ , offset , err := consumeStringRealPart (data , offset + 2 )
122
122
if err != nil {
123
- return - 1 , err
123
+ return nil , - 1 , err
124
124
}
125
125
126
126
// Read the number of elements in the object.
127
127
length , offset , err := consumeIntPart (data , offset )
128
128
if err != nil {
129
- return - 1 , err
129
+ return nil , - 1 , err
130
130
}
131
131
132
132
// Skip over the '{'
@@ -141,69 +141,95 @@ func consumeObject(data []byte, offset int, v interface{}) (int, error) {
141
141
// about this.
142
142
key , offset , err = consumeString (data , offset )
143
143
if err != nil {
144
- return - 1 , err
144
+ return nil , - 1 , err
145
145
}
146
146
147
- // Check the the key exists in the struct, otherwise the value
148
- // is discarded.
149
- //
150
- // We need to uppercase the first letter for compatibility.
151
- // The Marshal() function does the opposite of this.
152
- field := reflect .ValueOf (v ).Elem ().
153
- FieldByName (upperCaseFirstLetter (key ))
154
-
155
147
// If the next item is an object we can't simply consume it,
156
148
// rather we send the reflect.Value back through consumeObject
157
149
// so the recursion can be handled correctly.
158
150
if data [offset ] == 'O' {
159
- var subV interface {}
160
-
161
- if field .IsValid () {
162
- subV = field .Addr ().Interface ()
163
- } else {
164
- // If the field (key) does not exist on the
165
- // struct we pass through a dummy object that no
166
- // keys so that all of the values are discarded
167
- // but the parser can continue to operate
168
- // easily.
169
- subV = & dummyObject {}
170
- }
151
+ var subMap interface {}
171
152
172
- offset , err = consumeObject (data , offset , subV )
153
+ subMap , offset , err = consumeObjectAsMap (data , offset )
173
154
if err != nil {
174
- return - 1 , err
155
+ return nil , - 1 , err
175
156
}
157
+
158
+ result [key ] = subMap
176
159
} else {
177
160
value , offset , err = consumeNext (data , offset )
178
161
if err != nil {
179
- return - 1 , err
162
+ return nil , - 1 , err
180
163
}
181
164
182
- if field .IsValid () {
183
- setField (field , reflect .ValueOf (value ))
184
- }
165
+ result [key ] = value
185
166
}
186
167
}
187
168
188
169
// The +1 is for the final '}'
189
- return offset + 1 , nil
170
+ return result , offset + 1 , nil
190
171
}
191
172
192
- func setField (field , value reflect.Value ) {
193
- switch field .Type ().Kind () {
194
- case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 ,
195
- reflect .Int64 :
196
- field .SetInt (value .Int ())
173
+ func setField (obj interface {}, name string , value interface {}) error {
174
+ structValue := reflect .ValueOf (obj ).Elem ()
175
+
176
+ // We need to uppercase the first letter for compatibility.
177
+ // The Marshal() function does the opposite of this.
178
+ structFieldValue := structValue .FieldByName (upperCaseFirstLetter (name ))
179
+
180
+ if ! structFieldValue .IsValid () {
181
+ return fmt .Errorf ("no such field: %s in obj" , name )
182
+ }
183
+
184
+ if ! structFieldValue .CanSet () {
185
+ return fmt .Errorf ("cannot set %s field value" , name )
186
+ }
187
+
188
+ val := reflect .ValueOf (value )
189
+ switch structFieldValue .Type ().Kind () {
190
+ case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 :
191
+ structFieldValue .SetInt (val .Int ())
197
192
198
193
case reflect .Uint8 , reflect .Uint16 , reflect .Uint32 , reflect .Uint64 :
199
- field .SetUint (value .Uint ())
194
+ structFieldValue .SetUint (val .Uint ())
200
195
201
196
case reflect .Float32 , reflect .Float64 :
202
- field .SetFloat (value .Float ())
197
+ structFieldValue .SetFloat (val .Float ())
198
+
199
+ case reflect .Struct :
200
+ m := val .Interface ().(map [interface {}]interface {})
201
+ fillStruct (structFieldValue .Addr ().Interface (), m )
203
202
204
203
default :
205
- field .Set (value )
204
+ structFieldValue .Set (val )
205
+ }
206
+
207
+ return nil
208
+ }
209
+
210
+ // https://stackoverflow.com/questions/26744873/converting-map-to-struct
211
+ func fillStruct (obj interface {}, m map [interface {}]interface {}) error {
212
+ for k , v := range m {
213
+ err := setField (obj , k .(string ), v )
214
+ if err != nil {
215
+ return err
216
+ }
217
+ }
218
+
219
+ return nil
220
+ }
221
+
222
+ func consumeObject (data []byte , offset int , v interface {}) (int , error ) {
223
+ if ! checkType (data , 'O' , offset ) {
224
+ return - 1 , errors .New ("not an object" )
225
+ }
226
+
227
+ m , offset , err := consumeObjectAsMap (data , offset )
228
+ if err != nil {
229
+ return - 1 , err
206
230
}
231
+
232
+ return offset , fillStruct (v , m )
207
233
}
208
234
209
235
func consumeNext (data []byte , offset int ) (interface {}, int , error ) {
@@ -212,6 +238,8 @@ func consumeNext(data []byte, offset int) (interface{}, int, error) {
212
238
}
213
239
214
240
switch data [offset ] {
241
+ case 'a' :
242
+ return consumeArray (data , offset )
215
243
case 'b' :
216
244
return consumeBool (data , offset )
217
245
case 'd' :
@@ -222,8 +250,52 @@ func consumeNext(data []byte, offset int) (interface{}, int, error) {
222
250
return consumeString (data , offset )
223
251
case 'N' :
224
252
return consumeNil (data , offset )
253
+ case 'O' :
254
+ return consumeObjectAsMap (data , offset )
225
255
}
226
256
227
257
return nil , - 1 , errors .New ("can not consume type: " +
228
258
string (data [offset :]))
229
259
}
260
+
261
+ func consumeArray (data []byte , offset int ) ([]interface {}, int , error ) {
262
+ if ! checkType (data , 'a' , offset ) {
263
+ return []interface {}{}, - 1 , errors .New ("not an array" )
264
+ }
265
+
266
+ rawLength , offset := consumeStringUntilByte (data , ':' , offset + 2 )
267
+ length , err := strconv .Atoi (rawLength )
268
+ if err != nil {
269
+ return []interface {}{}, - 1 , err
270
+ }
271
+
272
+ // Skip over the ":{"
273
+ offset += 2
274
+
275
+ result := make ([]interface {}, length )
276
+ for i := 0 ; i < length ; i ++ {
277
+ // Even non-associative arrays (arrays that are zero-indexed)
278
+ // still have their keys serialized. We need to read these
279
+ // indexes to make sure we are actually decoding a slice and not
280
+ // a map.
281
+ var index int64
282
+ index , offset , err = consumeInt (data , offset )
283
+ if err != nil {
284
+ return []interface {}{}, - 1 , err
285
+ }
286
+
287
+ if index != int64 (i ) {
288
+ return []interface {}{}, - 1 ,
289
+ errors .New ("cannot decode map as slice" )
290
+ }
291
+
292
+ // Now we consume the value
293
+ result [i ], offset , err = consumeNext (data , offset )
294
+ if err != nil {
295
+ return []interface {}{}, - 1 , err
296
+ }
297
+ }
298
+
299
+ // The +1 is for the final '}'
300
+ return result , offset + 1 , nil
301
+ }
0 commit comments