xref: /dokuwiki/vendor/kissifrot/php-ixr/src/Client/Client.php (revision 934f970a961272cf366d019f15b7c0685de4fc7e)
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