PageRenderTime 65ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 0ms

/library/Zend/Serializer/Adapter/PythonPickle.php

https://bitbucket.org/Flightan/phpic
PHP | 1494 lines | 1220 code | 59 blank | 215 comment | 56 complexity | 3ccbf585073900a8537b57c22835d45a MD5 | raw file
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Serializer
  17. * @subpackage Adapter
  18. * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. * @version $Id: PythonPickle.php 20575 2010-01-24 17:48:27Z mabe $
  21. */
  22. /** @see Zend_Serializer_Adapter_AdapterAbstract */
  23. require_once 'Zend/Serializer/Adapter/AdapterAbstract.php';
  24. /**
  25. * @link http://www.python.org
  26. * @see Phython3.1/Lib/pickle.py
  27. * @see Phython3.1/Modules/_pickle.c
  28. * @link http://pickle-js.googlecode.com
  29. * @category Zend
  30. * @package Zend_Serializer
  31. * @subpackage Adapter
  32. * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  33. * @license http://framework.zend.com/license/new-bsd New BSD License
  34. */
  35. class Zend_Serializer_Adapter_PythonPickle extends Zend_Serializer_Adapter_AdapterAbstract
  36. {
  37. /* Pickle opcodes. See pickletools.py for extensive docs. The listing
  38. here is in kind-of alphabetical order of 1-character pickle code.
  39. pickletools groups them by purpose. */
  40. const OP_MARK = '('; // push special markobject on stack
  41. const OP_STOP = '.'; // every pickle ends with STOP
  42. const OP_POP = '0'; // discard topmost stack item
  43. const OP_POP_MARK = '1'; // discard stack top through topmost markobject
  44. const OP_DUP = '2'; // duplicate top stack item
  45. const OP_FLOAT = 'F'; // push float object; decimal string argument
  46. const OP_INT = 'I'; // push integer or bool; decimal string argument
  47. const OP_BININT = 'J'; // push four-byte signed int
  48. const OP_BININT1 = 'K'; // push 1-byte unsigned int
  49. const OP_LONG = 'L'; // push long; decimal string argument
  50. const OP_BININT2 = 'M'; // push 2-byte unsigned int
  51. const OP_NONE = 'N'; // push None
  52. const OP_PERSID = 'P'; // push persistent object; id is taken from string arg
  53. const OP_BINPERSID = 'Q'; // " " " ; " " " " stack
  54. const OP_REDUCE = 'R'; // apply callable to argtuple, both on stack
  55. const OP_STRING = 'S'; // push string; NL-terminated string argument
  56. const OP_BINSTRING = 'T'; // push string; counted binary string argument
  57. const OP_SHORT_BINSTRING = 'U'; // " " ; " " " " < 256 bytes
  58. const OP_UNICODE = 'V'; // push Unicode string; raw-unicode-escaped'd argument
  59. const OP_BINUNICODE = 'X'; // " " " ; counted UTF-8 string argument
  60. const OP_APPEND = 'a'; // append stack top to list below it
  61. const OP_BUILD = 'b'; // call __setstate__ or __dict__.update()
  62. const OP_GLOBAL = 'c'; // push self.find_class(modname, name); 2 string args
  63. const OP_DICT = 'd'; // build a dict from stack items
  64. const OP_EMPTY_DICT = '}'; // push empty dict
  65. const OP_APPENDS = 'e'; // extend list on stack by topmost stack slice
  66. const OP_GET = 'g'; // push item from memo on stack; index is string arg
  67. const OP_BINGET = 'h'; // " " " " " " ; " " 1-byte arg
  68. const OP_INST = 'i'; // build & push class instance
  69. const OP_LONG_BINGET = 'j'; // push item from memo on stack; index is 4-byte arg
  70. const OP_LIST = 'l'; // build list from topmost stack items
  71. const OP_EMPTY_LIST = ']'; // push empty list
  72. const OP_OBJ = 'o'; // build & push class instance
  73. const OP_PUT = 'p'; // store stack top in memo; index is string arg
  74. const OP_BINPUT = 'q'; // " " " " " ; " " 1-byte arg
  75. const OP_LONG_BINPUT = 'r'; // " " " " " ; " " 4-byte arg
  76. const OP_SETITEM = 's'; // add key+value pair to dict
  77. const OP_TUPLE = 't'; // build tuple from topmost stack items
  78. const OP_EMPTY_TUPLE = ')'; // push empty tuple
  79. const OP_SETITEMS = 'u'; // modify dict by adding topmost key+value pairs
  80. const OP_BINFLOAT = 'G'; // push float; arg is 8-byte float encoding
  81. /* Protocol 2 */
  82. const OP_PROTO = "\x80"; // identify pickle protocol
  83. const OP_NEWOBJ = "\x81"; // build object by applying cls.__new__ to argtuple
  84. const OP_EXT1 = "\x82"; // push object from extension registry; 1-byte index
  85. const OP_EXT2 = "\x83"; // ditto, but 2-byte index
  86. const OP_EXT4 = "\x84"; // ditto, but 4-byte index
  87. const OP_TUPLE1 = "\x85"; // build 1-tuple from stack top
  88. const OP_TUPLE2 = "\x86"; // build 2-tuple from two topmost stack items
  89. const OP_TUPLE3 = "\x87"; // build 3-tuple from three topmost stack items
  90. const OP_NEWTRUE = "\x88"; // push True
  91. const OP_NEWFALSE = "\x89"; // push False
  92. const OP_LONG1 = "\x8a"; // push long from < 256 bytes
  93. const OP_LONG4 = "\x8b"; // push really big long
  94. /* Protocol 3 (Python 3.x) */
  95. const OP_BINBYTES = 'B'; // push bytes; counted binary string argument
  96. const OP_SHORT_BINBYTES = 'C'; // " " ; " " " " < 256 bytes
  97. /**
  98. * @var bool Whether or not this is a PHP 6 binary
  99. */
  100. protected static $_isPhp6 = null;
  101. /**
  102. * @var bool Whether or not the system is little-endian
  103. */
  104. protected static $_isLittleEndian = null;
  105. /**
  106. * @var array Strings representing quotes
  107. */
  108. protected static $_quoteString = array(
  109. '\\' => '\\\\',
  110. "\x00" => '\\x00', "\x01" => '\\x01', "\x02" => '\\x02', "\x03" => '\\x03',
  111. "\x04" => '\\x04', "\x05" => '\\x05', "\x06" => '\\x06', "\x07" => '\\x07',
  112. "\x08" => '\\x08', "\x09" => '\\t', "\x0a" => '\\n', "\x0b" => '\\x0b',
  113. "\x0c" => '\\x0c', "\x0d" => '\\r', "\x0e" => '\\x0e', "\x0f" => '\\x0f',
  114. "\x10" => '\\x10', "\x11" => '\\x11', "\x12" => '\\x12', "\x13" => '\\x13',
  115. "\x14" => '\\x14', "\x15" => '\\x15', "\x16" => '\\x16', "\x17" => '\\x17',
  116. "\x18" => '\\x18', "\x19" => '\\x19', "\x1a" => '\\x1a', "\x1b" => '\\x1b',
  117. "\x1c" => '\\x1c', "\x1d" => '\\x1d', "\x1e" => '\\x1e', "\x1f" => '\\x1f',
  118. "\xff" => '\\xff'
  119. );
  120. /**
  121. * @var array Default options
  122. */
  123. protected $_options = array(
  124. 'protocol' => 0,
  125. );
  126. // process vars
  127. protected $_protocol = 0;
  128. protected $_binary = false;
  129. protected $_memo = array();
  130. protected $_pickle = '';
  131. protected $_pickleLen = 0;
  132. protected $_pos = 0;
  133. protected $_stack = array();
  134. protected $_marker = null;
  135. /**
  136. * Constructor
  137. *
  138. * @link Zend_Serializer_Adapter_AdapterAbstract::__construct()
  139. */
  140. public function __construct($opts=array())
  141. {
  142. parent::__construct($opts);
  143. // init
  144. if (self::$_isLittleEndian === null) {
  145. self::$_isLittleEndian = (pack('l', 1) === "\x01\x00\x00\x00");
  146. }
  147. if (self::$_isPhp6 === null) {
  148. self::$_isPhp6 = !version_compare(PHP_VERSION, '6.0.0', '<');
  149. }
  150. $this->_marker = new stdClass();
  151. }
  152. /**
  153. * Set an option
  154. *
  155. * @link Zend_Serializer_Adapter_AdapterAbstract::setOption()
  156. * @param string $name
  157. * @param mixed $value
  158. * @return Zend_Serializer_Adapter_PythonPickle
  159. */
  160. public function setOption($name, $value)
  161. {
  162. switch ($name) {
  163. case 'protocol':
  164. $value = $this->_checkProtocolNumber($value);
  165. break;
  166. }
  167. return parent::setOption($name, $value);
  168. }
  169. /**
  170. * Check and normalize pickle protocol number
  171. *
  172. * @param int $number
  173. * @return int
  174. * @throws Zend_Serializer_Exception
  175. */
  176. protected function _checkProtocolNumber($number)
  177. {
  178. $int = (int) $number;
  179. if ($int < 0 || $int > 3) {
  180. require_once 'Zend/Serializer/Exception.php';
  181. throw new Zend_Serializer_Exception('Invalid or unknown protocol version "'.$number.'"');
  182. }
  183. return $int;
  184. }
  185. /* serialize */
  186. /**
  187. * Serialize PHP to PythonPickle format
  188. *
  189. * @param mixed $value
  190. * @param array $opts
  191. * @return string
  192. */
  193. public function serialize($value, array $opts = array())
  194. {
  195. $opts = $opts + $this->_options;
  196. $this->_protocol = $this->_checkProtocolNumber($opts['protocol']);
  197. $this->_binary = $this->_protocol != 0;
  198. // clear process vars before serializing
  199. $this->_memo = array();
  200. $this->_pickle = '';
  201. // write
  202. if ($this->_protocol >= 2) {
  203. $this->_writeProto($this->_protocol);
  204. }
  205. $this->_write($value);
  206. $this->_writeStop();
  207. // clear process vars after serializing
  208. $this->_memo = array();
  209. $pickle = $this->_pickle;
  210. $this->_pickle = '';
  211. return $pickle;
  212. }
  213. /**
  214. * Write a value
  215. *
  216. * @param mixed $value
  217. * @return void
  218. * @throws Zend_Serializer_Exception on invalid or unrecognized value type
  219. */
  220. protected function _write($value)
  221. {
  222. if ($value === null) {
  223. $this->_writeNull();
  224. } elseif ($value === true) {
  225. $this->_writeTrue();
  226. } elseif ($value === false) {
  227. $this->_writeFalse();
  228. } elseif (is_int($value)) {
  229. $this->_writeInt($value);
  230. } elseif (is_float($value)) {
  231. $this->_writeFloat($value);
  232. } elseif (is_string($value)) {
  233. // TODO: write unicode / binary
  234. $this->_writeString($value);
  235. } elseif (is_array($value)) {
  236. if ($this->_isArrayAssoc($value)) {
  237. $this->_writeArrayDict($value);
  238. } else {
  239. $this->_writeArrayList($value);
  240. }
  241. } elseif (is_object($value)) {
  242. $this->_writeObject($value);
  243. } else {
  244. require_once 'Zend/Serializer/Exception.php';
  245. throw new Zend_Serializer_Exception(
  246. 'PHP-Type "'.gettype($value).'" isn\'t serializable with '.get_class($this)
  247. );
  248. }
  249. }
  250. /**
  251. * Write pickle protocol
  252. *
  253. * @param int $protocol
  254. * @return void
  255. */
  256. protected function _writeProto($protocol)
  257. {
  258. $this->_pickle .= self::OP_PROTO . $protocol;
  259. }
  260. /**
  261. * Write a get
  262. *
  263. * @param int $id Id of memo
  264. * @return void
  265. */
  266. protected function _writeGet($id)
  267. {
  268. if ($this->_binary) {
  269. if ($id <= 0xff) {
  270. // BINGET + chr(i)
  271. $this->_pickle .= self::OP_BINGET . chr($id);
  272. } else {
  273. // LONG_BINGET + pack("<i", i)
  274. $idBin = pack('l', $id);
  275. if (self::$_isLittleEndian === false) {
  276. $idBin = strrev($bin);
  277. }
  278. $this->_pickle .= self::OP_LONG_BINGET . $idBin;
  279. }
  280. } else {
  281. $this->_pickle .= self::OP_GET . $id . "\r\n";
  282. }
  283. }
  284. /**
  285. * Write a put
  286. *
  287. * @param int $id Id of memo
  288. * @return void
  289. */
  290. protected function _writePut($id)
  291. {
  292. if ($this->_binary) {
  293. if ($id <= 0xff) {
  294. // BINPUT + chr(i)
  295. $this->_pickle .= self::OP_BINPUT . chr($id);
  296. } else {
  297. // LONG_BINPUT + pack("<i", i)
  298. $idBin = pack('l', $id);
  299. if (self::$_isLittleEndian === false) {
  300. $idBin = strrev($bin);
  301. }
  302. $this->_pickle .= self::OP_LONG_BINPUT . $idBin;
  303. }
  304. } else {
  305. $this->_pickle .= self::OP_PUT . $id . "\r\n";
  306. }
  307. }
  308. /**
  309. * Write a null as None
  310. *
  311. * @return void
  312. */
  313. protected function _writeNull()
  314. {
  315. $this->_pickle .= self::OP_NONE;
  316. }
  317. /**
  318. * Write a boolean true
  319. *
  320. * @return void
  321. */
  322. protected function _writeTrue()
  323. {
  324. if ($this->_protocol >= 2) {
  325. $this->_pickle .= self::OP_NEWTRUE;
  326. } else {
  327. $this->_pickle .= self::OP_INT . "01\r\n";
  328. }
  329. }
  330. /**
  331. * Write a boolean false
  332. *
  333. * @return void
  334. */
  335. protected function _writeFalse()
  336. {
  337. if ($this->_protocol >= 2) {
  338. $this->_pickle .= self::OP_NEWFALSE;
  339. } else {
  340. $this->_pickle .= self::OP_INT . "00\r\n";
  341. }
  342. }
  343. /**
  344. * Write an integer value
  345. *
  346. * @param int $value
  347. * @return void
  348. */
  349. protected function _writeInt($value)
  350. {
  351. if ($this->_binary) {
  352. if ($value >= 0) {
  353. if ($value <= 0xff) {
  354. // self.write(BININT1 + chr(obj))
  355. $this->_pickle .= self::OP_BININT1 . chr($value);
  356. } elseif ($value <= 0xffff) {
  357. // self.write("%c%c%c" % (BININT2, obj&0xff, obj>>8))
  358. $this->_pickle .= self::OP_BININT2 . pack('v', $value);
  359. }
  360. return;
  361. }
  362. // Next check for 4-byte signed ints:
  363. $highBits = $value >> 31; // note that Python shift sign-extends
  364. if ($highBits == 0 || $highBits == -1) {
  365. // All high bits are copies of bit 2**31, so the value
  366. // fits in a 4-byte signed int.
  367. // self.write(BININT + pack("<i", obj))
  368. $bin = pack('l', $value);
  369. if (self::$_isLittleEndian === false) {
  370. $bin = strrev($bin);
  371. }
  372. $this->_pickle .= self::OP_BININT . $bin;
  373. return;
  374. }
  375. }
  376. $this->_pickle .= self::OP_INT . $value . "\r\n";
  377. }
  378. /**
  379. * Write a float value
  380. *
  381. * @param float $value
  382. * @return void
  383. */
  384. protected function _writeFloat($value)
  385. {
  386. if ($this->_binary) {
  387. // self.write(BINFLOAT + pack('>d', obj))
  388. $bin = pack('d', $value);
  389. if (self::$_isLittleEndian === true) {
  390. $bin = strrev($bin);
  391. }
  392. $this->_pickle .= self::OP_BINFLOAT . $bin;
  393. } else {
  394. $this->_pickle .= self::OP_FLOAT . $value . "\r\n";
  395. }
  396. }
  397. /**
  398. * Write a string value
  399. *
  400. * @param string $value
  401. * @return void
  402. */
  403. protected function _writeString($value)
  404. {
  405. if ( ($id=$this->_searchMomo($value)) !== false ) {
  406. $this->_writeGet($id);
  407. return;
  408. }
  409. if ($this->_binary) {
  410. $n = strlen($value);
  411. if ($n <= 0xff) {
  412. // self.write(SHORT_BINSTRING + chr(n) + obj)
  413. $this->_pickle .= self::OP_SHORT_BINSTRING . chr($n) . $value;
  414. } else {
  415. // self.write(BINSTRING + pack("<i", n) + obj)
  416. $binLen = pack('l', $n);
  417. if (self::$_isLittleEndian === false) {
  418. $binLen = strrev($binLen);
  419. }
  420. $this->_pickle .= self::OP_BINSTRING . $binLen . $value;
  421. }
  422. } else {
  423. $this->_pickle .= self::OP_STRING . $this->_quoteString($value) . "\r\n";
  424. }
  425. $this->_momorize($value);
  426. }
  427. /**
  428. * Write an associative array value as dictionary
  429. *
  430. * @param array $value
  431. * @return void
  432. */
  433. protected function _writeArrayDict(array $value)
  434. {
  435. if (($id=$this->_searchMomo($value)) !== false) {
  436. $this->_writeGet($id);;
  437. return;
  438. }
  439. $this->_pickle .= self::OP_MARK . self::OP_DICT;
  440. $this->_momorize($value);
  441. foreach ($value as $k => $v) {
  442. $this->_pickle .= $this->_write($k)
  443. . $this->_write($v)
  444. . self::OP_SETITEM;
  445. }
  446. }
  447. /**
  448. * Write a simple array value as list
  449. *
  450. * @param array $value
  451. * @return void
  452. */
  453. protected function _writeArrayList(array $value)
  454. {
  455. if (($id = $this->_searchMomo($value)) !== false) {
  456. $this->_writeGet($id);
  457. return;
  458. }
  459. $this->_pickle .= self::OP_MARK . self::OP_LIST;
  460. $this->_momorize($value);
  461. foreach ($value as $k => $v) {
  462. $this->_pickle .= $this->_write($v) . self::OP_APPEND;
  463. }
  464. }
  465. /**
  466. * Write an object as an dictionary
  467. *
  468. * @param object $value
  469. * @return void
  470. */
  471. protected function _writeObject($value)
  472. {
  473. // can't serialize php objects to python objects yet
  474. $this->_writeArrayDict(get_object_vars($value));
  475. }
  476. /**
  477. * Write stop
  478. *
  479. * @return void
  480. */
  481. protected function _writeStop()
  482. {
  483. $this->_pickle .= self::OP_STOP;
  484. }
  485. /* serialize helper */
  486. /**
  487. * Add a value to the memo and write the id
  488. *
  489. * @param mixed $value
  490. * @return void
  491. */
  492. protected function _momorize($value)
  493. {
  494. $id = count($this->_memo);
  495. $this->_memo[$id] = $value;
  496. $this->_writePut($id);
  497. }
  498. /**
  499. * Search a value in the meno and return the id
  500. *
  501. * @param mixed $value
  502. * @return int|false The id or false
  503. */
  504. protected function _searchMomo($value)
  505. {
  506. return array_search($value, $this->_memo, true);
  507. }
  508. /**
  509. * Is an array associative?
  510. *
  511. * @param array $value
  512. * @return boolean
  513. */
  514. protected function _isArrayAssoc(array $value)
  515. {
  516. return array_diff_key($value, array_keys(array_keys($value)));
  517. }
  518. /**
  519. * Quote/Escape a string
  520. *
  521. * @param string $str
  522. * @return string quoted string
  523. */
  524. protected function _quoteString($str)
  525. {
  526. $quoteArr = self::$_quoteString;
  527. if (($cntSingleQuote = substr_count($str, "'"))
  528. && ($cntDoubleQuote = substr_count($str, '"'))
  529. && ($cntSingleQuote < $cntDoubleQuote)
  530. ) {
  531. $quoteArr['"'] = '\\"';
  532. $enclosure = '"';
  533. } else {
  534. $quoteArr["'"] = "\\'";
  535. $enclosure = "'";
  536. }
  537. return $enclosure . strtr($str, $quoteArr) . $enclosure;
  538. }
  539. /* unserialize */
  540. /**
  541. * Unserialize from Python Pickle format to PHP
  542. *
  543. * @param string $pickle
  544. * @param array $opts
  545. * @return mixed
  546. * @throws Zend_Serializer_Exception on invalid Pickle string
  547. */
  548. public function unserialize($pickle, array $opts = array())
  549. {
  550. // init process vars
  551. $this->_pos = 0;
  552. $this->_pickle = $pickle;
  553. $this->_pickleLen = strlen($this->_pickle);
  554. $this->_memo = array();
  555. $this->_stack = array();
  556. // read pickle string
  557. while (($op=$this->_read(1)) !== self::OP_STOP) {
  558. $this->_load($op);
  559. }
  560. if (!count($this->_stack)) {
  561. require_once 'Zend/Serializer/Exception.php';
  562. throw new Zend_Serializer_Exception('No data found');
  563. }
  564. $ret = array_pop($this->_stack);
  565. // clear process vars
  566. $this->_pos = 0;
  567. $this->_pickle = '';
  568. $this->_pickleLen = 0;
  569. $this->_memo = array();
  570. $this->_stack = array();
  571. return $ret;
  572. }
  573. /**
  574. * Load a pickle opcode
  575. *
  576. * @param string $op
  577. * @return void
  578. * @throws Zend_Serializer_Exception on invalid opcode
  579. */
  580. protected function _load($op)
  581. {
  582. switch ($op) {
  583. case self::OP_PUT:
  584. $this->_loadPut();
  585. break;
  586. case self::OP_BINPUT:
  587. $this->_loadBinPut();
  588. break;
  589. case self::OP_LONG_BINPUT:
  590. $this->_loadLongBinPut();
  591. break;
  592. case self::OP_GET:
  593. $this->_loadGet();
  594. break;
  595. case self::OP_BINGET:
  596. $this->_loadBinGet();
  597. break;
  598. case self::OP_LONG_BINGET:
  599. $this->_loadLongBinGet();
  600. break;
  601. case self::OP_NONE:
  602. $this->_loadNone();
  603. break;
  604. case self::OP_NEWTRUE:
  605. $this->_loadNewTrue();
  606. break;
  607. case self::OP_NEWFALSE:
  608. $this->_loadNewFalse();
  609. break;
  610. case self::OP_INT:
  611. $this->_loadInt();
  612. break;
  613. case self::OP_BININT:
  614. $this->_loadBinInt();
  615. break;
  616. case self::OP_BININT1:
  617. $this->_loadBinInt1();
  618. break;
  619. case self::OP_BININT2:
  620. $this->_loadBinInt2();
  621. break;
  622. case self::OP_LONG:
  623. $this->_loadLong();
  624. break;
  625. case self::OP_LONG1:
  626. $this->_loadLong1();
  627. break;
  628. case self::OP_LONG4:
  629. $this->_loadLong4();
  630. break;
  631. case self::OP_FLOAT:
  632. $this->_loadFloat();
  633. break;
  634. case self::OP_BINFLOAT:
  635. $this->_loadBinFloat();
  636. break;
  637. case self::OP_STRING:
  638. $this->_loadString();
  639. break;
  640. case self::OP_BINSTRING:
  641. $this->_loadBinString();
  642. break;
  643. case self::OP_SHORT_BINSTRING:
  644. $this->_loadShortBinString();
  645. break;
  646. case self::OP_BINBYTES:
  647. $this->_loadBinBytes();
  648. break;
  649. case self::OP_SHORT_BINBYTES:
  650. $this->_loadShortBinBytes();
  651. break;
  652. case self::OP_UNICODE:
  653. $this->_loadUnicode();
  654. break;
  655. case self::OP_BINUNICODE:
  656. $this->_loadBinUnicode();
  657. break;
  658. case self::OP_MARK:
  659. $this->_loadMark();
  660. break;
  661. case self::OP_LIST:
  662. $this->_loadList();
  663. break;
  664. case self::OP_EMPTY_LIST:
  665. $this->_loadEmptyList();
  666. break;
  667. case self::OP_APPEND:
  668. $this->_loadAppend();
  669. break;
  670. case self::OP_APPENDS:
  671. $this->_loadAppends();
  672. break;
  673. case self::OP_DICT:
  674. $this->_loadDict();
  675. break;
  676. case self::OP_EMPTY_DICT:
  677. $this->_loadEmptyDict();
  678. break;
  679. case self::OP_SETITEM:
  680. $this->_loadSetItem();
  681. break;
  682. case self::OP_SETITEMS:
  683. $this->_loadSetItems();
  684. break;
  685. case self::OP_TUPLE:
  686. $this->_loadTuple();
  687. break;
  688. case self::OP_TUPLE1:
  689. $this->_loadTuple1();
  690. break;
  691. case self::OP_TUPLE2:
  692. $this->_loadTuple2();
  693. break;
  694. case self::OP_TUPLE3:
  695. $this->_loadTuple3();
  696. break;
  697. case self::OP_PROTO:
  698. $this->_loadProto();
  699. break;
  700. default:
  701. require_once 'Zend/Serializer/Exception.php';
  702. throw new Zend_Serializer_Exception('Invalid or unknown opcode "'.$op.'"');
  703. }
  704. }
  705. /**
  706. * Load a PUT opcode
  707. *
  708. * @return void
  709. * @throws Zend_Serializer_Exception on missing stack
  710. */
  711. protected function _loadPut()
  712. {
  713. $id = (int)$this->_readline();
  714. $lastStack = count($this->_stack)-1;
  715. if (!isset($this->_stack[$lastStack])) {
  716. require_once 'Zend/Serializer/Exception.php';
  717. throw new Zend_Serializer_Exception('No stack exist');
  718. }
  719. $this->_memo[$id] = & $this->_stack[$lastStack];
  720. }
  721. /**
  722. * Load a binary PUT
  723. *
  724. * @return void
  725. * @throws Zend_Serializer_Exception on missing stack
  726. */
  727. protected function _loadBinPut()
  728. {
  729. $id = ord($this->_read(1));
  730. $lastStack = count($this->_stack)-1;
  731. if (!isset($this->_stack[$lastStack])) {
  732. require_once 'Zend/Serializer/Exception.php';
  733. throw new Zend_Serializer_Exception('No stack exist');
  734. }
  735. $this->_memo[$id] = & $this->_stack[$lastStack];
  736. }
  737. /**
  738. * Load a long binary PUT
  739. *
  740. * @return void
  741. * @throws Zend_Serializer_Exception on missing stack
  742. */
  743. protected function _loadLongBinPut()
  744. {
  745. $bin = $this->_read(4);
  746. if (self::$_isLittleEndian === false) {
  747. $bin = strrev($bin);
  748. }
  749. list(, $id) = unpack('l', $bin);
  750. $lastStack = count($this->_stack)-1;
  751. if (!isset($this->_stack[$lastStack])) {
  752. require_once 'Zend/Serializer/Exception.php';
  753. throw new Zend_Serializer_Exception('No stack exist');
  754. }
  755. $this->_memo[$id] = & $this->_stack[$lastStack];
  756. }
  757. /**
  758. * Load a GET operation
  759. *
  760. * @return void
  761. * @throws Zend_Serializer_Exception on missing GET identifier
  762. */
  763. protected function _loadGet()
  764. {
  765. $id = (int)$this->_readline();
  766. if (!array_key_exists($id, $this->_memo)) {
  767. require_once 'Zend/Serializer/Exception.php';
  768. throw new Zend_Serializer_Exception('Get id "' . $id . '" not found in momo');
  769. }
  770. $this->_stack[] = & $this->_memo[$id];
  771. }
  772. /**
  773. * Load a binary GET operation
  774. *
  775. * @return void
  776. * @throws Zend_Serializer_Exception on missing GET identifier
  777. */
  778. protected function _loadBinGet()
  779. {
  780. $id = ord($this->_read(1));
  781. if (!array_key_exists($id, $this->_memo)) {
  782. require_once 'Zend/Serializer/Exception.php';
  783. throw new Zend_Serializer_Exception('Get id "' . $id . '" not found in momo');
  784. }
  785. $this->_stack[] = & $this->_memo[$id];
  786. }
  787. /**
  788. * Load a long binary GET operation
  789. *
  790. * @return void
  791. * @throws Zend_Serializer_Exception on missing GET identifier
  792. */
  793. protected function _loadLongBinGet()
  794. {
  795. $bin = $this->_read(4);
  796. if (self::$_isLittleEndian === false) {
  797. $bin = strrev($bin);
  798. }
  799. list(, $id) = unpack('l', $bin);
  800. if (!array_key_exists($id, $this->_memo)) {
  801. require_once 'Zend/Serializer/Exception.php';
  802. throw new Zend_Serializer_Exception('Get id "' . $id . '" not found in momo');
  803. }
  804. $this->_stack[] = & $this->_memo[$id];
  805. }
  806. /**
  807. * Load a NONE operator
  808. *
  809. * @return void
  810. */
  811. protected function _loadNone()
  812. {
  813. $this->_stack[] = null;
  814. }
  815. /**
  816. * Load a boolean TRUE operator
  817. *
  818. * @return void
  819. */
  820. protected function _loadNewTrue()
  821. {
  822. $this->_stack[] = true;
  823. }
  824. /**
  825. * Load a boolean FALSE operator
  826. *
  827. * @return void
  828. */
  829. protected function _loadNewFalse()
  830. {
  831. $this->_stack[] = false;
  832. }
  833. /**
  834. * Load an integer operator
  835. *
  836. * @return void
  837. */
  838. protected function _loadInt()
  839. {
  840. $line = $this->_readline();
  841. if ($line === '01') {
  842. $this->_stack[] = true;
  843. } elseif ($line === '00') {
  844. $this->_stack[] = false;
  845. } else {
  846. $this->_stack[] = (int)$line;
  847. }
  848. }
  849. /**
  850. * Load a binary integer operator
  851. *
  852. * @return void
  853. */
  854. protected function _loadBinInt()
  855. {
  856. $bin = $this->_read(4);
  857. if (self::$_isLittleEndian === false) {
  858. $bin = strrev($bin);
  859. }
  860. list(, $int) = unpack('l', $bin);
  861. $this->_stack[] = $int;
  862. }
  863. /**
  864. * Load the first byte of a binary integer
  865. *
  866. * @return void
  867. */
  868. protected function _loadBinInt1()
  869. {
  870. $this->_stack[] = ord($this->_read(1));
  871. }
  872. /**
  873. * Load the second byte of a binary integer
  874. *
  875. * @return void
  876. */
  877. protected function _loadBinInt2()
  878. {
  879. $bin = $this->_read(2);
  880. list(, $int) = unpack('v', $bin);
  881. $this->_stack[] = $int;
  882. }
  883. /**
  884. * Load a long (float) operator
  885. *
  886. * @return void
  887. */
  888. protected function _loadLong()
  889. {
  890. $data = rtrim($this->_readline(), 'L');
  891. if ($data === '') {
  892. $this->_stack[] = 0;
  893. } else {
  894. $this->_stack[] = $data;
  895. }
  896. }
  897. /**
  898. * Load a one byte long integer
  899. *
  900. * @return void
  901. */
  902. protected function _loadLong1()
  903. {
  904. $n = ord($this->_read(1));
  905. $data = $this->_read($n);
  906. $this->_stack[] = $this->_decodeBinLong($data);
  907. }
  908. /**
  909. * Load a 4 byte long integer
  910. *
  911. * @return void
  912. */
  913. protected function _loadLong4()
  914. {
  915. $nBin = $this->_read(4);
  916. if (self::$_isLittleEndian === false) {
  917. $nBin = strrev($$nBin);
  918. }
  919. list(, $n) = unpack('l', $nBin);
  920. $data = $this->_read($n);
  921. $this->_stack[] = $this->_decodeBinLong($data);
  922. }
  923. /**
  924. * Load a float value
  925. *
  926. * @return void
  927. */
  928. protected function _loadFloat()
  929. {
  930. $float = (float)$this->_readline();
  931. $this->_stack[] = $float;
  932. }
  933. /**
  934. * Load a binary float value
  935. *
  936. * @return void
  937. */
  938. protected function _loadBinFloat()
  939. {
  940. $bin = $this->_read(8);
  941. if (self::$_isLittleEndian === true) {
  942. $bin = strrev($bin);
  943. }
  944. list(, $float) = unpack('d', $bin);
  945. $this->_stack[] = $float;
  946. }
  947. /**
  948. * Load a string
  949. *
  950. * @return void
  951. */
  952. protected function _loadString()
  953. {
  954. $this->_stack[] = $this->_unquoteString((string)$this->_readline());
  955. }
  956. /**
  957. * Load a binary string
  958. *
  959. * @return void
  960. */
  961. protected function _loadBinString()
  962. {
  963. $bin = $this->_read(4);
  964. if (!self::$_isLittleEndian) {
  965. $bin = strrev($bin);
  966. }
  967. list(, $len) = unpack('l', $bin);
  968. $this->_stack[] = (string)$this->_read($len);
  969. }
  970. /**
  971. * Load a short binary string
  972. *
  973. * @return void
  974. */
  975. protected function _loadShortBinString()
  976. {
  977. $len = ord($this->_read(1));
  978. $this->_stack[] = (string)$this->_read($len);
  979. }
  980. /**
  981. * Load arbitrary binary bytes
  982. *
  983. * @return void
  984. */
  985. protected function _loadBinBytes()
  986. {
  987. // read byte length
  988. $nBin = $this->_read(4);
  989. if (self::$_isLittleEndian === false) {
  990. $nBin = strrev($$nBin);
  991. }
  992. list(, $n) = unpack('l', $nBin);
  993. $this->_stack[] = $this->_read($n);
  994. }
  995. /**
  996. * Load a single binary byte
  997. *
  998. * @return void
  999. */
  1000. protected function _loadShortBinBytes()
  1001. {
  1002. $n = ord($this->_read(1));
  1003. $this->_stack[] = $this->_read($n);
  1004. }
  1005. /**
  1006. * Load a unicode string
  1007. *
  1008. * @return void
  1009. */
  1010. protected function _loadUnicode()
  1011. {
  1012. $data = $this->_readline();
  1013. $pattern = '/\\\\u([a-fA-F0-9]{4})/u'; // \uXXXX
  1014. $data = preg_replace_callback($pattern, array($this, '_convertMatchingUnicodeSequence2Utf8'), $data);
  1015. if (self::$_isPhp6) {
  1016. $data = unicode_decode($data, 'UTF-8');
  1017. }
  1018. $this->_stack[] = $data;
  1019. }
  1020. /**
  1021. * Convert a unicode sequence to UTF-8
  1022. *
  1023. * @param array $match
  1024. * @return string
  1025. */
  1026. protected function _convertMatchingUnicodeSequence2Utf8(array $match)
  1027. {
  1028. return $this->_hex2Utf8($match[1]);
  1029. }
  1030. /**
  1031. * Convert a hex string to a UTF-8 string
  1032. *
  1033. * @param string $sequence
  1034. * @return string
  1035. * @throws Zend_Serializer_Exception on unmatched unicode sequence
  1036. */
  1037. protected function _hex2Utf8($hex)
  1038. {
  1039. $uniCode = hexdec($hex);
  1040. if ($uniCode < 0x80) { // 1Byte
  1041. $utf8Char = chr($uniCode);
  1042. } elseif ($uniCode < 0x800) { // 2Byte
  1043. $utf8Char = chr(0xC0 | $uniCode >> 6)
  1044. . chr(0x80 | $uniCode & 0x3F);
  1045. } elseif ($uniCode < 0x10000) { // 3Byte
  1046. $utf8Char = chr(0xE0 | $uniCode >> 12)
  1047. . chr(0x80 | $uniCode >> 6 & 0x3F)
  1048. . chr(0x80 | $uniCode & 0x3F);
  1049. } elseif ($uniCode < 0x110000) { // 4Byte
  1050. $utf8Char = chr(0xF0 | $uniCode >> 18)
  1051. . chr(0x80 | $uniCode >> 12 & 0x3F)
  1052. . chr(0x80 | $uniCode >> 6 & 0x3F)
  1053. . chr(0x80 | $uniCode & 0x3F);
  1054. } else {
  1055. require_once 'Zend/Serializer/Exception.php';
  1056. throw new Zend_Serializer_Exception('Unsupported unicode character found "' . dechex($uniCode) . '"');
  1057. }
  1058. return $utf8Char;
  1059. }
  1060. /**
  1061. * Load binary unicode sequence
  1062. *
  1063. * @return void
  1064. */
  1065. protected function _loadBinUnicode()
  1066. {
  1067. // read byte length
  1068. $n = $this->_read(4);
  1069. if (self::$_isLittleEndian === false) {
  1070. $n = strrev($n);
  1071. }
  1072. list(, $n) = unpack('l', $n);
  1073. $data = $this->_read($n);
  1074. if (self::$_isPhp6) {
  1075. $data = unicode_decode($data, 'UTF-8');
  1076. }
  1077. $this->_stack[] = $data;
  1078. }
  1079. /**
  1080. * Load a marker sequence
  1081. *
  1082. * @return void
  1083. */
  1084. protected function _loadMark()
  1085. {
  1086. $this->_stack[] = $this->_marker;
  1087. }
  1088. /**
  1089. * Load an array (list)
  1090. *
  1091. * @return void
  1092. */
  1093. protected function _loadList()
  1094. {
  1095. $k = $this->_lastMarker();
  1096. $this->_stack[$k] = array();
  1097. // remove all elements after marker
  1098. $max = count($this->_stack);
  1099. for ($i = $k+1, $max; $i < $max; $i++) {
  1100. unset($this->_stack[$i]);
  1101. }
  1102. }
  1103. /**
  1104. * Load an append (to list) sequence
  1105. *
  1106. * @return void
  1107. */
  1108. protected function _loadAppend()
  1109. {
  1110. $value = array_pop($this->_stack);
  1111. $list =& $this->_stack[count($this->_stack)-1];
  1112. $list[] = $value;
  1113. }
  1114. /**
  1115. * Load an empty list sequence
  1116. *
  1117. * @return void
  1118. */
  1119. protected function _loadEmptyList()
  1120. {
  1121. $this->_stack[] = array();
  1122. }
  1123. /**
  1124. * Load multiple append (to list) sequences at once
  1125. *
  1126. * @return void
  1127. */
  1128. protected function _loadAppends()
  1129. {
  1130. $k = $this->_lastMarker();
  1131. $list =& $this->_stack[$k - 1];
  1132. $max = count($this->_stack);
  1133. for ($i = $k + 1; $i < $max; $i++) {
  1134. $list[] = $this->_stack[$i];
  1135. unset($this->_stack[$i]);
  1136. }
  1137. unset($this->_stack[$k]);
  1138. }
  1139. /**
  1140. * Load an associative array (Python dictionary)
  1141. *
  1142. * @return void
  1143. */
  1144. protected function _loadDict()
  1145. {
  1146. $k = $this->_lastMarker();
  1147. $this->_stack[$k] = array();
  1148. // remove all elements after marker
  1149. $max = count($this->_stack);
  1150. for($i = $k + 1; $i < $max; $i++) {
  1151. unset($this->_stack[$i]);
  1152. }
  1153. }
  1154. /**
  1155. * Load an item from a set
  1156. *
  1157. * @return void
  1158. */
  1159. protected function _loadSetItem()
  1160. {
  1161. $value = array_pop($this->_stack);
  1162. $key = array_pop($this->_stack);
  1163. $dict =& $this->_stack[count($this->_stack) - 1];
  1164. $dict[$key] = $value;
  1165. }
  1166. /**
  1167. * Load an empty dictionary
  1168. *
  1169. * @return void
  1170. */
  1171. protected function _loadEmptyDict()
  1172. {
  1173. $this->_stack[] = array();
  1174. }
  1175. /**
  1176. * Load set items
  1177. *
  1178. * @return void
  1179. */
  1180. protected function _loadSetItems()
  1181. {
  1182. $k = $this->_lastMarker();
  1183. $dict =& $this->_stack[$k - 1];
  1184. $max = count($this->_stack);
  1185. for ($i = $k + 1; $i < $max; $i += 2) {
  1186. $key = $this->_stack[$i];
  1187. $value = $this->_stack[$i + 1];
  1188. $dict[$key] = $value;
  1189. unset($this->_stack[$i], $this->_stack[$i+1]);
  1190. }
  1191. unset($this->_stack[$k]);
  1192. }
  1193. /**
  1194. * Load a tuple
  1195. *
  1196. * @return void
  1197. */
  1198. protected function _loadTuple()
  1199. {
  1200. $k = $this->_lastMarker();
  1201. $this->_stack[$k] = array();
  1202. $tuple =& $this->_stack[$k];
  1203. $max = count($this->_stack);
  1204. for($i = $k + 1; $i < $max; $i++) {
  1205. $tuple[] = $this->_stack[$i];
  1206. unset($this->_stack[$i]);
  1207. }
  1208. }
  1209. /**
  1210. * Load single item tuple
  1211. *
  1212. * @return void
  1213. */
  1214. protected function _loadTuple1()
  1215. {
  1216. $value1 = array_pop($this->_stack);
  1217. $this->_stack[] = array($value1);
  1218. }
  1219. /**
  1220. * Load two item tuple
  1221. *
  1222. * @return void
  1223. */
  1224. protected function _loadTuple2()
  1225. {
  1226. $value2 = array_pop($this->_stack);
  1227. $value1 = array_pop($this->_stack);
  1228. $this->_stack[] = array($value1, $value2);
  1229. }
  1230. /**
  1231. * Load three item tuple
  1232. *
  1233. * @return void
  1234. */
  1235. protected function _loadTuple3() {
  1236. $value3 = array_pop($this->_stack);
  1237. $value2 = array_pop($this->_stack);
  1238. $value1 = array_pop($this->_stack);
  1239. $this->_stack[] = array($value1, $value2, $value3);
  1240. }
  1241. /**
  1242. * Load a proto value
  1243. *
  1244. * @return void
  1245. * @throws Zend_Serializer_Exception if Pickle version does not support this feature
  1246. */
  1247. protected function _loadProto()
  1248. {
  1249. $proto = ord($this->_read(1));
  1250. if ($proto < 2 || $proto > 3) {
  1251. require_once 'Zend/Serializer/Excception.php';
  1252. throw new Zend_Serializer_Exception('Invalid protocol version detected');
  1253. }
  1254. $this->_protocol = $proto;
  1255. }
  1256. /* unserialize helper */
  1257. /**
  1258. * Read a segment of the pickle
  1259. *
  1260. * @param mixed $len
  1261. * @return string
  1262. * @throws Zend_Serializer_Exception if position matches end of data
  1263. */
  1264. protected function _read($len)
  1265. {
  1266. if (($this->_pos + $len) > $this->_pickleLen) {
  1267. require_once 'Zend/Serializer/Exception.php';
  1268. throw new Zend_Serializer_Exception('End of data');
  1269. }
  1270. $this->_pos+= $len;
  1271. return substr($this->_pickle, ($this->_pos - $len), $len);
  1272. }
  1273. /**
  1274. * Read a line of the pickle at once
  1275. *
  1276. * @return string
  1277. * @throws Zend_Serializer_Exception if no EOL character found
  1278. */
  1279. protected function _readline()
  1280. {
  1281. $eolLen = 2;
  1282. $eolPos = strpos($this->_pickle, "\r\n", $this->_pos);
  1283. if ($eolPos === false) {
  1284. $eolPos = strpos($this->_pickle, "\n", $this->_pos);
  1285. $eolLen = 1;
  1286. }
  1287. if ($eolPos === false) {
  1288. require_once 'Zend/Serializer/Exception.php';
  1289. throw new Zend_Serializer_Exception('No new line found');
  1290. }
  1291. $ret = substr($this->_pickle, $this->_pos, $eolPos-$this->_pos);
  1292. $this->_pos = $eolPos + $eolLen;
  1293. return $ret;
  1294. }
  1295. /**
  1296. * Unquote/Unescape a quoted string
  1297. *
  1298. * @param string $str quoted string
  1299. * @return string unquoted string
  1300. */
  1301. protected function _unquoteString($str)
  1302. {
  1303. $quoteArr = array_flip(self::$_quoteString);
  1304. if ($str[0] == '"') {
  1305. $quoteArr['\\"'] = '"';
  1306. } else {
  1307. $quoteArr["\\'"] = "'";
  1308. }
  1309. return strtr(substr(trim($str), 1, -1), $quoteArr);
  1310. }
  1311. /**
  1312. * Return last marker position in stack
  1313. *
  1314. * @return int
  1315. */
  1316. protected function _lastMarker()
  1317. {
  1318. for ($k = count($this->_stack)-1; $k >= 0; $k -= 1) {
  1319. if ($this->_stack[$k] === $this->_marker) {
  1320. break;
  1321. }
  1322. }
  1323. return $k;
  1324. }
  1325. /**
  1326. * Decode a binary long sequence
  1327. *
  1328. * @param string $data
  1329. * @return int|float|string
  1330. */
  1331. protected function _decodeBinLong($data)
  1332. {
  1333. $nbytes = strlen($data);
  1334. if ($nbytes == 0) {
  1335. return 0;
  1336. }
  1337. $long = 0;
  1338. if ($nbytes > 7) {
  1339. if (!extension_loaded('bcmath')) {
  1340. return INF;
  1341. }
  1342. for ($i=0; $i<$nbytes; $i++) {
  1343. $long = bcadd($long, bcmul(ord($data[$i]), bcpow(256, $i, 0)));
  1344. }
  1345. if (0x80 <= ord($data[$nbytes-1])) {
  1346. $long = bcsub($long, bcpow(2, $nbytes * 8));
  1347. }
  1348. } else {
  1349. for ($i=0; $i<$nbytes; $i++) {
  1350. $long+= ord($data[$i]) * pow(256, $i);
  1351. }
  1352. if (0x80 <= ord($data[$nbytes-1])) {
  1353. $long-= pow(2, $nbytes * 8);
  1354. // $long-= 1 << ($nbytes * 8);
  1355. }
  1356. }
  1357. return $long;
  1358. }
  1359. }