Skip to content

Commit f754dc3

Browse files
committed
refactor: pack state needed during serialization into SerializationState::* object
1 parent d191233 commit f754dc3

File tree

1 file changed

+54
-19
lines changed

1 file changed

+54
-19
lines changed

lib/php_serialize.rb

Lines changed: 54 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,28 @@ def read_until(char)
1515
end
1616
end
1717

18+
module SerializationState
19+
class Base
20+
# @return [Boolean]
21+
attr_accessor :assoc
22+
end
23+
24+
class ToSerialize < Base
25+
end
26+
27+
class ToUnserialize < Base
28+
def initialize
29+
@classmap = {}
30+
end
31+
32+
# @return [Hash{String => Class}]
33+
attr_accessor :classmap
34+
35+
# @return [String]
36+
attr_accessor :original_encoding
37+
end
38+
end
39+
1840
# Returns a string representing the argument in a form PHP.unserialize
1941
# and PHP's unserialize() should both be able to load.
2042
#
@@ -29,17 +51,24 @@ def read_until(char)
2951
# array will be assumed to be an associative array, and will be serialized
3052
# as a PHP associative array rather than a multidimensional array.
3153
def PHP.serialize(var, assoc = false) # {{{
54+
state = SerializationState::ToSerialize.new.tap { |state|
55+
state.assoc = assoc
56+
}
57+
do_serialize(var, state)
58+
end
59+
60+
def PHP.do_serialize(var, state)
3261
s = String.new
3362
case var
3463
when Array
3564
s << "a:#{var.size}:{"
36-
if assoc and var.first.is_a?(Array) and var.first.size == 2
65+
if state.assoc and var.first.is_a?(Array) and var.first.size == 2
3766
var.each { |k,v|
38-
s << PHP.serialize(k, assoc) << PHP.serialize(v, assoc)
67+
s << PHP.do_serialize(k, state) << PHP.do_serialize(v, state)
3968
}
4069
else
4170
var.each_with_index { |v,i|
42-
s << "i:#{i};#{PHP.serialize(v, assoc)}"
71+
s << "i:#{i};#{PHP.do_serialize(v, state)}"
4372
}
4473
end
4574

@@ -48,15 +77,15 @@ def PHP.serialize(var, assoc = false) # {{{
4877
when Hash
4978
s << "a:#{var.size}:{"
5079
var.each do |k,v|
51-
s << "#{PHP.serialize(k, assoc)}#{PHP.serialize(v, assoc)}"
80+
s << "#{PHP.do_serialize(k, state)}#{PHP.do_serialize(v, state)}"
5281
end
5382
s << '}'
5483

5584
when Struct
5685
# encode as Object with same name
5786
s << "O:#{var.class.to_s.bytesize}:\"#{var.class.to_s.downcase}\":#{var.members.length}:{"
5887
var.members.each do |member|
59-
s << "#{PHP.serialize(member, assoc)}#{PHP.serialize(var[member], assoc)}"
88+
s << "#{PHP.do_serialize(member, state)}#{PHP.do_serialize(var[member], state)}"
6089
end
6190
s << '}'
6291

@@ -81,7 +110,7 @@ def PHP.serialize(var, assoc = false) # {{{
81110
# encode as Object with same name
82111
s << "O:#{var.class.to_s.bytesize}:\"#{var.class.to_s.downcase}\":#{v.length}:{"
83112
v.each do |k,v|
84-
s << "#{PHP.serialize(k.to_s, assoc)}#{PHP.serialize(v, assoc)}"
113+
s << "#{PHP.do_serialize(k.to_s, state)}#{PHP.do_serialize(v, state)}"
85114
end
86115
s << '}'
87116
else
@@ -164,19 +193,25 @@ def PHP.unserialize(string, classmap = nil, assoc = false) # {{{
164193

165194
ret = nil
166195
original_encoding = string.encoding
196+
state = SerializationState::ToUnserialize.new.tap { |state|
197+
state.assoc = assoc
198+
state.classmap = classmap
199+
state.original_encoding = original_encoding
200+
}
201+
167202
string = StringIOReader.new(string.force_encoding('BINARY'))
168203
while string.string[string.pos, 32] =~ /^(\w+)\|/ # session_name|serialized_data
169204
ret ||= {}
170205
string.pos += $&.size
171-
ret[$1] = PHP.do_unserialize(string, classmap, assoc, original_encoding)
206+
ret[$1] = PHP.do_unserialize(string, state)
172207
end
173208

174-
ret || PHP.do_unserialize(string, classmap, assoc, original_encoding)
209+
ret || PHP.do_unserialize(string, state)
175210
end
176211

177212
private
178213

179-
def PHP.do_unserialize(string, classmap, assoc, original_encoding)
214+
def PHP.do_unserialize(string, state)
180215
val = nil
181216
# determine a type
182217
type = string.read(2)[0,1]
@@ -185,7 +220,7 @@ def PHP.do_unserialize(string, classmap, assoc, original_encoding)
185220
count = string.read_until('{').to_i
186221
val = Array.new
187222
count.times do |i|
188-
val << [do_unserialize(string, classmap, assoc, original_encoding), do_unserialize(string, classmap, assoc, original_encoding)]
223+
val << [do_unserialize(string, state), do_unserialize(string, state)]
189224
end
190225
string.read(1) # skip the ending }
191226

@@ -203,13 +238,13 @@ def PHP.do_unserialize(string, classmap, assoc, original_encoding)
203238

204239
val = val.map { |tuple|
205240
tuple.map { |it|
206-
it.kind_of?(String) ? it.force_encoding(original_encoding) : it
241+
it.kind_of?(String) ? it.force_encoding(state.original_encoding) : it
207242
}
208243
}
209244

210245
if array
211246
val.map! {|_,value| value }
212-
elsif !assoc
247+
elsif !state.assoc
213248
val = Hash[val]
214249
end
215250

@@ -223,21 +258,21 @@ def PHP.do_unserialize(string, classmap, assoc, original_encoding)
223258
len = string.read_until('{').to_i
224259

225260
len.times do
226-
attr = (do_unserialize(string, classmap, assoc, original_encoding))
227-
attrs << [attr.intern, (attr << '=').intern, do_unserialize(string, classmap, assoc, original_encoding)]
261+
attr = (do_unserialize(string, state))
262+
attrs << [attr.intern, (attr << '=').intern, do_unserialize(string, state)]
228263
end
229264
string.read(1)
230265

231266
val = nil
232267
# See if we need to map to a particular object
233-
if classmap.has_key?(klass)
234-
val = classmap[klass].new
268+
if state.classmap.has_key?(klass)
269+
val = state.classmap[klass].new
235270
elsif Struct.const_defined?(klass) # Nope; see if there's a Struct
236-
classmap[klass] = val = Struct.const_get(klass)
271+
state.classmap[klass] = val = Struct.const_get(klass)
237272
val = val.new
238273
else # Nope; see if there's a Constant
239274
begin
240-
classmap[klass] = val = Module.const_get(klass)
275+
state.classmap[klass] = val = Module.const_get(klass)
241276

242277
val = val.new
243278
rescue NameError # Nope; make a new Struct
@@ -252,7 +287,7 @@ def PHP.do_unserialize(string, classmap, assoc, original_encoding)
252287

253288
when 's' # string, s:length:"data";
254289
len = string.read_until(':').to_i + 3 # quotes, separator
255-
val = string.read(len)[1...-2].force_encoding(original_encoding) # read it, kill useless quotes
290+
val = string.read(len)[1...-2].force_encoding(state.original_encoding) # read it, kill useless quotes
256291

257292
when 'i' # integer, i:123
258293
val = string.read_until(';').to_i

0 commit comments

Comments
 (0)