1 <?php
2 namespace IXR\Server;
3 
4 use IXR\DataType\Base64;
5 use IXR\DataType\Date;
6 use IXR\Message\Error;
7 
8 /**
9  * IXR_IntrospectionServer
10  *
11  * @package IXR
12  * @since 1.5.0
13  */
14 class IntrospectionServer extends Server
15 {
16 
17     private $signatures;
18     private $help;
19 
20     public function __construct()
21     {
22         $this->setCallbacks();
23         $this->setCapabilities();
24         $this->capabilities['introspection'] = [
25             'specUrl' => 'http://xmlrpc.usefulinc.com/doc/reserved.html',
26             'specVersion' => 1
27         ];
28         $this->addCallback(
29             'system.methodSignature',
30             'this:methodSignature',
31             ['array', 'string'],
32             'Returns an array describing the return type and required parameters of a method'
33         );
34         $this->addCallback(
35             'system.getCapabilities',
36             'this:getCapabilities',
37             ['struct'],
38             'Returns a struct describing the XML-RPC specifications supported by this server'
39         );
40         $this->addCallback(
41             'system.listMethods',
42             'this:listMethods',
43             ['array'],
44             'Returns an array of available methods on this server'
45         );
46         $this->addCallback(
47             'system.methodHelp',
48             'this:methodHelp',
49             ['string', 'string'],
50             'Returns a documentation string for the specified method'
51         );
52     }
53 
54     public function addCallback($method, $callback, $args, $help)
55     {
56         $this->callbacks[$method] = $callback;
57         $this->signatures[$method] = $args;
58         $this->help[$method] = $help;
59     }
60 
61     public function call($methodname, $args)
62     {
63         // Make sure it's in an array
64         if ($args && !is_array($args)) {
65             $args = [$args];
66         }
67 
68         // Over-rides default call method, adds signature check
69         if (!$this->hasMethod($methodname)) {
70             return new Error(-32601,
71                 'server error. requested method "' . $this->message->methodName . '" not specified.');
72         }
73         $method = $this->callbacks[$methodname];
74         $signature = $this->signatures[$methodname];
75         array_shift($signature);
76 
77         // Check the number of arguments
78         if (count($args) != count($signature)) {
79             return new Error(-32602, 'server error. wrong number of method parameters');
80         }
81 
82         // Check the argument types
83         $ok = true;
84         $argsbackup = $args;
85         for ($i = 0, $j = count($args); $i < $j; $i++) {
86             $arg = array_shift($args);
87             $type = array_shift($signature);
88             switch ($type) {
89                 case 'int':
90                 case 'i4':
91                     if (is_array($arg) || !is_int($arg)) {
92                         $ok = false;
93                     }
94                     break;
95                 case 'base64':
96                 case 'string':
97                     if (!is_string($arg)) {
98                         $ok = false;
99                     }
100                     break;
101                 case 'boolean':
102                     if ($arg !== false && $arg !== true) {
103                         $ok = false;
104                     }
105                     break;
106                 case 'float':
107                 case 'double':
108                     if (!is_float($arg)) {
109                         $ok = false;
110                     }
111                     break;
112                 case 'date':
113                 case 'dateTime.iso8601':
114                     if (!($arg instanceof Date)) {
115                         $ok = false;
116                     }
117                     break;
118             }
119             if (!$ok) {
120                 return new Error(-32602, 'server error. invalid method parameters');
121             }
122         }
123         // It passed the test - run the "real" method call
124         return parent::call($methodname, $argsbackup);
125     }
126 
127     public function methodSignature($method)
128     {
129         if (!$this->hasMethod($method)) {
130             return new Error(-32601, 'server error. requested method "' . $method . '" not specified.');
131         }
132         // We should be returning an array of types
133         $types = $this->signatures[$method];
134         $return = [];
135         foreach ($types as $type) {
136             switch ($type) {
137                 case 'string':
138                     $return[] = 'string';
139                     break;
140                 case 'int':
141                 case 'i4':
142                     $return[] = 42;
143                     break;
144                 case 'double':
145                     $return[] = 3.1415;
146                     break;
147                 case 'dateTime.iso8601':
148                     $return[] = new Date(time());
149                     break;
150                 case 'boolean':
151                     $return[] = true;
152                     break;
153                 case 'base64':
154                     $return[] = new Base64('base64');
155                     break;
156                 case 'array':
157                     $return[] = ['array'];
158                     break;
159                 case 'struct':
160                     $return[] = ['struct' => 'struct'];
161                     break;
162             }
163         }
164         return $return;
165     }
166 
167     public function methodHelp($method)
168     {
169         return $this->help[$method];
170     }
171 }
172