PageRenderTime 52ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/src/lib/Zend/Serializer/Adapter/PythonPickle.php

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