1<?php 2namespace IXR\Server; 3 4use IXR\DataType\Base64; 5use IXR\DataType\Date; 6use IXR\Message\Error; 7 8/** 9 * IXR_IntrospectionServer 10 * 11 * @package IXR 12 * @since 1.5.0 13 */ 14class 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