This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | /** |
||
4 | * This file is part of the DTForce Nette-Doctrine extension (http://www.dtforce.com/). |
||
5 | * |
||
6 | * This source file is subject to the GNU Lesser General Public License. |
||
7 | */ |
||
8 | |||
9 | namespace DTForce\DoctrineExtension\Debug; |
||
10 | |||
11 | use Doctrine\DBAL\Connection; |
||
12 | use Doctrine\DBAL\Logging\SQLLogger; |
||
13 | use Doctrine\ORM\Cache\Logging\CacheLoggerChain; |
||
14 | use Doctrine\ORM\Cache\Logging\StatisticsCacheLogger; |
||
15 | use Doctrine\ORM\EntityManager; |
||
16 | use Lekarna\Application\Debug\DebugHelper; |
||
17 | use Lekarna\Doctrine\Cache\RedisCache; |
||
18 | use Nette\Database\Helpers; |
||
19 | use Nette\InvalidStateException; |
||
20 | use Nette\Utils\Strings; |
||
21 | use Tracy\Debugger; |
||
22 | use Tracy\Dumper; |
||
23 | use Tracy\IBarPanel; |
||
24 | |||
25 | |||
26 | /** |
||
27 | * Debug panel for Doctrine |
||
28 | * |
||
29 | * @author David Grudl |
||
30 | * @author Patrik Votoček |
||
31 | * @author Jan Mareš |
||
32 | * @author Tomáš Pilař |
||
33 | */ |
||
34 | final class DoctrineSQLPanel implements IBarPanel, SQLLogger |
||
35 | { |
||
36 | |||
37 | const DATA_INDEX_SQL = 0; |
||
38 | const DATA_INDEX_PARAMS = 1; |
||
39 | const DATA_INDEX_TYPES = 2; |
||
40 | const DATA_INDEX_TIME = 3; |
||
41 | const DATA_INDEX_EXPLAIN = 4; |
||
42 | const DATA_INDEX_TRACE = 5; |
||
43 | const DATA_INDEX_COUNT = 6; |
||
44 | |||
45 | /** |
||
46 | * @var bool |
||
47 | */ |
||
48 | private $sortQueries = FALSE; |
||
49 | |||
50 | /** |
||
51 | * @var bool |
||
52 | */ |
||
53 | private $groupQueries = FALSE; |
||
54 | |||
55 | /** |
||
56 | * whether to do explain queries for selects or not |
||
57 | * @var bool |
||
58 | */ |
||
59 | private $doExplains = FALSE; |
||
60 | |||
61 | /** |
||
62 | * @var bool |
||
63 | */ |
||
64 | private $explainRunning = FALSE; |
||
65 | |||
66 | /** |
||
67 | * @var Connection|NULL |
||
68 | */ |
||
69 | private $connection; |
||
70 | |||
71 | /** |
||
72 | * @var int |
||
73 | */ |
||
74 | private $totalTime = 0; |
||
75 | |||
76 | /** |
||
77 | * @var array |
||
78 | */ |
||
79 | private $queries = []; |
||
80 | |||
81 | /** |
||
82 | * @var EntityManager |
||
83 | */ |
||
84 | private $entityManager; |
||
85 | |||
86 | |||
87 | public function __construct(EntityManager $entityManager) |
||
0 ignored issues
–
show
|
|||
88 | { |
||
89 | $this->entityManager = $entityManager; |
||
90 | } |
||
91 | |||
92 | |||
93 | /** |
||
94 | * {@inheritdoc} |
||
95 | */ |
||
96 | public function startQuery($sql, array $params = NULL, array $types = NULL) |
||
97 | { |
||
98 | if ($this->explainRunning) { |
||
99 | return; |
||
100 | } |
||
101 | |||
102 | Debugger::timer('doctrine'); |
||
103 | |||
104 | $this->queries[] = [ |
||
105 | self::DATA_INDEX_SQL => $sql, |
||
106 | self::DATA_INDEX_PARAMS => $params, |
||
107 | self::DATA_INDEX_TYPES => $types, |
||
108 | self::DATA_INDEX_TIME => 0, |
||
109 | self::DATA_INDEX_EXPLAIN => NULL, |
||
110 | self::DATA_INDEX_TRACE => debug_backtrace(PHP_VERSION_ID >= 50306 ? DEBUG_BACKTRACE_IGNORE_ARGS : FALSE) |
||
111 | ]; |
||
112 | } |
||
113 | |||
114 | |||
115 | /** |
||
116 | * {@inheritdoc} |
||
117 | */ |
||
118 | public function stopQuery() |
||
119 | { |
||
120 | if ($this->explainRunning) { |
||
121 | return; |
||
122 | } |
||
123 | |||
124 | $keys = array_keys($this->queries); |
||
125 | $key = end($keys); |
||
126 | $this->queries[$key][self::DATA_INDEX_TIME] = Debugger::timer('doctrine'); |
||
127 | $this->totalTime += $this->queries[$key][self::DATA_INDEX_TIME]; |
||
128 | |||
129 | // get EXPLAIN for SELECT queries |
||
130 | if ($this->doExplains) { |
||
131 | if ($this->connection === NULL) { |
||
132 | throw new InvalidStateException( |
||
133 | 'You must set a Doctrine\DBAL\Connection to get EXPLAIN.' |
||
134 | ); |
||
135 | } |
||
136 | |||
137 | $query = $this->queries[$key][self::DATA_INDEX_SQL]; |
||
138 | |||
139 | if ( ! Strings::startsWith($query, 'SELECT')) { // only SELECTs are supported |
||
140 | return; |
||
141 | } |
||
142 | |||
143 | // prevent logging explains & infinite recursion |
||
144 | $this->explainRunning = TRUE; |
||
145 | |||
146 | $params = $this->queries[$key][self::DATA_INDEX_PARAMS]; |
||
147 | $types = $this->queries[$key][self::DATA_INDEX_TYPES]; |
||
148 | |||
149 | $stmt = $this->connection->executeQuery('EXPLAIN ' . $query, $params, $types); |
||
0 ignored issues
–
show
If
$query can contain user-input, it is usually preferable to use a parameter placeholder like :paramName and pass the dynamic input as second argument array('param' => $query) .
Instead of embedding dynamic parameters in SQL, Doctrine also allows you to pass them separately and insert a placeholder instead: function findUser(Doctrine\DBAL\Connection $con, $email) {
// Unsafe
$con->executeQuery("SELECT * FROM users WHERE email = '".$email."'");
// Safe
$con->executeQuery(
"SELECT * FROM users WHERE email = :email",
array('email' => $email)
);
}
Loading history...
|
|||
150 | |||
151 | $this->queries[$key][self::DATA_INDEX_EXPLAIN] = $stmt->fetchAll(); |
||
152 | |||
153 | $this->explainRunning = FALSE; |
||
154 | } |
||
155 | } |
||
156 | |||
157 | |||
158 | /** |
||
159 | * {@inheritdoc} |
||
160 | */ |
||
161 | public function getTab() |
||
162 | { |
||
163 | return '<span title="Doctrine 2">' |
||
164 | // @codingStandardsIgnoreStart |
||
165 | . '<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAJcEhZcwAALiMAAC4jAXilP3YAAAAHdElNRQffCxUPDinoxmA5AAABO1BMVEUAAADjcTnqakDwgzrxgz7ziDv1gz72iUD5hz35iD75iT7/gAD/gED/gEf/gID/iz25VS33hDv3iUjngzf/hT7xgTz3hj31gTz8iTv4hj7tfTr5hj77ij78iT73hj36iD37hkD8iT35hz77hz77iT73hj36hz76hz/6hz76iD79iD/2hTz7iD75hz37iD76iD77iT75hz77iT75iD78ij/6iT/6iT74hz73hj37hz33hj30hD31gDb1gjj1gjn1hDr1hDv1hTz1hT31iEP1iUP2hT32hT72hj32i0b2lVX3hTr3hj33hz73k1L3mFz3mV33ml73m1/3nGP4hjz4hz74n2X4oGf4qXf4sYL5hz76iD781bz83cr849H96dz969/+7uT+8+v+8+3+9O7+9/H++PT//Pv//v7///+xeeLXAAAAO3RSTlMAAAAAAAAAAAAAAAAAAAAAAgUGBw0QEBERFBUZHR4fKSotP0RKW15lc32GlaWsydvo6err6+z2+vv8/YuXaogAAAABYktHRGjLbPQiAAAAuklEQVQYGQXBhyIVABQA0FNkr6zsGTKzZT83e5Vsks39/y/oHFD7ZbC+HADMLU82qgRQaSpivsUHAGVtCxE37aoBRRrGI+K6B1BhZGntVyHisB8wtBOX+bq7EWfdxfjUfBPx8yHf9wrxHfTF6vHvk5fMP4UfdTAafzMzM3NzqwsG4ur56fEt837loAk6D6Kwfp75by1moMpExEXmXcTOMPB5No7yNmJ7TA3g2+Lp/vX0VyWAUjp6W/mI/zsxJP3EcQMdAAAAAElFTkSuQmCC" />' |
||
166 | // @codingStandardsIgnoreEnd |
||
167 | . count($this->queries) . ' queries' |
||
168 | . ($this->totalTime ? ' / ' . sprintf('%0.1f', $this->totalTime * 1000) . 'ms' : '') |
||
169 | . '</span>'; |
||
170 | } |
||
171 | |||
172 | |||
173 | /** |
||
174 | * {@inheritdoc} |
||
175 | */ |
||
176 | public function getPanel() |
||
177 | { |
||
178 | $s = ''; |
||
179 | |||
180 | if ($this->groupQueries) { |
||
181 | $this->queries = $this->groupQueries($this->queries); |
||
182 | } |
||
183 | |||
184 | if ($this->sortQueries) { |
||
185 | $this->sortQueries($this->queries, self::DATA_INDEX_TIME); |
||
186 | } |
||
187 | |||
188 | foreach ($this->queries as $query) { |
||
189 | $s .= $this->processQuery($query); |
||
190 | } |
||
191 | |||
192 | return empty($this->queries) ? '' : |
||
193 | $this->renderStyles() . |
||
194 | '<h1>Queries: ' . count($this->queries) . |
||
195 | ($this->totalTime ? ', time: ' . sprintf('%0.3f', $this->totalTime * 1000) . ' ms' : '') . |
||
196 | '</h1> |
||
197 | <div class="tracy-inner nette-Doctrine2Panel"> |
||
198 | ' . $this->renderPanelCacheStatistics() . ' |
||
199 | <h2>Queries</h2> |
||
200 | <table> |
||
201 | <tr><th>Time ms</th><th>SQL</th><th>Params</th><th>Trace</th></tr>' . $s . |
||
202 | '</table> |
||
203 | </div>'; |
||
204 | } |
||
205 | |||
206 | |||
207 | /** |
||
208 | * Binds panel to debug bar. |
||
209 | */ |
||
210 | public function bindToBar() |
||
211 | { |
||
212 | if ( ! $this->isTracyEnabled()) { |
||
213 | return; |
||
214 | } |
||
215 | $this->entityManager->getConfiguration()->setSQLLogger($this); |
||
216 | $this->connection = $this->entityManager->getConnection(); |
||
217 | Debugger::getBar()->addPanel($this); |
||
218 | } |
||
219 | |||
220 | |||
221 | /** |
||
222 | * @return bool |
||
223 | */ |
||
224 | private function isTracyEnabled() |
||
225 | { |
||
226 | return ( ! defined('IS_CLI') || ! IS_CLI) && Debugger::isEnabled() && ! Debugger::$productionMode; |
||
227 | } |
||
228 | |||
229 | |||
230 | /** |
||
231 | * @param bool $sortQueries |
||
232 | */ |
||
233 | public function setSortQueries($sortQueries) |
||
234 | { |
||
235 | $this->sortQueries = $sortQueries; |
||
236 | } |
||
237 | |||
238 | |||
239 | /** |
||
240 | * @param bool $groupQueries |
||
241 | */ |
||
242 | public function setGroupQueries($groupQueries) |
||
243 | { |
||
244 | $this->groupQueries = $groupQueries; |
||
245 | } |
||
246 | |||
247 | |||
248 | /** |
||
249 | * @param array |
||
250 | * @return string |
||
251 | */ |
||
252 | protected function processQuery(array $query) |
||
253 | { |
||
254 | $s = '<tr>'; |
||
255 | $s .= '<td>' . sprintf('%0.3f', $query[self::DATA_INDEX_TIME] * 1000); |
||
256 | |||
257 | if ($this->doExplains && isset($query[self::DATA_INDEX_EXPLAIN])) { |
||
258 | static $counter; |
||
259 | $counter++; |
||
260 | $s .= "<br /><a href='#' class='nette-toggler' rel='#nette-Doctrine2Panel-row-$counter'>" . |
||
261 | "explain ►</a>"; |
||
262 | } |
||
263 | |||
264 | if (isset($query[self::DATA_INDEX_COUNT])) { |
||
265 | $s .= '/' . sprintf('%d', $query[self::DATA_INDEX_COUNT]); |
||
266 | } |
||
267 | $s .= '</td>'; |
||
268 | |||
269 | $s .= '<td class="nette-Doctrine2Panel-sql" style="min-width: 400px">' . |
||
270 | Helpers::dumpSql($query[self::DATA_INDEX_SQL]); |
||
271 | |||
272 | if ($this->doExplains && isset($query[self::DATA_INDEX_EXPLAIN])) { |
||
273 | $s .= "<table id='nette-Doctrine2Panel-row-$counter' class='nette-collapsed'><tr>"; |
||
0 ignored issues
–
show
The variable
$counter does not seem to be defined for all execution paths leading up to this point.
If you define a variable conditionally, it can happen that it is not defined for all execution paths. Let’s take a look at an example: function myFunction($a) {
switch ($a) {
case 'foo':
$x = 1;
break;
case 'bar':
$x = 2;
break;
}
// $x is potentially undefined here.
echo $x;
}
In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined. Available Fixes
Loading history...
|
|||
274 | foreach ($query[self::DATA_INDEX_EXPLAIN][0] as $col => $foo) { |
||
275 | $s .= '<th>' . htmlSpecialChars($col) . '</th>'; |
||
276 | } |
||
277 | $s .= '</tr>'; |
||
278 | foreach ($query[self::DATA_INDEX_EXPLAIN] as $row) { |
||
0 ignored issues
–
show
The expression
$query[self::DATA_INDEX_EXPLAIN] of type integer|double is not guaranteed to be traversable. How about adding an additional type check?
There are different options of fixing this problem.
Loading history...
|
|||
279 | $s .= '<tr>'; |
||
280 | foreach ($row as $col) { |
||
281 | $s .= '<td>' . htmlSpecialChars($col) . '</td>'; |
||
282 | } |
||
283 | $s .= '</tr>'; |
||
284 | } |
||
285 | $s .= '</table>'; |
||
286 | } |
||
287 | $s .= '</td>'; |
||
288 | $s .= '<td>' . Dumper::toHtml($query[self::DATA_INDEX_PARAMS]) . '</td>'; |
||
289 | $s .= '<td>' . Dumper::toHtml($query[self::DATA_INDEX_TRACE]) . '</td>'; |
||
290 | $s .= '</tr>'; |
||
291 | |||
292 | return $s; |
||
293 | } |
||
294 | |||
295 | |||
296 | protected function renderStyles() |
||
297 | { |
||
298 | return '<style> |
||
299 | #tracy-debug td.nette-Doctrine2Panel-sql { background: white !important } |
||
300 | #tracy-debug .nette-Doctrine2Panel-source { color: #BBB !important } |
||
301 | #tracy-debug div.tracy-inner.nette-Doctrine2Panel { max-width: 1000px } |
||
302 | #tracy-debug .nette-Doctrine2Panel tr table { margin: 8px 0; max-height: 150px; overflow:auto } |
||
303 | #tracy-debug .nette-Doctrine2Panel-cache-green { color: green !important; font-weight: bold } |
||
304 | #tracy-debug .nette-Doctrine2Panel-cache-red { color: red !important; font-weight: bold } |
||
305 | #tracy-debug .nette-Doctrine2Panel h2 { font-size: 23px; } |
||
306 | </style>'; |
||
307 | } |
||
308 | |||
309 | |||
310 | private function renderPanelCacheStatistics() |
||
311 | { |
||
312 | if (empty($this->entityManager)) { |
||
313 | return ''; |
||
314 | } |
||
315 | |||
316 | $config = $this->entityManager->getConfiguration(); |
||
317 | if ( ! $config->isSecondLevelCacheEnabled()) { |
||
318 | return ''; |
||
319 | } |
||
320 | |||
321 | $cacheLogger = $config->getSecondLevelCacheConfiguration()->getCacheLogger(); |
||
322 | |||
323 | if ( ! $cacheLogger instanceof CacheLoggerChain) { |
||
324 | return ''; |
||
325 | } |
||
326 | |||
327 | /** @var StatisticsCacheLogger $statistics */ |
||
328 | $statistics = $cacheLogger->getLogger('statistics'); |
||
329 | if ( ! ($statistics)) { |
||
330 | return ''; |
||
331 | } |
||
332 | |||
333 | $cacheDriver = $this->entityManager->getConfiguration()->getMetadataCacheImpl(); |
||
334 | $driverInformation = get_class($cacheDriver); |
||
335 | if ($cacheDriver instanceof RedisCache) { |
||
0 ignored issues
–
show
The class
Lekarna\Doctrine\Cache\RedisCache does not exist. Did you forget a USE statement, or did you not list all dependencies?
This error could be the result of: 1. Missing dependenciesPHP Analyzer uses your Are you sure this class is defined by one of your dependencies, or did you maybe
not list a dependency in either the 2. Missing use statementPHP does not complain about undefined classes in if ($x instanceof DoesNotExist) {
// Do something.
}
If you have not tested against this specific condition, such errors might go unnoticed.
Loading history...
|
|||
336 | $driverInformation .= ', database: ' . $cacheDriver->getCurrentDatabase(); |
||
337 | } |
||
338 | |||
339 | return '<h2>Second Level Cache</h2> |
||
340 | <table> |
||
341 | <tr> |
||
342 | <td>Driver</td> |
||
343 | <td><strong>' . $driverInformation . '</strong></td> |
||
344 | </tr> |
||
345 | <tr> |
||
346 | <td>Cache hits</td> |
||
347 | <td> |
||
348 | <strong class="nette-Doctrine2Panel-cache-green">' . $statistics->getHitCount() . '</strong> |
||
349 | </td> |
||
350 | </tr> |
||
351 | <tr> |
||
352 | <td>Cache misses</td> |
||
353 | <td> |
||
354 | <strong class="nette-Doctrine2Panel-cache-red">' . $statistics->getMissCount() . '</strong> |
||
355 | </td> |
||
356 | </tr> |
||
357 | <tr> |
||
358 | <td>Cache puts</td> |
||
359 | <td> |
||
360 | <strong class="nette-Doctrine2Panel-cache-red">' . $statistics->getPutCount() . '</strong> |
||
361 | </td> |
||
362 | </tr> |
||
363 | </table>'; |
||
364 | } |
||
365 | |||
366 | |||
367 | /** |
||
368 | * @return array |
||
369 | */ |
||
370 | protected function groupQueries(array $queries) |
||
371 | { |
||
372 | $indexed = []; |
||
373 | foreach ($queries as $query) { |
||
374 | $indexed[$query[self::DATA_INDEX_SQL]][] = $query; |
||
375 | } |
||
376 | $grouped = []; |
||
377 | foreach ($indexed as $item) { |
||
378 | if (count($item) === 1) { |
||
379 | $grouped[] = $item[0]; |
||
380 | continue; |
||
381 | } |
||
382 | $groupedItem = $item[0]; |
||
383 | $times = $params = $traces = []; |
||
384 | foreach ($item as $subItem) { |
||
385 | $times[] = $subItem[self::DATA_INDEX_TIME]; |
||
386 | $params[] = $subItem[self::DATA_INDEX_PARAMS]; |
||
387 | $traces[] = $subItem[self::DATA_INDEX_TRACE]; |
||
388 | } |
||
389 | $groupedItem[self::DATA_INDEX_TIME] = array_sum($times); |
||
390 | $groupedItem[self::DATA_INDEX_PARAMS] = $params; |
||
391 | $groupedItem[self::DATA_INDEX_TRACE] = $traces; |
||
392 | $groupedItem[self::DATA_INDEX_COUNT] = count($times); |
||
393 | $grouped[] = $groupedItem; |
||
394 | } |
||
395 | return $grouped; |
||
396 | } |
||
397 | |||
398 | |||
399 | /** |
||
400 | * @param array $queries |
||
401 | * @param string $key |
||
402 | */ |
||
403 | protected function sortQueries(array &$queries, $key) |
||
404 | { |
||
405 | uasort( |
||
406 | $queries, |
||
407 | function ($a, $b) use ($key) { |
||
408 | if ($a[$key] === $b[$key]) { |
||
409 | return 0; |
||
410 | } |
||
411 | return ($a[$key] > $b[$key]) ? -1 : 1; |
||
412 | } |
||
413 | ); |
||
414 | } |
||
415 | |||
416 | } |
||
417 |
The
EntityManager
might become unusable for example if a transaction is rolled back and it gets closed. Let’s assume that somewhere in your application, or in a third-party library, there is code such as the following:If that code throws an exception and the
EntityManager
is closed. Any other code which depends on the same instance of theEntityManager
during this request will fail.On the other hand, if you instead inject the
ManagerRegistry
, thegetManager()
method guarantees that you will always get a usable manager instance.