import v1.1.0_beta1 | 2009-08-21

This commit is contained in:
2019-07-17 22:16:19 +02:00
parent 2c1152f0d3
commit 8dee6b1a10
2306 changed files with 251360 additions and 23428 deletions

View File

@ -1,410 +1,420 @@
<?php
/**
* Zend Framework
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://framework.zend.com/license/new-bsd
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@zend.com so we can send you a copy immediately.
*
* @category Zend
* @package Zend_Amf
* @subpackage Parse_Amf3
* @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
/** Zend_Amf_Parse_Deserializer */
require_once 'Zend/Amf/Parse/Deserializer.php';
/** Zend_Amf_Parse_TypeLoader */
require_once 'Zend/Amf/Parse/TypeLoader.php';
/**
* Read an AMF3 input stream and convert it into PHP data types.
*
* @todo readObject to handle Typed Objects
* @todo readXMLStrimg to be implemented.
* @todo Class could be implmented as Factory Class with each data type it's own class.
* @package Zend_Amf
* @subpackage Parse_Amf3
* @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
class Zend_Amf_Parse_Amf3_Deserializer extends Zend_Amf_Parse_Deserializer
{
/**
* Total number of objects in the referenceObject array
* @var int
*/
protected $_objectCount;
/**
* An array of reference objects per amf body
* @var array
*/
protected $_referenceObjects = array();
/**
* An array of reference strings per amf body
* @var array
*/
protected $_referenceStrings = array();
/**
* An array of reference class definitions per body
* @var array
*/
protected $_referenceDefinitions = array();
/**
* Read AMF markers and dispatch for deserialization
*
* Checks for AMF marker types and calls the appropriate methods
* for deserializing those marker types. markers are the data type of
* the following value.
*
* @param integer $typeMarker
* @return mixed Whatever the corresponding PHP data type is
* @throws Zend_Amf_Exception for unidentified marker type
*/
public function readTypeMarker($typeMarker = null)
{
if(null === $typeMarker) {
$typeMarker = $this->_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);
}
}
<?php
/**
* Zend Framework
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://framework.zend.com/license/new-bsd
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@zend.com so we can send you a copy immediately.
*
* @category Zend
* @package Zend_Amf
* @subpackage Parse_Amf3
* @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
/** Zend_Amf_Parse_Deserializer */
require_once 'Zend/Amf/Parse/Deserializer.php';
/** Zend_Amf_Parse_TypeLoader */
require_once 'Zend/Amf/Parse/TypeLoader.php';
/**
* Read an AMF3 input stream and convert it into PHP data types.
*
* @todo readObject to handle Typed Objects
* @todo readXMLStrimg to be implemented.
* @todo Class could be implmented as Factory Class with each data type it's own class.
* @package Zend_Amf
* @subpackage Parse_Amf3
* @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
class Zend_Amf_Parse_Amf3_Deserializer extends Zend_Amf_Parse_Deserializer
{
/**
* Total number of objects in the referenceObject array
* @var int
*/
protected $_objectCount;
/**
* An array of reference objects per amf body
* @var array
*/
protected $_referenceObjects = array();
/**
* An array of reference strings per amf body
* @var array
*/
protected $_referenceStrings = array();
/**
* An array of reference class definitions per body
* @var array
*/
protected $_referenceDefinitions = array();
/**
* Read AMF markers and dispatch for deserialization
*
* Checks for AMF marker types and calls the appropriate methods
* for deserializing those marker types. markers are the data type of
* the following value.
*
* @param integer $typeMarker
* @return mixed Whatever the corresponding PHP data type is
* @throws Zend_Amf_Exception for unidentified marker type
*/
public function readTypeMarker($typeMarker = null)
{
if(null === $typeMarker) {
$typeMarker = $this->_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 |= ~0xFFFFFFF;
}
}
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 = 0x06 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 = 0x08 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 = 0x09 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|array
*/
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;
$properties = array(); // clear value
// 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
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,
);
}
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;
}
}
}
if($returnObject instanceof Zend_Amf_Value_Messaging_ArrayCollection) {
if(isset($returnObject->externalizedData)) {
$returnObject = $returnObject->externalizedData;
} else {
$returnObject = get_object_vars($returnObject);
}
}
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);
}
}

View File

@ -15,7 +15,7 @@
* @category Zend
* @package Zend_Amf
* @subpackage Parse_Amf3
* @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
* @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
@ -30,11 +30,29 @@ require_once 'Zend/Amf/Parse/TypeLoader.php';
*
* @package Zend_Amf
* @subpackage Parse_Amf3
* @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
* @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
class Zend_Amf_Parse_Amf3_Serializer extends Zend_Amf_Parse_Serializer
{
/**
* An array of reference objects per amf body
* @var array
*/
protected $_referenceObjects = array();
/**
* An array of reference strings per amf body
* @var array
*/
protected $_referenceStrings = array();
/**
* An array of reference class definitions, indexed by classname
* @var array
*/
protected $_referenceDefinitions = array();
/**
* Serialize PHP types to AMF3 and write to stream
*
@ -78,7 +96,10 @@ class Zend_Amf_Parse_Amf3_Serializer extends Zend_Amf_Parse_Serializer
$this->writeObject($data);
break;
case Zend_Amf_Constants::AMF3_BYTEARRAY:
$this->writeString($data);
$this->writeByteArray($data);
break;
case Zend_Amf_Constants::AMF3_XMLSTRING;
$this->writeXml($data);
break;
default:
require_once 'Zend/Amf/Exception.php';
@ -86,8 +107,11 @@ class Zend_Amf_Parse_Amf3_Serializer extends Zend_Amf_Parse_Serializer
}
} else {
// Detect Type Marker
if(is_resource($data)) {
$data = Zend_Amf_Parse_TypeLoader::handleResource($data);
}
switch (true) {
case (null === $data):
case (null === $data):
$markerType = Zend_Amf_Constants::AMF3_NULL;
break;
case (is_bool($data)):
@ -119,6 +143,8 @@ class Zend_Amf_Parse_Amf3_Serializer extends Zend_Amf_Parse_Serializer
$markerType = Zend_Amf_Constants::AMF3_DATE;
} else if ($data instanceof Zend_Amf_Value_ByteArray) {
$markerType = Zend_Amf_Constants::AMF3_BYTEARRAY;
} else if (($data instanceof DOMDocument) || ($data instanceof SimpleXMLElement)) {
$markerType = Zend_Amf_Constants::AMF3_XMLSTRING;
} else {
$markerType = Zend_Amf_Constants::AMF3_OBJECT;
}
@ -163,6 +189,21 @@ class Zend_Amf_Parse_Amf3_Serializer extends Zend_Amf_Parse_Serializer
$this->_stream->writeByte($int & 0xff);
return $this;
}
/**
* Send string to output stream, without trying to reference it.
* The string is prepended with strlen($string) << 1 | 0x01
*
* @param string $string
* @return Zend_Amf_Parse_Amf3_Serializer
*/
protected function writeBinaryString($string){
$ref = strlen($string) << 1 | 0x01;
$this->writeInteger($ref);
$this->_stream->writeBytes($string);
return $this;
}
/**
* Send string to output stream
@ -172,9 +213,74 @@ class Zend_Amf_Parse_Amf3_Serializer extends Zend_Amf_Parse_Serializer
*/
public function writeString($string)
{
$ref = strlen($string) << 1 | 0x01;
$this->writeInteger($ref);
$this->_stream->writeBytes($string);
$len = strlen($string);
if(!$len){
$this->writeInteger(0x01);
return $this;
}
$ref = array_search($string, $this->_referenceStrings, true);
if($ref === false){
$this->_referenceStrings[] = $string;
$this->writeBinaryString($string);
} else {
$ref <<= 1;
$this->writeInteger($ref);
}
return $this;
}
/**
* Send ByteArray to output stream
*
* @param string|Zend_Amf_Value_ByteArray $data
* @return Zend_Amf_Parse_Amf3_Serializer
*/
public function writeByteArray($data){
if($this->writeObjectReference($data)){
return $this;
}
if(is_string($data)) {
//nothing to do
} else if ($data instanceof Zend_Amf_Value_ByteArray) {
$data = $data->getData();
} else {
require_once 'Zend/Amf/Exception.php';
throw new Zend_Amf_Exception('Invalid ByteArray specified; must be a string or Zend_Amf_Value_ByteArray');
}
$this->writeBinaryString($data);
return $this;
}
/**
* Send xml to output stream
*
* @param DOMDocument|SimpleXMLElement $xml
* @return Zend_Amf_Parse_Amf3_Serializer
*/
public function writeXml($xml)
{
if($this->writeObjectReference($xml)){
return $this;
}
if(is_string($xml)) {
//nothing to do
} else if ($xml instanceof DOMDocument) {
$xml = $xml->saveXml();
} else if ($xml instanceof SimpleXMLElement) {
$xml = $xml->asXML();
} else {
require_once 'Zend/Amf/Exception.php';
throw new Zend_Amf_Exception('Invalid xml specified; must be a DOMDocument or SimpleXMLElement');
}
$this->writeBinaryString($xml);
return $this;
}
@ -186,6 +292,10 @@ class Zend_Amf_Parse_Amf3_Serializer extends Zend_Amf_Parse_Serializer
*/
public function writeDate($date)
{
if($this->writeObjectReference($date)){
return $this;
}
if ($date instanceof DateTime) {
$dateString = $date->format('U') * 1000;
} elseif ($date instanceof Zend_Date) {
@ -209,6 +319,10 @@ class Zend_Amf_Parse_Amf3_Serializer extends Zend_Amf_Parse_Serializer
*/
public function writeArray(array $array)
{
if($this->writeObjectReference($array)){
return $this;
}
// have to seperate mixed from numberic keys.
$numeric = array();
$string = array();
@ -238,6 +352,25 @@ class Zend_Amf_Parse_Amf3_Serializer extends Zend_Amf_Parse_Serializer
}
return $this;
}
/**
* Check if the given object is in the reference table, write the reference if it exists,
* otherwise add the object to the reference table
*
* @param mixed $object object to check for reference
* @return Boolean true, if the reference was written, false otherwise
*/
protected function writeObjectReference($object){
$ref = array_search($object, $this->_referenceObjects,true);
//quickly handle object references
if($ref !== false){
$ref <<= 1;
$this->writeInteger($ref);
return true;
}
$this->_referenceObjects[] = $object;
return false;
}
/**
* Write object to ouput stream
@ -247,7 +380,10 @@ class Zend_Amf_Parse_Amf3_Serializer extends Zend_Amf_Parse_Serializer
*/
public function writeObject($object)
{
$encoding = Zend_Amf_Constants::ET_PROPLIST;
if($this->writeObjectReference($object)){
return $this;
}
$className = '';
//Check to see if the object is a typed object and we need to change
@ -259,7 +395,6 @@ class Zend_Amf_Parse_Amf3_Serializer extends Zend_Amf_Parse_Serializer
// Check to see if the user has defined an explicit Action Script type.
case isset($object->_explicitType):
$className = $object->_explicitType;
unset($object->_explicitType);
break;
// Check if user has defined a method for accessing the Action Script type
@ -268,36 +403,87 @@ class Zend_Amf_Parse_Amf3_Serializer extends Zend_Amf_Parse_Serializer
break;
// No return class name is set make it a generic object
case ($object instanceof stdClass):
$className = '';
break;
// By default, use object's class name
default:
$className = get_class($object);
break;
}
$traitsInfo = Zend_Amf_Constants::AMF3_OBJECT_ENCODING;
$traitsInfo |= $encoding << 2;
$writeTraits = true;
//check to see, if we have a corresponding definition
if(array_key_exists($className, $this->_referenceDefinitions)){
$traitsInfo = $this->_referenceDefinitions[$className]['id'];
$encoding = $this->_referenceDefinitions[$className]['encoding'];
$propertyNames = $this->_referenceDefinitions[$className]['propertyNames'];
$traitsInfo = ($traitsInfo << 2) | 0x01;
$writeTraits = false;
} else {
$propertyNames = array();
if($className == ''){
//if there is no className, we interpret the class as dynamic without any sealed members
$encoding = Zend_Amf_Constants::ET_DYNAMIC;
} else {
$encoding = Zend_Amf_Constants::ET_PROPLIST;
foreach($object as $key => $value) {
if( $key[0] != "_") {
$propertyNames[] = $key;
}
}
}
$this->_referenceDefinitions[$className] = array(
'id' => count($this->_referenceDefinitions),
'encoding' => $encoding,
'propertyNames' => $propertyNames,
);
$traitsInfo = Zend_Amf_Constants::AMF3_OBJECT_ENCODING;
$traitsInfo |= $encoding << 2;
$traitsInfo |= (count($propertyNames) << 4);
}
$this->writeInteger($traitsInfo);
if($writeTraits){
$this->writeString($className);
foreach ($propertyNames as $value) {
$this->writeString($value);
}
}
try {
switch($encoding) {
case Zend_Amf_Constants::ET_PROPLIST:
$count = 0;
foreach($object as $key => $value) {
$count++;
//Write the sealed values to the output stream.
foreach ($propertyNames as $key) {
$this->writeTypeMarker($object->$key);
}
$traitsInfo |= ($count << 4);
// Write the object ID
$this->writeInteger($traitsInfo);
// Write the classname
$this->writeString($className);
// Write the object Key's to the output stream
foreach ($object as $key => $value) {
$this->writeString($key);
break;
case Zend_Amf_Constants::ET_DYNAMIC:
//Write the sealed values to the output stream.
foreach ($propertyNames as $key) {
$this->writeTypeMarker($object->$key);
}
//Write the object values to the output stream.
foreach ($object as $key => $value) {
$this->writeTypeMarker($value);
//Write remaining properties
foreach($object as $key => $value){
if(!in_array($key,$propertyNames) && $key[0] != "_"){
$this->writeString($key);
$this->writeTypeMarker($value);
}
}
//Write an empty string to end the dynamic part
$this->writeString('');
break;
case Zend_Amf_Constants::ET_EXTERNAL:
require_once 'Zend/Amf/Exception.php';