]> git.proxmox.com Git - ceph.git/blob - ceph/src/jaegertracing/thrift/lib/php/lib/Protocol/TJSONProtocol.php
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / jaegertracing / thrift / lib / php / lib / Protocol / TJSONProtocol.php
1 <?php
2
3 /*
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 *
21 * @package thrift.protocol
22 */
23
24 namespace Thrift\Protocol;
25
26 use Thrift\Type\TType;
27 use Thrift\Exception\TProtocolException;
28 use Thrift\Protocol\JSON\BaseContext;
29 use Thrift\Protocol\JSON\LookaheadReader;
30 use Thrift\Protocol\JSON\PairContext;
31 use Thrift\Protocol\JSON\ListContext;
32
33 /**
34 * JSON implementation of thrift protocol, ported from Java.
35 */
36 class TJSONProtocol extends TProtocol
37 {
38 const COMMA = ',';
39 const COLON = ':';
40 const LBRACE = '{';
41 const RBRACE = '}';
42 const LBRACKET = '[';
43 const RBRACKET = ']';
44 const QUOTE = '"';
45 const BACKSLASH = '\\';
46 const ZERO = '0';
47 const ESCSEQ = '\\';
48 const DOUBLEESC = '__DOUBLE_ESCAPE_SEQUENCE__';
49
50 const VERSION = 1;
51
52 public static $JSON_CHAR_TABLE = array(
53 /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
54 0, 0, 0, 0, 0, 0, 0, 0, 'b', 't', 'n', 0, 'f', 'r', 0, 0, // 0
55 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1
56 1, 1, '"', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2
57 );
58
59 public static $ESCAPE_CHARS = array('"', '\\', '/', "b", "f", "n", "r", "t");
60
61 public static $ESCAPE_CHAR_VALS = array(
62 '"', '\\', '/', "\x08", "\f", "\n", "\r", "\t",
63 );
64
65 const NAME_BOOL = "tf";
66 const NAME_BYTE = "i8";
67 const NAME_I16 = "i16";
68 const NAME_I32 = "i32";
69 const NAME_I64 = "i64";
70 const NAME_DOUBLE = "dbl";
71 const NAME_STRUCT = "rec";
72 const NAME_STRING = "str";
73 const NAME_MAP = "map";
74 const NAME_LIST = "lst";
75 const NAME_SET = "set";
76
77 private function getTypeNameForTypeID($typeID)
78 {
79 switch ($typeID) {
80 case TType::BOOL:
81 return self::NAME_BOOL;
82 case TType::BYTE:
83 return self::NAME_BYTE;
84 case TType::I16:
85 return self::NAME_I16;
86 case TType::I32:
87 return self::NAME_I32;
88 case TType::I64:
89 return self::NAME_I64;
90 case TType::DOUBLE:
91 return self::NAME_DOUBLE;
92 case TType::STRING:
93 return self::NAME_STRING;
94 case TType::STRUCT:
95 return self::NAME_STRUCT;
96 case TType::MAP:
97 return self::NAME_MAP;
98 case TType::SET:
99 return self::NAME_SET;
100 case TType::LST:
101 return self::NAME_LIST;
102 default:
103 throw new TProtocolException("Unrecognized type", TProtocolException::UNKNOWN);
104 }
105 }
106
107 private function getTypeIDForTypeName($name)
108 {
109 $result = TType::STOP;
110
111 if (strlen($name) > 1) {
112 switch (substr($name, 0, 1)) {
113 case 'd':
114 $result = TType::DOUBLE;
115 break;
116 case 'i':
117 switch (substr($name, 1, 1)) {
118 case '8':
119 $result = TType::BYTE;
120 break;
121 case '1':
122 $result = TType::I16;
123 break;
124 case '3':
125 $result = TType::I32;
126 break;
127 case '6':
128 $result = TType::I64;
129 break;
130 }
131 break;
132 case 'l':
133 $result = TType::LST;
134 break;
135 case 'm':
136 $result = TType::MAP;
137 break;
138 case 'r':
139 $result = TType::STRUCT;
140 break;
141 case 's':
142 if (substr($name, 1, 1) == 't') {
143 $result = TType::STRING;
144 } elseif (substr($name, 1, 1) == 'e') {
145 $result = TType::SET;
146 }
147 break;
148 case 't':
149 $result = TType::BOOL;
150 break;
151 }
152 }
153 if ($result == TType::STOP) {
154 throw new TProtocolException("Unrecognized type", TProtocolException::INVALID_DATA);
155 }
156
157 return $result;
158 }
159
160 public $contextStack_ = array();
161 public $context_;
162 public $reader_;
163
164 private function pushContext($c)
165 {
166 array_push($this->contextStack_, $this->context_);
167 $this->context_ = $c;
168 }
169
170 private function popContext()
171 {
172 $this->context_ = array_pop($this->contextStack_);
173 }
174
175 public function __construct($trans)
176 {
177 parent::__construct($trans);
178 $this->context_ = new BaseContext();
179 $this->reader_ = new LookaheadReader($this);
180 }
181
182 public function reset()
183 {
184 $this->contextStack_ = array();
185 $this->context_ = new BaseContext();
186 $this->reader_ = new LookaheadReader($this);
187 }
188
189 private $tmpbuf_ = array(4);
190
191 public function readJSONSyntaxChar($b)
192 {
193 $ch = $this->reader_->read();
194
195 if (substr($ch, 0, 1) != $b) {
196 throw new TProtocolException("Unexpected character: " . $ch, TProtocolException::INVALID_DATA);
197 }
198 }
199
200 private function hexVal($s)
201 {
202 for ($i = 0; $i < strlen($s); $i++) {
203 $ch = substr($s, $i, 1);
204
205 if (!($ch >= "a" && $ch <= "f") && !($ch >= "0" && $ch <= "9")) {
206 throw new TProtocolException("Expected hex character " . $ch, TProtocolException::INVALID_DATA);
207 }
208 }
209
210 return hexdec($s);
211 }
212
213 private function hexChar($val)
214 {
215 return dechex($val);
216 }
217
218 private function hasJSONUnescapedUnicode()
219 {
220 if (PHP_MAJOR_VERSION > 5 || (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4)) {
221 return true;
222 }
223
224 return false;
225 }
226
227 private function unescapedUnicode($str)
228 {
229 if ($this->hasJSONUnescapedUnicode()) {
230 return json_encode($str, JSON_UNESCAPED_UNICODE);
231 }
232
233 $json = json_encode($str);
234
235 /*
236 * Unescaped character outside the Basic Multilingual Plane
237 * High surrogate: 0xD800 - 0xDBFF
238 * Low surrogate: 0xDC00 - 0xDFFF
239 */
240 $json = preg_replace_callback(
241 '/\\\\u(d[89ab][0-9a-f]{2})\\\\u(d[cdef][0-9a-f]{2})/i',
242 function ($matches) {
243 return mb_convert_encoding(pack('H*', $matches[1] . $matches[2]), 'UTF-8', 'UTF-16BE');
244 },
245 $json
246 );
247
248 /*
249 * Unescaped characters within the Basic Multilingual Plane
250 */
251 $json = preg_replace_callback(
252 '/\\\\u([0-9a-f]{4})/i',
253 function ($matches) {
254 return mb_convert_encoding(pack('H*', $matches[1]), 'UTF-8', 'UTF-16BE');
255 },
256 $json
257 );
258
259 return $json;
260 }
261
262 private function writeJSONString($b)
263 {
264 $this->context_->write();
265
266 if (is_numeric($b) && $this->context_->escapeNum()) {
267 $this->trans_->write(self::QUOTE);
268 }
269
270 $this->trans_->write($this->unescapedUnicode($b));
271
272 if (is_numeric($b) && $this->context_->escapeNum()) {
273 $this->trans_->write(self::QUOTE);
274 }
275 }
276
277 private function writeJSONInteger($num)
278 {
279 $this->context_->write();
280
281 if ($this->context_->escapeNum()) {
282 $this->trans_->write(self::QUOTE);
283 }
284
285 $this->trans_->write($num);
286
287 if ($this->context_->escapeNum()) {
288 $this->trans_->write(self::QUOTE);
289 }
290 }
291
292 private function writeJSONDouble($num)
293 {
294 $this->context_->write();
295
296 if ($this->context_->escapeNum()) {
297 $this->trans_->write(self::QUOTE);
298 }
299
300 $this->trans_->write(json_encode($num));
301
302 if ($this->context_->escapeNum()) {
303 $this->trans_->write(self::QUOTE);
304 }
305 }
306
307 private function writeJSONBase64($data)
308 {
309 $this->context_->write();
310 $this->trans_->write(self::QUOTE);
311 $this->trans_->write(json_encode(base64_encode($data)));
312 $this->trans_->write(self::QUOTE);
313 }
314
315 private function writeJSONObjectStart()
316 {
317 $this->context_->write();
318 $this->trans_->write(self::LBRACE);
319 $this->pushContext(new PairContext($this));
320 }
321
322 private function writeJSONObjectEnd()
323 {
324 $this->popContext();
325 $this->trans_->write(self::RBRACE);
326 }
327
328 private function writeJSONArrayStart()
329 {
330 $this->context_->write();
331 $this->trans_->write(self::LBRACKET);
332 $this->pushContext(new ListContext($this));
333 }
334
335 private function writeJSONArrayEnd()
336 {
337 $this->popContext();
338 $this->trans_->write(self::RBRACKET);
339 }
340
341 private function readJSONString($skipContext)
342 {
343 if (!$skipContext) {
344 $this->context_->read();
345 }
346
347 $jsonString = '';
348 $lastChar = null;
349 while (true) {
350 $ch = $this->reader_->read();
351 $jsonString .= $ch;
352 if ($ch == self::QUOTE &&
353 $lastChar !== null &&
354 $lastChar !== self::ESCSEQ) {
355 break;
356 }
357 if ($ch == self::ESCSEQ && $lastChar == self::ESCSEQ) {
358 $lastChar = self::DOUBLEESC;
359 } else {
360 $lastChar = $ch;
361 }
362 }
363
364 return json_decode($jsonString);
365 }
366
367 private function isJSONNumeric($b)
368 {
369 switch ($b) {
370 case '+':
371 case '-':
372 case '.':
373 case '0':
374 case '1':
375 case '2':
376 case '3':
377 case '4':
378 case '5':
379 case '6':
380 case '7':
381 case '8':
382 case '9':
383 case 'E':
384 case 'e':
385 return true;
386 }
387
388 return false;
389 }
390
391 private function readJSONNumericChars()
392 {
393 $strbld = array();
394
395 while (true) {
396 $ch = $this->reader_->peek();
397
398 if (!$this->isJSONNumeric($ch)) {
399 break;
400 }
401
402 $strbld[] = $this->reader_->read();
403 }
404
405 return implode("", $strbld);
406 }
407
408 private function readJSONInteger()
409 {
410 $this->context_->read();
411
412 if ($this->context_->escapeNum()) {
413 $this->readJSONSyntaxChar(self::QUOTE);
414 }
415
416 $str = $this->readJSONNumericChars();
417
418 if ($this->context_->escapeNum()) {
419 $this->readJSONSyntaxChar(self::QUOTE);
420 }
421
422 if (!is_numeric($str)) {
423 throw new TProtocolException("Invalid data in numeric: " . $str, TProtocolException::INVALID_DATA);
424 }
425
426 return intval($str);
427 }
428
429 /**
430 * Identical to readJSONInteger but without the final cast.
431 * Needed for proper handling of i64 on 32 bit machines. Why a
432 * separate function? So we don't have to force the rest of the
433 * use cases through the extra conditional.
434 */
435 private function readJSONIntegerAsString()
436 {
437 $this->context_->read();
438
439 if ($this->context_->escapeNum()) {
440 $this->readJSONSyntaxChar(self::QUOTE);
441 }
442
443 $str = $this->readJSONNumericChars();
444
445 if ($this->context_->escapeNum()) {
446 $this->readJSONSyntaxChar(self::QUOTE);
447 }
448
449 if (!is_numeric($str)) {
450 throw new TProtocolException("Invalid data in numeric: " . $str, TProtocolException::INVALID_DATA);
451 }
452
453 return $str;
454 }
455
456 private function readJSONDouble()
457 {
458 $this->context_->read();
459
460 if (substr($this->reader_->peek(), 0, 1) == self::QUOTE) {
461 $arr = $this->readJSONString(true);
462
463 if ($arr == "NaN") {
464 return NAN;
465 } elseif ($arr == "Infinity") {
466 return INF;
467 } elseif (!$this->context_->escapeNum()) {
468 throw new TProtocolException(
469 "Numeric data unexpectedly quoted " . $arr,
470 TProtocolException::INVALID_DATA
471 );
472 }
473
474 return floatval($arr);
475 } else {
476 if ($this->context_->escapeNum()) {
477 $this->readJSONSyntaxChar(self::QUOTE);
478 }
479
480 return floatval($this->readJSONNumericChars());
481 }
482 }
483
484 private function readJSONBase64()
485 {
486 $arr = $this->readJSONString(false);
487 $data = base64_decode($arr, true);
488
489 if ($data === false) {
490 throw new TProtocolException("Invalid base64 data " . $arr, TProtocolException::INVALID_DATA);
491 }
492
493 return $data;
494 }
495
496 private function readJSONObjectStart()
497 {
498 $this->context_->read();
499 $this->readJSONSyntaxChar(self::LBRACE);
500 $this->pushContext(new PairContext($this));
501 }
502
503 private function readJSONObjectEnd()
504 {
505 $this->readJSONSyntaxChar(self::RBRACE);
506 $this->popContext();
507 }
508
509 private function readJSONArrayStart()
510 {
511 $this->context_->read();
512 $this->readJSONSyntaxChar(self::LBRACKET);
513 $this->pushContext(new ListContext($this));
514 }
515
516 private function readJSONArrayEnd()
517 {
518 $this->readJSONSyntaxChar(self::RBRACKET);
519 $this->popContext();
520 }
521
522 /**
523 * Writes the message header
524 *
525 * @param string $name Function name
526 * @param int $type message type TMessageType::CALL or TMessageType::REPLY
527 * @param int $seqid The sequence id of this message
528 */
529 public function writeMessageBegin($name, $type, $seqid)
530 {
531 $this->writeJSONArrayStart();
532 $this->writeJSONInteger(self::VERSION);
533 $this->writeJSONString($name);
534 $this->writeJSONInteger($type);
535 $this->writeJSONInteger($seqid);
536 }
537
538 /**
539 * Close the message
540 */
541 public function writeMessageEnd()
542 {
543 $this->writeJSONArrayEnd();
544 }
545
546 /**
547 * Writes a struct header.
548 *
549 * @param string $name Struct name
550 * @throws TException on write error
551 * @return int How many bytes written
552 */
553 public function writeStructBegin($name)
554 {
555 $this->writeJSONObjectStart();
556 }
557
558 /**
559 * Close a struct.
560 *
561 * @throws TException on write error
562 * @return int How many bytes written
563 */
564 public function writeStructEnd()
565 {
566 $this->writeJSONObjectEnd();
567 }
568
569 public function writeFieldBegin($fieldName, $fieldType, $fieldId)
570 {
571 $this->writeJSONInteger($fieldId);
572 $this->writeJSONObjectStart();
573 $this->writeJSONString($this->getTypeNameForTypeID($fieldType));
574 }
575
576 public function writeFieldEnd()
577 {
578 $this->writeJsonObjectEnd();
579 }
580
581 public function writeFieldStop()
582 {
583 }
584
585 public function writeMapBegin($keyType, $valType, $size)
586 {
587 $this->writeJSONArrayStart();
588 $this->writeJSONString($this->getTypeNameForTypeID($keyType));
589 $this->writeJSONString($this->getTypeNameForTypeID($valType));
590 $this->writeJSONInteger($size);
591 $this->writeJSONObjectStart();
592 }
593
594 public function writeMapEnd()
595 {
596 $this->writeJSONObjectEnd();
597 $this->writeJSONArrayEnd();
598 }
599
600 public function writeListBegin($elemType, $size)
601 {
602 $this->writeJSONArrayStart();
603 $this->writeJSONString($this->getTypeNameForTypeID($elemType));
604 $this->writeJSONInteger($size);
605 }
606
607 public function writeListEnd()
608 {
609 $this->writeJSONArrayEnd();
610 }
611
612 public function writeSetBegin($elemType, $size)
613 {
614 $this->writeJSONArrayStart();
615 $this->writeJSONString($this->getTypeNameForTypeID($elemType));
616 $this->writeJSONInteger($size);
617 }
618
619 public function writeSetEnd()
620 {
621 $this->writeJSONArrayEnd();
622 }
623
624 public function writeBool($bool)
625 {
626 $this->writeJSONInteger($bool ? 1 : 0);
627 }
628
629 public function writeByte($byte)
630 {
631 $this->writeJSONInteger($byte);
632 }
633
634 public function writeI16($i16)
635 {
636 $this->writeJSONInteger($i16);
637 }
638
639 public function writeI32($i32)
640 {
641 $this->writeJSONInteger($i32);
642 }
643
644 public function writeI64($i64)
645 {
646 $this->writeJSONInteger($i64);
647 }
648
649 public function writeDouble($dub)
650 {
651 $this->writeJSONDouble($dub);
652 }
653
654 public function writeString($str)
655 {
656 $this->writeJSONString($str);
657 }
658
659 /**
660 * Reads the message header
661 *
662 * @param string $name Function name
663 * @param int $type message type TMessageType::CALL or TMessageType::REPLY
664 * @parem int $seqid The sequence id of this message
665 */
666 public function readMessageBegin(&$name, &$type, &$seqid)
667 {
668 $this->readJSONArrayStart();
669
670 if ($this->readJSONInteger() != self::VERSION) {
671 throw new TProtocolException("Message contained bad version", TProtocolException::BAD_VERSION);
672 }
673
674 $name = $this->readJSONString(false);
675 $type = $this->readJSONInteger();
676 $seqid = $this->readJSONInteger();
677
678 return true;
679 }
680
681 /**
682 * Read the close of message
683 */
684 public function readMessageEnd()
685 {
686 $this->readJSONArrayEnd();
687 }
688
689 public function readStructBegin(&$name)
690 {
691 $this->readJSONObjectStart();
692
693 return 0;
694 }
695
696 public function readStructEnd()
697 {
698 $this->readJSONObjectEnd();
699 }
700
701 public function readFieldBegin(&$name, &$fieldType, &$fieldId)
702 {
703 $ch = $this->reader_->peek();
704 $name = "";
705
706 if (substr($ch, 0, 1) == self::RBRACE) {
707 $fieldType = TType::STOP;
708 } else {
709 $fieldId = $this->readJSONInteger();
710 $this->readJSONObjectStart();
711 $fieldType = $this->getTypeIDForTypeName($this->readJSONString(false));
712 }
713 }
714
715 public function readFieldEnd()
716 {
717 $this->readJSONObjectEnd();
718 }
719
720 public function readMapBegin(&$keyType, &$valType, &$size)
721 {
722 $this->readJSONArrayStart();
723 $keyType = $this->getTypeIDForTypeName($this->readJSONString(false));
724 $valType = $this->getTypeIDForTypeName($this->readJSONString(false));
725 $size = $this->readJSONInteger();
726 $this->readJSONObjectStart();
727 }
728
729 public function readMapEnd()
730 {
731 $this->readJSONObjectEnd();
732 $this->readJSONArrayEnd();
733 }
734
735 public function readListBegin(&$elemType, &$size)
736 {
737 $this->readJSONArrayStart();
738 $elemType = $this->getTypeIDForTypeName($this->readJSONString(false));
739 $size = $this->readJSONInteger();
740
741 return true;
742 }
743
744 public function readListEnd()
745 {
746 $this->readJSONArrayEnd();
747 }
748
749 public function readSetBegin(&$elemType, &$size)
750 {
751 $this->readJSONArrayStart();
752 $elemType = $this->getTypeIDForTypeName($this->readJSONString(false));
753 $size = $this->readJSONInteger();
754
755 return true;
756 }
757
758 public function readSetEnd()
759 {
760 $this->readJSONArrayEnd();
761 }
762
763 public function readBool(&$bool)
764 {
765 $bool = $this->readJSONInteger() == 0 ? false : true;
766
767 return true;
768 }
769
770 public function readByte(&$byte)
771 {
772 $byte = $this->readJSONInteger();
773
774 return true;
775 }
776
777 public function readI16(&$i16)
778 {
779 $i16 = $this->readJSONInteger();
780
781 return true;
782 }
783
784 public function readI32(&$i32)
785 {
786 $i32 = $this->readJSONInteger();
787
788 return true;
789 }
790
791 public function readI64(&$i64)
792 {
793 if (PHP_INT_SIZE === 4) {
794 $i64 = $this->readJSONIntegerAsString();
795 } else {
796 $i64 = $this->readJSONInteger();
797 }
798
799 return true;
800 }
801
802 public function readDouble(&$dub)
803 {
804 $dub = $this->readJSONDouble();
805
806 return true;
807 }
808
809 public function readString(&$str)
810 {
811 $str = $this->readJSONString(false);
812
813 return true;
814 }
815 }