File Coverage

blib/lib/Flash/FLAP/IO/Serializer.pm
Criterion Covered Total %
statement 6 109 5.5
branch 0 56 0.0
condition 0 6 0.0
subroutine 2 17 11.7
pod 2 15 13.3
total 10 203 4.9


line stmt bran cond sub pod time code
1             package Flash::FLAP::IO::Serializer;
2             # Copyright (c) 2003 by Vsevolod (Simon) Ilyushchenko. All rights reserved.
3             # This program is free software; you can redistribute it and/or modify it
4             # under the same terms as Perl itself.
5             # The code is based on the -PHP project (http://amfphp.sourceforge.net/)
6              
7             =head1 NAME
8              
9             Flash::FLAP::IO::Serializer
10              
11             =head1 DESCRIPTION
12              
13             Class used to convert physical perl objects into binary data.
14              
15             =head1 CHANGES
16              
17             =head2 Sat Mar 13 16:25:00 EST 2004
18              
19             =item Patch from Tilghman Lesher that detects numbers and dates in strings
20             and sets return type accordingly.
21              
22             =item Patch from Kostas Chatzikokolakis handling encoding and sending null value.
23              
24             =head2 Sun May 11 16:43:05 EDT 2003
25              
26             =item Changed writeData to set type to "NULL" when the incoming data is undef. Previously
27             it became a String, just like other scalars.
28              
29             =item Changed PHP's writeRecordset to a generic writeAMFObject. Verified Recordset support.
30              
31             =head2 Sun Mar 9 18:20:16 EST 2003
32              
33             =item Function writeObject should return the same as writeHash. This assumes that all meaningful data
34             are stored as hash keys.
35              
36             =cut
37              
38              
39 1     1   6 use strict;
  1         1  
  1         38  
40              
41 1     1   5 use Encode qw/from_to/;
  1         1  
  1         1675  
42              
43             # holder for the data
44             my $data;
45              
46             sub new
47             {
48 0     0 0   my ($proto, $stream, $encoding) = @_;
49             # save
50 0           my $self={};
51 0           bless $self, $proto;
52 0           $self->{out} = $stream;
53 0           $self->{encoding} = $encoding;
54 0           return $self;
55             }
56              
57             sub serialize
58             {
59 0     0 0   my ($self, $d) = @_;
60 0           $self->{amfout} = $d;
61             # write the version ???
62 0           $self->{out}->writeInt(0);
63            
64             # get the header count
65 0           my $count = $self->{amfout}->numHeader();
66             # write header count
67 0           $self->{out}->writeInt($count);
68            
69 0           for (my $i=0; $i<$count; $i++)
70             {
71 0           $self->writeHeader($i);
72             }
73            
74 0           $count = $self->{amfout}->numBody();
75             # write the body count
76 0           $self->{out}->writeInt($count);
77            
78 0           for (my $i=0; $i<$count; $i++)
79             {
80             # start writing the body
81 0           $self->writeBody($i);
82             }
83             }
84              
85             sub writeHeader
86             {
87 0     0 0   my ($self, $i)=@_;
88              
89            
90             # for all header values
91             # write the header to the output stream
92             # ignoring header for now
93             }
94              
95             sub writeBody
96             {
97 0     0 0   my ($self, $i)=@_;
98 0           my $body = $self->{amfout}->getBodyAt($i);
99             # write the responseURI header
100 0           $self->{out}->writeUTF($body->{"target"});
101             # write null, haven't found another use for this
102 0           $self->{out}->writeUTF($body->{"response"});
103             # always, always there is four bytes of FF, which is -1 of course
104 0           $self->{out}->writeLong(-1);
105             # write the data to the output stream
106 0           $self->writeData($body->{"value"}, $body->{"type"});
107              
108             }
109              
110             # writes a boolean
111             sub writeBoolean
112             {
113 0     0 0   my ($self, $d)=@_;
114             # write the boolean flag
115 0           $self->{out}->writeByte(1);
116             # write the boolean byte
117 0           $self->{out}->writeByte($d);
118             }
119             # writes a string under 65536 chars, a longUTF is used and isn't complete yet
120             sub writeString
121             {
122 0     0 0   my ($self, $d)=@_;
123             # write the string code
124 0           $self->{out}->writeByte(2);
125             # write the string value
126             #$self->{out}->writeUTF(utf8_encode($d));
127 0 0         from_to($d, $self->{encoding}, "utf8") if $self->{encoding};
128 0           $self->{out}->writeUTF($d);
129             }
130              
131             sub writeXML
132             {
133 0     0 0   my ($self, $d)=@_;
134 0           $self->{out}->writeByte(15);
135             #$self->{out}->writeLongUTF(utf8_encode($d));
136 0 0         from_to($d, $self->{encoding}, "utf8") if $self->{encoding};
137 0           $self->{out}->writeLongUTF($d);
138             }
139              
140             # must be used PHPRemoting with the service to set the return type to date
141             # still needs a more in depth look at the timezone
142             sub writeDate
143             {
144 0     0 0   my ($self, $d)=@_;
145             # write date code
146 0           $self->{out}->writeByte(11);
147             # write date (milliseconds from 1970)
148 0           $self->{out}->writeDouble($d);
149             # write timezone
150             # ?? this is wierd -- put what you like and it pumps it back into flash at the current GMT ??
151             # have a look at the amf it creates...
152 0           $self->{out}->writeInt(0);
153             }
154              
155             # write a number formatted as a double with the bytes reversed
156             # this may not work on a Win machine because i believe doubles are
157             # already reversed, to fix this comment out the reversing part
158             # of the writeDouble method
159             sub writeNumber
160             {
161 0     0 0   my ($self, $d)=@_;
162             # write the number code
163 0           $self->{out}->writeByte(0);
164             # write the number as a double
165 0           $self->{out}->writeDouble($d);
166             }
167             # write null
168             sub writeNull
169             {
170 0     0 0   my ($self)=@_;
171             # null is only a 0x05 flag
172 0           $self->{out}->writeByte(5);
173             }
174              
175             # write array
176             # since everything in php is an array this includes arrays with numeric and string indexes
177             sub writeArray
178             {
179 0     0 0   my ($self, $d)=@_;
180              
181             # grab the total number of elements
182 0           my $len = scalar(@$d);
183              
184             # write the numeric array code
185 0           $self->{out}->writeByte(10);
186             # write the count of items in the array
187 0           $self->{out}->writeLong($len);
188             # write all of the array elements
189 0           for(my $i=0 ; $i < $len ; $i++)
190             {
191 0           $self->writeData($d->[$i]);
192             }
193             }
194            
195             sub writeHash
196             {
197 0     0 0   my ($self, $d) = @_;
198             # this is an object so write the object code
199 0           $self->{out}->writeByte(3);
200             # write the object name/value pairs
201 0           $self->writeObject($d);
202             }
203             # writes an object to the stream
204             sub writeObject
205             {
206 0     0 1   my ($self, $d)=@_;
207             # loop over each element
208 0           while ( my ($key, $data) = each %$d)
209             {
210             # write the name of the object
211 0           $self->{out}->writeUTF($key);
212             # write the value of the object
213 0           $self->writeData($data);
214             }
215             # write the end object flag 0x00, 0x00, 0x09
216 0           $self->{out}->writeInt(0);
217 0           $self->{out}->writeByte(9);
218             }
219              
220             # write an AMF object
221             # The difference with regular object is that the code is different
222             # and the class name is explicitly sent. Good for RecordSets.
223             sub writeAMFObject
224             {
225 0     0 0   my ($self, $object)=@_;
226             # write the custom package code
227 0           $self->{out}->writeByte(16);
228             # write the package name
229 0           $self->{out}->writeUTF($object->{_explicitType});
230             # write the package's data
231 0           $self->writeObject($object);
232             }
233              
234              
235             # main switch for dynamically determining the data type
236             # this may prove to be inadequate because perl isn't a typed
237             # language and some confusion may be encountered as we discover more data types
238             # to be passed back to flash
239              
240             #All scalars are assumed to be strings, not numbers.
241             #Regular arrays and hashes are prohibited, as they are indistinguishable outside of perl context
242             #Only arrayrefs and hashrefs will work
243              
244             # were still lacking dates, xml, and strings longer than 65536 chars
245             sub writeData
246             {
247 0     0 1   my ($self, $d, $type)=@_;
248 0 0         $type = "unknown" unless $type;
249              
250             # **************** TO DO **********************
251             # Since we are now allowing the user to determine
252             # the datatype we have to validate the user's suggestion
253             # vs. the actual data being passed and throw an error
254             # if things don't check out.!!!!
255             # **********************************************
256              
257             # get the type of the data by checking its reference name
258             #if it was not explicitly passed
259 0 0         if ($type eq "unknown")
260             {
261 0 0         if (!defined $d) # convert undef to null, but not "" or 0
262             {
263 0           $type = "NULL";
264             }
265             else
266             {
267 0           my $myRef = ref $d;
268 0 0 0       if (!$myRef || $myRef =~ "SCALAR")
    0          
    0          
269             {
270 0 0         if ($myRef) {
271 0           study $$myRef;
272 0 0 0       if ($$myRef =~ m/^(\d{4})\-(\d{2})\-(\d{2})( (\d{2}):(\d{2}):(\d{2}))?$/) {
    0          
    0          
    0          
    0          
273             # Handle "YYYY-MM-DD" and "YYYY-MM-DD HH:MM:SS"
274 0           require POSIX;
275 0 0         if ($4) {
276 0           $$myRef = POSIX::mktime($7,$6,$5,$3,$2 - 1,$1 - 1900) * 1000;
277             } else {
278 0           $$myRef = POSIX::mktime(0,0,0,$3,$2 - 1,$1 - 1900) * 1000;
279             }
280 0           $type = "date";
281             } elsif ($$myRef =~ m/[^0-9\.\-]/) {
282 0           $type = "string";
283             } elsif ($$myRef =~ m/\..*\./) {
284             # More than 1 period (e.g. IP address)
285 0           $type = "string";
286             } elsif (($$myRef =~ m/.\-/) or ($$myRef eq '-')) {
287             # negative anywhere but at the beginning
288 0           $type = "string";
289             } elsif ($$myRef =~ m/\./) {
290 0           $type = "double";
291             } else {
292 0           $type = "integer";
293             }
294             } else {
295 0           $type = "string";
296             }
297             }
298             elsif ($myRef =~ "ARRAY")
299             {
300 0           $type = "array";
301             }
302             elsif ($myRef =~ "HASH")
303             {
304 0           $type = "hash";
305             }
306             else
307             {
308 0           $type = "object";
309             }
310             }
311             }
312            
313             #BOOLEANS
314 0 0         if ($type eq "boolean")
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
315             {
316 0           $self->writeBoolean($d);
317             }
318             #STRINGS
319             elsif ($type eq "string")
320             {
321 0           $self->writeString($d);
322             }
323             # DOUBLES
324             elsif ($type eq "double")
325             {
326 0           $self->writeNumber($d);
327             }
328             # INTEGERS
329             elsif ($type eq "integer")
330             {
331 0           $self->writeNumber($d);
332             }
333             # OBJECTS
334             elsif ($type eq "object")
335             {
336 0           $self->writeHash($d);
337             }
338             # ARRAYS
339             elsif ($type eq "array")
340             {
341 0           $self->writeArray($d);
342             }
343             # HASHAS
344             elsif ($type eq "hash")
345             {
346 0           $self->writeHash($d);
347             }
348             # NULL
349             elsif ($type eq "NULL")
350             {
351 0           $self->writeNull();
352             }
353             # UDF's
354             elsif ($type eq "user function")
355             {
356            
357             }
358             elsif ($type eq "resource")
359             {
360 0           my $resource = get_resource_type($d); # determine what the resource is
361 0           $self->writeData($d, $resource); # resend with $d's specific resource type
362             }
363             # XML
364             elsif (lc($type) eq "xml")
365             {
366 0           $self->writeXML($d);
367             }
368             # Dates
369             elsif (lc($type) eq "date")
370             {
371 0           $self->writeDate($d);
372             }
373             # mysql recordset resource
374             elsif (lc($type) eq "amfobject") # resource type
375             {
376             # write the record set to the output stream
377 0           $self->writeAMFObject($d); # writes recordset formatted for Flash
378             }
379             else
380             {
381 0           print STDERR "Unsupported Datatype $type in FLAP::IO::Serializer";
382 0           die;
383             }
384            
385             }
386             1;