1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Doctrine\Bundle\DoctrineBundle\Controller; |
4
|
|
|
|
5
|
|
|
use Doctrine\DBAL\Connection; |
6
|
|
|
use Doctrine\DBAL\Platforms\SqlitePlatform; |
7
|
|
|
use Doctrine\DBAL\Platforms\SQLServerPlatform; |
8
|
|
|
use Exception; |
9
|
|
|
use PDO; |
10
|
|
|
use Symfony\Component\DependencyInjection\ContainerAwareInterface; |
11
|
|
|
use Symfony\Component\DependencyInjection\ContainerInterface; |
12
|
|
|
use Symfony\Component\HttpFoundation\Response; |
13
|
|
|
use Symfony\Component\HttpKernel\Profiler\Profiler; |
14
|
|
|
use Symfony\Component\VarDumper\Cloner\Data; |
15
|
|
|
|
16
|
|
|
class ProfilerController implements ContainerAwareInterface |
17
|
|
|
{ |
18
|
|
|
/** @var ContainerInterface */ |
19
|
|
|
private $container; |
20
|
|
|
|
21
|
|
|
/** |
22
|
|
|
* {@inheritDoc} |
23
|
|
|
*/ |
24
|
|
|
public function setContainer(ContainerInterface $container = null) |
25
|
|
|
{ |
26
|
|
|
$this->container = $container; |
27
|
|
|
} |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* Renders the profiler panel for the given token. |
31
|
|
|
* |
32
|
|
|
* @param string $token The profiler token |
33
|
|
|
* @param string $connectionName |
34
|
|
|
* @param int $query |
35
|
|
|
* |
36
|
|
|
* @return Response A Response instance |
37
|
|
|
*/ |
38
|
|
|
public function explainAction($token, $connectionName, $query) |
39
|
|
|
{ |
40
|
|
|
/** @var Profiler $profiler */ |
41
|
|
|
$profiler = $this->container->get('profiler'); |
42
|
|
|
$profiler->disable(); |
43
|
|
|
|
44
|
|
|
$profile = $profiler->loadProfile($token); |
45
|
|
|
$queries = $profile->getCollector('db')->getQueries(); |
46
|
|
|
|
47
|
|
|
if (! isset($queries[$connectionName][$query])) { |
48
|
|
|
return new Response('This query does not exist.'); |
49
|
|
|
} |
50
|
|
|
|
51
|
|
|
$query = $queries[$connectionName][$query]; |
52
|
|
|
if (! $query['explainable']) { |
53
|
|
|
return new Response('This query cannot be explained.'); |
54
|
|
|
} |
55
|
|
|
|
56
|
|
|
/** @var Connection $connection */ |
57
|
|
|
$connection = $this->container->get('doctrine')->getConnection($connectionName); |
58
|
|
|
try { |
59
|
|
|
$platform = $connection->getDatabasePlatform(); |
60
|
|
|
if ($platform instanceof SqlitePlatform) { |
61
|
|
|
$results = $this->explainSQLitePlatform($connection, $query); |
62
|
|
|
} elseif ($platform instanceof SQLServerPlatform) { |
63
|
|
|
$results = $this->explainSQLServerPlatform($connection, $query); |
64
|
|
|
} else { |
65
|
|
|
$results = $this->explainOtherPlatform($connection, $query); |
66
|
|
|
} |
67
|
|
|
} catch (Exception $e) { |
68
|
|
|
return new Response('This query cannot be explained.'); |
69
|
|
|
} |
70
|
|
|
|
71
|
|
|
return new Response($this->container->get('twig')->render('@Doctrine/Collector/explain.html.twig', [ |
72
|
|
|
'data' => $results, |
73
|
|
|
'query' => $query, |
74
|
|
|
])); |
75
|
|
|
} |
76
|
|
|
|
77
|
|
|
/** |
78
|
|
|
* @param mixed[] $query |
79
|
|
|
*/ |
80
|
|
View Code Duplication |
private function explainSQLitePlatform(Connection $connection, array $query) |
|
|
|
|
81
|
|
|
{ |
82
|
|
|
$params = $query['params']; |
83
|
|
|
|
84
|
|
|
if ($params instanceof Data) { |
85
|
|
|
$params = $params->getValue(true); |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
return $connection->executeQuery('EXPLAIN QUERY PLAN ' . $query['sql'], $params, $query['types']) |
|
|
|
|
89
|
|
|
->fetchAll(PDO::FETCH_ASSOC); |
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
private function explainSQLServerPlatform(Connection $connection, $query) |
93
|
|
|
{ |
94
|
|
|
if (stripos($query['sql'], 'SELECT') === 0) { |
95
|
|
|
$sql = 'SET STATISTICS PROFILE ON; ' . $query['sql'] . '; SET STATISTICS PROFILE OFF;'; |
96
|
|
|
} else { |
97
|
|
|
$sql = 'SET SHOWPLAN_TEXT ON; GO; SET NOEXEC ON; ' . $query['sql'] . '; SET NOEXEC OFF; GO; SET SHOWPLAN_TEXT OFF;'; |
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
$params = $query['params']; |
101
|
|
|
|
102
|
|
|
if ($params instanceof Data) { |
103
|
|
|
$params = $params->getValue(true); |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
$stmt = $connection->executeQuery($sql, $params, $query['types']); |
107
|
|
|
$stmt->nextRowset(); |
108
|
|
|
|
109
|
|
|
return $stmt->fetchAll(PDO::FETCH_ASSOC); |
110
|
|
|
} |
111
|
|
|
|
112
|
|
View Code Duplication |
private function explainOtherPlatform(Connection $connection, $query) |
113
|
|
|
{ |
114
|
|
|
$params = $query['params']; |
115
|
|
|
|
116
|
|
|
if ($params instanceof Data) { |
117
|
|
|
$params = $params->getValue(true); |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
return $connection->executeQuery('EXPLAIN ' . $query['sql'], $params, $query['types']) |
121
|
|
|
->fetchAll(PDO::FETCH_ASSOC); |
122
|
|
|
} |
123
|
|
|
} |
124
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.