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->_responseString; 212 213 try { 214 if ($this->getJsonBigintConversion()) { 215 $response = JSON::parse($response, true, 512, \JSON_BIGINT_AS_STRING); 216 } else { 217 $response = JSON::parse($response); 218 } 219 } catch (\JsonException $e) { 220 // leave response as is if parse fails 221 } 222 223 if (empty($response)) { 224 $response = []; 225 } 226 227 if (\is_string($response)) { 228 $response = ['message' => $response]; 229 } 230 231 $this->_response = $response; 232 $this->_responseString = ''; 233 } 234 235 return $this->_response; 236 } 237 238 /** 239 * Gets the transfer information. 240 * 241 * @return array information about the curl request 242 */ 243 public function getTransferInfo() 244 { 245 return $this->_transferInfo; 246 } 247 248 /** 249 * Sets the transfer info of the curl request. This function is called 250 * from the \Elastica\Client::_callService . 251 * 252 * @param array $transferInfo the curl transfer information 253 * 254 * @return $this 255 */ 256 public function setTransferInfo(array $transferInfo) 257 { 258 $this->_transferInfo = $transferInfo; 259 260 return $this; 261 } 262 263 /** 264 * Returns query execution time. 265 * 266 * @return float Query time 267 */ 268 public function getQueryTime() 269 { 270 return $this->_queryTime; 271 } 272 273 /** 274 * Sets the query time. 275 * 276 * @param float $queryTime Query time 277 * 278 * @return $this 279 */ 280 public function setQueryTime($queryTime) 281 { 282 $this->_queryTime = $queryTime; 283 284 return $this; 285 } 286 287 /** 288 * Time request took. 289 * 290 * @throws NotFoundException 291 * 292 * @return int Time request took 293 */ 294 public function getEngineTime() 295 { 296 $data = $this->getData(); 297 298 if (!isset($data['took'])) { 299 throw new NotFoundException('Unable to find the field [took]from the response'); 300 } 301 302 return $data['took']; 303 } 304 305 /** 306 * Get the _shard statistics for the response. 307 * 308 * @throws NotFoundException 309 * 310 * @return array 311 */ 312 public function getShardsStatistics() 313 { 314 $data = $this->getData(); 315 316 if (!isset($data['_shards'])) { 317 throw new NotFoundException('Unable to find the field [_shards] from the response'); 318 } 319 320 return $data['_shards']; 321 } 322 323 /** 324 * Get the _scroll value for the response. 325 * 326 * @throws NotFoundException 327 * 328 * @return string 329 */ 330 public function getScrollId() 331 { 332 $data = $this->getData(); 333 334 if (!isset($data['_scroll_id'])) { 335 throw new NotFoundException('Unable to find the field [_scroll_id] from the response'); 336 } 337 338 return $data['_scroll_id']; 339 } 340 341 /** 342 * Sets whether or not to apply bigint conversion on the JSON result. 343 * 344 * @param bool $jsonBigintConversion 345 */ 346 public function setJsonBigintConversion($jsonBigintConversion): void 347 { 348 $this->_jsonBigintConversion = $jsonBigintConversion; 349 } 350 351 /** 352 * Gets whether or not to apply bigint conversion on the JSON result. 353 * 354 * @return bool 355 */ 356 public function getJsonBigintConversion() 357 { 358 return $this->_jsonBigintConversion; 359 } 360} 361