<?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; } } ?>