r1313@opsdev009 (orig r70232): dweatherford | 2007-11-15 19:29:47 -0800
[amiethrift.git] / lib / php / src / Thrift.php
blob10728a17fdf239bd7eade2e901a419ce290622da
1 <?php
3 /**
4 * Copyright (c) 2006- Facebook
5 * Distributed under the Thrift Software License
7 * See accompanying file LICENSE or visit the Thrift site at:
8 * http://developers.facebook.com/thrift/
10 * @package thrift
11 * @author Mark Slee <mcslee@facebook.com>
14 /**
15 * Data types that can be sent via Thrift
17 class TType {
18 const STOP = 0;
19 const VOID = 1;
20 const BOOL = 2;
21 const BYTE = 3;
22 const I08 = 3;
23 const DOUBLE = 4;
24 const I16 = 6;
25 const I32 = 8;
26 const I64 = 10;
27 const STRING = 11;
28 const UTF7 = 11;
29 const STRUCT = 12;
30 const MAP = 13;
31 const SET = 14;
32 const LST = 15; // N.B. cannot use LIST keyword in PHP!
33 const UTF8 = 16;
34 const UTF16 = 17;
37 /**
38 * Message types for RPC
40 class TMessageType {
41 const CALL = 1;
42 const REPLY = 2;
43 const EXCEPTION = 3;
46 /**
47 * NOTE(mcslee): This currently contains a ton of duplicated code from TBase
48 * because we need to save CPU cycles and this is not yet in an extension.
49 * Ideally we'd multiply-inherit TException from both Exception and Base, but
50 * that's not possible in PHP and there are no modules either, so for now we
51 * apologetically take a trip to HackTown.
53 * Can be called with standard Exception constructor (message, code) or with
54 * Thrift Base object constructor (spec, vals).
56 * @param mixed $p1 Message (string) or type-spec (array)
57 * @param mixed $p2 Code (integer) or values (array)
59 class TException extends Exception {
60 function __construct($p1=null, $p2=0) {
61 if (is_array($p1) && is_array($p2)) {
62 $spec = $p1;
63 $vals = $p2;
64 foreach ($spec as $fid => $fspec) {
65 $var = $fspec['var'];
66 if (isset($vals[$var])) {
67 $this->$var = $vals[$var];
70 } else {
71 parent::__construct($p1, $p2);
75 static $tmethod = array(TType::BOOL => 'Bool',
76 TType::BYTE => 'Byte',
77 TType::I16 => 'I16',
78 TType::I32 => 'I32',
79 TType::I64 => 'I64',
80 TType::DOUBLE => 'Double',
81 TType::STRING => 'String');
83 private function _readMap(&$var, $spec, $input) {
84 $xfer = 0;
85 $ktype = $spec['ktype'];
86 $vtype = $spec['vtype'];
87 $kread = $vread = null;
88 if (isset(TBase::$tmethod[$ktype])) {
89 $kread = 'read'.TBase::$tmethod[$ktype];
90 } else {
91 $kspec = $spec['key'];
93 if (isset(TBase::$tmethod[$vtype])) {
94 $vread = 'read'.TBase::$tmethod[$vtype];
95 } else {
96 $vspec = $spec['val'];
98 $var = array();
99 $_ktype = $_vtype = $size = 0;
100 $xfer += $input->readMapBegin($_ktype, $_vtype, $size);
101 for ($i = 0; $i < $size; ++$i) {
102 $key = $val = null;
103 if ($kread !== null) {
104 $xfer += $input->$kread($key);
105 } else {
106 switch ($ktype) {
107 case TType::STRUCT:
108 $class = $kspec['class'];
109 $key = new $class();
110 $xfer += $key->read($input);
111 break;
112 case TType::MAP:
113 $xfer += $this->_readMap($key, $kspec, $input);
114 break;
115 case TType::LST:
116 $xfer += $this->_readList($key, $kspec, $input, false);
117 break;
118 case TType::SET:
119 $xfer += $this->_readList($key, $kspec, $input, true);
120 break;
123 if ($vread !== null) {
124 $xfer += $input->$vread($val);
125 } else {
126 switch ($vtype) {
127 case TType::STRUCT:
128 $class = $vspec['class'];
129 $val = new $class();
130 $xfer += $val->read($input);
131 break;
132 case TType::MAP:
133 $xfer += $this->_readMap($val, $vspec, $input);
134 break;
135 case TType::LST:
136 $xfer += $this->_readList($val, $vspec, $input, false);
137 break;
138 case TType::SET:
139 $xfer += $this->_readList($val, $vspec, $input, true);
140 break;
143 $var[$key] = $val;
145 $xfer += $input->readMapEnd();
146 return $xfer;
149 private function _readList(&$var, $spec, $input, $set=false) {
150 $xfer = 0;
151 $etype = $spec['etype'];
152 $eread = $vread = null;
153 if (isset(TBase::$tmethod[$etype])) {
154 $eread = 'read'.TBase::$tmethod[$etype];
155 } else {
156 $espec = $spec['elem'];
158 $var = array();
159 $_etype = $size = 0;
160 if ($set) {
161 $xfer += $input->readSetBegin($_etype, $size);
162 } else {
163 $xfer += $input->readListBegin($_etype, $size);
165 for ($i = 0; $i < $size; ++$i) {
166 $elem = null;
167 if ($eread !== null) {
168 $xfer += $input->$eread($elem);
169 } else {
170 $espec = $spec['elem'];
171 switch ($etype) {
172 case TType::STRUCT:
173 $class = $espec['class'];
174 $elem = new $class();
175 $xfer += $elem->read($input);
176 break;
177 case TType::MAP:
178 $xfer += $this->_readMap($elem, $espec, $input);
179 break;
180 case TType::LST:
181 $xfer += $this->_readList($elem, $espec, $input, false);
182 break;
183 case TType::SET:
184 $xfer += $this->_readList($elem, $espec, $input, true);
185 break;
188 if ($set) {
189 $var[$elem] = true;
190 } else {
191 $var []= $elem;
194 if ($set) {
195 $xfer += $input->readSetEnd();
196 } else {
197 $xfer += $input->readListEnd();
199 return $xfer;
202 protected function _read($class, $spec, $input) {
203 $xfer = 0;
204 $fname = null;
205 $ftype = 0;
206 $fid = 0;
207 $xfer += $input->readStructBegin($fname);
208 $fast_binary = (bool)
209 is_a($input, 'TBinaryProtocolAccelerated') &&
210 function_exists('thrift_protocol_binary_deserialize');
212 while (true) {
213 $xfer += $input->readFieldBegin($fname, $ftype, $fid);
214 if ($ftype == TType::STOP) {
215 break;
217 if (isset($spec[$fid])) {
218 $fspec = $spec[$fid];
219 $var = $fspec['var'];
220 if ($ftype == $fspec['type']) {
221 $xfer = 0;
222 if ($fast_binary) {
223 $class = isset($fspec['class']) ? $fspec['class'] : '';
224 $this->$var = thrift_protocol_binary_deserialize($ftype, $input, $class);
225 } else {
226 if (isset(TBase::$tmethod[$ftype])) {
227 $func = 'read'.TBase::$tmethod[$ftype];
228 $xfer += $input->$func($this->$var);
229 } else {
230 switch ($ftype) {
231 case TType::STRUCT:
232 $class = $fspec['class'];
233 $this->$var = new $class();
234 $xfer += $this->$var->read($input);
235 break;
236 case TType::MAP:
237 $xfer += $this->_readMap($this->$var, $fspec, $input);
238 break;
239 case TType::LST:
240 $xfer += $this->_readList($this->$var, $fspec, $input, false);
241 break;
242 case TType::SET:
243 $xfer += $this->_readList($this->$var, $fspec, $input, true);
244 break;
248 } else {
249 $xfer += $input->skip($ftype);
251 } else {
252 $xfer += $input->skip($ftype);
254 $xfer += $input->readFieldEnd();
256 $xfer += $input->readStructEnd();
257 return $xfer;
260 private function _writeMap($var, $spec, $output) {
261 $xfer = 0;
262 $ktype = $spec['ktype'];
263 $vtype = $spec['vtype'];
264 $kwrite = $vwrite = null;
265 if (isset(TBase::$tmethod[$ktype])) {
266 $kwrite = 'write'.TBase::$tmethod[$ktype];
267 } else {
268 $kspec = $spec['key'];
270 if (isset(TBase::$tmethod[$vtype])) {
271 $vwrite = 'write'.TBase::$tmethod[$vtype];
272 } else {
273 $vspec = $spec['val'];
275 $xfer += $output->writeMapBegin($ktype, $vtype, count($var));
276 foreach ($var as $key => $val) {
277 if (isset($kwrite)) {
278 $xfer += $output->$kwrite($key);
279 } else {
280 switch ($ktype) {
281 case TType::STRUCT:
282 $xfer += $key->write($output);
283 break;
284 case TType::MAP:
285 $xfer += $this->_writeMap($key, $kspec, $output);
286 break;
287 case TType::LST:
288 $xfer += $this->_writeList($key, $kspec, $output, false);
289 break;
290 case TType::SET:
291 $xfer += $this->_writeList($key, $kspec, $output, true);
292 break;
295 if (isset($vwrite)) {
296 $xfer += $output->$vwrite($val);
297 } else {
298 switch ($vtype) {
299 case TType::STRUCT:
300 $xfer += $val->write($output);
301 break;
302 case TType::MAP:
303 $xfer += $this->_writeMap($val, $vspec, $output);
304 break;
305 case TType::LST:
306 $xfer += $this->_writeList($val, $vspec, $output, false);
307 break;
308 case TType::SET:
309 $xfer += $this->_writeList($val, $vspec, $output, true);
310 break;
314 $xfer += $output->writeMapEnd();
315 return $xfer;
318 private function _writeList($var, $spec, $output, $set=false) {
319 $xfer = 0;
320 $etype = $spec['etype'];
321 $ewrite = null;
322 if (isset(TBase::$tmethod[$etype])) {
323 $ewrite = 'write'.TBase::$tmethod[$etype];
324 } else {
325 $espec = $spec['elem'];
327 if ($set) {
328 $xfer += $output->writeSetBegin($etype, count($var));
329 } else {
330 $xfer += $output->writeListBegin($etype, count($var));
332 foreach ($var as $key => $val) {
333 $elem = $set ? $key : $val;
334 if (isset($ewrite)) {
335 $xfer += $output->$ewrite($elem);
336 } else {
337 switch ($etype) {
338 case TType::STRUCT:
339 $xfer += $elem->write($output);
340 break;
341 case TType::MAP:
342 $xfer += $this->_writeMap($elem, $espec, $output);
343 break;
344 case TType::LST:
345 $xfer += $this->_writeList($elem, $espec, $output, false);
346 break;
347 case TType::SET:
348 $xfer += $this->_writeList($elem, $espec, $output, true);
349 break;
353 if ($set) {
354 $xfer += $output->writeSetEnd();
355 } else {
356 $xfer += $output->writeListEnd();
358 return $xfer;
361 protected function _write($class, $spec, $output) {
362 $xfer = 0;
363 $xfer += $output->writeStructBegin($class);
364 foreach ($spec as $fid => $fspec) {
365 $var = $fspec['var'];
366 if ($this->$var !== null) {
367 $ftype = $fspec['type'];
368 $xfer += $output->writeFieldBegin($var, $ftype, $fid);
369 if (isset(TBase::$tmethod[$ftype])) {
370 $func = 'write'.TBase::$tmethod[$ftype];
371 $xfer += $output->$func($this->$var);
372 } else {
373 switch ($ftype) {
374 case TType::STRUCT:
375 $xfer += $this->$var->write($output);
376 break;
377 case TType::MAP:
378 $xfer += $this->_writeMap($this->$var, $fspec, $output);
379 break;
380 case TType::LST:
381 $xfer += $this->_writeList($this->$var, $fspec, $output, false);
382 break;
383 case TType::SET:
384 $xfer += $this->_writeList($this->$var, $fspec, $output, true);
385 break;
388 $xfer += $output->writeFieldEnd();
391 $xfer += $output->writeFieldStop();
392 $xfer += $output->writeStructEnd();
393 return $xfer;
399 * Base class from which other Thrift structs extend. This is so that we can
400 * cut back on the size of the generated code which is turning out to have a
401 * nontrivial cost just to load thanks to the wondrously abysmal implementation
402 * of PHP. Note that code is intentionally duplicated in here to avoid making
403 * function calls for every field or member of a container..
405 abstract class TBase {
407 static $tmethod = array(TType::BOOL => 'Bool',
408 TType::BYTE => 'Byte',
409 TType::I16 => 'I16',
410 TType::I32 => 'I32',
411 TType::I64 => 'I64',
412 TType::DOUBLE => 'Double',
413 TType::STRING => 'String');
415 abstract function read($input);
417 abstract function write($output);
419 public function __construct($spec=null, $vals=null) {
420 if (is_array($spec) && is_array($vals)) {
421 foreach ($spec as $fid => $fspec) {
422 $var = $fspec['var'];
423 if (isset($vals[$var])) {
424 $this->$var = $vals[$var];
430 private function _readMap(&$var, $spec, $input) {
431 $xfer = 0;
432 $ktype = $spec['ktype'];
433 $vtype = $spec['vtype'];
434 $kread = $vread = null;
435 if (isset(TBase::$tmethod[$ktype])) {
436 $kread = 'read'.TBase::$tmethod[$ktype];
437 } else {
438 $kspec = $spec['key'];
440 if (isset(TBase::$tmethod[$vtype])) {
441 $vread = 'read'.TBase::$tmethod[$vtype];
442 } else {
443 $vspec = $spec['val'];
445 $var = array();
446 $_ktype = $_vtype = $size = 0;
447 $xfer += $input->readMapBegin($_ktype, $_vtype, $size);
448 for ($i = 0; $i < $size; ++$i) {
449 $key = $val = null;
450 if ($kread !== null) {
451 $xfer += $input->$kread($key);
452 } else {
453 switch ($ktype) {
454 case TType::STRUCT:
455 $class = $kspec['class'];
456 $key = new $class();
457 $xfer += $key->read($input);
458 break;
459 case TType::MAP:
460 $xfer += $this->_readMap($elem, $kspec, $input);
461 break;
462 case TType::LST:
463 $xfer += $this->_readList($elem, $kspec, $input, false);
464 break;
465 case TType::SET:
466 $xfer += $this->_readList($elem, $kspec, $input, true);
467 break;
470 if ($vread !== null) {
471 $xfer += $input->$vread($val);
472 } else {
473 switch ($vtype) {
474 case TType::STRUCT:
475 $class = $vspec['class'];
476 $val = new $class();
477 $xfer += $val->read($input);
478 break;
479 case TType::MAP:
480 $xfer += $this->_readMap($val, $vspec, $input);
481 break;
482 case TType::LST:
483 $xfer += $this->_readList($val, $vspec, $input, false);
484 break;
485 case TType::SET:
486 $xfer += $this->_readList($val, $vspec, $input, true);
487 break;
490 $var[$key] = $val;
492 $xfer += $input->readMapEnd();
493 return $xfer;
496 private function _readList(&$var, $spec, $input, $set=false) {
497 $xfer = 0;
498 $etype = $spec['etype'];
499 $eread = $vread = null;
500 if (isset(TBase::$tmethod[$etype])) {
501 $eread = 'read'.TBase::$tmethod[$etype];
502 } else {
503 $espec = $spec['elem'];
505 $var = array();
506 $_etype = $size = 0;
507 if ($set) {
508 $xfer += $input->readSetBegin($_etype, $size);
509 } else {
510 $xfer += $input->readListBegin($_etype, $size);
512 for ($i = 0; $i < $size; ++$i) {
513 $elem = null;
514 if ($eread !== null) {
515 $xfer += $input->$eread($elem);
516 } else {
517 $espec = $spec['elem'];
518 switch ($etype) {
519 case TType::STRUCT:
520 $class = $espec['class'];
521 $elem = new $class();
522 $xfer += $elem->read($input);
523 break;
524 case TType::MAP:
525 $xfer += $this->_readMap($key, $espec, $input);
526 break;
527 case TType::LST:
528 $xfer += $this->_readList($key, $espec, $input, false);
529 break;
530 case TType::SET:
531 $xfer += $this->_readList($key, $espec, $input, true);
532 break;
535 if ($set) {
536 $var[$elem] = true;
537 } else {
538 $var []= $elem;
541 if ($set) {
542 $xfer += $input->readSetEnd();
543 } else {
544 $xfer += $input->readListEnd();
546 return $xfer;
549 protected function _read($class, $spec, $input) {
550 $xfer = 0;
551 $fname = null;
552 $ftype = 0;
553 $fid = 0;
554 $xfer += $input->readStructBegin($fname);
555 $fast_binary = (bool)
556 is_a($input, 'TBinaryProtocolAccelerated') &&
557 function_exists('thrift_protocol_binary_deserialize');
559 while (true) {
560 $xfer += $input->readFieldBegin($fname, $ftype, $fid);
561 if ($ftype == TType::STOP) {
562 break;
564 if (isset($spec[$fid])) {
565 $fspec = $spec[$fid];
566 $var = $fspec['var'];
567 if ($ftype == $fspec['type']) {
568 $xfer = 0;
569 if ($fast_binary) {
570 $class = isset($fspec['class']) ? $fspec['class'] : '';
571 $this->$var = thrift_protocol_binary_deserialize($ftype, $input, $class);
572 } else {
573 if (isset(TBase::$tmethod[$ftype])) {
574 $func = 'read'.TBase::$tmethod[$ftype];
575 $xfer += $input->$func($this->$var);
576 } else {
577 switch ($ftype) {
578 case TType::STRUCT:
579 $class = $fspec['class'];
580 $this->$var = new $class();
581 $xfer += $this->$var->read($input);
582 break;
583 case TType::MAP:
584 $xfer += $this->_readMap($this->$var, $fspec, $input);
585 break;
586 case TType::LST:
587 $xfer += $this->_readList($this->$var, $fspec, $input, false);
588 break;
589 case TType::SET:
590 $xfer += $this->_readList($this->$var, $fspec, $input, true);
591 break;
595 } else {
596 $xfer += $input->skip($ftype);
598 } else {
599 $xfer += $input->skip($ftype);
601 $xfer += $input->readFieldEnd();
603 $xfer += $input->readStructEnd();
604 return $xfer;
607 private function _writeMap($var, $spec, $output) {
608 $xfer = 0;
609 $ktype = $spec['ktype'];
610 $vtype = $spec['vtype'];
611 $kwrite = $vwrite = null;
612 if (isset(TBase::$tmethod[$ktype])) {
613 $kwrite = 'write'.TBase::$tmethod[$ktype];
614 } else {
615 $kspec = $spec['key'];
617 if (isset(TBase::$tmethod[$vtype])) {
618 $vwrite = 'write'.TBase::$tmethod[$vtype];
619 } else {
620 $vspec = $spec['val'];
622 $xfer += $output->writeMapBegin($ktype, $vtype, count($var));
623 foreach ($var as $key => $val) {
624 if (isset($kwrite)) {
625 $xfer += $output->$kwrite($key);
626 } else {
627 switch ($ktype) {
628 case TType::STRUCT:
629 $xfer += $key->write($output);
630 break;
631 case TType::MAP:
632 $xfer += $this->_writeMap($key, $kspec, $output);
633 break;
634 case TType::LST:
635 $xfer += $this->_writeList($key, $kspec, $output, false);
636 break;
637 case TType::SET:
638 $xfer += $this->_writeList($key, $kspec, $output, true);
639 break;
642 if (isset($vwrite)) {
643 $xfer += $output->$vwrite($val);
644 } else {
645 switch ($vtype) {
646 case TType::STRUCT:
647 $xfer += $val->write($output);
648 break;
649 case TType::MAP:
650 $xfer += $this->_writeMap($val, $vspec, $output);
651 break;
652 case TType::LST:
653 $xfer += $this->_writeList($val, $vspec, $output, false);
654 break;
655 case TType::SET:
656 $xfer += $this->_writeList($val, $vspec, $output, true);
657 break;
661 $xfer += $output->writeMapEnd();
662 return $xfer;
665 private function _writeList($var, $spec, $output, $set=false) {
666 $xfer = 0;
667 $etype = $spec['etype'];
668 $ewrite = null;
669 if (isset(TBase::$tmethod[$etype])) {
670 $ewrite = 'write'.TBase::$tmethod[$etype];
671 } else {
672 $espec = $spec['elem'];
674 if ($set) {
675 $xfer += $output->writeSetBegin($etype, count($var));
676 } else {
677 $xfer += $output->writeListBegin($etype, count($var));
679 foreach ($var as $key => $val) {
680 $elem = $set ? $key : $val;
681 if (isset($ewrite)) {
682 $xfer += $output->$ewrite($elem);
683 } else {
684 switch ($etype) {
685 case TType::STRUCT:
686 $xfer += $elem->write($output);
687 break;
688 case TType::MAP:
689 $xfer += $this->_writeMap($elem, $espec, $output);
690 break;
691 case TType::LST:
692 $xfer += $this->_writeList($elem, $espec, $output, false);
693 break;
694 case TType::SET:
695 $xfer += $this->_writeList($elem, $espec, $output, true);
696 break;
700 if ($set) {
701 $xfer += $output->writeSetEnd();
702 } else {
703 $xfer += $output->writeListEnd();
705 return $xfer;
708 protected function _write($class, $spec, $output) {
709 $xfer = 0;
710 $xfer += $output->writeStructBegin($class);
711 foreach ($spec as $fid => $fspec) {
712 $var = $fspec['var'];
713 if ($this->$var !== null) {
714 $ftype = $fspec['type'];
715 $xfer += $output->writeFieldBegin($var, $ftype, $fid);
716 if (isset(TBase::$tmethod[$ftype])) {
717 $func = 'write'.TBase::$tmethod[$ftype];
718 $xfer += $output->$func($this->$var);
719 } else {
720 switch ($ftype) {
721 case TType::STRUCT:
722 $xfer += $this->$var->write($output);
723 break;
724 case TType::MAP:
725 $xfer += $this->_writeMap($this->$var, $fspec, $output);
726 break;
727 case TType::LST:
728 $xfer += $this->_writeList($this->$var, $fspec, $output, false);
729 break;
730 case TType::SET:
731 $xfer += $this->_writeList($this->$var, $fspec, $output, true);
732 break;
735 $xfer += $output->writeFieldEnd();
738 $xfer += $output->writeFieldStop();
739 $xfer += $output->writeStructEnd();
740 return $xfer;
744 class TApplicationException extends TException {
745 static $_TSPEC =
746 array(1 => array('var' => 'message',
747 'type' => TType::STRING),
748 2 => array('var' => 'code',
749 'type' => TType::I32));
751 const UNKNOWN = 0;
752 const UNKNOWN_METHOD = 1;
753 const INVALID_MESSAGE_TYPE = 2;
754 const WRONG_METHOD_NAME = 3;
755 const BAD_SEQUENCE_ID = 4;
756 const MISSING_RESULT = 5;
758 function __construct($message=null, $code=0) {
759 parent::__construct($message, $code);
762 public function read($output) {
763 return $this->_read('TApplicationException', self::$_TSPEC, $output);
766 public function write($output) {
767 $xfer = 0;
768 $xfer += $output->writeStructBegin('TApplicationException');
769 if ($message = $this->getMessage()) {
770 $xfer += $output->writeFieldBegin('message', TType::STRING, 1);
771 $xfer += $output->writeString($message);
772 $xfer += $output->writeFieldEnd();
774 if ($code = $this->getCode()) {
775 $xfer += $output->writeFieldBegin('type', TType::I32, 2);
776 $xfer += $output->writeI32($code);
777 $xfer += $output->writeFieldEnd();
779 $xfer += $output->writeFieldStop();
780 $xfer += $output->writeStructEnd();
781 return $xfer;
786 * Set global THRIFT ROOT automatically via inclusion here
788 if (!isset($GLOBALS['THRIFT_ROOT'])) {
789 $GLOBALS['THRIFT_ROOT'] = dirname(__FILE__);
791 include_once $GLOBALS['THRIFT_ROOT'].'/protocol/TProtocol.php';
792 include_once $GLOBALS['THRIFT_ROOT'].'/transport/TTransport.php';