1<?php 2namespace IXR\Client; 3 4use IXR\Message\Error; 5use IXR\Message\Message; 6use IXR\Request\Request; 7 8/** 9 * IXR_Client 10 * 11 * @package IXR 12 * @since 1.5.0 13 * 14 */ 15class Client 16{ 17 protected $server; 18 protected $port; 19 protected $path; 20 protected $useragent; 21 protected $response; 22 /** @var bool|Message */ 23 protected $message = false; 24 protected $debug = false; 25 /** @var int Connection timeout in seconds */ 26 protected $timeout; 27 /** @var null|int Timeout for actual data transfer; in seconds */ 28 protected $timeout_io = null; 29 protected $headers = []; 30 31 /** 32 * @var null|Error 33 * 34 * Storage place for an error message 35 */ 36 private $error = null; 37 38 public function __construct($server, $path = false, $port = 80, $timeout = 15, $timeout_io = null) 39 { 40 if (!$path) { 41 // Assume we have been given a URL instead 42 $bits = parse_url($server); 43 $this->server = $bits['host']; 44 $this->port = isset($bits['port']) ? $bits['port'] : 80; 45 $this->path = isset($bits['path']) ? $bits['path'] : '/'; 46 47 // Make absolutely sure we have a path 48 if (!$this->path) { 49 $this->path = '/'; 50 } 51 52 if (!empty($bits['query'])) { 53 $this->path .= '?' . $bits['query']; 54 } 55 } else { 56 $this->server = $server; 57 $this->path = $path; 58 $this->port = $port; 59 } 60 $this->useragent = 'The Incutio XML-RPC PHP Library'; 61 $this->timeout = $timeout; 62 $this->timeout_io = $timeout_io; 63 } 64 65 public function query() 66 { 67 $args = func_get_args(); 68 $method = array_shift($args); 69 $request = new Request($method, $args); 70 $length = $request->getLength(); 71 $xml = $request->getXml(); 72 $r = "\r\n"; 73 $request = "POST {$this->path} HTTP/1.0$r"; 74 75 // Merged from WP #8145 - allow custom headers 76 $this->headers['Host'] = $this->server; 77 $this->headers['Content-Type'] = 'text/xml'; 78 $this->headers['User-Agent'] = $this->useragent; 79 $this->headers['Content-Length'] = $length; 80 81 foreach ($this->headers as $header => $value) { 82 $request .= "{$header}: {$value}{$r}"; 83 } 84 $request .= $r; 85 86 $request .= $xml; 87 88 // Now send the request 89 if ($this->debug) { 90 echo '<pre class="ixr_request">' . htmlspecialchars($request) . "\n</pre>\n\n"; 91 } 92 93 if ($this->timeout) { 94 try { 95 $fp = fsockopen($this->server, $this->port, $errno, $errstr, $this->timeout); 96 } catch (\Exception $e) { 97 $fp = false; 98 } 99 } else { 100 try { 101 $fp = fsockopen($this->server, $this->port, $errno, $errstr); 102 } catch (\Exception $e) { 103 $fp = false; 104 } 105 } 106 if (!$fp) { 107 return $this->handleError(-32300, 'transport error - could not open socket'); 108 } 109 if (null !== $this->timeout_io) { 110 stream_set_timeout($fp, $this->timeout_io); 111 } 112 fputs($fp, $request); 113 $contents = ''; 114 $debugContents = ''; 115 $gotFirstLine = false; 116 $gettingHeaders = true; 117 while (!feof($fp)) { 118 $line = fgets($fp, 4096); 119 if (!$gotFirstLine) { 120 // Check line for '200' 121 if (strstr($line, '200') === false) { 122 return $this->handleError(-32300, 'transport error - HTTP status code was not 200'); 123 } 124 $gotFirstLine = true; 125 } 126 if (trim($line) == '') { 127 $gettingHeaders = false; 128 } 129 if (!$gettingHeaders) { 130 // merged from WP #12559 - remove trim 131 $contents .= $line; 132 } 133 if ($this->debug) { 134 $debugContents .= $line; 135 } 136 } 137 if ($this->debug) { 138 echo '<pre class="ixr_response">' . htmlspecialchars($debugContents) . "\n</pre>\n\n"; 139 } 140 141 // Now parse what we've got back 142 $this->message = new Message($contents); 143 if (!$this->message->parse()) { 144 // XML error 145 return $this->handleError(-32700, 'Parse error. Message not well formed'); 146 } 147 148 // Is the message a fault? 149 if ($this->message->messageType == 'fault') { 150 return $this->handleError($this->message->faultCode, $this->message->faultString); 151 } 152 153 // Message must be OK 154 return true; 155 } 156 157 public function getResponse() 158 { 159 // methodResponses can only have one param - return that 160 return $this->message->params[0]; 161 } 162 163 public function isError() 164 { 165 return (is_object($this->error)); 166 } 167 168 protected function handleError($errorCode, $errorMessage) 169 { 170 $this->error = new Error($errorCode, $errorMessage); 171 172 return false; 173 } 174 175 public function getError() 176 { 177 return $this->error; 178 } 179 180 public function getErrorCode() 181 { 182 return $this->error->code; 183 } 184 185 public function getErrorMessage() 186 { 187 return $this->error->message; 188 } 189 190 191 /** 192 * Gets the current timeout set for data transfer 193 * @return int|null 194 */ 195 public function getTimeoutIo() 196 { 197 return $this->timeout_io; 198 } 199 200 /** 201 * Sets the timeout for data transfer 202 * @param int $timeout_io 203 * @return $this 204 */ 205 public function setTimeoutIo($timeout_io) 206 { 207 $this->timeout_io = $timeout_io; 208 } 209} 210