dkd-kaehm /
ext-solr
| 1 | <?php |
||||
| 2 | namespace ApacheSolrForTypo3\Solr\System\Solr\Service; |
||||
| 3 | |||||
| 4 | /*************************************************************** |
||||
| 5 | * Copyright notice |
||||
| 6 | * |
||||
| 7 | * (c) 2009-2017 Timo Hund <[email protected]> |
||||
| 8 | * All rights reserved |
||||
| 9 | * |
||||
| 10 | * This script is part of the TYPO3 project. The TYPO3 project is |
||||
| 11 | * free software; you can redistribute it and/or modify |
||||
| 12 | * it under the terms of the GNU General Public License as published by |
||||
| 13 | * the Free Software Foundation; either version 3 of the License, or |
||||
| 14 | * (at your option) any later version. |
||||
| 15 | * |
||||
| 16 | * The GNU General Public License can be found at |
||||
| 17 | * http://www.gnu.org/copyleft/gpl.html. |
||||
| 18 | * |
||||
| 19 | * This script is distributed in the hope that it will be useful, |
||||
| 20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
| 21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
| 22 | * GNU General Public License for more details. |
||||
| 23 | * |
||||
| 24 | * This copyright notice MUST APPEAR in all copies of the script! |
||||
| 25 | ***************************************************************/ |
||||
| 26 | |||||
| 27 | use ApacheSolrForTypo3\Solr\PingFailedException; |
||||
| 28 | use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration; |
||||
| 29 | use ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager; |
||||
| 30 | use ApacheSolrForTypo3\Solr\System\Solr\ResponseAdapter; |
||||
| 31 | use ApacheSolrForTypo3\Solr\Util; |
||||
| 32 | |||||
| 33 | use Solarium\Client; |
||||
| 34 | use Solarium\Core\Client\Endpoint; |
||||
| 35 | use Solarium\Core\Client\Request; |
||||
| 36 | use Solarium\Core\Query\QueryInterface; |
||||
| 37 | use Solarium\Exception\HttpException; |
||||
| 38 | use TYPO3\CMS\Core\Utility\GeneralUtility; |
||||
| 39 | |||||
| 40 | abstract class AbstractSolrService { |
||||
| 41 | |||||
| 42 | /** |
||||
| 43 | * @var array |
||||
| 44 | */ |
||||
| 45 | protected static $pingCache = []; |
||||
| 46 | |||||
| 47 | /** |
||||
| 48 | * @var TypoScriptConfiguration |
||||
| 49 | */ |
||||
| 50 | protected $configuration; |
||||
| 51 | |||||
| 52 | /** |
||||
| 53 | * @var \ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager |
||||
| 54 | */ |
||||
| 55 | protected $logger = null; |
||||
| 56 | |||||
| 57 | /** |
||||
| 58 | * @var Client |
||||
| 59 | */ |
||||
| 60 | protected $client = null; |
||||
| 61 | |||||
| 62 | /** |
||||
| 63 | * SolrReadService constructor. |
||||
| 64 | */ |
||||
| 65 | public function __construct(Client $client, $typoScriptConfiguration = null, $logManager = null) |
||||
| 66 | { |
||||
| 67 | $this->client = $client; |
||||
| 68 | $this->configuration = $typoScriptConfiguration ?? Util::getSolrConfiguration(); |
||||
| 69 | 131 | $this->logger = $logManager ?? GeneralUtility::makeInstance(SolrLogManager::class, /** @scrutinizer ignore-type */ __CLASS__); |
|||
| 70 | } |
||||
| 71 | 131 | ||||
| 72 | 130 | /** |
|||
| 73 | * Returns the path to the core solr path + core path. |
||||
| 74 | 130 | * |
|||
| 75 | 130 | * @return string |
|||
| 76 | 130 | */ |
|||
| 77 | 130 | public function getCorePath() |
|||
| 78 | { |
||||
| 79 | $endpoint = $this->getPrimaryEndpoint(); |
||||
| 80 | return is_null($endpoint) ? '' : $endpoint->getPath() .'/'. $endpoint->getCore(); |
||||
| 81 | } |
||||
| 82 | |||||
| 83 | /** |
||||
| 84 | 130 | * Return a valid http URL given this server's host, port and path and a provided servlet name |
|||
| 85 | * |
||||
| 86 | 130 | * @param string $servlet |
|||
| 87 | 130 | * @param array $params |
|||
| 88 | 1 | * @return string |
|||
| 89 | */ |
||||
| 90 | 130 | protected function _constructUrl($servlet, $params = []) |
|||
| 91 | { |
||||
| 92 | $queryString = count($params) ? '?' . http_build_query($params, null, '&') : ''; |
||||
| 93 | return $this->__toString() . $servlet . $queryString; |
||||
| 94 | } |
||||
| 95 | |||||
| 96 | /** |
||||
| 97 | * Creates a string representation of the Solr connection. Specifically |
||||
| 98 | 77 | * will return the Solr URL. |
|||
| 99 | * |
||||
| 100 | 77 | * @return string The Solr URL. |
|||
| 101 | */ |
||||
| 102 | public function __toString() |
||||
| 103 | { |
||||
| 104 | $endpoint = $this->getPrimaryEndpoint(); |
||||
| 105 | if (!$endpoint instanceof Endpoint) { |
||||
| 106 | return ''; |
||||
| 107 | } |
||||
| 108 | 3 | ||||
| 109 | return $endpoint->getScheme(). '://' . $endpoint->getHost() . ':' . $endpoint->getPort() . $endpoint->getPath() . '/' . $endpoint->getCore() . '/'; |
||||
| 110 | 3 | } |
|||
| 111 | |||||
| 112 | /** |
||||
| 113 | * @return Endpoint|null |
||||
| 114 | */ |
||||
| 115 | public function getPrimaryEndpoint() |
||||
| 116 | { |
||||
| 117 | return is_array($this->client->getEndpoints()) ? reset($this->client->getEndpoints()) : null; |
||||
|
0 ignored issues
–
show
introduced
by
Loading history...
|
|||||
| 118 | } |
||||
| 119 | 131 | ||||
| 120 | /** |
||||
| 121 | * Central method for making a get operation against this Solr Server |
||||
| 122 | 131 | * |
|||
| 123 | 2 | * @param string $url |
|||
| 124 | * @return ResponseAdapter |
||||
| 125 | */ |
||||
| 126 | 130 | protected function _sendRawGet($url) |
|||
| 127 | 130 | { |
|||
| 128 | 1 | return $this->_sendRawRequest($url, Request::METHOD_GET); |
|||
| 129 | } |
||||
| 130 | |||||
| 131 | /** |
||||
| 132 | 130 | * Central method for making a HTTP DELETE operation against the Solr server |
|||
| 133 | * |
||||
| 134 | 130 | * @param string $url |
|||
| 135 | 1 | * @return ResponseAdapter |
|||
| 136 | */ |
||||
| 137 | 130 | protected function _sendRawDelete($url) |
|||
| 138 | { |
||||
| 139 | return $this->_sendRawRequest($url, Request::METHOD_DELETE); |
||||
| 140 | } |
||||
| 141 | |||||
| 142 | /** |
||||
| 143 | * Central method for making a post operation against this Solr Server |
||||
| 144 | * |
||||
| 145 | * @param string $url |
||||
| 146 | * @param string $rawPost |
||||
| 147 | 130 | * @param string $contentType |
|||
| 148 | * @return ResponseAdapter |
||||
| 149 | 130 | */ |
|||
| 150 | protected function _sendRawPost($url, $rawPost, $contentType = 'text/xml; charset=UTF-8') |
||||
| 151 | 130 | { |
|||
| 152 | 2 | $initializeRequest = function(Request $request) use ($rawPost, $contentType) { |
|||
| 153 | $request->setRawData($rawPost); |
||||
| 154 | $request->addHeader('Content-Type: ' . $contentType); |
||||
| 155 | return $request; |
||||
| 156 | 2 | }; |
|||
| 157 | |||||
| 158 | return $this->_sendRawRequest($url, Request::METHOD_POST, $rawPost, $initializeRequest); |
||||
| 159 | 130 | } |
|||
| 160 | |||||
| 161 | /** |
||||
| 162 | * Method that performs an http request with the solarium client. |
||||
| 163 | * |
||||
| 164 | * @param string $url |
||||
| 165 | * @param string $method |
||||
| 166 | * @param string $body |
||||
| 167 | * @param \Closure $initializeRequest |
||||
| 168 | * @return ResponseAdapter |
||||
| 169 | 66 | */ |
|||
| 170 | protected function _sendRawRequest($url, $method = Request::METHOD_GET, $body = '', \Closure $initializeRequest = null) |
||||
| 171 | 66 | { |
|||
| 172 | 66 | $logSeverity = SolrLogManager::INFO; |
|||
| 173 | $exception = null; |
||||
| 174 | |||||
| 175 | 66 | try { |
|||
| 176 | 8 | $request = $this->buildSolariumRequestFromUrl($url, $method); |
|||
| 177 | 8 | ||||
| 178 | 8 | if($initializeRequest !== null) { |
|||
| 179 | $request = $initializeRequest($request); |
||||
| 180 | } |
||||
| 181 | 66 | ||||
| 182 | 8 | $response = $this->executeRequest($request); |
|||
| 183 | } catch (HttpException $exception) { |
||||
| 184 | $logSeverity = SolrLogManager::ERROR; |
||||
| 185 | 66 | $response = new ResponseAdapter($exception->getBody(), $exception->getCode(), $exception->getMessage()); |
|||
| 186 | } |
||||
| 187 | |||||
| 188 | if ($this->configuration->getLoggingQueryRawPost() || $response->getHttpStatus() != 200) { |
||||
| 189 | $message = 'Querying Solr using '.$method; |
||||
| 190 | $this->writeLog($logSeverity, $message, $url, $response, $exception, $body); |
||||
| 191 | } |
||||
| 192 | |||||
| 193 | return $response; |
||||
| 194 | } |
||||
| 195 | 4 | ||||
| 196 | /** |
||||
| 197 | 4 | * Build the log data and writes the message to the log |
|||
| 198 | 4 | * |
|||
| 199 | * @param integer $logSeverity |
||||
| 200 | * @param string $message |
||||
| 201 | 4 | * @param string $url |
|||
| 202 | 4 | * @param ResponseAdapter $solrResponse |
|||
| 203 | 4 | * @param \Exception $exception |
|||
| 204 | * @param string $contentSend |
||||
| 205 | 4 | */ |
|||
| 206 | 4 | protected function writeLog($logSeverity, $message, $url, $solrResponse, $exception = null, $contentSend = '') |
|||
| 207 | { |
||||
| 208 | $logData = $this->buildLogDataFromResponse($solrResponse, $exception, $url, $contentSend); |
||||
| 209 | $this->logger->log($logSeverity, $message, $logData); |
||||
| 210 | } |
||||
| 211 | |||||
| 212 | /** |
||||
| 213 | 4 | * Parses the solr information to build data for the logger. |
|||
| 214 | * |
||||
| 215 | * @param ResponseAdapter $solrResponse |
||||
| 216 | * @param \Exception $e |
||||
| 217 | 4 | * @param string $url |
|||
| 218 | * @param string $contentSend |
||||
| 219 | * @return array |
||||
| 220 | */ |
||||
| 221 | protected function buildLogDataFromResponse(ResponseAdapter $solrResponse, \Exception $e = null, $url = '', $contentSend = '') |
||||
| 222 | { |
||||
| 223 | $logData = ['query url' => $url, 'response' => (array)$solrResponse]; |
||||
| 224 | |||||
| 225 | if ($contentSend !== '') { |
||||
| 226 | $logData['content'] = $contentSend; |
||||
| 227 | } |
||||
| 228 | |||||
| 229 | 94 | if (!empty($e)) { |
|||
| 230 | $logData['exception'] = $e->__toString(); |
||||
| 231 | 94 | return $logData; |
|||
| 232 | 94 | } else { |
|||
| 233 | // trigger data parsing |
||||
| 234 | // @extensionScannerIgnoreLine |
||||
| 235 | 94 | $solrResponse->response; |
|||
| 236 | $logData['response data'] = print_r($solrResponse, true); |
||||
| 237 | return $logData; |
||||
| 238 | } |
||||
| 239 | } |
||||
| 240 | |||||
| 241 | 94 | /** |
|||
| 242 | * Call the /admin/ping servlet, can be used to quickly tell if a connection to the |
||||
| 243 | * server is available. |
||||
| 244 | * |
||||
| 245 | 94 | * Simply overrides the SolrPhpClient implementation, changing ping from a |
|||
| 246 | * HEAD to a GET request, see http://forge.typo3.org/issues/44167 |
||||
| 247 | * |
||||
| 248 | * Also does not report the time, see https://forge.typo3.org/issues/64551 |
||||
| 249 | * |
||||
| 250 | * @param boolean $useCache indicates if the ping result should be cached in the instance or not |
||||
| 251 | * @return bool TRUE if Solr can be reached, FALSE if not |
||||
| 252 | */ |
||||
| 253 | public function ping($useCache = true) |
||||
| 254 | { |
||||
| 255 | try { |
||||
| 256 | $httpResponse = $this->performPingRequest($useCache); |
||||
| 257 | } catch (HttpException $exception) { |
||||
| 258 | return false; |
||||
| 259 | 8 | } |
|||
| 260 | |||||
| 261 | 8 | return ($httpResponse->getHttpStatus() === 200); |
|||
| 262 | 8 | } |
|||
| 263 | 8 | ||||
| 264 | /** |
||||
| 265 | * Call the /admin/ping servlet, can be used to get the runtime of a ping request. |
||||
| 266 | * |
||||
| 267 | * @param boolean $useCache indicates if the ping result should be cached in the instance or not |
||||
| 268 | * @return double runtime in milliseconds |
||||
| 269 | * @throws \ApacheSolrForTypo3\Solr\PingFailedException |
||||
| 270 | */ |
||||
| 271 | public function getPingRoundTripRuntime($useCache = true) |
||||
| 272 | { |
||||
| 273 | try { |
||||
| 274 | 8 | $start = $this->getMilliseconds(); |
|||
| 275 | $httpResponse = $this->performPingRequest($useCache); |
||||
| 276 | 8 | $end = $this->getMilliseconds(); |
|||
| 277 | } catch (HttpException $e) { |
||||
| 278 | 8 | $message = 'Solr ping failed with unexpected response code: ' . $e->getCode(); |
|||
| 279 | /** @var $exception \ApacheSolrForTypo3\Solr\PingFailedException */ |
||||
| 280 | $exception = GeneralUtility::makeInstance(PingFailedException::class, /** @scrutinizer ignore-type */ $message); |
||||
| 281 | throw $exception; |
||||
| 282 | 8 | } |
|||
| 283 | 8 | ||||
| 284 | 8 | if ($httpResponse->getHttpStatus() !== 200) { |
|||
| 285 | $message = 'Solr ping failed with unexpected response code: ' . $httpResponse->getHttpStatus(); |
||||
| 286 | /** @var $exception \ApacheSolrForTypo3\Solr\PingFailedException */ |
||||
| 287 | $exception = GeneralUtility::makeInstance(PingFailedException::class, /** @scrutinizer ignore-type */ $message); |
||||
| 288 | throw $exception; |
||||
| 289 | } |
||||
| 290 | |||||
| 291 | return $end - $start; |
||||
| 292 | } |
||||
| 293 | |||||
| 294 | /** |
||||
| 295 | * Performs a ping request and returns the result. |
||||
| 296 | * |
||||
| 297 | * @param boolean $useCache indicates if the ping result should be cached in the instance or not |
||||
| 298 | * @return ResponseAdapter |
||||
| 299 | 22 | */ |
|||
| 300 | protected function performPingRequest($useCache = true) |
||||
| 301 | 22 | { |
|||
| 302 | 22 | $cacheKey = (string)($this); |
|||
| 303 | 22 | if ($useCache && isset(static::$pingCache[$cacheKey])) { |
|||
| 304 | return static::$pingCache[$cacheKey]; |
||||
| 305 | } |
||||
| 306 | |||||
| 307 | $pingQuery = $this->client->createPing(); |
||||
| 308 | $pingResult = $this->createAndExecuteRequest($pingQuery); |
||||
| 309 | |||||
| 310 | if ($useCache) { |
||||
| 311 | 7 | static::$pingCache[$cacheKey] = $pingResult; |
|||
| 312 | } |
||||
| 313 | 7 | ||||
| 314 | 7 | return $pingResult; |
|||
| 315 | } |
||||
| 316 | |||||
| 317 | /** |
||||
| 318 | * Returns the current time in milliseconds. |
||||
| 319 | * |
||||
| 320 | * @return double |
||||
| 321 | */ |
||||
| 322 | protected function getMilliseconds() |
||||
| 323 | { |
||||
| 324 | return GeneralUtility::milliseconds(); |
||||
|
0 ignored issues
–
show
The function
TYPO3\CMS\Core\Utility\G...Utility::milliseconds() has been deprecated: will be removed in TYPO3 v11.0. Use the native PHP functions round(microtime(true) * 1000) instead.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This function has been deprecated. The supplier of the function has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead. Loading history...
|
|||||
| 325 | } |
||||
| 326 | |||||
| 327 | /** |
||||
| 328 | * @param QueryInterface $query |
||||
| 329 | * @return ResponseAdapter |
||||
| 330 | 72 | */ |
|||
| 331 | protected function createAndExecuteRequest(QueryInterface $query): ResponseAdapter |
||||
| 332 | 72 | { |
|||
| 333 | 72 | $request = $this->client->createRequest($query); |
|||
| 334 | return $this->executeRequest($request); |
||||
| 335 | } |
||||
| 336 | |||||
| 337 | /** |
||||
| 338 | * @param $request |
||||
| 339 | * @return ResponseAdapter |
||||
| 340 | */ |
||||
| 341 | protected function executeRequest($request): ResponseAdapter |
||||
| 342 | { |
||||
| 343 | $result = $this->client->executeRequest($request); |
||||
| 344 | 3 | return new ResponseAdapter($result->getBody(), $result->getStatusCode(), $result->getStatusMessage()); |
|||
| 345 | } |
||||
| 346 | 3 | ||||
| 347 | 3 | /** |
|||
| 348 | 3 | * @param string $url |
|||
| 349 | * @param string $httpMethod |
||||
| 350 | 3 | * @return Request |
|||
| 351 | 1 | */ |
|||
| 352 | protected function buildSolariumRequestFromUrl($url, $httpMethod = Request::METHOD_GET): Request |
||||
| 353 | 1 | { |
|||
| 354 | 1 | $params = []; |
|||
| 355 | 1 | parse_str(parse_url($url, PHP_URL_QUERY), $params); |
|||
| 356 | |||||
| 357 | $path = parse_url($url, PHP_URL_PATH); |
||||
| 358 | 2 | $endpoint = $this->getPrimaryEndpoint(); |
|||
| 359 | $coreBasePath = $endpoint->getPath() . '/' . $endpoint->getCore() . '/'; |
||||
| 360 | $handler = str_replace($coreBasePath, '', $path); |
||||
| 361 | |||||
| 362 | $request = new Request(); |
||||
| 363 | $request->setMethod($httpMethod); |
||||
| 364 | $request->setParams($params); |
||||
| 365 | $request->setHandler($handler); |
||||
| 366 | return $request; |
||||
| 367 | } |
||||
| 368 | } |
||||
| 369 |