php-serialize4ruby 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ # Add dependencies to develop your gem here.
7
+ # Include everything needed to run rake, tests, features, etc.
8
+ group :development do
9
+ gem "rspec", "~> 2.8.0"
10
+ gem "rdoc", "~> 3.12"
11
+ gem "bundler", ">= 1.0.0"
12
+ gem "jeweler", "~> 1.8.3"
13
+ gem "simplecov"
14
+ end
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Kasper Johansen
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,34 @@
1
+ Ruby PHP Serializer
2
+ ===================
3
+
4
+ This module provides two methods: PHP.serialize() and PHP.unserialize(), both
5
+ of which should be compatible with the similarly named functions in PHP.
6
+
7
+ Basic usage:
8
+
9
+ require 'php_serialize'
10
+ in = {'foo' => 'bar'}
11
+ php = PHP.serialize(in)
12
+ # pass string to PHP unserialize() to get array('foo' => 'bar')
13
+ out = PHP.unserialize(php) # => {'foo' => 'bar'}
14
+
15
+
16
+ PHP.unserialize can also read PHP sessions, which are collections of named
17
+ serialized objects. These can be reserialized using PHP.serialize_session(),
18
+ which has the same semantics as PHP.serialize(), but which only supports
19
+ Hash and associative Arrays for the root object.
20
+
21
+
22
+ Acknowledgements
23
+ ================
24
+
25
+ TJ Vanderpoel, initial PHP serialized session support.
26
+
27
+ Philip Hallstrom, fix for self-generated Structs on unserialization.
28
+
29
+ Edward Speyer, fix for assoc serialization in nested structures.
30
+
31
+
32
+
33
+ Author: Thomas Hurst <tom@hur.st>, http://hur.st/
34
+ WWW: http://www.aagh.net/projects/ruby-php-serialize
@@ -0,0 +1,19 @@
1
+ = php-serialize4ruby
2
+
3
+ Description goes here.
4
+
5
+ == Contributing to php-serialize4ruby
6
+
7
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
8
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
9
+ * Fork the project.
10
+ * Start a feature/bugfix branch.
11
+ * Commit and push until you are happy with your contribution.
12
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
13
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
14
+
15
+ == Copyright
16
+
17
+ Copyright (c) 2012 Kasper Johansen. See LICENSE.txt for
18
+ further details.
19
+
@@ -0,0 +1,49 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "php-serialize4ruby"
18
+ gem.homepage = "http://github.com/kaspernj/php-serialize4ruby"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{A fork of the original gem "php-serialize" because I have no hope of the original guy to update the gem}
21
+ gem.description = %Q{Read the original gems description.}
22
+ gem.email = "k@spernj.org"
23
+ gem.authors = ["Kasper Johansen"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rspec/core'
29
+ require 'rspec/core/rake_task'
30
+ RSpec::Core::RakeTask.new(:spec) do |spec|
31
+ spec.pattern = FileList['spec/**/*_spec.rb']
32
+ end
33
+
34
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
35
+ spec.pattern = 'spec/**/*_spec.rb'
36
+ spec.rcov = true
37
+ end
38
+
39
+ task :default => :spec
40
+
41
+ require 'rdoc/task'
42
+ Rake::RDocTask.new do |rdoc|
43
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
44
+
45
+ rdoc.rdoc_dir = 'rdoc'
46
+ rdoc.title = "php-serialize4ruby #{version}"
47
+ rdoc.rdoc_files.include('README*')
48
+ rdoc.rdoc_files.include('lib/**/*.rb')
49
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.0
@@ -0,0 +1,314 @@
1
+ #!/usr/bin/env ruby
2
+ # Copyright (c) 2003-2009 Thomas Hurst <tom@hur.st>
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+
22
+ # PHP serialize() and unserialize() workalikes
23
+ #
24
+ # Release History:
25
+ # 1.0.0 - 2003-06-02 - First release.
26
+ # 1.0.1 - 2003-06-16 - Minor bugfixes.
27
+ # 1.0.2 - 2004-09-17 - Switch all {}'s to explicit Hash.new's.
28
+ # 1.1.0 - 2009-04-01 - Pass assoc to recursive calls (thanks to Edward Speyer).
29
+ # - Serialize Symbol like String.
30
+ # - Add testsuite.
31
+ # - Instantiate auto-generated Structs properly (thanks
32
+ # to Philip Hallstrom).
33
+ # - Unserialize arrays properly in assoc mode.
34
+ # - Add PHP session support (thanks to TJ Vanderpoel).
35
+ # - Release as tarball and gem.
36
+ #
37
+ # See http://www.php.net/serialize and http://www.php.net/unserialize for
38
+ # details on the PHP side of all this.
39
+ module PHP
40
+ # string = PHP.serialize(mixed var[, bool assoc])
41
+ #
42
+ # Returns a string representing the argument in a form PHP.unserialize
43
+ # and PHP's unserialize() should both be able to load.
44
+ #
45
+ # Array, Hash, Fixnum, Float, True/FalseClass, NilClass, String and Struct
46
+ # are supported; as are objects which support the to_assoc method, which
47
+ # returns an array of the form [['attr_name', 'value']..]. Anything else
48
+ # will raise a TypeError.
49
+ #
50
+ # If 'assoc' is specified, Array's who's first element is a two value
51
+ # array will be assumed to be an associative array, and will be serialized
52
+ # as a PHP associative array rather than a multidimensional array.
53
+ def PHP.serialize(var, assoc = false) # {{{
54
+ s = ''
55
+ case var
56
+ when Array
57
+ s << "a:#{var.size}:{"
58
+ if assoc and var.first.is_a?(Array) and var.first.size == 2
59
+ var.each { |k,v|
60
+ s << PHP.serialize(k, assoc) << PHP.serialize(v, assoc)
61
+ }
62
+ else
63
+ var.each_with_index { |v,i|
64
+ s << "i:#{i};#{PHP.serialize(v, assoc)}"
65
+ }
66
+ end
67
+
68
+ s << '}'
69
+
70
+ when Hash
71
+ s << "a:#{var.size}:{"
72
+ var.each do |k,v|
73
+ s << "#{PHP.serialize(k, assoc)}#{PHP.serialize(v, assoc)}"
74
+ end
75
+ s << '}'
76
+
77
+ when Struct
78
+ # encode as Object with same name
79
+ s << "O:#{var.class.to_s.length}:\"#{var.class.to_s.downcase}\":#{var.members.length}:{"
80
+ var.members.each do |member|
81
+ s << "#{PHP.serialize(member, assoc)}#{PHP.serialize(var[member], assoc)}"
82
+ end
83
+ s << '}'
84
+
85
+ when String, Symbol
86
+ s << "s:#{var.to_s.bytesize}:\"#{var.to_s}\";"
87
+
88
+ when Fixnum # PHP doesn't have bignums
89
+ s << "i:#{var};"
90
+
91
+ when Float
92
+ s << "d:#{var};"
93
+
94
+ when NilClass
95
+ s << 'N;'
96
+
97
+ when FalseClass, TrueClass
98
+ s << "b:#{var ? 1 :0};"
99
+
100
+ else
101
+ if var.respond_to?(:to_assoc)
102
+ v = var.to_assoc
103
+ # encode as Object with same name
104
+ s << "O:#{var.class.to_s.length}:\"#{var.class.to_s.downcase}\":#{v.length}:{"
105
+ v.each do |k,v|
106
+ s << "#{PHP.serialize(k.to_s, assoc)}#{PHP.serialize(v, assoc)}"
107
+ end
108
+ s << '}'
109
+ else
110
+ raise TypeError, "Unable to serialize type #{var.class}"
111
+ end
112
+ end
113
+
114
+ s
115
+ end # }}}
116
+
117
+ # string = PHP.serialize_session(mixed var[, bool assoc])
118
+ #
119
+ # Like PHP.serialize, but only accepts a Hash or associative Array as the root
120
+ # type. The results are returned in PHP session format.
121
+ def PHP.serialize_session(var, assoc = false) # {{{
122
+ s = ''
123
+ case var
124
+ when Hash
125
+ var.each do |key,value|
126
+ if key.to_s =~ /\|/
127
+ raise IndexError, "Top level names may not contain pipes"
128
+ end
129
+ s << "#{key}|#{PHP.serialize(value, assoc)}"
130
+ end
131
+ when Array
132
+ var.each do |x|
133
+ case x
134
+ when Array
135
+ if x.size == 2
136
+ s << "#{x[0]}|#{PHP.serialize(x[1])}"
137
+ else
138
+ raise TypeError, "Array is not associative"
139
+ end
140
+ end
141
+ end
142
+ else
143
+ raise TypeError, "Unable to serialize sessions with top level types other than Hash and associative Array"
144
+ end
145
+ s
146
+ end # }}}
147
+
148
+ # mixed = PHP.unserialize(string serialized, [hash classmap, [bool assoc]])
149
+ #
150
+ # Returns an object containing the reconstituted data from serialized.
151
+ #
152
+ # If a PHP array (associative; like an ordered hash) is encountered, it
153
+ # scans the keys; if they're all incrementing integers counting from 0,
154
+ # it's unserialized as an Array, otherwise it's unserialized as a Hash.
155
+ # Note: this will lose ordering. To avoid this, specify assoc=true,
156
+ # and it will be unserialized as an associative array: [[key,value],...]
157
+ #
158
+ # If a serialized object is encountered, the hash 'classmap' is searched for
159
+ # the class name (as a symbol). Since PHP classnames are not case-preserving,
160
+ # this *must* be a .capitalize()d representation. The value is expected
161
+ # to be the class itself; i.e. something you could call .new on.
162
+ #
163
+ # If it's not found in 'classmap', the current constant namespace is searched,
164
+ # and failing that, a new Struct(classname) is generated, with the arguments
165
+ # for .new specified in the same order PHP provided; since PHP uses hashes
166
+ # to represent attributes, this should be the same order they're specified
167
+ # in PHP, but this is untested.
168
+ #
169
+ # each serialized attribute is sent to the new object using the respective
170
+ # {attribute}=() method; you'll get a NameError if the method doesn't exist.
171
+ #
172
+ # Array, Hash, Fixnum, Float, True/FalseClass, NilClass and String should
173
+ # be returned identically (i.e. foo == PHP.unserialize(PHP.serialize(foo))
174
+ # for these types); Struct should be too, provided it's in the namespace
175
+ # Module.const_get within unserialize() can see, or you gave it the same
176
+ # name in the Struct.new(<structname>), otherwise you should provide it in
177
+ # classmap.
178
+ #
179
+ # Note: StringIO is required for unserialize(); it's loaded as needed
180
+ def PHP.unserialize(string, classmap = nil, assoc = false) # {{{
181
+ if classmap == true or classmap == false
182
+ assoc = classmap
183
+ classmap = {}
184
+ end
185
+ classmap ||= {}
186
+
187
+ require 'stringio'
188
+ string = StringIO.new(string)
189
+ def string.read_until(char)
190
+ val = ''
191
+ while (c = self.read(1)) != char
192
+ val << c
193
+ end
194
+ val
195
+ end
196
+
197
+ if string.string =~ /^(\w+)\|/ # session_name|serialized_data
198
+ ret = Hash.new
199
+ loop do
200
+ if string.string[string.pos, 32] =~ /^(\w+)\|/
201
+ string.pos += $&.size
202
+ ret[$1] = PHP.do_unserialize(string, classmap, assoc)
203
+ else
204
+ break
205
+ end
206
+ end
207
+ ret
208
+ else
209
+ PHP.do_unserialize(string, classmap, assoc)
210
+ end
211
+ end
212
+
213
+ private
214
+ def PHP.do_unserialize(string, classmap, assoc)
215
+ val = nil
216
+ # determine a type
217
+ type = string.read(2)[0,1]
218
+ case type
219
+ when 'a' # associative array, a:length:{[index][value]...}
220
+ count = string.read_until('{').to_i
221
+ val = vals = Array.new
222
+ count.times do |i|
223
+ vals << [do_unserialize(string, classmap, assoc), do_unserialize(string, classmap, assoc)]
224
+ end
225
+ string.read(1) # skip the ending }
226
+
227
+ # now, we have an associative array, let's clean it up a bit...
228
+ # arrays have all numeric indexes, in order; otherwise we assume a hash
229
+ array = true
230
+ i = 0
231
+ vals.each do |key,value|
232
+ if key != i # wrong index -> assume hash
233
+ array = false
234
+ break
235
+ end
236
+ i += 1
237
+ end
238
+
239
+ if array
240
+ vals.collect! do |key,value|
241
+ value
242
+ end
243
+ else
244
+ if assoc
245
+ val = vals.map {|v| v }
246
+ else
247
+ val = Hash.new
248
+ vals.each do |key,value|
249
+ val[key] = value
250
+ end
251
+ end
252
+ end
253
+
254
+ when 'O' # object, O:length:"class":length:{[attribute][value]...}
255
+ # class name (lowercase in PHP, grr)
256
+ len = string.read_until(':').to_i + 3 # quotes, seperator
257
+ klass = string.read(len)[1...-2].capitalize.intern # read it, kill useless quotes
258
+
259
+ # read the attributes
260
+ attrs = []
261
+ len = string.read_until('{').to_i
262
+
263
+ len.times do
264
+ attr = (do_unserialize(string, classmap, assoc))
265
+ attrs << [attr.intern, (attr << '=').intern, do_unserialize(string, classmap, assoc)]
266
+ end
267
+ string.read(1)
268
+
269
+ val = nil
270
+ # See if we need to map to a particular object
271
+ if classmap.has_key?(klass)
272
+ val = classmap[klass].new
273
+ elsif Struct.const_defined?(klass) # Nope; see if there's a Struct
274
+ classmap[klass] = val = Struct.const_get(klass)
275
+ val = val.new
276
+ else # Nope; see if there's a Constant
277
+ begin
278
+ classmap[klass] = val = Module.const_get(klass)
279
+
280
+ val = val.new
281
+ rescue NameError # Nope; make a new Struct
282
+ classmap[klass] = Struct.new(klass.to_s, *attrs.collect { |v| v[0].to_s })
283
+ val = val.new
284
+ end
285
+ end
286
+
287
+ attrs.each do |attr,attrassign,v|
288
+ val.__send__(attrassign, v)
289
+ end
290
+
291
+ when 's' # string, s:length:"data";
292
+ len = string.read_until(':').to_i + 3 # quotes, separator
293
+ val = string.read(len)[1...-2] # read it, kill useless quotes
294
+
295
+ when 'i' # integer, i:123
296
+ val = string.read_until(';').to_i
297
+
298
+ when 'd' # double (float), d:1.23
299
+ val = string.read_until(';').to_f
300
+
301
+ when 'N' # NULL, N;
302
+ val = nil
303
+
304
+ when 'b' # bool, b:0 or 1
305
+ val = (string.read(2)[0] == ?1 ? true : false)
306
+
307
+ else
308
+ raise TypeError, "Unable to unserialize type '#{type}'"
309
+ end
310
+
311
+ val
312
+ end # }}}
313
+ end
314
+
@@ -0,0 +1,314 @@
1
+ #!/usr/bin/env ruby
2
+ # Copyright (c) 2003-2009 Thomas Hurst <tom@hur.st>
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+
22
+ # PHP serialize() and unserialize() workalikes
23
+ #
24
+ # Release History:
25
+ # 1.0.0 - 2003-06-02 - First release.
26
+ # 1.0.1 - 2003-06-16 - Minor bugfixes.
27
+ # 1.0.2 - 2004-09-17 - Switch all {}'s to explicit Hash.new's.
28
+ # 1.1.0 - 2009-04-01 - Pass assoc to recursive calls (thanks to Edward Speyer).
29
+ # - Serialize Symbol like String.
30
+ # - Add testsuite.
31
+ # - Instantiate auto-generated Structs properly (thanks
32
+ # to Philip Hallstrom).
33
+ # - Unserialize arrays properly in assoc mode.
34
+ # - Add PHP session support (thanks to TJ Vanderpoel).
35
+ # - Release as tarball and gem.
36
+ #
37
+ # See http://www.php.net/serialize and http://www.php.net/unserialize for
38
+ # details on the PHP side of all this.
39
+ module PHP
40
+ # string = PHP.serialize(mixed var[, bool assoc])
41
+ #
42
+ # Returns a string representing the argument in a form PHP.unserialize
43
+ # and PHP's unserialize() should both be able to load.
44
+ #
45
+ # Array, Hash, Fixnum, Float, True/FalseClass, NilClass, String and Struct
46
+ # are supported; as are objects which support the to_assoc method, which
47
+ # returns an array of the form [['attr_name', 'value']..]. Anything else
48
+ # will raise a TypeError.
49
+ #
50
+ # If 'assoc' is specified, Array's who's first element is a two value
51
+ # array will be assumed to be an associative array, and will be serialized
52
+ # as a PHP associative array rather than a multidimensional array.
53
+ def PHP.serialize(var, assoc = false) # {{{
54
+ s = ''
55
+ case var
56
+ when Array
57
+ s << "a:#{var.size}:{"
58
+ if assoc and var.first.is_a?(Array) and var.first.size == 2
59
+ var.each { |k,v|
60
+ s << PHP.serialize(k, assoc) << PHP.serialize(v, assoc)
61
+ }
62
+ else
63
+ var.each_with_index { |v,i|
64
+ s << "i:#{i};#{PHP.serialize(v, assoc)}"
65
+ }
66
+ end
67
+
68
+ s << '}'
69
+
70
+ when Hash
71
+ s << "a:#{var.size}:{"
72
+ var.each do |k,v|
73
+ s << "#{PHP.serialize(k, assoc)}#{PHP.serialize(v, assoc)}"
74
+ end
75
+ s << '}'
76
+
77
+ when Struct
78
+ # encode as Object with same name
79
+ s << "O:#{var.class.to_s.length}:\"#{var.class.to_s.downcase}\":#{var.members.length}:{"
80
+ var.members.each do |member|
81
+ s << "#{PHP.serialize(member, assoc)}#{PHP.serialize(var[member], assoc)}"
82
+ end
83
+ s << '}'
84
+
85
+ when String, Symbol
86
+ s << "s:#{var.to_s.bytesize}:\"#{var.to_s}\";"
87
+
88
+ when Fixnum # PHP doesn't have bignums
89
+ s << "i:#{var};"
90
+
91
+ when Float
92
+ s << "d:#{var};"
93
+
94
+ when NilClass
95
+ s << 'N;'
96
+
97
+ when FalseClass, TrueClass
98
+ s << "b:#{var ? 1 :0};"
99
+
100
+ else
101
+ if var.respond_to?(:to_assoc)
102
+ v = var.to_assoc
103
+ # encode as Object with same name
104
+ s << "O:#{var.class.to_s.length}:\"#{var.class.to_s.downcase}\":#{v.length}:{"
105
+ v.each do |k,v|
106
+ s << "#{PHP.serialize(k.to_s, assoc)}#{PHP.serialize(v, assoc)}"
107
+ end
108
+ s << '}'
109
+ else
110
+ raise TypeError, "Unable to serialize type #{var.class}"
111
+ end
112
+ end
113
+
114
+ s
115
+ end # }}}
116
+
117
+ # string = PHP.serialize_session(mixed var[, bool assoc])
118
+ #
119
+ # Like PHP.serialize, but only accepts a Hash or associative Array as the root
120
+ # type. The results are returned in PHP session format.
121
+ def PHP.serialize_session(var, assoc = false) # {{{
122
+ s = ''
123
+ case var
124
+ when Hash
125
+ var.each do |key,value|
126
+ if key.to_s =~ /\|/
127
+ raise IndexError, "Top level names may not contain pipes"
128
+ end
129
+ s << "#{key}|#{PHP.serialize(value, assoc)}"
130
+ end
131
+ when Array
132
+ var.each do |x|
133
+ case x
134
+ when Array
135
+ if x.size == 2
136
+ s << "#{x[0]}|#{PHP.serialize(x[1])}"
137
+ else
138
+ raise TypeError, "Array is not associative"
139
+ end
140
+ end
141
+ end
142
+ else
143
+ raise TypeError, "Unable to serialize sessions with top level types other than Hash and associative Array"
144
+ end
145
+ s
146
+ end # }}}
147
+
148
+ # mixed = PHP.unserialize(string serialized, [hash classmap, [bool assoc]])
149
+ #
150
+ # Returns an object containing the reconstituted data from serialized.
151
+ #
152
+ # If a PHP array (associative; like an ordered hash) is encountered, it
153
+ # scans the keys; if they're all incrementing integers counting from 0,
154
+ # it's unserialized as an Array, otherwise it's unserialized as a Hash.
155
+ # Note: this will lose ordering. To avoid this, specify assoc=true,
156
+ # and it will be unserialized as an associative array: [[key,value],...]
157
+ #
158
+ # If a serialized object is encountered, the hash 'classmap' is searched for
159
+ # the class name (as a symbol). Since PHP classnames are not case-preserving,
160
+ # this *must* be a .capitalize()d representation. The value is expected
161
+ # to be the class itself; i.e. something you could call .new on.
162
+ #
163
+ # If it's not found in 'classmap', the current constant namespace is searched,
164
+ # and failing that, a new Struct(classname) is generated, with the arguments
165
+ # for .new specified in the same order PHP provided; since PHP uses hashes
166
+ # to represent attributes, this should be the same order they're specified
167
+ # in PHP, but this is untested.
168
+ #
169
+ # each serialized attribute is sent to the new object using the respective
170
+ # {attribute}=() method; you'll get a NameError if the method doesn't exist.
171
+ #
172
+ # Array, Hash, Fixnum, Float, True/FalseClass, NilClass and String should
173
+ # be returned identically (i.e. foo == PHP.unserialize(PHP.serialize(foo))
174
+ # for these types); Struct should be too, provided it's in the namespace
175
+ # Module.const_get within unserialize() can see, or you gave it the same
176
+ # name in the Struct.new(<structname>), otherwise you should provide it in
177
+ # classmap.
178
+ #
179
+ # Note: StringIO is required for unserialize(); it's loaded as needed
180
+ def PHP.unserialize(string, classmap = nil, assoc = false) # {{{
181
+ if classmap == true or classmap == false
182
+ assoc = classmap
183
+ classmap = {}
184
+ end
185
+ classmap ||= {}
186
+
187
+ require 'stringio'
188
+ string = StringIO.new(string)
189
+ def string.read_until(char)
190
+ val = ''
191
+ while (c = self.read(1)) != char
192
+ val << c
193
+ end
194
+ val
195
+ end
196
+
197
+ if string.string =~ /^(\w+)\|/ # session_name|serialized_data
198
+ ret = Hash.new
199
+ loop do
200
+ if string.string[string.pos, 32] =~ /^(\w+)\|/
201
+ string.pos += $&.size
202
+ ret[$1] = PHP.do_unserialize(string, classmap, assoc)
203
+ else
204
+ break
205
+ end
206
+ end
207
+ ret
208
+ else
209
+ PHP.do_unserialize(string, classmap, assoc)
210
+ end
211
+ end
212
+
213
+ private
214
+ def PHP.do_unserialize(string, classmap, assoc)
215
+ val = nil
216
+ # determine a type
217
+ type = string.read(2)[0,1]
218
+ case type
219
+ when 'a' # associative array, a:length:{[index][value]...}
220
+ count = string.read_until('{').to_i
221
+ val = vals = Array.new
222
+ count.times do |i|
223
+ vals << [do_unserialize(string, classmap, assoc), do_unserialize(string, classmap, assoc)]
224
+ end
225
+ string.read(1) # skip the ending }
226
+
227
+ # now, we have an associative array, let's clean it up a bit...
228
+ # arrays have all numeric indexes, in order; otherwise we assume a hash
229
+ array = true
230
+ i = 0
231
+ vals.each do |key,value|
232
+ if key != i # wrong index -> assume hash
233
+ array = false
234
+ break
235
+ end
236
+ i += 1
237
+ end
238
+
239
+ if array
240
+ vals.collect! do |key,value|
241
+ value
242
+ end
243
+ else
244
+ if assoc
245
+ val = vals.map {|v| v }
246
+ else
247
+ val = Hash.new
248
+ vals.each do |key,value|
249
+ val[key] = value
250
+ end
251
+ end
252
+ end
253
+
254
+ when 'O' # object, O:length:"class":length:{[attribute][value]...}
255
+ # class name (lowercase in PHP, grr)
256
+ len = string.read_until(':').to_i + 3 # quotes, seperator
257
+ klass = string.read(len)[1...-2].capitalize.intern # read it, kill useless quotes
258
+
259
+ # read the attributes
260
+ attrs = []
261
+ len = string.read_until('{').to_i
262
+
263
+ len.times do
264
+ attr = (do_unserialize(string, classmap, assoc))
265
+ attrs << [attr.intern, (attr << '=').intern, do_unserialize(string, classmap, assoc)]
266
+ end
267
+ string.read(1)
268
+
269
+ val = nil
270
+ # See if we need to map to a particular object
271
+ if classmap.has_key?(klass)
272
+ val = classmap[klass].new
273
+ elsif Struct.const_defined?(klass) # Nope; see if there's a Struct
274
+ classmap[klass] = val = Struct.const_get(klass)
275
+ val = val.new
276
+ else # Nope; see if there's a Constant
277
+ begin
278
+ classmap[klass] = val = Module.const_get(klass)
279
+
280
+ val = val.new
281
+ rescue NameError # Nope; make a new Struct
282
+ classmap[klass] = Struct.new(klass.to_s, *attrs.collect { |v| v[0].to_s })
283
+ val = val.new
284
+ end
285
+ end
286
+
287
+ attrs.each do |attr,attrassign,v|
288
+ val.__send__(attrassign, v)
289
+ end
290
+
291
+ when 's' # string, s:length:"data";
292
+ len = string.read_until(':').to_i + 3 # quotes, separator
293
+ val = string.read(len)[1...-2] # read it, kill useless quotes
294
+
295
+ when 'i' # integer, i:123
296
+ val = string.read_until(';').to_i
297
+
298
+ when 'd' # double (float), d:1.23
299
+ val = string.read_until(';').to_f
300
+
301
+ when 'N' # NULL, N;
302
+ val = nil
303
+
304
+ when 'b' # bool, b:0 or 1
305
+ val = (string.read(2)[0] == ?1 ? true : false)
306
+
307
+ else
308
+ raise TypeError, "Unable to unserialize type '#{type}'"
309
+ end
310
+
311
+ val
312
+ end # }}}
313
+ end
314
+
@@ -0,0 +1,19 @@
1
+
2
+ Gem::Specification.new do |spec|
3
+ spec.name = "php-serialize"
4
+ spec.version = "1.1.0"
5
+ spec.summary = "Ruby analogs to PHP's serialize() and unserialize() functions"
6
+ spec.require_path = 'lib/'
7
+ spec.files = Dir['lib/*.rb']
8
+ spec.author = "Thomas Hurst"
9
+ spec.email = "tom@hur.st"
10
+ spec.homepage = "http://www.aagh.net/projects/ruby-php-serialize"
11
+ spec.description = <<-EOF
12
+ This module provides two methods: PHP.serialize() and PHP.unserialize(), both
13
+ of which should be compatible with the similarly named functions in PHP.
14
+
15
+ It can also serialize and unserialize PHP sessions.
16
+ EOF
17
+ spec.test_file = 'test.rb'
18
+ spec.has_rdoc = true
19
+ end
@@ -0,0 +1,67 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{php-serialize4ruby}
8
+ s.version = "0.0.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Kasper Johansen"]
12
+ s.date = %q{2012-05-06}
13
+ s.description = %q{Read the original gems description.}
14
+ s.email = %q{k@spernj.org}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README",
18
+ "README.rdoc"
19
+ ]
20
+ s.files = [
21
+ ".document",
22
+ ".rspec",
23
+ "Gemfile",
24
+ "LICENSE.txt",
25
+ "README",
26
+ "README.rdoc",
27
+ "Rakefile",
28
+ "VERSION",
29
+ "lib/php-serialize4ruby.rb",
30
+ "lib/php_serialize.rb",
31
+ "php-serialize.gemspec",
32
+ "php-serialize4ruby.gemspec",
33
+ "spec/php-serialize4ruby_spec.rb",
34
+ "spec/spec_helper.rb",
35
+ "test.rb"
36
+ ]
37
+ s.homepage = %q{http://github.com/kaspernj/php-serialize4ruby}
38
+ s.licenses = ["MIT"]
39
+ s.require_paths = ["lib"]
40
+ s.rubygems_version = %q{1.6.2}
41
+ s.summary = %q{A fork of the original gem "php-serialize" because I have no hope of the original guy to update the gem}
42
+
43
+ if s.respond_to? :specification_version then
44
+ s.specification_version = 3
45
+
46
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
47
+ s.add_development_dependency(%q<rspec>, ["~> 2.8.0"])
48
+ s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
49
+ s.add_development_dependency(%q<bundler>, [">= 1.0.0"])
50
+ s.add_development_dependency(%q<jeweler>, ["~> 1.8.3"])
51
+ s.add_development_dependency(%q<simplecov>, [">= 0"])
52
+ else
53
+ s.add_dependency(%q<rspec>, ["~> 2.8.0"])
54
+ s.add_dependency(%q<rdoc>, ["~> 3.12"])
55
+ s.add_dependency(%q<bundler>, [">= 1.0.0"])
56
+ s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
57
+ s.add_dependency(%q<simplecov>, [">= 0"])
58
+ end
59
+ else
60
+ s.add_dependency(%q<rspec>, ["~> 2.8.0"])
61
+ s.add_dependency(%q<rdoc>, ["~> 3.12"])
62
+ s.add_dependency(%q<bundler>, [">= 1.0.0"])
63
+ s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
64
+ s.add_dependency(%q<simplecov>, [">= 0"])
65
+ end
66
+ end
67
+
@@ -0,0 +1,9 @@
1
+ #coding: utf-8
2
+
3
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
4
+
5
+ describe "PhpSerialize4ruby" do
6
+ it "should be able to some stuff without failing" do
7
+ PHP.serialize(:type => "hmm", :test => "åæø")
8
+ end
9
+ end
@@ -0,0 +1,12 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+ require 'php-serialize4ruby'
5
+
6
+ # Requires supporting files with custom matchers and macros, etc,
7
+ # in ./support/ and its subdirectories.
8
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
9
+
10
+ RSpec.configure do |config|
11
+
12
+ end
data/test.rb ADDED
@@ -0,0 +1,119 @@
1
+ #!/usr/local/bin/ruby
2
+ # encoding: utf-8
3
+
4
+ if RUBY_VERSION == "1.9.2"
5
+ # needed, when running in Ruby 1.9.2 -> no stdlib test/unit
6
+ gem 'test-unit'
7
+ end
8
+ require 'test/unit'
9
+ require 'test/unit/ui/console/testrunner'
10
+
11
+ $:.unshift "lib"
12
+ require 'php_serialize'
13
+
14
+ TestStruct = Struct.new(:name, :value)
15
+ class TestClass
16
+ attr_accessor :name
17
+ attr_accessor :value
18
+
19
+ def initialize(name = nil, value = nil)
20
+ @name = name
21
+ @value = value
22
+ end
23
+
24
+ def to_assoc
25
+ [['name', @name], ['value', @value]]
26
+ end
27
+
28
+ def ==(other)
29
+ other.class == self.class and other.name == @name and other.value == @value
30
+ end
31
+ end
32
+
33
+ ClassMap = {
34
+ TestStruct.name.capitalize.intern => TestStruct,
35
+ TestClass.name.capitalize.intern => TestClass
36
+ }
37
+
38
+ class TestPhpSerialize < Test::Unit::TestCase
39
+ def self.test(ruby, php, opts = {})
40
+ if opts[:name]
41
+ name = opts[:name]
42
+ else
43
+ name = ruby.to_s
44
+ end
45
+
46
+ define_method("test_#{name}".intern) do
47
+ assert_nothing_thrown do
48
+ serialized = PHP.serialize(ruby)
49
+ assert_equal php, serialized
50
+
51
+ unserialized = PHP.unserialize(serialized, ClassMap)
52
+ case ruby
53
+ when Symbol
54
+ assert_equal ruby.to_s, unserialized
55
+ else
56
+ assert_equal ruby, unserialized
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ test nil, 'N;'
63
+ test false, 'b:0;'
64
+ test true, 'b:1;'
65
+ test 42, 'i:42;'
66
+ test -42, 'i:-42;'
67
+ test 2147483647, "i:2147483647;", :name => 'Max Fixnum'
68
+ test -2147483648, "i:-2147483648;", :name => 'Min Fixnum'
69
+ test 4.2, 'd:4.2;'
70
+ test 'test', 's:4:"test";'
71
+ test :test, 's:4:"test";', :name => 'Symbol'
72
+ test "\"\n\t\"", "s:4:\"\"\n\t\"\";", :name => 'Complex string'
73
+ test [nil, true, false, 42, 4.2, 'test'], 'a:6:{i:0;N;i:1;b:1;i:2;b:0;i:3;i:42;i:4;d:4.2;i:5;s:4:"test";}',
74
+ :name => 'Array'
75
+ test({'foo' => 'bar', 4 => [5,4,3,2]}, 'a:2:{s:3:"foo";s:3:"bar";i:4;a:4:{i:0;i:5;i:1;i:4;i:2;i:3;i:3;i:2;}}', :name => 'Hash')
76
+ test TestStruct.new("Foo", 65), 'O:10:"teststruct":2:{s:4:"name";s:3:"Foo";s:5:"value";i:65;}',
77
+ :name => 'Struct'
78
+ test TestClass.new("Foo", 65), 'O:9:"testclass":2:{s:4:"name";s:3:"Foo";s:5:"value";i:65;}',
79
+ :name => 'Class'
80
+
81
+ # PHP counts multibyte string, not string length
82
+ def test_multibyte_string
83
+ assert_equal "s:6:\"öäü\";", PHP.serialize("öäü")
84
+ end
85
+ # Verify assoc is passed down calls.
86
+ # Slightly awkward because hashes don't guarantee order.
87
+ def test_assoc
88
+ assert_nothing_raised do
89
+ ruby = {'foo' => ['bar','baz'], 'hash' => {'hash' => 'smoke'}}
90
+ ruby_assoc = [['foo', ['bar','baz']], ['hash', [['hash','smoke']]]]
91
+ phps = [
92
+ 'a:2:{s:4:"hash";a:1:{s:4:"hash";s:5:"smoke";}s:3:"foo";a:2:{i:0;s:3:"bar";i:1;s:3:"baz";}}',
93
+ 'a:2:{s:3:"foo";a:2:{i:0;s:3:"bar";i:1;s:3:"baz";}s:4:"hash";a:1:{s:4:"hash";s:5:"smoke";}}'
94
+ ]
95
+ serialized = PHP.serialize(ruby, true)
96
+ assert phps.include?(serialized)
97
+ unserialized = PHP.unserialize(serialized, true)
98
+ assert_equal ruby_assoc.sort, unserialized.sort
99
+ end
100
+ end
101
+
102
+ def test_sessions
103
+ assert_nothing_raised do
104
+ ruby = {'session_id' => 42, 'user_data' => {'uid' => 666}}
105
+ phps = [
106
+ 'session_id|i:42;user_data|a:1:{s:3:"uid";i:666;}',
107
+ 'user_data|a:1:{s:3:"uid";i:666;}session_id|i:42;'
108
+ ]
109
+ unserialized = PHP.unserialize(phps.first)
110
+ assert_equal ruby, unserialized
111
+ serialized = PHP.serialize_session(ruby)
112
+ assert phps.include?(serialized)
113
+ end
114
+ end
115
+ end
116
+
117
+ require 'test/unit/ui/console/testrunner'
118
+ Test::Unit::UI::Console::TestRunner.run(TestPhpSerialize)
119
+
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: php-serialize4ruby
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.0
6
+ platform: ruby
7
+ authors:
8
+ - Kasper Johansen
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2012-05-06 00:00:00 +02:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: rspec
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ~>
22
+ - !ruby/object:Gem::Version
23
+ version: 2.8.0
24
+ type: :development
25
+ prerelease: false
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: rdoc
29
+ requirement: &id002 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ~>
33
+ - !ruby/object:Gem::Version
34
+ version: "3.12"
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: bundler
40
+ requirement: &id003 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: 1.0.0
46
+ type: :development
47
+ prerelease: false
48
+ version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: jeweler
51
+ requirement: &id004 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ~>
55
+ - !ruby/object:Gem::Version
56
+ version: 1.8.3
57
+ type: :development
58
+ prerelease: false
59
+ version_requirements: *id004
60
+ - !ruby/object:Gem::Dependency
61
+ name: simplecov
62
+ requirement: &id005 !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: "0"
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: *id005
71
+ description: Read the original gems description.
72
+ email: k@spernj.org
73
+ executables: []
74
+
75
+ extensions: []
76
+
77
+ extra_rdoc_files:
78
+ - LICENSE.txt
79
+ - README
80
+ - README.rdoc
81
+ files:
82
+ - .document
83
+ - .rspec
84
+ - Gemfile
85
+ - LICENSE.txt
86
+ - README
87
+ - README.rdoc
88
+ - Rakefile
89
+ - VERSION
90
+ - lib/php-serialize4ruby.rb
91
+ - lib/php_serialize.rb
92
+ - php-serialize.gemspec
93
+ - php-serialize4ruby.gemspec
94
+ - spec/php-serialize4ruby_spec.rb
95
+ - spec/spec_helper.rb
96
+ - test.rb
97
+ has_rdoc: true
98
+ homepage: http://github.com/kaspernj/php-serialize4ruby
99
+ licenses:
100
+ - MIT
101
+ post_install_message:
102
+ rdoc_options: []
103
+
104
+ require_paths:
105
+ - lib
106
+ required_ruby_version: !ruby/object:Gem::Requirement
107
+ none: false
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ hash: -4127720001001404955
112
+ segments:
113
+ - 0
114
+ version: "0"
115
+ required_rubygems_version: !ruby/object:Gem::Requirement
116
+ none: false
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: "0"
121
+ requirements: []
122
+
123
+ rubyforge_project:
124
+ rubygems_version: 1.6.2
125
+ signing_key:
126
+ specification_version: 3
127
+ summary: A fork of the original gem "php-serialize" because I have no hope of the original guy to update the gem
128
+ test_files: []
129
+