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_Parse_Deserializer */ 0024 // require_once 'Zend/Amf/Parse/Deserializer.php'; 0025 0026 /** Zend_Xml_Security */ 0027 // require_once 'Zend/Xml/Security.php'; 0028 0029 /** Zend_Amf_Parse_TypeLoader */ 0030 // require_once 'Zend/Amf/Parse/TypeLoader.php'; 0031 0032 /** 0033 * Read an AMF3 input stream and convert it into PHP data types. 0034 * 0035 * @todo readObject to handle Typed Objects 0036 * @todo readXMLStrimg to be implemented. 0037 * @todo Class could be implemented as Factory Class with each data type it's own class. 0038 * @package Zend_Amf 0039 * @subpackage Parse_Amf3 0040 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0041 * @license http://framework.zend.com/license/new-bsd New BSD License 0042 */ 0043 class Zend_Amf_Parse_Amf3_Deserializer extends Zend_Amf_Parse_Deserializer 0044 { 0045 /** 0046 * Total number of objects in the referenceObject array 0047 * @var int 0048 */ 0049 protected $_objectCount; 0050 0051 /** 0052 * An array of reference objects per amf body 0053 * @var array 0054 */ 0055 protected $_referenceObjects = array(); 0056 0057 /** 0058 * An array of reference strings per amf body 0059 * @var array 0060 */ 0061 protected $_referenceStrings = array(); 0062 0063 /** 0064 * An array of reference class definitions per body 0065 * @var array 0066 */ 0067 protected $_referenceDefinitions = array(); 0068 0069 /** 0070 * Read AMF markers and dispatch for deserialization 0071 * 0072 * Checks for AMF marker types and calls the appropriate methods 0073 * for deserializing those marker types. markers are the data type of 0074 * the following value. 0075 * 0076 * @param integer $typeMarker 0077 * @return mixed Whatever the corresponding PHP data type is 0078 * @throws Zend_Amf_Exception for unidentified marker type 0079 */ 0080 public function readTypeMarker($typeMarker = null) 0081 { 0082 if(null === $typeMarker) { 0083 $typeMarker = $this->_stream->readByte(); 0084 } 0085 0086 switch($typeMarker) { 0087 case Zend_Amf_Constants::AMF3_UNDEFINED: 0088 return null; 0089 case Zend_Amf_Constants::AMF3_NULL: 0090 return null; 0091 case Zend_Amf_Constants::AMF3_BOOLEAN_FALSE: 0092 return false; 0093 case Zend_Amf_Constants::AMF3_BOOLEAN_TRUE: 0094 return true; 0095 case Zend_Amf_Constants::AMF3_INTEGER: 0096 return $this->readInteger(); 0097 case Zend_Amf_Constants::AMF3_NUMBER: 0098 return $this->_stream->readDouble(); 0099 case Zend_Amf_Constants::AMF3_STRING: 0100 return $this->readString(); 0101 case Zend_Amf_Constants::AMF3_DATE: 0102 return $this->readDate(); 0103 case Zend_Amf_Constants::AMF3_ARRAY: 0104 return $this->readArray(); 0105 case Zend_Amf_Constants::AMF3_OBJECT: 0106 return $this->readObject(); 0107 case Zend_Amf_Constants::AMF3_XML: 0108 case Zend_Amf_Constants::AMF3_XMLSTRING: 0109 return $this->readXmlString(); 0110 case Zend_Amf_Constants::AMF3_BYTEARRAY: 0111 return $this->readString(); 0112 default: 0113 // require_once 'Zend/Amf/Exception.php'; 0114 throw new Zend_Amf_Exception('Unsupported type marker: ' . $typeMarker); 0115 } 0116 } 0117 0118 /** 0119 * Read and deserialize an integer 0120 * 0121 * AMF 3 represents smaller integers with fewer bytes using the most 0122 * significant bit of each byte. The worst case uses 32-bits 0123 * to represent a 29-bit number, which is what we would have 0124 * done with no compression. 0125 * - 0x00000000 - 0x0000007F : 0xxxxxxx 0126 * - 0x00000080 - 0x00003FFF : 1xxxxxxx 0xxxxxxx 0127 * - 0x00004000 - 0x001FFFFF : 1xxxxxxx 1xxxxxxx 0xxxxxxx 0128 * - 0x00200000 - 0x3FFFFFFF : 1xxxxxxx 1xxxxxxx 1xxxxxxx xxxxxxxx 0129 * - 0x40000000 - 0xFFFFFFFF : throw range exception 0130 * 0131 * 0x04 -> integer type code, followed by up to 4 bytes of data. 0132 * 0133 * Parsing integers on OSFlash for the AMF3 integer data format: 0134 * @link http://osflash.org/amf3/parsing_integers 0135 * @return int|float 0136 */ 0137 public function readInteger() 0138 { 0139 $count = 1; 0140 $intReference = $this->_stream->readByte(); 0141 $result = 0; 0142 while ((($intReference & 0x80) != 0) && $count < 4) { 0143 $result <<= 7; 0144 $result |= ($intReference & 0x7f); 0145 $intReference = $this->_stream->readByte(); 0146 $count++; 0147 } 0148 if ($count < 4) { 0149 $result <<= 7; 0150 $result |= $intReference; 0151 } else { 0152 // Use all 8 bits from the 4th byte 0153 $result <<= 8; 0154 $result |= $intReference; 0155 0156 // Check if the integer should be negative 0157 if (($result & 0x10000000) != 0) { 0158 //and extend the sign bit 0159 $result |= ~0xFFFFFFF; 0160 } 0161 } 0162 return $result; 0163 } 0164 0165 /** 0166 * Read and deserialize a string 0167 * 0168 * Strings can be sent as a reference to a previously 0169 * occurring String by using an index to the implicit string reference table. 0170 * Strings are encoding using UTF-8 - however the header may either 0171 * describe a string literal or a string reference. 0172 * 0173 * - string = 0x06 string-data 0174 * - string-data = integer-data [ modified-utf-8 ] 0175 * - modified-utf-8 = *OCTET 0176 * 0177 * @return String 0178 */ 0179 public function readString() 0180 { 0181 $stringReference = $this->readInteger(); 0182 0183 //Check if this is a reference string 0184 if (($stringReference & 0x01) == 0) { 0185 // reference string 0186 $stringReference = $stringReference >> 1; 0187 if ($stringReference >= count($this->_referenceStrings)) { 0188 // require_once 'Zend/Amf/Exception.php'; 0189 throw new Zend_Amf_Exception('Undefined string reference: ' . $stringReference); 0190 } 0191 // reference string found 0192 return $this->_referenceStrings[$stringReference]; 0193 } 0194 0195 $length = $stringReference >> 1; 0196 if ($length) { 0197 $string = $this->_stream->readBytes($length); 0198 $this->_referenceStrings[] = $string; 0199 } else { 0200 $string = ""; 0201 } 0202 return $string; 0203 } 0204 0205 /** 0206 * Read and deserialize a date 0207 * 0208 * Data is the number of milliseconds elapsed since the epoch 0209 * of midnight, 1st Jan 1970 in the UTC time zone. 0210 * Local time zone information is not sent to flash. 0211 * 0212 * - date = 0x08 integer-data [ number-data ] 0213 * 0214 * @return Zend_Date 0215 */ 0216 public function readDate() 0217 { 0218 $dateReference = $this->readInteger(); 0219 if (($dateReference & 0x01) == 0) { 0220 $dateReference = $dateReference >> 1; 0221 if ($dateReference>=count($this->_referenceObjects)) { 0222 // require_once 'Zend/Amf/Exception.php'; 0223 throw new Zend_Amf_Exception('Undefined date reference: ' . $dateReference); 0224 } 0225 return $this->_referenceObjects[$dateReference]; 0226 } 0227 0228 $timestamp = floor($this->_stream->readDouble() / 1000); 0229 0230 // require_once 'Zend/Date.php'; 0231 $dateTime = new Zend_Date($timestamp); 0232 $this->_referenceObjects[] = $dateTime; 0233 return $dateTime; 0234 } 0235 0236 /** 0237 * Read amf array to PHP array 0238 * 0239 * - array = 0x09 integer-data ( [ 1OCTET *amf3-data ] | [OCTET *amf3-data 1] | [ OCTET *amf-data ] ) 0240 * 0241 * @return array 0242 */ 0243 public function readArray() 0244 { 0245 $arrayReference = $this->readInteger(); 0246 if (($arrayReference & 0x01)==0){ 0247 $arrayReference = $arrayReference >> 1; 0248 if ($arrayReference>=count($this->_referenceObjects)) { 0249 // require_once 'Zend/Amf/Exception.php'; 0250 throw new Zend_Amf_Exception('Unknow array reference: ' . $arrayReference); 0251 } 0252 return $this->_referenceObjects[$arrayReference]; 0253 } 0254 0255 // Create a holder for the array in the reference list 0256 $data = array(); 0257 $this->_referenceObjects[] =& $data; 0258 $key = $this->readString(); 0259 0260 // Iterating for string based keys. 0261 while ($key != '') { 0262 $data[$key] = $this->readTypeMarker(); 0263 $key = $this->readString(); 0264 } 0265 0266 $arrayReference = $arrayReference >>1; 0267 0268 //We have a dense array 0269 for ($i=0; $i < $arrayReference; $i++) { 0270 $data[] = $this->readTypeMarker(); 0271 } 0272 0273 return $data; 0274 } 0275 0276 /** 0277 * Read an object from the AMF stream and convert it into a PHP object 0278 * 0279 * @todo Rather than using an array of traitsInfo create Zend_Amf_Value_TraitsInfo 0280 * @return object|array 0281 */ 0282 public function readObject() 0283 { 0284 $traitsInfo = $this->readInteger(); 0285 $storedObject = ($traitsInfo & 0x01)==0; 0286 $traitsInfo = $traitsInfo >> 1; 0287 0288 // Check if the Object is in the stored Objects reference table 0289 if ($storedObject) { 0290 $ref = $traitsInfo; 0291 if (!isset($this->_referenceObjects[$ref])) { 0292 // require_once 'Zend/Amf/Exception.php'; 0293 throw new Zend_Amf_Exception('Unknown Object reference: ' . $ref); 0294 } 0295 $returnObject = $this->_referenceObjects[$ref]; 0296 } else { 0297 // Check if the Object is in the stored Definitions reference table 0298 $storedClass = ($traitsInfo & 0x01) == 0; 0299 $traitsInfo = $traitsInfo >> 1; 0300 if ($storedClass) { 0301 $ref = $traitsInfo; 0302 if (!isset($this->_referenceDefinitions[$ref])) { 0303 // require_once 'Zend/Amf/Exception.php'; 0304 throw new Zend_Amf_Exception('Unknows Definition reference: '. $ref); 0305 } 0306 // Populate the reference attributes 0307 $className = $this->_referenceDefinitions[$ref]['className']; 0308 $encoding = $this->_referenceDefinitions[$ref]['encoding']; 0309 $propertyNames = $this->_referenceDefinitions[$ref]['propertyNames']; 0310 } else { 0311 // The class was not in the reference tables. Start reading rawdata to build traits. 0312 // Create a traits table. Zend_Amf_Value_TraitsInfo would be ideal 0313 $className = $this->readString(); 0314 $encoding = $traitsInfo & 0x03; 0315 $propertyNames = array(); 0316 $traitsInfo = $traitsInfo >> 2; 0317 } 0318 0319 // We now have the object traits defined in variables. Time to go to work: 0320 if (!$className) { 0321 // No class name generic object 0322 $returnObject = new stdClass(); 0323 } else { 0324 // Defined object 0325 // Typed object lookup against registered classname maps 0326 if ($loader = Zend_Amf_Parse_TypeLoader::loadType($className)) { 0327 $returnObject = new $loader(); 0328 } else { 0329 //user defined typed object 0330 // require_once 'Zend/Amf/Exception.php'; 0331 throw new Zend_Amf_Exception('Typed object not found: '. $className . ' '); 0332 } 0333 } 0334 0335 // Add the Object to the reference table 0336 $this->_referenceObjects[] = $returnObject; 0337 0338 $properties = array(); // clear value 0339 // Check encoding types for additional processing. 0340 switch ($encoding) { 0341 case (Zend_Amf_Constants::ET_EXTERNAL): 0342 // Externalizable object such as {ArrayCollection} and {ObjectProxy} 0343 if (!$storedClass) { 0344 $this->_referenceDefinitions[] = array( 0345 'className' => $className, 0346 'encoding' => $encoding, 0347 'propertyNames' => $propertyNames, 0348 ); 0349 } 0350 $returnObject->externalizedData = $this->readTypeMarker(); 0351 break; 0352 case (Zend_Amf_Constants::ET_DYNAMIC): 0353 // used for Name-value encoding 0354 if (!$storedClass) { 0355 $this->_referenceDefinitions[] = array( 0356 'className' => $className, 0357 'encoding' => $encoding, 0358 'propertyNames' => $propertyNames, 0359 ); 0360 } 0361 // not a reference object read name value properties from byte stream 0362 do { 0363 $property = $this->readString(); 0364 if ($property != "") { 0365 $propertyNames[] = $property; 0366 $properties[$property] = $this->readTypeMarker(); 0367 } 0368 } while ($property !=""); 0369 break; 0370 default: 0371 // basic property list object. 0372 if (!$storedClass) { 0373 $count = $traitsInfo; // Number of properties in the list 0374 for($i=0; $i< $count; $i++) { 0375 $propertyNames[] = $this->readString(); 0376 } 0377 // Add a reference to the class. 0378 $this->_referenceDefinitions[] = array( 0379 'className' => $className, 0380 'encoding' => $encoding, 0381 'propertyNames' => $propertyNames, 0382 ); 0383 } 0384 foreach ($propertyNames as $property) { 0385 $properties[$property] = $this->readTypeMarker(); 0386 } 0387 break; 0388 } 0389 0390 // Add properties back to the return object. 0391 if (!is_array($properties)) $properties = array(); 0392 foreach($properties as $key=>$value) { 0393 if($key) { 0394 $returnObject->$key = $value; 0395 } 0396 } 0397 0398 0399 } 0400 0401 if ($returnObject instanceof Zend_Amf_Value_Messaging_ArrayCollection) { 0402 if (isset($returnObject->externalizedData)) { 0403 $returnObject = $returnObject->externalizedData; 0404 } else { 0405 $returnObject = get_object_vars($returnObject); 0406 } 0407 } 0408 0409 return $returnObject; 0410 } 0411 0412 /** 0413 * Convert XML to SimpleXml 0414 * If user wants DomDocument they can use dom_import_simplexml 0415 * 0416 * @return SimpleXml Object 0417 */ 0418 public function readXmlString() 0419 { 0420 $xmlReference = $this->readInteger(); 0421 $length = $xmlReference >> 1; 0422 $string = $this->_stream->readBytes($length); 0423 return Zend_Xml_Security::scan($string); 0424 } 0425 }