File indexing completed on 2024-12-22 05:37:01
0001 <?php 0002 /** 0003 * Zend Framework 0004 * 0005 * LICENSE 0006 * 0007 * This source file is subject to the new BSD license that is bundled 0008 * with this package in the file LICENSE.txt. 0009 * It is also available through the world-wide-web at this URL: 0010 * http://framework.zend.com/license/new-bsd 0011 * If you did not receive a copy of the license and are unable to 0012 * obtain it through the world-wide-web, please send an email 0013 * to license@zend.com so we can send you a copy immediately. 0014 * 0015 * @category Zend 0016 * @package Zend_Serializer 0017 * @subpackage Adapter 0018 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0019 * @license http://framework.zend.com/license/new-bsd New BSD License 0020 * @version $Id$ 0021 */ 0022 0023 /** @see Zend_Serializer_Adapter_AdapterAbstract */ 0024 // require_once 'Zend/Serializer/Adapter/AdapterAbstract.php'; 0025 0026 /** 0027 * @link http://www.python.org 0028 * @see Phython3.1/Lib/pickle.py 0029 * @see Phython3.1/Modules/_pickle.c 0030 * @link http://pickle-js.googlecode.com 0031 * @category Zend 0032 * @package Zend_Serializer 0033 * @subpackage Adapter 0034 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0035 * @license http://framework.zend.com/license/new-bsd New BSD License 0036 */ 0037 class Zend_Serializer_Adapter_PythonPickle extends Zend_Serializer_Adapter_AdapterAbstract 0038 { 0039 /* Pickle opcodes. See pickletools.py for extensive docs. The listing 0040 here is in kind-of alphabetical order of 1-character pickle code. 0041 pickletools groups them by purpose. */ 0042 const OP_MARK = '('; // push special markobject on stack 0043 const OP_STOP = '.'; // every pickle ends with STOP 0044 const OP_POP = '0'; // discard topmost stack item 0045 const OP_POP_MARK = '1'; // discard stack top through topmost markobject 0046 const OP_DUP = '2'; // duplicate top stack item 0047 const OP_FLOAT = 'F'; // push float object; decimal string argument 0048 const OP_INT = 'I'; // push integer or bool; decimal string argument 0049 const OP_BININT = 'J'; // push four-byte signed int 0050 const OP_BININT1 = 'K'; // push 1-byte unsigned int 0051 const OP_LONG = 'L'; // push long; decimal string argument 0052 const OP_BININT2 = 'M'; // push 2-byte unsigned int 0053 const OP_NONE = 'N'; // push None 0054 const OP_PERSID = 'P'; // push persistent object; id is taken from string arg 0055 const OP_BINPERSID = 'Q'; // " " " ; " " " " stack 0056 const OP_REDUCE = 'R'; // apply callable to argtuple, both on stack 0057 const OP_STRING = 'S'; // push string; NL-terminated string argument 0058 const OP_BINSTRING = 'T'; // push string; counted binary string argument 0059 const OP_SHORT_BINSTRING = 'U'; // " " ; " " " " < 256 bytes 0060 const OP_UNICODE = 'V'; // push Unicode string; raw-unicode-escaped'd argument 0061 const OP_BINUNICODE = 'X'; // " " " ; counted UTF-8 string argument 0062 const OP_APPEND = 'a'; // append stack top to list below it 0063 const OP_BUILD = 'b'; // call __setstate__ or __dict__.update() 0064 const OP_GLOBAL = 'c'; // push self.find_class(modname, name); 2 string args 0065 const OP_DICT = 'd'; // build a dict from stack items 0066 const OP_EMPTY_DICT = '}'; // push empty dict 0067 const OP_APPENDS = 'e'; // extend list on stack by topmost stack slice 0068 const OP_GET = 'g'; // push item from memo on stack; index is string arg 0069 const OP_BINGET = 'h'; // " " " " " " ; " " 1-byte arg 0070 const OP_INST = 'i'; // build & push class instance 0071 const OP_LONG_BINGET = 'j'; // push item from memo on stack; index is 4-byte arg 0072 const OP_LIST = 'l'; // build list from topmost stack items 0073 const OP_EMPTY_LIST = ']'; // push empty list 0074 const OP_OBJ = 'o'; // build & push class instance 0075 const OP_PUT = 'p'; // store stack top in memo; index is string arg 0076 const OP_BINPUT = 'q'; // " " " " " ; " " 1-byte arg 0077 const OP_LONG_BINPUT = 'r'; // " " " " " ; " " 4-byte arg 0078 const OP_SETITEM = 's'; // add key+value pair to dict 0079 const OP_TUPLE = 't'; // build tuple from topmost stack items 0080 const OP_EMPTY_TUPLE = ')'; // push empty tuple 0081 const OP_SETITEMS = 'u'; // modify dict by adding topmost key+value pairs 0082 const OP_BINFLOAT = 'G'; // push float; arg is 8-byte float encoding 0083 0084 /* Protocol 2 */ 0085 const OP_PROTO = "\x80"; // identify pickle protocol 0086 const OP_NEWOBJ = "\x81"; // build object by applying cls.__new__ to argtuple 0087 const OP_EXT1 = "\x82"; // push object from extension registry; 1-byte index 0088 const OP_EXT2 = "\x83"; // ditto, but 2-byte index 0089 const OP_EXT4 = "\x84"; // ditto, but 4-byte index 0090 const OP_TUPLE1 = "\x85"; // build 1-tuple from stack top 0091 const OP_TUPLE2 = "\x86"; // build 2-tuple from two topmost stack items 0092 const OP_TUPLE3 = "\x87"; // build 3-tuple from three topmost stack items 0093 const OP_NEWTRUE = "\x88"; // push True 0094 const OP_NEWFALSE = "\x89"; // push False 0095 const OP_LONG1 = "\x8a"; // push long from < 256 bytes 0096 const OP_LONG4 = "\x8b"; // push really big long 0097 0098 /* Protocol 3 (Python 3.x) */ 0099 const OP_BINBYTES = 'B'; // push bytes; counted binary string argument 0100 const OP_SHORT_BINBYTES = 'C'; // " " ; " " " " < 256 bytes 0101 0102 /** 0103 * @var bool Whether or not the system is little-endian 0104 */ 0105 protected static $_isLittleEndian = null; 0106 0107 /** 0108 * @var array Strings representing quotes 0109 */ 0110 protected static $_quoteString = array( 0111 '\\' => '\\\\', 0112 "\x00" => '\\x00', "\x01" => '\\x01', "\x02" => '\\x02', "\x03" => '\\x03', 0113 "\x04" => '\\x04', "\x05" => '\\x05', "\x06" => '\\x06', "\x07" => '\\x07', 0114 "\x08" => '\\x08', "\x09" => '\\t', "\x0a" => '\\n', "\x0b" => '\\x0b', 0115 "\x0c" => '\\x0c', "\x0d" => '\\r', "\x0e" => '\\x0e', "\x0f" => '\\x0f', 0116 "\x10" => '\\x10', "\x11" => '\\x11', "\x12" => '\\x12', "\x13" => '\\x13', 0117 "\x14" => '\\x14', "\x15" => '\\x15', "\x16" => '\\x16', "\x17" => '\\x17', 0118 "\x18" => '\\x18', "\x19" => '\\x19', "\x1a" => '\\x1a', "\x1b" => '\\x1b', 0119 "\x1c" => '\\x1c', "\x1d" => '\\x1d', "\x1e" => '\\x1e', "\x1f" => '\\x1f', 0120 "\xff" => '\\xff' 0121 ); 0122 0123 /** 0124 * @var array Default options 0125 */ 0126 protected $_options = array( 0127 'protocol' => 0, 0128 ); 0129 0130 // process vars 0131 protected $_protocol = 0; 0132 protected $_binary = false; 0133 protected $_memo = array(); 0134 protected $_pickle = ''; 0135 protected $_pickleLen = 0; 0136 protected $_pos = 0; 0137 protected $_stack = array(); 0138 protected $_marker = null; 0139 0140 /** 0141 * Constructor 0142 * 0143 * @link Zend_Serializer_Adapter_AdapterAbstract::__construct() 0144 */ 0145 public function __construct($opts=array()) 0146 { 0147 parent::__construct($opts); 0148 0149 // init 0150 if (self::$_isLittleEndian === null) { 0151 self::$_isLittleEndian = (pack('l', 1) === "\x01\x00\x00\x00"); 0152 } 0153 0154 $this->_marker = new stdClass(); 0155 } 0156 0157 /** 0158 * Set an option 0159 * 0160 * @link Zend_Serializer_Adapter_AdapterAbstract::setOption() 0161 * @param string $name 0162 * @param mixed $value 0163 * @return Zend_Serializer_Adapter_PythonPickle 0164 */ 0165 public function setOption($name, $value) 0166 { 0167 switch ($name) { 0168 case 'protocol': 0169 $value = $this->_checkProtocolNumber($value); 0170 break; 0171 } 0172 0173 return parent::setOption($name, $value); 0174 } 0175 0176 /** 0177 * Check and normalize pickle protocol number 0178 * 0179 * @param int $number 0180 * @return int 0181 * @throws Zend_Serializer_Exception 0182 */ 0183 protected function _checkProtocolNumber($number) 0184 { 0185 $int = (int) $number; 0186 if ($int < 0 || $int > 3) { 0187 // require_once 'Zend/Serializer/Exception.php'; 0188 throw new Zend_Serializer_Exception('Invalid or unknown protocol version "'.$number.'"'); 0189 } 0190 return $int; 0191 } 0192 0193 /* serialize */ 0194 0195 /** 0196 * Serialize PHP to PythonPickle format 0197 * 0198 * @param mixed $value 0199 * @param array $opts 0200 * @return string 0201 */ 0202 public function serialize($value, array $opts = array()) 0203 { 0204 $opts = $opts + $this->_options; 0205 0206 $this->_protocol = $this->_checkProtocolNumber($opts['protocol']); 0207 $this->_binary = $this->_protocol != 0; 0208 0209 // clear process vars before serializing 0210 $this->_memo = array(); 0211 $this->_pickle = ''; 0212 0213 // write 0214 if ($this->_protocol >= 2) { 0215 $this->_writeProto($this->_protocol); 0216 } 0217 $this->_write($value); 0218 $this->_writeStop(); 0219 0220 // clear process vars after serializing 0221 $this->_memo = array(); 0222 $pickle = $this->_pickle; 0223 $this->_pickle = ''; 0224 0225 return $pickle; 0226 } 0227 0228 /** 0229 * Write a value 0230 * 0231 * @param mixed $value 0232 * @return void 0233 * @throws Zend_Serializer_Exception on invalid or unrecognized value type 0234 */ 0235 protected function _write($value) 0236 { 0237 if ($value === null) { 0238 $this->_writeNull(); 0239 } elseif ($value === true) { 0240 $this->_writeTrue(); 0241 } elseif ($value === false) { 0242 $this->_writeFalse(); 0243 } elseif (is_int($value)) { 0244 $this->_writeInt($value); 0245 } elseif (is_float($value)) { 0246 $this->_writeFloat($value); 0247 } elseif (is_string($value)) { 0248 // TODO: write unicode / binary 0249 $this->_writeString($value); 0250 } elseif (is_array($value)) { 0251 if ($this->_isArrayAssoc($value)) { 0252 $this->_writeArrayDict($value); 0253 } else { 0254 $this->_writeArrayList($value); 0255 } 0256 } elseif (is_object($value)) { 0257 $this->_writeObject($value); 0258 } else { 0259 // require_once 'Zend/Serializer/Exception.php'; 0260 throw new Zend_Serializer_Exception( 0261 'PHP-Type "'.gettype($value).'" isn\'t serializable with '.get_class($this) 0262 ); 0263 } 0264 } 0265 0266 /** 0267 * Write pickle protocol 0268 * 0269 * @param int $protocol 0270 * @return void 0271 */ 0272 protected function _writeProto($protocol) 0273 { 0274 $this->_pickle .= self::OP_PROTO . $protocol; 0275 } 0276 0277 /** 0278 * Write a get 0279 * 0280 * @param int $id Id of memo 0281 * @return void 0282 */ 0283 protected function _writeGet($id) 0284 { 0285 if ($this->_binary) { 0286 if ($id <= 0xff) { 0287 // BINGET + chr(i) 0288 $this->_pickle .= self::OP_BINGET . chr($id); 0289 } else { 0290 // LONG_BINGET + pack("<i", i) 0291 $bin = pack('l', $id); 0292 if (self::$_isLittleEndian === false) { 0293 $bin = strrev($bin); 0294 } 0295 $this->_pickle .= self::OP_LONG_BINGET . $bin; 0296 } 0297 } else { 0298 $this->_pickle .= self::OP_GET . $id . "\r\n"; 0299 } 0300 } 0301 0302 /** 0303 * Write a put 0304 * 0305 * @param int $id Id of memo 0306 * @return void 0307 */ 0308 protected function _writePut($id) 0309 { 0310 if ($this->_binary) { 0311 if ($id <= 0xff) { 0312 // BINPUT + chr(i) 0313 $this->_pickle .= self::OP_BINPUT . chr($id); 0314 } else { 0315 // LONG_BINPUT + pack("<i", i) 0316 $bin = pack('l', $id); 0317 if (self::$_isLittleEndian === false) { 0318 $bin = strrev($bin); 0319 } 0320 $this->_pickle .= self::OP_LONG_BINPUT . $bin; 0321 } 0322 } else { 0323 $this->_pickle .= self::OP_PUT . $id . "\r\n"; 0324 } 0325 } 0326 0327 /** 0328 * Write a null as None 0329 * 0330 * @return void 0331 */ 0332 protected function _writeNull() 0333 { 0334 $this->_pickle .= self::OP_NONE; 0335 } 0336 0337 /** 0338 * Write a boolean true 0339 * 0340 * @return void 0341 */ 0342 protected function _writeTrue() 0343 { 0344 if ($this->_protocol >= 2) { 0345 $this->_pickle .= self::OP_NEWTRUE; 0346 } else { 0347 $this->_pickle .= self::OP_INT . "01\r\n"; 0348 } 0349 } 0350 0351 /** 0352 * Write a boolean false 0353 * 0354 * @return void 0355 */ 0356 protected function _writeFalse() 0357 { 0358 if ($this->_protocol >= 2) { 0359 $this->_pickle .= self::OP_NEWFALSE; 0360 } else { 0361 $this->_pickle .= self::OP_INT . "00\r\n"; 0362 } 0363 } 0364 0365 /** 0366 * Write an integer value 0367 * 0368 * @param int $value 0369 * @return void 0370 */ 0371 protected function _writeInt($value) 0372 { 0373 if ($this->_binary) { 0374 if ($value >= 0) { 0375 if ($value <= 0xff) { 0376 // self.write(BININT1 + chr(obj)) 0377 $this->_pickle .= self::OP_BININT1 . chr($value); 0378 } elseif ($value <= 0xffff) { 0379 // self.write("%c%c%c" % (BININT2, obj&0xff, obj>>8)) 0380 $this->_pickle .= self::OP_BININT2 . pack('v', $value); 0381 } 0382 return; 0383 } 0384 0385 // Next check for 4-byte signed ints: 0386 $highBits = $value >> 31; // note that Python shift sign-extends 0387 if ($highBits == 0 || $highBits == -1) { 0388 // All high bits are copies of bit 2**31, so the value 0389 // fits in a 4-byte signed int. 0390 // self.write(BININT + pack("<i", obj)) 0391 $bin = pack('l', $value); 0392 if (self::$_isLittleEndian === false) { 0393 $bin = strrev($bin); 0394 } 0395 $this->_pickle .= self::OP_BININT . $bin; 0396 return; 0397 } 0398 } 0399 0400 $this->_pickle .= self::OP_INT . $value . "\r\n"; 0401 } 0402 0403 /** 0404 * Write a float value 0405 * 0406 * @param float $value 0407 * @return void 0408 */ 0409 protected function _writeFloat($value) 0410 { 0411 if ($this->_binary) { 0412 // self.write(BINFLOAT + pack('>d', obj)) 0413 $bin = pack('d', $value); 0414 if (self::$_isLittleEndian === true) { 0415 $bin = strrev($bin); 0416 } 0417 $this->_pickle .= self::OP_BINFLOAT . $bin; 0418 } else { 0419 $this->_pickle .= self::OP_FLOAT . $value . "\r\n"; 0420 } 0421 } 0422 0423 /** 0424 * Write a string value 0425 * 0426 * @param string $value 0427 * @return void 0428 */ 0429 protected function _writeString($value) 0430 { 0431 if ( ($id=$this->_searchMomo($value)) !== false ) { 0432 $this->_writeGet($id); 0433 return; 0434 } 0435 0436 if ($this->_binary) { 0437 $n = strlen($value); 0438 if ($n <= 0xff) { 0439 // self.write(SHORT_BINSTRING + chr(n) + obj) 0440 $this->_pickle .= self::OP_SHORT_BINSTRING . chr($n) . $value; 0441 } else { 0442 // self.write(BINSTRING + pack("<i", n) + obj) 0443 $binLen = pack('l', $n); 0444 if (self::$_isLittleEndian === false) { 0445 $binLen = strrev($binLen); 0446 } 0447 $this->_pickle .= self::OP_BINSTRING . $binLen . $value; 0448 } 0449 } else { 0450 $this->_pickle .= self::OP_STRING . $this->_quoteString($value) . "\r\n"; 0451 } 0452 0453 $this->_momorize($value); 0454 } 0455 0456 /** 0457 * Write an associative array value as dictionary 0458 * 0459 * @param array $value 0460 * @return void 0461 */ 0462 protected function _writeArrayDict(array $value) 0463 { 0464 if (($id=$this->_searchMomo($value)) !== false) { 0465 $this->_writeGet($id);; 0466 return; 0467 } 0468 0469 $this->_pickle .= self::OP_MARK . self::OP_DICT; 0470 $this->_momorize($value); 0471 0472 foreach ($value as $k => $v) { 0473 $this->_pickle .= $this->_write($k) 0474 . $this->_write($v) 0475 . self::OP_SETITEM; 0476 } 0477 } 0478 0479 /** 0480 * Write a simple array value as list 0481 * 0482 * @param array $value 0483 * @return void 0484 */ 0485 protected function _writeArrayList(array $value) 0486 { 0487 if (($id = $this->_searchMomo($value)) !== false) { 0488 $this->_writeGet($id); 0489 return; 0490 } 0491 0492 $this->_pickle .= self::OP_MARK . self::OP_LIST; 0493 $this->_momorize($value); 0494 0495 foreach ($value as $k => $v) { 0496 $this->_pickle .= $this->_write($v) . self::OP_APPEND; 0497 } 0498 } 0499 0500 /** 0501 * Write an object as an dictionary 0502 * 0503 * @param object $value 0504 * @return void 0505 */ 0506 protected function _writeObject($value) 0507 { 0508 // can't serialize php objects to python objects yet 0509 $this->_writeArrayDict(get_object_vars($value)); 0510 } 0511 0512 /** 0513 * Write stop 0514 * 0515 * @return void 0516 */ 0517 protected function _writeStop() 0518 { 0519 $this->_pickle .= self::OP_STOP; 0520 } 0521 0522 /* serialize helper */ 0523 0524 /** 0525 * Add a value to the memo and write the id 0526 * 0527 * @param mixed $value 0528 * @return void 0529 */ 0530 protected function _momorize($value) 0531 { 0532 $id = count($this->_memo); 0533 $this->_memo[$id] = $value; 0534 $this->_writePut($id); 0535 } 0536 0537 /** 0538 * Search a value in the meno and return the id 0539 * 0540 * @param mixed $value 0541 * @return int|false The id or false 0542 */ 0543 protected function _searchMomo($value) 0544 { 0545 return array_search($value, $this->_memo, true); 0546 } 0547 0548 /** 0549 * Is an array associative? 0550 * 0551 * @param array $value 0552 * @return boolean 0553 */ 0554 protected function _isArrayAssoc(array $value) 0555 { 0556 return array_diff_key($value, array_keys(array_keys($value))); 0557 } 0558 0559 /** 0560 * Quote/Escape a string 0561 * 0562 * @param string $str 0563 * @return string quoted string 0564 */ 0565 protected function _quoteString($str) 0566 { 0567 $quoteArr = self::$_quoteString; 0568 0569 if (($cntSingleQuote = substr_count($str, "'")) 0570 && ($cntDoubleQuote = substr_count($str, '"')) 0571 && ($cntSingleQuote < $cntDoubleQuote) 0572 ) { 0573 $quoteArr['"'] = '\\"'; 0574 $enclosure = '"'; 0575 } else { 0576 $quoteArr["'"] = "\\'"; 0577 $enclosure = "'"; 0578 } 0579 0580 return $enclosure . strtr($str, $quoteArr) . $enclosure; 0581 } 0582 0583 /* unserialize */ 0584 0585 /** 0586 * Unserialize from Python Pickle format to PHP 0587 * 0588 * @param string $pickle 0589 * @param array $opts 0590 * @return mixed 0591 * @throws Zend_Serializer_Exception on invalid Pickle string 0592 */ 0593 public function unserialize($pickle, array $opts = array()) 0594 { 0595 // init process vars 0596 $this->_pos = 0; 0597 $this->_pickle = $pickle; 0598 $this->_pickleLen = strlen($this->_pickle); 0599 $this->_memo = array(); 0600 $this->_stack = array(); 0601 0602 // read pickle string 0603 while (($op=$this->_read(1)) !== self::OP_STOP) { 0604 $this->_load($op); 0605 } 0606 0607 if (!count($this->_stack)) { 0608 // require_once 'Zend/Serializer/Exception.php'; 0609 throw new Zend_Serializer_Exception('No data found'); 0610 } 0611 0612 $ret = array_pop($this->_stack); 0613 0614 // clear process vars 0615 $this->_pos = 0; 0616 $this->_pickle = ''; 0617 $this->_pickleLen = 0; 0618 $this->_memo = array(); 0619 $this->_stack = array(); 0620 0621 return $ret; 0622 } 0623 0624 /** 0625 * Load a pickle opcode 0626 * 0627 * @param string $op 0628 * @return void 0629 * @throws Zend_Serializer_Exception on invalid opcode 0630 */ 0631 protected function _load($op) 0632 { 0633 switch ($op) { 0634 case self::OP_PUT: 0635 $this->_loadPut(); 0636 break; 0637 case self::OP_BINPUT: 0638 $this->_loadBinPut(); 0639 break; 0640 case self::OP_LONG_BINPUT: 0641 $this->_loadLongBinPut(); 0642 break; 0643 case self::OP_GET: 0644 $this->_loadGet(); 0645 break; 0646 case self::OP_BINGET: 0647 $this->_loadBinGet(); 0648 break; 0649 case self::OP_LONG_BINGET: 0650 $this->_loadLongBinGet(); 0651 break; 0652 case self::OP_NONE: 0653 $this->_loadNone(); 0654 break; 0655 case self::OP_NEWTRUE: 0656 $this->_loadNewTrue(); 0657 break; 0658 case self::OP_NEWFALSE: 0659 $this->_loadNewFalse(); 0660 break; 0661 case self::OP_INT: 0662 $this->_loadInt(); 0663 break; 0664 case self::OP_BININT: 0665 $this->_loadBinInt(); 0666 break; 0667 case self::OP_BININT1: 0668 $this->_loadBinInt1(); 0669 break; 0670 case self::OP_BININT2: 0671 $this->_loadBinInt2(); 0672 break; 0673 case self::OP_LONG: 0674 $this->_loadLong(); 0675 break; 0676 case self::OP_LONG1: 0677 $this->_loadLong1(); 0678 break; 0679 case self::OP_LONG4: 0680 $this->_loadLong4(); 0681 break; 0682 case self::OP_FLOAT: 0683 $this->_loadFloat(); 0684 break; 0685 case self::OP_BINFLOAT: 0686 $this->_loadBinFloat(); 0687 break; 0688 case self::OP_STRING: 0689 $this->_loadString(); 0690 break; 0691 case self::OP_BINSTRING: 0692 $this->_loadBinString(); 0693 break; 0694 case self::OP_SHORT_BINSTRING: 0695 $this->_loadShortBinString(); 0696 break; 0697 case self::OP_BINBYTES: 0698 $this->_loadBinBytes(); 0699 break; 0700 case self::OP_SHORT_BINBYTES: 0701 $this->_loadShortBinBytes(); 0702 break; 0703 case self::OP_UNICODE: 0704 $this->_loadUnicode(); 0705 break; 0706 case self::OP_BINUNICODE: 0707 $this->_loadBinUnicode(); 0708 break; 0709 case self::OP_MARK: 0710 $this->_loadMark(); 0711 break; 0712 case self::OP_LIST: 0713 $this->_loadList(); 0714 break; 0715 case self::OP_EMPTY_LIST: 0716 $this->_loadEmptyList(); 0717 break; 0718 case self::OP_APPEND: 0719 $this->_loadAppend(); 0720 break; 0721 case self::OP_APPENDS: 0722 $this->_loadAppends(); 0723 break; 0724 case self::OP_DICT: 0725 $this->_loadDict(); 0726 break; 0727 case self::OP_EMPTY_DICT: 0728 $this->_loadEmptyDict(); 0729 break; 0730 case self::OP_SETITEM: 0731 $this->_loadSetItem(); 0732 break; 0733 case self::OP_SETITEMS: 0734 $this->_loadSetItems(); 0735 break; 0736 case self::OP_TUPLE: 0737 $this->_loadTuple(); 0738 break; 0739 case self::OP_TUPLE1: 0740 $this->_loadTuple1(); 0741 break; 0742 case self::OP_TUPLE2: 0743 $this->_loadTuple2(); 0744 break; 0745 case self::OP_TUPLE3: 0746 $this->_loadTuple3(); 0747 break; 0748 case self::OP_PROTO: 0749 $this->_loadProto(); 0750 break; 0751 default: 0752 // require_once 'Zend/Serializer/Exception.php'; 0753 throw new Zend_Serializer_Exception('Invalid or unknown opcode "'.$op.'"'); 0754 } 0755 } 0756 0757 /** 0758 * Load a PUT opcode 0759 * 0760 * @return void 0761 * @throws Zend_Serializer_Exception on missing stack 0762 */ 0763 protected function _loadPut() 0764 { 0765 $id = (int)$this->_readline(); 0766 0767 $lastStack = count($this->_stack)-1; 0768 if (!isset($this->_stack[$lastStack])) { 0769 // require_once 'Zend/Serializer/Exception.php'; 0770 throw new Zend_Serializer_Exception('No stack exist'); 0771 } 0772 $this->_memo[$id] = & $this->_stack[$lastStack]; 0773 } 0774 0775 /** 0776 * Load a binary PUT 0777 * 0778 * @return void 0779 * @throws Zend_Serializer_Exception on missing stack 0780 */ 0781 protected function _loadBinPut() 0782 { 0783 $id = ord($this->_read(1)); 0784 0785 $lastStack = count($this->_stack)-1; 0786 if (!isset($this->_stack[$lastStack])) { 0787 // require_once 'Zend/Serializer/Exception.php'; 0788 throw new Zend_Serializer_Exception('No stack exist'); 0789 } 0790 $this->_memo[$id] = & $this->_stack[$lastStack]; 0791 } 0792 0793 /** 0794 * Load a long binary PUT 0795 * 0796 * @return void 0797 * @throws Zend_Serializer_Exception on missing stack 0798 */ 0799 protected function _loadLongBinPut() 0800 { 0801 $bin = $this->_read(4); 0802 if (self::$_isLittleEndian === false) { 0803 $bin = strrev($bin); 0804 } 0805 list(, $id) = unpack('l', $bin); 0806 0807 $lastStack = count($this->_stack)-1; 0808 if (!isset($this->_stack[$lastStack])) { 0809 // require_once 'Zend/Serializer/Exception.php'; 0810 throw new Zend_Serializer_Exception('No stack exist'); 0811 } 0812 $this->_memo[$id] = & $this->_stack[$lastStack]; 0813 } 0814 0815 /** 0816 * Load a GET operation 0817 * 0818 * @return void 0819 * @throws Zend_Serializer_Exception on missing GET identifier 0820 */ 0821 protected function _loadGet() 0822 { 0823 $id = (int)$this->_readline(); 0824 0825 if (!array_key_exists($id, $this->_memo)) { 0826 // require_once 'Zend/Serializer/Exception.php'; 0827 throw new Zend_Serializer_Exception('Get id "' . $id . '" not found in momo'); 0828 } 0829 $this->_stack[] = & $this->_memo[$id]; 0830 } 0831 0832 /** 0833 * Load a binary GET operation 0834 * 0835 * @return void 0836 * @throws Zend_Serializer_Exception on missing GET identifier 0837 */ 0838 protected function _loadBinGet() 0839 { 0840 $id = ord($this->_read(1)); 0841 0842 if (!array_key_exists($id, $this->_memo)) { 0843 // require_once 'Zend/Serializer/Exception.php'; 0844 throw new Zend_Serializer_Exception('Get id "' . $id . '" not found in momo'); 0845 } 0846 $this->_stack[] = & $this->_memo[$id]; 0847 } 0848 0849 /** 0850 * Load a long binary GET operation 0851 * 0852 * @return void 0853 * @throws Zend_Serializer_Exception on missing GET identifier 0854 */ 0855 protected function _loadLongBinGet() 0856 { 0857 $bin = $this->_read(4); 0858 if (self::$_isLittleEndian === false) { 0859 $bin = strrev($bin); 0860 } 0861 list(, $id) = unpack('l', $bin); 0862 0863 if (!array_key_exists($id, $this->_memo)) { 0864 // require_once 'Zend/Serializer/Exception.php'; 0865 throw new Zend_Serializer_Exception('Get id "' . $id . '" not found in momo'); 0866 } 0867 $this->_stack[] = & $this->_memo[$id]; 0868 } 0869 0870 /** 0871 * Load a NONE operator 0872 * 0873 * @return void 0874 */ 0875 protected function _loadNone() 0876 { 0877 $this->_stack[] = null; 0878 } 0879 0880 /** 0881 * Load a boolean TRUE operator 0882 * 0883 * @return void 0884 */ 0885 protected function _loadNewTrue() 0886 { 0887 $this->_stack[] = true; 0888 } 0889 0890 /** 0891 * Load a boolean FALSE operator 0892 * 0893 * @return void 0894 */ 0895 protected function _loadNewFalse() 0896 { 0897 $this->_stack[] = false; 0898 } 0899 0900 /** 0901 * Load an integer operator 0902 * 0903 * @return void 0904 */ 0905 protected function _loadInt() 0906 { 0907 $line = $this->_readline(); 0908 if ($line === '01') { 0909 $this->_stack[] = true; 0910 } elseif ($line === '00') { 0911 $this->_stack[] = false; 0912 } else { 0913 $this->_stack[] = (int)$line; 0914 } 0915 } 0916 0917 /** 0918 * Load a binary integer operator 0919 * 0920 * @return void 0921 */ 0922 protected function _loadBinInt() 0923 { 0924 $bin = $this->_read(4); 0925 if (self::$_isLittleEndian === false) { 0926 $bin = strrev($bin); 0927 } 0928 list(, $int) = unpack('l', $bin); 0929 $this->_stack[] = $int; 0930 } 0931 0932 /** 0933 * Load the first byte of a binary integer 0934 * 0935 * @return void 0936 */ 0937 protected function _loadBinInt1() 0938 { 0939 $this->_stack[] = ord($this->_read(1)); 0940 } 0941 0942 /** 0943 * Load the second byte of a binary integer 0944 * 0945 * @return void 0946 */ 0947 protected function _loadBinInt2() 0948 { 0949 $bin = $this->_read(2); 0950 list(, $int) = unpack('v', $bin); 0951 $this->_stack[] = $int; 0952 } 0953 0954 /** 0955 * Load a long (float) operator 0956 * 0957 * @return void 0958 */ 0959 protected function _loadLong() 0960 { 0961 $data = rtrim($this->_readline(), 'L'); 0962 if ($data === '') { 0963 $this->_stack[] = 0; 0964 } else { 0965 $this->_stack[] = $data; 0966 } 0967 } 0968 0969 /** 0970 * Load a one byte long integer 0971 * 0972 * @return void 0973 */ 0974 protected function _loadLong1() 0975 { 0976 $n = ord($this->_read(1)); 0977 $data = $this->_read($n); 0978 $this->_stack[] = $this->_decodeBinLong($data); 0979 } 0980 0981 /** 0982 * Load a 4 byte long integer 0983 * 0984 * @return void 0985 */ 0986 protected function _loadLong4() 0987 { 0988 $nBin = $this->_read(4); 0989 if (self::$_isLittleEndian === false) { 0990 $nBin = strrev($$nBin); 0991 } 0992 list(, $n) = unpack('l', $nBin); 0993 $data = $this->_read($n); 0994 0995 $this->_stack[] = $this->_decodeBinLong($data); 0996 } 0997 0998 /** 0999 * Load a float value 1000 * 1001 * @return void 1002 */ 1003 protected function _loadFloat() 1004 { 1005 $float = (float)$this->_readline(); 1006 $this->_stack[] = $float; 1007 } 1008 1009 /** 1010 * Load a binary float value 1011 * 1012 * @return void 1013 */ 1014 protected function _loadBinFloat() 1015 { 1016 $bin = $this->_read(8); 1017 if (self::$_isLittleEndian === true) { 1018 $bin = strrev($bin); 1019 } 1020 list(, $float) = unpack('d', $bin); 1021 $this->_stack[] = $float; 1022 } 1023 1024 /** 1025 * Load a string 1026 * 1027 * @return void 1028 */ 1029 protected function _loadString() 1030 { 1031 $this->_stack[] = $this->_unquoteString((string)$this->_readline()); 1032 } 1033 1034 /** 1035 * Load a binary string 1036 * 1037 * @return void 1038 */ 1039 protected function _loadBinString() 1040 { 1041 $bin = $this->_read(4); 1042 if (!self::$_isLittleEndian) { 1043 $bin = strrev($bin); 1044 } 1045 list(, $len) = unpack('l', $bin); 1046 $this->_stack[] = (string)$this->_read($len); 1047 } 1048 1049 /** 1050 * Load a short binary string 1051 * 1052 * @return void 1053 */ 1054 protected function _loadShortBinString() 1055 { 1056 $len = ord($this->_read(1)); 1057 $this->_stack[] = (string)$this->_read($len); 1058 } 1059 1060 /** 1061 * Load arbitrary binary bytes 1062 * 1063 * @return void 1064 */ 1065 protected function _loadBinBytes() 1066 { 1067 // read byte length 1068 $nBin = $this->_read(4); 1069 if (self::$_isLittleEndian === false) { 1070 $nBin = strrev($$nBin); 1071 } 1072 list(, $n) = unpack('l', $nBin); 1073 $this->_stack[] = $this->_read($n); 1074 } 1075 1076 /** 1077 * Load a single binary byte 1078 * 1079 * @return void 1080 */ 1081 protected function _loadShortBinBytes() 1082 { 1083 $n = ord($this->_read(1)); 1084 $this->_stack[] = $this->_read($n); 1085 } 1086 1087 /** 1088 * Load a unicode string 1089 * 1090 * @return void 1091 */ 1092 protected function _loadUnicode() 1093 { 1094 $data = $this->_readline(); 1095 $pattern = '/\\\\u([a-fA-F0-9]{4})/u'; // \uXXXX 1096 $data = preg_replace_callback($pattern, array($this, '_convertMatchingUnicodeSequence2Utf8'), $data); 1097 1098 $this->_stack[] = $data; 1099 } 1100 1101 /** 1102 * Convert a unicode sequence to UTF-8 1103 * 1104 * @param array $match 1105 * @return string 1106 */ 1107 protected function _convertMatchingUnicodeSequence2Utf8(array $match) 1108 { 1109 return $this->_hex2Utf8($match[1]); 1110 } 1111 1112 /** 1113 * Convert a hex string to a UTF-8 string 1114 * 1115 * @param string $sequence 1116 * @return string 1117 * @throws Zend_Serializer_Exception on unmatched unicode sequence 1118 */ 1119 protected function _hex2Utf8($hex) 1120 { 1121 $uniCode = hexdec($hex); 1122 1123 if ($uniCode < 0x80) { // 1Byte 1124 $utf8Char = chr($uniCode); 1125 1126 } elseif ($uniCode < 0x800) { // 2Byte 1127 $utf8Char = chr(0xC0 | $uniCode >> 6) 1128 . chr(0x80 | $uniCode & 0x3F); 1129 1130 } elseif ($uniCode < 0x10000) { // 3Byte 1131 $utf8Char = chr(0xE0 | $uniCode >> 12) 1132 . chr(0x80 | $uniCode >> 6 & 0x3F) 1133 . chr(0x80 | $uniCode & 0x3F); 1134 1135 } elseif ($uniCode < 0x110000) { // 4Byte 1136 $utf8Char = chr(0xF0 | $uniCode >> 18) 1137 . chr(0x80 | $uniCode >> 12 & 0x3F) 1138 . chr(0x80 | $uniCode >> 6 & 0x3F) 1139 . chr(0x80 | $uniCode & 0x3F); 1140 } else { 1141 // require_once 'Zend/Serializer/Exception.php'; 1142 throw new Zend_Serializer_Exception('Unsupported unicode character found "' . dechex($uniCode) . '"'); 1143 } 1144 1145 return $utf8Char; 1146 } 1147 1148 /** 1149 * Load binary unicode sequence 1150 * 1151 * @return void 1152 */ 1153 protected function _loadBinUnicode() 1154 { 1155 // read byte length 1156 $n = $this->_read(4); 1157 if (self::$_isLittleEndian === false) { 1158 $n = strrev($n); 1159 } 1160 list(, $n) = unpack('l', $n); 1161 $data = $this->_read($n); 1162 1163 $this->_stack[] = $data; 1164 } 1165 1166 /** 1167 * Load a marker sequence 1168 * 1169 * @return void 1170 */ 1171 protected function _loadMark() 1172 { 1173 $this->_stack[] = $this->_marker; 1174 } 1175 1176 /** 1177 * Load an array (list) 1178 * 1179 * @return void 1180 */ 1181 protected function _loadList() 1182 { 1183 $k = $this->_lastMarker(); 1184 $this->_stack[$k] = array(); 1185 1186 // remove all elements after marker 1187 $max = count($this->_stack); 1188 for ($i = $k+1, $max; $i < $max; $i++) { 1189 unset($this->_stack[$i]); 1190 } 1191 } 1192 1193 /** 1194 * Load an append (to list) sequence 1195 * 1196 * @return void 1197 */ 1198 protected function _loadAppend() 1199 { 1200 $value = array_pop($this->_stack); 1201 $list =& $this->_stack[count($this->_stack)-1]; 1202 $list[] = $value; 1203 } 1204 1205 /** 1206 * Load an empty list sequence 1207 * 1208 * @return void 1209 */ 1210 protected function _loadEmptyList() 1211 { 1212 $this->_stack[] = array(); 1213 } 1214 1215 /** 1216 * Load multiple append (to list) sequences at once 1217 * 1218 * @return void 1219 */ 1220 protected function _loadAppends() 1221 { 1222 $k = $this->_lastMarker(); 1223 $list =& $this->_stack[$k - 1]; 1224 $max = count($this->_stack); 1225 for ($i = $k + 1; $i < $max; $i++) { 1226 $list[] = $this->_stack[$i]; 1227 unset($this->_stack[$i]); 1228 } 1229 unset($this->_stack[$k]); 1230 } 1231 1232 /** 1233 * Load an associative array (Python dictionary) 1234 * 1235 * @return void 1236 */ 1237 protected function _loadDict() 1238 { 1239 $k = $this->_lastMarker(); 1240 $this->_stack[$k] = array(); 1241 1242 // remove all elements after marker 1243 $max = count($this->_stack); 1244 for($i = $k + 1; $i < $max; $i++) { 1245 unset($this->_stack[$i]); 1246 } 1247 } 1248 1249 /** 1250 * Load an item from a set 1251 * 1252 * @return void 1253 */ 1254 protected function _loadSetItem() 1255 { 1256 $value = array_pop($this->_stack); 1257 $key = array_pop($this->_stack); 1258 $dict =& $this->_stack[count($this->_stack) - 1]; 1259 $dict[$key] = $value; 1260 } 1261 1262 /** 1263 * Load an empty dictionary 1264 * 1265 * @return void 1266 */ 1267 protected function _loadEmptyDict() 1268 { 1269 $this->_stack[] = array(); 1270 } 1271 1272 /** 1273 * Load set items 1274 * 1275 * @return void 1276 */ 1277 protected function _loadSetItems() 1278 { 1279 $k = $this->_lastMarker(); 1280 $dict =& $this->_stack[$k - 1]; 1281 $max = count($this->_stack); 1282 for ($i = $k + 1; $i < $max; $i += 2) { 1283 $key = $this->_stack[$i]; 1284 $value = $this->_stack[$i + 1]; 1285 $dict[$key] = $value; 1286 unset($this->_stack[$i], $this->_stack[$i+1]); 1287 } 1288 unset($this->_stack[$k]); 1289 } 1290 1291 /** 1292 * Load a tuple 1293 * 1294 * @return void 1295 */ 1296 protected function _loadTuple() 1297 { 1298 $k = $this->_lastMarker(); 1299 $this->_stack[$k] = array(); 1300 $tuple =& $this->_stack[$k]; 1301 $max = count($this->_stack); 1302 for($i = $k + 1; $i < $max; $i++) { 1303 $tuple[] = $this->_stack[$i]; 1304 unset($this->_stack[$i]); 1305 } 1306 } 1307 1308 /** 1309 * Load single item tuple 1310 * 1311 * @return void 1312 */ 1313 protected function _loadTuple1() 1314 { 1315 $value1 = array_pop($this->_stack); 1316 $this->_stack[] = array($value1); 1317 } 1318 1319 /** 1320 * Load two item tuple 1321 * 1322 * @return void 1323 */ 1324 protected function _loadTuple2() 1325 { 1326 $value2 = array_pop($this->_stack); 1327 $value1 = array_pop($this->_stack); 1328 $this->_stack[] = array($value1, $value2); 1329 } 1330 1331 /** 1332 * Load three item tuple 1333 * 1334 * @return void 1335 */ 1336 protected function _loadTuple3() { 1337 $value3 = array_pop($this->_stack); 1338 $value2 = array_pop($this->_stack); 1339 $value1 = array_pop($this->_stack); 1340 $this->_stack[] = array($value1, $value2, $value3); 1341 } 1342 1343 /** 1344 * Load a proto value 1345 * 1346 * @return void 1347 * @throws Zend_Serializer_Exception if Pickle version does not support this feature 1348 */ 1349 protected function _loadProto() 1350 { 1351 $proto = ord($this->_read(1)); 1352 if ($proto < 2 || $proto > 3) { 1353 // require_once 'Zend/Serializer/Exception.php'; 1354 throw new Zend_Serializer_Exception('Invalid protocol version detected'); 1355 } 1356 $this->_protocol = $proto; 1357 } 1358 1359 /* unserialize helper */ 1360 1361 /** 1362 * Read a segment of the pickle 1363 * 1364 * @param mixed $len 1365 * @return string 1366 * @throws Zend_Serializer_Exception if position matches end of data 1367 */ 1368 protected function _read($len) 1369 { 1370 if (($this->_pos + $len) > $this->_pickleLen) { 1371 // require_once 'Zend/Serializer/Exception.php'; 1372 throw new Zend_Serializer_Exception('End of data'); 1373 } 1374 1375 $this->_pos+= $len; 1376 return substr($this->_pickle, ($this->_pos - $len), $len); 1377 } 1378 1379 /** 1380 * Read a line of the pickle at once 1381 * 1382 * @return string 1383 * @throws Zend_Serializer_Exception if no EOL character found 1384 */ 1385 protected function _readline() 1386 { 1387 $eolLen = 2; 1388 $eolPos = strpos($this->_pickle, "\r\n", $this->_pos); 1389 if ($eolPos === false) { 1390 $eolPos = strpos($this->_pickle, "\n", $this->_pos); 1391 $eolLen = 1; 1392 } 1393 1394 if ($eolPos === false) { 1395 // require_once 'Zend/Serializer/Exception.php'; 1396 throw new Zend_Serializer_Exception('No new line found'); 1397 } 1398 $ret = substr($this->_pickle, $this->_pos, $eolPos-$this->_pos); 1399 $this->_pos = $eolPos + $eolLen; 1400 1401 return $ret; 1402 } 1403 1404 /** 1405 * Unquote/Unescape a quoted string 1406 * 1407 * @param string $str quoted string 1408 * @return string unquoted string 1409 */ 1410 protected function _unquoteString($str) 1411 { 1412 $quoteArr = array_flip(self::$_quoteString); 1413 1414 if ($str[0] == '"') { 1415 $quoteArr['\\"'] = '"'; 1416 } else { 1417 $quoteArr["\\'"] = "'"; 1418 } 1419 1420 return strtr(substr(trim($str), 1, -1), $quoteArr); 1421 } 1422 1423 /** 1424 * Return last marker position in stack 1425 * 1426 * @return int 1427 */ 1428 protected function _lastMarker() 1429 { 1430 for ($k = count($this->_stack)-1; $k >= 0; $k -= 1) { 1431 if ($this->_stack[$k] === $this->_marker) { 1432 break; 1433 } 1434 } 1435 return $k; 1436 } 1437 1438 /** 1439 * Decode a binary long sequence 1440 * 1441 * @param string $data 1442 * @return int|float|string 1443 */ 1444 protected function _decodeBinLong($data) 1445 { 1446 $nbytes = strlen($data); 1447 1448 if ($nbytes == 0) { 1449 return 0; 1450 } 1451 1452 $long = 0; 1453 1454 if ($nbytes > 7) { 1455 if (!extension_loaded('bcmath')) { 1456 return INF; 1457 } 1458 1459 for ($i=0; $i<$nbytes; $i++) { 1460 $long = bcadd($long, bcmul(ord($data[$i]), bcpow(256, $i, 0))); 1461 } 1462 if (0x80 <= ord($data[$nbytes-1])) { 1463 $long = bcsub($long, bcpow(2, $nbytes * 8)); 1464 } 1465 1466 } else { 1467 for ($i=0; $i<$nbytes; $i++) { 1468 $long+= ord($data[$i]) * pow(256, $i); 1469 } 1470 if (0x80 <= ord($data[$nbytes-1])) { 1471 $long-= pow(2, $nbytes * 8); 1472 // $long-= 1 << ($nbytes * 8); 1473 } 1474 } 1475 1476 return $long; 1477 } 1478 }