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
24namespace Thrift\Protocol;
25
26use Thrift\Exception\TException;
27use Thrift\Exception\TProtocolException;
28use Thrift\Protocol\SimpleJSON\Context;
29use Thrift\Protocol\SimpleJSON\ListContext;
30use Thrift\Protocol\SimpleJSON\StructContext;
31use Thrift\Protocol\SimpleJSON\MapContext;
32use Thrift\Protocol\SimpleJSON\CollectionMapKeyException;
33
34/**
35 * SimpleJSON implementation of thrift protocol, ported from Java.
36 */
37class TSimpleJSONProtocol extends TProtocol
38{
39    const COMMA = ',';
40    const COLON = ':';
41    const LBRACE = '{';
42    const RBRACE = '}';
43    const LBRACKET = '[';
44    const RBRACKET = ']';
45    const QUOTE = '"';
46
47    const NAME_MAP = "map";
48    const NAME_LIST = "lst";
49    const NAME_SET = "set";
50
51    protected $writeContext_ = null;
52    protected $writeContextStack_ = [];
53
54    /**
55     * Push a new write context onto the stack.
56     */
57    protected function pushWriteContext(Context $c)
58    {
59        $this->writeContextStack_[] = $this->writeContext_;
60        $this->writeContext_ = $c;
61    }
62
63    /**
64     * Pop the last write context off the stack
65     */
66    protected function popWriteContext()
67    {
68        $this->writeContext_ = array_pop($this->writeContextStack_);
69    }
70
71    /**
72     * Used to make sure that we are not encountering a map whose keys are containers
73     */
74    protected function assertContextIsNotMapKey($invalidKeyType)
75    {
76        if ($this->writeContext_->isMapKey()) {
77            throw new CollectionMapKeyException(
78                "Cannot serialize a map with keys that are of type " .
79                $invalidKeyType
80            );
81        }
82    }
83
84    private function writeJSONString($b)
85    {
86        $this->writeContext_->write();
87
88        $this->trans_->write(json_encode((string)$b));
89    }
90
91    private function writeJSONInteger($num)
92    {
93        $isMapKey = $this->writeContext_->isMapKey();
94
95        $this->writeContext_->write();
96
97        if ($isMapKey) {
98            $this->trans_->write(self::QUOTE);
99        }
100
101        $this->trans_->write((int)$num);
102
103        if ($isMapKey) {
104            $this->trans_->write(self::QUOTE);
105        }
106    }
107
108    private function writeJSONDouble($num)
109    {
110        $isMapKey = $this->writeContext_->isMapKey();
111
112        $this->writeContext_->write();
113
114        if ($isMapKey) {
115            $this->trans_->write(self::QUOTE);
116        }
117
118        $this->trans_->write(json_encode((float)$num));
119
120        if ($isMapKey) {
121            $this->trans_->write(self::QUOTE);
122        }
123    }
124
125    /**
126     * Constructor
127     */
128    public function __construct($trans)
129    {
130        parent::__construct($trans);
131        $this->writeContext_ = new Context();
132    }
133
134    /**
135     * Writes the message header
136     *
137     * @param string $name  Function name
138     * @param int    $type  message type TMessageType::CALL or TMessageType::REPLY
139     * @param int    $seqid The sequence id of this message
140     */
141    public function writeMessageBegin($name, $type, $seqid)
142    {
143        $this->trans_->write(self::LBRACKET);
144        $this->pushWriteContext(new ListContext($this));
145        $this->writeJSONString($name);
146        $this->writeJSONInteger($type);
147        $this->writeJSONInteger($seqid);
148    }
149
150    /**
151     * Close the message
152     */
153    public function writeMessageEnd()
154    {
155        $this->popWriteContext();
156        $this->trans_->write(self::RBRACKET);
157    }
158
159    /**
160     * Writes a struct header.
161     *
162     * @param  string     $name Struct name
163     */
164    public function writeStructBegin($name)
165    {
166        $this->writeContext_->write();
167        $this->trans_->write(self::LBRACE);
168        $this->pushWriteContext(new StructContext($this));
169    }
170
171    /**
172     * Close a struct.
173     */
174    public function writeStructEnd()
175    {
176        $this->popWriteContext();
177        $this->trans_->write(self::RBRACE);
178    }
179
180    public function writeFieldBegin($fieldName, $fieldType, $fieldId)
181    {
182        $this->writeJSONString($fieldName);
183    }
184
185    public function writeFieldEnd()
186    {
187    }
188
189    public function writeFieldStop()
190    {
191    }
192
193    public function writeMapBegin($keyType, $valType, $size)
194    {
195        $this->assertContextIsNotMapKey(self::NAME_MAP);
196        $this->writeContext_->write();
197        $this->trans_->write(self::LBRACE);
198        $this->pushWriteContext(new MapContext($this));
199    }
200
201    public function writeMapEnd()
202    {
203        $this->popWriteContext();
204        $this->trans_->write(self::RBRACE);
205    }
206
207    public function writeListBegin($elemType, $size)
208    {
209        $this->assertContextIsNotMapKey(self::NAME_LIST);
210        $this->writeContext_->write();
211        $this->trans_->write(self::LBRACKET);
212        $this->pushWriteContext(new ListContext($this));
213        // No metadata!
214    }
215
216    public function writeListEnd()
217    {
218        $this->popWriteContext();
219        $this->trans_->write(self::RBRACKET);
220    }
221
222    public function writeSetBegin($elemType, $size)
223    {
224        $this->assertContextIsNotMapKey(self::NAME_SET);
225        $this->writeContext_->write();
226        $this->trans_->write(self::LBRACKET);
227        $this->pushWriteContext(new ListContext($this));
228        // No metadata!
229    }
230
231    public function writeSetEnd()
232    {
233        $this->popWriteContext();
234        $this->trans_->write(self::RBRACKET);
235    }
236
237    public function writeBool($bool)
238    {
239        $this->writeJSONInteger($bool ? 1 : 0);
240    }
241
242    public function writeByte($byte)
243    {
244        $this->writeJSONInteger($byte);
245    }
246
247    public function writeI16($i16)
248    {
249        $this->writeJSONInteger($i16);
250    }
251
252    public function writeI32($i32)
253    {
254        $this->writeJSONInteger($i32);
255    }
256
257    public function writeI64($i64)
258    {
259        $this->writeJSONInteger($i64);
260    }
261
262    public function writeDouble($dub)
263    {
264        $this->writeJSONDouble($dub);
265    }
266
267    public function writeString($str)
268    {
269        $this->writeJSONString($str);
270    }
271
272    /**
273     * Reading methods.
274     *
275     * simplejson is not meant to be read back into thrift
276     * - see http://wiki.apache.org/thrift/ThriftUsageJava
277     * - use JSON instead
278     */
279
280    public function readMessageBegin(&$name, &$type, &$seqid)
281    {
282        throw new TException("Not implemented");
283    }
284
285    public function readMessageEnd()
286    {
287        throw new TException("Not implemented");
288    }
289
290    public function readStructBegin(&$name)
291    {
292        throw new TException("Not implemented");
293    }
294
295    public function readStructEnd()
296    {
297        throw new TException("Not implemented");
298    }
299
300    public function readFieldBegin(&$name, &$fieldType, &$fieldId)
301    {
302        throw new TException("Not implemented");
303    }
304
305    public function readFieldEnd()
306    {
307        throw new TException("Not implemented");
308    }
309
310    public function readMapBegin(&$keyType, &$valType, &$size)
311    {
312        throw new TException("Not implemented");
313    }
314
315    public function readMapEnd()
316    {
317        throw new TException("Not implemented");
318    }
319
320    public function readListBegin(&$elemType, &$size)
321    {
322        throw new TException("Not implemented");
323    }
324
325    public function readListEnd()
326    {
327        throw new TException("Not implemented");
328    }
329
330    public function readSetBegin(&$elemType, &$size)
331    {
332        throw new TException("Not implemented");
333    }
334
335    public function readSetEnd()
336    {
337        throw new TException("Not implemented");
338    }
339
340    public function readBool(&$bool)
341    {
342        throw new TException("Not implemented");
343    }
344
345    public function readByte(&$byte)
346    {
347        throw new TException("Not implemented");
348    }
349
350    public function readI16(&$i16)
351    {
352        throw new TException("Not implemented");
353    }
354
355    public function readI32(&$i32)
356    {
357        throw new TException("Not implemented");
358    }
359
360    public function readI64(&$i64)
361    {
362        throw new TException("Not implemented");
363    }
364
365    public function readDouble(&$dub)
366    {
367        throw new TException("Not implemented");
368    }
369
370    public function readString(&$str)
371    {
372        throw new TException("Not implemented");
373    }
374}
375