See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 401 and 402] [Versions 401 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 18 /** 19 * REST web service implementation classes and methods. 20 * 21 * @package webservice_rest 22 * @copyright 2009 Jerome Mouneyrac 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 26 require_once("$CFG->dirroot/webservice/lib.php"); 27 28 /** 29 * REST service server implementation. 30 * 31 * @package webservice_rest 32 * @copyright 2009 Petr Skoda (skodak) 33 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 34 */ 35 class webservice_rest_server extends webservice_base_server { 36 37 /** @var string return method ('xml' or 'json') */ 38 protected $restformat; 39 40 /** 41 * Contructor 42 * 43 * @param string $authmethod authentication method of the web service (WEBSERVICE_AUTHMETHOD_PERMANENT_TOKEN, ...) 44 * @param string $restformat Format of the return values: 'xml' or 'json' 45 */ 46 public function __construct($authmethod) { 47 parent::__construct($authmethod); 48 $this->wsname = 'rest'; 49 } 50 51 /** 52 * Set the request format to. 53 */ 54 public function set_rest_format(): void { 55 // Get GET and POST parameters. 56 $methodvariables = array_merge($_GET, $_POST); 57 58 // Retrieve REST format parameter - 'xml' (default) or 'json'. 59 $restformatisset = isset($methodvariables['moodlewsrestformat']) 60 && (($methodvariables['moodlewsrestformat'] == 'xml' || $methodvariables['moodlewsrestformat'] == 'json')); 61 $this->restformat = $restformatisset ? $methodvariables['moodlewsrestformat'] : 'xml'; 62 } 63 64 /** 65 * This method parses the $_POST and $_GET superglobals and looks for 66 * the following information: 67 * 1/ user authentication - username+password or token (wsusername, wspassword and wstoken parameters) 68 * 2/ function name (wsfunction parameter) 69 * 3/ function parameters (all other parameters except those above) 70 * 4/ text format parameters 71 * 5/ return rest format xml/json 72 */ 73 protected function parse_request() { 74 75 // Retrieve and clean the POST/GET parameters from the parameters specific to the server. 76 parent::set_web_service_call_settings(); 77 78 // Get GET and POST parameters. 79 $methodvariables = array_merge($_GET, $_POST); 80 $this->set_rest_format(); 81 unset($methodvariables['moodlewsrestformat']); 82 83 if ($this->authmethod == WEBSERVICE_AUTHMETHOD_USERNAME) { 84 $this->username = isset($methodvariables['wsusername']) ? $methodvariables['wsusername'] : null; 85 unset($methodvariables['wsusername']); 86 87 $this->password = isset($methodvariables['wspassword']) ? $methodvariables['wspassword'] : null; 88 unset($methodvariables['wspassword']); 89 90 $this->functionname = isset($methodvariables['wsfunction']) ? $methodvariables['wsfunction'] : null; 91 unset($methodvariables['wsfunction']); 92 93 $this->parameters = $methodvariables; 94 95 } else { 96 $this->token = isset($methodvariables['wstoken']) ? $methodvariables['wstoken'] : null; 97 unset($methodvariables['wstoken']); 98 99 $this->functionname = isset($methodvariables['wsfunction']) ? $methodvariables['wsfunction'] : null; 100 unset($methodvariables['wsfunction']); 101 102 $this->parameters = $methodvariables; 103 } 104 } 105 106 /** 107 * Send the result of function call to the WS client 108 * formatted as XML document. 109 */ 110 protected function send_response() { 111 112 //Check that the returned values are valid 113 try { 114 if ($this->function->returns_desc != null) { 115 $validatedvalues = external_api::clean_returnvalue($this->function->returns_desc, $this->returns); 116 } else { 117 $validatedvalues = null; 118 } 119 } catch (Exception $ex) { 120 $exception = $ex; 121 } 122 123 if (!empty($exception)) { 124 $response = $this->generate_error($exception); 125 } else { 126 //We can now convert the response to the requested REST format 127 if ($this->restformat == 'json') { 128 $response = json_encode($validatedvalues); 129 } else { 130 $response = '<?xml version="1.0" encoding="UTF-8" ?>'."\n"; 131 $response .= '<RESPONSE>'."\n"; 132 $response .= self::xmlize_result($validatedvalues, $this->function->returns_desc); 133 $response .= '</RESPONSE>'."\n"; 134 } 135 } 136 137 $this->send_headers(); 138 echo $response; 139 } 140 141 /** 142 * Send the error information to the WS client 143 * formatted as XML document. 144 * Note: the exception is never passed as null, 145 * it only matches the abstract function declaration. 146 * @param exception $ex the exception that we are sending 147 */ 148 protected function send_error($ex=null) { 149 // Unless debugging is completely off, log the error to server error log. 150 if (debugging('', DEBUG_MINIMAL)) { 151 $info = get_exception_info($ex); 152 // This format is the same as default_exception_handler() in setuplib.php but with the 153 // word 'REST' instead of 'Default', to make it easy to reuse any existing processing. 154 error_log('REST exception handler: ' . $info->message . ' Debug: ' . 155 $info->debuginfo . "\n" . format_backtrace($info->backtrace, true)); 156 } 157 158 $this->send_headers(); 159 echo $this->generate_error($ex); 160 } 161 162 /** 163 * Build the error information matching the REST returned value format (JSON or XML) 164 * @param exception $ex the exception we are converting in the server rest format 165 * @return string the error in the requested REST format 166 */ 167 protected function generate_error($ex) { 168 if ($this->restformat == 'json') { 169 $errorobject = new stdClass; 170 $errorobject->exception = get_class($ex); 171 if (isset($ex->errorcode)) { 172 $errorobject->errorcode = $ex->errorcode; 173 } 174 $errorobject->message = $ex->getMessage(); 175 if (debugging() and isset($ex->debuginfo)) { 176 $errorobject->debuginfo = $ex->debuginfo; 177 } 178 $error = json_encode($errorobject); 179 } else { 180 $error = '<?xml version="1.0" encoding="UTF-8" ?>'."\n"; 181 $error .= '<EXCEPTION class="'.get_class($ex).'">'."\n"; 182 if (isset($ex->errorcode)) { 183 $error .= '<ERRORCODE>' . htmlspecialchars($ex->errorcode, ENT_COMPAT, 'UTF-8') 184 . '</ERRORCODE>' . "\n"; 185 } 186 $error .= '<MESSAGE>'.htmlspecialchars($ex->getMessage(), ENT_COMPAT, 'UTF-8').'</MESSAGE>'."\n"; 187 if (debugging() and isset($ex->debuginfo)) { 188 $error .= '<DEBUGINFO>'.htmlspecialchars($ex->debuginfo, ENT_COMPAT, 'UTF-8').'</DEBUGINFO>'."\n"; 189 } 190 $error .= '</EXCEPTION>'."\n"; 191 } 192 return $error; 193 } 194 195 /** 196 * Internal implementation - sending of page headers. 197 */ 198 protected function send_headers() { 199 if ($this->restformat == 'json') { 200 header('Content-type: application/json'); 201 } else { 202 header('Content-Type: application/xml; charset=utf-8'); 203 header('Content-Disposition: inline; filename="response.xml"'); 204 } 205 header('Cache-Control: private, must-revalidate, pre-check=0, post-check=0, max-age=0'); 206 header('Expires: '. gmdate('D, d M Y H:i:s', 0) .' GMT'); 207 header('Pragma: no-cache'); 208 header('Accept-Ranges: none'); 209 // Allow cross-origin requests only for Web Services. 210 // This allow to receive requests done by Web Workers or webapps in different domains. 211 header('Access-Control-Allow-Origin: *'); 212 } 213 214 /** 215 * Internal implementation - recursive function producing XML markup. 216 * 217 * @param mixed $returns the returned values 218 * @param external_description $desc 219 * @return string 220 */ 221 protected static function xmlize_result($returns, $desc) { 222 if ($desc === null) { 223 return ''; 224 225 } else if ($desc instanceof external_value) { 226 if (is_bool($returns)) { 227 // we want 1/0 instead of true/false here 228 $returns = (int)$returns; 229 } 230 if (is_null($returns)) { 231 return '<VALUE null="null"/>'."\n"; 232 } else { 233 return '<VALUE>'.htmlspecialchars($returns, ENT_COMPAT, 'UTF-8').'</VALUE>'."\n"; 234 } 235 236 } else if ($desc instanceof external_multiple_structure) { 237 $mult = '<MULTIPLE>'."\n"; 238 if (!empty($returns)) { 239 foreach ($returns as $val) { 240 $mult .= self::xmlize_result($val, $desc->content); 241 } 242 } 243 $mult .= '</MULTIPLE>'."\n"; 244 return $mult; 245 246 } else if ($desc instanceof external_single_structure) { 247 $single = '<SINGLE>'."\n"; 248 foreach ($desc->keys as $key=>$subdesc) { 249 $value = isset($returns[$key]) ? $returns[$key] : null; 250 $single .= '<KEY name="'.$key.'">'.self::xmlize_result($value, $subdesc).'</KEY>'."\n"; 251 } 252 $single .= '</SINGLE>'."\n"; 253 return $single; 254 } 255 } 256 } 257 258 259 /** 260 * REST test client class 261 * 262 * @package webservice_rest 263 * @copyright 2009 Petr Skoda (skodak) 264 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 265 */ 266 class webservice_rest_test_client implements webservice_test_client_interface { 267 /** 268 * Execute test client WS request 269 * @param string $serverurl server url (including token parameter or username/password parameters) 270 * @param string $function function name 271 * @param array $params parameters of the called function 272 * @return mixed 273 */ 274 public function simpletest($serverurl, $function, $params) { 275 return download_file_content($serverurl.'&wsfunction='.$function, null, $params); 276 } 277 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body