Serializer.php 13 KB

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