├── README.md ├── Thrift ├── Base │ └── TBase.php ├── ClassLoader │ └── ThriftClassLoader.php ├── Exception │ ├── TApplicationException.php │ ├── TException.php │ ├── TProtocolException.php │ └── TTransportException.php ├── Factory │ ├── TBinaryProtocolFactory.php │ ├── TCompactProtocolFactory.php │ ├── TJSONProtocolFactory.php │ ├── TProtocolFactory.php │ ├── TStringFuncFactory.php │ └── TTransportFactory.php ├── Protocol │ ├── JSON │ │ ├── BaseContext.php │ │ ├── ListContext.php │ │ ├── LookaheadReader.php │ │ └── PairContext.php │ ├── SimpleJSON │ │ ├── CollectionMapKeyException.php │ │ ├── Context.php │ │ ├── ListContext.php │ │ ├── MapContext.php │ │ └── StructContext.php │ ├── TBinaryProtocol.php │ ├── TBinaryProtocolAccelerated.php │ ├── TCompactProtocol.php │ ├── TJSONProtocol.php │ ├── TMultiplexedProtocol.php │ ├── TProtocol.php │ ├── TProtocolDecorator.php │ └── TSimpleJSONProtocol.php ├── Serializer │ └── TBinarySerializer.php ├── Server │ ├── TForkingServer.php │ ├── TServer.php │ ├── TServerSocket.php │ ├── TServerTransport.php │ └── TSimpleServer.php ├── StringFunc │ ├── Core.php │ ├── Mbstring.php │ └── TStringFunc.php ├── TMultiplexedProcessor.php ├── Transport │ ├── TBufferedTransport.php │ ├── TCurlClient.php │ ├── TFramedTransport.php │ ├── THttpClient.php │ ├── TMemoryBuffer.php │ ├── TNullTransport.php │ ├── TPhpStream.php │ ├── TSocket.php │ ├── TSocketPool.php │ └── TTransport.php └── Type │ ├── TConstant.php │ ├── TMessageType.php │ └── TType.php ├── client.php ├── gen-php ├── shared │ ├── SharedService.php │ └── Types.php └── tutorial │ ├── Calculator.php │ └── Types.php ├── server.php ├── shared.thrift └── tutorial.thrift /README.md: -------------------------------------------------------------------------------- 1 | # php thrift server 2 | 3 | 4 | 一个简单的php thrift client和server示例。 主要参考自thrift官网样例参考,集成了各个模块,使得执行更加简单,利用`php -S`使得server的显示和thrift调用更加清楚。 5 | 6 | 7 | 在根目录下执行命令即可执行。 8 | 9 | server执行方法:`php -S localhost:9999` 10 | 11 | client执行方法:`php client.php` 12 | 13 | A simple demo of php thrift client with server. Reference from thrift, we simplify the module and use `php -S` , you can simply use it, making understanding thrift more clear. 14 | 15 | Do commands under the root document of repo. 16 | 17 | For Server : `php -S localhost:9999` 18 | 19 | 20 | For Client: `php client.php` 21 | 22 | -------------------------------------------------------------------------------- /Thrift/Base/TBase.php: -------------------------------------------------------------------------------- 1 | 'Bool', 37 | TType::BYTE => 'Byte', 38 | TType::I16 => 'I16', 39 | TType::I32 => 'I32', 40 | TType::I64 => 'I64', 41 | TType::DOUBLE => 'Double', 42 | TType::STRING => 'String'); 43 | 44 | abstract public function read($input); 45 | 46 | abstract public function write($output); 47 | 48 | public function __construct($spec=null, $vals=null) 49 | { 50 | if (is_array($spec) && is_array($vals)) { 51 | foreach ($spec as $fid => $fspec) { 52 | $var = $fspec['var']; 53 | if (isset($vals[$var])) { 54 | $this->$var = $vals[$var]; 55 | } 56 | } 57 | } 58 | } 59 | 60 | public function __wakeup() 61 | { 62 | $this->__construct(get_object_vars($this)); 63 | } 64 | 65 | private function _readMap(&$var, $spec, $input) 66 | { 67 | $xfer = 0; 68 | $ktype = $spec['ktype']; 69 | $vtype = $spec['vtype']; 70 | $kread = $vread = null; 71 | if (isset(TBase::$tmethod[$ktype])) { 72 | $kread = 'read'.TBase::$tmethod[$ktype]; 73 | } else { 74 | $kspec = $spec['key']; 75 | } 76 | if (isset(TBase::$tmethod[$vtype])) { 77 | $vread = 'read'.TBase::$tmethod[$vtype]; 78 | } else { 79 | $vspec = $spec['val']; 80 | } 81 | $var = array(); 82 | $_ktype = $_vtype = $size = 0; 83 | $xfer += $input->readMapBegin($_ktype, $_vtype, $size); 84 | for ($i = 0; $i < $size; ++$i) { 85 | $key = $val = null; 86 | if ($kread !== null) { 87 | $xfer += $input->$kread($key); 88 | } else { 89 | switch ($ktype) { 90 | case TType::STRUCT: 91 | $class = $kspec['class']; 92 | $key = new $class(); 93 | $xfer += $key->read($input); 94 | break; 95 | case TType::MAP: 96 | $xfer += $this->_readMap($key, $kspec, $input); 97 | break; 98 | case TType::LST: 99 | $xfer += $this->_readList($key, $kspec, $input, false); 100 | break; 101 | case TType::SET: 102 | $xfer += $this->_readList($key, $kspec, $input, true); 103 | break; 104 | } 105 | } 106 | if ($vread !== null) { 107 | $xfer += $input->$vread($val); 108 | } else { 109 | switch ($vtype) { 110 | case TType::STRUCT: 111 | $class = $vspec['class']; 112 | $val = new $class(); 113 | $xfer += $val->read($input); 114 | break; 115 | case TType::MAP: 116 | $xfer += $this->_readMap($val, $vspec, $input); 117 | break; 118 | case TType::LST: 119 | $xfer += $this->_readList($val, $vspec, $input, false); 120 | break; 121 | case TType::SET: 122 | $xfer += $this->_readList($val, $vspec, $input, true); 123 | break; 124 | } 125 | } 126 | $var[$key] = $val; 127 | } 128 | $xfer += $input->readMapEnd(); 129 | 130 | return $xfer; 131 | } 132 | 133 | private function _readList(&$var, $spec, $input, $set=false) 134 | { 135 | $xfer = 0; 136 | $etype = $spec['etype']; 137 | $eread = $vread = null; 138 | if (isset(TBase::$tmethod[$etype])) { 139 | $eread = 'read'.TBase::$tmethod[$etype]; 140 | } else { 141 | $espec = $spec['elem']; 142 | } 143 | $var = array(); 144 | $_etype = $size = 0; 145 | if ($set) { 146 | $xfer += $input->readSetBegin($_etype, $size); 147 | } else { 148 | $xfer += $input->readListBegin($_etype, $size); 149 | } 150 | for ($i = 0; $i < $size; ++$i) { 151 | $elem = null; 152 | if ($eread !== null) { 153 | $xfer += $input->$eread($elem); 154 | } else { 155 | $espec = $spec['elem']; 156 | switch ($etype) { 157 | case TType::STRUCT: 158 | $class = $espec['class']; 159 | $elem = new $class(); 160 | $xfer += $elem->read($input); 161 | break; 162 | case TType::MAP: 163 | $xfer += $this->_readMap($elem, $espec, $input); 164 | break; 165 | case TType::LST: 166 | $xfer += $this->_readList($elem, $espec, $input, false); 167 | break; 168 | case TType::SET: 169 | $xfer += $this->_readList($elem, $espec, $input, true); 170 | break; 171 | } 172 | } 173 | if ($set) { 174 | $var[$elem] = true; 175 | } else { 176 | $var []= $elem; 177 | } 178 | } 179 | if ($set) { 180 | $xfer += $input->readSetEnd(); 181 | } else { 182 | $xfer += $input->readListEnd(); 183 | } 184 | 185 | return $xfer; 186 | } 187 | 188 | protected function _read($class, $spec, $input) 189 | { 190 | $xfer = 0; 191 | $fname = null; 192 | $ftype = 0; 193 | $fid = 0; 194 | $xfer += $input->readStructBegin($fname); 195 | while (true) { 196 | $xfer += $input->readFieldBegin($fname, $ftype, $fid); 197 | if ($ftype == TType::STOP) { 198 | break; 199 | } 200 | if (isset($spec[$fid])) { 201 | $fspec = $spec[$fid]; 202 | $var = $fspec['var']; 203 | if ($ftype == $fspec['type']) { 204 | $xfer = 0; 205 | if (isset(TBase::$tmethod[$ftype])) { 206 | $func = 'read'.TBase::$tmethod[$ftype]; 207 | $xfer += $input->$func($this->$var); 208 | } else { 209 | switch ($ftype) { 210 | case TType::STRUCT: 211 | $class = $fspec['class']; 212 | $this->$var = new $class(); 213 | $xfer += $this->$var->read($input); 214 | break; 215 | case TType::MAP: 216 | $xfer += $this->_readMap($this->$var, $fspec, $input); 217 | break; 218 | case TType::LST: 219 | $xfer += $this->_readList($this->$var, $fspec, $input, false); 220 | break; 221 | case TType::SET: 222 | $xfer += $this->_readList($this->$var, $fspec, $input, true); 223 | break; 224 | } 225 | } 226 | } else { 227 | $xfer += $input->skip($ftype); 228 | } 229 | } else { 230 | $xfer += $input->skip($ftype); 231 | } 232 | $xfer += $input->readFieldEnd(); 233 | } 234 | $xfer += $input->readStructEnd(); 235 | 236 | return $xfer; 237 | } 238 | 239 | private function _writeMap($var, $spec, $output) 240 | { 241 | $xfer = 0; 242 | $ktype = $spec['ktype']; 243 | $vtype = $spec['vtype']; 244 | $kwrite = $vwrite = null; 245 | if (isset(TBase::$tmethod[$ktype])) { 246 | $kwrite = 'write'.TBase::$tmethod[$ktype]; 247 | } else { 248 | $kspec = $spec['key']; 249 | } 250 | if (isset(TBase::$tmethod[$vtype])) { 251 | $vwrite = 'write'.TBase::$tmethod[$vtype]; 252 | } else { 253 | $vspec = $spec['val']; 254 | } 255 | $xfer += $output->writeMapBegin($ktype, $vtype, count($var)); 256 | foreach ($var as $key => $val) { 257 | if (isset($kwrite)) { 258 | $xfer += $output->$kwrite($key); 259 | } else { 260 | switch ($ktype) { 261 | case TType::STRUCT: 262 | $xfer += $key->write($output); 263 | break; 264 | case TType::MAP: 265 | $xfer += $this->_writeMap($key, $kspec, $output); 266 | break; 267 | case TType::LST: 268 | $xfer += $this->_writeList($key, $kspec, $output, false); 269 | break; 270 | case TType::SET: 271 | $xfer += $this->_writeList($key, $kspec, $output, true); 272 | break; 273 | } 274 | } 275 | if (isset($vwrite)) { 276 | $xfer += $output->$vwrite($val); 277 | } else { 278 | switch ($vtype) { 279 | case TType::STRUCT: 280 | $xfer += $val->write($output); 281 | break; 282 | case TType::MAP: 283 | $xfer += $this->_writeMap($val, $vspec, $output); 284 | break; 285 | case TType::LST: 286 | $xfer += $this->_writeList($val, $vspec, $output, false); 287 | break; 288 | case TType::SET: 289 | $xfer += $this->_writeList($val, $vspec, $output, true); 290 | break; 291 | } 292 | } 293 | } 294 | $xfer += $output->writeMapEnd(); 295 | 296 | return $xfer; 297 | } 298 | 299 | private function _writeList($var, $spec, $output, $set=false) 300 | { 301 | $xfer = 0; 302 | $etype = $spec['etype']; 303 | $ewrite = null; 304 | if (isset(TBase::$tmethod[$etype])) { 305 | $ewrite = 'write'.TBase::$tmethod[$etype]; 306 | } else { 307 | $espec = $spec['elem']; 308 | } 309 | if ($set) { 310 | $xfer += $output->writeSetBegin($etype, count($var)); 311 | } else { 312 | $xfer += $output->writeListBegin($etype, count($var)); 313 | } 314 | foreach ($var as $key => $val) { 315 | $elem = $set ? $key : $val; 316 | if (isset($ewrite)) { 317 | $xfer += $output->$ewrite($elem); 318 | } else { 319 | switch ($etype) { 320 | case TType::STRUCT: 321 | $xfer += $elem->write($output); 322 | break; 323 | case TType::MAP: 324 | $xfer += $this->_writeMap($elem, $espec, $output); 325 | break; 326 | case TType::LST: 327 | $xfer += $this->_writeList($elem, $espec, $output, false); 328 | break; 329 | case TType::SET: 330 | $xfer += $this->_writeList($elem, $espec, $output, true); 331 | break; 332 | } 333 | } 334 | } 335 | if ($set) { 336 | $xfer += $output->writeSetEnd(); 337 | } else { 338 | $xfer += $output->writeListEnd(); 339 | } 340 | 341 | return $xfer; 342 | } 343 | 344 | protected function _write($class, $spec, $output) 345 | { 346 | $xfer = 0; 347 | $xfer += $output->writeStructBegin($class); 348 | foreach ($spec as $fid => $fspec) { 349 | $var = $fspec['var']; 350 | if ($this->$var !== null) { 351 | $ftype = $fspec['type']; 352 | $xfer += $output->writeFieldBegin($var, $ftype, $fid); 353 | if (isset(TBase::$tmethod[$ftype])) { 354 | $func = 'write'.TBase::$tmethod[$ftype]; 355 | $xfer += $output->$func($this->$var); 356 | } else { 357 | switch ($ftype) { 358 | case TType::STRUCT: 359 | $xfer += $this->$var->write($output); 360 | break; 361 | case TType::MAP: 362 | $xfer += $this->_writeMap($this->$var, $fspec, $output); 363 | break; 364 | case TType::LST: 365 | $xfer += $this->_writeList($this->$var, $fspec, $output, false); 366 | break; 367 | case TType::SET: 368 | $xfer += $this->_writeList($this->$var, $fspec, $output, true); 369 | break; 370 | } 371 | } 372 | $xfer += $output->writeFieldEnd(); 373 | } 374 | } 375 | $xfer += $output->writeFieldStop(); 376 | $xfer += $output->writeStructEnd(); 377 | 378 | return $xfer; 379 | } 380 | } 381 | -------------------------------------------------------------------------------- /Thrift/ClassLoader/ThriftClassLoader.php: -------------------------------------------------------------------------------- 1 | apc = $apc; 62 | $this->apc_prefix = $apc_prefix; 63 | } 64 | 65 | /** 66 | * Registers a namespace. 67 | * 68 | * @param string $namespace The namespace 69 | * @param array|string $paths The location(s) of the namespace 70 | */ 71 | public function registerNamespace($namespace, $paths) 72 | { 73 | $this->namespaces[$namespace] = (array) $paths; 74 | } 75 | 76 | /** 77 | * Registers a Thrift definition namespace. 78 | * 79 | * @param string $namespace The definition namespace 80 | * @param array|string $paths The location(s) of the definition namespace 81 | */ 82 | public function registerDefinition($namespace, $paths) 83 | { 84 | $this->definitions[$namespace] = (array) $paths; 85 | } 86 | 87 | /** 88 | * Registers this instance as an autoloader. 89 | * 90 | * @param Boolean $prepend Whether to prepend the autoloader or not 91 | */ 92 | public function register($prepend = false) 93 | { 94 | spl_autoload_register(array($this, 'loadClass'), true, $prepend); 95 | } 96 | 97 | /** 98 | * Loads the given class, definition or interface. 99 | * 100 | * @param string $class The name of the class 101 | */ 102 | public function loadClass($class) 103 | { 104 | if ( 105 | (true === $this->apc && ($file = $this->findFileInApc($class))) or 106 | ($file = $this->findFile($class)) 107 | ) 108 | { 109 | require_once $file; 110 | } 111 | } 112 | 113 | /** 114 | * Loads the given class or interface in APC. 115 | * @param string $class The name of the class 116 | * @return string 117 | */ 118 | protected function findFileInApc($class) 119 | { 120 | if (false === $file = apc_fetch($this->apc_prefix.$class)) { 121 | apc_store($this->apc_prefix.$class, $file = $this->findFile($class)); 122 | } 123 | 124 | return $file; 125 | } 126 | 127 | /** 128 | * Find class in namespaces or definitions directories 129 | * @param string $class 130 | * @return string 131 | */ 132 | public function findFile($class) 133 | { 134 | // Remove first backslash 135 | if ('\\' == $class[0]) { 136 | $class = substr($class, 1); 137 | } 138 | 139 | if (false !== $pos = strrpos($class, '\\')) { 140 | // Namespaced class name 141 | $namespace = substr($class, 0, $pos); 142 | 143 | // Iterate in normal namespaces 144 | foreach ($this->namespaces as $ns => $dirs) { 145 | //Don't interfere with other autoloaders 146 | if (0 !== strpos($namespace, $ns)) { 147 | continue; 148 | } 149 | 150 | foreach ($dirs as $dir) { 151 | $className = substr($class, $pos + 1); 152 | 153 | $file = $dir.DIRECTORY_SEPARATOR. 154 | str_replace('\\', DIRECTORY_SEPARATOR, $namespace). 155 | DIRECTORY_SEPARATOR. 156 | $className.'.php'; 157 | 158 | if (file_exists($file)) { 159 | return $file; 160 | } 161 | } 162 | } 163 | 164 | // Iterate in Thrift namespaces 165 | 166 | // Remove first part of namespace 167 | $m = explode('\\', $class); 168 | 169 | // Ignore wrong call 170 | if (count($m) <= 1) { 171 | return; 172 | } 173 | 174 | $class = array_pop($m); 175 | $namespace = implode('\\', $m); 176 | 177 | foreach ($this->definitions as $ns => $dirs) { 178 | //Don't interfere with other autoloaders 179 | if (0 !== strpos($namespace, $ns)) { 180 | continue; 181 | } 182 | 183 | foreach ($dirs as $dir) { 184 | /** 185 | * Available in service: Interface, Client, Processor, Rest 186 | * And every service methods (_.+) 187 | */ 188 | if( 189 | 0 === preg_match('#(.+)(if|client|processor|rest)$#i', $class, $n) and 190 | 0 === preg_match('#(.+)_[a-z0-9]+_(args|result)$#i', $class, $n) 191 | ) 192 | { 193 | $className = 'Types'; 194 | } else { 195 | $className = $n[1]; 196 | } 197 | 198 | $file = $dir.DIRECTORY_SEPARATOR . 199 | str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . 200 | DIRECTORY_SEPARATOR . 201 | $className . '.php'; 202 | 203 | if (file_exists($file)) { 204 | return $file; 205 | } 206 | } 207 | } 208 | } 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /Thrift/Exception/TApplicationException.php: -------------------------------------------------------------------------------- 1 | array('var' => 'message', 31 | 'type' => TType::STRING), 32 | 2 => array('var' => 'code', 33 | 'type' => TType::I32)); 34 | 35 | const UNKNOWN = 0; 36 | const UNKNOWN_METHOD = 1; 37 | const INVALID_MESSAGE_TYPE = 2; 38 | const WRONG_METHOD_NAME = 3; 39 | const BAD_SEQUENCE_ID = 4; 40 | const MISSING_RESULT = 5; 41 | const INTERNAL_ERROR = 6; 42 | const PROTOCOL_ERROR = 7; 43 | const INVALID_TRANSFORM = 8; 44 | const INVALID_PROTOCOL = 9; 45 | const UNSUPPORTED_CLIENT_TYPE = 10; 46 | 47 | public function __construct($message=null, $code=0) 48 | { 49 | parent::__construct($message, $code); 50 | } 51 | 52 | public function read($output) 53 | { 54 | return $this->_read('TApplicationException', self::$_TSPEC, $output); 55 | } 56 | 57 | public function write($output) 58 | { 59 | $xfer = 0; 60 | $xfer += $output->writeStructBegin('TApplicationException'); 61 | if ($message = $this->getMessage()) { 62 | $xfer += $output->writeFieldBegin('message', TType::STRING, 1); 63 | $xfer += $output->writeString($message); 64 | $xfer += $output->writeFieldEnd(); 65 | } 66 | if ($code = $this->getCode()) { 67 | $xfer += $output->writeFieldBegin('type', TType::I32, 2); 68 | $xfer += $output->writeI32($code); 69 | $xfer += $output->writeFieldEnd(); 70 | } 71 | $xfer += $output->writeFieldStop(); 72 | $xfer += $output->writeStructEnd(); 73 | 74 | return $xfer; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Thrift/Exception/TException.php: -------------------------------------------------------------------------------- 1 | $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 | static $tmethod = array(TType::BOOL => 'Bool', 60 | TType::BYTE => 'Byte', 61 | TType::I16 => 'I16', 62 | TType::I32 => 'I32', 63 | TType::I64 => 'I64', 64 | TType::DOUBLE => 'Double', 65 | TType::STRING => 'String'); 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 | } 384 | -------------------------------------------------------------------------------- /Thrift/Exception/TProtocolException.php: -------------------------------------------------------------------------------- 1 | strictRead_ = $strictRead; 38 | $this->strictWrite_ = $strictWrite; 39 | } 40 | 41 | public function getProtocol($trans) 42 | { 43 | return new TBinaryProtocol($trans, $this->strictRead_, $this->strictWrite_); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Thrift/Factory/TCompactProtocolFactory.php: -------------------------------------------------------------------------------- 1 | p_ = $p; 35 | } 36 | 37 | public function write() 38 | { 39 | if ($this->first_) { 40 | $this->first_ = false; 41 | } else { 42 | $this->p_->getTransport()->write(TJSONProtocol::COMMA); 43 | } 44 | } 45 | 46 | public function read() 47 | { 48 | if ($this->first_) { 49 | $this->first_ = false; 50 | } else { 51 | $this->p_->readJSONSyntaxChar(TJSONProtocol::COMMA); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Thrift/Protocol/JSON/LookaheadReader.php: -------------------------------------------------------------------------------- 1 | p_ = $p; 34 | } 35 | 36 | public function read() 37 | { 38 | if ($this->hasData_) { 39 | $this->hasData_ = false; 40 | } else { 41 | $this->data_ = $this->p_->getTransport()->readAll(1); 42 | } 43 | 44 | return substr($this->data_, 0, 1); 45 | } 46 | 47 | public function peek() 48 | { 49 | if (!$this->hasData_) { 50 | $this->data_ = $this->p_->getTransport()->readAll(1); 51 | } 52 | 53 | $this->hasData_ = true; 54 | 55 | return substr($this->data_, 0, 1); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Thrift/Protocol/JSON/PairContext.php: -------------------------------------------------------------------------------- 1 | p_ = $p; 36 | } 37 | 38 | public function write() 39 | { 40 | if ($this->first_) { 41 | $this->first_ = false; 42 | $this->colon_ = true; 43 | } else { 44 | $this->p_->getTransport()->write($this->colon_ ? TJSONProtocol::COLON : TJSONProtocol::COMMA); 45 | $this->colon_ = !$this->colon_; 46 | } 47 | } 48 | 49 | public function read() 50 | { 51 | if ($this->first_) { 52 | $this->first_ = false; 53 | $this->colon_ = true; 54 | } else { 55 | $this->p_->readJSONSyntaxChar($this->colon_ ? TJSONProtocol::COLON : TJSONProtocol::COMMA); 56 | $this->colon_ = !$this->colon_; 57 | } 58 | } 59 | 60 | public function escapeNum() 61 | { 62 | return $this->colon_; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Thrift/Protocol/SimpleJSON/CollectionMapKeyException.php: -------------------------------------------------------------------------------- 1 | p_ = $p; 35 | } 36 | 37 | public function write() 38 | { 39 | if ($this->first_) { 40 | $this->first_ = false; 41 | } else { 42 | $this->p_->getTransport()->write(TSimpleJSONProtocol::COMMA); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Thrift/Protocol/SimpleJSON/MapContext.php: -------------------------------------------------------------------------------- 1 | isKey = !$this->isKey; 41 | } 42 | 43 | public function isMapKey() 44 | { 45 | // we want to coerce map keys to json strings regardless 46 | // of their type 47 | return $this->isKey; 48 | } 49 | } 50 | 51 | 52 | -------------------------------------------------------------------------------- /Thrift/Protocol/SimpleJSON/StructContext.php: -------------------------------------------------------------------------------- 1 | p_ = $p; 36 | } 37 | 38 | public function write() 39 | { 40 | if ($this->first_) { 41 | $this->first_ = false; 42 | $this->colon_ = true; 43 | } else { 44 | $this->p_->getTransport()->write( 45 | $this->colon_ ? 46 | TSimpleJSONProtocol::COLON : 47 | TSimpleJSONProtocol::COMMA 48 | ); 49 | $this->colon_ = !$this->colon_; 50 | } 51 | } 52 | } 53 | 54 | -------------------------------------------------------------------------------- /Thrift/Protocol/TBinaryProtocol.php: -------------------------------------------------------------------------------- 1 | strictRead_ = $strictRead; 45 | $this->strictWrite_ = $strictWrite; 46 | } 47 | 48 | public function writeMessageBegin($name, $type, $seqid) 49 | { 50 | if ($this->strictWrite_) { 51 | $version = self::VERSION_1 | $type; 52 | 53 | return 54 | $this->writeI32($version) + 55 | $this->writeString($name) + 56 | $this->writeI32($seqid); 57 | } else { 58 | return 59 | $this->writeString($name) + 60 | $this->writeByte($type) + 61 | $this->writeI32($seqid); 62 | } 63 | } 64 | 65 | public function writeMessageEnd() 66 | { 67 | return 0; 68 | } 69 | 70 | public function writeStructBegin($name) 71 | { 72 | return 0; 73 | } 74 | 75 | public function writeStructEnd() 76 | { 77 | return 0; 78 | } 79 | 80 | public function writeFieldBegin($fieldName, $fieldType, $fieldId) 81 | { 82 | return 83 | $this->writeByte($fieldType) + 84 | $this->writeI16($fieldId); 85 | } 86 | 87 | public function writeFieldEnd() 88 | { 89 | return 0; 90 | } 91 | 92 | public function writeFieldStop() 93 | { 94 | return 95 | $this->writeByte(TType::STOP); 96 | } 97 | 98 | public function writeMapBegin($keyType, $valType, $size) 99 | { 100 | return 101 | $this->writeByte($keyType) + 102 | $this->writeByte($valType) + 103 | $this->writeI32($size); 104 | } 105 | 106 | public function writeMapEnd() 107 | { 108 | return 0; 109 | } 110 | 111 | public function writeListBegin($elemType, $size) 112 | { 113 | return 114 | $this->writeByte($elemType) + 115 | $this->writeI32($size); 116 | } 117 | 118 | public function writeListEnd() 119 | { 120 | return 0; 121 | } 122 | 123 | public function writeSetBegin($elemType, $size) 124 | { 125 | return 126 | $this->writeByte($elemType) + 127 | $this->writeI32($size); 128 | } 129 | 130 | public function writeSetEnd() 131 | { 132 | return 0; 133 | } 134 | 135 | public function writeBool($value) 136 | { 137 | $data = pack('c', $value ? 1 : 0); 138 | $this->trans_->write($data, 1); 139 | 140 | return 1; 141 | } 142 | 143 | public function writeByte($value) 144 | { 145 | $data = pack('c', $value); 146 | $this->trans_->write($data, 1); 147 | 148 | return 1; 149 | } 150 | 151 | public function writeI16($value) 152 | { 153 | $data = pack('n', $value); 154 | $this->trans_->write($data, 2); 155 | 156 | return 2; 157 | } 158 | 159 | public function writeI32($value) 160 | { 161 | $data = pack('N', $value); 162 | $this->trans_->write($data, 4); 163 | 164 | return 4; 165 | } 166 | 167 | public function writeI64($value) 168 | { 169 | // If we are on a 32bit architecture we have to explicitly deal with 170 | // 64-bit twos-complement arithmetic since PHP wants to treat all ints 171 | // as signed and any int over 2^31 - 1 as a float 172 | if (PHP_INT_SIZE == 4) { 173 | $neg = $value < 0; 174 | 175 | if ($neg) { 176 | $value *= -1; 177 | } 178 | 179 | $hi = (int) ($value / 4294967296); 180 | $lo = (int) $value; 181 | 182 | if ($neg) { 183 | $hi = ~$hi; 184 | $lo = ~$lo; 185 | if (($lo & (int) 0xffffffff) == (int) 0xffffffff) { 186 | $lo = 0; 187 | $hi++; 188 | } else { 189 | $lo++; 190 | } 191 | } 192 | $data = pack('N2', $hi, $lo); 193 | 194 | } else { 195 | $hi = $value >> 32; 196 | $lo = $value & 0xFFFFFFFF; 197 | $data = pack('N2', $hi, $lo); 198 | } 199 | 200 | $this->trans_->write($data, 8); 201 | 202 | return 8; 203 | } 204 | 205 | public function writeDouble($value) 206 | { 207 | $data = pack('d', $value); 208 | $this->trans_->write(strrev($data), 8); 209 | 210 | return 8; 211 | } 212 | 213 | public function writeString($value) 214 | { 215 | $len = TStringFuncFactory::create()->strlen($value); 216 | $result = $this->writeI32($len); 217 | if ($len) { 218 | $this->trans_->write($value, $len); 219 | } 220 | 221 | return $result + $len; 222 | } 223 | 224 | public function readMessageBegin(&$name, &$type, &$seqid) 225 | { 226 | $result = $this->readI32($sz); 227 | if ($sz < 0) { 228 | $version = (int) ($sz & self::VERSION_MASK); 229 | if ($version != (int) self::VERSION_1) { 230 | throw new TProtocolException('Bad version identifier: '.$sz, TProtocolException::BAD_VERSION); 231 | } 232 | $type = $sz & 0x000000ff; 233 | $result += 234 | $this->readString($name) + 235 | $this->readI32($seqid); 236 | } else { 237 | if ($this->strictRead_) { 238 | throw new TProtocolException('No version identifier, old protocol client?', TProtocolException::BAD_VERSION); 239 | } else { 240 | // Handle pre-versioned input 241 | $name = $this->trans_->readAll($sz); 242 | $result += 243 | $sz + 244 | $this->readByte($type) + 245 | $this->readI32($seqid); 246 | } 247 | } 248 | 249 | return $result; 250 | } 251 | 252 | public function readMessageEnd() 253 | { 254 | return 0; 255 | } 256 | 257 | public function readStructBegin(&$name) 258 | { 259 | $name = ''; 260 | 261 | return 0; 262 | } 263 | 264 | public function readStructEnd() 265 | { 266 | return 0; 267 | } 268 | 269 | public function readFieldBegin(&$name, &$fieldType, &$fieldId) 270 | { 271 | $result = $this->readByte($fieldType); 272 | if ($fieldType == TType::STOP) { 273 | $fieldId = 0; 274 | 275 | return $result; 276 | } 277 | $result += $this->readI16($fieldId); 278 | 279 | return $result; 280 | } 281 | 282 | public function readFieldEnd() 283 | { 284 | return 0; 285 | } 286 | 287 | public function readMapBegin(&$keyType, &$valType, &$size) 288 | { 289 | return 290 | $this->readByte($keyType) + 291 | $this->readByte($valType) + 292 | $this->readI32($size); 293 | } 294 | 295 | public function readMapEnd() 296 | { 297 | return 0; 298 | } 299 | 300 | public function readListBegin(&$elemType, &$size) 301 | { 302 | return 303 | $this->readByte($elemType) + 304 | $this->readI32($size); 305 | } 306 | 307 | public function readListEnd() 308 | { 309 | return 0; 310 | } 311 | 312 | public function readSetBegin(&$elemType, &$size) 313 | { 314 | return 315 | $this->readByte($elemType) + 316 | $this->readI32($size); 317 | } 318 | 319 | public function readSetEnd() 320 | { 321 | return 0; 322 | } 323 | 324 | public function readBool(&$value) 325 | { 326 | $data = $this->trans_->readAll(1); 327 | $arr = unpack('c', $data); 328 | $value = $arr[1] == 1; 329 | 330 | return 1; 331 | } 332 | 333 | public function readByte(&$value) 334 | { 335 | $data = $this->trans_->readAll(1); 336 | $arr = unpack('c', $data); 337 | $value = $arr[1]; 338 | 339 | return 1; 340 | } 341 | 342 | public function readI16(&$value) 343 | { 344 | $data = $this->trans_->readAll(2); 345 | $arr = unpack('n', $data); 346 | $value = $arr[1]; 347 | if ($value > 0x7fff) { 348 | $value = 0 - (($value - 1) ^ 0xffff); 349 | } 350 | 351 | return 2; 352 | } 353 | 354 | public function readI32(&$value) 355 | { 356 | $data = $this->trans_->readAll(4); 357 | $arr = unpack('N', $data); 358 | $value = $arr[1]; 359 | if ($value > 0x7fffffff) { 360 | $value = 0 - (($value - 1) ^ 0xffffffff); 361 | } 362 | 363 | return 4; 364 | } 365 | 366 | public function readI64(&$value) 367 | { 368 | $data = $this->trans_->readAll(8); 369 | 370 | $arr = unpack('N2', $data); 371 | 372 | // If we are on a 32bit architecture we have to explicitly deal with 373 | // 64-bit twos-complement arithmetic since PHP wants to treat all ints 374 | // as signed and any int over 2^31 - 1 as a float 375 | if (PHP_INT_SIZE == 4) { 376 | 377 | $hi = $arr[1]; 378 | $lo = $arr[2]; 379 | $isNeg = $hi < 0; 380 | 381 | // Check for a negative 382 | if ($isNeg) { 383 | $hi = ~$hi & (int) 0xffffffff; 384 | $lo = ~$lo & (int) 0xffffffff; 385 | 386 | if ($lo == (int) 0xffffffff) { 387 | $hi++; 388 | $lo = 0; 389 | } else { 390 | $lo++; 391 | } 392 | } 393 | 394 | // Force 32bit words in excess of 2G to pe positive - we deal wigh sign 395 | // explicitly below 396 | 397 | if ($hi & (int) 0x80000000) { 398 | $hi &= (int) 0x7fffffff; 399 | $hi += 0x80000000; 400 | } 401 | 402 | if ($lo & (int) 0x80000000) { 403 | $lo &= (int) 0x7fffffff; 404 | $lo += 0x80000000; 405 | } 406 | 407 | $value = $hi * 4294967296 + $lo; 408 | 409 | if ($isNeg) { 410 | $value = 0 - $value; 411 | } 412 | } else { 413 | 414 | // Upcast negatives in LSB bit 415 | if ($arr[2] & 0x80000000) { 416 | $arr[2] = $arr[2] & 0xffffffff; 417 | } 418 | 419 | // Check for a negative 420 | if ($arr[1] & 0x80000000) { 421 | $arr[1] = $arr[1] & 0xffffffff; 422 | $arr[1] = $arr[1] ^ 0xffffffff; 423 | $arr[2] = $arr[2] ^ 0xffffffff; 424 | $value = 0 - $arr[1]*4294967296 - $arr[2] - 1; 425 | } else { 426 | $value = $arr[1]*4294967296 + $arr[2]; 427 | } 428 | } 429 | 430 | return 8; 431 | } 432 | 433 | public function readDouble(&$value) 434 | { 435 | $data = strrev($this->trans_->readAll(8)); 436 | $arr = unpack('d', $data); 437 | $value = $arr[1]; 438 | 439 | return 8; 440 | } 441 | 442 | public function readString(&$value) 443 | { 444 | $result = $this->readI32($len); 445 | if ($len) { 446 | $value = $this->trans_->readAll($len); 447 | } else { 448 | $value = ''; 449 | } 450 | 451 | return $result + $len; 452 | } 453 | } 454 | -------------------------------------------------------------------------------- /Thrift/Protocol/TBinaryProtocolAccelerated.php: -------------------------------------------------------------------------------- 1 | strictRead_; 60 | } 61 | public function isStrictWrite() 62 | { 63 | return $this->strictWrite_; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Thrift/Protocol/TMultiplexedProtocol.php: -------------------------------------------------------------------------------- 1 | TMultiplexedProtocol is a protocol-independent concrete decorator 29 | * that allows a Thrift client to communicate with a multiplexing Thrift server, 30 | * by prepending the service name to the function name during function calls. 31 | * 32 | * @package Thrift\Protocol 33 | */ 34 | class TMultiplexedProtocol extends TProtocolDecorator 35 | { 36 | /** 37 | * Separator between service name and function name. 38 | * Should be the same as used at multiplexed Thrift server. 39 | * 40 | * @var string 41 | */ 42 | const SEPARATOR = ":"; 43 | 44 | /** 45 | * The name of service. 46 | * 47 | * @var string 48 | */ 49 | private $serviceName_; 50 | 51 | /** 52 | * Constructor of TMultiplexedProtocol class. 53 | * 54 | * Wrap the specified protocol, allowing it to be used to communicate with a 55 | * multiplexing server. The $serviceName is required as it is 56 | * prepended to the message header so that the multiplexing server can broker 57 | * the function call to the proper service. 58 | * 59 | * @param TProtocol $protocol 60 | * @param string $serviceName The name of service. 61 | */ 62 | public function __construct(TProtocol $protocol, $serviceName) 63 | { 64 | parent::__construct($protocol); 65 | $this->serviceName_ = $serviceName; 66 | } 67 | 68 | /** 69 | * Writes the message header. 70 | * Prepends the service name to the function name, separated by TMultiplexedProtocol::SEPARATOR. 71 | * 72 | * @param string $name Function name. 73 | * @param int $type Message type. 74 | * @param int $seqid The sequence id of this message. 75 | */ 76 | public function writeMessageBegin($name, $type, $seqid) 77 | { 78 | if ($type == TMessageType::CALL || $type == TMessageType::ONEWAY) { 79 | $nameWithService = $this->serviceName_ . self::SEPARATOR . $name; 80 | parent::writeMessageBegin($nameWithService, $type, $seqid); 81 | } else { 82 | parent::writeMessageBegin($name, $type, $seqid); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Thrift/Protocol/TProtocol.php: -------------------------------------------------------------------------------- 1 | trans_ = $trans; 46 | } 47 | 48 | /** 49 | * Accessor for transport 50 | * 51 | * @return TTransport 52 | */ 53 | public function getTransport() 54 | { 55 | return $this->trans_; 56 | } 57 | 58 | /** 59 | * Writes the message header 60 | * 61 | * @param string $name Function name 62 | * @param int $type message type TMessageType::CALL or TMessageType::REPLY 63 | * @param int $seqid The sequence id of this message 64 | */ 65 | abstract public function writeMessageBegin($name, $type, $seqid); 66 | 67 | /** 68 | * Close the message 69 | */ 70 | abstract public function writeMessageEnd(); 71 | 72 | /** 73 | * Writes a struct header. 74 | * 75 | * @param string $name Struct name 76 | * @throws TException on write error 77 | * @return int How many bytes written 78 | */ 79 | abstract public function writeStructBegin($name); 80 | 81 | /** 82 | * Close a struct. 83 | * 84 | * @throws TException on write error 85 | * @return int How many bytes written 86 | */ 87 | abstract public function writeStructEnd(); 88 | 89 | /* 90 | * Starts a field. 91 | * 92 | * @param string $name Field name 93 | * @param int $type Field type 94 | * @param int $fid Field id 95 | * @throws TException on write error 96 | * @return int How many bytes written 97 | */ 98 | abstract public function writeFieldBegin($fieldName, $fieldType, $fieldId); 99 | 100 | abstract public function writeFieldEnd(); 101 | 102 | abstract public function writeFieldStop(); 103 | 104 | abstract public function writeMapBegin($keyType, $valType, $size); 105 | 106 | abstract public function writeMapEnd(); 107 | 108 | abstract public function writeListBegin($elemType, $size); 109 | 110 | abstract public function writeListEnd(); 111 | 112 | abstract public function writeSetBegin($elemType, $size); 113 | 114 | abstract public function writeSetEnd(); 115 | 116 | abstract public function writeBool($bool); 117 | 118 | abstract public function writeByte($byte); 119 | 120 | abstract public function writeI16($i16); 121 | 122 | abstract public function writeI32($i32); 123 | 124 | abstract public function writeI64($i64); 125 | 126 | abstract public function writeDouble($dub); 127 | 128 | abstract public function writeString($str); 129 | 130 | /** 131 | * Reads the message header 132 | * 133 | * @param string $name Function name 134 | * @param int $type message type TMessageType::CALL or TMessageType::REPLY 135 | * @parem int $seqid The sequence id of this message 136 | */ 137 | abstract public function readMessageBegin(&$name, &$type, &$seqid); 138 | 139 | /** 140 | * Read the close of message 141 | */ 142 | abstract public function readMessageEnd(); 143 | 144 | abstract public function readStructBegin(&$name); 145 | 146 | abstract public function readStructEnd(); 147 | 148 | abstract public function readFieldBegin(&$name, &$fieldType, &$fieldId); 149 | 150 | abstract public function readFieldEnd(); 151 | 152 | abstract public function readMapBegin(&$keyType, &$valType, &$size); 153 | 154 | abstract public function readMapEnd(); 155 | 156 | abstract public function readListBegin(&$elemType, &$size); 157 | 158 | abstract public function readListEnd(); 159 | 160 | abstract public function readSetBegin(&$elemType, &$size); 161 | 162 | abstract public function readSetEnd(); 163 | 164 | abstract public function readBool(&$bool); 165 | 166 | abstract public function readByte(&$byte); 167 | 168 | abstract public function readI16(&$i16); 169 | 170 | abstract public function readI32(&$i32); 171 | 172 | abstract public function readI64(&$i64); 173 | 174 | abstract public function readDouble(&$dub); 175 | 176 | abstract public function readString(&$str); 177 | 178 | /** 179 | * The skip function is a utility to parse over unrecognized date without 180 | * causing corruption. 181 | * 182 | * @param TType $type What type is it 183 | */ 184 | public function skip($type) 185 | { 186 | switch ($type) { 187 | case TType::BOOL: 188 | return $this->readBool($bool); 189 | case TType::BYTE: 190 | return $this->readByte($byte); 191 | case TType::I16: 192 | return $this->readI16($i16); 193 | case TType::I32: 194 | return $this->readI32($i32); 195 | case TType::I64: 196 | return $this->readI64($i64); 197 | case TType::DOUBLE: 198 | return $this->readDouble($dub); 199 | case TType::STRING: 200 | return $this->readString($str); 201 | case TType::STRUCT: 202 | { 203 | $result = $this->readStructBegin($name); 204 | while (true) { 205 | $result += $this->readFieldBegin($name, $ftype, $fid); 206 | if ($ftype == TType::STOP) { 207 | break; 208 | } 209 | $result += $this->skip($ftype); 210 | $result += $this->readFieldEnd(); 211 | } 212 | $result += $this->readStructEnd(); 213 | 214 | return $result; 215 | } 216 | case TType::MAP: 217 | { 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 | { 229 | $result = $this->readSetBegin($elemType, $size); 230 | for ($i = 0; $i < $size; $i++) { 231 | $result += $this->skip($elemType); 232 | } 233 | $result += $this->readSetEnd(); 234 | 235 | return $result; 236 | } 237 | case TType::LST: 238 | { 239 | $result = $this->readListBegin($elemType, $size); 240 | for ($i = 0; $i < $size; $i++) { 241 | $result += $this->skip($elemType); 242 | } 243 | $result += $this->readListEnd(); 244 | 245 | return $result; 246 | } 247 | default: 248 | throw new TProtocolException('Unknown field type: '.$type, 249 | TProtocolException::INVALID_DATA); 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 | case TType::STRUCT: 283 | { 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 | { 303 | // Ktype 304 | $data = $itrans->readAll(1); 305 | $arr = unpack('c', $data); 306 | $ktype = $arr[1]; 307 | // Vtype 308 | $data = $itrans->readAll(1); 309 | $arr = unpack('c', $data); 310 | $vtype = $arr[1]; 311 | // Size 312 | $data = $itrans->readAll(4); 313 | $arr = unpack('N', $data); 314 | $size = $arr[1]; 315 | if ($size > 0x7fffffff) { 316 | $size = 0 - (($size - 1) ^ 0xffffffff); 317 | } 318 | $result = 6; 319 | for ($i = 0; $i < $size; $i++) { 320 | $result += self::skipBinary($itrans, $ktype); 321 | $result += self::skipBinary($itrans, $vtype); 322 | } 323 | 324 | return $result; 325 | } 326 | case TType::SET: 327 | case TType::LST: 328 | { 329 | // Vtype 330 | $data = $itrans->readAll(1); 331 | $arr = unpack('c', $data); 332 | $vtype = $arr[1]; 333 | // Size 334 | $data = $itrans->readAll(4); 335 | $arr = unpack('N', $data); 336 | $size = $arr[1]; 337 | if ($size > 0x7fffffff) { 338 | $size = 0 - (($size - 1) ^ 0xffffffff); 339 | } 340 | $result = 5; 341 | for ($i = 0; $i < $size; $i++) { 342 | $result += self::skipBinary($itrans, $vtype); 343 | } 344 | 345 | return $result; 346 | } 347 | default: 348 | throw new TProtocolException('Unknown field type: '.$type, 349 | TProtocolException::INVALID_DATA); 350 | } 351 | } 352 | } 353 | -------------------------------------------------------------------------------- /Thrift/Protocol/TProtocolDecorator.php: -------------------------------------------------------------------------------- 1 | TProtocolDecorator forwards all requests to an enclosed 28 | * TProtocol instance, providing a way to author concise 29 | * concrete decorator subclasses. While it has no abstract methods, it 30 | * is marked abstract as a reminder that by itself, it does not modify 31 | * the behaviour of the enclosed TProtocol. 32 | * 33 | * @package Thrift\Protocol 34 | */ 35 | abstract class TProtocolDecorator extends TProtocol 36 | { 37 | /** 38 | * Instance of protocol, to which all operations will be forwarded. 39 | * 40 | * @var TProtocol 41 | */ 42 | private $concreteProtocol_; 43 | 44 | /** 45 | * Constructor of TProtocolDecorator class. 46 | * Encloses the specified protocol. 47 | * 48 | * @param TProtocol $protocol All operations will be forward to this instance. Must be non-null. 49 | */ 50 | protected function __construct(TProtocol $protocol) 51 | { 52 | parent::__construct($protocol->getTransport()); 53 | $this->concreteProtocol_ = $protocol; 54 | } 55 | 56 | /** 57 | * Writes the message header. 58 | * 59 | * @param string $name Function name 60 | * @param int $type message type TMessageType::CALL or TMessageType::REPLY 61 | * @param int $seqid The sequence id of this message 62 | */ 63 | public function writeMessageBegin($name, $type, $seqid) 64 | { 65 | return $this->concreteProtocol_->writeMessageBegin($name, $type, $seqid); 66 | } 67 | 68 | /** 69 | * Closes the message. 70 | */ 71 | public function writeMessageEnd() 72 | { 73 | return $this->concreteProtocol_->writeMessageEnd(); 74 | } 75 | 76 | /** 77 | * Writes a struct header. 78 | * 79 | * @param string $name Struct name 80 | * 81 | * @throws TException on write error 82 | * @return int How many bytes written 83 | */ 84 | public function writeStructBegin($name) 85 | { 86 | return $this->concreteProtocol_->writeStructBegin($name); 87 | } 88 | 89 | /** 90 | * Close a struct. 91 | * 92 | * @throws TException on write error 93 | * @return int How many bytes written 94 | */ 95 | public function writeStructEnd() 96 | { 97 | return $this->concreteProtocol_->writeStructEnd(); 98 | } 99 | 100 | public function writeFieldBegin($fieldName, $fieldType, $fieldId) 101 | { 102 | return $this->concreteProtocol_->writeFieldBegin($fieldName, $fieldType, $fieldId); 103 | } 104 | 105 | public function writeFieldEnd() 106 | { 107 | return $this->concreteProtocol_->writeFieldEnd(); 108 | } 109 | 110 | public function writeFieldStop() 111 | { 112 | return $this->concreteProtocol_->writeFieldStop(); 113 | } 114 | 115 | public function writeMapBegin($keyType, $valType, $size) 116 | { 117 | return $this->concreteProtocol_->writeMapBegin($keyType, $valType, $size); 118 | } 119 | 120 | public function writeMapEnd() 121 | { 122 | return $this->concreteProtocol_->writeMapEnd(); 123 | } 124 | 125 | public function writeListBegin($elemType, $size) 126 | { 127 | return $this->concreteProtocol_->writeListBegin($elemType, $size); 128 | } 129 | 130 | public function writeListEnd() 131 | { 132 | return $this->concreteProtocol_->writeListEnd(); 133 | } 134 | 135 | public function writeSetBegin($elemType, $size) 136 | { 137 | return $this->concreteProtocol_->writeSetBegin($elemType, $size); 138 | } 139 | 140 | public function writeSetEnd() 141 | { 142 | return $this->concreteProtocol_->writeSetEnd(); 143 | } 144 | 145 | public function writeBool($bool) 146 | { 147 | return $this->concreteProtocol_->writeBool($bool); 148 | } 149 | 150 | public function writeByte($byte) 151 | { 152 | return $this->concreteProtocol_->writeByte($byte); 153 | } 154 | 155 | public function writeI16($i16) 156 | { 157 | return $this->concreteProtocol_->writeI16($i16); 158 | } 159 | 160 | public function writeI32($i32) 161 | { 162 | return $this->concreteProtocol_->writeI32($i32); 163 | } 164 | 165 | public function writeI64($i64) 166 | { 167 | return $this->concreteProtocol_->writeI64($i64); 168 | } 169 | 170 | public function writeDouble($dub) 171 | { 172 | return $this->concreteProtocol_->writeDouble($dub); 173 | } 174 | 175 | public function writeString($str) 176 | { 177 | return $this->concreteProtocol_->writeString($str); 178 | } 179 | 180 | /** 181 | * Reads the message header 182 | * 183 | * @param string $name Function name 184 | * @param int $type message type TMessageType::CALL or TMessageType::REPLY 185 | * @param int $seqid The sequence id of this message 186 | */ 187 | public function readMessageBegin(&$name, &$type, &$seqid) 188 | { 189 | return $this->concreteProtocol_->readMessageBegin($name, $type, $seqid); 190 | } 191 | 192 | /** 193 | * Read the close of message 194 | */ 195 | public function readMessageEnd() 196 | { 197 | return $this->concreteProtocol_->readMessageEnd(); 198 | } 199 | 200 | public function readStructBegin(&$name) 201 | { 202 | return $this->concreteProtocol_->readStructBegin($name); 203 | } 204 | 205 | public function readStructEnd() 206 | { 207 | return $this->concreteProtocol_->readStructEnd(); 208 | } 209 | 210 | public function readFieldBegin(&$name, &$fieldType, &$fieldId) 211 | { 212 | return $this->concreteProtocol_->readFieldBegin($name, $fieldType, $fieldId); 213 | } 214 | 215 | public function readFieldEnd() 216 | { 217 | return $this->concreteProtocol_->readFieldEnd(); 218 | } 219 | 220 | public function readMapBegin(&$keyType, &$valType, &$size) 221 | { 222 | $this->concreteProtocol_->readMapBegin($keyType, $valType, $size); 223 | } 224 | 225 | public function readMapEnd() 226 | { 227 | return $this->concreteProtocol_->readMapEnd(); 228 | } 229 | 230 | public function readListBegin(&$elemType, &$size) 231 | { 232 | $this->concreteProtocol_->readListBegin($elemType, $size); 233 | } 234 | 235 | public function readListEnd() 236 | { 237 | return $this->concreteProtocol_->readListEnd(); 238 | } 239 | 240 | public function readSetBegin(&$elemType, &$size) 241 | { 242 | return $this->concreteProtocol_->readSetBegin($elemType, $size); 243 | } 244 | 245 | public function readSetEnd() 246 | { 247 | return $this->concreteProtocol_->readSetEnd(); 248 | } 249 | 250 | public function readBool(&$bool) 251 | { 252 | return $this->concreteProtocol_->readBool($bool); 253 | } 254 | 255 | public function readByte(&$byte) 256 | { 257 | return $this->concreteProtocol_->readByte($byte); 258 | } 259 | 260 | public function readI16(&$i16) 261 | { 262 | return $this->concreteProtocol_->readI16($i16); 263 | } 264 | 265 | public function readI32(&$i32) 266 | { 267 | return $this->concreteProtocol_->readI32($i32); 268 | } 269 | 270 | public function readI64(&$i64) 271 | { 272 | return $this->concreteProtocol_->readI64($i64); 273 | } 274 | 275 | public function readDouble(&$dub) 276 | { 277 | return $this->concreteProtocol_->readDouble($dub); 278 | } 279 | 280 | public function readString(&$str) 281 | { 282 | return $this->concreteProtocol_->readString($str); 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /Thrift/Protocol/TSimpleJSONProtocol.php: -------------------------------------------------------------------------------- 1 | writeContextStack_[] = $this->writeContext_; 59 | $this->writeContext_ = $c; 60 | } 61 | 62 | /** 63 | * Pop the last write context off the stack 64 | */ 65 | protected function popWriteContext() { 66 | $this->writeContext_ = array_pop($this->writeContextStack_); 67 | } 68 | 69 | /** 70 | * Used to make sure that we are not encountering a map whose keys are containers 71 | */ 72 | protected function assertContextIsNotMapKey($invalidKeyType) { 73 | if ($this->writeContext_->isMapKey()) { 74 | throw new CollectionMapKeyException( 75 | "Cannot serialize a map with keys that are of type " . 76 | $invalidKeyType 77 | ); 78 | } 79 | } 80 | 81 | private function writeJSONString($b) 82 | { 83 | $this->writeContext_->write(); 84 | 85 | $this->trans_->write(json_encode((string)$b)); 86 | } 87 | 88 | private function writeJSONInteger($num) 89 | { 90 | $isMapKey = $this->writeContext_->isMapKey(); 91 | 92 | $this->writeContext_->write(); 93 | 94 | if ($isMapKey) { 95 | $this->trans_->write(self::QUOTE); 96 | } 97 | 98 | $this->trans_->write((int)$num); 99 | 100 | if ($isMapKey) { 101 | $this->trans_->write(self::QUOTE); 102 | } 103 | } 104 | 105 | private function writeJSONDouble($num) 106 | { 107 | $isMapKey = $this->writeContext_->isMapKey(); 108 | 109 | $this->writeContext_->write(); 110 | 111 | if ($isMapKey) { 112 | $this->trans_->write(self::QUOTE); 113 | } 114 | 115 | $this->trans_->write(json_encode((float)$num)); 116 | 117 | if ($isMapKey) { 118 | $this->trans_->write(self::QUOTE); 119 | } 120 | } 121 | 122 | /** 123 | * Constructor 124 | */ 125 | public function __construct($trans) 126 | { 127 | parent::__construct($trans); 128 | $this->writeContext_ = new Context(); 129 | } 130 | 131 | /** 132 | * Writes the message header 133 | * 134 | * @param string $name Function name 135 | * @param int $type message type TMessageType::CALL or TMessageType::REPLY 136 | * @param int $seqid The sequence id of this message 137 | */ 138 | public function writeMessageBegin($name, $type, $seqid) 139 | { 140 | $this->trans_->write(self::LBRACKET); 141 | $this->pushWriteContext(new ListContext($this)); 142 | $this->writeJSONString($name); 143 | $this->writeJSONInteger($type); 144 | $this->writeJSONInteger($seqid); 145 | } 146 | 147 | /** 148 | * Close the message 149 | */ 150 | public function writeMessageEnd() 151 | { 152 | $this->popWriteContext(); 153 | $this->trans_->write(self::RBRACKET); 154 | } 155 | 156 | /** 157 | * Writes a struct header. 158 | * 159 | * @param string $name Struct name 160 | */ 161 | public function writeStructBegin($name) 162 | { 163 | $this->writeContext_->write(); 164 | $this->trans_->write(self::LBRACE); 165 | $this->pushWriteContext(new StructContext($this)); 166 | } 167 | 168 | /** 169 | * Close a struct. 170 | */ 171 | public function writeStructEnd() 172 | { 173 | $this->popWriteContext(); 174 | $this->trans_->write(self::RBRACE); 175 | } 176 | 177 | public function writeFieldBegin($fieldName, $fieldType, $fieldId) 178 | { 179 | $this->writeJSONString($fieldName); 180 | } 181 | 182 | public function writeFieldEnd() 183 | { 184 | } 185 | 186 | public function writeFieldStop() 187 | { 188 | } 189 | 190 | public function writeMapBegin($keyType, $valType, $size) 191 | { 192 | $this->assertContextIsNotMapKey(self::NAME_MAP); 193 | $this->writeContext_->write(); 194 | $this->trans_->write(self::LBRACE); 195 | $this->pushWriteContext(new MapContext($this)); 196 | } 197 | 198 | public function writeMapEnd() 199 | { 200 | $this->popWriteContext(); 201 | $this->trans_->write(self::RBRACE); 202 | } 203 | 204 | public function writeListBegin($elemType, $size) 205 | { 206 | $this->assertContextIsNotMapKey(self::NAME_LIST); 207 | $this->writeContext_->write(); 208 | $this->trans_->write(self::LBRACKET); 209 | $this->pushWriteContext(new ListContext($this)); 210 | // No metadata! 211 | } 212 | 213 | public function writeListEnd() 214 | { 215 | $this->popWriteContext(); 216 | $this->trans_->write(self::RBRACKET); 217 | } 218 | 219 | public function writeSetBegin($elemType, $size) 220 | { 221 | $this->assertContextIsNotMapKey(self::NAME_SET); 222 | $this->writeContext_->write(); 223 | $this->trans_->write(self::LBRACKET); 224 | $this->pushWriteContext(new ListContext($this)); 225 | // No metadata! 226 | } 227 | 228 | public function writeSetEnd() 229 | { 230 | $this->popWriteContext(); 231 | $this->trans_->write(self::RBRACKET); 232 | } 233 | 234 | public function writeBool($bool) 235 | { 236 | $this->writeJSONInteger($bool ? 1 : 0); 237 | } 238 | 239 | public function writeByte($byte) 240 | { 241 | $this->writeJSONInteger($byte); 242 | } 243 | 244 | public function writeI16($i16) 245 | { 246 | $this->writeJSONInteger($i16); 247 | } 248 | 249 | public function writeI32($i32) 250 | { 251 | $this->writeJSONInteger($i32); 252 | } 253 | 254 | public function writeI64($i64) 255 | { 256 | $this->writeJSONInteger($i64); 257 | } 258 | 259 | public function writeDouble($dub) 260 | { 261 | $this->writeJSONDouble($dub); 262 | } 263 | 264 | public function writeString($str) 265 | { 266 | $this->writeJSONString($str); 267 | } 268 | 269 | /** 270 | * Reading methods. 271 | * 272 | * simplejson is not meant to be read back into thrift 273 | * - see http://wiki.apache.org/thrift/ThriftUsageJava 274 | * - use JSON instead 275 | */ 276 | 277 | public function readMessageBegin(&$name, &$type, &$seqid) 278 | { 279 | throw new TException("Not implemented"); 280 | } 281 | 282 | public function readMessageEnd() 283 | { 284 | throw new TException("Not implemented"); 285 | } 286 | 287 | public function readStructBegin(&$name) 288 | { 289 | throw new TException("Not implemented"); 290 | } 291 | 292 | public function readStructEnd() 293 | { 294 | throw new TException("Not implemented"); 295 | } 296 | 297 | public function readFieldBegin(&$name, &$fieldType, &$fieldId) 298 | { 299 | throw new TException("Not implemented"); 300 | } 301 | 302 | public function readFieldEnd() 303 | { 304 | throw new TException("Not implemented"); 305 | } 306 | 307 | public function readMapBegin(&$keyType, &$valType, &$size) 308 | { 309 | throw new TException("Not implemented"); 310 | } 311 | 312 | public function readMapEnd() 313 | { 314 | throw new TException("Not implemented"); 315 | } 316 | 317 | public function readListBegin(&$elemType, &$size) 318 | { 319 | throw new TException("Not implemented"); 320 | } 321 | 322 | public function readListEnd() 323 | { 324 | throw new TException("Not implemented"); 325 | } 326 | 327 | public function readSetBegin(&$elemType, &$size) 328 | { 329 | throw new TException("Not implemented"); 330 | } 331 | 332 | public function readSetEnd() 333 | { 334 | throw new TException("Not implemented"); 335 | } 336 | 337 | public function readBool(&$bool) 338 | { 339 | throw new TException("Not implemented"); 340 | } 341 | 342 | public function readByte(&$byte) 343 | { 344 | throw new TException("Not implemented"); 345 | } 346 | 347 | public function readI16(&$i16) 348 | { 349 | throw new TException("Not implemented"); 350 | } 351 | 352 | public function readI32(&$i32) 353 | { 354 | throw new TException("Not implemented"); 355 | } 356 | 357 | public function readI64(&$i64) 358 | { 359 | throw new TException("Not implemented"); 360 | } 361 | 362 | public function readDouble(&$dub) 363 | { 364 | throw new TException("Not implemented"); 365 | } 366 | 367 | public function readString(&$str) 368 | { 369 | throw new TException("Not implemented"); 370 | } 371 | } 372 | -------------------------------------------------------------------------------- /Thrift/Serializer/TBinarySerializer.php: -------------------------------------------------------------------------------- 1 | getName(), 47 | TMessageType::REPLY, $object, 48 | 0, $protocol->isStrictWrite()); 49 | 50 | $protocol->readMessageBegin($unused_name, $unused_type, 51 | $unused_seqid); 52 | } else { 53 | $object->write($protocol); 54 | } 55 | $protocol->getTransport()->flush(); 56 | 57 | return $transport->getBuffer(); 58 | } 59 | 60 | public static function deserialize($string_object, $class_name) 61 | { 62 | $transport = new TMemoryBuffer(); 63 | $protocol = new TBinaryProtocolAccelerated($transport); 64 | if (function_exists('thrift_protocol_read_binary')) { 65 | // NOTE (t.heintz) TBinaryProtocolAccelerated internally wraps our TMemoryBuffer in a 66 | // TBufferedTransport, so we have to retrieve it again or risk losing data when writing 67 | // less than 512 bytes to the transport (see the comment there as well). 68 | // @see THRIFT-1579 69 | $protocol->writeMessageBegin('', TMessageType::REPLY, 0); 70 | $protocolTransport = $protocol->getTransport(); 71 | $protocolTransport->write($string_object); 72 | $protocolTransport->flush(); 73 | 74 | return thrift_protocol_read_binary($protocol, $class_name, 75 | $protocol->isStrictRead()); 76 | } else { 77 | $transport->write($string_object); 78 | $object = new $class_name(); 79 | $object->read($protocol); 80 | 81 | return $object; 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /Thrift/Server/TForkingServer.php: -------------------------------------------------------------------------------- 1 | transport_->listen(); 40 | 41 | while (!$this->stop_) { 42 | try { 43 | $transport = $this->transport_->accept(); 44 | 45 | if ($transport != null) { 46 | $pid = pcntl_fork(); 47 | 48 | if ($pid > 0) { 49 | $this->handleParent($transport, $pid); 50 | } elseif ($pid === 0) { 51 | $this->handleChild($transport); 52 | } else { 53 | throw new TException('Failed to fork'); 54 | } 55 | } 56 | } catch (TTransportException $e) { } 57 | 58 | $this->collectChildren(); 59 | } 60 | } 61 | 62 | /** 63 | * Code run by the parent 64 | * 65 | * @param TTransport $transport 66 | * @param int $pid 67 | * @return void 68 | */ 69 | private function handleParent(TTransport $transport, $pid) 70 | { 71 | $this->children_[$pid] = $transport; 72 | } 73 | 74 | /** 75 | * Code run by the child. 76 | * 77 | * @param TTransport $transport 78 | * @return void 79 | */ 80 | private function handleChild(TTransport $transport) 81 | { 82 | try { 83 | $inputTransport = $this->inputTransportFactory_->getTransport($transport); 84 | $outputTransport = $this->outputTransportFactory_->getTransport($transport); 85 | $inputProtocol = $this->inputProtocolFactory_->getProtocol($inputTransport); 86 | $outputProtocol = $this->outputProtocolFactory_->getProtocol($outputTransport); 87 | while ($this->processor_->process($inputProtocol, $outputProtocol)) { } 88 | @$transport->close(); 89 | } catch (TTransportException $e) { } 90 | 91 | exit(0); 92 | } 93 | 94 | /** 95 | * Collects any children we may have 96 | * 97 | * @return void 98 | */ 99 | private function collectChildren() 100 | { 101 | foreach ($this->children_ as $pid => $transport) { 102 | if (pcntl_waitpid($pid, $status, WNOHANG) > 0) { 103 | unset($this->children_[$pid]); 104 | if ($transport) @$transport->close(); 105 | } 106 | } 107 | } 108 | 109 | /** 110 | * Stops the server running. Kills the transport 111 | * and then stops the main serving loop 112 | * 113 | * @return void 114 | */ 115 | public function stop() 116 | { 117 | $this->transport_->close(); 118 | $this->stop_ = true; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /Thrift/Server/TServer.php: -------------------------------------------------------------------------------- 1 | processor_ = $processor; 76 | $this->transport_ = $transport; 77 | $this->inputTransportFactory_ = $inputTransportFactory; 78 | $this->outputTransportFactory_ = $outputTransportFactory; 79 | $this->inputProtocolFactory_ = $inputProtocolFactory; 80 | $this->outputProtocolFactory_ = $outputProtocolFactory; 81 | } 82 | 83 | /** 84 | * Serves the server. This should never return 85 | * unless a problem permits it to do so or it 86 | * is interrupted intentionally 87 | * 88 | * @abstract 89 | * @return void 90 | */ 91 | abstract public function serve(); 92 | 93 | /** 94 | * Stops the server serving 95 | * 96 | * @abstract 97 | * @return void 98 | */ 99 | abstract public function stop(); 100 | } 101 | -------------------------------------------------------------------------------- /Thrift/Server/TServerSocket.php: -------------------------------------------------------------------------------- 1 | host_ = $host; 52 | $this->port_ = $port; 53 | } 54 | 55 | /** 56 | * Sets the accept timeout 57 | * 58 | * @param int $acceptTimeout 59 | * @return void 60 | */ 61 | public function setAcceptTimeout($acceptTimeout) 62 | { 63 | $this->acceptTimeout_ = $acceptTimeout; 64 | } 65 | 66 | /** 67 | * Opens a new socket server handle 68 | * 69 | * @return void 70 | */ 71 | public function listen() 72 | { 73 | $this->listener_ = stream_socket_server('tcp://' . $this->host_ . ':' . $this->port_); 74 | } 75 | 76 | /** 77 | * Closes the socket server handle 78 | * 79 | * @return void 80 | */ 81 | public function close() 82 | { 83 | @fclose($this->listener_); 84 | $this->listener_ = null; 85 | } 86 | 87 | /** 88 | * Implementation of accept. If not client is accepted in the given time 89 | * 90 | * @return TSocket 91 | */ 92 | protected function acceptImpl() 93 | { 94 | $handle = @stream_socket_accept($this->listener_, $this->acceptTimeout_ / 1000.0); 95 | if(!$handle) return null; 96 | 97 | $socket = new TSocket(); 98 | $socket->setHandle($handle); 99 | 100 | return $socket; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /Thrift/Server/TServerTransport.php: -------------------------------------------------------------------------------- 1 | acceptImpl(); 49 | 50 | if ($transport == null) { 51 | throw new TTransportException("accept() may not return NULL"); 52 | } 53 | 54 | return $transport; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Thrift/Server/TSimpleServer.php: -------------------------------------------------------------------------------- 1 | transport_->listen(); 31 | 32 | while (!$this->stop_) { 33 | try { 34 | $transport = $this->transport_->accept(); 35 | 36 | if ($transport != null) { 37 | $inputTransport = $this->inputTransportFactory_->getTransport($transport); 38 | $outputTransport = $this->outputTransportFactory_->getTransport($transport); 39 | $inputProtocol = $this->inputProtocolFactory_->getProtocol($inputTransport); 40 | $outputProtocol = $this->outputProtocolFactory_->getProtocol($outputTransport); 41 | while ($this->processor_->process($inputProtocol, $outputProtocol)) { } 42 | } 43 | } catch (TTransportException $e) { } 44 | } 45 | } 46 | 47 | /** 48 | * Stops the server running. Kills the transport 49 | * and then stops the main serving loop 50 | * 51 | * @return void 52 | */ 53 | public function stop() 54 | { 55 | $this->transport_->close(); 56 | $this->stop_ = true; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Thrift/StringFunc/Core.php: -------------------------------------------------------------------------------- 1 | strlen($str) - $start; 37 | } 38 | 39 | return mb_substr($str, $start, $length, '8bit'); 40 | } 41 | 42 | public function strlen($str) 43 | { 44 | return mb_strlen($str, '8bit'); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Thrift/StringFunc/TStringFunc.php: -------------------------------------------------------------------------------- 1 | TMultiplexedProcessor is a Processor allowing 33 | * a single TServer to provide multiple services. 34 | * 35 | *

To do so, you instantiate the processor and then register additional 36 | * processors with it, as shown in the following example:

37 | * 38 | *
39 | * $processor = new TMultiplexedProcessor(); 40 | * 41 | * processor->registerProcessor( 42 | * "Calculator", 43 | * new \tutorial\CalculatorProcessor(new CalculatorHandler())); 44 | * 45 | * processor->registerProcessor( 46 | * "WeatherReport", 47 | * new \tutorial\WeatherReportProcessor(new WeatherReportHandler())); 48 | * 49 | * $processor->process($protocol, $protocol); 50 | *
51 | */ 52 | 53 | class TMultiplexedProcessor 54 | { 55 | private $serviceProcessorMap_; 56 | 57 | /** 58 | * 'Register' a service with this TMultiplexedProcessor. This 59 | * allows us to broker requests to individual services by using the service 60 | * name to select them at request time. 61 | * 62 | * @param serviceName Name of a service, has to be identical to the name 63 | * declared in the Thrift IDL, e.g. "WeatherReport". 64 | * @param processor Implementation of a service, usually referred to 65 | * as "handlers", e.g. WeatherReportHandler implementing WeatherReport.Iface. 66 | */ 67 | public function registerProcessor($serviceName, $processor) 68 | { 69 | $this->serviceProcessorMap_[$serviceName] = $processor; 70 | } 71 | 72 | /** 73 | * This implementation of process performs the following steps: 74 | * 75 | *
    76 | *
  1. Read the beginning of the message.
  2. 77 | *
  3. Extract the service name from the message.
  4. 78 | *
  5. Using the service name to locate the appropriate processor.
  6. 79 | *
  7. Dispatch to the processor, with a decorated instance of TProtocol 80 | * that allows readMessageBegin() to return the original Message.
  8. 81 | *
82 | * 83 | * @throws TException If the message type is not CALL or ONEWAY, if 84 | * the service name was not found in the message, or if the service 85 | * name was not found in the service map. 86 | */ 87 | public function process(TProtocol $input, TProtocol $output) 88 | { 89 | /* 90 | Use the actual underlying protocol (e.g. TBinaryProtocol) to read the 91 | message header. This pulls the message "off the wire", which we'll 92 | deal with at the end of this method. 93 | */ 94 | $input->readMessageBegin($fname, $mtype, $rseqid); 95 | 96 | if ($mtype !== TMessageType::CALL && $mtype != TMessageType::ONEWAY) { 97 | throw new TException("This should not have happened!?"); 98 | } 99 | 100 | // Extract the service name and the new Message name. 101 | if (strpos($fname, TMultiplexedProtocol::SEPARATOR) === false) { 102 | throw new TException("Service name not found in message name: {$fname}. Did you " . 103 | "forget to use a TMultiplexProtocol in your client?"); 104 | } 105 | list($serviceName, $messageName) = explode(':', $fname, 2); 106 | if (!array_key_exists($serviceName, $this->serviceProcessorMap_)) { 107 | throw new TException("Service name not found: {$serviceName}. Did you forget " . 108 | "to call registerProcessor()?"); 109 | } 110 | 111 | // Dispatch processing to the stored processor 112 | $processor = $this->serviceProcessorMap_[$serviceName]; 113 | 114 | return $processor->process( 115 | new StoredMessageProtocol($input, $messageName, $mtype, $rseqid), $output 116 | ); 117 | } 118 | } 119 | 120 | /** 121 | * Our goal was to work with any protocol. In order to do that, we needed 122 | * to allow them to call readMessageBegin() and get the Message in exactly 123 | * the standard format, without the service name prepended to the Message name. 124 | */ 125 | class StoredMessageProtocol extends TProtocolDecorator 126 | { 127 | private $fname_, $mtype_, $rseqid_; 128 | 129 | public function __construct(TProtocol $protocol, $fname, $mtype, $rseqid) 130 | { 131 | parent::__construct($protocol); 132 | $this->fname_ = $fname; 133 | $this->mtype_ = $mtype; 134 | $this->rseqid_ = $rseqid; 135 | } 136 | 137 | public function readMessageBegin(&$name, &$type, &$seqid) 138 | { 139 | $name = $this->fname_; 140 | $type = $this->mtype_; 141 | $seqid = $this->rseqid_; 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /Thrift/Transport/TBufferedTransport.php: -------------------------------------------------------------------------------- 1 | transport_ = $transport; 42 | $this->rBufSize_ = $rBufSize; 43 | $this->wBufSize_ = $wBufSize; 44 | } 45 | 46 | /** 47 | * The underlying transport 48 | * 49 | * @var TTransport 50 | */ 51 | protected $transport_ = null; 52 | 53 | /** 54 | * The receive buffer size 55 | * 56 | * @var int 57 | */ 58 | protected $rBufSize_ = 512; 59 | 60 | /** 61 | * The write buffer size 62 | * 63 | * @var int 64 | */ 65 | protected $wBufSize_ = 512; 66 | 67 | /** 68 | * The write buffer. 69 | * 70 | * @var string 71 | */ 72 | protected $wBuf_ = ''; 73 | 74 | /** 75 | * The read buffer. 76 | * 77 | * @var string 78 | */ 79 | protected $rBuf_ = ''; 80 | 81 | public function isOpen() 82 | { 83 | return $this->transport_->isOpen(); 84 | } 85 | 86 | public function open() 87 | { 88 | $this->transport_->open(); 89 | } 90 | 91 | public function close() 92 | { 93 | $this->transport_->close(); 94 | } 95 | 96 | public function putBack($data) 97 | { 98 | if (TStringFuncFactory::create()->strlen($this->rBuf_) === 0) { 99 | $this->rBuf_ = $data; 100 | } else { 101 | $this->rBuf_ = ($data . $this->rBuf_); 102 | } 103 | } 104 | 105 | /** 106 | * The reason that we customize readAll here is that the majority of PHP 107 | * streams are already internally buffered by PHP. The socket stream, for 108 | * example, buffers internally and blocks if you call read with $len greater 109 | * than the amount of data available, unlike recv() in C. 110 | * 111 | * Therefore, use the readAll method of the wrapped transport inside 112 | * the buffered readAll. 113 | */ 114 | public function readAll($len) 115 | { 116 | $have = TStringFuncFactory::create()->strlen($this->rBuf_); 117 | if ($have == 0) { 118 | $data = $this->transport_->readAll($len); 119 | } elseif ($have < $len) { 120 | $data = $this->rBuf_; 121 | $this->rBuf_ = ''; 122 | $data .= $this->transport_->readAll($len - $have); 123 | } elseif ($have == $len) { 124 | $data = $this->rBuf_; 125 | $this->rBuf_ = ''; 126 | } elseif ($have > $len) { 127 | $data = TStringFuncFactory::create()->substr($this->rBuf_, 0, $len); 128 | $this->rBuf_ = TStringFuncFactory::create()->substr($this->rBuf_, $len); 129 | } 130 | 131 | return $data; 132 | } 133 | 134 | public function read($len) 135 | { 136 | if (TStringFuncFactory::create()->strlen($this->rBuf_) === 0) { 137 | $this->rBuf_ = $this->transport_->read($this->rBufSize_); 138 | } 139 | 140 | if (TStringFuncFactory::create()->strlen($this->rBuf_) <= $len) { 141 | $ret = $this->rBuf_; 142 | $this->rBuf_ = ''; 143 | 144 | return $ret; 145 | } 146 | 147 | $ret = TStringFuncFactory::create()->substr($this->rBuf_, 0, $len); 148 | $this->rBuf_ = TStringFuncFactory::create()->substr($this->rBuf_, $len); 149 | 150 | return $ret; 151 | } 152 | 153 | public function write($buf) 154 | { 155 | $this->wBuf_ .= $buf; 156 | if (TStringFuncFactory::create()->strlen($this->wBuf_) >= $this->wBufSize_) { 157 | $out = $this->wBuf_; 158 | 159 | // Note that we clear the internal wBuf_ prior to the underlying write 160 | // to ensure we're in a sane state (i.e. internal buffer cleaned) 161 | // if the underlying write throws up an exception 162 | $this->wBuf_ = ''; 163 | $this->transport_->write($out); 164 | } 165 | } 166 | 167 | public function flush() 168 | { 169 | if (TStringFuncFactory::create()->strlen($this->wBuf_) > 0) { 170 | $out = $this->wBuf_; 171 | 172 | // Note that we clear the internal wBuf_ prior to the underlying write 173 | // to ensure we're in a sane state (i.e. internal buffer cleaned) 174 | // if the underlying write throws up an exception 175 | $this->wBuf_ = ''; 176 | $this->transport_->write($out); 177 | } 178 | $this->transport_->flush(); 179 | } 180 | 181 | } 182 | -------------------------------------------------------------------------------- /Thrift/Transport/TCurlClient.php: -------------------------------------------------------------------------------- 1 | strlen($uri) > 0) && ($uri{0} != '/')) { 96 | $uri = '/'.$uri; 97 | } 98 | $this->scheme_ = $scheme; 99 | $this->host_ = $host; 100 | $this->port_ = $port; 101 | $this->uri_ = $uri; 102 | $this->request_ = ''; 103 | $this->response_ = null; 104 | $this->timeout_ = null; 105 | } 106 | 107 | /** 108 | * Set read timeout 109 | * 110 | * @param float $timeout 111 | */ 112 | public function setTimeoutSecs($timeout) 113 | { 114 | $this->timeout_ = $timeout; 115 | } 116 | 117 | /** 118 | * Whether this transport is open. 119 | * 120 | * @return boolean true if open 121 | */ 122 | public function isOpen() 123 | { 124 | return true; 125 | } 126 | 127 | /** 128 | * Open the transport for reading/writing 129 | * 130 | * @throws TTransportException if cannot open 131 | */ 132 | public function open() 133 | { 134 | } 135 | 136 | /** 137 | * Close the transport. 138 | */ 139 | public function close() 140 | { 141 | $this->request_ = ''; 142 | $this->response_ = null; 143 | } 144 | 145 | /** 146 | * Read some data into the array. 147 | * 148 | * @param int $len How much to read 149 | * @return string The data that has been read 150 | * @throws TTransportException if cannot read any more data 151 | */ 152 | public function read($len) 153 | { 154 | if ($len >= strlen($this->response_)) { 155 | return $this->response_; 156 | } else { 157 | $ret = substr($this->response_, 0, $len); 158 | $this->response_ = substr($this->response_, $len); 159 | 160 | return $ret; 161 | } 162 | } 163 | 164 | /** 165 | * Writes some data into the pending buffer 166 | * 167 | * @param string $buf The data to write 168 | * @throws TTransportException if writing fails 169 | */ 170 | public function write($buf) 171 | { 172 | $this->request_ .= $buf; 173 | } 174 | 175 | /** 176 | * Opens and sends the actual request over the HTTP connection 177 | * 178 | * @throws TTransportException if a writing error occurs 179 | */ 180 | public function flush() 181 | { 182 | if (!self::$curlHandle) { 183 | register_shutdown_function(array('Thrift\\Transport\\TCurlClient', 'closeCurlHandle')); 184 | self::$curlHandle = curl_init(); 185 | curl_setopt(self::$curlHandle, CURLOPT_RETURNTRANSFER, true); 186 | curl_setopt(self::$curlHandle, CURLOPT_BINARYTRANSFER, true); 187 | curl_setopt(self::$curlHandle, CURLOPT_USERAGENT, 'PHP/TCurlClient'); 188 | curl_setopt(self::$curlHandle, CURLOPT_CUSTOMREQUEST, 'POST'); 189 | curl_setopt(self::$curlHandle, CURLOPT_FOLLOWLOCATION, true); 190 | curl_setopt(self::$curlHandle, CURLOPT_MAXREDIRS, 1); 191 | } 192 | // God, PHP really has some esoteric ways of doing simple things. 193 | $host = $this->host_.($this->port_ != 80 ? ':'.$this->port_ : ''); 194 | $fullUrl = $this->scheme_."://".$host.$this->uri_; 195 | 196 | $headers = array('Accept: application/x-thrift', 197 | 'Content-Type: application/x-thrift', 198 | 'Content-Length: '.TStringFuncFactory::create()->strlen($this->request_)); 199 | curl_setopt(self::$curlHandle, CURLOPT_HTTPHEADER, $headers); 200 | 201 | if ($this->timeout_ > 0) { 202 | curl_setopt(self::$curlHandle, CURLOPT_TIMEOUT, $this->timeout_); 203 | } 204 | curl_setopt(self::$curlHandle, CURLOPT_POSTFIELDS, $this->request_); 205 | $this->request_ = ''; 206 | 207 | curl_setopt(self::$curlHandle, CURLOPT_URL, $fullUrl); 208 | $this->response_ = curl_exec(self::$curlHandle); 209 | 210 | // Connect failed? 211 | if (!$this->response_) { 212 | curl_close(self::$curlHandle); 213 | self::$curlHandle = null; 214 | $error = 'TCurlClient: Could not connect to '.$fullUrl; 215 | throw new TTransportException($error, TTransportException::NOT_OPEN); 216 | } 217 | } 218 | 219 | public static function closeCurlHandle() 220 | { 221 | try { 222 | if (self::$curlHandle) { 223 | curl_close(self::$curlHandle); 224 | self::$curlHandle = null; 225 | } 226 | } catch (\Exception $x) { 227 | error_log('There was an error closing the curl handle: ' . $x->getMessage()); 228 | } 229 | } 230 | 231 | } 232 | -------------------------------------------------------------------------------- /Thrift/Transport/TFramedTransport.php: -------------------------------------------------------------------------------- 1 | transport_ = $transport; 78 | $this->read_ = $read; 79 | $this->write_ = $write; 80 | } 81 | 82 | public function isOpen() 83 | { 84 | return $this->transport_->isOpen(); 85 | } 86 | 87 | public function open() 88 | { 89 | $this->transport_->open(); 90 | } 91 | 92 | public function close() 93 | { 94 | $this->transport_->close(); 95 | } 96 | 97 | /** 98 | * Reads from the buffer. When more data is required reads another entire 99 | * chunk and serves future reads out of that. 100 | * 101 | * @param int $len How much data 102 | */ 103 | public function read($len) 104 | { 105 | if (!$this->read_) { 106 | return $this->transport_->read($len); 107 | } 108 | 109 | if (TStringFuncFactory::create()->strlen($this->rBuf_) === 0) { 110 | $this->readFrame(); 111 | } 112 | 113 | // Just return full buff 114 | if ($len >= TStringFuncFactory::create()->strlen($this->rBuf_)) { 115 | $out = $this->rBuf_; 116 | $this->rBuf_ = null; 117 | 118 | return $out; 119 | } 120 | 121 | // Return TStringFuncFactory::create()->substr 122 | $out = TStringFuncFactory::create()->substr($this->rBuf_, 0, $len); 123 | $this->rBuf_ = TStringFuncFactory::create()->substr($this->rBuf_, $len); 124 | 125 | return $out; 126 | } 127 | 128 | /** 129 | * Put previously read data back into the buffer 130 | * 131 | * @param string $data data to return 132 | */ 133 | public function putBack($data) 134 | { 135 | if (TStringFuncFactory::create()->strlen($this->rBuf_) === 0) { 136 | $this->rBuf_ = $data; 137 | } else { 138 | $this->rBuf_ = ($data . $this->rBuf_); 139 | } 140 | } 141 | 142 | /** 143 | * Reads a chunk of data into the internal read buffer. 144 | */ 145 | private function readFrame() 146 | { 147 | $buf = $this->transport_->readAll(4); 148 | $val = unpack('N', $buf); 149 | $sz = $val[1]; 150 | 151 | $this->rBuf_ = $this->transport_->readAll($sz); 152 | } 153 | 154 | /** 155 | * Writes some data to the pending output buffer. 156 | * 157 | * @param string $buf The data 158 | * @param int $len Limit of bytes to write 159 | */ 160 | public function write($buf, $len=null) 161 | { 162 | if (!$this->write_) { 163 | return $this->transport_->write($buf, $len); 164 | } 165 | 166 | if ($len !== null && $len < TStringFuncFactory::create()->strlen($buf)) { 167 | $buf = TStringFuncFactory::create()->substr($buf, 0, $len); 168 | } 169 | $this->wBuf_ .= $buf; 170 | } 171 | 172 | /** 173 | * Writes the output buffer to the stream in the format of a 4-byte length 174 | * followed by the actual data. 175 | */ 176 | public function flush() 177 | { 178 | if (!$this->write_ || TStringFuncFactory::create()->strlen($this->wBuf_) == 0) { 179 | return $this->transport_->flush(); 180 | } 181 | 182 | $out = pack('N', TStringFuncFactory::create()->strlen($this->wBuf_)); 183 | $out .= $this->wBuf_; 184 | 185 | // Note that we clear the internal wBuf_ prior to the underlying write 186 | // to ensure we're in a sane state (i.e. internal buffer cleaned) 187 | // if the underlying write throws up an exception 188 | $this->wBuf_ = ''; 189 | $this->transport_->write($out); 190 | $this->transport_->flush(); 191 | } 192 | 193 | } 194 | -------------------------------------------------------------------------------- /Thrift/Transport/THttpClient.php: -------------------------------------------------------------------------------- 1 | strlen($uri) > 0) && ($uri{0} != '/')) { 101 | $uri = '/'.$uri; 102 | } 103 | $this->scheme_ = $scheme; 104 | $this->host_ = $host; 105 | $this->port_ = $port; 106 | $this->uri_ = $uri; 107 | $this->buf_ = ''; 108 | $this->handle_ = null; 109 | $this->timeout_ = null; 110 | $this->headers_ = array(); 111 | } 112 | 113 | /** 114 | * Set read timeout 115 | * 116 | * @param float $timeout 117 | */ 118 | public function setTimeoutSecs($timeout) 119 | { 120 | $this->timeout_ = $timeout; 121 | } 122 | 123 | /** 124 | * Whether this transport is open. 125 | * 126 | * @return boolean true if open 127 | */ 128 | public function isOpen() 129 | { 130 | return true; 131 | } 132 | 133 | /** 134 | * Open the transport for reading/writing 135 | * 136 | * @throws TTransportException if cannot open 137 | */ 138 | public function open() {} 139 | 140 | /** 141 | * Close the transport. 142 | */ 143 | public function close() 144 | { 145 | if ($this->handle_) { 146 | @fclose($this->handle_); 147 | $this->handle_ = null; 148 | } 149 | } 150 | 151 | /** 152 | * Read some data into the array. 153 | * 154 | * @param int $len How much to read 155 | * @return string The data that has been read 156 | * @throws TTransportException if cannot read any more data 157 | */ 158 | public function read($len) 159 | { 160 | $data = @fread($this->handle_, $len); 161 | if ($data === FALSE || $data === '') { 162 | $md = stream_get_meta_data($this->handle_); 163 | if ($md['timed_out']) { 164 | throw new TTransportException('THttpClient: timed out reading '.$len.' bytes from '.$this->host_.':'.$this->port_.$this->uri_, TTransportException::TIMED_OUT); 165 | } else { 166 | throw new TTransportException('THttpClient: Could not read '.$len.' bytes from '.$this->host_.':'.$this->port_.$this->uri_, TTransportException::UNKNOWN); 167 | } 168 | } 169 | 170 | return $data; 171 | } 172 | 173 | /** 174 | * Writes some data into the pending buffer 175 | * 176 | * @param string $buf The data to write 177 | * @throws TTransportException if writing fails 178 | */ 179 | public function write($buf) 180 | { 181 | $this->buf_ .= $buf; 182 | } 183 | 184 | /** 185 | * Opens and sends the actual request over the HTTP connection 186 | * 187 | * @throws TTransportException if a writing error occurs 188 | */ 189 | public function flush() 190 | { 191 | // God, PHP really has some esoteric ways of doing simple things. 192 | $host = $this->host_.($this->port_ != 80 ? ':'.$this->port_ : ''); 193 | 194 | $headers = array(); 195 | $defaultHeaders = array('Host' => $host, 196 | 'Accept' => 'application/x-thrift', 197 | 'User-Agent' => 'PHP/THttpClient', 198 | 'Content-Type' => 'application/x-thrift', 199 | 'Content-Length' => TStringFuncFactory::create()->strlen($this->buf_)); 200 | foreach (array_merge($defaultHeaders, $this->headers_) as $key => $value) { 201 | $headers[] = "$key: $value"; 202 | } 203 | 204 | $options = array('method' => 'POST', 205 | 'header' => implode("\r\n", $headers), 206 | 'max_redirects' => 1, 207 | 'content' => $this->buf_); 208 | if ($this->timeout_ > 0) { 209 | $options['timeout'] = $this->timeout_; 210 | } 211 | $this->buf_ = ''; 212 | 213 | $contextid = stream_context_create(array('http' => $options)); 214 | $this->handle_ = @fopen($this->scheme_.'://'.$host.$this->uri_, 'r', false, $contextid); 215 | 216 | // Connect failed? 217 | if ($this->handle_ === FALSE) { 218 | $this->handle_ = null; 219 | $error = 'THttpClient: Could not connect to '.$host.$this->uri_; 220 | throw new TTransportException($error, TTransportException::NOT_OPEN); 221 | } 222 | } 223 | 224 | public function addHeaders($headers) 225 | { 226 | $this->headers_ = array_merge($this->headers_, $headers); 227 | } 228 | 229 | } 230 | -------------------------------------------------------------------------------- /Thrift/Transport/TMemoryBuffer.php: -------------------------------------------------------------------------------- 1 | buf_ = $buf; 45 | } 46 | 47 | protected $buf_ = ''; 48 | 49 | public function isOpen() 50 | { 51 | return true; 52 | } 53 | 54 | public function open() {} 55 | 56 | public function close() {} 57 | 58 | public function write($buf) 59 | { 60 | $this->buf_ .= $buf; 61 | } 62 | 63 | public function read($len) 64 | { 65 | $bufLength = TStringFuncFactory::create()->strlen($this->buf_); 66 | 67 | if ($bufLength === 0) { 68 | throw new TTransportException('TMemoryBuffer: Could not read ' . 69 | $len . ' bytes from buffer.', 70 | TTransportException::UNKNOWN); 71 | } 72 | 73 | if ($bufLength <= $len) { 74 | $ret = $this->buf_; 75 | $this->buf_ = ''; 76 | 77 | return $ret; 78 | } 79 | 80 | $ret = TStringFuncFactory::create()->substr($this->buf_, 0, $len); 81 | $this->buf_ = TStringFuncFactory::create()->substr($this->buf_, $len); 82 | 83 | return $ret; 84 | } 85 | 86 | public function getBuffer() 87 | { 88 | return $this->buf_; 89 | } 90 | 91 | public function available() 92 | { 93 | return TStringFuncFactory::create()->strlen($this->buf_); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Thrift/Transport/TNullTransport.php: -------------------------------------------------------------------------------- 1 | read_ = $mode & self::MODE_R; 50 | $this->write_ = $mode & self::MODE_W; 51 | } 52 | 53 | public function open() 54 | { 55 | if ($this->read_) { 56 | $this->inStream_ = @fopen(self::inStreamName(), 'r'); 57 | if (!is_resource($this->inStream_)) { 58 | throw new TException('TPhpStream: Could not open php://input'); 59 | } 60 | } 61 | if ($this->write_) { 62 | $this->outStream_ = @fopen('php://output', 'w'); 63 | if (!is_resource($this->outStream_)) { 64 | throw new TException('TPhpStream: Could not open php://output'); 65 | } 66 | } 67 | } 68 | 69 | public function close() 70 | { 71 | if ($this->read_) { 72 | @fclose($this->inStream_); 73 | $this->inStream_ = null; 74 | } 75 | if ($this->write_) { 76 | @fclose($this->outStream_); 77 | $this->outStream_ = null; 78 | } 79 | } 80 | 81 | public function isOpen() 82 | { 83 | return 84 | (!$this->read_ || is_resource($this->inStream_)) && 85 | (!$this->write_ || is_resource($this->outStream_)); 86 | } 87 | 88 | public function read($len) 89 | { 90 | $data = @fread($this->inStream_, $len); 91 | if ($data === FALSE || $data === '') { 92 | throw new TException('TPhpStream: Could not read '.$len.' bytes'); 93 | } 94 | 95 | return $data; 96 | } 97 | 98 | public function write($buf) 99 | { 100 | while (TStringFuncFactory::create()->strlen($buf) > 0) { 101 | $got = @fwrite($this->outStream_, $buf); 102 | if ($got === 0 || $got === FALSE) { 103 | throw new TException('TPhpStream: Could not write '.TStringFuncFactory::create()->strlen($buf).' bytes'); 104 | } 105 | $buf = TStringFuncFactory::create()->substr($buf, $got); 106 | } 107 | } 108 | 109 | public function flush() 110 | { 111 | @fflush($this->outStream_); 112 | } 113 | 114 | private static function inStreamName() 115 | { 116 | if (php_sapi_name() == 'cli') { 117 | return 'php://stdin'; 118 | } 119 | 120 | return 'php://input'; 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /Thrift/Transport/TSocket.php: -------------------------------------------------------------------------------- 1 | host_ = $host; 127 | $this->port_ = $port; 128 | $this->persist_ = $persist; 129 | $this->debugHandler_ = $debugHandler ? $debugHandler : 'error_log'; 130 | } 131 | 132 | /** 133 | * @param resource $handle 134 | * @return void 135 | */ 136 | public function setHandle($handle) 137 | { 138 | $this->handle_ = $handle; 139 | } 140 | 141 | /** 142 | * Sets the send timeout. 143 | * 144 | * @param int $timeout Timeout in milliseconds. 145 | */ 146 | public function setSendTimeout($timeout) 147 | { 148 | $this->sendTimeoutSec_ = floor($timeout / 1000); 149 | $this->sendTimeoutUsec_ = 150 | ($timeout - ($this->sendTimeoutSec_ * 1000)) * 1000; 151 | } 152 | 153 | /** 154 | * Sets the receive timeout. 155 | * 156 | * @param int $timeout Timeout in milliseconds. 157 | */ 158 | public function setRecvTimeout($timeout) 159 | { 160 | $this->recvTimeoutSec_ = floor($timeout / 1000); 161 | $this->recvTimeoutUsec_ = 162 | ($timeout - ($this->recvTimeoutSec_ * 1000)) * 1000; 163 | } 164 | 165 | /** 166 | * Sets debugging output on or off 167 | * 168 | * @param bool $debug 169 | */ 170 | public function setDebug($debug) 171 | { 172 | $this->debug_ = $debug; 173 | } 174 | 175 | /** 176 | * Get the host that this socket is connected to 177 | * 178 | * @return string host 179 | */ 180 | public function getHost() 181 | { 182 | return $this->host_; 183 | } 184 | 185 | /** 186 | * Get the remote port that this socket is connected to 187 | * 188 | * @return int port 189 | */ 190 | public function getPort() 191 | { 192 | return $this->port_; 193 | } 194 | 195 | /** 196 | * Tests whether this is open 197 | * 198 | * @return bool true if the socket is open 199 | */ 200 | public function isOpen() 201 | { 202 | return is_resource($this->handle_); 203 | } 204 | 205 | /** 206 | * Connects the socket. 207 | */ 208 | public function open() 209 | { 210 | if ($this->isOpen()) { 211 | throw new TTransportException('Socket already connected', TTransportException::ALREADY_OPEN); 212 | } 213 | 214 | if (empty($this->host_)) { 215 | throw new TTransportException('Cannot open null host', TTransportException::NOT_OPEN); 216 | } 217 | 218 | if ($this->port_ <= 0) { 219 | throw new TTransportException('Cannot open without port', TTransportException::NOT_OPEN); 220 | } 221 | 222 | if ($this->persist_) { 223 | $this->handle_ = @pfsockopen($this->host_, 224 | $this->port_, 225 | $errno, 226 | $errstr, 227 | $this->sendTimeoutSec_ + ($this->sendTimeoutUsec_ / 1000000)); 228 | } else { 229 | $this->handle_ = @fsockopen($this->host_, 230 | $this->port_, 231 | $errno, 232 | $errstr, 233 | $this->sendTimeoutSec_ + ($this->sendTimeoutUsec_ / 1000000)); 234 | } 235 | 236 | // Connect failed? 237 | if ($this->handle_ === FALSE) { 238 | $error = 'TSocket: Could not connect to '.$this->host_.':'.$this->port_.' ('.$errstr.' ['.$errno.'])'; 239 | if ($this->debug_) { 240 | call_user_func($this->debugHandler_, $error); 241 | } 242 | throw new TException($error); 243 | } 244 | } 245 | 246 | /** 247 | * Closes the socket. 248 | */ 249 | public function close() 250 | { 251 | if (!$this->persist_) { 252 | @fclose($this->handle_); 253 | $this->handle_ = null; 254 | } 255 | } 256 | 257 | /** 258 | * Read from the socket at most $len bytes. 259 | * 260 | * This method will not wait for all the requested data, it will return as 261 | * soon as any data is received. 262 | * 263 | * @param int $len Maximum number of bytes to read. 264 | * @return string Binary data 265 | */ 266 | public function read($len) 267 | { 268 | $null = null; 269 | $read = array($this->handle_); 270 | $readable = @stream_select($read, $null, $null, $this->recvTimeoutSec_, $this->recvTimeoutUsec_); 271 | 272 | if ($readable > 0) { 273 | $data = @stream_socket_recvfrom($this->handle_, $len); 274 | if ($data === false) { 275 | throw new TTransportException('TSocket: Could not read '.$len.' bytes from '. 276 | $this->host_.':'.$this->port_); 277 | } elseif ($data == '' && feof($this->handle_)) { 278 | throw new TTransportException('TSocket read 0 bytes'); 279 | } 280 | 281 | return $data; 282 | } elseif ($readable === 0) { 283 | throw new TTransportException('TSocket: timed out reading '.$len.' bytes from '. 284 | $this->host_.':'.$this->port_); 285 | } else { 286 | throw new TTransportException('TSocket: Could not read '.$len.' bytes from '. 287 | $this->host_.':'.$this->port_); 288 | } 289 | } 290 | 291 | /** 292 | * Write to the socket. 293 | * 294 | * @param string $buf The data to write 295 | */ 296 | public function write($buf) 297 | { 298 | $null = null; 299 | $write = array($this->handle_); 300 | 301 | // keep writing until all the data has been written 302 | while (TStringFuncFactory::create()->strlen($buf) > 0) { 303 | // wait for stream to become available for writing 304 | $writable = @stream_select($null, $write, $null, $this->sendTimeoutSec_, $this->sendTimeoutUsec_); 305 | if ($writable > 0) { 306 | // write buffer to stream 307 | $written = @stream_socket_sendto($this->handle_, $buf); 308 | if ($written === -1 || $written === false) { 309 | throw new TTransportException('TSocket: Could not write '.TStringFuncFactory::create()->strlen($buf).' bytes '. 310 | $this->host_.':'.$this->port_); 311 | } 312 | // determine how much of the buffer is left to write 313 | $buf = TStringFuncFactory::create()->substr($buf, $written); 314 | } elseif ($writable === 0) { 315 | throw new TTransportException('TSocket: timed out writing '.TStringFuncFactory::create()->strlen($buf).' bytes from '. 316 | $this->host_.':'.$this->port_); 317 | } else { 318 | throw new TTransportException('TSocket: Could not write '.TStringFuncFactory::create()->strlen($buf).' bytes '. 319 | $this->host_.':'.$this->port_); 320 | } 321 | } 322 | } 323 | 324 | /** 325 | * Flush output to the socket. 326 | * 327 | * Since read(), readAll() and write() operate on the sockets directly, 328 | * this is a no-op 329 | * 330 | * If you wish to have flushable buffering behaviour, wrap this TSocket 331 | * in a TBufferedTransport. 332 | */ 333 | public function flush() 334 | { 335 | // no-op 336 | } 337 | } 338 | -------------------------------------------------------------------------------- /Thrift/Transport/TSocketPool.php: -------------------------------------------------------------------------------- 1 | $val) { 105 | $ports[$key] = $port; 106 | } 107 | } 108 | 109 | foreach ($hosts as $key => $host) { 110 | $this->servers_ []= array('host' => $host, 111 | 'port' => $ports[$key]); 112 | } 113 | } 114 | 115 | /** 116 | * Add a server to the pool 117 | * 118 | * This function does not prevent you from adding a duplicate server entry. 119 | * 120 | * @param string $host hostname or IP 121 | * @param int $port port 122 | */ 123 | public function addServer($host, $port) 124 | { 125 | $this->servers_[] = array('host' => $host, 'port' => $port); 126 | } 127 | 128 | /** 129 | * Sets how many time to keep retrying a host in the connect function. 130 | * 131 | * @param int $numRetries 132 | */ 133 | public function setNumRetries($numRetries) 134 | { 135 | $this->numRetries_ = $numRetries; 136 | } 137 | 138 | /** 139 | * Sets how long to wait until retrying a host if it was marked down 140 | * 141 | * @param int $numRetries 142 | */ 143 | public function setRetryInterval($retryInterval) 144 | { 145 | $this->retryInterval_ = $retryInterval; 146 | } 147 | 148 | /** 149 | * Sets how many time to keep retrying a host before marking it as down. 150 | * 151 | * @param int $numRetries 152 | */ 153 | public function setMaxConsecutiveFailures($maxConsecutiveFailures) 154 | { 155 | $this->maxConsecutiveFailures_ = $maxConsecutiveFailures; 156 | } 157 | 158 | /** 159 | * Turns randomization in connect order on or off. 160 | * 161 | * @param bool $randomize 162 | */ 163 | public function setRandomize($randomize) 164 | { 165 | $this->randomize_ = $randomize; 166 | } 167 | 168 | /** 169 | * Whether to always try the last server. 170 | * 171 | * @param bool $alwaysTryLast 172 | */ 173 | public function setAlwaysTryLast($alwaysTryLast) 174 | { 175 | $this->alwaysTryLast_ = $alwaysTryLast; 176 | } 177 | 178 | /** 179 | * Connects the socket by iterating through all the servers in the pool 180 | * and trying to find one that works. 181 | */ 182 | public function open() 183 | { 184 | // Check if we want order randomization 185 | if ($this->randomize_) { 186 | shuffle($this->servers_); 187 | } 188 | 189 | // Count servers to identify the "last" one 190 | $numServers = count($this->servers_); 191 | 192 | for ($i = 0; $i < $numServers; ++$i) { 193 | 194 | // This extracts the $host and $port variables 195 | extract($this->servers_[$i]); 196 | 197 | // Check APC cache for a record of this server being down 198 | $failtimeKey = 'thrift_failtime:'.$host.':'.$port.'~'; 199 | 200 | // Cache miss? Assume it's OK 201 | $lastFailtime = apc_fetch($failtimeKey); 202 | if ($lastFailtime === FALSE) { 203 | $lastFailtime = 0; 204 | } 205 | 206 | $retryIntervalPassed = false; 207 | 208 | // Cache hit...make sure enough the retry interval has elapsed 209 | if ($lastFailtime > 0) { 210 | $elapsed = time() - $lastFailtime; 211 | if ($elapsed > $this->retryInterval_) { 212 | $retryIntervalPassed = true; 213 | if ($this->debug_) { 214 | call_user_func($this->debugHandler_, 215 | 'TSocketPool: retryInterval '. 216 | '('.$this->retryInterval_.') '. 217 | 'has passed for host '.$host.':'.$port); 218 | } 219 | } 220 | } 221 | 222 | // Only connect if not in the middle of a fail interval, OR if this 223 | // is the LAST server we are trying, just hammer away on it 224 | $isLastServer = false; 225 | if ($this->alwaysTryLast_) { 226 | $isLastServer = ($i == ($numServers - 1)); 227 | } 228 | 229 | if (($lastFailtime === 0) || 230 | ($isLastServer) || 231 | ($lastFailtime > 0 && $retryIntervalPassed)) { 232 | 233 | // Set underlying TSocket params to this one 234 | $this->host_ = $host; 235 | $this->port_ = $port; 236 | 237 | // Try up to numRetries_ connections per server 238 | for ($attempt = 0; $attempt < $this->numRetries_; $attempt++) { 239 | try { 240 | // Use the underlying TSocket open function 241 | parent::open(); 242 | 243 | // Only clear the failure counts if required to do so 244 | if ($lastFailtime > 0) { 245 | apc_store($failtimeKey, 0); 246 | } 247 | 248 | // Successful connection, return now 249 | return; 250 | 251 | } catch (TException $tx) { 252 | // Connection failed 253 | } 254 | } 255 | 256 | // Mark failure of this host in the cache 257 | $consecfailsKey = 'thrift_consecfails:'.$host.':'.$port.'~'; 258 | 259 | // Ignore cache misses 260 | $consecfails = apc_fetch($consecfailsKey); 261 | if ($consecfails === FALSE) { 262 | $consecfails = 0; 263 | } 264 | 265 | // Increment by one 266 | $consecfails++; 267 | 268 | // Log and cache this failure 269 | if ($consecfails >= $this->maxConsecutiveFailures_) { 270 | if ($this->debug_) { 271 | call_user_func($this->debugHandler_, 272 | 'TSocketPool: marking '.$host.':'.$port. 273 | ' as down for '.$this->retryInterval_.' secs '. 274 | 'after '.$consecfails.' failed attempts.'); 275 | } 276 | // Store the failure time 277 | apc_store($failtimeKey, time()); 278 | 279 | // Clear the count of consecutive failures 280 | apc_store($consecfailsKey, 0); 281 | } else { 282 | apc_store($consecfailsKey, $consecfails); 283 | } 284 | } 285 | } 286 | 287 | // Oh no; we failed them all. The system is totally ill! 288 | $error = 'TSocketPool: All hosts in pool are down. '; 289 | $hosts = array(); 290 | foreach ($this->servers_ as $server) { 291 | $hosts []= $server['host'].':'.$server['port']; 292 | } 293 | $hostlist = implode(',', $hosts); 294 | $error .= '('.$hostlist.')'; 295 | if ($this->debug_) { 296 | call_user_func($this->debugHandler_, $error); 297 | } 298 | throw new TException($error); 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /Thrift/Transport/TTransport.php: -------------------------------------------------------------------------------- 1 | read($len); 71 | 72 | $data = ''; 73 | $got = 0; 74 | while (($got = TStringFuncFactory::create()->strlen($data)) < $len) { 75 | $data .= $this->read($len - $got); 76 | } 77 | 78 | return $data; 79 | } 80 | 81 | /** 82 | * Writes the given data out. 83 | * 84 | * @param string $buf The data to write 85 | * @throws TTransportException if writing fails 86 | */ 87 | abstract public function write($buf); 88 | 89 | /** 90 | * Flushes any pending data out of a buffer 91 | * 92 | * @throws TTransportException if a writing error occurs 93 | */ 94 | public function flush() {} 95 | } 96 | -------------------------------------------------------------------------------- /Thrift/Type/TConstant.php: -------------------------------------------------------------------------------- 1 | registerNamespace('Thrift', __DIR__); 32 | $loader->registerDefinition('shared', $GEN_DIR); 33 | $loader->registerDefinition('tutorial', $GEN_DIR); 34 | $loader->register(); 35 | 36 | use Thrift\Protocol\TBinaryProtocol; 37 | use Thrift\Transport\TSocket; 38 | use Thrift\Transport\THttpClient; 39 | use Thrift\Transport\TBufferedTransport; 40 | use Thrift\Exception\TException; 41 | 42 | try { 43 | $socket = new THttpClient('localhost', 9999, '/server.php'); 44 | $transport = new TBufferedTransport($socket, 512, 512); 45 | $protocol = new TBinaryProtocol($transport); 46 | $client = new \tutorial\CalculatorClient($protocol); 47 | 48 | $transport->open(); 49 | 50 | $client->ping(); 51 | print "ping()\n"; 52 | 53 | $sum = $client->add(1,1); 54 | print "1+1=$sum\n"; 55 | 56 | $work = new \tutorial\Work(); 57 | 58 | $work->op = \tutorial\Operation::DIVIDE; 59 | $work->num1 = 1; 60 | $work->num2 = 0; 61 | 62 | try { 63 | $client->calculate(1, $work); 64 | print "Whoa! We can divide by zero?\n"; 65 | } catch (\tutorial\InvalidOperation $io) { 66 | print "InvalidOperation: $io->why\n"; 67 | } 68 | 69 | $work->op = \tutorial\Operation::SUBTRACT; 70 | $work->num1 = 15; 71 | $work->num2 = 10; 72 | $diff = $client->calculate(1, $work); 73 | print "15-10=$diff\n"; 74 | 75 | $log = $client->getStruct(1); 76 | print "Log: $log->value\n"; 77 | 78 | $transport->close(); 79 | 80 | } catch (TException $tx) { 81 | print 'TException: '.$tx->getMessage()."\n"; 82 | } 83 | 84 | -------------------------------------------------------------------------------- /gen-php/shared/SharedService.php: -------------------------------------------------------------------------------- 1 | input_ = $input; 35 | $this->output_ = $output ? $output : $input; 36 | } 37 | 38 | public function getStruct($key) 39 | { 40 | $this->send_getStruct($key); 41 | return $this->recv_getStruct(); 42 | } 43 | 44 | public function send_getStruct($key) 45 | { 46 | $args = new \shared\SharedService_getStruct_args(); 47 | $args->key = $key; 48 | $bin_accel = ($this->output_ instanceof TBinaryProtocolAccelerated) && function_exists('thrift_protocol_write_binary'); 49 | if ($bin_accel) 50 | { 51 | thrift_protocol_write_binary($this->output_, 'getStruct', TMessageType::CALL, $args, $this->seqid_, $this->output_->isStrictWrite()); 52 | } 53 | else 54 | { 55 | $this->output_->writeMessageBegin('getStruct', TMessageType::CALL, $this->seqid_); 56 | $args->write($this->output_); 57 | $this->output_->writeMessageEnd(); 58 | $this->output_->getTransport()->flush(); 59 | } 60 | } 61 | 62 | public function recv_getStruct() 63 | { 64 | $bin_accel = ($this->input_ instanceof TBinaryProtocolAccelerated) && function_exists('thrift_protocol_read_binary'); 65 | if ($bin_accel) $result = thrift_protocol_read_binary($this->input_, '\shared\SharedService_getStruct_result', $this->input_->isStrictRead()); 66 | else 67 | { 68 | $rseqid = 0; 69 | $fname = null; 70 | $mtype = 0; 71 | 72 | $this->input_->readMessageBegin($fname, $mtype, $rseqid); 73 | if ($mtype == TMessageType::EXCEPTION) { 74 | $x = new TApplicationException(); 75 | $x->read($this->input_); 76 | $this->input_->readMessageEnd(); 77 | throw $x; 78 | } 79 | $result = new \shared\SharedService_getStruct_result(); 80 | $result->read($this->input_); 81 | $this->input_->readMessageEnd(); 82 | } 83 | if ($result->success !== null) { 84 | return $result->success; 85 | } 86 | throw new \Exception("getStruct failed: unknown result"); 87 | } 88 | 89 | } 90 | 91 | // HELPER FUNCTIONS AND STRUCTURES 92 | 93 | class SharedService_getStruct_args { 94 | static $_TSPEC; 95 | 96 | /** 97 | * @var int 98 | */ 99 | public $key = null; 100 | 101 | public function __construct($vals=null) { 102 | if (!isset(self::$_TSPEC)) { 103 | self::$_TSPEC = array( 104 | 1 => array( 105 | 'var' => 'key', 106 | 'type' => TType::I32, 107 | ), 108 | ); 109 | } 110 | if (is_array($vals)) { 111 | if (isset($vals['key'])) { 112 | $this->key = $vals['key']; 113 | } 114 | } 115 | } 116 | 117 | public function getName() { 118 | return 'SharedService_getStruct_args'; 119 | } 120 | 121 | public function read($input) 122 | { 123 | $xfer = 0; 124 | $fname = null; 125 | $ftype = 0; 126 | $fid = 0; 127 | $xfer += $input->readStructBegin($fname); 128 | while (true) 129 | { 130 | $xfer += $input->readFieldBegin($fname, $ftype, $fid); 131 | if ($ftype == TType::STOP) { 132 | break; 133 | } 134 | switch ($fid) 135 | { 136 | case 1: 137 | if ($ftype == TType::I32) { 138 | $xfer += $input->readI32($this->key); 139 | } else { 140 | $xfer += $input->skip($ftype); 141 | } 142 | break; 143 | default: 144 | $xfer += $input->skip($ftype); 145 | break; 146 | } 147 | $xfer += $input->readFieldEnd(); 148 | } 149 | $xfer += $input->readStructEnd(); 150 | return $xfer; 151 | } 152 | 153 | public function write($output) { 154 | $xfer = 0; 155 | $xfer += $output->writeStructBegin('SharedService_getStruct_args'); 156 | if ($this->key !== null) { 157 | $xfer += $output->writeFieldBegin('key', TType::I32, 1); 158 | $xfer += $output->writeI32($this->key); 159 | $xfer += $output->writeFieldEnd(); 160 | } 161 | $xfer += $output->writeFieldStop(); 162 | $xfer += $output->writeStructEnd(); 163 | return $xfer; 164 | } 165 | 166 | } 167 | 168 | class SharedService_getStruct_result { 169 | static $_TSPEC; 170 | 171 | /** 172 | * @var \shared\SharedStruct 173 | */ 174 | public $success = null; 175 | 176 | public function __construct($vals=null) { 177 | if (!isset(self::$_TSPEC)) { 178 | self::$_TSPEC = array( 179 | 0 => array( 180 | 'var' => 'success', 181 | 'type' => TType::STRUCT, 182 | 'class' => '\shared\SharedStruct', 183 | ), 184 | ); 185 | } 186 | if (is_array($vals)) { 187 | if (isset($vals['success'])) { 188 | $this->success = $vals['success']; 189 | } 190 | } 191 | } 192 | 193 | public function getName() { 194 | return 'SharedService_getStruct_result'; 195 | } 196 | 197 | public function read($input) 198 | { 199 | $xfer = 0; 200 | $fname = null; 201 | $ftype = 0; 202 | $fid = 0; 203 | $xfer += $input->readStructBegin($fname); 204 | while (true) 205 | { 206 | $xfer += $input->readFieldBegin($fname, $ftype, $fid); 207 | if ($ftype == TType::STOP) { 208 | break; 209 | } 210 | switch ($fid) 211 | { 212 | case 0: 213 | if ($ftype == TType::STRUCT) { 214 | $this->success = new \shared\SharedStruct(); 215 | $xfer += $this->success->read($input); 216 | } else { 217 | $xfer += $input->skip($ftype); 218 | } 219 | break; 220 | default: 221 | $xfer += $input->skip($ftype); 222 | break; 223 | } 224 | $xfer += $input->readFieldEnd(); 225 | } 226 | $xfer += $input->readStructEnd(); 227 | return $xfer; 228 | } 229 | 230 | public function write($output) { 231 | $xfer = 0; 232 | $xfer += $output->writeStructBegin('SharedService_getStruct_result'); 233 | if ($this->success !== null) { 234 | if (!is_object($this->success)) { 235 | throw new TProtocolException('Bad type in structure.', TProtocolException::INVALID_DATA); 236 | } 237 | $xfer += $output->writeFieldBegin('success', TType::STRUCT, 0); 238 | $xfer += $this->success->write($output); 239 | $xfer += $output->writeFieldEnd(); 240 | } 241 | $xfer += $output->writeFieldStop(); 242 | $xfer += $output->writeStructEnd(); 243 | return $xfer; 244 | } 245 | 246 | } 247 | 248 | class SharedServiceProcessor { 249 | protected $handler_ = null; 250 | public function __construct($handler) { 251 | $this->handler_ = $handler; 252 | } 253 | 254 | public function process($input, $output) { 255 | $rseqid = 0; 256 | $fname = null; 257 | $mtype = 0; 258 | 259 | $input->readMessageBegin($fname, $mtype, $rseqid); 260 | $methodname = 'process_'.$fname; 261 | if (!method_exists($this, $methodname)) { 262 | $input->skip(TType::STRUCT); 263 | $input->readMessageEnd(); 264 | $x = new TApplicationException('Function '.$fname.' not implemented.', TApplicationException::UNKNOWN_METHOD); 265 | $output->writeMessageBegin($fname, TMessageType::EXCEPTION, $rseqid); 266 | $x->write($output); 267 | $output->writeMessageEnd(); 268 | $output->getTransport()->flush(); 269 | return; 270 | } 271 | $this->$methodname($rseqid, $input, $output); 272 | return true; 273 | } 274 | 275 | protected function process_getStruct($seqid, $input, $output) { 276 | $args = new \shared\SharedService_getStruct_args(); 277 | $args->read($input); 278 | $input->readMessageEnd(); 279 | $result = new \shared\SharedService_getStruct_result(); 280 | $result->success = $this->handler_->getStruct($args->key); 281 | $bin_accel = ($output instanceof TBinaryProtocolAccelerated) && function_exists('thrift_protocol_write_binary'); 282 | if ($bin_accel) 283 | { 284 | thrift_protocol_write_binary($output, 'getStruct', TMessageType::REPLY, $result, $seqid, $output->isStrictWrite()); 285 | } 286 | else 287 | { 288 | $output->writeMessageBegin('getStruct', TMessageType::REPLY, $seqid); 289 | $result->write($output); 290 | $output->writeMessageEnd(); 291 | $output->getTransport()->flush(); 292 | } 293 | } 294 | } 295 | 296 | -------------------------------------------------------------------------------- /gen-php/shared/Types.php: -------------------------------------------------------------------------------- 1 | array( 36 | 'var' => 'key', 37 | 'type' => TType::I32, 38 | ), 39 | 2 => array( 40 | 'var' => 'value', 41 | 'type' => TType::STRING, 42 | ), 43 | ); 44 | } 45 | if (is_array($vals)) { 46 | if (isset($vals['key'])) { 47 | $this->key = $vals['key']; 48 | } 49 | if (isset($vals['value'])) { 50 | $this->value = $vals['value']; 51 | } 52 | } 53 | } 54 | 55 | public function getName() { 56 | return 'SharedStruct'; 57 | } 58 | 59 | public function read($input) 60 | { 61 | $xfer = 0; 62 | $fname = null; 63 | $ftype = 0; 64 | $fid = 0; 65 | $xfer += $input->readStructBegin($fname); 66 | while (true) 67 | { 68 | $xfer += $input->readFieldBegin($fname, $ftype, $fid); 69 | if ($ftype == TType::STOP) { 70 | break; 71 | } 72 | switch ($fid) 73 | { 74 | case 1: 75 | if ($ftype == TType::I32) { 76 | $xfer += $input->readI32($this->key); 77 | } else { 78 | $xfer += $input->skip($ftype); 79 | } 80 | break; 81 | case 2: 82 | if ($ftype == TType::STRING) { 83 | $xfer += $input->readString($this->value); 84 | } else { 85 | $xfer += $input->skip($ftype); 86 | } 87 | break; 88 | default: 89 | $xfer += $input->skip($ftype); 90 | break; 91 | } 92 | $xfer += $input->readFieldEnd(); 93 | } 94 | $xfer += $input->readStructEnd(); 95 | return $xfer; 96 | } 97 | 98 | public function write($output) { 99 | $xfer = 0; 100 | $xfer += $output->writeStructBegin('SharedStruct'); 101 | if ($this->key !== null) { 102 | $xfer += $output->writeFieldBegin('key', TType::I32, 1); 103 | $xfer += $output->writeI32($this->key); 104 | $xfer += $output->writeFieldEnd(); 105 | } 106 | if ($this->value !== null) { 107 | $xfer += $output->writeFieldBegin('value', TType::STRING, 2); 108 | $xfer += $output->writeString($this->value); 109 | $xfer += $output->writeFieldEnd(); 110 | } 111 | $xfer += $output->writeFieldStop(); 112 | $xfer += $output->writeStructEnd(); 113 | return $xfer; 114 | } 115 | 116 | } 117 | 118 | 119 | -------------------------------------------------------------------------------- /gen-php/tutorial/Types.php: -------------------------------------------------------------------------------- 1 | 'ADD', 31 | 2 => 'SUBTRACT', 32 | 3 => 'MULTIPLY', 33 | 4 => 'DIVIDE', 34 | ); 35 | } 36 | 37 | /** 38 | * Structs are the basic complex data structures. They are comprised of fields 39 | * which each have an integer identifier, a type, a symbolic name, and an 40 | * optional default value. 41 | * 42 | * Fields can be declared "optional", which ensures they will not be included 43 | * in the serialized output if they aren't set. Note that this requires some 44 | * manual management in some languages. 45 | */ 46 | class Work { 47 | static $_TSPEC; 48 | 49 | /** 50 | * @var int 51 | */ 52 | public $num1 = 0; 53 | /** 54 | * @var int 55 | */ 56 | public $num2 = null; 57 | /** 58 | * @var int 59 | */ 60 | public $op = null; 61 | /** 62 | * @var string 63 | */ 64 | public $comment = null; 65 | 66 | public function __construct($vals=null) { 67 | if (!isset(self::$_TSPEC)) { 68 | self::$_TSPEC = array( 69 | 1 => array( 70 | 'var' => 'num1', 71 | 'type' => TType::I32, 72 | ), 73 | 2 => array( 74 | 'var' => 'num2', 75 | 'type' => TType::I32, 76 | ), 77 | 3 => array( 78 | 'var' => 'op', 79 | 'type' => TType::I32, 80 | ), 81 | 4 => array( 82 | 'var' => 'comment', 83 | 'type' => TType::STRING, 84 | ), 85 | ); 86 | } 87 | if (is_array($vals)) { 88 | if (isset($vals['num1'])) { 89 | $this->num1 = $vals['num1']; 90 | } 91 | if (isset($vals['num2'])) { 92 | $this->num2 = $vals['num2']; 93 | } 94 | if (isset($vals['op'])) { 95 | $this->op = $vals['op']; 96 | } 97 | if (isset($vals['comment'])) { 98 | $this->comment = $vals['comment']; 99 | } 100 | } 101 | } 102 | 103 | public function getName() { 104 | return 'Work'; 105 | } 106 | 107 | public function read($input) 108 | { 109 | $xfer = 0; 110 | $fname = null; 111 | $ftype = 0; 112 | $fid = 0; 113 | $xfer += $input->readStructBegin($fname); 114 | while (true) 115 | { 116 | $xfer += $input->readFieldBegin($fname, $ftype, $fid); 117 | if ($ftype == TType::STOP) { 118 | break; 119 | } 120 | switch ($fid) 121 | { 122 | case 1: 123 | if ($ftype == TType::I32) { 124 | $xfer += $input->readI32($this->num1); 125 | } else { 126 | $xfer += $input->skip($ftype); 127 | } 128 | break; 129 | case 2: 130 | if ($ftype == TType::I32) { 131 | $xfer += $input->readI32($this->num2); 132 | } else { 133 | $xfer += $input->skip($ftype); 134 | } 135 | break; 136 | case 3: 137 | if ($ftype == TType::I32) { 138 | $xfer += $input->readI32($this->op); 139 | } else { 140 | $xfer += $input->skip($ftype); 141 | } 142 | break; 143 | case 4: 144 | if ($ftype == TType::STRING) { 145 | $xfer += $input->readString($this->comment); 146 | } else { 147 | $xfer += $input->skip($ftype); 148 | } 149 | break; 150 | default: 151 | $xfer += $input->skip($ftype); 152 | break; 153 | } 154 | $xfer += $input->readFieldEnd(); 155 | } 156 | $xfer += $input->readStructEnd(); 157 | return $xfer; 158 | } 159 | 160 | public function write($output) { 161 | $xfer = 0; 162 | $xfer += $output->writeStructBegin('Work'); 163 | if ($this->num1 !== null) { 164 | $xfer += $output->writeFieldBegin('num1', TType::I32, 1); 165 | $xfer += $output->writeI32($this->num1); 166 | $xfer += $output->writeFieldEnd(); 167 | } 168 | if ($this->num2 !== null) { 169 | $xfer += $output->writeFieldBegin('num2', TType::I32, 2); 170 | $xfer += $output->writeI32($this->num2); 171 | $xfer += $output->writeFieldEnd(); 172 | } 173 | if ($this->op !== null) { 174 | $xfer += $output->writeFieldBegin('op', TType::I32, 3); 175 | $xfer += $output->writeI32($this->op); 176 | $xfer += $output->writeFieldEnd(); 177 | } 178 | if ($this->comment !== null) { 179 | $xfer += $output->writeFieldBegin('comment', TType::STRING, 4); 180 | $xfer += $output->writeString($this->comment); 181 | $xfer += $output->writeFieldEnd(); 182 | } 183 | $xfer += $output->writeFieldStop(); 184 | $xfer += $output->writeStructEnd(); 185 | return $xfer; 186 | } 187 | 188 | } 189 | 190 | /** 191 | * Structs can also be exceptions, if they are nasty. 192 | */ 193 | class InvalidOperation extends TException { 194 | static $_TSPEC; 195 | 196 | /** 197 | * @var int 198 | */ 199 | public $whatOp = null; 200 | /** 201 | * @var string 202 | */ 203 | public $why = null; 204 | 205 | public function __construct($vals=null) { 206 | if (!isset(self::$_TSPEC)) { 207 | self::$_TSPEC = array( 208 | 1 => array( 209 | 'var' => 'whatOp', 210 | 'type' => TType::I32, 211 | ), 212 | 2 => array( 213 | 'var' => 'why', 214 | 'type' => TType::STRING, 215 | ), 216 | ); 217 | } 218 | if (is_array($vals)) { 219 | if (isset($vals['whatOp'])) { 220 | $this->whatOp = $vals['whatOp']; 221 | } 222 | if (isset($vals['why'])) { 223 | $this->why = $vals['why']; 224 | } 225 | } 226 | } 227 | 228 | public function getName() { 229 | return 'InvalidOperation'; 230 | } 231 | 232 | public function read($input) 233 | { 234 | $xfer = 0; 235 | $fname = null; 236 | $ftype = 0; 237 | $fid = 0; 238 | $xfer += $input->readStructBegin($fname); 239 | while (true) 240 | { 241 | $xfer += $input->readFieldBegin($fname, $ftype, $fid); 242 | if ($ftype == TType::STOP) { 243 | break; 244 | } 245 | switch ($fid) 246 | { 247 | case 1: 248 | if ($ftype == TType::I32) { 249 | $xfer += $input->readI32($this->whatOp); 250 | } else { 251 | $xfer += $input->skip($ftype); 252 | } 253 | break; 254 | case 2: 255 | if ($ftype == TType::STRING) { 256 | $xfer += $input->readString($this->why); 257 | } else { 258 | $xfer += $input->skip($ftype); 259 | } 260 | break; 261 | default: 262 | $xfer += $input->skip($ftype); 263 | break; 264 | } 265 | $xfer += $input->readFieldEnd(); 266 | } 267 | $xfer += $input->readStructEnd(); 268 | return $xfer; 269 | } 270 | 271 | public function write($output) { 272 | $xfer = 0; 273 | $xfer += $output->writeStructBegin('InvalidOperation'); 274 | if ($this->whatOp !== null) { 275 | $xfer += $output->writeFieldBegin('whatOp', TType::I32, 1); 276 | $xfer += $output->writeI32($this->whatOp); 277 | $xfer += $output->writeFieldEnd(); 278 | } 279 | if ($this->why !== null) { 280 | $xfer += $output->writeFieldBegin('why', TType::STRING, 2); 281 | $xfer += $output->writeString($this->why); 282 | $xfer += $output->writeFieldEnd(); 283 | } 284 | $xfer += $output->writeFieldStop(); 285 | $xfer += $output->writeStructEnd(); 286 | return $xfer; 287 | } 288 | 289 | } 290 | 291 | final class Constant extends \Thrift\Type\TConstant { 292 | static protected $INT32CONSTANT; 293 | static protected $MAPCONSTANT; 294 | 295 | static protected function init_INT32CONSTANT() { 296 | return /** 297 | * Thrift also lets you define constants for use across languages. Complex 298 | * types and structs are specified using JSON notation. 299 | */ 300 | 9853; 301 | } 302 | 303 | static protected function init_MAPCONSTANT() { 304 | return array( 305 | "hello" => "world", 306 | "goodnight" => "moon", 307 | ); 308 | } 309 | } 310 | 311 | 312 | -------------------------------------------------------------------------------- /server.php: -------------------------------------------------------------------------------- 1 | registerNamespace('Thrift', __DIR__); 30 | $loader->registerDefinition('shared', $GEN_DIR); 31 | $loader->registerDefinition('tutorial', $GEN_DIR); 32 | $loader->register(); 33 | 34 | if (php_sapi_name() == 'cli') { 35 | ini_set("display_errors", "stderr"); 36 | } 37 | 38 | use Thrift\Protocol\TBinaryProtocol; 39 | use Thrift\Transport\TPhpStream; 40 | use Thrift\Transport\TBufferedTransport; 41 | 42 | class CalculatorHandler implements \tutorial\CalculatorIf { 43 | protected $log = array(); 44 | 45 | public function ping() { 46 | error_log("ping()"); 47 | } 48 | 49 | public function add($num1, $num2) { 50 | error_log("add({$num1}, {$num2})"); 51 | return $num1 + $num2; 52 | } 53 | 54 | public function calculate($logid, \tutorial\Work $w) { 55 | error_log("calculate({$logid}, {{$w->op}, {$w->num1}, {$w->num2}})"); 56 | switch ($w->op) { 57 | case \tutorial\Operation::ADD: 58 | $val = $w->num1 + $w->num2; 59 | break; 60 | case \tutorial\Operation::SUBTRACT: 61 | $val = $w->num1 - $w->num2; 62 | break; 63 | case \tutorial\Operation::MULTIPLY: 64 | $val = $w->num1 * $w->num2; 65 | break; 66 | case \tutorial\Operation::DIVIDE: 67 | if ($w->num2 == 0) { 68 | $io = new \tutorial\InvalidOperation(); 69 | $io->whatOp = $w->op; 70 | $io->why = "Cannot divide by 0"; 71 | throw $io; 72 | } 73 | $val = $w->num1 / $w->num2; 74 | break; 75 | default: 76 | $io = new \tutorial\InvalidOperation(); 77 | $io->whatOp = $w->op; 78 | $io->why = "Invalid Operation"; 79 | throw $io; 80 | } 81 | 82 | $log = new \shared\SharedStruct(); 83 | $log->key = $logid; 84 | $log->value = (string)$val; 85 | $this->log[$logid] = $log; 86 | 87 | return $val; 88 | } 89 | 90 | public function getStruct($key) { 91 | error_log("getStruct({$key})"); 92 | // This actually doesn't work because the PHP interpreter is 93 | // restarted for every request. 94 | //return $this->log[$key]; 95 | return new \shared\SharedStruct(array("key" => $key, "value" => "PHP is stateless!")); 96 | } 97 | 98 | public function zip() { 99 | error_log("zip()"); 100 | } 101 | 102 | }; 103 | 104 | header('Content-Type', 'application/x-thrift'); 105 | if (php_sapi_name() == 'cli') { 106 | echo "\r\n"; 107 | } 108 | 109 | $handler = new CalculatorHandler(); 110 | $processor = new \tutorial\CalculatorProcessor($handler); 111 | 112 | $transport = new TBufferedTransport(new TPhpStream(TPhpStream::MODE_R | TPhpStream::MODE_W)); 113 | $protocol = new TBinaryProtocol($transport, true, true); 114 | 115 | $transport->open(); 116 | $processor->process($protocol, $protocol); 117 | $transport->close(); 118 | -------------------------------------------------------------------------------- /shared.thrift: -------------------------------------------------------------------------------- 1 | namespace php shared 2 | 3 | struct SharedStruct { 4 | 1: i32 key 5 | 2: string value 6 | } 7 | 8 | service SharedService { 9 | SharedStruct getStruct(1: i32 key) 10 | } 11 | -------------------------------------------------------------------------------- /tutorial.thrift: -------------------------------------------------------------------------------- 1 | /** 2 | * The first thing to know about are types. The available types in Thrift are: 3 | * 4 | * bool Boolean, one byte 5 | * i8 (byte) Signed 8-bit integer 6 | * i16 Signed 16-bit integer 7 | * i32 Signed 32-bit integer 8 | * i64 Signed 64-bit integer 9 | * double 64-bit floating point value 10 | * string String 11 | * binary Blob (byte array) 12 | * map Map from one type to another 13 | * list Ordered list of one type 14 | * set Set of unique elements of one type 15 | * 16 | * Did you also notice that Thrift supports C style comments? 17 | */ 18 | 19 | // Just in case you were wondering... yes. We support simple C comments too. 20 | 21 | /** 22 | * Thrift files can reference other Thrift files to include common struct 23 | * and service definitions. These are found using the current path, or by 24 | * searching relative to any paths specified with the -I compiler flag. 25 | * 26 | * Included objects are accessed using the name of the .thrift file as a 27 | * prefix. i.e. shared.SharedObject 28 | */ 29 | include "shared.thrift" 30 | 31 | /** 32 | * Thrift files can namespace, package, or prefix their output in various 33 | * target languages. 34 | */ 35 | namespace php tutorial 36 | 37 | /** 38 | * Thrift lets you do typedefs to get pretty names for your types. Standard 39 | * C style here. 40 | */ 41 | typedef i32 MyInteger 42 | 43 | /** 44 | * Thrift also lets you define constants for use across languages. Complex 45 | * types and structs are specified using JSON notation. 46 | */ 47 | const i32 INT32CONSTANT = 9853 48 | const map MAPCONSTANT = {'hello':'world', 'goodnight':'moon'} 49 | 50 | /** 51 | * You can define enums, which are just 32 bit integers. Values are optional 52 | * and start at 1 if not supplied, C style again. 53 | */ 54 | enum Operation { 55 | ADD = 1, 56 | SUBTRACT = 2, 57 | MULTIPLY = 3, 58 | DIVIDE = 4 59 | } 60 | 61 | /** 62 | * Structs are the basic complex data structures. They are comprised of fields 63 | * which each have an integer identifier, a type, a symbolic name, and an 64 | * optional default value. 65 | * 66 | * Fields can be declared "optional", which ensures they will not be included 67 | * in the serialized output if they aren't set. Note that this requires some 68 | * manual management in some languages. 69 | */ 70 | struct Work { 71 | 1: i32 num1 = 0, 72 | 2: i32 num2, 73 | 3: Operation op, 74 | 4: optional string comment, 75 | } 76 | 77 | /** 78 | * Structs can also be exceptions, if they are nasty. 79 | */ 80 | exception InvalidOperation { 81 | 1: i32 whatOp, 82 | 2: string why 83 | } 84 | 85 | /** 86 | * Ahh, now onto the cool part, defining a service. Services just need a name 87 | * and can optionally inherit from another service using the extends keyword. 88 | */ 89 | service Calculator extends shared.SharedService { 90 | 91 | /** 92 | * A method definition looks like C code. It has a return type, arguments, 93 | * and optionally a list of exceptions that it may throw. Note that argument 94 | * lists and exception lists are specified using the exact same syntax as 95 | * field lists in struct or exception definitions. 96 | */ 97 | 98 | void ping(), 99 | 100 | i32 add(1:i32 num1, 2:i32 num2), 101 | 102 | i32 calculate(1:i32 logid, 2:Work w) throws (1:InvalidOperation ouch), 103 | 104 | /** 105 | * This method has a oneway modifier. That means the client only makes 106 | * a request and does not listen for any response at all. Oneway methods 107 | * must be void. 108 | */ 109 | oneway void zip() 110 | 111 | } 112 | --------------------------------------------------------------------------------