Merge branch 'MDL-81713-main' of https://github.com/junpataleta/moodle
[moodle.git] / lib / phpxmlrpc / Response.php
blobd822d3c4fa39bd68d8ec979e1977acf3d6b5b4ef
1 <?php
3 namespace PhpXmlRpc;
5 use PhpXmlRpc\Exception\StateErrorException;
6 use PhpXmlRpc\Traits\CharsetEncoderAware;
7 use PhpXmlRpc\Traits\DeprecationLogger;
8 use PhpXmlRpc\Traits\PayloadBearer;
10 /**
11 * This class provides the representation of the response of an XML-RPC server.
12 * Server-side, a server method handler will construct a Response and pass it as its return value.
13 * An identical Response object will be returned by the result of an invocation of the send() method of the Client class.
15 * @property Value|string|mixed $val deprecated - public access left in purely for BC. Access via value()/__construct()
16 * @property string $valtyp deprecated - public access left in purely for BC. Access via valueType()/__construct()
17 * @property int $errno deprecated - public access left in purely for BC. Access via faultCode()/__construct()
18 * @property string $errstr deprecated - public access left in purely for BC. Access faultString()/__construct()
19 * @property string $payload deprecated - public access left in purely for BC. Access via getPayload()/setPayload()
20 * @property string $content_type deprecated - public access left in purely for BC. Access via getContentType()/setPayload()
21 * @property array $hdrs deprecated. Access via httpResponse()['headers'], set via $httpResponse['headers']
22 * @property array _cookies deprecated. Access via httpResponse()['cookies'], set via $httpResponse['cookies']
23 * @property string $raw_data deprecated. Access via httpResponse()['raw_data'], set via $httpResponse['raw_data']
25 class Response
27 use CharsetEncoderAware;
28 use DeprecationLogger;
29 use PayloadBearer;
31 /** @var Value|string|mixed */
32 protected $val = 0;
33 /** @var string */
34 protected $valtyp;
35 /** @var int */
36 protected $errno = 0;
37 /** @var string */
38 protected $errstr = '';
40 protected $httpResponse = array('headers' => array(), 'cookies' => array(), 'raw_data' => '', 'status_code' => null);
42 /**
43 * @param Value|string|mixed $val either a Value object, a php value or the xml serialization of an xml-rpc value (a string).
44 * Note that using anything other than a Value object wll have an impact on serialization.
45 * @param integer $fCode set it to anything but 0 to create an error response. In that case, $val is discarded
46 * @param string $fString the error string, in case of an error response
47 * @param string $valType The type of $val passed in. Either 'xmlrpcvals', 'phpvals' or 'xml'. Leave empty to let
48 * the code guess the correct type by looking at $val - in which case strings are assumed
49 * to be serialized xml
50 * @param array|null $httpResponse this should be set when the response is being built out of data received from
51 * http (i.e. not when programmatically building a Response server-side). Array
52 * keys should include, if known: headers, cookies, raw_data, status_code
54 * @todo add check that $val / $fCode / $fString is of correct type? We could at least log a warning for fishy cases...
55 * NB: as of now we do not do it, since it might be either an xml-rpc value or a plain php val, or a complete
56 * xml chunk, depending on usage of Client::send() inside which the constructor is called.
58 public function __construct($val, $fCode = 0, $fString = '', $valType = '', $httpResponse = null)
60 if ($fCode != 0) {
61 // error response
62 $this->errno = $fCode;
63 $this->errstr = $fString;
64 } else {
65 // successful response
66 $this->val = $val;
67 if ($valType == '') {
68 // user did not declare type of response value: try to guess it
69 if (is_object($this->val) && is_a($this->val, 'PhpXmlRpc\Value')) {
70 $this->valtyp = 'xmlrpcvals';
71 } elseif (is_string($this->val)) {
72 $this->valtyp = 'xml';
73 } else {
74 $this->valtyp = 'phpvals';
76 } else {
77 $this->valtyp = $valType;
78 // user declares the type of resp value: we "almost" trust it... but log errors just in case
79 if (($this->valtyp == 'xmlrpcvals' && (!is_a($this->val, 'PhpXmlRpc\Value'))) ||
80 ($this->valtyp == 'xml' && (!is_string($this->val)))) {
81 $this->getLogger()->error('XML-RPC: ' . __METHOD__ . ': value passed in does not match type ' . $valType);
86 if (is_array($httpResponse)) {
87 $this->httpResponse = array_merge(array('headers' => array(), 'cookies' => array(), 'raw_data' => '', 'status_code' => null), $httpResponse);
91 /**
92 * Returns the error code of the response.
94 * @return integer the error code of this response (0 for not-error responses)
96 public function faultCode()
98 return $this->errno;
102 * Returns the error code of the response.
104 * @return string the error string of this response ('' for not-error responses)
106 public function faultString()
108 return $this->errstr;
112 * Returns the value received by the server. If the Response's faultCode is non-zero then the value returned by this
113 * method should not be used (it may not even be an object).
115 * @return Value|string|mixed the Value object returned by the server. Might be an xml string or plain php value
116 * depending on the convention adopted when creating the Response
118 public function value()
120 return $this->val;
124 * @return string
126 public function valueType()
128 return $this->valtyp;
132 * Returns an array with the cookies received from the server.
133 * Array has the form: $cookiename => array ('value' => $val, $attr1 => $val1, $attr2 => $val2, ...)
134 * with attributes being e.g. 'expires', 'path', domain'.
135 * NB: cookies sent as 'expired' by the server (i.e. with an expiry date in the past) are still present in the array.
136 * It is up to the user-defined code to decide how to use the received cookies, and whether they have to be sent back
137 * with the next request to the server (using $client->setCookie) or not.
138 * The values are filled in at constructor time, and might not be set for specific debug values used.
140 * @return array[] array of cookies received from the server
142 public function cookies()
144 return $this->httpResponse['cookies'];
148 * Returns an array with info about the http response received from the server.
149 * The values are filled in at constructor time, and might not be set for specific debug values used.
151 * @return array array with keys 'headers', 'cookies', 'raw_data' and 'status_code'.
153 public function httpResponse()
155 return $this->httpResponse;
159 * Returns xml representation of the response, XML prologue _not_ included. Sets `payload` and `content_type` properties
161 * @param string $charsetEncoding the charset to be used for serialization. If null, US-ASCII is assumed
162 * @return string the xml representation of the response
163 * @throws StateErrorException if the response was built out of a value of an unsupported type
165 public function serialize($charsetEncoding = '')
167 if ($charsetEncoding != '') {
168 $this->content_type = 'text/xml; charset=' . $charsetEncoding;
169 } else {
170 $this->content_type = 'text/xml';
173 if (PhpXmlRpc::$xmlrpc_null_apache_encoding) {
174 $result = "<methodResponse xmlns:ex=\"" . PhpXmlRpc::$xmlrpc_null_apache_encoding_ns . "\">\n";
175 } else {
176 $result = "<methodResponse>\n";
178 if ($this->errno) {
179 // Let non-ASCII response messages be tolerated by clients by xml-encoding non ascii chars
180 $result .= "<fault>\n" .
181 "<value>\n<struct><member><name>faultCode</name>\n<value><int>" . $this->errno .
182 "</int></value>\n</member>\n<member>\n<name>faultString</name>\n<value><string>" .
183 $this->getCharsetEncoder()->encodeEntities($this->errstr, PhpXmlRpc::$xmlrpc_internalencoding, $charsetEncoding) .
184 "</string></value>\n</member>\n</struct>\n</value>\n</fault>";
185 } else {
186 if (is_object($this->val) && is_a($this->val, 'PhpXmlRpc\Value')) {
187 $result .= "<params>\n<param>\n" . $this->val->serialize($charsetEncoding) . "</param>\n</params>";
188 } else if (is_string($this->val) && $this->valtyp == 'xml') {
189 $result .= "<params>\n<param>\n" .
190 $this->val .
191 "</param>\n</params>";
192 } else if ($this->valtyp == 'phpvals') {
193 $encoder = new Encoder();
194 $val = $encoder->encode($this->val);
195 $result .= "<params>\n<param>\n" . $val->serialize($charsetEncoding) . "</param>\n</params>";
196 } else {
197 throw new StateErrorException('cannot serialize xmlrpc response objects whose content is native php values');
200 $result .= "\n</methodResponse>";
202 $this->payload = $result;
204 return $result;
208 * @param string $charsetEncoding
209 * @return string
211 public function xml_header($charsetEncoding = '')
213 if ($charsetEncoding != '') {
214 return "<?xml version=\"1.0\" encoding=\"$charsetEncoding\"?" . ">\n";
215 } else {
216 return "<?xml version=\"1.0\"?" . ">\n";
220 // *** BC layer ***
222 // we have to make this return by ref in order to allow calls such as `$resp->_cookies['name'] = ['value' => 'something'];`
223 public function &__get($name)
225 switch ($name) {
226 case 'val':
227 case 'valtyp':
228 case 'errno':
229 case 'errstr':
230 case 'payload':
231 case 'content_type':
232 $this->logDeprecation('Getting property Response::' . $name . ' is deprecated');
233 return $this->$name;
234 case 'hdrs':
235 $this->logDeprecation('Getting property Response::' . $name . ' is deprecated');
236 return $this->httpResponse['headers'];
237 case '_cookies':
238 $this->logDeprecation('Getting property Response::' . $name . ' is deprecated');
239 return $this->httpResponse['cookies'];
240 case 'raw_data':
241 $this->logDeprecation('Getting property Response::' . $name . ' is deprecated');
242 return $this->httpResponse['raw_data'];
243 default:
244 /// @todo throw instead? There are very few other places where the lib trigger errors which can potentially reach stdout...
245 $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
246 trigger_error('Undefined property via __get(): ' . $name . ' in ' . $trace[0]['file'] . ' on line ' . $trace[0]['line'], E_USER_WARNING);
247 $result = null;
248 return $result;
252 public function __set($name, $value)
254 switch ($name) {
255 case 'val':
256 case 'valtyp':
257 case 'errno':
258 case 'errstr':
259 case 'payload':
260 case 'content_type':
261 $this->logDeprecation('Setting property Response::' . $name . ' is deprecated');
262 $this->$name = $value;
263 break;
264 case 'hdrs':
265 $this->logDeprecation('Setting property Response::' . $name . ' is deprecated');
266 $this->httpResponse['headers'] = $value;
267 break;
268 case '_cookies':
269 $this->logDeprecation('Setting property Response::' . $name . ' is deprecated');
270 $this->httpResponse['cookies'] = $value;
271 break;
272 case 'raw_data':
273 $this->logDeprecation('Setting property Response::' . $name . ' is deprecated');
274 $this->httpResponse['raw_data'] = $value;
275 break;
276 default:
277 /// @todo throw instead? There are very few other places where the lib trigger errors which can potentially reach stdout...
278 $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
279 trigger_error('Undefined property via __set(): ' . $name . ' in ' . $trace[0]['file'] . ' on line ' . $trace[0]['line'], E_USER_WARNING);
283 public function __isset($name)
285 switch ($name) {
286 case 'val':
287 case 'valtyp':
288 case 'errno':
289 case 'errstr':
290 case 'payload':
291 case 'content_type':
292 $this->logDeprecation('Checking property Response::' . $name . ' is deprecated');
293 return isset($this->$name);
294 case 'hdrs':
295 $this->logDeprecation('Checking property Response::' . $name . ' is deprecated');
296 return isset($this->httpResponse['headers']);
297 case '_cookies':
298 $this->logDeprecation('Checking property Response::' . $name . ' is deprecated');
299 return isset($this->httpResponse['cookies']);
300 case 'raw_data':
301 $this->logDeprecation('Checking property Response::' . $name . ' is deprecated');
302 return isset($this->httpResponse['raw_data']);
303 default:
304 return false;
308 public function __unset($name)
310 switch ($name) {
311 case 'val':
312 case 'valtyp':
313 case 'errno':
314 case 'errstr':
315 case 'payload':
316 case 'content_type':
317 $this->logDeprecation('Setting property Response::' . $name . ' is deprecated');
318 unset($this->$name);
319 break;
320 case 'hdrs':
321 $this->logDeprecation('Unsetting property Response::' . $name . ' is deprecated');
322 unset($this->httpResponse['headers']);
323 break;
324 case '_cookies':
325 $this->logDeprecation('Unsetting property Response::' . $name . ' is deprecated');
326 unset($this->httpResponse['cookies']);
327 break;
328 case 'raw_data':
329 $this->logDeprecation('Unsetting property Response::' . $name . ' is deprecated');
330 unset($this->httpResponse['raw_data']);
331 break;
332 default:
333 /// @todo throw instead? There are very few other places where the lib trigger errors which can potentially reach stdout...
334 $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
335 trigger_error('Undefined property via __unset(): ' . $name . ' in ' . $trace[0]['file'] . ' on line ' . $trace[0]['line'], E_USER_WARNING);