File indexing completed on 2024-12-29 05:27:26
0001 <?php 0002 /** 0003 * Zend Framework 0004 * 0005 * LICENSE 0006 * 0007 * This source file is subject to the new BSD license that is bundled 0008 * with this package in the file LICENSE.txt. 0009 * It is also available through the world-wide-web at this URL: 0010 * http://framework.zend.com/license/new-bsd 0011 * If you did not receive a copy of the license and are unable to 0012 * obtain it through the world-wide-web, please send an email 0013 * to license@zend.com so we can send you a copy immediately. 0014 * 0015 * @category Zend 0016 * @package Zend_Amf 0017 * @subpackage Parse_Amf3 0018 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0019 * @license http://framework.zend.com/license/new-bsd New BSD License 0020 * @version $Id$ 0021 */ 0022 0023 /** Zend_Amf_Constants */ 0024 // require_once 'Zend/Amf/Constants.php'; 0025 0026 0027 /** Zend_Amf_Parse_Serializer */ 0028 // require_once 'Zend/Amf/Parse/Serializer.php'; 0029 0030 /** Zend_Amf_Parse_TypeLoader */ 0031 // require_once 'Zend/Amf/Parse/TypeLoader.php'; 0032 0033 /** 0034 * Detect PHP object type and convert it to a corresponding AMF3 object type 0035 * 0036 * @package Zend_Amf 0037 * @subpackage Parse_Amf3 0038 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0039 * @license http://framework.zend.com/license/new-bsd New BSD License 0040 */ 0041 class Zend_Amf_Parse_Amf3_Serializer extends Zend_Amf_Parse_Serializer 0042 { 0043 /** 0044 * A constant empty string 0045 * @var string 0046 */ 0047 protected $_strEmpty = ''; 0048 0049 /** 0050 * An array of reference objects per amf body 0051 * @var array 0052 */ 0053 protected $_referenceObjects = array(); 0054 0055 /** 0056 * An array of reference strings per amf body 0057 * @var array 0058 */ 0059 protected $_referenceStrings = array(); 0060 0061 /** 0062 * An array of reference class definitions, indexed by classname 0063 * @var array 0064 */ 0065 protected $_referenceDefinitions = array(); 0066 0067 /** 0068 * Serialize PHP types to AMF3 and write to stream 0069 * 0070 * Checks to see if the type was declared and then either 0071 * auto negotiates the type or use the user defined markerType to 0072 * serialize the data from php back to AMF3 0073 * 0074 * @param mixed $data 0075 * @param int $markerType 0076 * @param mixed $dataByVal 0077 * @return void 0078 */ 0079 public function writeTypeMarker(&$data, $markerType = null, $dataByVal = false) 0080 { 0081 // Workaround for PHP5 with E_STRICT enabled complaining about "Only 0082 // variables should be passed by reference" 0083 if ((null === $data) && ($dataByVal !== false)) { 0084 $data = &$dataByVal; 0085 } 0086 if (null !== $markerType) { 0087 // Write the Type Marker to denote the following action script data type 0088 $this->_stream->writeByte($markerType); 0089 0090 switch ($markerType) { 0091 case Zend_Amf_Constants::AMF3_NULL: 0092 break; 0093 case Zend_Amf_Constants::AMF3_BOOLEAN_FALSE: 0094 break; 0095 case Zend_Amf_Constants::AMF3_BOOLEAN_TRUE: 0096 break; 0097 case Zend_Amf_Constants::AMF3_INTEGER: 0098 $this->writeInteger($data); 0099 break; 0100 case Zend_Amf_Constants::AMF3_NUMBER: 0101 $this->_stream->writeDouble($data); 0102 break; 0103 case Zend_Amf_Constants::AMF3_STRING: 0104 $this->writeString($data); 0105 break; 0106 case Zend_Amf_Constants::AMF3_DATE: 0107 $this->writeDate($data); 0108 break; 0109 case Zend_Amf_Constants::AMF3_ARRAY: 0110 $this->writeArray($data); 0111 break; 0112 case Zend_Amf_Constants::AMF3_OBJECT: 0113 $this->writeObject($data); 0114 break; 0115 case Zend_Amf_Constants::AMF3_BYTEARRAY: 0116 $this->writeByteArray($data); 0117 break; 0118 case Zend_Amf_Constants::AMF3_XMLSTRING; 0119 $this->writeXml($data); 0120 break; 0121 default: 0122 // require_once 'Zend/Amf/Exception.php'; 0123 throw new Zend_Amf_Exception('Unknown Type Marker: ' . $markerType); 0124 } 0125 } else { 0126 // Detect Type Marker 0127 if (is_resource($data)) { 0128 $data = Zend_Amf_Parse_TypeLoader::handleResource($data); 0129 } 0130 switch (true) { 0131 case (null === $data): 0132 $markerType = Zend_Amf_Constants::AMF3_NULL; 0133 break; 0134 case (is_bool($data)): 0135 if ($data){ 0136 $markerType = Zend_Amf_Constants::AMF3_BOOLEAN_TRUE; 0137 } else { 0138 $markerType = Zend_Amf_Constants::AMF3_BOOLEAN_FALSE; 0139 } 0140 break; 0141 case (is_int($data)): 0142 if (($data > 0xFFFFFFF) || ($data < -268435456)) { 0143 $markerType = Zend_Amf_Constants::AMF3_NUMBER; 0144 } else { 0145 $markerType = Zend_Amf_Constants::AMF3_INTEGER; 0146 } 0147 break; 0148 case (is_float($data)): 0149 $markerType = Zend_Amf_Constants::AMF3_NUMBER; 0150 break; 0151 case (is_string($data)): 0152 $markerType = Zend_Amf_Constants::AMF3_STRING; 0153 break; 0154 case (is_array($data)): 0155 $markerType = Zend_Amf_Constants::AMF3_ARRAY; 0156 break; 0157 case (is_object($data)): 0158 // Handle object types. 0159 if (($data instanceof DateTime) || ($data instanceof Zend_Date)) { 0160 $markerType = Zend_Amf_Constants::AMF3_DATE; 0161 } else if ($data instanceof Zend_Amf_Value_ByteArray) { 0162 $markerType = Zend_Amf_Constants::AMF3_BYTEARRAY; 0163 } else if (($data instanceof DOMDocument) || ($data instanceof SimpleXMLElement)) { 0164 $markerType = Zend_Amf_Constants::AMF3_XMLSTRING; 0165 } else { 0166 $markerType = Zend_Amf_Constants::AMF3_OBJECT; 0167 } 0168 break; 0169 default: 0170 // require_once 'Zend/Amf/Exception.php'; 0171 throw new Zend_Amf_Exception('Unsupported data type: ' . gettype($data)); 0172 } 0173 $this->writeTypeMarker($data, $markerType); 0174 } 0175 } 0176 0177 /** 0178 * Write an AMF3 integer 0179 * 0180 * @param int|float $data 0181 * @return Zend_Amf_Parse_Amf3_Serializer 0182 */ 0183 public function writeInteger($int) 0184 { 0185 if (($int & 0xffffff80) == 0) { 0186 $this->_stream->writeByte($int & 0x7f); 0187 return $this; 0188 } 0189 0190 if (($int & 0xffffc000) == 0 ) { 0191 $this->_stream->writeByte(($int >> 7 ) | 0x80); 0192 $this->_stream->writeByte($int & 0x7f); 0193 return $this; 0194 } 0195 0196 if (($int & 0xffe00000) == 0) { 0197 $this->_stream->writeByte(($int >> 14 ) | 0x80); 0198 $this->_stream->writeByte(($int >> 7 ) | 0x80); 0199 $this->_stream->writeByte($int & 0x7f); 0200 return $this; 0201 } 0202 0203 $this->_stream->writeByte(($int >> 22 ) | 0x80); 0204 $this->_stream->writeByte(($int >> 15 ) | 0x80); 0205 $this->_stream->writeByte(($int >> 8 ) | 0x80); 0206 $this->_stream->writeByte($int & 0xff); 0207 return $this; 0208 } 0209 0210 /** 0211 * Send string to output stream, without trying to reference it. 0212 * The string is prepended with strlen($string) << 1 | 0x01 0213 * 0214 * @param string $string 0215 * @return Zend_Amf_Parse_Amf3_Serializer 0216 */ 0217 protected function writeBinaryString(&$string){ 0218 $ref = ($this->_mbStringFunctionsOverloaded ? mb_strlen($string, '8bit') : strlen($string)) << 1 | 0x01; 0219 $this->writeInteger($ref); 0220 $this->_stream->writeBytes($string); 0221 0222 return $this; 0223 } 0224 0225 /** 0226 * Send string to output stream 0227 * 0228 * @param string $string 0229 * @return Zend_Amf_Parse_Amf3_Serializer 0230 */ 0231 public function writeString(&$string) 0232 { 0233 $len = $this->_mbStringFunctionsOverloaded ? mb_strlen($string, '8bit') : strlen($string); 0234 if(!$len){ 0235 $this->writeInteger(0x01); 0236 return $this; 0237 } 0238 0239 $ref = array_key_exists($string, $this->_referenceStrings) 0240 ? $this->_referenceStrings[$string] 0241 : false; 0242 if ($ref === false){ 0243 $this->_referenceStrings[$string] = count($this->_referenceStrings); 0244 $this->writeBinaryString($string); 0245 } else { 0246 $ref <<= 1; 0247 $this->writeInteger($ref); 0248 } 0249 0250 return $this; 0251 } 0252 0253 /** 0254 * Send ByteArray to output stream 0255 * 0256 * @param string|Zend_Amf_Value_ByteArray $data 0257 * @return Zend_Amf_Parse_Amf3_Serializer 0258 */ 0259 public function writeByteArray(&$data) 0260 { 0261 if ($this->writeObjectReference($data)) { 0262 return $this; 0263 } 0264 0265 if (is_string($data)) { 0266 //nothing to do 0267 } else if ($data instanceof Zend_Amf_Value_ByteArray) { 0268 $data = $data->getData(); 0269 } else { 0270 // require_once 'Zend/Amf/Exception.php'; 0271 throw new Zend_Amf_Exception('Invalid ByteArray specified; must be a string or Zend_Amf_Value_ByteArray'); 0272 } 0273 0274 $this->writeBinaryString($data); 0275 0276 return $this; 0277 } 0278 0279 /** 0280 * Send xml to output stream 0281 * 0282 * @param DOMDocument|SimpleXMLElement $xml 0283 * @return Zend_Amf_Parse_Amf3_Serializer 0284 */ 0285 public function writeXml($xml) 0286 { 0287 if ($this->writeObjectReference($xml)) { 0288 return $this; 0289 } 0290 0291 if(is_string($xml)) { 0292 //nothing to do 0293 } else if ($xml instanceof DOMDocument) { 0294 $xml = $xml->saveXml(); 0295 } else if ($xml instanceof SimpleXMLElement) { 0296 $xml = $xml->asXML(); 0297 } else { 0298 // require_once 'Zend/Amf/Exception.php'; 0299 throw new Zend_Amf_Exception('Invalid xml specified; must be a DOMDocument or SimpleXMLElement'); 0300 } 0301 0302 $this->writeBinaryString($xml); 0303 0304 return $this; 0305 } 0306 0307 /** 0308 * Convert DateTime/Zend_Date to AMF date 0309 * 0310 * @param DateTime|Zend_Date $date 0311 * @return Zend_Amf_Parse_Amf3_Serializer 0312 */ 0313 public function writeDate($date) 0314 { 0315 if ($this->writeObjectReference($date)) { 0316 return $this; 0317 } 0318 0319 if ($date instanceof DateTime) { 0320 $dateString = $date->format('U') * 1000; 0321 } elseif ($date instanceof Zend_Date) { 0322 $dateString = $date->toString('U') * 1000; 0323 } else { 0324 // require_once 'Zend/Amf/Exception.php'; 0325 throw new Zend_Amf_Exception('Invalid date specified; must be a string DateTime or Zend_Date object'); 0326 } 0327 0328 $this->writeInteger(0x01); 0329 // write time to stream minus milliseconds 0330 $this->_stream->writeDouble($dateString); 0331 return $this; 0332 } 0333 0334 /** 0335 * Write a PHP array back to the amf output stream 0336 * 0337 * @param array $array 0338 * @return Zend_Amf_Parse_Amf3_Serializer 0339 */ 0340 public function writeArray(&$array) 0341 { 0342 // arrays aren't reference here but still counted 0343 $this->_referenceObjects[] = $array; 0344 0345 // have to seperate mixed from numberic keys. 0346 $numeric = array(); 0347 $string = array(); 0348 foreach ($array as $key => &$value) { 0349 if (is_int($key)) { 0350 $numeric[] = $value; 0351 } else { 0352 $string[$key] = $value; 0353 } 0354 } 0355 0356 // write the preamble id of the array 0357 $length = count($numeric); 0358 $id = ($length << 1) | 0x01; 0359 $this->writeInteger($id); 0360 0361 //Write the mixed type array to the output stream 0362 foreach($string as $key => &$value) { 0363 $this->writeString($key) 0364 ->writeTypeMarker($value); 0365 } 0366 $this->writeString($this->_strEmpty); 0367 0368 // Write the numeric array to ouput stream 0369 foreach($numeric as &$value) { 0370 $this->writeTypeMarker($value); 0371 } 0372 return $this; 0373 } 0374 0375 /** 0376 * Check if the given object is in the reference table, write the reference if it exists, 0377 * otherwise add the object to the reference table 0378 * 0379 * @param mixed $object object reference to check for reference 0380 * @param mixed $objectByVal object to check for reference 0381 * @return Boolean true, if the reference was written, false otherwise 0382 */ 0383 protected function writeObjectReference(&$object, $objectByVal = false) 0384 { 0385 // Workaround for PHP5 with E_STRICT enabled complaining about "Only 0386 // variables should be passed by reference" 0387 if ((null === $object) && ($objectByVal !== false)) { 0388 $object = &$objectByVal; 0389 } 0390 0391 $hash = spl_object_hash($object); 0392 $ref = array_key_exists($hash, $this->_referenceObjects) 0393 ? $this->_referenceObjects[$hash] 0394 : false; 0395 0396 // quickly handle object references 0397 if ($ref !== false){ 0398 $ref <<= 1; 0399 $this->writeInteger($ref); 0400 return true; 0401 } 0402 $this->_referenceObjects[$hash] = count($this->_referenceObjects); 0403 return false; 0404 } 0405 0406 /** 0407 * Write object to ouput stream 0408 * 0409 * @param mixed $data 0410 * @return Zend_Amf_Parse_Amf3_Serializer 0411 */ 0412 public function writeObject($object) 0413 { 0414 if($this->writeObjectReference($object)){ 0415 return $this; 0416 } 0417 0418 $className = ''; 0419 0420 //Check to see if the object is a typed object and we need to change 0421 switch (true) { 0422 // the return class mapped name back to actionscript class name. 0423 case ($className = Zend_Amf_Parse_TypeLoader::getMappedClassName(get_class($object))): 0424 break; 0425 0426 // Check to see if the user has defined an explicit Action Script type. 0427 case isset($object->_explicitType): 0428 $className = $object->_explicitType; 0429 break; 0430 0431 // Check if user has defined a method for accessing the Action Script type 0432 case method_exists($object, 'getASClassName'): 0433 $className = $object->getASClassName(); 0434 break; 0435 0436 // No return class name is set make it a generic object 0437 case ($object instanceof stdClass): 0438 $className = ''; 0439 break; 0440 0441 // By default, use object's class name 0442 default: 0443 $className = get_class($object); 0444 break; 0445 } 0446 0447 $writeTraits = true; 0448 0449 //check to see, if we have a corresponding definition 0450 if(array_key_exists($className, $this->_referenceDefinitions)){ 0451 $traitsInfo = $this->_referenceDefinitions[$className]['id']; 0452 $encoding = $this->_referenceDefinitions[$className]['encoding']; 0453 $propertyNames = $this->_referenceDefinitions[$className]['propertyNames']; 0454 0455 $traitsInfo = ($traitsInfo << 2) | 0x01; 0456 0457 $writeTraits = false; 0458 } else { 0459 $propertyNames = array(); 0460 0461 if($className == ''){ 0462 //if there is no className, we interpret the class as dynamic without any sealed members 0463 $encoding = Zend_Amf_Constants::ET_DYNAMIC; 0464 } else { 0465 $encoding = Zend_Amf_Constants::ET_PROPLIST; 0466 0467 foreach($object as $key => $value) { 0468 if( $key[0] != "_") { 0469 $propertyNames[] = $key; 0470 } 0471 } 0472 } 0473 0474 $this->_referenceDefinitions[$className] = array( 0475 'id' => count($this->_referenceDefinitions), 0476 'encoding' => $encoding, 0477 'propertyNames' => $propertyNames, 0478 ); 0479 0480 $traitsInfo = Zend_Amf_Constants::AMF3_OBJECT_ENCODING; 0481 $traitsInfo |= $encoding << 2; 0482 $traitsInfo |= (count($propertyNames) << 4); 0483 } 0484 0485 $this->writeInteger($traitsInfo); 0486 0487 if($writeTraits){ 0488 $this->writeString($className); 0489 foreach ($propertyNames as $value) { 0490 $this->writeString($value); 0491 } 0492 } 0493 0494 try { 0495 switch($encoding) { 0496 case Zend_Amf_Constants::ET_PROPLIST: 0497 //Write the sealed values to the output stream. 0498 foreach ($propertyNames as $key) { 0499 $this->writeTypeMarker($object->$key); 0500 } 0501 break; 0502 case Zend_Amf_Constants::ET_DYNAMIC: 0503 //Write the sealed values to the output stream. 0504 foreach ($propertyNames as $key) { 0505 $this->writeTypeMarker($object->$key); 0506 } 0507 0508 //Write remaining properties 0509 foreach($object as $key => $value){ 0510 if(!in_array($key,$propertyNames) && $key[0] != "_"){ 0511 $this->writeString($key); 0512 $this->writeTypeMarker($value); 0513 } 0514 } 0515 0516 //Write an empty string to end the dynamic part 0517 $this->writeString($this->_strEmpty); 0518 break; 0519 case Zend_Amf_Constants::ET_EXTERNAL: 0520 // require_once 'Zend/Amf/Exception.php'; 0521 throw new Zend_Amf_Exception('External Object Encoding not implemented'); 0522 break; 0523 default: 0524 // require_once 'Zend/Amf/Exception.php'; 0525 throw new Zend_Amf_Exception('Unknown Object Encoding type: ' . $encoding); 0526 } 0527 } catch (Exception $e) { 0528 // require_once 'Zend/Amf/Exception.php'; 0529 throw new Zend_Amf_Exception('Unable to writeObject output: ' . $e->getMessage(), 0, $e); 0530 } 0531 0532 return $this; 0533 } 0534 }