1<?php
2/*
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 *   http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 * @package thrift.protocol
21 */
22
23namespace Thrift\Protocol;
24
25use Thrift\Exception\TException;
26use Thrift\Transport\TTransport;
27use Thrift\Type\TType;
28use Thrift\Exception\TProtocolException;
29
30/**
31 * Protocol base class module.
32 */
33abstract class TProtocol
34{
35    /**
36     * Underlying transport
37     *
38     * @var TTransport
39     */
40    protected $trans_;
41
42    /**
43     * @param TTransport $trans
44     */
45    protected function __construct($trans)
46    {
47        $this->trans_ = $trans;
48    }
49
50    /**
51     * Accessor for transport
52     *
53     * @return TTransport
54     */
55    public function getTransport()
56    {
57        return $this->trans_;
58    }
59
60    /**
61     * Writes the message header
62     *
63     * @param string $name Function name
64     * @param int $type message type TMessageType::CALL or TMessageType::REPLY
65     * @param int $seqid The sequence id of this message
66     */
67    abstract public function writeMessageBegin($name, $type, $seqid);
68
69    /**
70     * Close the message
71     */
72    abstract public function writeMessageEnd();
73
74    /**
75     * Writes a struct header.
76     *
77     * @param string $name Struct name
78     * @throws TException on write error
79     * @return int How many bytes written
80     */
81    abstract public function writeStructBegin($name);
82
83    /**
84     * Close a struct.
85     *
86     * @throws TException on write error
87     * @return int How many bytes written
88     */
89    abstract public function writeStructEnd();
90
91    /*
92     * Starts a field.
93     *
94     * @param string     $name Field name
95     * @param int        $type Field type
96     * @param int        $fid  Field id
97     * @throws TException on write error
98     * @return int How many bytes written
99     */
100    abstract public function writeFieldBegin($fieldName, $fieldType, $fieldId);
101
102    abstract public function writeFieldEnd();
103
104    abstract public function writeFieldStop();
105
106    abstract public function writeMapBegin($keyType, $valType, $size);
107
108    abstract public function writeMapEnd();
109
110    abstract public function writeListBegin($elemType, $size);
111
112    abstract public function writeListEnd();
113
114    abstract public function writeSetBegin($elemType, $size);
115
116    abstract public function writeSetEnd();
117
118    abstract public function writeBool($bool);
119
120    abstract public function writeByte($byte);
121
122    abstract public function writeI16($i16);
123
124    abstract public function writeI32($i32);
125
126    abstract public function writeI64($i64);
127
128    abstract public function writeDouble($dub);
129
130    abstract public function writeString($str);
131
132    /**
133     * Reads the message header
134     *
135     * @param string $name Function name
136     * @param int $type message type TMessageType::CALL or TMessageType::REPLY
137     * @parem int $seqid The sequence id of this message
138     */
139    abstract public function readMessageBegin(&$name, &$type, &$seqid);
140
141    /**
142     * Read the close of message
143     */
144    abstract public function readMessageEnd();
145
146    abstract public function readStructBegin(&$name);
147
148    abstract public function readStructEnd();
149
150    abstract public function readFieldBegin(&$name, &$fieldType, &$fieldId);
151
152    abstract public function readFieldEnd();
153
154    abstract public function readMapBegin(&$keyType, &$valType, &$size);
155
156    abstract public function readMapEnd();
157
158    abstract public function readListBegin(&$elemType, &$size);
159
160    abstract public function readListEnd();
161
162    abstract public function readSetBegin(&$elemType, &$size);
163
164    abstract public function readSetEnd();
165
166    abstract public function readBool(&$bool);
167
168    abstract public function readByte(&$byte);
169
170    abstract public function readI16(&$i16);
171
172    abstract public function readI32(&$i32);
173
174    abstract public function readI64(&$i64);
175
176    abstract public function readDouble(&$dub);
177
178    abstract public function readString(&$str);
179
180    /**
181     * The skip function is a utility to parse over unrecognized date without
182     * causing corruption.
183     *
184     * @param TType $type What type is it
185     */
186    public function skip($type)
187    {
188        switch ($type) {
189            case TType::BOOL:
190                return $this->readBool($bool);
191            case TType::BYTE:
192                return $this->readByte($byte);
193            case TType::I16:
194                return $this->readI16($i16);
195            case TType::I32:
196                return $this->readI32($i32);
197            case TType::I64:
198                return $this->readI64($i64);
199            case TType::DOUBLE:
200                return $this->readDouble($dub);
201            case TType::STRING:
202                return $this->readString($str);
203            case TType::STRUCT:
204                $result = $this->readStructBegin($name);
205                while (true) {
206                    $result += $this->readFieldBegin($name, $ftype, $fid);
207                    if ($ftype == TType::STOP) {
208                        break;
209                    }
210                    $result += $this->skip($ftype);
211                    $result += $this->readFieldEnd();
212                }
213                $result += $this->readStructEnd();
214
215                return $result;
216
217            case TType::MAP:
218                $result = $this->readMapBegin($keyType, $valType, $size);
219                for ($i = 0; $i < $size; $i++) {
220                    $result += $this->skip($keyType);
221                    $result += $this->skip($valType);
222                }
223                $result += $this->readMapEnd();
224
225                return $result;
226
227            case TType::SET:
228                $result = $this->readSetBegin($elemType, $size);
229                for ($i = 0; $i < $size; $i++) {
230                    $result += $this->skip($elemType);
231                }
232                $result += $this->readSetEnd();
233
234                return $result;
235
236            case TType::LST:
237                $result = $this->readListBegin($elemType, $size);
238                for ($i = 0; $i < $size; $i++) {
239                    $result += $this->skip($elemType);
240                }
241                $result += $this->readListEnd();
242
243                return $result;
244
245            default:
246                throw new TProtocolException(
247                    'Unknown field type: ' . $type,
248                    TProtocolException::INVALID_DATA
249                );
250        }
251    }
252
253    /**
254     * Utility for skipping binary data
255     *
256     * @param TTransport $itrans TTransport object
257     * @param int $type Field type
258     */
259    public static function skipBinary($itrans, $type)
260    {
261        switch ($type) {
262            case TType::BOOL:
263                return $itrans->readAll(1);
264            case TType::BYTE:
265                return $itrans->readAll(1);
266            case TType::I16:
267                return $itrans->readAll(2);
268            case TType::I32:
269                return $itrans->readAll(4);
270            case TType::I64:
271                return $itrans->readAll(8);
272            case TType::DOUBLE:
273                return $itrans->readAll(8);
274            case TType::STRING:
275                $len = unpack('N', $itrans->readAll(4));
276                $len = $len[1];
277                if ($len > 0x7fffffff) {
278                    $len = 0 - (($len - 1) ^ 0xffffffff);
279                }
280
281                return 4 + $itrans->readAll($len);
282
283            case TType::STRUCT:
284                $result = 0;
285                while (true) {
286                    $ftype = 0;
287                    $fid = 0;
288                    $data = $itrans->readAll(1);
289                    $arr = unpack('c', $data);
290                    $ftype = $arr[1];
291                    if ($ftype == TType::STOP) {
292                        break;
293                    }
294                    // I16 field id
295                    $result += $itrans->readAll(2);
296                    $result += self::skipBinary($itrans, $ftype);
297                }
298
299                return $result;
300
301            case TType::MAP:
302                // Ktype
303                $data = $itrans->readAll(1);
304                $arr = unpack('c', $data);
305                $ktype = $arr[1];
306                // Vtype
307                $data = $itrans->readAll(1);
308                $arr = unpack('c', $data);
309                $vtype = $arr[1];
310                // Size
311                $data = $itrans->readAll(4);
312                $arr = unpack('N', $data);
313                $size = $arr[1];
314                if ($size > 0x7fffffff) {
315                    $size = 0 - (($size - 1) ^ 0xffffffff);
316                }
317                $result = 6;
318                for ($i = 0; $i < $size; $i++) {
319                    $result += self::skipBinary($itrans, $ktype);
320                    $result += self::skipBinary($itrans, $vtype);
321                }
322
323                return $result;
324
325            case TType::SET:
326            case TType::LST:
327                // Vtype
328                $data = $itrans->readAll(1);
329                $arr = unpack('c', $data);
330                $vtype = $arr[1];
331                // Size
332                $data = $itrans->readAll(4);
333                $arr = unpack('N', $data);
334                $size = $arr[1];
335                if ($size > 0x7fffffff) {
336                    $size = 0 - (($size - 1) ^ 0xffffffff);
337                }
338                $result = 5;
339                for ($i = 0; $i < $size; $i++) {
340                    $result += self::skipBinary($itrans, $vtype);
341                }
342
343                return $result;
344
345            default:
346                throw new TProtocolException(
347                    'Unknown field type: ' . $type,
348                    TProtocolException::INVALID_DATA
349                );
350        }
351    }
352}
353