DoctrineDataCollector.php 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.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 Symfony\Bridge\Doctrine\DataCollector;
  11. use Doctrine\Common\Persistence\ManagerRegistry;
  12. use Doctrine\DBAL\Logging\DebugStack;
  13. use Doctrine\DBAL\Types\ConversionException;
  14. use Doctrine\DBAL\Types\Type;
  15. use Symfony\Component\HttpFoundation\Request;
  16. use Symfony\Component\HttpFoundation\Response;
  17. use Symfony\Component\HttpKernel\DataCollector\DataCollector;
  18. /**
  19. * DoctrineDataCollector.
  20. *
  21. * @author Fabien Potencier <fabien@symfony.com>
  22. */
  23. class DoctrineDataCollector extends DataCollector
  24. {
  25. private $registry;
  26. private $connections;
  27. private $managers;
  28. private $loggers = array();
  29. public function __construct(ManagerRegistry $registry)
  30. {
  31. $this->registry = $registry;
  32. $this->connections = $registry->getConnectionNames();
  33. $this->managers = $registry->getManagerNames();
  34. }
  35. /**
  36. * Adds the stack logger for a connection.
  37. *
  38. * @param string $name
  39. * @param DebugStack $logger
  40. */
  41. public function addLogger($name, DebugStack $logger)
  42. {
  43. $this->loggers[$name] = $logger;
  44. }
  45. /**
  46. * {@inheritdoc}
  47. */
  48. public function collect(Request $request, Response $response, \Exception $exception = null)
  49. {
  50. $queries = array();
  51. foreach ($this->loggers as $name => $logger) {
  52. $queries[$name] = $this->sanitizeQueries($name, $logger->queries);
  53. }
  54. $this->data = array(
  55. 'queries' => $queries,
  56. 'connections' => $this->connections,
  57. 'managers' => $this->managers,
  58. );
  59. }
  60. public function getManagers()
  61. {
  62. return $this->data['managers'];
  63. }
  64. public function getConnections()
  65. {
  66. return $this->data['connections'];
  67. }
  68. public function getQueryCount()
  69. {
  70. return array_sum(array_map('count', $this->data['queries']));
  71. }
  72. public function getQueries()
  73. {
  74. return $this->data['queries'];
  75. }
  76. public function getTime()
  77. {
  78. $time = 0;
  79. foreach ($this->data['queries'] as $queries) {
  80. foreach ($queries as $query) {
  81. $time += $query['executionMS'];
  82. }
  83. }
  84. return $time;
  85. }
  86. /**
  87. * {@inheritdoc}
  88. */
  89. public function getName()
  90. {
  91. return 'db';
  92. }
  93. private function sanitizeQueries($connectionName, $queries)
  94. {
  95. foreach ($queries as $i => $query) {
  96. $queries[$i] = $this->sanitizeQuery($connectionName, $query);
  97. }
  98. return $queries;
  99. }
  100. private function sanitizeQuery($connectionName, $query)
  101. {
  102. $query['explainable'] = true;
  103. if (null === $query['params']) {
  104. $query['params'] = array();
  105. }
  106. if (!\is_array($query['params'])) {
  107. $query['params'] = array($query['params']);
  108. }
  109. foreach ($query['params'] as $j => $param) {
  110. if (isset($query['types'][$j])) {
  111. // Transform the param according to the type
  112. $type = $query['types'][$j];
  113. if (\is_string($type)) {
  114. $type = Type::getType($type);
  115. }
  116. if ($type instanceof Type) {
  117. $query['types'][$j] = $type->getBindingType();
  118. try {
  119. $param = $type->convertToDatabaseValue($param, $this->registry->getConnection($connectionName)->getDatabasePlatform());
  120. } catch (\TypeError $e) {
  121. // Error thrown while processing params, query is not explainable.
  122. $query['explainable'] = false;
  123. } catch (ConversionException $e) {
  124. $query['explainable'] = false;
  125. }
  126. }
  127. }
  128. list($query['params'][$j], $explainable) = $this->sanitizeParam($param);
  129. if (!$explainable) {
  130. $query['explainable'] = false;
  131. }
  132. }
  133. return $query;
  134. }
  135. /**
  136. * Sanitizes a param.
  137. *
  138. * The return value is an array with the sanitized value and a boolean
  139. * indicating if the original value was kept (allowing to use the sanitized
  140. * value to explain the query).
  141. *
  142. * @param mixed $var
  143. *
  144. * @return array
  145. */
  146. private function sanitizeParam($var)
  147. {
  148. if (\is_object($var)) {
  149. return array(sprintf('Object(%s)', \get_class($var)), false);
  150. }
  151. if (\is_array($var)) {
  152. $a = array();
  153. $original = true;
  154. foreach ($var as $k => $v) {
  155. list($value, $orig) = $this->sanitizeParam($v);
  156. $original = $original && $orig;
  157. $a[$k] = $value;
  158. }
  159. return array($a, $original);
  160. }
  161. if (\is_resource($var)) {
  162. return array(sprintf('Resource(%s)', get_resource_type($var)), false);
  163. }
  164. return array($var, true);
  165. }
  166. }