1<?php 2 3namespace Elastica; 4 5use Elastica\Exception\NotFoundException; 6 7/** 8 * Elastica Response object. 9 * 10 * Stores query time, and result array -> is given to result set, returned by ... 11 * 12 * @author Nicolas Ruflin <spam@ruflin.com> 13 */ 14class Response 15{ 16 /** 17 * Query time. 18 * 19 * @var float Query time 20 */ 21 protected $_queryTime; 22 23 /** 24 * Response string (json). 25 * 26 * @var string Response 27 */ 28 protected $_responseString = ''; 29 30 /** 31 * Transfer info. 32 * 33 * @var array transfer info 34 */ 35 protected $_transferInfo = []; 36 37 /** 38 * Response. 39 * 40 * @var array|null 41 */ 42 protected $_response; 43 44 /** 45 * HTTP response status code. 46 * 47 * @var int 48 */ 49 protected $_status; 50 51 /** 52 * Whether or not to convert bigint results to string (see issue #717). 53 * 54 * @var bool 55 */ 56 protected $_jsonBigintConversion = false; 57 58 /** 59 * Construct. 60 * 61 * @param array|string $responseString Response string (json) 62 * @param int $responseStatus http status code 63 */ 64 public function __construct($responseString, $responseStatus = null) 65 { 66 if (\is_array($responseString)) { 67 $this->_response = $responseString; 68 } else { 69 $this->_responseString = $responseString; 70 } 71 $this->_status = $responseStatus; 72 } 73 74 /** 75 * Error message. 76 * 77 * @return string Error message 78 */ 79 public function getError() 80 { 81 $error = $this->getFullError(); 82 83 if (!$error) { 84 return ''; 85 } 86 87 if (\is_string($error)) { 88 return $error; 89 } 90 91 $rootError = $error['root_cause'][0] ?? $error; 92 93 $message = $rootError['reason']; 94 if (isset($rootError['index'])) { 95 $message .= ' [index: '.$rootError['index'].']'; 96 } 97 98 if (isset($error['reason']) && $rootError['reason'] !== $error['reason']) { 99 $message .= ' [reason: '.$error['reason'].']'; 100 } 101 102 return $message; 103 } 104 105 /** 106 * A keyed array representing any errors that occurred. 107 * 108 * In case of http://localhost:9200/_alias/test the error is a string 109 * 110 * @return array|string|null Error data or null if there is no error 111 */ 112 public function getFullError() 113 { 114 $response = $this->getData(); 115 116 return $response['error'] ?? null; 117 } 118 119 /** 120 * @return string Error string based on the error object 121 */ 122 public function getErrorMessage() 123 { 124 return $this->getError(); 125 } 126 127 /** 128 * True if response has error. 129 * 130 * @return bool True if response has error 131 */ 132 public function hasError() 133 { 134 $response = $this->getData(); 135 136 return isset($response['error']); 137 } 138 139 /** 140 * True if response has failed shards. 141 * 142 * @return bool True if response has failed shards 143 */ 144 public function hasFailedShards() 145 { 146 try { 147 $shardsStatistics = $this->getShardsStatistics(); 148 } catch (NotFoundException $e) { 149 return false; 150 } 151 152 return \array_key_exists('failures', $shardsStatistics); 153 } 154 155 /** 156 * Checks if the query returned ok. 157 * 158 * @return bool True if ok 159 */ 160 public function isOk() 161 { 162 $data = $this->getData(); 163 164 // Bulk insert checks. Check every item 165 if (isset($data['status'])) { 166 return $data['status'] >= 200 && $data['status'] <= 300; 167 } 168 169 if (isset($data['items'])) { 170 if (isset($data['errors']) && true === $data['errors']) { 171 return false; 172 } 173 174 foreach ($data['items'] as $item) { 175 if (isset($item['index']['ok']) && false == $item['index']['ok']) { 176 return false; 177 } 178 179 if (isset($item['index']['status']) && ($item['index']['status'] < 200 || $item['index']['status'] >= 300)) { 180 return false; 181 } 182 } 183 184 return true; 185 } 186 187 if ($this->_status >= 200 && $this->_status <= 300) { 188 // http status is ok 189 return true; 190 } 191 192 return isset($data['ok']) && $data['ok']; 193 } 194 195 /** 196 * @return int 197 */ 198 public function getStatus() 199 { 200 return $this->_status; 201 } 202 203 /** 204 * Response data array. 205 * 206 * @return array<string, mixed> Response data array 207 */ 208 public function getData() 209 { 210 if (null == $this->_response) { 211 $response = $this->getResponseString(); 212 $this->_response = $response; 213 $this->_responseString = ''; 214 } 215 216 return $this->_response; 217 } 218 219 /** 220 * Gets the transfer information. 221 * 222 * @return array information about the curl request 223 */ 224 public function getTransferInfo() 225 { 226 return $this->_transferInfo; 227 } 228 229 /** 230 * Sets the transfer info of the curl request. This function is called 231 * from the \Elastica\Client::_callService . 232 * 233 * @param array $transferInfo the curl transfer information 234 * 235 * @return $this 236 */ 237 public function setTransferInfo(array $transferInfo) 238 { 239 $this->_transferInfo = $transferInfo; 240 241 return $this; 242 } 243 244 /** 245 * Returns query execution time. 246 * 247 * @return float Query time 248 */ 249 public function getQueryTime() 250 { 251 return $this->_queryTime; 252 } 253 254 /** 255 * Sets the query time. 256 * 257 * @param float $queryTime Query time 258 * 259 * @return $this 260 */ 261 public function setQueryTime($queryTime) 262 { 263 $this->_queryTime = $queryTime; 264 265 return $this; 266 } 267 268 /** 269 * Time request took. 270 * 271 * @throws NotFoundException 272 * 273 * @return int Time request took 274 */ 275 public function getEngineTime() 276 { 277 $data = $this->getData(); 278 279 if (!isset($data['took'])) { 280 throw new NotFoundException('Unable to find the field [took]from the response'); 281 } 282 283 return $data['took']; 284 } 285 286 /** 287 * Get the _shard statistics for the response. 288 * 289 * @throws NotFoundException 290 * 291 * @return array 292 */ 293 public function getShardsStatistics() 294 { 295 $data = $this->getData(); 296 297 if (!isset($data['_shards'])) { 298 throw new NotFoundException('Unable to find the field [_shards] from the response'); 299 } 300 301 return $data['_shards']; 302 } 303 304 /** 305 * Get the _scroll value for the response. 306 * 307 * @throws NotFoundException 308 * 309 * @return string 310 */ 311 public function getScrollId() 312 { 313 $data = $this->getData(); 314 315 if (!isset($data['_scroll_id'])) { 316 throw new NotFoundException('Unable to find the field [_scroll_id] from the response'); 317 } 318 319 return $data['_scroll_id']; 320 } 321 322 /** 323 * Sets whether or not to apply bigint conversion on the JSON result. 324 * 325 * @param bool $jsonBigintConversion 326 */ 327 public function setJsonBigintConversion($jsonBigintConversion): void 328 { 329 $this->_jsonBigintConversion = $jsonBigintConversion; 330 } 331 332 /** 333 * Gets whether or not to apply bigint conversion on the JSON result. 334 * 335 * @return bool 336 */ 337 public function getJsonBigintConversion() 338 { 339 return $this->_jsonBigintConversion; 340 } 341 342 /** 343 * @return array|string[] 344 */ 345 private function getResponseString() 346 { 347 $response = $this->_responseString; 348 if (empty($response)) { 349 return []; 350 } 351 try { 352 if ($this->getJsonBigintConversion()) { 353 $response = JSON::parse($response, true, 512, \JSON_BIGINT_AS_STRING); 354 } else { 355 $response = JSON::parse($response); 356 } 357 } catch (\JsonException $e) { 358 // leave response as is if parse fails 359 } 360 361 if (\is_string($response)) { 362 $response = ['message' => $response]; 363 } 364 365 return $response; 366 } 367} 368