web/Zend/Amf/Parse/Amf3/Deserializer.php
changeset 0 4eba9c11703f
equal deleted inserted replaced
-1:000000000000 0:4eba9c11703f
       
     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_Amf3
       
    18  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    19  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    20  * @version    $Id: Deserializer.php 21968 2010-04-22 03:53:34Z matthew $
       
    21  */
       
    22 
       
    23 /** Zend_Amf_Parse_Deserializer */
       
    24 require_once 'Zend/Amf/Parse/Deserializer.php';
       
    25 
       
    26 /** Zend_Amf_Parse_TypeLoader */
       
    27 require_once 'Zend/Amf/Parse/TypeLoader.php';
       
    28 
       
    29 /**
       
    30  * Read an AMF3 input stream and convert it into PHP data types.
       
    31  *
       
    32  * @todo       readObject to handle Typed Objects
       
    33  * @todo       readXMLStrimg to be implemented.
       
    34  * @todo       Class could be implemented as Factory Class with each data type it's own class.
       
    35  * @package    Zend_Amf
       
    36  * @subpackage Parse_Amf3
       
    37  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    38  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    39  */
       
    40 class Zend_Amf_Parse_Amf3_Deserializer extends Zend_Amf_Parse_Deserializer
       
    41 {
       
    42     /**
       
    43      * Total number of objects in the referenceObject array
       
    44      * @var int
       
    45      */
       
    46     protected $_objectCount;
       
    47 
       
    48     /**
       
    49      * An array of reference objects per amf body
       
    50      * @var array
       
    51      */
       
    52     protected $_referenceObjects = array();
       
    53 
       
    54     /**
       
    55      * An array of reference strings per amf body
       
    56      * @var array
       
    57      */
       
    58     protected $_referenceStrings = array();
       
    59 
       
    60     /**
       
    61      * An array of reference class definitions per body
       
    62      * @var array
       
    63      */
       
    64     protected $_referenceDefinitions = array();
       
    65 
       
    66     /**
       
    67      * Read AMF markers and dispatch for deserialization
       
    68      *
       
    69      * Checks for AMF marker types and calls the appropriate methods
       
    70      * for deserializing those marker types. markers are the data type of
       
    71      * the following value.
       
    72      *
       
    73      * @param  integer $typeMarker
       
    74      * @return mixed Whatever the corresponding PHP data type is
       
    75      * @throws Zend_Amf_Exception for unidentified marker type
       
    76      */
       
    77     public function readTypeMarker($typeMarker = null)
       
    78     {
       
    79         if(null === $typeMarker) {
       
    80             $typeMarker = $this->_stream->readByte();
       
    81         }
       
    82 
       
    83         switch($typeMarker) {
       
    84             case Zend_Amf_Constants::AMF3_UNDEFINED:
       
    85                  return null;
       
    86             case Zend_Amf_Constants::AMF3_NULL:
       
    87                  return null;
       
    88             case Zend_Amf_Constants::AMF3_BOOLEAN_FALSE:
       
    89                  return false;
       
    90             case Zend_Amf_Constants::AMF3_BOOLEAN_TRUE:
       
    91                  return true;
       
    92             case Zend_Amf_Constants::AMF3_INTEGER:
       
    93                  return $this->readInteger();
       
    94             case Zend_Amf_Constants::AMF3_NUMBER:
       
    95                  return $this->_stream->readDouble();
       
    96             case Zend_Amf_Constants::AMF3_STRING:
       
    97                  return $this->readString();
       
    98             case Zend_Amf_Constants::AMF3_DATE:
       
    99                  return $this->readDate();
       
   100             case Zend_Amf_Constants::AMF3_ARRAY:
       
   101                  return $this->readArray();
       
   102             case Zend_Amf_Constants::AMF3_OBJECT:
       
   103                  return $this->readObject();
       
   104             case Zend_Amf_Constants::AMF3_XML:
       
   105             case Zend_Amf_Constants::AMF3_XMLSTRING:
       
   106                  return $this->readXmlString();
       
   107             case Zend_Amf_Constants::AMF3_BYTEARRAY:
       
   108                  return $this->readString();
       
   109             default:
       
   110                 require_once 'Zend/Amf/Exception.php';
       
   111                 throw new Zend_Amf_Exception('Unsupported type marker: ' . $typeMarker);
       
   112         }
       
   113     }
       
   114 
       
   115     /**
       
   116      * Read and deserialize an integer
       
   117      *
       
   118      * AMF 3 represents smaller integers with fewer bytes using the most
       
   119      * significant bit of each byte. The worst case uses 32-bits
       
   120      * to represent a 29-bit number, which is what we would have
       
   121      * done with no compression.
       
   122      * - 0x00000000 - 0x0000007F : 0xxxxxxx
       
   123      * - 0x00000080 - 0x00003FFF : 1xxxxxxx 0xxxxxxx
       
   124      * - 0x00004000 - 0x001FFFFF : 1xxxxxxx 1xxxxxxx 0xxxxxxx
       
   125      * - 0x00200000 - 0x3FFFFFFF : 1xxxxxxx 1xxxxxxx 1xxxxxxx xxxxxxxx
       
   126      * - 0x40000000 - 0xFFFFFFFF : throw range exception
       
   127      *
       
   128      * 0x04 -> integer type code, followed by up to 4 bytes of data.
       
   129      *
       
   130      * Parsing integers on OSFlash for the AMF3 integer data format:
       
   131      * @link http://osflash.org/amf3/parsing_integers
       
   132      * @return int|float
       
   133      */
       
   134     public function readInteger()
       
   135     {
       
   136         $count        = 1;
       
   137         $intReference = $this->_stream->readByte();
       
   138         $result       = 0;
       
   139         while ((($intReference & 0x80) != 0) && $count < 4) {
       
   140             $result       <<= 7;
       
   141             $result        |= ($intReference & 0x7f);
       
   142             $intReference   = $this->_stream->readByte();
       
   143             $count++;
       
   144         }
       
   145         if ($count < 4) {
       
   146             $result <<= 7;
       
   147             $result  |= $intReference;
       
   148         } else {
       
   149             // Use all 8 bits from the 4th byte
       
   150             $result <<= 8;
       
   151             $result  |= $intReference;
       
   152 
       
   153             // Check if the integer should be negative
       
   154             if (($result & 0x10000000) != 0) {
       
   155                 //and extend the sign bit
       
   156                 $result |= ~0xFFFFFFF;
       
   157             }
       
   158         }
       
   159         return $result;
       
   160     }
       
   161 
       
   162     /**
       
   163      * Read and deserialize a string
       
   164      *
       
   165      * Strings can be sent as a reference to a previously
       
   166      * occurring String by using an index to the implicit string reference table.
       
   167      * Strings are encoding using UTF-8 - however the header may either
       
   168      * describe a string literal or a string reference.
       
   169      *
       
   170      * - string = 0x06 string-data
       
   171      * - string-data = integer-data [ modified-utf-8 ]
       
   172      * - modified-utf-8 = *OCTET
       
   173      *
       
   174      * @return String
       
   175      */
       
   176     public function readString()
       
   177     {
       
   178         $stringReference = $this->readInteger();
       
   179 
       
   180         //Check if this is a reference string
       
   181         if (($stringReference & 0x01) == 0) {
       
   182             // reference string
       
   183             $stringReference = $stringReference >> 1;
       
   184             if ($stringReference >= count($this->_referenceStrings)) {
       
   185                 require_once 'Zend/Amf/Exception.php';
       
   186                 throw new Zend_Amf_Exception('Undefined string reference: ' . $stringReference);
       
   187             }
       
   188             // reference string found
       
   189             return $this->_referenceStrings[$stringReference];
       
   190         }
       
   191 
       
   192         $length = $stringReference >> 1;
       
   193         if ($length) {
       
   194             $string = $this->_stream->readBytes($length);
       
   195             $this->_referenceStrings[] = $string;
       
   196         } else {
       
   197             $string = "";
       
   198         }
       
   199         return $string;
       
   200     }
       
   201 
       
   202     /**
       
   203      * Read and deserialize a date
       
   204      *
       
   205      * Data is the number of milliseconds elapsed since the epoch
       
   206      * of midnight, 1st Jan 1970 in the UTC time zone.
       
   207      * Local time zone information is not sent to flash.
       
   208      *
       
   209      * - date = 0x08 integer-data [ number-data ]
       
   210      *
       
   211      * @return Zend_Date
       
   212      */
       
   213     public function readDate()
       
   214     {
       
   215         $dateReference = $this->readInteger();
       
   216         if (($dateReference & 0x01) == 0) {
       
   217             $dateReference = $dateReference >> 1;
       
   218             if ($dateReference>=count($this->_referenceObjects)) {
       
   219                 require_once 'Zend/Amf/Exception.php';
       
   220                 throw new Zend_Amf_Exception('Undefined date reference: ' . $dateReference);
       
   221             }
       
   222             return $this->_referenceObjects[$dateReference];
       
   223         }
       
   224 
       
   225         $timestamp = floor($this->_stream->readDouble() / 1000);
       
   226 
       
   227         require_once 'Zend/Date.php';
       
   228         $dateTime  = new Zend_Date((int) $timestamp);
       
   229         $this->_referenceObjects[] = $dateTime;
       
   230         return $dateTime;
       
   231     }
       
   232 
       
   233     /**
       
   234      * Read amf array to PHP array
       
   235      *
       
   236      * - array = 0x09 integer-data ( [ 1OCTET *amf3-data ] | [OCTET *amf3-data 1] | [ OCTET *amf-data ] )
       
   237      *
       
   238      * @return array
       
   239      */
       
   240     public function readArray()
       
   241     {
       
   242         $arrayReference = $this->readInteger();
       
   243         if (($arrayReference & 0x01)==0){
       
   244             $arrayReference = $arrayReference >> 1;
       
   245             if ($arrayReference>=count($this->_referenceObjects)) {
       
   246                 require_once 'Zend/Amf/Exception.php';
       
   247                 throw new Zend_Amf_Exception('Unknow array reference: ' . $arrayReference);
       
   248             }
       
   249             return $this->_referenceObjects[$arrayReference];
       
   250         }
       
   251 
       
   252         // Create a holder for the array in the reference list
       
   253         $data = array();
       
   254         $this->_referenceObjects[] =& $data;
       
   255         $key = $this->readString();
       
   256 
       
   257         // Iterating for string based keys.
       
   258         while ($key != '') {
       
   259             $data[$key] = $this->readTypeMarker();
       
   260             $key = $this->readString();
       
   261         }
       
   262 
       
   263         $arrayReference = $arrayReference >>1;
       
   264 
       
   265         //We have a dense array
       
   266         for ($i=0; $i < $arrayReference; $i++) {
       
   267             $data[] = $this->readTypeMarker();
       
   268         }
       
   269 
       
   270         return $data;
       
   271     }
       
   272 
       
   273     /**
       
   274      * Read an object from the AMF stream and convert it into a PHP object
       
   275      *
       
   276      * @todo   Rather than using an array of traitsInfo create Zend_Amf_Value_TraitsInfo
       
   277      * @return object|array
       
   278      */
       
   279     public function readObject()
       
   280     {
       
   281         $traitsInfo   = $this->readInteger();
       
   282         $storedObject = ($traitsInfo & 0x01)==0;
       
   283         $traitsInfo   = $traitsInfo >> 1;
       
   284 
       
   285         // Check if the Object is in the stored Objects reference table
       
   286         if ($storedObject) {
       
   287             $ref = $traitsInfo;
       
   288             if (!isset($this->_referenceObjects[$ref])) {
       
   289                 require_once 'Zend/Amf/Exception.php';
       
   290                 throw new Zend_Amf_Exception('Unknown Object reference: ' . $ref);
       
   291             }
       
   292             $returnObject = $this->_referenceObjects[$ref];
       
   293         } else {
       
   294             // Check if the Object is in the stored Definitions reference table
       
   295             $storedClass = ($traitsInfo & 0x01) == 0;
       
   296             $traitsInfo  = $traitsInfo >> 1;
       
   297             if ($storedClass) {
       
   298                 $ref = $traitsInfo;
       
   299                 if (!isset($this->_referenceDefinitions[$ref])) {
       
   300                     require_once 'Zend/Amf/Exception.php';
       
   301                     throw new Zend_Amf_Exception('Unknows Definition reference: '. $ref);
       
   302                 }
       
   303                 // Populate the reference attributes
       
   304                 $className     = $this->_referenceDefinitions[$ref]['className'];
       
   305                 $encoding      = $this->_referenceDefinitions[$ref]['encoding'];
       
   306                 $propertyNames = $this->_referenceDefinitions[$ref]['propertyNames'];
       
   307             } else {
       
   308                 // The class was not in the reference tables. Start reading rawdata to build traits.
       
   309                 // Create a traits table. Zend_Amf_Value_TraitsInfo would be ideal
       
   310                 $className     = $this->readString();
       
   311                 $encoding      = $traitsInfo & 0x03;
       
   312                 $propertyNames = array();
       
   313                 $traitsInfo    = $traitsInfo >> 2;
       
   314             }
       
   315 
       
   316             // We now have the object traits defined in variables. Time to go to work:
       
   317             if (!$className) {
       
   318                 // No class name generic object
       
   319                 $returnObject = new stdClass();
       
   320             } else {
       
   321                 // Defined object
       
   322                 // Typed object lookup against registered classname maps
       
   323                 if ($loader = Zend_Amf_Parse_TypeLoader::loadType($className)) {
       
   324                     $returnObject = new $loader();
       
   325                 } else {
       
   326                     //user defined typed object
       
   327                     require_once 'Zend/Amf/Exception.php';
       
   328                     throw new Zend_Amf_Exception('Typed object not found: '. $className . ' ');
       
   329                 }
       
   330             }
       
   331 
       
   332             // Add the Object to the reference table
       
   333             $this->_referenceObjects[] = $returnObject;
       
   334 
       
   335             $properties = array(); // clear value
       
   336             // Check encoding types for additional processing.
       
   337             switch ($encoding) {
       
   338                 case (Zend_Amf_Constants::ET_EXTERNAL):
       
   339                     // Externalizable object such as {ArrayCollection} and {ObjectProxy}
       
   340                     if (!$storedClass) {
       
   341                         $this->_referenceDefinitions[] = array(
       
   342                             'className'     => $className,
       
   343                             'encoding'      => $encoding,
       
   344                             'propertyNames' => $propertyNames,
       
   345                         );
       
   346                     }
       
   347                     $returnObject->externalizedData = $this->readTypeMarker();
       
   348                     break;
       
   349                 case (Zend_Amf_Constants::ET_DYNAMIC):
       
   350                     // used for Name-value encoding
       
   351                     if (!$storedClass) {
       
   352                         $this->_referenceDefinitions[] = array(
       
   353                             'className'     => $className,
       
   354                             'encoding'      => $encoding,
       
   355                             'propertyNames' => $propertyNames,
       
   356                         );
       
   357                     }
       
   358                     // not a reference object read name value properties from byte stream
       
   359                     do {
       
   360                         $property = $this->readString();
       
   361                         if ($property != "") {
       
   362                             $propertyNames[]       = $property;
       
   363                             $properties[$property] = $this->readTypeMarker();
       
   364                         }
       
   365                     } while ($property !="");
       
   366                     break;
       
   367                 default:
       
   368                     // basic property list object.
       
   369                     if (!$storedClass) {
       
   370                         $count = $traitsInfo; // Number of properties in the list
       
   371                         for($i=0; $i< $count; $i++) {
       
   372                             $propertyNames[] = $this->readString();
       
   373                         }
       
   374                         // Add a reference to the class.
       
   375                         $this->_referenceDefinitions[] = array(
       
   376                             'className'     => $className,
       
   377                             'encoding'      => $encoding,
       
   378                             'propertyNames' => $propertyNames,
       
   379                         );
       
   380                     }
       
   381                     foreach ($propertyNames as $property) {
       
   382                         $properties[$property] = $this->readTypeMarker();
       
   383                     }
       
   384                     break;
       
   385             }
       
   386 
       
   387             // Add properties back to the return object.
       
   388             foreach($properties as $key=>$value) {
       
   389                 if($key) {
       
   390                     $returnObject->$key = $value;
       
   391                 }
       
   392             }
       
   393 
       
   394 
       
   395         }
       
   396 
       
   397        if ($returnObject instanceof Zend_Amf_Value_Messaging_ArrayCollection) {
       
   398             if (isset($returnObject->externalizedData)) {
       
   399                 $returnObject = $returnObject->externalizedData;
       
   400             } else {
       
   401                 $returnObject = get_object_vars($returnObject);
       
   402             }
       
   403        }
       
   404 
       
   405         return $returnObject;
       
   406     }
       
   407 
       
   408     /**
       
   409      * Convert XML to SimpleXml
       
   410      * If user wants DomDocument they can use dom_import_simplexml
       
   411      *
       
   412      * @return SimpleXml Object
       
   413      */
       
   414     public function readXmlString()
       
   415     {
       
   416         $xmlReference = $this->readInteger();
       
   417         $length = $xmlReference >> 1;
       
   418         $string = $this->_stream->readBytes($length);
       
   419         return simplexml_load_string($string);
       
   420     }
       
   421 }