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