XRL  2.0.0
Simple XML-RPC Library (both client and server)
CapableServer.php
1 <?php
2 /*
3  * This file is part of XRL, a simple XML-RPC Library for PHP.
4  *
5  * Copyright (c) 2012, XRL Team. All rights reserved.
6  * XRL is licensed under the 3-clause BSD License.
7  *
8  * For the full copyright and license information, please view the LICENSE
9  * file that was distributed with this source code.
10  */
11 
12 namespace fpoirotte\XRL;
13 
20 {
22  protected $server;
23 
25  protected $whitelist;
26 
39  protected function __construct(\fpoirotte\XRL\Server $server, array $whitelist = null)
40  {
41  $this->server = $server;
42  $this->whitelist = $whitelist;
43  }
44 
61  public static function enable(\fpoirotte\XRL\Server $server, array $whitelist = null)
62  {
63  $wrapper = new static($server, $whitelist);
64  $server->expose($wrapper, 'system');
65  return $server;
66  }
67 
82  protected static function extractTypes($doc)
83  {
84  $doc = trim(substr($doc, 3, -2), " \t\r\n*");
85  $doc = str_replace(array("\r\n", "\r"), "\n", $doc);
86  $lines = explode("\n", $doc . "\n");
87 
88  $tag = null;
89  $tags = array(
90  'params' => array(),
91  'retval' => 'null',
92  );
93  $buffer = '';
94 
95  foreach ($lines as $line) {
96  $line = trim($line, " \r\n\t*");
97 
98  if ($tag !== null && $line === '') {
99  switch ($tag) {
100  case 'param':
101  $type = (string) substr($buffer, 0, strcspn($buffer, " \r\n\t"));
102  $buffer = ltrim(substr($buffer, strcspn($buffer, " \r\n\t")));
103  if (strncmp($buffer, '$', 1)) {
104  break;
105  }
106 
107  $name = (string) substr($buffer, 1, strcspn($buffer, " \r\n\t") - 1);
108  $tags['params'][$name] = $type;
109  break;
110 
111  case 'retval':
112  $type = (string) substr($buffer, 0, strcspn($buffer, " \r\n\t"));
113  $tags['retval'] = $type;
114  break;
115  }
116 
117  $tag = null;
118  $buffer = '';
119  continue;
120  }
121 
122  if ($tag === null) {
123  // \command or @command.
124  if (!strncmp($line, '\\', 1) || !strncmp($line, '@', 1)) {
125  $tag = (string) substr($line, 1, strcspn($line, " \r\n\t") - 1);
126  $buffer = ltrim(substr($line, strcspn($line, " \r\n\t")));
127  }
128  } else {
129  // Continuation of previous paragraph.
130  $buffer .= "\n$line";
131  }
132  }
133  return $tags;
134  }
135 
147  protected static function adaptType($type)
148  {
149  switch ($type) {
150  case 'integer':
151  return 'int';
152 
153  case 'float':
154  return 'double';
155 
156  case 'null':
157  return 'nil';
158 
159  case 'DateTime':
160  return 'dateTime.iso8601';
161 
162  case 'boolean':
163  return 'bool';
164 
165  case 'int':
166  case 'double':
167  case 'string':
168  case 'bool':
169  case 'array':
170  return $type;
171  }
172  return null;
173  }
174 
184  public function getCapabilities()
185  {
186  return array(
187  'xmlrpc' => array(
188  'specUrl' => 'http://www.xmlrpc.com/spec',
189  'specVersion' => 1,
190  ),
191 
192  'introspect' => array(
193  'specUrl' => 'http://xmlrpc-c.sourceforge.net/xmlrpc-c/introspection.html',
194  'specVersion' => 1,
195  ),
196 
197  'faults_interop' => array(
198  'specUrl' => 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php',
199  'specVersion' => 20010516,
200  ),
201  );
202  }
203 
210  public function listMethods()
211  {
212  $methods = array_keys($this->server->getIterator()->getArrayCopy());
213  if ($this->whitelist !== null) {
214  $methods = array_intersect($methods, $this->whitelist);
215  }
216  return $methods;
217  }
218 
230  public function methodSignature($method)
231  {
232  if (!is_string($method) || !isset($this->server[$method])) {
233  throw new \InvalidArgumentException('Invalid method');
234  }
235 
236  $reflector = $this->server[$method]->getReflector();
237  $doc = $reflector->getDocComment();
238  if ($doc === false) {
239  return 'undef';
240  }
241 
242  $tags = static::extractTypes($doc);
243  $returnType = static::adaptType($tags['retval']);
244  if ($returnType === null) {
245  return 'undef';
246  }
247 
248  $params = array();
249  foreach ($reflector->getParameters() as $param) {
250  if (!isset($tags['params'][$param->getName()])) {
251  return 'undef';
252  }
253  $type = static::adaptType($tags['params'][$param->getName()]);
254  if ($type === null) {
255  return 'undef';
256  }
257  $params[] = $type;
258  }
259 
260  return array(array_merge(array($returnType), $params));
261  }
262 
272  public function methodHelp($method)
273  {
274  if (!is_string($method) || !isset($this->server[$method])) {
275  throw new \InvalidArgumentException('Invalid method');
276  }
277 
278  $reflector = $this->server[$method]->getReflector();
279  $doc = $reflector->getDocComment();
280  if ($doc === false) {
281  return '';
282  }
283 
284  // Remove comment delimiters.
285  $doc = substr($doc, 2, -2);
286 
287  // Normalize line endings.
288  $doc = str_replace(array("\r\n", "\r"), "\n", $doc);
289 
290  // Trim leading/trailing whitespace and '*' for every line.
291  $help = array_map(
292  function ($l) {
293  return trim(trim($l), '*');
294  },
295  explode("\n", $doc)
296  );
297 
298  // Count number of empty columns on non-empty lines
299  // before the actual start of the text.
300  $cols = min(
301  array_map(
302  function ($l) {
303  return strspn($l, " \t");
304  },
305  array_filter($help, 'strlen')
306  )
307  );
308 
309  // Remove those columns from the result.
310  $help = array_map(
311  function ($l) use ($cols) {
312  return (string) substr($l, $cols);
313  },
314  $help
315  );
316 
317  // Produce the final output.
318  return implode("\n", $help);
319  }
320 
340  public function multicall(array $requests)
341  {
342  $responses = array();
343  foreach ($requests as $request) {
344  try {
345  if (!is_array($request)) {
346  throw new \BadFunctionCallException('Expected struct');
347  }
348  if (!isset($request['methodName'])) {
349  throw new \BadFunctionCallException('Missing methodName');
350  }
351  if (!isset($request['params'])) {
352  throw new \BadFunctionCallException('Missing params');
353  }
354  if ($request['methodName'] === 'system.multicall') {
355  throw new \BadFunctionCallException('Recursive call');
356  }
357 
358  $result = $this->server->call($request['methodName'], $request['params']);
359  // Results are wrapped in an array to make it possible
360  // to distinguish faults from regular structs.
361  $responses[] = array($result);
362  } catch (\Exception $error) {
363  $responses[] = $error;
364  }
365  }
366  return $responses;
367  }
368 }
__construct(\fpoirotte\XRL\Server $server, array $whitelist=null)
An exception that is used to represent XML-RPC errors.
Definition: Exception.php:21
$whitelist
Whitelist of XML-RPC methods to announce.
A simple XML-RPC server.
Definition: Server.php:67
static enable(\fpoirotte\XRL\Server $server, array $whitelist=null)
A class that adds various capabilities to an existing XML-RPC server.
$server
Original XML-RPC server.