PageRenderTime 7ms CodeModel.GetById 47ms app.highlight 40ms RepoModel.GetById 1ms app.codeStats 0ms

/admin/app/plugins/cpamf/vendors/amfphp/core/amf/io/AMFSerializer.php

https://bitbucket.org/fxrialab/tickets
PHP | 1048 lines | 738 code | 97 blank | 213 comment | 111 complexity | 1871078f454afecca48b92611b5b96cb MD5 | raw file
   1<?php
   2/**
   3 * AMFSerializer manages the job of translating PHP objects into
   4 * the actionscript equivalent via amf.  The main method of the serializer
   5 * is the serialize method which takes and AMFObject as it's argument
   6 * and builds the resulting amf body.
   7 * 
   8 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
   9 * @copyright (c) 2003 amfphp.org
  10 * @package flashservices
  11 * @subpackage io
  12 * @version $Id: AMFSerializer.php,v 1.39 2005/07/22 10:58:11 pmineault Exp $
  13 */
  14
  15define("MAX_STORED_OBJECTS", 1024);
  16
  17include_once(AMFPHP_BASE . "amf/io/AMFBaseSerializer.php");
  18
  19class AMFSerializer extends AMFBaseSerializer {
  20
  21	/**
  22	 * Classes that are serialized as recordsets
  23	 */                         
  24   var $amf0StoredObjects = array();
  25   var $storedObjects = array();
  26   var $storedDefinitions = 0;
  27   var $storedStrings = array();
  28   var $outBuffer;
  29   var $encounteredStrings = array();
  30   
  31   var $native = false;
  32
  33	/**
  34	 * AMFSerializer is the constructor function.  You must pass the
  35	 * method an AMFOutputStream as the single argument.
  36	 * 
  37	 * @param object $stream The AMFOutputStream
  38	 */
  39	function AMFSerializer() {
  40		AMFBaseSerializer::AMFBaseSerializer();
  41	} 
  42
  43	/**
  44	 * writeBoolean writes the boolean code (0x01) and the data to the output stream
  45	 * 
  46	 * @param bool $d The boolean value
  47	 */
  48	function writeBoolean($d) {
  49		$this->writeByte(1); // write the boolean flag
  50		$this->writeByte($d); // write  the boolean byte
  51	} 
  52
  53	/**
  54	 * writeString writes the string code (0x02) and the UTF8 encoded
  55	 * string to the output stream.
  56	 * Note: strings are truncated to 64k max length. Use XML as type 
  57	 * to send longer strings
  58	 * 
  59	 * @param string $d The string data
  60	 */
  61	function writeString($d) {
  62		$count = strlen($d);
  63		if($count < 65536)
  64		{
  65			$this->writeByte(2);
  66			$this->writeUTF($d);
  67		}
  68		else
  69		{
  70			$this->writeByte(12);
  71			$this->writeLongUTF($d);
  72		}
  73	}
  74	
  75	/**
  76	 * writeXML writes the xml code (0x0F) and the XML string to the output stream
  77	 * Note: strips whitespace
  78	 * @param string $d The XML string
  79	 */
  80	function writeXML($d) {
  81		if(!$this->writeReferenceIfExists($d))
  82		{
  83			$this->writeByte(15);
  84			$this->writeLongUTF(preg_replace('/\>(\n|\r|\r\n| |\t)*\</','><',trim($d)));
  85		}
  86	} 
  87
  88	/**
  89	 * writeData writes the date code (0x0B) and the date value to the output stream
  90	 * 
  91	 * @param date $d The date value
  92	 */
  93	function writeDate($d) {
  94		$this->writeByte(11); // write  date code
  95		$this->writeDouble($d); //  write date (milliseconds from 1970)
  96		/**
  97		 * write timezone
  98		 * ?? this is wierd -- put what you like and it pumps it back into flash at the current GMT ??
  99		 * have a look at the amf it creates...
 100		 */
 101		$this->writeInt(0);
 102	}
 103
 104	/**
 105	 * writeNumber writes the number code (0x00) and the numeric data to the output stream
 106	 * All numbers passed through remoting are floats.
 107	 * 
 108	 * @param int $d The numeric data
 109	 */
 110	function writeNumber($d) {
 111		$this->writeByte(0); // write the number code
 112		$this->writeDouble(floatval($d)); // write  the number as a double
 113	} 
 114
 115	/**
 116	 * writeNull writes the null code (0x05) to the output stream
 117	 */
 118	function writeNull() {
 119		$this->writeByte(5); // null is only a  0x05 flag
 120	} 
 121
 122	/**
 123	 * writeArray first deterines if the PHP array contains all numeric indexes
 124	 * or a mix of keys.  Then it either writes the array code (0x0A) or the
 125	 * object code (0x03) and then the associated data.
 126	 * 
 127	 * @param array $d The php array
 128	 */
 129	function writeArray($d) 
 130	{
 131		if($this->writeReferenceIfExists($d))
 132		{
 133			return;
 134		}
 135		
 136		$numeric = array(); // holder to store the numeric keys
 137		$string = array(); // holder to store the string keys
 138		$len = count($d); // get the total number of entries for the array
 139		$largestKey = -1;
 140		foreach($d as $key => $data) { // loop over each element
 141			if (is_int($key) && ($key >= 0)) { // make sure the keys are numeric
 142				$numeric[$key] = $data; // The key is an index in an array
 143				$largestKey = max($largestKey, $key);
 144			} else {
 145				$string[$key] = $data; // The key is a property of an object
 146			} 
 147		} 
 148		$num_count = count($numeric); // get the number of numeric keys
 149		$str_count = count($string); // get the number of string keys
 150
 151		if ( ($num_count > 0 && $str_count > 0) || 
 152			 ($num_count > 0 && $largestKey != $num_count - 1)) { // this is a mixed array
 153			
 154			$this->writeByte(8); // write the mixed array code
 155			$this->writeLong($num_count); // write  the count of items in the array
 156			$this->writeObjectFromArray($numeric + $string); // write the numeric and string keys in the mixed array
 157		} else if ($num_count > 0) { // this is just an array
 158			
 159			$num_count = count($numeric); // get the new count
 160			
 161			$this->writeByte(10); // write  the array code
 162			$this->writeLong($num_count); // write  the count of items in the array
 163			for($i = 0 ; $i < $num_count ; $i++) { // write all of the array elements
 164				$this->writeData($numeric[$i]);
 165			} 
 166		} else if($str_count > 0) { // this is an object
 167			$this->writeByte(3); // this is an  object so write the object code
 168			$this->writeObjectFromArray($string); // write the object name/value pairs
 169		} else { //Patch submitted by Jason Justman
 170			
 171			$this->writeByte(10); // make this  an array still
 172			$this->writeInt(0); //  give it 0 elements
 173			$this->writeInt(0); //  give it an element pad, this looks like a bug in Flash,
 174											//but keeps the next alignment proper
 175		}
 176	}
 177	
 178	function writeReferenceIfExists($d)
 179	{
 180		if(count($this->amf0StoredObjects) >= MAX_STORED_OBJECTS)
 181		{
 182			return false;
 183		}
 184		if(is_array($d))
 185		{
 186			$this->amf0StoredObjects[] = "";
 187			return false;
 188		}
 189		if(($key = patched_array_search($d, $this->amf0StoredObjects, true)) !== FALSE)
 190		{
 191			$this->writeReference($key);
 192			return true;
 193		}
 194		else
 195		{
 196			$this->amf0StoredObjects[] = & $d;
 197			return false;
 198		}
 199	}
 200	
 201	function writeReference($num)
 202	{
 203		$this->writeByte(0x07);
 204		$this->writeInt($num);
 205	}
 206	
 207	/**
 208	 * Write a plain numeric array without anything fancy
 209	 */
 210	function writePlainArray($d)
 211	{
 212		if(!$this->writeReferenceIfExists($d))
 213		{
 214			$num_count = count($d);
 215			$this->writeByte(10); // write  the mixed array code
 216			$this->writeLong($num_count); // write  the count of items in the array
 217			for($i = 0 ; $i < $num_count ; $i++) { // write all of the array elements
 218				$this->writeData($d[$i]);
 219			}
 220		} 
 221	}
 222
 223	/**
 224	 * writeObject handles writing a php array with string or mixed keys.  It does
 225	 * not write the object code as that is handled by the writeArray and this method
 226	 * is shared with the CustomClass writer which doesn't use the object code.
 227	 * 
 228	 * @param array $d The php array with string keys
 229	 */
 230	function writeObjectFromArray($d) {
 231		foreach($d as $key => $data) { // loop over each element
 232			$this->writeUTF($key);  // write the name of the object
 233			$this->writeData($data); // write the value of the object
 234		} 
 235		$this->writeInt(0); //  write the end object flag 0x00, 0x00, 0x09
 236		$this->writeByte(9);
 237	} 
 238	
 239	/**
 240	 * writeObject handles writing a php array with string or mixed keys.  It does
 241	 * not write the object code as that is handled by the writeArray and this method
 242	 * is shared with the CustomClass writer which doesn't use the object code.
 243	 * 
 244	 * @param array $d The php array with string keys
 245	 */
 246	function writeAnonymousObject($d) {
 247		if(!$this->writeReferenceIfExists($d))
 248		{
 249			$this->writeByte(3);
 250			$objVars = (array) $d;
 251			foreach($d as $key => $data) { // loop over each element
 252				if($key[0] != "\0")
 253				{
 254					$this->writeUTF($key);  // write the name of the object
 255					$this->writeData($data); // write the value of the object
 256				}
 257			} 
 258			$this->writeInt(0); //  write the end object flag 0x00, 0x00, 0x09
 259			$this->writeByte(9);
 260		}
 261	} 
 262
 263	/**
 264	 * writePHPObject takes an instance of a class and writes the variables defined
 265	 * in it to the output stream.
 266	 * To accomplish this we just blanket grab all of the object vars with get_object_vars
 267	 * 
 268	 * @param object $d The object to serialize the properties
 269	 */
 270	function writeTypedObject($d) {
 271		if($this->writeReferenceIfExists($d))
 272		{
 273			return;
 274		}
 275		
 276		$this->writeByte(16); // write  the custom class code
 277		$classname = $this->getClassName($d);
 278		
 279		$this->writeUTF($classname); // write the class name
 280		if(AMFPHP_PHP5)
 281		{
 282			$objVars = $d;
 283		}
 284		else
 285		{
 286			$objVars = (array) $d;
 287		}
 288		foreach($objVars as $key => $data) { // loop over each element
 289			if($key[0] != "\0")
 290			{
 291				$this->writeUTF($key);  // write the name of the object
 292				$this->writeData($data); // write the value of the object
 293			}
 294		} 
 295		$this->writeInt(0); //  write the end object flag 0x00, 0x00, 0x09
 296		$this->writeByte(9);
 297	} 
 298
 299	/**
 300	 * writeRecordSet is the abstracted method to write a custom class recordset object.
 301	 * Any recordset from any datasource can be written here, it just needs to be properly formatted
 302	 * beforehand.
 303	 *
 304	 * This was unrolled with at the expense of readability for a 
 305	 * 10 fold increase in speed in large recordsets
 306	 * 
 307	 * @param object $rs The formatted RecordSet object
 308	 */
 309	 
 310	function writeRecordSet(&$rs) 
 311	{
 312		//Low-level everything here to make things faster
 313		//This is the bottleneck of AMFPHP, hence the attention in making things faster
 314		if($this->writeReferenceIfExists($rs))
 315		{
 316			return;
 317		}
 318		
 319		$ob = "";
 320		$data = $rs->rows;
 321		
 322		if($GLOBALS['amfphp']['encoding'] == 'amf0')
 323		{
 324			
 325			$this->writeByte(16); // write  the custom class code
 326			$this->writeUTF("RecordSet"); // write  the class name
 327			$this->writeUTF("serverInfo");
 328			
 329			//Start writing inner object
 330			$this->writeByte(3); // this is an  object so write the object code
 331			
 332			//Write total count
 333			$this->writeUTF("totalCount");
 334			$this->writeNumber($rs->getRowCount());
 335			
 336			//Write initial data
 337			$this->writeUTF("initialData");
 338			
 339			//Inner numeric array
 340			$colnames = $rs->columns;
 341			
 342			$num_count = count($rs->rows);
 343			$this->writeByte(10); // write  the mixed array code
 344			$this->writeLong($num_count); // write  the count of items in the array
 345	
 346			//Allow recordsets to create their own serialized data, which is faster
 347			//since the recordset array is traversed only once
 348			$numcols = count($colnames);
 349			
 350			$ob = "";
 351			$be = $this->isBigEndian;
 352			$fc = pack('N', $numcols);
 353			
 354			for($i = 0 ; $i < $num_count ; $i++) 
 355			{ 
 356				// write all of the array elements
 357				$ob .= "\12" . $fc;
 358
 359				for($j = 0; $j < $numcols; $j++) { // write all of the array elements
 360					
 361					$d = $data[$i][$j];
 362					if (is_string($d)) 
 363					{ // type as string
 364						$os = $this->rsCharsetHandler->transliterate($d);
 365						//string flag, string length, and string
 366						$ob .= "\2" . pack('n', strlen($os)) . $os;
 367					}
 368					elseif (is_float($d) || is_int($d)) 
 369					{ // type as double
 370						$ob .= "\0";
 371						$b = pack('d', $d); // pack the bytes
 372						if ($be) { // if we are a big-endian processor
 373							$r = strrev($b);
 374						} else { // add the bytes to the output
 375							$r = $b;
 376						} 
 377						$ob .= $r;
 378					} 
 379					elseif (is_bool($d)) 
 380					{ //type as bool
 381						$ob .= "\1";
 382						$ob .= pack('c', $d);
 383					} 
 384					elseif (is_null($d)) 
 385					{ // null
 386						$ob .= "\5";
 387					} 
 388				} 
 389			}
 390			$this->outBuffer .= $ob;
 391	
 392			//Write cursor
 393			$this->writeUTF("cursor");
 394			$this->writeNumber(1);
 395			
 396			//Write service name
 397			$this->writeUTF("serviceName");
 398			$this->writeString("PageAbleResult");
 399			
 400			//Write column names
 401			$this->writeUTF("columnNames");
 402			$this->writePlainArray($colnames, 'string');
 403			
 404			//Write version number
 405			$this->writeUTF("version");
 406			$this->writeNumber(1);
 407			
 408			//Write id
 409			$this->writeUTF("id");
 410			$this->writeString($rs->getID());
 411			
 412			//End inner serverInfo object
 413			$this->writeInt(0); //  write the end object flag 0x00, 0x00, 0x09
 414			$this->writeByte(9);
 415			
 416			//End outer recordset object
 417			$this->writeInt(0); //  write the end object flag 0x00, 0x00, 0x09
 418			$this->writeByte(9);
 419			
 420			$this->paging = -1;
 421		}
 422		else
 423		{	
 424			$numObjects= 0;
 425			$this->writeAmf3ArrayCollectionPreamble();
 426			
 427			//Amf3 array code
 428			$this->writeByte(0x09);
 429			$numObjects++;
 430			
 431			$numRows = count($rs->rows);
 432			$toPack = 2*$numRows + 1;
 433			
 434			//Write the number of rows
 435			$this->writeAmf3Int($toPack);
 436			
 437			//No string keys in this array
 438			$this->writeByte(0x01);
 439			
 440			$numCols = count($rs->columns);
 441			
 442			$columnStringOffsets = array();
 443			if($numRows > 0)
 444			{
 445				$j = 0;
 446				$colNames = array();
 447				$rows = $rs->rows;
 448				
 449				foreach ($rows as $key => $line) {
 450					
 451					//Usually we don't use class defs in the serializer since we don't 
 452					//have sealed objects in php, but for recordsets we do use them
 453					//since they are well suited for what we have to do (the same keys
 454					//across all objects)
 455					if($key == 0)
 456					{
 457						$this->outBuffer .= "\12";
 458						$this->writeAmf3Int($numCols << 4 | 3);
 459						$this->outBuffer .= "\1";
 460						foreach($rs->columns as $key => $val)
 461						{
 462							$this->writeAmf3String($val);
 463						}
 464						$defOffset = $this->getAmf3Int(
 465							($this->storedDefinitions) << 2 | 1
 466							);
 467						$this->storedDefinitions++;
 468					}
 469					else
 470					{
 471						$this->outBuffer .= "\12" . $defOffset;
 472					}
 473					$numObjects++;
 474					
 475					for($i = 0; $i < $numCols; $i++)
 476					{
 477						//Write the col name
 478						$value = $line[$i];
 479						if(is_string($value))
 480						{
 481							$this->outBuffer .= "\6";
 482							$value = $this->rsCharsetHandler->transliterate($value);
 483							$this->writeAmf3String($value, true);
 484						}
 485						elseif(is_int($value)) 
 486						{ //int
 487							$this->writeAmf3Number($value);
 488						} 
 489						elseif(is_float($value))
 490						{ //double
 491							$this->outBuffer .= "\5";
 492							$b = pack("d", $value); // pack the bytes
 493							if ($this->isBigEndian) { // if we are a big-endian processor
 494								$r = strrev($b);
 495							} else { // add the bytes to the output
 496								$r = $b;
 497							} 
 498							
 499							$this->outBuffer .= $r;
 500						}
 501						elseif(is_bool($value))
 502						{
 503							$this->outBuffer .= $value ? "\3" : "\2";
 504						}
 505						else
 506						{
 507							$this->outBuffer .= "\1"; //null
 508						}
 509					}
 510					//End object
 511				}
 512			}
 513			
 514			//Add fake objects to make sure the object counter still works
 515			for($i = 0; $i < $numObjects; $i++)
 516			{
 517				$this->storedObjects[] = "";
 518			}
 519		}
 520	}
 521
 522	/**
 523	 * writeData checks to see if the type was declared and then either
 524	 * auto negotiates the type or relies on the user defined type to
 525	 * serialize the data into amf
 526	 *
 527	 * Note that autoNegotiateType was eliminated in order to tame the 
 528	 * call stack which was getting huge and was causing leaks
 529	 *
 530	 * manualType allows the developer to explicitly set the type of
 531	 * the returned data.  The returned data is validated for most of the
 532	 * cases when possible.  Some datatypes like xml and date have to
 533	 * be returned this way in order for the Flash client to correctly serialize them
 534	 * 
 535	 * recordsets appears top on the list because that will probably be the most
 536	 * common hit in this method.  Followed by the
 537	 * datatypes that have to be manually set.  Then the auto negotiatable types last.
 538	 * The order may be changed for optimization.
 539	 * 
 540	 * @param mixed $d The data
 541	 * @param string $type The optional type
 542	 */
 543	function writeData(& $d) {
 544		if (is_int($d) || is_float($d)) 
 545		{ // double
 546			$this->writeNumber($d);
 547			return;
 548		} 
 549		elseif (is_string($d)) 
 550		{ // string
 551			$this->writeString($d);
 552			return;
 553		} 
 554		elseif (is_bool($d)) 
 555		{ // boolean
 556			$this->writeBoolean($d);
 557			return;
 558		} 
 559		elseif (is_null($d)) 
 560		{ // null
 561			$this->writeNull();
 562			return;
 563		} 
 564		elseif ($GLOBALS['amfphp']['encoding'] == 'amf3')
 565		{
 566			$this->writeByte(0x11);
 567			$this->writeAmf3Data($d);
 568			return;
 569		}
 570		elseif (is_array($d)) 
 571		{ // array
 572			$this->writeArray($d);
 573			return;
 574		} 
 575		elseif (is_resource($d)) 
 576		{ // resource
 577			$type = get_resource_type($d);
 578			list($type, $subtype) = $this->sanitizeType($type);
 579		} 
 580		elseif (is_object($d))
 581		{
 582			$className = strtolower(get_class($d));
 583			if(array_key_exists($className, $this->resourceObjects))
 584			{
 585				$type = "__RECORDSET__";
 586				$subtype = $this->resourceObjects[strtolower(get_class($d))];
 587			}
 588			else if(AMFPHP_PHP5 && $className == 'domdocument')
 589			{
 590				$this->writeXML($d->saveXml());
 591				return;
 592			}
 593			else if(!AMFPHP_PHP5 && $className == 'domdocument')
 594			{
 595				$this->writeXML($d->dump_mem());
 596				return;
 597			}
 598			elseif($className == "simplexmlelement")
 599			{
 600				$this->writeXML($d->asXML());
 601				return;
 602			}
 603			else if($className == 'stdclass' && !isset($d->_explicitType))
 604			{
 605				$this->writeAnonymousObject($d);
 606				return;
 607			}
 608			elseif(is_a($d, 'ArrayAccess') || is_a($d, 'ArrayObject'))
 609			{
 610				$this->writeArray($d);
 611				return;
 612			}
 613			else
 614			{
 615				$this->writeTypedObject($d);
 616				return;
 617			}
 618		}
 619		else
 620		{
 621			$type = gettype($d);
 622		}
 623		
 624		switch ($type) {
 625			case "__RECORDSET__" :
 626				$classname = $subtype . "Adapter"; // full class name
 627				$includeFile = include_once(AMFPHP_BASE . "shared/adapters/" . $classname . ".php"); // try to load the recordset library from the sql folder
 628				if (!$includeFile) {
 629					trigger_error("The recordset filter class " . $classname . " was not found", E_USER_ERROR);
 630				} 
 631				$recordSet = new $classname($d); // returns formatted recordset
 632				$this->writeRecordSet($recordSet); // writes the recordset formatted for Flash
 633				break;
 634			default: 
 635				// non of the above so lets assume its a Custom Class thats defined in the client
 636				$this->writeTypedObject($unsanitizedType, $d);
 637				// trigger_error("Unsupported Datatype");
 638				break;
 639		} 
 640	}
 641	
 642	/********************************************************************************
 643	 *                             AMF3 related code
 644	 *******************************************************************************/
 645	
 646	function writeAmf3Data(& $d)
 647	{
 648		if (is_int($d)) 
 649		{ //int
 650			$this->writeAmf3Number($d);
 651			return;
 652		} 
 653		elseif(is_float($d))
 654		{ //double
 655			$this->outBuffer .= "\5";
 656			$this->writeDouble($d);
 657			return;
 658		}
 659		elseif (is_string($d)) 
 660		{ // string
 661			$this->outBuffer .= "\6";
 662			$this->writeAmf3String($d);
 663			return;
 664		} 
 665		elseif (is_bool($d)) 
 666		{ // boolean
 667			$this->writeAmf3Bool($d);
 668			return;
 669		} 
 670		elseif (is_null($d)) 
 671		{ // null
 672			$this->writeAmf3Null();
 673			return;
 674		} 
 675		elseif (is_array($d)) 
 676		{ // array
 677			$this->writeAmf3Array($d);
 678			return;
 679		} 
 680		elseif (is_resource($d)) 
 681		{ // resource
 682			$type = get_resource_type($d);
 683			list($type, $subtype) = $this->sanitizeType($type);
 684		} 
 685		elseif (is_object($d))
 686		{
 687			$className = strtolower(get_class($d));
 688			if(array_key_exists($className, $this->resourceObjects))
 689			{
 690				$type = "__RECORDSET__";
 691				$subtype = $this->resourceObjects[strtolower(get_class($d))];
 692			}
 693			else if(AMFPHP_PHP5 && $className == 'domdocument')
 694			{
 695				$this->writeAmf3Xml($d->saveXml());
 696				return;
 697			}
 698			else if(!AMFPHP_PHP5 && $className == 'domdocument')
 699			{
 700				$this->writeAmf3Xml($d->dump_mem());
 701				return;
 702			}
 703			elseif($className == "simplexmlelement")
 704			{
 705				$this->writeAmf3Xml($d->asXML());
 706				return;
 707			}
 708			elseif($className == 'bytearray')
 709			{
 710				$this->writeAmf3ByteArray($d->data);
 711				return;
 712			}
 713			elseif(is_a($d, 'ArrayAccess') || is_a($d, 'ArrayObject'))
 714			{
 715				$this->writeAmf3Array($d, true);
 716				return;
 717			}
 718			else
 719			{
 720				$this->writeAmf3Object($d);
 721				return;
 722			}
 723		}
 724		else
 725		{
 726			$type = gettype($d);
 727		}
 728		
 729		switch ($type) {
 730			case "__RECORDSET__" :
 731				$classname = $subtype . "Adapter"; // full class name
 732				$includeFile = include_once(AMFPHP_BASE . "shared/adapters/" . $classname . ".php"); // try to load the recordset library from the sql folder
 733				if (!$includeFile) {
 734					trigger_error("The recordset filter class " . $classname . " was not found");
 735				} 
 736				$GLOBALS['amfphp']['stringOffset'] = count($this->storedStrings);
 737				$recordSet = new $classname($d); // returns formatted recordset
 738				
 739				$this->writeRecordSet($recordSet); // writes the recordset formatted for Flash
 740				break;
 741			default: 
 742				// non of the above so lets assume its a Custom Class thats defined in the client
 743				//$this->writeTypedObject($unsanitizedType, $d);
 744				trigger_error("Unsupported Datatype: " . $type);
 745				break;
 746		} 
 747	}
 748	
 749	/**
 750	 * Write an ArrayCollection
 751	 */
 752	function writeAmf3ArrayCollectionPreamble()
 753	{
 754		$this->writeByte(0x0a);
 755		$this->writeByte(0x07);
 756		$this->writeAmf3String("flex.messaging.io.ArrayCollection");
 757		$this->storedDefinitions++;
 758		$this->storedObjects[] = "";
 759	}
 760
 761	function writeAmf3Null()
 762	{
 763		//Write the null code (0x1) to the output stream.
 764		$this->outBuffer .= "\1";
 765	}
 766
 767	function writeAmf3Bool($d)
 768	{
 769		$this->outBuffer .= $d ? "\3" : "\2";
 770	}
 771	
 772	function writeAmf3Int($d)
 773	{
 774		//Sign contraction - the high order bit of the resulting value must match every bit removed from the number
 775		//Clear 3 bits 
 776		$d &= 0x1fffffff;
 777		if($d < 0x80)
 778		{
 779			$this->outBuffer .= chr($d);
 780		}
 781		elseif($d < 0x4000)
 782		{
 783			$this->outBuffer .= chr($d >> 7 & 0x7f | 0x80) . chr($d & 0x7f);
 784		}
 785		elseif($d < 0x200000)
 786		{
 787			$this->outBuffer .= chr($d >> 14 & 0x7f | 0x80) . chr($d >> 7 & 0x7f | 0x80) . chr($d & 0x7f);
 788		} 
 789		else
 790		{
 791			$this->outBuffer .= chr($d >> 22 & 0x7f | 0x80) . chr($d >> 15 & 0x7f | 0x80) . 
 792				   chr($d >> 8 & 0x7f | 0x80) . chr($d & 0xff);
 793		}
 794	}
 795
 796	function writeAmf3String($d, $raw = false)
 797	{
 798		if( $d == "" )
 799		{
 800			//Write 0x01 to specify the empty ctring
 801			$this->outBuffer .= "\1";
 802		}
 803		else
 804		{
 805			if( !isset($this->storedStrings[$d]))
 806			{
 807				if(strlen($d) < 64)
 808				{
 809					$this->storedStrings[$d] = $this->encounteredStrings;
 810				}
 811				if(!$raw)
 812				{
 813					$d = $this->charsetHandler->transliterate($d);
 814				}
 815				
 816				$handle = strlen($d);
 817				$this->writeAmf3Int($handle*2 + 1);
 818				$this->outBuffer .= $d;
 819				$this->encounteredStrings++;
 820				return $this->encounteredStrings - 1;
 821			}
 822			else
 823			{
 824				$key = $this->storedStrings[$d];
 825				$handle = $key << 1;
 826				$this->writeAmf3Int($handle);
 827				return $key;
 828			}
 829		}
 830	}
 831
 832	function writeAmf3Array($d, $arrayCollectionable = false)
 833	{
 834		//Circular referencing is disabled in arrays
 835		//Because if the array contains only primitive values,
 836		//Then === will say that the two arrays are strictly equal
 837		//if they contain the same values, even if they are really distinct
 838		//if(($key = patched_array_search($d, $this->storedObjects, TRUE)) === FALSE )
 839		//{
 840			if(count($this->storedObjects) < MAX_STORED_OBJECTS)
 841			{
 842				$this->storedObjects[] = & $d;
 843			}
 844			
 845			$numeric = array(); // holder to store the numeric keys
 846			$string = array(); // holder to store the string keys
 847			$len = count($d); // get the total number of entries for the array
 848			$largestKey = -1;
 849			foreach($d as $key => $data) { // loop over each element
 850				if (is_int($key) && ($key >= 0)) { // make sure the keys are numeric
 851					$numeric[$key] = $data; // The key is an index in an array
 852					$largestKey = max($largestKey, $key);
 853				} else {
 854					$string[$key] = $data; // The key is a property of an object
 855				} 
 856			} 
 857			$num_count = count($numeric); // get the number of numeric keys
 858			$str_count = count($string); // get the number of string keys
 859
 860			if (($str_count > 0 && $num_count == 0)  || 
 861			    ($num_count > 0 && $largestKey != $num_count - 1)) { // this is a mixed array
 862				$this->writeAmf3ObjectFromArray($numeric + $string); // write the numeric and string keys in the mixed array
 863			} else { // this is just an array
 864				if($arrayCollectionable)
 865				{
 866					$this->writeAmf3ArrayCollectionPreamble();
 867				}
 868				
 869				$num_count = count($numeric);
 870				
 871				$this->outBuffer .= "\11";
 872				$handle = $num_count * 2 + 1;
 873				$this->writeAmf3Int($handle);
 874				
 875				foreach($string as $key => $val)
 876				{
 877					$this->writeAmf3String($key);
 878					$this->writeAmf3Data($val);
 879				}
 880				$this->writeAmf3String(""); //End start hash
 881				
 882				for($i = 0; $i < $num_count; $i++)
 883				{
 884					$this->writeAmf3Data($numeric[$i]);
 885				}
 886			}
 887		//}
 888		//else
 889		//{
 890		//	$handle = $key << 1;
 891		//	$this->outBuffer .= "\11";
 892		//	$this->writeAmf3Int($handle);
 893		//}
 894	}
 895	
 896	function writeAmf3ObjectFromArray($d)
 897	{
 898		//Type this as a dynamic object
 899		$this->outBuffer .= "\12\13\1";
 900		
 901		foreach($d as $key => $val)
 902		{
 903			$this->writeAmf3String($key);
 904			$this->writeAmf3Data($val);
 905		}
 906		//Now we close the open object
 907		$this->outBuffer .= "\1";
 908	}
 909
 910	/*
 911	public void WriteAMF3DateTime(DateTime value)
 912	{
 913		if( !_objectReferences.Contains(value) )
 914		{
 915			_objectReferences.Add(value, _objectReferences.Count);
 916			int handle = 1;
 917			WriteAMF3IntegerData(handle);
 918
 919			// Write date (milliseconds from 1970).
 920			DateTime timeStart = new DateTime(1970, 1, 1, 0, 0, 0);
 921
 922			string timezoneCompensation = System.Configuration.ConfigurationSettings.AppSettings["timezoneCompensation"];
 923			if( timezoneCompensation != null && ( timezoneCompensation.ToLower() == "auto" ) )
 924			{
 925				value = value.ToUniversalTime();
 926			}
 927			
 928			TimeSpan span = value.Subtract(timeStart);
 929			long milliSeconds = (long)span.TotalMilliseconds;
 930			long date = BitConverter.DoubleToInt64Bits((double)milliSeconds);
 931			this.WriteLong(date);
 932		}
 933		else
 934		{
 935			int handle = (int)_objectReferences[value];
 936			handle = handle << 1;
 937			WriteAMF3IntegerData(handle);
 938		}
 939	}
 940	*/
 941	
 942	function getAmf3Int($d)
 943	{
 944		$d &= 0x1fffffff;
 945		if($d < 0x80)
 946		{
 947			return chr($d);
 948		}
 949		elseif($d < 0x4000)
 950		{
 951			return chr($d >> 7 & 0x7f | 0x80) . chr($d & 0x7f);
 952		}
 953		elseif($d < 0x200000)
 954		{
 955			return chr($d >> 14 & 0x7f | 0x80) . chr($d >> 7 & 0x7f | 0x80) . chr($d & 0x7f);
 956		} 
 957		else
 958		{
 959			return chr($d >> 22 & 0x7f | 0x80) . chr($d >> 15 & 0x7f | 0x80) . 
 960				   chr($d >> 8 & 0x7f | 0x80) . chr($d & 0xff);
 961		}
 962	}
 963
 964	function writeAmf3Number($d)
 965	{
 966		if($d >= -268435456 && $d <= 268435455)//check valid range for 29bits
 967		{
 968			$this->outBuffer .= "\4";
 969			$this->writeAmf3Int($d);
 970		}
 971		else
 972		{
 973			//overflow condition would occur upon int conversion
 974			$this->outBuffer .= "\5";
 975			$this->writeDouble($d);
 976		}
 977	}
 978
 979	function writeAmf3Xml($d)
 980	{
 981		$d = preg_replace('/\>(\n|\r|\r\n| |\t)*\</','><',trim($d));
 982		$this->writeByte(0x07);
 983		$this->writeAmf3String($d);
 984	}
 985	
 986	function writeAmf3ByteArray($d)
 987	{
 988		$this->writeByte(0x0C);
 989		$this->writeAmf3String($d, true);
 990	}
 991
 992	function writeAmf3Object($d)
 993	{
 994		//Write the object tag
 995		$this->outBuffer .= "\12";
 996		if( ($key = patched_array_search($d, $this->storedObjects, TRUE)) === FALSE && $key === FALSE)
 997		{
 998			if(count($this->storedObjects) < MAX_STORED_OBJECTS)
 999			{
1000				$this->storedObjects[] = & $d;
1001			}
1002			
1003			$this->storedDefinitions++;
1004
1005			//Type the object as an array
1006			if(AMFPHP_PHP5)
1007			{
1008				$obj = $d;
1009			}
1010			else
1011			{
1012				$obj = (array) $d;
1013			}
1014			$realObj = array();
1015			foreach($obj as $key => $val)
1016			{
1017				if($key[0] != "\0" && $key != '_explicitType') //Don't show private members
1018				{
1019					$realObj[$key] = $val;
1020				}
1021			}
1022			
1023			//Type this as a dynamic object
1024			$this->outBuffer .= "\13";
1025			
1026			$classname = $this->getClassName($d);
1027			
1028			$this->writeAmf3String($classname);
1029			
1030			foreach($realObj as $key => $val)
1031			{
1032				$this->writeAmf3String($key);
1033				$this->writeAmf3Data($val);
1034			}
1035			//Now we close the open object
1036			$this->outBuffer .= "\1";
1037		}
1038		else
1039		{
1040			$handle = $key << 1;
1041			$this->writeAmf3Int($handle);
1042		}
1043	}
1044}
1045
1046
1047
1048?>