LoggingStatement.php 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  5. * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  6. *
  7. * Licensed under The MIT License
  8. * For full copyright and license information, please see the LICENSE.txt
  9. * Redistributions of files must retain the above copyright notice.
  10. *
  11. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  12. * @link https://cakephp.org CakePHP(tm) Project
  13. * @since 3.0.0
  14. * @license https://opensource.org/licenses/mit-license.php MIT License
  15. */
  16. namespace Cake\Database\Log;
  17. use Cake\Database\Statement\StatementDecorator;
  18. use Exception;
  19. use Psr\Log\LoggerInterface;
  20. /**
  21. * Statement decorator used to
  22. *
  23. * @internal
  24. */
  25. class LoggingStatement extends StatementDecorator
  26. {
  27. /**
  28. * Logger instance responsible for actually doing the logging task
  29. *
  30. * @var \Psr\Log\LoggerInterface
  31. */
  32. protected $_logger;
  33. /**
  34. * Holds bound params
  35. *
  36. * @var array<array>
  37. */
  38. protected $_compiledParams = [];
  39. /**
  40. * Query execution start time.
  41. *
  42. * @var float
  43. */
  44. protected $startTime = 0.0;
  45. /**
  46. * Logged query
  47. *
  48. * @var \Cake\Database\Log\LoggedQuery|null
  49. */
  50. protected $loggedQuery;
  51. /**
  52. * Wrapper for the execute function to calculate time spent
  53. * and log the query afterwards.
  54. *
  55. * @param array|null $params List of values to be bound to query
  56. * @return bool True on success, false otherwise
  57. * @throws \Exception Re-throws any exception raised during query execution.
  58. */
  59. public function execute(?array $params = null): bool
  60. {
  61. $this->startTime = microtime(true);
  62. $this->loggedQuery = new LoggedQuery();
  63. $this->loggedQuery->driver = $this->_driver;
  64. $this->loggedQuery->params = $params ?: $this->_compiledParams;
  65. try {
  66. $result = parent::execute($params);
  67. $this->loggedQuery->took = (int)round((microtime(true) - $this->startTime) * 1000, 0);
  68. } catch (Exception $e) {
  69. if (version_compare(PHP_VERSION, '8.2.0', '<')) {
  70. deprecationWarning(
  71. '4.4.12 - Having queryString set on exceptions is deprecated.' .
  72. 'If you are not using this attribute there is no action to take.'
  73. );
  74. /** @psalm-suppress UndefinedPropertyAssignment */
  75. $e->queryString = $this->queryString;
  76. }
  77. $this->loggedQuery->error = $e;
  78. $this->_log();
  79. throw $e;
  80. }
  81. if (preg_match('/^(?!SELECT)/i', $this->queryString)) {
  82. $this->rowCount();
  83. }
  84. return $result;
  85. }
  86. /**
  87. * @inheritDoc
  88. */
  89. public function fetch($type = self::FETCH_TYPE_NUM)
  90. {
  91. $record = parent::fetch($type);
  92. if ($this->loggedQuery) {
  93. $this->rowCount();
  94. }
  95. return $record;
  96. }
  97. /**
  98. * @inheritDoc
  99. */
  100. public function fetchAll($type = self::FETCH_TYPE_NUM)
  101. {
  102. $results = parent::fetchAll($type);
  103. if ($this->loggedQuery) {
  104. $this->rowCount();
  105. }
  106. return $results;
  107. }
  108. /**
  109. * @inheritDoc
  110. */
  111. public function rowCount(): int
  112. {
  113. $result = parent::rowCount();
  114. if ($this->loggedQuery) {
  115. $this->loggedQuery->numRows = $result;
  116. $this->_log();
  117. }
  118. return $result;
  119. }
  120. /**
  121. * Copies the logging data to the passed LoggedQuery and sends it
  122. * to the logging system.
  123. *
  124. * @return void
  125. */
  126. protected function _log(): void
  127. {
  128. if ($this->loggedQuery === null) {
  129. return;
  130. }
  131. $this->loggedQuery->query = $this->queryString;
  132. $this->getLogger()->debug((string)$this->loggedQuery, ['query' => $this->loggedQuery]);
  133. $this->loggedQuery = null;
  134. }
  135. /**
  136. * Wrapper for bindValue function to gather each parameter to be later used
  137. * in the logger function.
  138. *
  139. * @param string|int $column Name or param position to be bound
  140. * @param mixed $value The value to bind to variable in query
  141. * @param string|int|null $type PDO type or name of configured Type class
  142. * @return void
  143. */
  144. public function bindValue($column, $value, $type = 'string'): void
  145. {
  146. parent::bindValue($column, $value, $type);
  147. if ($type === null) {
  148. $type = 'string';
  149. }
  150. if (!ctype_digit($type)) {
  151. $value = $this->cast($value, $type)[0];
  152. }
  153. $this->_compiledParams[$column] = $value;
  154. }
  155. /**
  156. * Sets a logger
  157. *
  158. * @param \Psr\Log\LoggerInterface $logger Logger object
  159. * @return void
  160. */
  161. public function setLogger(LoggerInterface $logger): void
  162. {
  163. $this->_logger = $logger;
  164. }
  165. /**
  166. * Gets the logger object
  167. *
  168. * @return \Psr\Log\LoggerInterface logger instance
  169. */
  170. public function getLogger(): LoggerInterface
  171. {
  172. return $this->_logger;
  173. }
  174. }