PageRenderTime 53ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/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
Possible License(s): LGPL-2.1, AGPL-1.0, GPL-2.0, MIT
  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. elseif(is_a($d, 'ArrayAccess') || is_a($d, 'ArrayObject'))
  547. {
  548. $this->writeArray($d);
  549. return;
  550. }
  551. else
  552. {
  553. $this->writeTypedObject($d);
  554. return;
  555. }
  556. }
  557. else
  558. {
  559. $type = gettype($d);
  560. }
  561. switch ($type) {
  562. case "__RECORDSET__" :
  563. $classname = $subtype . "Adapter"; // full class name
  564. $includeFile = include_once(AMFPHP_BASE . "shared/adapters/" . $classname . ".php"); // try to load the recordset library from the sql folder
  565. if (!$includeFile) {
  566. trigger_error("The recordset filter class " . $classname . " was not found", E_USER_ERROR);
  567. }
  568. $recordSet = new $classname($d); // returns formatted recordset
  569. $this->writeRecordSet($recordSet); // writes the recordset formatted for Flash
  570. break;
  571. default:
  572. // non of the above so lets assume its a Custom Class thats defined in the client
  573. $this->writeTypedObject($unsanitizedType, $d);
  574. // trigger_error("Unsupported Datatype");
  575. break;
  576. }
  577. }
  578. /********************************************************************************
  579. * AMF3 related code
  580. *******************************************************************************/
  581. function writeAmf3Data(& $d)
  582. {
  583. if (is_int($d))
  584. { //int
  585. $this->writeAmf3Number($d);
  586. return;
  587. }
  588. elseif(is_float($d))
  589. { //double
  590. $this->outBuffer .= "\5";
  591. $this->writeDouble($d);
  592. return;
  593. }
  594. elseif (is_string($d))
  595. { // string
  596. $this->outBuffer .= "\6";
  597. $this->writeAmf3String($d);
  598. return;
  599. }
  600. elseif (is_bool($d))
  601. { // boolean
  602. $this->writeAmf3Bool($d);
  603. return;
  604. }
  605. elseif (is_null($d))
  606. { // null
  607. $this->writeAmf3Null();
  608. return;
  609. }
  610. elseif (is_array($d))
  611. { // array
  612. $this->writeAmf3Array($d);
  613. return;
  614. }
  615. elseif (is_resource($d))
  616. { // resource
  617. $type = get_resource_type($d);
  618. list($type, $subtype) = $this->sanitizeType($type);
  619. }
  620. elseif (is_object($d))
  621. {
  622. $className = strtolower(get_class($d));
  623. if(array_key_exists($className, $this->resourceObjects))
  624. {
  625. $type = "__RECORDSET__";
  626. $subtype = $this->resourceObjects[strtolower(get_class($d))];
  627. }
  628. else if(AMFPHP_PHP5 && $className == 'domdocument')
  629. {
  630. $this->writeAmf3Xml($d->saveXml());
  631. return;
  632. }
  633. else if(!AMFPHP_PHP5 && $className == 'domdocument')
  634. {
  635. $this->writeAmf3Xml($d->dump_mem());
  636. return;
  637. }
  638. elseif($className == "simplexmlelement")
  639. {
  640. $this->writeAmf3Xml($d->asXML());
  641. return;
  642. }
  643. elseif($className == 'bytearray')
  644. {
  645. $this->writeAmf3ByteArray($d->data);
  646. return;
  647. }
  648. elseif(is_a($d, 'ArrayAccess') || is_a($d, 'ArrayObject'))
  649. {
  650. $this->writeAmf3Array($d, true);
  651. return;
  652. }
  653. else
  654. {
  655. $this->writeAmf3Object($d);
  656. return;
  657. }
  658. }
  659. else
  660. {
  661. $type = gettype($d);
  662. }
  663. switch ($type) {
  664. case "__RECORDSET__" :
  665. $classname = $subtype . "Adapter"; // full class name
  666. $includeFile = include_once(AMFPHP_BASE . "shared/adapters/" . $classname . ".php"); // try to load the recordset library from the sql folder
  667. if (!$includeFile) {
  668. trigger_error("The recordset filter class " . $classname . " was not found");
  669. }
  670. $GLOBALS['amfphp']['stringOffset'] = count($this->storedStrings);
  671. $recordSet = new $classname($d); // returns formatted recordset
  672. $this->writeRecordSet($recordSet); // writes the recordset formatted for Flash
  673. break;
  674. default:
  675. // non of the above so lets assume its a Custom Class thats defined in the client
  676. //$this->writeTypedObject($unsanitizedType, $d);
  677. trigger_error("Unsupported Datatype: " . $type);
  678. break;
  679. }
  680. }
  681. /**
  682. * Write an ArrayCollection
  683. */
  684. function writeAmf3ArrayCollectionPreamble()
  685. {
  686. $this->writeByte(0x0a);
  687. $this->writeByte(0x07);
  688. $this->writeAmf3String("flex.messaging.io.ArrayCollection");
  689. $this->storedDefinitions++;
  690. $this->storedObjects[] = "";
  691. }
  692. function writeAmf3Null()
  693. {
  694. //Write the null code (0x1) to the output stream.
  695. $this->outBuffer .= "\1";
  696. }
  697. function writeAmf3Bool($d)
  698. {
  699. $this->outBuffer .= $d ? "\3" : "\2";
  700. }
  701. function writeAmf3Int($d)
  702. {
  703. //Sign contraction - the high order bit of the resulting value must match every bit removed from the number
  704. //Clear 3 bits
  705. $d &= 0x1fffffff;
  706. if($d < 0x80)
  707. {
  708. $this->outBuffer .= chr($d);
  709. }
  710. elseif($d < 0x4000)
  711. {
  712. $this->outBuffer .= chr($d >> 7 & 0x7f | 0x80) . chr($d & 0x7f);
  713. }
  714. elseif($d < 0x200000)
  715. {
  716. $this->outBuffer .= chr($d >> 14 & 0x7f | 0x80) . chr($d >> 7 & 0x7f | 0x80) . chr($d & 0x7f);
  717. }
  718. else
  719. {
  720. $this->outBuffer .= chr($d >> 22 & 0x7f | 0x80) . chr($d >> 15 & 0x7f | 0x80) .
  721. chr($d >> 8 & 0x7f | 0x80) . chr($d & 0xff);
  722. }
  723. }
  724. function writeAmf3String($d, $raw = false)
  725. {
  726. if( $d == "" )
  727. {
  728. //Write 0x01 to specify the empty ctring
  729. $this->outBuffer .= "\1";
  730. }
  731. else
  732. {
  733. if( !isset($this->storedStrings[$d]))
  734. {
  735. if(strlen($d) < 64)
  736. {
  737. $this->storedStrings[$d] = $this->encounteredStrings;
  738. }
  739. if(!$raw)
  740. {
  741. $d = $this->charsetHandler->transliterate($d);
  742. }
  743. $handle = strlen($d);
  744. $this->writeAmf3Int($handle*2 + 1);
  745. $this->outBuffer .= $d;
  746. $this->encounteredStrings++;
  747. return $this->encounteredStrings - 1;
  748. }
  749. else
  750. {
  751. $key = $this->storedStrings[$d];
  752. $handle = $key << 1;
  753. $this->writeAmf3Int($handle);
  754. return $key;
  755. }
  756. }
  757. }
  758. function writeAmf3Array($d, $arrayCollectionable = false)
  759. {
  760. //Circular referencing is disabled in arrays
  761. //Because if the array contains only primitive values,
  762. //Then === will say that the two arrays are strictly equal
  763. //if they contain the same values, even if they are really distinct
  764. //if(($key = patched_array_search($d, $this->storedObjects, TRUE)) === FALSE )
  765. //{
  766. if(count($this->storedObjects) < MAX_STORED_OBJECTS)
  767. {
  768. $this->storedObjects[] = & $d;
  769. }
  770. $numeric = array(); // holder to store the numeric keys
  771. $string = array(); // holder to store the string keys
  772. $len = count($d); // get the total number of entries for the array
  773. $largestKey = -1;
  774. foreach($d as $key => $data) { // loop over each element
  775. if (is_int($key) && ($key >= 0)) { // make sure the keys are numeric
  776. $numeric[$key] = $data; // The key is an index in an array
  777. $largestKey = max($largestKey, $key);
  778. } else {
  779. $string[$key] = $data; // The key is a property of an object
  780. }
  781. }
  782. $num_count = count($numeric); // get the number of numeric keys
  783. $str_count = count($string); // get the number of string keys
  784. if (($str_count > 0 && $num_count == 0) ||
  785. ($num_count > 0 && $largestKey != $num_count - 1)) { // this is a mixed array
  786. $this->writeAmf3ObjectFromArray($numeric + $string); // write the numeric and string keys in the mixed array
  787. } else { // this is just an array
  788. if($arrayCollectionable)
  789. {
  790. $this->writeAmf3ArrayCollectionPreamble();
  791. }
  792. $num_count = count($numeric);
  793. $this->outBuffer .= "\11";
  794. $handle = $num_count * 2 + 1;
  795. $this->writeAmf3Int($handle);
  796. foreach($string as $key => $val)
  797. {
  798. $this->writeAmf3String($key);
  799. $this->writeAmf3Data($val);
  800. }
  801. $this->writeAmf3String(""); //End start hash
  802. for($i = 0; $i < $num_count; $i++)
  803. {
  804. $this->writeAmf3Data($numeric[$i]);
  805. }
  806. }
  807. //}
  808. //else
  809. //{
  810. // $handle = $key << 1;
  811. // $this->outBuffer .= "\11";
  812. // $this->writeAmf3Int($handle);
  813. //}
  814. }
  815. function writeAmf3ObjectFromArray($d)
  816. {
  817. //Type this as a dynamic object
  818. $this->outBuffer .= "\12\13\1";
  819. foreach($d as $key => $val)
  820. {
  821. $this->writeAmf3String($key);
  822. $this->writeAmf3Data($val);
  823. }
  824. //Now we close the open object
  825. $this->outBuffer .= "\1";
  826. }
  827. /*
  828. public void WriteAMF3DateTime(DateTime value)
  829. {
  830. if( !_objectReferences.Contains(value) )
  831. {
  832. _objectReferences.Add(value, _objectReferences.Count);
  833. int handle = 1;
  834. WriteAMF3IntegerData(handle);
  835. // Write date (milliseconds from 1970).
  836. DateTime timeStart = new DateTime(1970, 1, 1, 0, 0, 0);
  837. string timezoneCompensation = System.Configuration.ConfigurationSettings.AppSettings["timezoneCompensation"];
  838. if( timezoneCompensation != null && ( timezoneCompensation.ToLower() == "auto" ) )
  839. {
  840. value = value.ToUniversalTime();
  841. }
  842. TimeSpan span = value.Subtract(timeStart);
  843. long milliSeconds = (long)span.TotalMilliseconds;
  844. long date = BitConverter.DoubleToInt64Bits((double)milliSeconds);
  845. this.WriteLong(date);
  846. }
  847. else
  848. {
  849. int handle = (int)_objectReferences[value];
  850. handle = handle << 1;
  851. WriteAMF3IntegerData(handle);
  852. }
  853. }
  854. */
  855. function getAmf3Int($d)
  856. {
  857. $d &= 0x1fffffff;
  858. if($d < 0x80)
  859. {
  860. return chr($d);
  861. }
  862. elseif($d < 0x4000)
  863. {
  864. return chr($d >> 7 & 0x7f | 0x80) . chr($d & 0x7f);
  865. }
  866. elseif($d < 0x200000)
  867. {
  868. return chr($d >> 14 & 0x7f | 0x80) . chr($d >> 7 & 0x7f | 0x80) . chr($d & 0x7f);
  869. }
  870. else
  871. {
  872. return chr($d >> 22 & 0x7f | 0x80) . chr($d >> 15 & 0x7f | 0x80) .
  873. chr($d >> 8 & 0x7f | 0x80) . chr($d & 0xff);
  874. }
  875. }
  876. function writeAmf3Number($d)
  877. {
  878. if($d >= -268435456 && $d <= 268435455)//check valid range for 29bits
  879. {
  880. $this->outBuffer .= "\4";
  881. $this->writeAmf3Int($d);
  882. }
  883. else
  884. {
  885. //overflow condition would occur upon int conversion
  886. $this->outBuffer .= "\5";
  887. $this->writeDouble($d);
  888. }
  889. }
  890. function writeAmf3Xml($d)
  891. {
  892. $d = preg_replace('/\>(\n|\r|\r\n| |\t)*\</','><',trim($d));
  893. $this->writeByte(0x07);
  894. $this->writeAmf3String($d);
  895. }
  896. function writeAmf3ByteArray($d)
  897. {
  898. $this->writeByte(0x0C);
  899. $this->writeAmf3String($d, true);
  900. }
  901. function writeAmf3Object($d)
  902. {
  903. //Write the object tag
  904. $this->outBuffer .= "\12";
  905. if( ($key = patched_array_search($d, $this->storedObjects, TRUE)) === FALSE && $key === FALSE)
  906. {
  907. if(count($this->storedObjects) < MAX_STORED_OBJECTS)
  908. {
  909. $this->storedObjects[] = & $d;
  910. }
  911. $this->storedDefinitions++;
  912. //Type the object as an array
  913. if(AMFPHP_PHP5)
  914. {
  915. $obj = $d;
  916. }
  917. else
  918. {
  919. $obj = (array) $d;
  920. }
  921. $realObj = array();
  922. foreach($obj as $key => $val)
  923. {
  924. if($key[0] != "\0" && $key != '_explicitType') //Don't show private members
  925. {
  926. $realObj[$key] = $val;
  927. }
  928. }
  929. //Type this as a dynamic object
  930. $this->outBuffer .= "\13";
  931. $classname = $this->getClassName($d);
  932. $this->writeAmf3String($classname);
  933. foreach($realObj as $key => $val)
  934. {
  935. $this->writeAmf3String($key);
  936. $this->writeAmf3Data($val);
  937. }
  938. //Now we close the open object
  939. $this->outBuffer .= "\1";
  940. }
  941. else
  942. {
  943. $handle = $key << 1;
  944. $this->writeAmf3Int($handle);
  945. }
  946. }
  947. }
  948. ?>