1 package PHP::Serialization;
5 use Scalar::Util qw/blessed/;
6 use Carp qw(croak confess carp);
9 use vars qw/$VERSION @ISA @EXPORT_OK/;
14 @EXPORT_OK = qw(unserialize serialize);
18 PHP::Serialization - simple flexible means of converting the output of PHP's serialize() into the equivalent Perl memory structure, and vice versa.
22 use PHP::Serialization qw(serialize unserialize);
23 my $encoded = serialize({ a => 1, b => 2});
24 my $hashref = unserialize($encoded);
31 Provides a simple, quick means of serializing perl memory structures (including object data!) into a format that PHP can deserialize() and access, and vice versa.
33 NOTE: Converts PHP arrays into Perl Arrays when the PHP array used exclusively numeric indexes, and into Perl Hashes then the PHP array did not.
39 my $self = bless {}, blessed($class) ? blessed($class) : $class;
45 Exportable functions..
49 =head2 serialize($var,[optional $asString,[optional $sortHashes]])
51 Serializes the memory structure pointed to by $var, and returns a scalar value of encoded data.
53 If the optional $asString is true, $var will be encoded as string if it is double or float.
55 If the optional $sortHashes is true, all hashes will be sorted before serialization.
57 NOTE: Will recursively encode objects, hashes, arrays, etc.
64 return __PACKAGE__->new->encode(@_);
67 =head2 unserialize($encoded,[optional CLASS])
69 Deserializes the encoded data in $encoded, and returns a value (be it a hashref, arrayref, scalar, etc)
70 representing the data structure serialized in $encoded_string.
72 If the optional CLASS is specified, any objects are blessed into CLASS::$serialized_class. Otherwise, O
73 bjects are blessed into PHP::Serialization::Object::$serialized_class. (which has no methods)
80 return __PACKAGE__->new->decode(@_);
85 Functionality available if using the object interface..
89 =head2 decode($encoded_string,[optional CLASS])
91 Deserializes the encoded data in $encoded, and returns a value (be it a hashref, arrayref, scalar, etc)
92 representing the data structure serialized in $encoded_string.
94 If the optional CLASS is specified, any objects are blessed into CLASS::$serialized_class. Otherwise,
95 Objects are blessed into PHP::Serialization::Object::$serialized_class. (which has no methods)
97 SEE ALSO: unserialize()
104 my ($self, $string, $class, $shash) = @_;
105 $sorthash=$shash if defined($shash);
108 $self->{string} = \$string;
109 $self->{cursor} = \$cursor;
110 $self->{strlen} = length($string);
112 if ( defined $class ) {
113 $self->{class} = $class;
116 $self->{class} = 'PHP::Serialization::Object';
119 # Ok, start parsing...
120 my @values = $self->_parse();
122 # Ok, we SHOULD only have one value..
123 if ( $#values == -1 ) {
127 elsif ( $#values == 0 ) {
128 # Ok, return our one value..
132 # Ok, return a reference to the list.
136 } # End of decode sub.
150 my $elemcount = shift;
151 my $cursor = $self->{cursor};
152 my $string = $self->{string};
153 my $strlen = $self->{strlen};
154 confess("No cursor") unless $cursor;
155 confess("No string") unless $string;
156 confess("No strlen") unless $strlen;
159 my @shash_arr = ('some') if (($sorthash) and (ref($sorthash) eq 'HASH'));
161 $self->_skipchar('{');
162 foreach my $i (1..$elemcount*2) {
163 push(@elems,$self->_parse_elem);
164 if (($i % 2) and (@shash_arr)) {
165 $shash_arr[0]= ((($i-1)/2) eq $elems[$#elems])? 'array' : 'hash' unless ($shash_arr[0] eq 'hash');
166 push(@shash_arr,$elems[$#elems]);
169 $self->_skipchar('}');
170 push(@elems,\@shash_arr) if (@shash_arr);
176 my $cursor = $self->{cursor};
177 my $string = $self->{string};
178 my $strlen = $self->{strlen};
182 my $type_c = $self->_readchar();
183 my $type = $type_table{$type_c};
184 if (!defined $type) {
185 croak("ERROR: Unknown type $type_c.");
188 if ( $type eq 'object' ) {
189 $self->_skipchar(':');
190 # Ok, get our name count...
191 my $namelen = $self->_readnum();
192 $self->_skipchar(':');
194 # Ok, get our object name...
195 $self->_skipchar('"');
196 my $name = $self->_readstr($namelen);
197 $self->_skipchar('"');
199 # Ok, our sub elements...
200 $self->_skipchar(':');
201 my $elemcount = $self->_readnum();
202 $self->_skipchar(':');
204 my %value = $self->_parse_array($elemcount);
207 # TODO: Support for objecttypes
208 return bless(\%value, $self->{class} . '::' . $name);
209 } elsif ( $type eq 'array' ) {
210 $self->_skipchar(':');
211 # Ok, our sub elements...
212 my $elemcount = $self->_readnum();
213 $self->_skipchar(':');
215 my @values = $self->_parse_array($elemcount);
216 # If every other key is not numeric, map to a hash..
217 my $subtype = 'array';
219 my @shash_arr=@{pop(@values)} if (ref($sorthash) eq 'HASH');
220 foreach ( 0..$#values ) {
222 push(@newlist, $values[$_]);
224 } elsif (($_ / 2) ne $values[$_]) {
228 if ( $values[$_] !~ /^\d+$/ ) {
233 if ( $subtype eq 'array' ) {
237 # Ok, force into hash..
239 ${$sorthash}{\%hash}=@shash_arr if ((ref($sorthash) eq 'HASH') and @shash_arr and (shift(@shash_arr) ne 'array'));
243 elsif ( $type eq 'scalar' ) {
244 $self->_skipchar(':');
245 # Ok, get our string size count...
246 my $strlen = $self->_readnum;
247 $self->_skipchar(':');
249 $self->_skipchar('"');
250 my $string = $self->_readstr($strlen);
251 $self->_skipchar('"');
252 $self->_skipchar(';');
255 elsif ( $type eq 'integer' || $type eq 'float' ) {
256 $self->_skipchar(':');
257 # Ok, read the value..
258 my $val = $self->_readnum;
259 if ( $type eq 'integer' ) { $val = int($val); }
260 $self->_skipchar(';');
263 elsif ( $type eq 'boolean' ) {
264 $self->_skipchar(':');
265 # Ok, read our boolen value..
266 my $bool = $self->_readchar;
274 elsif ( $type eq 'undef' ) {
275 $self->_skipchar(';');
279 confess "Unknown element type '$type' found! (cursor $$cursor)";
287 my $cursor = $self->{cursor};
288 my $string = $self->{string};
289 my $strlen = $self->{strlen};
290 confess("No cursor") unless $cursor;
291 confess("No string") unless $string;
292 confess("No strlen") unless $strlen;
294 push(@elems,$self->_parse_elem);
296 # warn if we have unused chars
297 if ($$cursor != $strlen) {
298 carp("WARN: Unused characters in string after $$cursor.");
305 my ($self, $length) = @_;
306 my $string = $self->{string};
307 my $cursor = $self->{cursor};
308 if ($$cursor + $length > length($$string)) {
309 croak("ERROR: Read past end of string. Want $length after $$cursor. (".$$string.")");
311 my $str = substr($$string, $$cursor, $length);
319 return $self->_readstr(1);
323 # Reads in a character at a time until we run out of numbers to read...
325 my $cursor = $self->{cursor};
329 my $char = $self->_readchar;
330 if ( $char !~ /^[\d\.-]+$/ ) {
343 my $c = $self->_readchar();
344 if (($want)&&($c ne $want)) {
345 my $cursor = $self->{cursor};
346 my $str = $self->{string};
347 croak("ERROR: Wrong char $c, expected $want at position ".$$cursor." (".$$str.")");
349 print "_skipchar: WRONG char $c ($want)\n" if (($want)&&($c ne $want));
350 # ${$$self{cursor}}++;
351 } # Move our cursor one bytes ahead...
354 =head2 encode($reference,[optional $asString,[optional $sortHashes]])
356 Serializes the memory structure pointed to by $reference, and returns a scalar value of encoded data.
358 If the optional $asString is true, $reference will be encoded as string if it is double or float.
360 If the optional $sortHashes is true, all hashes will be sorted before serialization.
362 NOTE: Will recursively encode objects, hashes, arrays, etc.
364 SEE ALSO: serialize()
369 my ($self, $val, $iskey, $shash) = @_;
370 $iskey=0 unless defined $iskey;
371 $sorthash=$shash if defined $shash;
373 if ( ! defined $val ) {
374 return $self->_encode('null', $val);
376 elsif ( blessed $val ) {
377 return $self->_encode('obj', $val);
379 elsif ( ! ref($val) ) {
380 if ( $val =~ /^-?(?:[0-9]|[1-9]\d{1,10})$/ && abs($val) < 2**31 ) {
381 return $self->_encode('int', $val);
383 elsif ( $val =~ /^-?\d+\.\d*$/ && !$iskey) {
384 return $self->_encode('float', $val);
387 return $self->_encode('string', $val);
391 my $type = ref($val);
392 if ($type eq 'HASH' || $type eq 'ARRAY' ) {
393 return $self->_sort_hash_encode($val) if (($sorthash) and ($type eq 'HASH'));
394 return $self->_encode('array', $val);
397 confess "I can't serialize data of type '$type'!";
402 sub _sort_hash_encode {
403 my ($self, $val) = @_;
406 my @hsort = ((ref($sorthash) eq 'HASH') and (ref(${$sorthash}{$val}) eq 'ARRAY')) ? ${$sorthash}{$val} : sort keys %{$val};
407 $buffer .= sprintf('a:%d:',scalar(@hsort)) . '{';
409 $buffer .= $self->encode($_,1);
410 $buffer .= $self->encode($$val{$_});
417 my ($self, $type, $val) = @_;
420 if ( $type eq 'null' ) {
423 elsif ( $type eq 'int' ) {
424 $buffer .= sprintf('i:%d;', $val);
426 elsif ( $type eq 'float' ) {
427 $buffer .= sprintf('d:%s;', $val);
429 elsif ( $type eq 'string' ) {
430 $buffer .= sprintf('s:%d:"%s";', length($val), $val);
432 elsif ( $type eq 'array' ) {
433 if ( ref($val) eq 'ARRAY' ) {
434 $buffer .= sprintf('a:%d:',($#{$val}+1)) . '{';
436 $buffer .= $self->encode($_);
437 $buffer .= $self->encode($$val[$_]);
442 $buffer .= sprintf('a:%d:',scalar(keys(%{$val}))) . '{';
443 while ( my ($key, $value) = each(%{$val}) ) {
444 $buffer .= $self->encode($key,1);
445 $buffer .= $self->encode($value);
450 elsif ( $type eq 'obj' ) {
451 my $class = ref($val);
454 $buffer .= sprintf('O:%d:"%s":%d:', length($subclass), $subclass, scalar(keys %{$val})) . '{';
455 foreach ( %{$val} ) {
456 $buffer .= $self->encode($_);
461 confess "Unknown encode type!";
469 Support diffrent object types
471 =head1 AUTHOR INFORMATION
473 Copyright (c) 2003 Jesse Brown <jbrown@cpan.org>. All rights reserved. This program is free software;
474 you can redistribute it and/or modify it under the same terms as Perl itself.
476 Various patches contributed by assorted authors on rt.cpan.org (as detailed in Changes file).
478 Currently maintained by Tomas Doran <bobtfish@bobtfish.net>.
480 Rewritten to solve all known bugs by Bjørn-Olav Strand <bolav@cpan.org>
484 package PHP::Serialization::Object;