PageRenderTime 66ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/system/services/core/amf/io/AMFSerializer.php

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