xref: /dokuwiki/vendor/kissifrot/php-ixr/src/Server/Server.php (revision 934f970a961272cf366d019f15b7c0685de4fc7e)
17f8f2456SAndreas Gohr<?php
27f8f2456SAndreas Gohr
37f8f2456SAndreas Gohrnamespace IXR\Server;
47f8f2456SAndreas Gohr
57f8f2456SAndreas Gohr
67f8f2456SAndreas Gohruse IXR\DataType\Value;
77f8f2456SAndreas Gohruse IXR\Exception\ServerException;
87f8f2456SAndreas Gohruse IXR\Message\Error;
97f8f2456SAndreas Gohruse IXR\Message\Message;
107f8f2456SAndreas Gohr
117f8f2456SAndreas Gohrclass Server
127f8f2456SAndreas Gohr{
137f8f2456SAndreas Gohr    protected $callbacks = [];
147f8f2456SAndreas Gohr    protected $message;
157f8f2456SAndreas Gohr    protected $capabilities;
167f8f2456SAndreas Gohr
17*934f970aSAndreas Gohr    /**
18*934f970aSAndreas Gohr     * @throws ServerException
19*934f970aSAndreas Gohr     */
207f8f2456SAndreas Gohr    public function __construct($callbacks = false, $data = false, $wait = false)
217f8f2456SAndreas Gohr    {
227f8f2456SAndreas Gohr        $this->setCapabilities();
237f8f2456SAndreas Gohr        if ($callbacks) {
247f8f2456SAndreas Gohr            $this->callbacks = $callbacks;
257f8f2456SAndreas Gohr        }
267f8f2456SAndreas Gohr        $this->setCallbacks();
277f8f2456SAndreas Gohr        if (!$wait) {
287f8f2456SAndreas Gohr            $this->serve($data);
297f8f2456SAndreas Gohr        }
307f8f2456SAndreas Gohr    }
317f8f2456SAndreas Gohr
32*934f970aSAndreas Gohr    /**
33*934f970aSAndreas Gohr     * @throws ServerException
34*934f970aSAndreas Gohr     */
357f8f2456SAndreas Gohr    public function serve($data = false)
367f8f2456SAndreas Gohr    {
377f8f2456SAndreas Gohr        if (!$data) {
387f8f2456SAndreas Gohr            if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] !== 'POST') {
397f8f2456SAndreas Gohr                header('Content-Type: text/plain'); // merged from WP #9093
407f8f2456SAndreas Gohr                throw new ServerException('XML-RPC server accepts POST requests only.');
417f8f2456SAndreas Gohr            }
427f8f2456SAndreas Gohr
437f8f2456SAndreas Gohr            $data = file_get_contents('php://input');
447f8f2456SAndreas Gohr        }
457f8f2456SAndreas Gohr        $this->message = new Message($data);
467f8f2456SAndreas Gohr        if (!$this->message->parse()) {
477f8f2456SAndreas Gohr            $this->error(-32700, 'parse error. not well formed');
487f8f2456SAndreas Gohr        }
497f8f2456SAndreas Gohr        if ($this->message->messageType != 'methodCall') {
507f8f2456SAndreas Gohr            $this->error(-32600, 'server error. invalid xml-rpc. not conforming to spec. Request must be a methodCall');
517f8f2456SAndreas Gohr        }
527f8f2456SAndreas Gohr        $result = $this->call($this->message->methodName, $this->message->params);
537f8f2456SAndreas Gohr
547f8f2456SAndreas Gohr        // Is the result an error?
557f8f2456SAndreas Gohr        if ($result instanceof Error) {
567f8f2456SAndreas Gohr            $this->error($result);
577f8f2456SAndreas Gohr        }
587f8f2456SAndreas Gohr
597f8f2456SAndreas Gohr        // Encode the result
607f8f2456SAndreas Gohr        $r = new Value($result);
617f8f2456SAndreas Gohr        $resultxml = $r->getXml();
627f8f2456SAndreas Gohr
637f8f2456SAndreas Gohr        // Create the XML
647f8f2456SAndreas Gohr        $xml = <<<EOD
657f8f2456SAndreas Gohr<methodResponse>
667f8f2456SAndreas Gohr  <params>
677f8f2456SAndreas Gohr    <param>
687f8f2456SAndreas Gohr      <value>
697f8f2456SAndreas Gohr      $resultxml
707f8f2456SAndreas Gohr      </value>
717f8f2456SAndreas Gohr    </param>
727f8f2456SAndreas Gohr  </params>
737f8f2456SAndreas Gohr</methodResponse>
747f8f2456SAndreas Gohr
757f8f2456SAndreas GohrEOD;
767f8f2456SAndreas Gohr        // Send it
777f8f2456SAndreas Gohr        $this->output($xml);
787f8f2456SAndreas Gohr    }
797f8f2456SAndreas Gohr
807f8f2456SAndreas Gohr    protected function call($methodname, $args)
817f8f2456SAndreas Gohr    {
827f8f2456SAndreas Gohr        if (!$this->hasMethod($methodname)) {
837f8f2456SAndreas Gohr            return new Error(-32601, 'server error. requested method ' . $methodname . ' does not exist.');
847f8f2456SAndreas Gohr        }
857f8f2456SAndreas Gohr        $method = $this->callbacks[$methodname];
867f8f2456SAndreas Gohr        // Perform the callback and send the response
877f8f2456SAndreas Gohr
887f8f2456SAndreas Gohr        if (is_array($args) && count($args) == 1) {
897f8f2456SAndreas Gohr            // If only one parameter just send that instead of the whole array
907f8f2456SAndreas Gohr            $args = $args[0];
917f8f2456SAndreas Gohr        }
927f8f2456SAndreas Gohr
937f8f2456SAndreas Gohr        try {
947f8f2456SAndreas Gohr            // Are we dealing with a function or a method?
957f8f2456SAndreas Gohr            if (is_string($method) && substr($method, 0, 5) === 'this:') {
967f8f2456SAndreas Gohr                // It's a class method - check it exists
977f8f2456SAndreas Gohr                $method = substr($method, 5);
987f8f2456SAndreas Gohr
997f8f2456SAndreas Gohr                return $this->$method($args);
1007f8f2456SAndreas Gohr            }
1017f8f2456SAndreas Gohr
1027f8f2456SAndreas Gohr            return call_user_func($method, $args);
1037f8f2456SAndreas Gohr        } catch (\BadFunctionCallException $exception) {
1047f8f2456SAndreas Gohr            return new Error(-32601, "server error. requested callable '{$method}' does not exist.");
1057f8f2456SAndreas Gohr        }
1067f8f2456SAndreas Gohr
1077f8f2456SAndreas Gohr    }
1087f8f2456SAndreas Gohr
1097f8f2456SAndreas Gohr    public function error($error, $message = false)
1107f8f2456SAndreas Gohr    {
1117f8f2456SAndreas Gohr        // Accepts either an error object or an error code and message
1127f8f2456SAndreas Gohr        if ($message && !is_object($error)) {
1137f8f2456SAndreas Gohr            $error = new Error($error, $message);
1147f8f2456SAndreas Gohr        }
1157f8f2456SAndreas Gohr        $this->output($error->getXml());
1167f8f2456SAndreas Gohr    }
1177f8f2456SAndreas Gohr
1187f8f2456SAndreas Gohr    public function output($xml)
1197f8f2456SAndreas Gohr    {
1207f8f2456SAndreas Gohr        $xml = '<?xml version="1.0"?>' . "\n" . $xml;
1217f8f2456SAndreas Gohr        $length = strlen($xml);
1227f8f2456SAndreas Gohr        header('Connection: close');
1237f8f2456SAndreas Gohr        header('Content-Length: ' . $length);
1247f8f2456SAndreas Gohr        header('Content-Type: text/xml');
1257f8f2456SAndreas Gohr        header('Date: ' . date('r'));
1267f8f2456SAndreas Gohr        echo $xml;
1277f8f2456SAndreas Gohr        exit;
1287f8f2456SAndreas Gohr    }
1297f8f2456SAndreas Gohr
1307f8f2456SAndreas Gohr    protected function hasMethod($method)
1317f8f2456SAndreas Gohr    {
1327f8f2456SAndreas Gohr        return in_array($method, array_keys($this->callbacks));
1337f8f2456SAndreas Gohr    }
1347f8f2456SAndreas Gohr
1357f8f2456SAndreas Gohr    protected function setCapabilities()
1367f8f2456SAndreas Gohr    {
1377f8f2456SAndreas Gohr        // Initialises capabilities array
1387f8f2456SAndreas Gohr        $this->capabilities = [
1397f8f2456SAndreas Gohr            'xmlrpc' => [
1407f8f2456SAndreas Gohr                'specUrl' => 'http://www.xmlrpc.com/spec',
1417f8f2456SAndreas Gohr                'specVersion' => 1
1427f8f2456SAndreas Gohr            ],
1437f8f2456SAndreas Gohr            'faults_interop' => [
1447f8f2456SAndreas Gohr                'specUrl' => 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php',
1457f8f2456SAndreas Gohr                'specVersion' => 20010516
1467f8f2456SAndreas Gohr            ],
1477f8f2456SAndreas Gohr            'system.multicall' => [
1487f8f2456SAndreas Gohr                'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208',
1497f8f2456SAndreas Gohr                'specVersion' => 1
1507f8f2456SAndreas Gohr            ],
1517f8f2456SAndreas Gohr        ];
1527f8f2456SAndreas Gohr    }
1537f8f2456SAndreas Gohr
1547f8f2456SAndreas Gohr    public function getCapabilities($args)
1557f8f2456SAndreas Gohr    {
1567f8f2456SAndreas Gohr        return $this->capabilities;
1577f8f2456SAndreas Gohr    }
1587f8f2456SAndreas Gohr
1597f8f2456SAndreas Gohr    public function setCallbacks()
1607f8f2456SAndreas Gohr    {
1617f8f2456SAndreas Gohr        $this->callbacks['system.getCapabilities'] = 'this:getCapabilities';
1627f8f2456SAndreas Gohr        $this->callbacks['system.listMethods'] = 'this:listMethods';
1637f8f2456SAndreas Gohr        $this->callbacks['system.multicall'] = 'this:multiCall';
1647f8f2456SAndreas Gohr    }
1657f8f2456SAndreas Gohr
1667f8f2456SAndreas Gohr    public function listMethods($args)
1677f8f2456SAndreas Gohr    {
1687f8f2456SAndreas Gohr        // Returns a list of methods - uses array_reverse to ensure user defined
1697f8f2456SAndreas Gohr        // methods are listed before server defined methods
1707f8f2456SAndreas Gohr        return array_reverse(array_keys($this->callbacks));
1717f8f2456SAndreas Gohr    }
1727f8f2456SAndreas Gohr
1737f8f2456SAndreas Gohr    public function multiCall($methodcalls)
1747f8f2456SAndreas Gohr    {
1757f8f2456SAndreas Gohr        // See http://www.xmlrpc.com/discuss/msgReader$1208
1767f8f2456SAndreas Gohr        $return = [];
1777f8f2456SAndreas Gohr        foreach ($methodcalls as $call) {
1787f8f2456SAndreas Gohr            $method = $call['methodName'];
1797f8f2456SAndreas Gohr            $params = $call['params'];
1807f8f2456SAndreas Gohr            if ($method == 'system.multicall') {
1817f8f2456SAndreas Gohr                $result = new Error(-32600, 'Recursive calls to system.multicall are forbidden');
1827f8f2456SAndreas Gohr            } else {
1837f8f2456SAndreas Gohr                $result = $this->call($method, $params);
1847f8f2456SAndreas Gohr            }
1857f8f2456SAndreas Gohr            if ($result instanceof Error) {
1867f8f2456SAndreas Gohr                $return[] = [
1877f8f2456SAndreas Gohr                    'faultCode' => $result->code,
1887f8f2456SAndreas Gohr                    'faultString' => $result->message
1897f8f2456SAndreas Gohr                ];
1907f8f2456SAndreas Gohr            } else {
1917f8f2456SAndreas Gohr                $return[] = [$result];
1927f8f2456SAndreas Gohr            }
1937f8f2456SAndreas Gohr        }
1947f8f2456SAndreas Gohr        return $return;
1957f8f2456SAndreas Gohr    }
1967f8f2456SAndreas Gohr}
197