$f_v) { $repeater = intval(substr($f_v, 1)); if ($repeater == 0) { $repeater = 1; } if ($f_v{1} == '*') { $repeater = count($ar) - $i; } if ($f_v{0} != 'd') { $i += $repeater; continue; } $j = $i + $repeater; for ($a = $i; $a < $j; ++$a) { $p = pack('d', $vals[$i]); $p = strrev($p); list($vals[$i]) = array_values(unpack('d1d', $p)); ++$i; } } $a = 0; foreach ($ar as $ar_k => $ar_v) { $ar[$ar_k] = $vals[$a]; ++$a; } return $ar; } } } } function __construct($hostname = 'localhost', $port = 5000, $timeout) { $this->socket = false; $this->reqhandle = 0x80000000; $this->init($hostname, $port, $timeout); } function __destruct() { $this->terminate(); } protected function init($hostname, $port, $timeout) { $this->bigEndianTest(); // open connection $this->socket = @fsockopen($hostname, $port, $errno, $errstr, $timeout); if (!$this->socket) { throw new Exception("transport error - could not open socket (error: $errno, $errstr)", -32300); } // handshake $array_result = unpack('Vsize', fread($this->socket, 4)); $size = $array_result['size']; if ($size > 64) { throw new Exception('transport error - wrong lowlevel protocol header', -32300); } $handshake = fread($this->socket, $size); if ($handshake == 'GBXRemote 1') { $this->protocol = 1; } elseif ($handshake == 'GBXRemote 2') { $this->protocol = 2; } else { throw new Exception('transport error - wrong lowlevel protocol version', -32300); } } function terminate() { if ($this->socket) { fclose($this->socket); $this->socket = false; } } protected function sendRequest(Request $request) { $xml = $request->getXml(); @stream_set_timeout($this->socket, 20); // timeout 20 s (to write the request) // send request $this->reqhandle++; if ($this->protocol == 1) { $bytes = pack('Va*', strlen($xml), $xml); } else { $bytes = pack('VVa*', strlen($xml), $this->reqhandle, $xml); } $bytes_to_write = strlen($bytes); // increase sent counter ... self::$sent += $bytes_to_write; while ($bytes_to_write > 0) { $r = fwrite($this->socket, $bytes); if ($r === false || $r == 0) { throw new Exception('Connection interupted'); } $bytes_to_write -= $r; if ($bytes_to_write == 0) { break; } $bytes = substr($bytes, $r); } } protected function getResult() { $contents = ''; $contents_length = 0; do { $size = 0; $recvhandle = 0; @stream_set_timeout($this->socket, 5); // timeout 20 s (to read the reply header) // Get result if ($this->protocol == 1) { $contents = fread($this->socket, 4); if (strlen($contents) == 0) { throw new Exception('transport error - connection interrupted!', -32700); } $array_result = unpack('Vsize', $contents); $size = $array_result['size']; $recvhandle = $this->reqhandle; } else { $contents = fread($this->socket, 8); if (strlen($contents) == 0) { throw new Exception('transport error - connection interrupted!', -32700); } $array_result = unpack('Vsize/Vhandle', $contents); $size = $array_result['size']; $recvhandle = $array_result['handle']; // -- amd64 support -- $bits = sprintf('%b', $recvhandle); if (strlen($bits) == 64) { $recvhandle = bindec(substr($bits, 32)); } } if ($recvhandle == 0 || $size == 0) { throw new Exception('transport error - connection interrupted!', -32700); } if ($size > SIZE_MAX) { throw new Exception("transport error - answer too big ($size)", -32700); } self::$received += $size; $contents = ''; $contents_length = 0; @stream_set_timeout($this->socket, 0, 10000); // timeout 10 ms (for successive reads until end) while ($contents_length < $size) { $contents .= fread($this->socket, $size-$contents_length); $contents_length = strlen($contents); } if (($recvhandle & 0x80000000) == 0) { // this is a callback, not our answer! handle= $recvhandle, xml-rpc= $contents // just add it to the message list for the user to read $new_cb_message = new Message($contents); if ($new_cb_message->parse() && $new_cb_message->messageType != 'fault') { array_push($this->cb_message, array($new_cb_message->methodName, $new_cb_message->params)); } } } while ((int)$recvhandle != (int)$this->reqhandle); $this->message = new Message($contents); if (!$this->message->parse()) { // XML error throw new Exception('parse error. not well formed', -32700); } // Is the message a fault? if ($this->message->messageType == 'fault') { throw new Exception($this->message->faultString, $this->message->faultCode); } return $this->message; } function query() { $args = func_get_args(); $method = array_shift($args); if (!$this->socket || $this->protocol == 0) { throw new Exception('transport error - Client not initialized', -32300); } $request = new Request($method, $args); // Check if request is larger than 512 Kbytes if ($request->getLength() > 512*1024-8) { throw new Exception('transport error - request too large!', -32700); } $this->sendRequest($request); return $this->getResult(); } // Non-blocking query method: doesn't read the response function queryIgnoreResult() { $args = func_get_args(); $method = array_shift($args); if (!$this->socket || $this->protocol == 0) { throw new Exception('transport error - Client not initialized', -32300); } $request = new Request($method, $args); // Check if the request is greater than 512 Kbytes to avoid errors // If the method is system.multicall, make two calls (possibly recursively) if ($request->getLength() > 512*1024-8) { if ($method == 'system.multicall' && isset($args[0])) { $count = count($args[0]); // If count is 1, query cannot be reduced if ($count < 2) { throw new Exception('transport error - request too large!', -32700); } $length = floor($count/2); $args1 = array_slice($args[0], 0, $length); $args2 = array_slice($args[0], $length, ($count-$length)); $res1 = $this->queryIgnoreResult('system.multicall', $args1); $res2 = $this->queryIgnoreResult('system.multicall', $args2); return ($res1 && $res2); } // If the method is not a multicall, just stop else { throw new Exception('transport error - request too large!', -32700); } } $this->sendRequest($request); } function getResponse() { // methodResponses can only have one param - return that return $this->message->params[0]; } function readCallbacks($timeout = 2000) { if (!$this->socket || $this->protocol == 0) throw new Exception('transport error - Client not initialized', -32300); if ($this->protocol == 1) return false; // flo: moved to end //$something_received = count($this->cb_message)>0; $contents = ''; $contents_length = 0; @stream_set_timeout($this->socket, 0, 10000); // timeout 10 ms (to read available data) // (assignment in arguments is forbidden since php 5.1.1) $read = array($this->socket); $write = NULL; $except = NULL; $nb = false; try { $nb = @stream_select($read, $write, $except, 0, $timeout); } catch (\Exception $e) { if (strpos($e->getMessage(), 'Invalid CRT') !== false) { $nb = true; } elseif (strpos($e->getMessage(), 'Interrupted system call') !== false) { return; } else { throw $e; } } // workaround for stream_select bug with amd64 if ($nb !== false) { $nb = count($read); } while ($nb !== false && $nb > 0) { $timeout = 0; // we don't want to wait for the full time again, just flush the available data $size = 0; $recvhandle = 0; // Get result $contents = fread($this->socket, 8); if (strlen($contents) == 0) { throw new Exception('transport error - connection interrupted!', -32700); } $array_result = unpack('Vsize/Vhandle', $contents); $size = $array_result['size']; $recvhandle = $array_result['handle']; if ($recvhandle == 0 || $size == 0) { throw new Exception('transport error - connection interrupted!', -32700); } if ($size > SIZE_MAX) { throw new Exception("transport error - answer too big ($size)", -32700); } self::$received += $size; $contents = ''; $contents_length = 0; while ($contents_length < $size) { $contents .= fread($this->socket, $size-$contents_length); $contents_length = strlen($contents); } if (($recvhandle & 0x80000000) == 0) { // this is a callback. handle= $recvhandle, xml-rpc= $contents //echo 'CALLBACK('.$contents_length.')[ '.$contents.' ]' . LF; $new_cb_message = new Message($contents); if ($new_cb_message->parse() && $new_cb_message->messageType != 'fault') { array_push($this->cb_message, array($new_cb_message->methodName, $new_cb_message->params)); } // flo: moved to end ... // $something_received = true; } // (assignment in arguments is forbidden since php 5.1.1) $read = array($this->socket); $write = NULL; $except = NULL; try { $nb = @stream_select($read, $write, $except, 0, $timeout); } catch (\Exception $e) { if (strpos($e->getMessage(), 'Invalid CRT') !== false) { $nb = true; } else { throw $e; } } // workaround for stream_select bug with amd64 if ($nb !== false) { $nb = count($read); } } return !empty($this->cb_message); } function getCallbackResponses() { // (look at the end of basic.php for an example) $messages = $this->cb_message; $this->cb_message = array(); return $messages; } } ?>