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