xref: /dokuwiki/vendor/kissifrot/php-ixr/src/Server/Server.php (revision 7f8f24562b596c56d79e46eba9f82780df5725cb)
1*7f8f2456SAndreas Gohr<?php
2*7f8f2456SAndreas Gohr
3*7f8f2456SAndreas Gohrnamespace IXR\Server;
4*7f8f2456SAndreas Gohr
5*7f8f2456SAndreas Gohr
6*7f8f2456SAndreas Gohruse IXR\DataType\Value;
7*7f8f2456SAndreas Gohruse IXR\Exception\ServerException;
8*7f8f2456SAndreas Gohruse IXR\Message\Error;
9*7f8f2456SAndreas Gohruse IXR\Message\Message;
10*7f8f2456SAndreas Gohr
11*7f8f2456SAndreas Gohrclass Server
12*7f8f2456SAndreas Gohr{
13*7f8f2456SAndreas Gohr    protected $callbacks = [];
14*7f8f2456SAndreas Gohr    protected $message;
15*7f8f2456SAndreas Gohr    protected $capabilities;
16*7f8f2456SAndreas Gohr
17*7f8f2456SAndreas Gohr    public function __construct($callbacks = false, $data = false, $wait = false)
18*7f8f2456SAndreas Gohr    {
19*7f8f2456SAndreas Gohr        $this->setCapabilities();
20*7f8f2456SAndreas Gohr        if ($callbacks) {
21*7f8f2456SAndreas Gohr            $this->callbacks = $callbacks;
22*7f8f2456SAndreas Gohr        }
23*7f8f2456SAndreas Gohr        $this->setCallbacks();
24*7f8f2456SAndreas Gohr        if (!$wait) {
25*7f8f2456SAndreas Gohr            $this->serve($data);
26*7f8f2456SAndreas Gohr        }
27*7f8f2456SAndreas Gohr    }
28*7f8f2456SAndreas Gohr
29*7f8f2456SAndreas Gohr    public function serve($data = false)
30*7f8f2456SAndreas Gohr    {
31*7f8f2456SAndreas Gohr        if (!$data) {
32*7f8f2456SAndreas Gohr            if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] !== 'POST') {
33*7f8f2456SAndreas Gohr                header('Content-Type: text/plain'); // merged from WP #9093
34*7f8f2456SAndreas Gohr                throw new ServerException('XML-RPC server accepts POST requests only.');
35*7f8f2456SAndreas Gohr            }
36*7f8f2456SAndreas Gohr
37*7f8f2456SAndreas Gohr            $data = file_get_contents('php://input');
38*7f8f2456SAndreas Gohr        }
39*7f8f2456SAndreas Gohr        $this->message = new Message($data);
40*7f8f2456SAndreas Gohr        if (!$this->message->parse()) {
41*7f8f2456SAndreas Gohr            $this->error(-32700, 'parse error. not well formed');
42*7f8f2456SAndreas Gohr        }
43*7f8f2456SAndreas Gohr        if ($this->message->messageType != 'methodCall') {
44*7f8f2456SAndreas Gohr            $this->error(-32600, 'server error. invalid xml-rpc. not conforming to spec. Request must be a methodCall');
45*7f8f2456SAndreas Gohr        }
46*7f8f2456SAndreas Gohr        $result = $this->call($this->message->methodName, $this->message->params);
47*7f8f2456SAndreas Gohr
48*7f8f2456SAndreas Gohr        // Is the result an error?
49*7f8f2456SAndreas Gohr        if ($result instanceof Error) {
50*7f8f2456SAndreas Gohr            $this->error($result);
51*7f8f2456SAndreas Gohr        }
52*7f8f2456SAndreas Gohr
53*7f8f2456SAndreas Gohr        // Encode the result
54*7f8f2456SAndreas Gohr        $r = new Value($result);
55*7f8f2456SAndreas Gohr        $resultxml = $r->getXml();
56*7f8f2456SAndreas Gohr
57*7f8f2456SAndreas Gohr        // Create the XML
58*7f8f2456SAndreas Gohr        $xml = <<<EOD
59*7f8f2456SAndreas Gohr<methodResponse>
60*7f8f2456SAndreas Gohr  <params>
61*7f8f2456SAndreas Gohr    <param>
62*7f8f2456SAndreas Gohr      <value>
63*7f8f2456SAndreas Gohr      $resultxml
64*7f8f2456SAndreas Gohr      </value>
65*7f8f2456SAndreas Gohr    </param>
66*7f8f2456SAndreas Gohr  </params>
67*7f8f2456SAndreas Gohr</methodResponse>
68*7f8f2456SAndreas Gohr
69*7f8f2456SAndreas GohrEOD;
70*7f8f2456SAndreas Gohr        // Send it
71*7f8f2456SAndreas Gohr        $this->output($xml);
72*7f8f2456SAndreas Gohr    }
73*7f8f2456SAndreas Gohr
74*7f8f2456SAndreas Gohr    protected function call($methodname, $args)
75*7f8f2456SAndreas Gohr    {
76*7f8f2456SAndreas Gohr        if (!$this->hasMethod($methodname)) {
77*7f8f2456SAndreas Gohr            return new Error(-32601, 'server error. requested method ' . $methodname . ' does not exist.');
78*7f8f2456SAndreas Gohr        }
79*7f8f2456SAndreas Gohr        $method = $this->callbacks[$methodname];
80*7f8f2456SAndreas Gohr        // Perform the callback and send the response
81*7f8f2456SAndreas Gohr
82*7f8f2456SAndreas Gohr        if (is_array($args) && count($args) == 1) {
83*7f8f2456SAndreas Gohr            // If only one parameter just send that instead of the whole array
84*7f8f2456SAndreas Gohr            $args = $args[0];
85*7f8f2456SAndreas Gohr        }
86*7f8f2456SAndreas Gohr
87*7f8f2456SAndreas Gohr        try {
88*7f8f2456SAndreas Gohr            // Are we dealing with a function or a method?
89*7f8f2456SAndreas Gohr            if (is_string($method) && substr($method, 0, 5) === 'this:') {
90*7f8f2456SAndreas Gohr                // It's a class method - check it exists
91*7f8f2456SAndreas Gohr                $method = substr($method, 5);
92*7f8f2456SAndreas Gohr
93*7f8f2456SAndreas Gohr                return $this->$method($args);
94*7f8f2456SAndreas Gohr            }
95*7f8f2456SAndreas Gohr
96*7f8f2456SAndreas Gohr            return call_user_func($method, $args);
97*7f8f2456SAndreas Gohr        } catch (\BadFunctionCallException $exception) {
98*7f8f2456SAndreas Gohr            return new Error(-32601, "server error. requested callable '{$method}' does not exist.");
99*7f8f2456SAndreas Gohr        }
100*7f8f2456SAndreas Gohr
101*7f8f2456SAndreas Gohr    }
102*7f8f2456SAndreas Gohr
103*7f8f2456SAndreas Gohr    public function error($error, $message = false)
104*7f8f2456SAndreas Gohr    {
105*7f8f2456SAndreas Gohr        // Accepts either an error object or an error code and message
106*7f8f2456SAndreas Gohr        if ($message && !is_object($error)) {
107*7f8f2456SAndreas Gohr            $error = new Error($error, $message);
108*7f8f2456SAndreas Gohr        }
109*7f8f2456SAndreas Gohr        $this->output($error->getXml());
110*7f8f2456SAndreas Gohr    }
111*7f8f2456SAndreas Gohr
112*7f8f2456SAndreas Gohr    public function output($xml)
113*7f8f2456SAndreas Gohr    {
114*7f8f2456SAndreas Gohr        $xml = '<?xml version="1.0"?>' . "\n" . $xml;
115*7f8f2456SAndreas Gohr        $length = strlen($xml);
116*7f8f2456SAndreas Gohr        header('Connection: close');
117*7f8f2456SAndreas Gohr        header('Content-Length: ' . $length);
118*7f8f2456SAndreas Gohr        header('Content-Type: text/xml');
119*7f8f2456SAndreas Gohr        header('Date: ' . date('r'));
120*7f8f2456SAndreas Gohr        echo $xml;
121*7f8f2456SAndreas Gohr        exit;
122*7f8f2456SAndreas Gohr    }
123*7f8f2456SAndreas Gohr
124*7f8f2456SAndreas Gohr    protected function hasMethod($method)
125*7f8f2456SAndreas Gohr    {
126*7f8f2456SAndreas Gohr        return in_array($method, array_keys($this->callbacks));
127*7f8f2456SAndreas Gohr    }
128*7f8f2456SAndreas Gohr
129*7f8f2456SAndreas Gohr    protected function setCapabilities()
130*7f8f2456SAndreas Gohr    {
131*7f8f2456SAndreas Gohr        // Initialises capabilities array
132*7f8f2456SAndreas Gohr        $this->capabilities = [
133*7f8f2456SAndreas Gohr            'xmlrpc' => [
134*7f8f2456SAndreas Gohr                'specUrl' => 'http://www.xmlrpc.com/spec',
135*7f8f2456SAndreas Gohr                'specVersion' => 1
136*7f8f2456SAndreas Gohr            ],
137*7f8f2456SAndreas Gohr            'faults_interop' => [
138*7f8f2456SAndreas Gohr                'specUrl' => 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php',
139*7f8f2456SAndreas Gohr                'specVersion' => 20010516
140*7f8f2456SAndreas Gohr            ],
141*7f8f2456SAndreas Gohr            'system.multicall' => [
142*7f8f2456SAndreas Gohr                'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208',
143*7f8f2456SAndreas Gohr                'specVersion' => 1
144*7f8f2456SAndreas Gohr            ],
145*7f8f2456SAndreas Gohr        ];
146*7f8f2456SAndreas Gohr    }
147*7f8f2456SAndreas Gohr
148*7f8f2456SAndreas Gohr    public function getCapabilities($args)
149*7f8f2456SAndreas Gohr    {
150*7f8f2456SAndreas Gohr        return $this->capabilities;
151*7f8f2456SAndreas Gohr    }
152*7f8f2456SAndreas Gohr
153*7f8f2456SAndreas Gohr    public function setCallbacks()
154*7f8f2456SAndreas Gohr    {
155*7f8f2456SAndreas Gohr        $this->callbacks['system.getCapabilities'] = 'this:getCapabilities';
156*7f8f2456SAndreas Gohr        $this->callbacks['system.listMethods'] = 'this:listMethods';
157*7f8f2456SAndreas Gohr        $this->callbacks['system.multicall'] = 'this:multiCall';
158*7f8f2456SAndreas Gohr    }
159*7f8f2456SAndreas Gohr
160*7f8f2456SAndreas Gohr    public function listMethods($args)
161*7f8f2456SAndreas Gohr    {
162*7f8f2456SAndreas Gohr        // Returns a list of methods - uses array_reverse to ensure user defined
163*7f8f2456SAndreas Gohr        // methods are listed before server defined methods
164*7f8f2456SAndreas Gohr        return array_reverse(array_keys($this->callbacks));
165*7f8f2456SAndreas Gohr    }
166*7f8f2456SAndreas Gohr
167*7f8f2456SAndreas Gohr    public function multiCall($methodcalls)
168*7f8f2456SAndreas Gohr    {
169*7f8f2456SAndreas Gohr        // See http://www.xmlrpc.com/discuss/msgReader$1208
170*7f8f2456SAndreas Gohr        $return = [];
171*7f8f2456SAndreas Gohr        foreach ($methodcalls as $call) {
172*7f8f2456SAndreas Gohr            $method = $call['methodName'];
173*7f8f2456SAndreas Gohr            $params = $call['params'];
174*7f8f2456SAndreas Gohr            if ($method == 'system.multicall') {
175*7f8f2456SAndreas Gohr                $result = new Error(-32600, 'Recursive calls to system.multicall are forbidden');
176*7f8f2456SAndreas Gohr            } else {
177*7f8f2456SAndreas Gohr                $result = $this->call($method, $params);
178*7f8f2456SAndreas Gohr            }
179*7f8f2456SAndreas Gohr            if ($result instanceof Error) {
180*7f8f2456SAndreas Gohr                $return[] = [
181*7f8f2456SAndreas Gohr                    'faultCode' => $result->code,
182*7f8f2456SAndreas Gohr                    'faultString' => $result->message
183*7f8f2456SAndreas Gohr                ];
184*7f8f2456SAndreas Gohr            } else {
185*7f8f2456SAndreas Gohr                $return[] = [$result];
186*7f8f2456SAndreas Gohr            }
187*7f8f2456SAndreas Gohr        }
188*7f8f2456SAndreas Gohr        return $return;
189*7f8f2456SAndreas Gohr    }
190*7f8f2456SAndreas Gohr}
191