vendor/friendsofsymfony/elastica-bundle/src/Elastica/Client.php line 62

Open in your IDE?
  1. <?php
  2. /*
  3. * This file is part of the FOSElasticaBundle package.
  4. *
  5. * (c) FriendsOfSymfony <https://friendsofsymfony.github.com/>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace FOS\ElasticaBundle\Elastica;
  11. use Elastica\Client as BaseClient;
  12. use Elastica\Exception\ClientException;
  13. use Elastica\Exception\ExceptionInterface;
  14. use Elastica\Index as BaseIndex;
  15. use Elastica\Request;
  16. use Elastica\Response;
  17. use FOS\ElasticaBundle\Event\ElasticaRequestExceptionEvent;
  18. use FOS\ElasticaBundle\Event\PostElasticaRequestEvent;
  19. use FOS\ElasticaBundle\Event\PreElasticaRequestEvent;
  20. use FOS\ElasticaBundle\Logger\ElasticaLogger;
  21. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  22. use Symfony\Component\Stopwatch\Stopwatch;
  23. /**
  24. * Extends the default Elastica client to provide logging for errors that occur
  25. * during communication with ElasticSearch.
  26. *
  27. * @author Gordon Franke <info@nevalon.de>
  28. */
  29. class Client extends BaseClient
  30. {
  31. /**
  32. * Stores created indexes to avoid recreation.
  33. *
  34. * @var array<string, BaseIndex>
  35. */
  36. private $indexCache = [];
  37. /**
  38. * Stores created index template to avoid recreation.
  39. *
  40. * @var array<string, IndexTemplate>
  41. */
  42. private $indexTemplateCache = [];
  43. /**
  44. * Symfony's debugging Stopwatch.
  45. *
  46. * @var Stopwatch|null
  47. */
  48. private $stopwatch;
  49. private ?EventDispatcherInterface $dispatcher = null;
  50. /**
  51. * @param array<mixed> $data
  52. * @param array<mixed> $query
  53. */
  54. public function request(string $path, string $method = Request::GET, $data = [], array $query = [], string $contentType = Request::DEFAULT_CONTENT_TYPE): Response
  55. {
  56. if ($this->stopwatch) {
  57. $this->stopwatch->start('es_request', 'fos_elastica');
  58. }
  59. if ($this->dispatcher) {
  60. $this->dispatcher->dispatch(new PreElasticaRequestEvent($path, $method, $data, $query, $contentType));
  61. }
  62. try {
  63. $response = parent::request($path, $method, $data, $query, $contentType);
  64. if ($this->dispatcher) {
  65. $this->dispatcher->dispatch(new PostElasticaRequestEvent($this->getLastRequest(), $this->getLastResponse()));
  66. }
  67. } catch (ExceptionInterface $e) {
  68. $this->logQuery($path, $method, $data, $query, 0, 0, 0);
  69. if ($this->dispatcher) {
  70. $this->dispatcher->dispatch(new ElasticaRequestExceptionEvent($this->getLastRequest(), $e));
  71. }
  72. throw $e;
  73. }
  74. $responseData = $response->getData();
  75. $transportInfo = $response->getTransferInfo();
  76. $connection = $this->getLastRequest()->getConnection();
  77. $forbiddenHttpCodes = $connection->hasConfig('http_error_codes') ? $connection->getConfig('http_error_codes') : [];
  78. if (isset($transportInfo['http_code']) && \in_array($transportInfo['http_code'], $forbiddenHttpCodes, true)) {
  79. $body = \json_encode($responseData);
  80. $message = \sprintf('Error in transportInfo: response code is %s, response body is %s', $transportInfo['http_code'], $body);
  81. throw new ClientException($message);
  82. }
  83. if (isset($responseData['took'], $responseData['hits'])) {
  84. $this->logQuery($path, $method, $data, $query, $response->getQueryTime(), $response->getEngineTime(), $responseData['hits']['total']['value'] ?? 0);
  85. } else {
  86. $this->logQuery($path, $method, $data, $query, $response->getQueryTime(), 0, 0);
  87. }
  88. if ($this->stopwatch) {
  89. $this->stopwatch->stop('es_request');
  90. }
  91. return $response;
  92. }
  93. public function getIndex(string $name): BaseIndex
  94. {
  95. // TODO PHP >= 7.4 ??=
  96. return $this->indexCache[$name] ?? ($this->indexCache[$name] = new Index($this, $name));
  97. }
  98. /**
  99. * @param string $name
  100. */
  101. public function getIndexTemplate($name): IndexTemplate
  102. {
  103. // TODO PHP >= 7.4 ??=
  104. return $this->indexTemplateCache[$name] ?? ($this->indexTemplateCache[$name] = new IndexTemplate($this, $name));
  105. }
  106. /**
  107. * Sets a stopwatch instance for debugging purposes.
  108. */
  109. public function setStopwatch(?Stopwatch $stopwatch = null): void
  110. {
  111. $this->stopwatch = $stopwatch;
  112. }
  113. public function setEventDispatcher(?EventDispatcherInterface $dispatcher = null): void
  114. {
  115. $this->dispatcher = $dispatcher;
  116. }
  117. /**
  118. * Log the query if we have an instance of ElasticaLogger.
  119. *
  120. * @param array<mixed>|string $data
  121. * @param array<mixed> $query
  122. * @param float $queryTime
  123. * @param int $engineMS
  124. */
  125. private function logQuery(string $path, string $method, $data, array $query, $queryTime, $engineMS = 0, int $itemCount = 0): void
  126. {
  127. if (!$this->_logger instanceof ElasticaLogger) {
  128. return;
  129. }
  130. $connection = $this->getLastRequest()->getConnection();
  131. $connectionArray = [
  132. 'host' => $connection->getHost(),
  133. 'port' => $connection->getPort(),
  134. 'transport' => $connection->getTransport(),
  135. 'headers' => $connection->hasConfig('headers') ? $connection->getConfig('headers') : [],
  136. ];
  137. $this->_logger->logQuery($path, $method, $data, $queryTime, $connectionArray, $query, $engineMS, $itemCount);
  138. }
  139. }