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 }