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
21 */
22
23namespace Thrift\Exception;
24
25use Thrift\Type\TType;
26use Thrift\Base\TBase;
27
28/**
29 * NOTE(mcslee): This currently contains a ton of duplicated code from TBase
30 * because we need to save CPU cycles and this is not yet in an extension.
31 * Ideally we'd multiply-inherit TException from both Exception and Base, but
32 * that's not possible in PHP and there are no modules either, so for now we
33 * apologetically take a trip to HackTown.
34 *
35 * Can be called with standard Exception constructor (message, code) or with
36 * Thrift Base object constructor (spec, vals).
37 *
38 * @param mixed $p1 Message (string) or type-spec (array)
39 * @param mixed $p2 Code (integer) or values (array)
40 */
41class TException extends \Exception
42{
43    public function __construct($p1 = null, $p2 = 0)
44    {
45        if (is_array($p1) && is_array($p2)) {
46            $spec = $p1;
47            $vals = $p2;
48            foreach ($spec as $fid => $fspec) {
49                $var = $fspec['var'];
50                if (isset($vals[$var])) {
51                    $this->$var = $vals[$var];
52                }
53            }
54        } else {
55            parent::__construct($p1, $p2);
56        }
57    }
58
59    public static $tmethod = array(
60        TType::BOOL => 'Bool',
61        TType::BYTE => 'Byte',
62        TType::I16 => 'I16',
63        TType::I32 => 'I32',
64        TType::I64 => 'I64',
65        TType::DOUBLE => 'Double',
66        TType::STRING => 'String'
67    );
68
69    private function _readMap(&$var, $spec, $input)
70    {
71        $xfer = 0;
72        $ktype = $spec['ktype'];
73        $vtype = $spec['vtype'];
74        $kread = $vread = null;
75        if (isset(TBase::$tmethod[$ktype])) {
76            $kread = 'read' . TBase::$tmethod[$ktype];
77        } else {
78            $kspec = $spec['key'];
79        }
80        if (isset(TBase::$tmethod[$vtype])) {
81            $vread = 'read' . TBase::$tmethod[$vtype];
82        } else {
83            $vspec = $spec['val'];
84        }
85        $var = array();
86        $_ktype = $_vtype = $size = 0;
87        $xfer += $input->readMapBegin($_ktype, $_vtype, $size);
88        for ($i = 0; $i < $size; ++$i) {
89            $key = $val = null;
90            if ($kread !== null) {
91                $xfer += $input->$kread($key);
92            } else {
93                switch ($ktype) {
94                    case TType::STRUCT:
95                        $class = $kspec['class'];
96                        $key = new $class();
97                        $xfer += $key->read($input);
98                        break;
99                    case TType::MAP:
100                        $xfer += $this->_readMap($key, $kspec, $input);
101                        break;
102                    case TType::LST:
103                        $xfer += $this->_readList($key, $kspec, $input, false);
104                        break;
105                    case TType::SET:
106                        $xfer += $this->_readList($key, $kspec, $input, true);
107                        break;
108                }
109            }
110            if ($vread !== null) {
111                $xfer += $input->$vread($val);
112            } else {
113                switch ($vtype) {
114                    case TType::STRUCT:
115                        $class = $vspec['class'];
116                        $val = new $class();
117                        $xfer += $val->read($input);
118                        break;
119                    case TType::MAP:
120                        $xfer += $this->_readMap($val, $vspec, $input);
121                        break;
122                    case TType::LST:
123                        $xfer += $this->_readList($val, $vspec, $input, false);
124                        break;
125                    case TType::SET:
126                        $xfer += $this->_readList($val, $vspec, $input, true);
127                        break;
128                }
129            }
130            $var[$key] = $val;
131        }
132        $xfer += $input->readMapEnd();
133
134        return $xfer;
135    }
136
137    private function _readList(&$var, $spec, $input, $set = false)
138    {
139        $xfer = 0;
140        $etype = $spec['etype'];
141        $eread = $vread = null;
142        if (isset(TBase::$tmethod[$etype])) {
143            $eread = 'read' . TBase::$tmethod[$etype];
144        } else {
145            $espec = $spec['elem'];
146        }
147        $var = array();
148        $_etype = $size = 0;
149        if ($set) {
150            $xfer += $input->readSetBegin($_etype, $size);
151        } else {
152            $xfer += $input->readListBegin($_etype, $size);
153        }
154        for ($i = 0; $i < $size; ++$i) {
155            $elem = null;
156            if ($eread !== null) {
157                $xfer += $input->$eread($elem);
158            } else {
159                $espec = $spec['elem'];
160                switch ($etype) {
161                    case TType::STRUCT:
162                        $class = $espec['class'];
163                        $elem = new $class();
164                        $xfer += $elem->read($input);
165                        break;
166                    case TType::MAP:
167                        $xfer += $this->_readMap($elem, $espec, $input);
168                        break;
169                    case TType::LST:
170                        $xfer += $this->_readList($elem, $espec, $input, false);
171                        break;
172                    case TType::SET:
173                        $xfer += $this->_readList($elem, $espec, $input, true);
174                        break;
175                }
176            }
177            if ($set) {
178                $var[$elem] = true;
179            } else {
180                $var [] = $elem;
181            }
182        }
183        if ($set) {
184            $xfer += $input->readSetEnd();
185        } else {
186            $xfer += $input->readListEnd();
187        }
188
189        return $xfer;
190    }
191
192    protected function _read($class, $spec, $input)
193    {
194        $xfer = 0;
195        $fname = null;
196        $ftype = 0;
197        $fid = 0;
198        $xfer += $input->readStructBegin($fname);
199        while (true) {
200            $xfer += $input->readFieldBegin($fname, $ftype, $fid);
201            if ($ftype == TType::STOP) {
202                break;
203            }
204            if (isset($spec[$fid])) {
205                $fspec = $spec[$fid];
206                $var = $fspec['var'];
207                if ($ftype == $fspec['type']) {
208                    $xfer = 0;
209                    if (isset(TBase::$tmethod[$ftype])) {
210                        $func = 'read' . TBase::$tmethod[$ftype];
211                        $xfer += $input->$func($this->$var);
212                    } else {
213                        switch ($ftype) {
214                            case TType::STRUCT:
215                                $class = $fspec['class'];
216                                $this->$var = new $class();
217                                $xfer += $this->$var->read($input);
218                                break;
219                            case TType::MAP:
220                                $xfer += $this->_readMap($this->$var, $fspec, $input);
221                                break;
222                            case TType::LST:
223                                $xfer += $this->_readList($this->$var, $fspec, $input, false);
224                                break;
225                            case TType::SET:
226                                $xfer += $this->_readList($this->$var, $fspec, $input, true);
227                                break;
228                        }
229                    }
230                } else {
231                    $xfer += $input->skip($ftype);
232                }
233            } else {
234                $xfer += $input->skip($ftype);
235            }
236            $xfer += $input->readFieldEnd();
237        }
238        $xfer += $input->readStructEnd();
239
240        return $xfer;
241    }
242
243    private function _writeMap($var, $spec, $output)
244    {
245        $xfer = 0;
246        $ktype = $spec['ktype'];
247        $vtype = $spec['vtype'];
248        $kwrite = $vwrite = null;
249        if (isset(TBase::$tmethod[$ktype])) {
250            $kwrite = 'write' . TBase::$tmethod[$ktype];
251        } else {
252            $kspec = $spec['key'];
253        }
254        if (isset(TBase::$tmethod[$vtype])) {
255            $vwrite = 'write' . TBase::$tmethod[$vtype];
256        } else {
257            $vspec = $spec['val'];
258        }
259        $xfer += $output->writeMapBegin($ktype, $vtype, count($var));
260        foreach ($var as $key => $val) {
261            if (isset($kwrite)) {
262                $xfer += $output->$kwrite($key);
263            } else {
264                switch ($ktype) {
265                    case TType::STRUCT:
266                        $xfer += $key->write($output);
267                        break;
268                    case TType::MAP:
269                        $xfer += $this->_writeMap($key, $kspec, $output);
270                        break;
271                    case TType::LST:
272                        $xfer += $this->_writeList($key, $kspec, $output, false);
273                        break;
274                    case TType::SET:
275                        $xfer += $this->_writeList($key, $kspec, $output, true);
276                        break;
277                }
278            }
279            if (isset($vwrite)) {
280                $xfer += $output->$vwrite($val);
281            } else {
282                switch ($vtype) {
283                    case TType::STRUCT:
284                        $xfer += $val->write($output);
285                        break;
286                    case TType::MAP:
287                        $xfer += $this->_writeMap($val, $vspec, $output);
288                        break;
289                    case TType::LST:
290                        $xfer += $this->_writeList($val, $vspec, $output, false);
291                        break;
292                    case TType::SET:
293                        $xfer += $this->_writeList($val, $vspec, $output, true);
294                        break;
295                }
296            }
297        }
298        $xfer += $output->writeMapEnd();
299
300        return $xfer;
301    }
302
303    private function _writeList($var, $spec, $output, $set = false)
304    {
305        $xfer = 0;
306        $etype = $spec['etype'];
307        $ewrite = null;
308        if (isset(TBase::$tmethod[$etype])) {
309            $ewrite = 'write' . TBase::$tmethod[$etype];
310        } else {
311            $espec = $spec['elem'];
312        }
313        if ($set) {
314            $xfer += $output->writeSetBegin($etype, count($var));
315        } else {
316            $xfer += $output->writeListBegin($etype, count($var));
317        }
318        foreach ($var as $key => $val) {
319            $elem = $set ? $key : $val;
320            if (isset($ewrite)) {
321                $xfer += $output->$ewrite($elem);
322            } else {
323                switch ($etype) {
324                    case TType::STRUCT:
325                        $xfer += $elem->write($output);
326                        break;
327                    case TType::MAP:
328                        $xfer += $this->_writeMap($elem, $espec, $output);
329                        break;
330                    case TType::LST:
331                        $xfer += $this->_writeList($elem, $espec, $output, false);
332                        break;
333                    case TType::SET:
334                        $xfer += $this->_writeList($elem, $espec, $output, true);
335                        break;
336                }
337            }
338        }
339        if ($set) {
340            $xfer += $output->writeSetEnd();
341        } else {
342            $xfer += $output->writeListEnd();
343        }
344
345        return $xfer;
346    }
347
348    protected function _write($class, $spec, $output)
349    {
350        $xfer = 0;
351        $xfer += $output->writeStructBegin($class);
352        foreach ($spec as $fid => $fspec) {
353            $var = $fspec['var'];
354            if ($this->$var !== null) {
355                $ftype = $fspec['type'];
356                $xfer += $output->writeFieldBegin($var, $ftype, $fid);
357                if (isset(TBase::$tmethod[$ftype])) {
358                    $func = 'write' . TBase::$tmethod[$ftype];
359                    $xfer += $output->$func($this->$var);
360                } else {
361                    switch ($ftype) {
362                        case TType::STRUCT:
363                            $xfer += $this->$var->write($output);
364                            break;
365                        case TType::MAP:
366                            $xfer += $this->_writeMap($this->$var, $fspec, $output);
367                            break;
368                        case TType::LST:
369                            $xfer += $this->_writeList($this->$var, $fspec, $output, false);
370                            break;
371                        case TType::SET:
372                            $xfer += $this->_writeList($this->$var, $fspec, $output, true);
373                            break;
374                    }
375                }
376                $xfer += $output->writeFieldEnd();
377            }
378        }
379        $xfer += $output->writeFieldStop();
380        $xfer += $output->writeStructEnd();
381
382        return $xfer;
383    }
384}
385