Differences Between: [Versions 401 and 402]
1 <?php 2 3 namespace PhpXmlRpc; 4 5 use PhpXmlRpc\Exception\StateErrorException; 6 use PhpXmlRpc\Traits\CharsetEncoderAware; 7 use PhpXmlRpc\Traits\DeprecationLogger; 8 use PhpXmlRpc\Traits\PayloadBearer; 9 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. 14 * 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'] 24 */ 25 class Response 26 { 27 use CharsetEncoderAware; 28 use DeprecationLogger; 29 use PayloadBearer; 30 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 = ''; 39 40 protected $httpResponse = array('headers' => array(), 'cookies' => array(), 'raw_data' => '', 'status_code' => null); 41 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 53 * 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. 57 */ 58 public function __construct($val, $fCode = 0, $fString = '', $valType = '', $httpResponse = null) 59 { 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'; 75 } 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); 82 } 83 } 84 } 85 86 if (is_array($httpResponse)) { 87 $this->httpResponse = array_merge(array('headers' => array(), 'cookies' => array(), 'raw_data' => '', 'status_code' => null), $httpResponse); 88 } 89 } 90 91 /** 92 * Returns the error code of the response. 93 * 94 * @return integer the error code of this response (0 for not-error responses) 95 */ 96 public function faultCode() 97 { 98 return $this->errno; 99 } 100 101 /** 102 * Returns the error code of the response. 103 * 104 * @return string the error string of this response ('' for not-error responses) 105 */ 106 public function faultString() 107 { 108 return $this->errstr; 109 } 110 111 /** 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). 114 * 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 117 */ 118 public function value() 119 { 120 return $this->val; 121 } 122 123 /** 124 * @return string 125 */ 126 public function valueType() 127 { 128 return $this->valtyp; 129 } 130 131 /** 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. 139 * 140 * @return array[] array of cookies received from the server 141 */ 142 public function cookies() 143 { 144 return $this->httpResponse['cookies']; 145 } 146 147 /** 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. 150 * 151 * @return array array with keys 'headers', 'cookies', 'raw_data' and 'status_code'. 152 */ 153 public function httpResponse() 154 { 155 return $this->httpResponse; 156 } 157 158 /** 159 * Returns xml representation of the response, XML prologue _not_ included. Sets `payload` and `content_type` properties 160 * 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 164 */ 165 public function serialize($charsetEncoding = '') 166 { 167 if ($charsetEncoding != '') { 168 $this->content_type = 'text/xml; charset=' . $charsetEncoding; 169 } else { 170 $this->content_type = 'text/xml'; 171 } 172 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"; 177 } 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'); 198 } 199 } 200 $result .= "\n</methodResponse>"; 201 202 $this->payload = $result; 203 204 return $result; 205 } 206 207 /** 208 * @param string $charsetEncoding 209 * @return string 210 */ 211 public function xml_header($charsetEncoding = '') 212 { 213 if ($charsetEncoding != '') { 214 return "<?xml version=\"1.0\" encoding=\"$charsetEncoding\"?" . ">\n"; 215 } else { 216 return "<?xml version=\"1.0\"?" . ">\n"; 217 } 218 } 219 220 // *** BC layer *** 221 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) 224 { 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; 249 } 250 } 251 252 public function __set($name, $value) 253 { 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); 280 } 281 } 282 283 public function __isset($name) 284 { 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; 305 } 306 } 307 308 public function __unset($name) 309 { 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); 336 } 337 } 338 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body