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