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