|
1 <?php |
|
2 /** |
|
3 * Zend Framework |
|
4 * |
|
5 * LICENSE |
|
6 * |
|
7 * This source file is subject to the new BSD license that is bundled |
|
8 * with this package in the file LICENSE.txt. |
|
9 * It is also available through the world-wide-web at this URL: |
|
10 * http://framework.zend.com/license/new-bsd |
|
11 * If you did not receive a copy of the license and are unable to |
|
12 * obtain it through the world-wide-web, please send an email |
|
13 * to license@zend.com so we can send you a copy immediately. |
|
14 * |
|
15 * @category Zend |
|
16 * @package Zend_Amf |
|
17 * @subpackage Parse_Amf0 |
|
18 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
19 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
20 * @version $Id: Serializer.php 21968 2010-04-22 03:53:34Z matthew $ |
|
21 */ |
|
22 |
|
23 /** Zend_Amf_Constants */ |
|
24 require_once 'Zend/Amf/Constants.php'; |
|
25 |
|
26 /** @see Zend_Amf_Parse_Serializer */ |
|
27 require_once 'Zend/Amf/Parse/Serializer.php'; |
|
28 |
|
29 /** |
|
30 * Serializer PHP misc types back to there corresponding AMF0 Type Marker. |
|
31 * |
|
32 * @uses Zend_Amf_Parse_Serializer |
|
33 * @package Zend_Amf |
|
34 * @subpackage Parse_Amf0 |
|
35 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
36 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
37 */ |
|
38 class Zend_Amf_Parse_Amf0_Serializer extends Zend_Amf_Parse_Serializer |
|
39 { |
|
40 /** |
|
41 * @var string Name of the class to be returned |
|
42 */ |
|
43 protected $_className = ''; |
|
44 |
|
45 /** |
|
46 * An array of reference objects |
|
47 * @var array |
|
48 */ |
|
49 protected $_referenceObjects = array(); |
|
50 |
|
51 /** |
|
52 * Determine type and serialize accordingly |
|
53 * |
|
54 * Checks to see if the type was declared and then either |
|
55 * auto negotiates the type or relies on the user defined markerType to |
|
56 * serialize the data into amf |
|
57 * |
|
58 * @param mixed $data |
|
59 * @param mixed $markerType |
|
60 * @param mixed $dataByVal |
|
61 * @return Zend_Amf_Parse_Amf0_Serializer |
|
62 * @throws Zend_Amf_Exception for unrecognized types or data |
|
63 */ |
|
64 public function writeTypeMarker(&$data, $markerType = null, $dataByVal = false) |
|
65 { |
|
66 // Workaround for PHP5 with E_STRICT enabled complaining about "Only |
|
67 // variables should be passed by reference" |
|
68 if ((null === $data) && ($dataByVal !== false)) { |
|
69 $data = &$dataByVal; |
|
70 } |
|
71 if (null !== $markerType) { |
|
72 //try to reference the given object |
|
73 if (!$this->writeObjectReference($data, $markerType)) { |
|
74 // Write the Type Marker to denote the following action script data type |
|
75 $this->_stream->writeByte($markerType); |
|
76 switch($markerType) { |
|
77 case Zend_Amf_Constants::AMF0_NUMBER: |
|
78 $this->_stream->writeDouble($data); |
|
79 break; |
|
80 case Zend_Amf_Constants::AMF0_BOOLEAN: |
|
81 $this->_stream->writeByte($data); |
|
82 break; |
|
83 case Zend_Amf_Constants::AMF0_STRING: |
|
84 $this->_stream->writeUTF($data); |
|
85 break; |
|
86 case Zend_Amf_Constants::AMF0_OBJECT: |
|
87 $this->writeObject($data); |
|
88 break; |
|
89 case Zend_Amf_Constants::AMF0_NULL: |
|
90 break; |
|
91 case Zend_Amf_Constants::AMF0_REFERENCE: |
|
92 $this->_stream->writeInt($data); |
|
93 break; |
|
94 case Zend_Amf_Constants::AMF0_MIXEDARRAY: |
|
95 // Write length of numeric keys as zero. |
|
96 $this->_stream->writeLong(0); |
|
97 $this->writeObject($data); |
|
98 break; |
|
99 case Zend_Amf_Constants::AMF0_ARRAY: |
|
100 $this->writeArray($data); |
|
101 break; |
|
102 case Zend_Amf_Constants::AMF0_DATE: |
|
103 $this->writeDate($data); |
|
104 break; |
|
105 case Zend_Amf_Constants::AMF0_LONGSTRING: |
|
106 $this->_stream->writeLongUTF($data); |
|
107 break; |
|
108 case Zend_Amf_Constants::AMF0_TYPEDOBJECT: |
|
109 $this->writeTypedObject($data); |
|
110 break; |
|
111 case Zend_Amf_Constants::AMF0_AMF3: |
|
112 $this->writeAmf3TypeMarker($data); |
|
113 break; |
|
114 default: |
|
115 require_once 'Zend/Amf/Exception.php'; |
|
116 throw new Zend_Amf_Exception("Unknown Type Marker: " . $markerType); |
|
117 } |
|
118 } |
|
119 } else { |
|
120 if (is_resource($data)) { |
|
121 $data = Zend_Amf_Parse_TypeLoader::handleResource($data); |
|
122 } |
|
123 switch (true) { |
|
124 case (is_int($data) || is_float($data)): |
|
125 $markerType = Zend_Amf_Constants::AMF0_NUMBER; |
|
126 break; |
|
127 case (is_bool($data)): |
|
128 $markerType = Zend_Amf_Constants::AMF0_BOOLEAN; |
|
129 break; |
|
130 case (is_string($data) && (strlen($data) > 65536)): |
|
131 $markerType = Zend_Amf_Constants::AMF0_LONGSTRING; |
|
132 break; |
|
133 case (is_string($data)): |
|
134 $markerType = Zend_Amf_Constants::AMF0_STRING; |
|
135 break; |
|
136 case (is_object($data)): |
|
137 if (($data instanceof DateTime) || ($data instanceof Zend_Date)) { |
|
138 $markerType = Zend_Amf_Constants::AMF0_DATE; |
|
139 } else { |
|
140 |
|
141 if($className = $this->getClassName($data)){ |
|
142 //Object is a Typed object set classname |
|
143 $markerType = Zend_Amf_Constants::AMF0_TYPEDOBJECT; |
|
144 $this->_className = $className; |
|
145 } else { |
|
146 // Object is a generic classname |
|
147 $markerType = Zend_Amf_Constants::AMF0_OBJECT; |
|
148 } |
|
149 break; |
|
150 } |
|
151 break; |
|
152 case (null === $data): |
|
153 $markerType = Zend_Amf_Constants::AMF0_NULL; |
|
154 break; |
|
155 case (is_array($data)): |
|
156 // check if it is an associative array |
|
157 $i = 0; |
|
158 foreach (array_keys($data) as $key) { |
|
159 // check if it contains non-integer keys |
|
160 if (!is_numeric($key) || intval($key) != $key) { |
|
161 $markerType = Zend_Amf_Constants::AMF0_OBJECT; |
|
162 break; |
|
163 // check if it is a sparse indexed array |
|
164 } else if ($key != $i) { |
|
165 $markerType = Zend_Amf_Constants::AMF0_MIXEDARRAY; |
|
166 break; |
|
167 } |
|
168 $i++; |
|
169 } |
|
170 // Dealing with a standard numeric array |
|
171 if(!$markerType){ |
|
172 $markerType = Zend_Amf_Constants::AMF0_ARRAY; |
|
173 break; |
|
174 } |
|
175 break; |
|
176 default: |
|
177 require_once 'Zend/Amf/Exception.php'; |
|
178 throw new Zend_Amf_Exception('Unsupported data type: ' . gettype($data)); |
|
179 } |
|
180 |
|
181 $this->writeTypeMarker($data, $markerType); |
|
182 } |
|
183 return $this; |
|
184 } |
|
185 |
|
186 /** |
|
187 * Check if the given object is in the reference table, write the reference if it exists, |
|
188 * otherwise add the object to the reference table |
|
189 * |
|
190 * @param mixed $object object reference to check for reference |
|
191 * @param $markerType AMF type of the object to write |
|
192 * @param mixed $objectByVal object to check for reference |
|
193 * @return Boolean true, if the reference was written, false otherwise |
|
194 */ |
|
195 protected function writeObjectReference(&$object, $markerType, $objectByVal = false) |
|
196 { |
|
197 // Workaround for PHP5 with E_STRICT enabled complaining about "Only |
|
198 // variables should be passed by reference" |
|
199 if ((null === $object) && ($objectByVal !== false)) { |
|
200 $object = &$objectByVal; |
|
201 } |
|
202 |
|
203 if ($markerType == Zend_Amf_Constants::AMF0_OBJECT |
|
204 || $markerType == Zend_Amf_Constants::AMF0_MIXEDARRAY |
|
205 || $markerType == Zend_Amf_Constants::AMF0_ARRAY |
|
206 || $markerType == Zend_Amf_Constants::AMF0_TYPEDOBJECT |
|
207 ) { |
|
208 $ref = array_search($object, $this->_referenceObjects, true); |
|
209 //handle object reference |
|
210 if($ref !== false){ |
|
211 $this->writeTypeMarker($ref,Zend_Amf_Constants::AMF0_REFERENCE); |
|
212 return true; |
|
213 } |
|
214 |
|
215 $this->_referenceObjects[] = $object; |
|
216 } |
|
217 |
|
218 return false; |
|
219 } |
|
220 |
|
221 /** |
|
222 * Write a PHP array with string or mixed keys. |
|
223 * |
|
224 * @param object $data |
|
225 * @return Zend_Amf_Parse_Amf0_Serializer |
|
226 */ |
|
227 public function writeObject($object) |
|
228 { |
|
229 // Loop each element and write the name of the property. |
|
230 foreach ($object as $key => &$value) { |
|
231 // skip variables starting with an _ private transient |
|
232 if( $key[0] == "_") continue; |
|
233 $this->_stream->writeUTF($key); |
|
234 $this->writeTypeMarker($value); |
|
235 } |
|
236 |
|
237 // Write the end object flag |
|
238 $this->_stream->writeInt(0); |
|
239 $this->_stream->writeByte(Zend_Amf_Constants::AMF0_OBJECTTERM); |
|
240 return $this; |
|
241 } |
|
242 |
|
243 /** |
|
244 * Write a standard numeric array to the output stream. If a mixed array |
|
245 * is encountered call writeTypeMarker with mixed array. |
|
246 * |
|
247 * @param array $array |
|
248 * @return Zend_Amf_Parse_Amf0_Serializer |
|
249 */ |
|
250 public function writeArray(&$array) |
|
251 { |
|
252 $length = count($array); |
|
253 if (!$length < 0) { |
|
254 // write the length of the array |
|
255 $this->_stream->writeLong(0); |
|
256 } else { |
|
257 // Write the length of the numeric array |
|
258 $this->_stream->writeLong($length); |
|
259 for ($i=0; $i<$length; $i++) { |
|
260 $value = isset($array[$i]) ? $array[$i] : null; |
|
261 $this->writeTypeMarker($value); |
|
262 } |
|
263 } |
|
264 return $this; |
|
265 } |
|
266 |
|
267 /** |
|
268 * Convert the DateTime into an AMF Date |
|
269 * |
|
270 * @param DateTime|Zend_Date $data |
|
271 * @return Zend_Amf_Parse_Amf0_Serializer |
|
272 */ |
|
273 public function writeDate($data) |
|
274 { |
|
275 if ($data instanceof DateTime) { |
|
276 $dateString = $data->format('U'); |
|
277 } elseif ($data instanceof Zend_Date) { |
|
278 $dateString = $data->toString('U'); |
|
279 } else { |
|
280 require_once 'Zend/Amf/Exception.php'; |
|
281 throw new Zend_Amf_Exception('Invalid date specified; must be a DateTime or Zend_Date object'); |
|
282 } |
|
283 $dateString *= 1000; |
|
284 |
|
285 // Make the conversion and remove milliseconds. |
|
286 $this->_stream->writeDouble($dateString); |
|
287 |
|
288 // Flash does not respect timezone but requires it. |
|
289 $this->_stream->writeInt(0); |
|
290 |
|
291 return $this; |
|
292 } |
|
293 |
|
294 /** |
|
295 * Write a class mapped object to the output stream. |
|
296 * |
|
297 * @param object $data |
|
298 * @return Zend_Amf_Parse_Amf0_Serializer |
|
299 */ |
|
300 public function writeTypedObject($data) |
|
301 { |
|
302 $this->_stream->writeUTF($this->_className); |
|
303 $this->writeObject($data); |
|
304 return $this; |
|
305 } |
|
306 |
|
307 /** |
|
308 * Encountered and AMF3 Type Marker use AMF3 serializer. Once AMF3 is |
|
309 * encountered it will not return to AMf0. |
|
310 * |
|
311 * @param string $data |
|
312 * @return Zend_Amf_Parse_Amf0_Serializer |
|
313 */ |
|
314 public function writeAmf3TypeMarker(&$data) |
|
315 { |
|
316 require_once 'Zend/Amf/Parse/Amf3/Serializer.php'; |
|
317 $serializer = new Zend_Amf_Parse_Amf3_Serializer($this->_stream); |
|
318 $serializer->writeTypeMarker($data); |
|
319 return $this; |
|
320 } |
|
321 |
|
322 /** |
|
323 * Find if the class name is a class mapped name and return the |
|
324 * respective classname if it is. |
|
325 * |
|
326 * @param object $object |
|
327 * @return false|string $className |
|
328 */ |
|
329 protected function getClassName($object) |
|
330 { |
|
331 require_once 'Zend/Amf/Parse/TypeLoader.php'; |
|
332 //Check to see if the object is a typed object and we need to change |
|
333 $className = ''; |
|
334 switch (true) { |
|
335 // the return class mapped name back to actionscript class name. |
|
336 case Zend_Amf_Parse_TypeLoader::getMappedClassName(get_class($object)): |
|
337 $className = Zend_Amf_Parse_TypeLoader::getMappedClassName(get_class($object)); |
|
338 break; |
|
339 // Check to see if the user has defined an explicit Action Script type. |
|
340 case isset($object->_explicitType): |
|
341 $className = $object->_explicitType; |
|
342 break; |
|
343 // Check if user has defined a method for accessing the Action Script type |
|
344 case method_exists($object, 'getASClassName'): |
|
345 $className = $object->getASClassName(); |
|
346 break; |
|
347 // No return class name is set make it a generic object |
|
348 case ($object instanceof stdClass): |
|
349 $className = ''; |
|
350 break; |
|
351 // By default, use object's class name |
|
352 default: |
|
353 $className = get_class($object); |
|
354 break; |
|
355 } |
|
356 if(!$className == '') { |
|
357 return $className; |
|
358 } else { |
|
359 return false; |
|
360 } |
|
361 } |
|
362 } |