<?php
/**
* AMFSerializer manages the job of translating PHP objects into
* the actionscript equivalent via amf. The main method of the serializer
* is the serialize method which takes and AMFObject as it's argument
* and builds the resulting amf body.
*
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
* @copyright (c) 2003 amfphp.org
* @package flashservices
* @subpackage io
* @version $Id$
*/
class AMFBaseSerializer {
/**
* Classes that are serialized as recordsets
*/
var $amf0StoredObjects = array();
var $storedObjects = array();
var $storedDefinitions = 0;
var $storedStrings = array();
var $outBuffer;
var $encounteredStrings = 0;
var $native = false;
/**
* AMFSerializer is the constructor function. You must pass the
* method an AMFOutputStream as the single argument.
*
* @param object $stream The AMFOutputStream
*/
function AMFBaseSerializer() {
$this->isBigEndian = AMFPHP_BIG_ENDIAN;
$this->outBuffer = ""; // the buffer
$this->charsetHandler = new CharsetHandler('phptoflash');
$this->rsCharsetHandler = new CharsetHandler('sqltoflash');
$this->resourceObjects = $GLOBALS['amfphp']['adapterMappings'];
$this->native = $GLOBALS['amfphp']['native'] && function_exists('amf_decode');
$this->encodeFlags = (AMFPHP_BIG_ENDIAN?2:0) |
($GLOBALS['amfphp']['encoding'] == 'amf3' ? 1:0);
}
/**
* serialize is the run method of the class. When serialize is called
* the AMFObject passed in is read and converted into the amf binary
* representing the PHP data represented.
*
* @param object $d the AMFObject to serialize
*/
function serialize(&$amfout) {
$encodeCallback = array(&$this,"encodeCallback");
$this->writeInt(0); // write the version ???
$count = $amfout->numOutgoingHeader();
$this->writeInt($count); // write header count
for ($i = 0; $i < $count; $i++) {
//write headers
$header = &$amfout->getOutgoingHeaderAt($i);
$this->writeUTF($header->name);
$this->writeByte(0);
$tempBuf = $this->outBuffer;
$this->outBuffer = "";
if($this->native)
$this->outBuffer .= amf_encode($header->value,$this->encodeFlags, $encodeCallback);
else
$this->writeData($header->value);
$tempBuf2 = $this->outBuffer;
$this->outBuffer = $tempBuf;
$this->writeLong(strlen($tempBuf2));
$this->outBuffer .= $tempBuf2;
}
$count = $amfout->numBody();
$this->writeInt($count); // write the body count
for ($i = 0; $i < $count; $i++) {
//write body
$this->amf0StoredObjects = array();
$this->storedStrings = array();
$this->storedObjects = array();
$this->encounteredStrings = 0;
$this->storedDefinitions = 0;
$body = &$amfout->getBodyAt($i);
$this->currentBody = & $body;
$this->writeUTF($body->responseURI); // write the responseURI header
$this->writeUTF($body->responseTarget); // write null, haven't found another use for this
$tempBuf = $this->outBuffer;
$this->outBuffer = "";
if($this->native)
$this->outBuffer .= amf_encode($body->getResults(),$this->encodeFlags, $encodeCallback);
else
$this->writeData($body->getResults());
$tempBuf2 = $this->outBuffer;
$this->outBuffer = $tempBuf;
$this->writeLong(strlen($tempBuf2));
$this->outBuffer .= $tempBuf2;
}
return $this->outBuffer;
}
function encodeCallback($value)
{
///print_r($value);
if(is_object($value))
{
$className = strtolower(get_class($value));
if(AMFPHP_PHP5 && $className == 'domdocument')
{
return array($this->cleanXml($value->saveXml()),1);
}
else if(array_key_exists($className, $GLOBALS['amfphp']['adapterMappings']))
{
$subtype = $GLOBALS['amfphp']['adapterMappings'][strtolower($className)];
$classname = $subtype . "Adapter"; // full class name
$includeFile = include_once(AMFPHP_BASE . "shared/adapters/" . $classname . ".php"); // try to load the recordset library from the sql folder
if (!$includeFile) {
trigger_error("The recordset filter class " . $classname . " was not found", E_USER_ERROR);
}
$recordSet = new $classname($value); // returns formatted recordset
return array(
array("__amf_recordset__" => 2,
"rows" => $recordSet->rows,
"columns" => $recordSet->columns),
5);
}
else if(AMFPHP_PHP5 == 0 && $className == 'domdocument')
{
return array($this->cleanXml($value->dump_mem()),1);
}
else if($className == 'simplexmlelement')
{
return array($this->cleanXml($value->asXML()),1);
}
elseif($className == 'bytearray' && $this->encodeFlags & 1 == 1)
{
return array($value->data, 7);
}
else
{
$className = $this->getClassName($value);
return array($value,3,$className);
}
}
else
{
//A resource
$type = get_resource_type($value);
list($type, $subtype) = $this->sanitizeType($type);
$classname = $subtype . "Adapter"; // full class name
$includeFile = include_once(AMFPHP_BASE . "shared/adapters/" . $classname . ".php"); // try to load the recordset library from the sql folder
if (!$includeFile) {
trigger_error("The recordset filter class " . $classname . " was not found", E_USER_ERROR);
}
$recordSet = new $classname($value); // returns formatted recordset
return array(
array("__amf_recordset__" => 2,
"rows" => $recordSet->rows,
"columns" => $recordSet->columns),
5);
}
}
function cleanXml($d)
{
return preg_replace('/\>(\n|\r|\r\n| |\t)*\</','><',trim($d));
}
/**********************************************************************************
* This code used to be in AMFOutputStream
********************************************************************************/
/**
* writeByte writes a singe byte to the output stream
* 0-255 range
*
* @param int $b An int that can be converted to a byte
*/
function writeByte($b) {
$this->outBuffer .= pack("c", $b); // use pack with the c flag
}
/**
* writeInt takes an int and writes it as 2 bytes to the output stream
* 0-65535 range
*
* @param int $n An integer to convert to a 2 byte binary string
*/
function writeInt($n) {
$this->outBuffer .= pack("n", $n); // use pack with the n flag
}
/**
* writeLong takes an int, float or double and converts it to a 4 byte binary string and
* adds it to the output buffer
*
* @param long $l A long to convert to a 4 byte binary string
*/
function writeLong($l) {
$this->outBuffer .= pack("N", $l); // use pack with the N flag
}
/**
* writeUTF takes and input string, writes the length as an int and then
* appends the string to the output buffer
*
* @param string $s The string less than 65535 characters to add to the stream
*/
function writeUTF($s) {
$os = $this->charsetHandler->transliterate($s);
$this->writeInt(strlen($os)); // write the string length - max 65535
$this->outBuffer .= $os; // write the string chars
}
/**
* writeBinary takes and input string, writes the length as an int and then
* appends the string to the output buffer
*
* @param string $s The string less than 65535 characters to add to the stream
*/
function writeBinary($s) {
$this->outBuffer .= $s; // write the string chars
}
/**
* writeLongUTF will write a string longer than 65535 characters.
* It works exactly as writeUTF does except uses a long for the length
* flag.
*
* @param string $s A string to add to the byte stream
*/
function writeLongUTF($s) {
$os = $this->charsetHandler->transliterate($s);
$this->writeLong(strlen($os));
$this->outBuffer .= $os; // write the string chars
}
/**
* writeDouble takes a float as the input and writes it to the output stream.
* Then if the system is big-endian, it reverses the bytes order because all
* doubles passed via remoting are passed little-endian.
*
* @param double $d The double to add to the output buffer
*/
function writeDouble($d) {
$b = pack("d", $d); // pack the bytes
if ($this->isBigEndian) { // if we are a big-endian processor
$r = strrev($b);
} else { // add the bytes to the output
$r = $b;
}
$this->outBuffer .= $r;
}
function sanitizeType($type)
{
$subtype = -1;
$type = strtolower($type);
if($type == NULL || trim($type) == "")
{
$type = -1;
}
if(strpos($type, ' ') !== false)
{
$str = explode(' ', $type);
if(in_array($str[1], array("result", 'resultset', "recordset", "statement")))
{
$type = "__RECORDSET__";
$subtype = $str[0];
}
}
return array($type, $subtype);
}
function getClassName(&$d)
{
$classname = get_class($d);
if(strtolower($classname) == 'stdclass' && !isset($d->_explicitType) )
{
return "";
}
if(isset($d->_explicitType))
{
$type = $d->_explicitType;
unset($d->_explicitType);
return $type;
}
if(isset($GLOBALS['amfphp']['outgoingClassMappings'][strtolower($classname)]))
{
return $GLOBALS['amfphp']['outgoingClassMappings'][strtolower($classname)];
}
if(class_exists("ReflectionClass")) //Another way of doing things, by Renaun Erickson
{
$reflectionClass = new ReflectionClass( $classname );
$fileName = $reflectionClass->getFileName();
$basePath = $GLOBALS['amfphp']['customMappingsPath'];
if( $basePath == "" )
$basePath = getcwd();
// Handle OS filesystem differences
if( DIRECTORY_SEPARATOR == "\\" && ( strpos( $basePath, DIRECTORY_SEPARATOR ) === false ) )
$basePath = str_replace( "/", DIRECTORY_SEPARATOR, $basePath );
if(strpos($fileName, $basePath) === FALSE)
{
return $classname;
}
$fullClassName = substr( $fileName, strpos( $fileName, $basePath ) );
$fullClassName = substr( $fullClassName, strlen( $basePath ) );
$fullClassName = substr( $fullClassName, 0, strlen( $fullClassName ) - 4 );
$fullClassName = str_replace( DIRECTORY_SEPARATOR, '.', $fullClassName );
return $fullClassName;
}
return $classname;
}
}
?>