_stream->readByte(); } switch($typeMarker) { case Zend_Amf_Constants::AMF3_UNDEFINED: return null; case Zend_Amf_Constants::AMF3_NULL: return null; case Zend_Amf_Constants::AMF3_BOOLEAN_FALSE: return false; case Zend_Amf_Constants::AMF3_BOOLEAN_TRUE: return true; case Zend_Amf_Constants::AMF3_INTEGER: return $this->readInteger(); case Zend_Amf_Constants::AMF3_NUMBER: return $this->_stream->readDouble(); case Zend_Amf_Constants::AMF3_STRING: return $this->readString(); case Zend_Amf_Constants::AMF3_DATE: return $this->readDate(); case Zend_Amf_Constants::AMF3_ARRAY: return $this->readArray(); case Zend_Amf_Constants::AMF3_OBJECT: return $this->readObject(); case Zend_Amf_Constants::AMF3_XML: case Zend_Amf_Constants::AMF3_XMLSTRING: return $this->readXmlString(); case Zend_Amf_Constants::AMF3_BYTEARRAY: return $this->readString(); default: require_once 'Zend/Amf/Exception.php'; throw new Zend_Amf_Exception('Unsupported type marker: ' . $typeMarker); } } /** * Read and deserialize an integer * * AMF 3 represents smaller integers with fewer bytes using the most * significant bit of each byte. The worst case uses 32-bits * to represent a 29-bit number, which is what we would have * done with no compression. * - 0x00000000 - 0x0000007F : 0xxxxxxx * - 0x00000080 - 0x00003FFF : 1xxxxxxx 0xxxxxxx * - 0x00004000 - 0x001FFFFF : 1xxxxxxx 1xxxxxxx 0xxxxxxx * - 0x00200000 - 0x3FFFFFFF : 1xxxxxxx 1xxxxxxx 1xxxxxxx xxxxxxxx * - 0x40000000 - 0xFFFFFFFF : throw range exception * * * 0x04 -> integer type code, followed by up to 4 bytes of data. * * @see: Parsing integers on OSFlash {http://osflash.org/amf3/parsing_integers>} for the AMF3 integer data format. * @return int|float */ public function readInteger() { $count = 1; $intReference = $this->_stream->readByte(); $result = 0; while ((($intReference & 0x80) != 0) && $count < 4) { $result <<= 7; $result |= ($intReference & 0x7f); $intReference = $this->_stream->readByte(); $count++; } if ($count < 4) { $result <<= 7; $result |= $intReference; } else { // Use all 8 bits from the 4th byte $result <<= 8; $result |= $intReference; // Check if the integer should be negative if (($result & 0x10000000) != 0) { //and extend the sign bit $result |= 0xe0000000; } } return $result; } /** * Read and deserialize a string * * Strings can be sent as a reference to a previously * occurring String by using an index to the implicit string reference table. * Strings are encoding using UTF-8 - however the header may either * describe a string literal or a string reference. * * - string = 0×06 string-data * - string-data = integer-data [ modified-utf-8 ] * - modified-utf-8 = *OCTET * * @return String */ public function readString() { $stringReference = $this->readInteger(); //Check if this is a reference string if (($stringReference & 0x01) == 0) { // reference string $stringReference = $stringReference >> 1; if ($stringReference >= count($this->_referenceStrings)) { require_once 'Zend/Amf/Exception.php'; throw new Zend_Amf_Exception('Undefined string reference: ' . $stringReference); } // reference string found return $this->_referenceStrings[$stringReference]; } $length = $stringReference >> 1; if ($length) { $string = $this->_stream->readBytes($length); $this->_referenceStrings[] = $string; } else { $string = ""; } return $string; } /** * Read and deserialize a date * * Data is the number of milliseconds elapsed since the epoch * of midnight, 1st Jan 1970 in the UTC time zone. * Local time zone information is not sent to flash. * * - date = 0×08 integer-data [ number-data ] * * @return Zend_Date */ public function readDate() { $dateReference = $this->readInteger(); if (($dateReference & 0x01) == 0) { $dateReference = $dateReference >> 1; if ($dateReference>=count($this->_referenceObjects)) { require_once 'Zend/Amf/Exception.php'; throw new Zend_Amf_Exception('Undefined date reference: ' . $dateReference); } return $this->_referenceObjects[$dateReference]; } $timestamp = floor($this->_stream->readDouble() / 1000); require_once 'Zend/Date.php'; $dateTime = new Zend_Date((int) $timestamp); $this->_referenceObjects[] = $dateTime; return $dateTime; } /** * Read amf array to PHP array * * - array = 0×09 integer-data ( [ 1OCTET *amf3-data ] | [OCTET *amf3-data 1] | [ OCTET *amf-data ] ) * * @return array */ public function readArray() { $arrayReference = $this->readInteger(); if (($arrayReference & 0x01)==0){ $arrayReference = $arrayReference >> 1; if ($arrayReference>=count($this->_referenceObjects)) { require_once 'Zend/Amf/Exception.php'; throw new Zend_Amf_Exception('Unknow array reference: ' . $arrayReference); } return $this->_referenceObjects[$arrayReference]; } // Create a holder for the array in the reference list $data = array(); $this->_referenceObjects[] &= $data; $key = $this->readString(); // Iterating for string based keys. while ($key != '') { $data[$key] = $this->readTypeMarker(); $key = $this->readString(); } $arrayReference = $arrayReference >>1; //We have a dense array for ($i=0; $i < $arrayReference; $i++) { $data[] = $this->readTypeMarker(); } return $data; } /** * Read an object from the AMF stream and convert it into a PHP object * * @todo Rather than using an array of traitsInfo create Zend_Amf_Value_TraitsInfo * @return object */ public function readObject() { $traitsInfo = $this->readInteger(); $storedObject = ($traitsInfo & 0x01)==0; $traitsInfo = $traitsInfo >> 1; // Check if the Object is in the stored Objects reference table if ($storedObject) { $ref = $traitsInfo; if (!isset($this->_referenceObjects[$ref])) { require_once 'Zend/Amf/Exception.php'; throw new Zend_Amf_Exception('Unknown Object reference: ' . $ref); } $returnObject = $this->_referenceObjects[$ref]; } else { // Check if the Object is in the stored Definistions reference table $storedClass = ($traitsInfo & 0x01) == 0; $traitsInfo = $traitsInfo >> 1; if ($storedClass) { $ref = $traitsInfo; if (!isset($this->_referenceDefinitions[$ref])) { require_once 'Zend/Amf/Exception.php'; throw new Zend_Amf_Exception('Unknows Definition reference: '. $ref); } // Populate the reference attributes $className = $this->_referenceDefinitions[$ref]['className']; $encoding = $this->_referenceDefinitions[$ref]['encoding']; $propertyNames = $this->_referenceDefinitions[$ref]['propertyNames']; } else { // The class was not in the reference tables. Start reading rawdata to build traits. // Create a traits table. Zend_Amf_Value_TraitsInfo would be ideal $className = $this->readString(); $encoding = $traitsInfo & 0x03; $propertyNames = array(); $traitsInfo = $traitsInfo >> 2; } // We now have the object traits defined in variables. Time to go to work: if (!$className) { // No class name generic object $returnObject = new stdClass(); } else { // Defined object // Typed object lookup agsinst registered classname maps if ($loader = Zend_Amf_Parse_TypeLoader::loadType($className)) { $returnObject = new $loader(); } else { //user defined typed object require_once 'Zend/Amf/Exception.php'; throw new Zend_Amf_Exception('Typed object not found: '. $className . ' '); } } // Add the Object ot the reference table $this->_referenceObjects[] = $returnObject; // Check encoding types for additional processing. switch ($encoding) { case (Zend_Amf_Constants::ET_EXTERNAL): // Externalizable object such as {ArrayCollection} and {ObjectProxy} if (!$storedClass) { $this->_referenceDefinitions[] = array( 'className' => $className, 'encoding' => $encoding, 'propertyNames' => $propertyNames, ); } $returnObject->externalizedData = $this->readTypeMarker(); break; case (Zend_Amf_Constants::ET_DYNAMIC): // used for Name-value encoding if (!$storedClass) { $this->_referenceDefinitions[] = array( 'className' => $className, 'encoding' => $encoding, 'propertyNames' => $propertyNames, ); } // not a refrence object read name value properties from byte stream $properties = array(); // clear value do { $property = $this->readString(); if ($property != "") { $propertyNames[] = $property; $properties[$property] = $this->readTypeMarker(); } } while ($property !=""); break; default: // basic property list object. if (!$storedClass) { $count = $traitsInfo; // Number of properties in the list for($i=0; $i< $count; $i++) { $propertyNames[] = $this->readString(); } // Add a refrence to the class. $this->_referenceDefinitions[] = array( 'className' => $className, 'encoding' => $encoding, 'propertyNames' => $propertyNames, ); } $properties = array(); // clear value foreach ($propertyNames as $property) { $properties[$property] = $this->readTypeMarker(); } break; } // Add properties back to the return object. foreach($properties as $key=>$value) { if($key) { $returnObject->$key = $value; } } } return $returnObject; } /** * Convert XML to SimpleXml * If user wants DomDocument they can use dom_import_simplexml * * @return SimpleXml Object */ public function readXmlString() { $xmlReference = $this->readInteger(); $length = $xmlReference >> 1; $string = $this->_stream->readBytes($length); return simplexml_load_string($string); } }