ILIAS  trunk Revision v12.0_alpha-1227-g7ff6d300864
class.ilRpcClient.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
44{
45 protected string $url;
46 protected string $prefix = '';
47 protected int $timeout = 0;
48
49 protected ilLogger $logger;
50
57 public function __construct(string $url, string $prefix = '', int $timeout = 0)
58 {
59 global $DIC;
60
61 $this->logger = $DIC->logger()->wsrv();
62 $this->url = $url;
63 $this->prefix = $prefix;
64 $this->timeout = $timeout;
65 }
66
73 public function __call(string $method, array $parameters): string|bool|stdClass
74 {
75 //prepare xml post data
76 $method_name = str_replace('_', '.', $this->prefix . $method);
77
78 $post_data = $this->encodeRequest($method_name, $parameters);
79
80 //try to connect to the given url
81 try {
82 $curl = new ilCurlConnection($this->url);
83 $curl->init(false);
84 $curl->setOpt(CURLOPT_HTTPHEADER, ['Content-Type: text/xml']);
85 $curl->setOpt(CURLOPT_POST, (strlen($post_data) > 0));
86 $curl->setOpt(CURLOPT_POSTFIELDS, $post_data);
87 $curl->setOpt(CURLOPT_RETURNTRANSFER, 1);
88
89 if ($this->timeout > 0) {
90 $curl->setOpt(CURLOPT_TIMEOUT, $this->timeout);
91 }
92 $this->logger->debug('RpcClient request to ' . $this->url . ' / ' . $method_name);
93 $xml_response = $curl->exec();
95 $this->logger->error(
96 'RpcClient could not connect to ' . $this->url . ' ' .
97 'Reason ' . $e->getCode() . ': ' . $e->getMessage()
98 );
99 throw new ilRpcClientException($e->getMessage(), $e->getCode());
100 }
101
102 //return output, throw exception if rpc fault is detected
103 return $this->handleResponse($xml_response);
104 }
105
110 protected function encodeRequest(string $method, array $parameters): string
111 {
112 $xml = new DOMDocument('1.0', 'UTF-8');
113 $method_call = $xml->createElement('methodCall');
114 $method_name = $xml->createElement('methodName', $method);
115 $params = $xml->createElement('params');
116
117 foreach ($parameters as $parameter) {
118 match (true) {
119 is_string($parameter) => $encoded_parameter = $this->encodeString($parameter),
120 is_int($parameter) => $encoded_parameter = $this->encodeInteger($parameter),
121 is_bool($parameter) => $encoded_parameter = $this->encodeBoolean($parameter),
122 $this->isListOfIntegers($parameter) => $encoded_parameter = $this->encodeListOfIntegers(...$parameter),
123 default => throw new ilRpcClientException(
124 'Invalid parameter type, only string, int, bool, and int[] are supported.'
125 )
126 };
127 $params->appendChild($xml->importNode($this->wrapParameter($encoded_parameter)->documentElement, true));
128 }
129
130 $method_call->appendChild($method_name);
131 $method_call->appendChild($params);
132
133 $xml->appendChild($method_call);
134 return $xml->saveXML();
135 }
136
137 protected function isListOfIntegers(mixed $parameter): bool
138 {
139 if (!is_array($parameter)) {
140 return false;
141 }
142 foreach ($parameter as $entries) {
143 if (!is_int($entries)) {
144 return false;
145 }
146 }
147 return true;
148 }
149
150 protected function wrapParameter(DOMDocument $encoded_parameter): DOMDocument
151 {
152 $xml = new DOMDocument('1.0', 'UTF-8');
153 $param = $xml->createElement('param');
154 $value = $xml->createElement('value');
155
156 $value->appendChild($xml->importNode($encoded_parameter->documentElement, true));
157 $param->appendChild($value);
158
159 $xml->appendChild($param);
160 return $xml;
161 }
162
163 protected function encodeString(string $parameter): DOMDocument
164 {
165 $xml = new DOMDocument('1.0', 'UTF-8');
166 $xml->appendChild($xml->createElement('string', $parameter));
167 return $xml;
168 }
169
170 protected function encodeInteger(int $parameter): DOMDocument
171 {
172 $xml = new DOMDocument('1.0', 'UTF-8');
173 $xml->appendChild($xml->createElement('int', (string) $parameter));
174 return $xml;
175 }
176
177 protected function encodeBoolean(bool $parameter): DOMDocument
178 {
179 $xml = new DOMDocument('1.0', 'UTF-8');
180 $xml->appendChild($xml->createElement('boolean', $parameter ? '1' : '0'));
181 return $xml;
182 }
183
184 protected function encodeListOfIntegers(int ...$parameters): DOMDocument
185 {
186 $xml = new DOMDocument('1.0', 'UTF-8');
187 $array = $xml->createElement('array');
188 $data = $xml->createElement('data');
189
190 foreach ($parameters as $parameter) {
191 $value = $xml->createElement('value');
192 $value->appendChild($xml->importNode($this->encodeInteger($parameter)->documentElement, true));
193 $data->appendChild($value);
194 }
195 $array->appendChild($data);
196
197 $xml->appendChild($array);
198 return $xml;
199 }
200
205 public function handleResponse(string $xml): string|bool|stdClass
206 {
207 $response = new DOMDocument('1.0', 'UTF-8');
208 $response->preserveWhiteSpace = false;
209 $response->loadXML($xml);
210
211 if (!$response) {
212 throw new ilRpcClientException('Invalid XML response');
213 }
214
215 $response_body = $response->documentElement->childNodes->item(0);
216
217 if ($response_body === null) {
218 throw new ilRpcClientException('Empty response');
219 }
220
221 $this->logger->dump($response_body);
222
223 return match ($response_body->nodeName) {
224 'params' => $this->decodeOKResponse($response_body),
225 'fault' => $this->handleFaultResponse($response_body),
226 default => throw new ilRpcClientException('Unexpected element in response: ' . get_class($response_body)),
227 };
228 }
229
230 protected function decodeOKResponse(DOMElement $response_body): string|bool|stdClass
231 {
232 $param_child = $response_body->getElementsByTagName('value')->item(0)?->childNodes?->item(0);
233
234 if ($param_child === null) {
235 throw new ilRpcClientException('No value in response');
236 }
237
238 return match ($param_child->nodeName) {
239 'string' => $this->decodeString($param_child),
240 '#text' => $this->decodeString($param_child), // org.apache.xmlrpc returns java strings as unwrapped text node
241 'base64' => $this->decodeBase64($param_child),
242 'boolean' => $this->decodeBoolean($param_child),
243 default => throw new ilRpcClientException('Unexpected element in response value: ' . $param_child->nodeName),
244 };
245 }
246
247 protected function decodeString(DOMNode $string): string
248 {
249 return (string) $string->nodeValue;
250 }
251
252 protected function decodeBase64(DOMNode $base64): stdClass
253 {
254 return (object) base64_decode((string) $base64->nodeValue);
255 }
256
257 protected function decodeBoolean(DOMNode $boolean): bool
258 {
259 return (bool) $boolean->nodeValue;
260 }
261
265 protected function handleFaultResponse(DOMElement $response_body): string
266 {
267 $fault_code = null;
268 $fault_string = null;
269
270 $members = $response_body->getElementsByTagName('member');
271 foreach ($members as $member) {
272 $name = $member->getElementsByTagName('name')->item(0)?->nodeValue;
273 if ($name === 'faultCode') {
274 if ($fault_code !== null) {
275 throw new ilRpcClientException('Multiple codes in fault response.');
276 }
277 $fault_code = (int) $member->getElementsByTagName('int')->item(0)?->nodeValue;
278 }
279 if ($name === 'faultString') {
280 if ($fault_string !== null) {
281 throw new ilRpcClientException('Multiple strings in fault response.');
282 }
283 $fault_string = $member->getElementsByTagName('string')->item(0)?->nodeValue;
284 }
285 }
286
287 if ($fault_code === null || $fault_string === null) {
288 throw new ilRpcClientException('No code or no string in fault respsonse');
289 }
290
291 $this->logger->error('RpcClient recieved error ' . $fault_code . ': ' . $fault_string);
292 throw new ilRpcClientException(
293 'RPC-Server returned fault message: ' .
294 $fault_string,
295 $fault_code
296 );
297 }
298}
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Component logger with individual log levels by component id.
Class ilRpcClientException.
isListOfIntegers(mixed $parameter)
encodeRequest(string $method, array $parameters)
__construct(string $url, string $prefix='', int $timeout=0)
wrapParameter(DOMDocument $encoded_parameter)
encodeListOfIntegers(int ... $parameters)
decodeBoolean(DOMNode $boolean)
encodeInteger(int $parameter)
__call(string $method, array $parameters)
handleResponse(string $xml)
Returns decoded response if not faulty, otherwise throws exception.
decodeBase64(DOMNode $base64)
encodeBoolean(bool $parameter)
decodeOKResponse(DOMElement $response_body)
decodeString(DOMNode $string)
handleFaultResponse(DOMElement $response_body)
encodeString(string $parameter)
if(! $DIC->user() ->getId()||!ilLTIConsumerAccess::hasCustomProviderCreationAccess()) $params
Definition: ltiregstart.php:31
global $DIC
Definition: shib_login.php:26
$param
Definition: xapitoken.php:44
$response
Definition: xapitoken.php:90