[BUGFIX] EXT:dbal: Shortcuts now working correct
[Packages/TYPO3.CMS.git] / typo3 / sysext / dbal / Classes / Controller / ModuleController.php
1 <?php
2 namespace TYPO3\CMS\Dbal\Controller;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use Psr\Http\Message\ResponseInterface;
18 use Psr\Http\Message\ServerRequestInterface;
19 use TYPO3\CMS\Backend\Module\BaseScriptClass;
20 use TYPO3\CMS\Backend\Template\ModuleTemplate;
21 use TYPO3\CMS\Backend\Utility\BackendUtility;
22 use TYPO3\CMS\Core\Database\DatabaseConnection;
23 use TYPO3\CMS\Core\Utility\GeneralUtility;
24
25 /**
26 * Script class; Backend module for DBAL extension
27 */
28 class ModuleController extends BaseScriptClass
29 {
30 /**
31 * @var string
32 */
33 protected $thisScript;
34
35 /**
36 * The name of the module
37 *
38 * @var string
39 */
40 protected $moduleName = 'tools_txdbalM1';
41
42 /**
43 * ModuleTemplateContainer
44 *
45 * @var ModuleTemplate
46 */
47 protected $moduleTemplate;
48
49 /**
50 * Initializes this module.
51 *
52 * @return void
53 */
54 public function init()
55 {
56 $this->MCONF = array(
57 'name' => $this->moduleName,
58 );
59 $this->getLanguageService()->includeLLFile('EXT:dbal/Resources/Private/Language/locallang.xlf');
60 parent::init();
61 $this->moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
62 }
63
64 /**
65 * Adds items to the ->MOD_MENU array. Used for the function menu selector.
66 *
67 * @return void
68 */
69 public function menuConfig()
70 {
71 $languageService = $this->getLanguageService();
72 $this->MOD_MENU = array(
73 'function' => array(
74 0 => $languageService->getLL('Debug_log'),
75 'info' => $languageService->getLL('Cached_info'),
76 'sqlcheck' => $languageService->getLL('SQL_check')
77 )
78 );
79 parent::menuConfig();
80 }
81
82 /**
83 * Main function of the module. Write the content to $this->content
84 *
85 * @return void
86 */
87 public function main()
88 {
89 $languageService = $this->getLanguageService();
90 $this->thisScript = BackendUtility::getModuleUrl($this->MCONF['name']);
91 // Clean up settings:
92 $this->MOD_SETTINGS = BackendUtility::getModuleData(
93 $this->MOD_MENU,
94 GeneralUtility::_GP('SET'),
95 $this->MCONF['name']
96 );
97 // Draw the header
98 // DBAL page title:
99 $this->content .= '<h1>' . $languageService->getLL('title') . '</h1>';
100 $this->generateMenu();
101 // Debug log:
102 switch ($this->MOD_SETTINGS['function']) {
103 case 'info':
104 $this->content .= $this->moduleTemplate->section(
105 $languageService->getLL('Cached_info'),
106 $this->printCachedInfo()
107 );
108 break;
109 case 'sqlcheck':
110 $this->content .= $this->moduleTemplate->section(
111 $languageService->getLL('SQL_check'),
112 $this->printSqlCheck()
113 );
114 break;
115 case 0:
116 $this->content .= $this->moduleTemplate->section(
117 $languageService->getLL('Debug_log'),
118 $this->printLogMgm()
119 );
120 break;
121 }
122 // ShortCut
123 $shortcutButton = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar()->makeShortcutButton()
124 ->setModuleName($this->MCONF['name'])
125 ->setSetVariables(['function']);
126 $this->moduleTemplate->getDocHeaderComponent()->getButtonBar()->addButton($shortcutButton);
127 }
128
129 /**
130 * Injects the request object for the current request or subrequest
131 * As this controller goes only through the main() method, it is rather simple for now
132 *
133 * @param ServerRequestInterface $request the current request
134 * @param ResponseInterface $response
135 * @return ResponseInterface the response with the content
136 */
137 public function mainAction(ServerRequestInterface $request, ResponseInterface $response)
138 {
139 $GLOBALS['SOBE'] = $this;
140 $this->init();
141 $this->main();
142 $this->moduleTemplate->setContent($this->content);
143 $response->getBody()->write($this->moduleTemplate->renderContent());
144 return $response;
145 }
146
147 /**
148 * Displays a form to check DBAL SQL methods and parse raw SQL.
149 *
150 * @return string HTML output
151 */
152 protected function printSqlCheck()
153 {
154 $input = GeneralUtility::_GP('tx_dbal');
155 $out = '
156 <form name="sql_check" action="' . $this->thisScript . '" method="post" enctype="multipart/form-data">
157 <script type="text/javascript">
158 /*<![CDATA[*/
159 function updateQryForm(s) {
160 document.getElementById(\'tx-dbal-result\').style.display = \'none\';
161 switch(s) {
162 case \'SELECT\':
163 document.getElementById(\'tx-dbal-qryupdate\').style.display = \'none\';
164 document.getElementById(\'tx-dbal-qryfields\').style.display = \'table-row\';
165 document.getElementById(\'tx-dbal-qryinsertvalues\').style.display = \'none\';
166 document.getElementById(\'tx-dbal-qryupdatevalues\').style.display = \'none\';
167 document.getElementById(\'tx-dbal-qryfrom\').style.display = \'table-row\';
168 document.getElementById(\'tx-dbal-qryinto\').style.display = \'none\';
169 document.getElementById(\'tx-dbal-qrywhere\').style.display = \'table-row\';
170 document.getElementById(\'tx-dbal-qrygroup\').style.display = \'table-row\';
171 document.getElementById(\'tx-dbal-qryorder\').style.display = \'table-row\';
172 document.getElementById(\'tx-dbal-qrylimit\').style.display = \'table-row\';
173 break;
174 case \'INSERT\':
175 document.getElementById(\'tx-dbal-qryupdate\').style.display = \'none\';
176 document.getElementById(\'tx-dbal-qryfields\').style.display = \'none\';
177 document.getElementById(\'tx-dbal-qryinsertvalues\').style.display = \'table-row\';
178 document.getElementById(\'tx-dbal-qryupdatevalues\').style.display = \'none\';
179 document.getElementById(\'tx-dbal-qryfrom\').style.display = \'none\';
180 document.getElementById(\'tx-dbal-qryinto\').style.display = \'table-row\';
181 document.getElementById(\'tx-dbal-qrywhere\').style.display = \'table-row\';
182 document.getElementById(\'tx-dbal-qrygroup\').style.display = \'table-row\';
183 document.getElementById(\'tx-dbal-qryorder\').style.display = \'table-row\';
184 document.getElementById(\'tx-dbal-qrylimit\').style.display = \'table-row\';
185 break;
186 case \'UPDATE\':
187 document.getElementById(\'tx-dbal-qryupdate\').style.display = \'table-row\';
188 document.getElementById(\'tx-dbal-qryfields\').style.display = \'none\';
189 document.getElementById(\'tx-dbal-qryinsertvalues\').style.display = \'none\';
190 document.getElementById(\'tx-dbal-qryupdatevalues\').style.display = \'table-row\';
191 document.getElementById(\'tx-dbal-qryfrom\').style.display = \'none\';
192 document.getElementById(\'tx-dbal-qryinto\').style.display = \'none\';
193 document.getElementById(\'tx-dbal-qryupdate\').style.display = \'table-row\';
194 document.getElementById(\'tx-dbal-qrywhere\').style.display = \'table-row\';
195 document.getElementById(\'tx-dbal-qrygroup\').style.display = \'none\';
196 document.getElementById(\'tx-dbal-qryorder\').style.display = \'none\';
197 document.getElementById(\'tx-dbal-qrylimit\').style.display = \'none\';
198 break;
199 case \'DELETE\':
200 document.getElementById(\'tx-dbal-qryupdate\').style.display = \'none\';
201 document.getElementById(\'tx-dbal-qryfields\').style.display = \'none\';
202 document.getElementById(\'tx-dbal-qryinsertvalues\').style.display = \'none\';
203 document.getElementById(\'tx-dbal-qryupdatevalues\').style.display = \'none\';
204 document.getElementById(\'tx-dbal-qryfrom\').style.display = \'table-row\';
205 document.getElementById(\'tx-dbal-qryinto\').style.display = \'none\';
206 document.getElementById(\'tx-dbal-qrywhere\').style.display = \'table-row\';
207 document.getElementById(\'tx-dbal-qrygroup\').style.display = \'none\';
208 document.getElementById(\'tx-dbal-qryorder\').style.display = \'none\';
209 document.getElementById(\'tx-dbal-qrylimit\').style.display = \'none\';
210 break;
211 }
212 }
213 /*]]>*/
214 </script>
215 <table>
216 <tr class="tableheader bgColor5"><th colspan="2">Easy SQL check</th></tr>
217 <tr><td colspan="2">
218 <select name="tx_dbal[QUERY]"size="1" onchange="updateQryForm(this.options[this.selectedIndex].value)">
219 <option value="SELECT" ' . ($input['QUERY'] === 'SELECT' ? 'selected="selected"' : '') . '>SELECT</option>
220 <option value="INSERT" ' . ($input['QUERY'] === 'INSERT' ? 'selected="selected"' : '') . '>INSERT</option>
221 <option value="UPDATE" ' . ($input['QUERY'] === 'UPDATE' ? 'selected="selected"' : '') . '>UPDATE</option>
222 <option value="DELETE" ' . ($input['QUERY'] === 'DELETE' ? 'selected="selected"' : '') . '>DELETE</option>
223 </select>
224 </td></tr>
225 <tr id="tx-dbal-qryupdate" style="display:none;"><td></td><td><input name="tx_dbal[UPDATE]" value="' . $input['UPDATE'] . '" type="text" size="30" maxsize="100" /></td></tr>
226 <tr id="tx-dbal-qryfields"><td></td><td><input name="tx_dbal[FIELDS]" value="' . $input['FIELDS'] . '" type="text" size="30" maxsize="100" /></td></tr>
227 <tr id="tx-dbal-qryinsertvalues" style="display:none;"><td></td><td><textarea name="tx_dbal[INSERTVALUES]" cols="30" rows="4">' . $input['INSERTVALUES'] . '</textarea></td></tr>
228 <tr id="tx-dbal-qryupdatevalues" style="display:none;"><th>SET</th><td><textarea name="tx_dbal[UPDATEVALUES]" cols="30" rows="4">' . $input['UPDATEVALUES'] . '</textarea></td></tr>
229 <tr id="tx-dbal-qryfrom"><th>FROM</th><td><input name="tx_dbal[FROM]" value="' . $input['FROM'] . '" type="text" size="30" maxsize="100" /></td></tr>
230 <tr id="tx-dbal-qryinto" style="display:none;"><th>INTO</th><td><input name="tx_dbal[INTO]" value="' . $input['INTO'] . '" type="text" size="30" maxsize="100" /></td></tr>
231 <tr id="tx-dbal-qrywhere"><th>WHERE</th><td><input name="tx_dbal[WHERE]" value="' . $input['WHERE'] . '" type="text" size="30" maxsize="100" /></td></tr>
232 <tr id="tx-dbal-qrygroup"><th>GROUP BY</th><td><input name="tx_dbal[GROUP]" value="' . $input['GROUP'] . '" type="text" size="30" maxsize="100" /></td></tr>
233 <tr id="tx-dbal-qryorder"><th>ORDER BY</th><td><input name="tx_dbal[ORDER]" value="' . $input['ORDER'] . '" type="text" size="30" maxsize="100" /></td></tr>
234 <tr id="tx-dbal-qrylimit"><th>LIMIT</th><td><input name="tx_dbal[LIMIT]" value="' . $input['LIMIT'] . '" type="text" size="30" maxsize="100" /></td></tr>
235 <tr>
236 <td></td>
237 <td style="text-align:right;">
238 <input class="btn btn-default" type="submit" value="CHECK" />
239 </td>
240 </tr>
241 <script type="text/javascript">
242 /*<![CDATA[*/
243 updateQryForm(\'' . $input['QUERY'] . '\');
244 /*]]>*/
245 </script>
246 ';
247 $out .= '<tr id="tx-dbal-result" class="bgColor4"><th>Result:</th><td>';
248 switch ($input['QUERY']) {
249 case 'SELECT':
250 $qry = $this->getDatabaseConnection()->SELECTquery($input['FIELDS'], $input['FROM'], $input['WHERE'], $input['GROUP'], $input['ORDER'], $input['LIMIT']);
251 break;
252 case 'INSERT':
253 $qry = $this->getDatabaseConnection()->INSERTquery($input['INTO'], $this->createFieldsValuesArray($input['INSERTVALUES']));
254 break;
255 case 'UPDATE':
256 $qry = $GLOBALS['TYPO3_DB']->UPDATEquery($input['UPDATE'], $input['WHERE'], $this->createFieldsValuesArray($input['UPDATEVALUES']));
257 break;
258 case 'DELETE':
259 $qry = $GLOBALS['TYPO3_DB']->DELETEquery($input['FROM'], $input['WHERE']);
260 break;
261 }
262 $out .= '<pre>' . htmlspecialchars($qry) . '</pre></td></tr>';
263 $out .= '
264 <tr class="tableheader bgColor5">
265 <th colspan="2">RAW SQL check</th>
266 </tr>
267 <tr>
268 <td colspan="2" style="text-align:right;">
269 <textarea name="tx_dbal[RAWSQL]" cols="60" rows="5">' . $input['RAWSQL'] . '</textarea>
270 <br />
271 <input class="btn btn-default" type="submit" value="CHECK" />
272 </td>
273 </tr>';
274 if (!empty($input['RAWSQL'])) {
275 $out .= '<tr class="bgColor4">';
276 $parseResult = $GLOBALS['TYPO3_DB']->SQLparser->parseSQL($input['RAWSQL']);
277 if (is_array($parseResult)) {
278 $newQuery = $GLOBALS['TYPO3_DB']->SQLparser->compileSQL($parseResult);
279 $testResult = $GLOBALS['TYPO3_DB']->SQLparser->debug_parseSQLpartCompare($input['RAWSQL'], $newQuery);
280 if (!is_array($testResult)) {
281 $out .= '<td colspan="2">' . $newQuery;
282 } else {
283 $out .= '<td colspan="2">' . htmlspecialchars($testResult[0]) . '</td></tr>
284 <tr><th>Error:</th><td style="border:2px solid #f00;">Input query did not match the parsed and recompiled query exactly (not observing whitespace):<br />' . htmlspecialchars($testResult[1]);
285 }
286 } else {
287 $out .= '<th>Result:</th><td style="border:2px solid #f00;">' . $parseResult;
288 }
289 $out .= '</td></tr>';
290 }
291 $out .= '</table></form>';
292 return $out;
293 }
294
295 /**
296 * Parses a very simple text format into an array.
297 *
298 * Each line is seen as a key/value pair that is exploded at =. This is used
299 * in the simple SQL check to input values for INSERT and UPDATE statements.
300 *
301 * @param string $in String to parse into key/value array.
302 * @return array Array created from the input string.
303 */
304 protected function createFieldsValuesArray($in)
305 {
306 $ret = array();
307 $in = explode(LF, $in);
308 foreach ($in as $v) {
309 $fv = explode('=', $v);
310 $ret[$fv[0]] = $fv[1];
311 }
312 return $ret;
313 }
314
315 /**
316 * Prints out the cached information about the database.
317 *
318 * The DBAL caches a lot of information, e.g. about auto increment fields,
319 * field types and primary keys. This method formats all this into a HTML
320 * table to display in the BE.
321 *
322 * @return string
323 */
324 protected function printCachedInfo()
325 {
326 // Get cmd:
327 if ((string)GeneralUtility::_GP('cmd') === 'clear') {
328 $this->getDatabaseConnection()->clearCachedFieldInfo();
329 $GLOBALS['TYPO3_DB']->cacheFieldInfo();
330 }
331 $out = '<a name="autoincrement"></a><h2>auto_increment</h2>';
332 $out .= '<table border="1" cellspacing="0"><tbody><tr><th>Table</th><th>Field</th></tr>';
333 ksort($GLOBALS['TYPO3_DB']->cache_autoIncFields);
334 foreach ($GLOBALS['TYPO3_DB']->cache_autoIncFields as $table => $field) {
335 $out .= '<tr>';
336 $out .= '<td>' . $table . '</td>';
337 $out .= '<td>' . $field . '</td>';
338 $out .= '</tr>';
339 }
340 $out .= '</tbody></table>';
341 $out .= '<a name="primarykeys"></a><h2>Primary keys</h2>';
342 $out .= '<table border="1" cellspacing="0"><tbody><tr><th>Table</th><th>Field(s)</th></tr>';
343 ksort($GLOBALS['TYPO3_DB']->cache_primaryKeys);
344 foreach ($GLOBALS['TYPO3_DB']->cache_primaryKeys as $table => $field) {
345 $out .= '<tr>';
346 $out .= '<td>' . $table . '</td>';
347 $out .= '<td>' . $field . '</td>';
348 $out .= '</tr>';
349 }
350 $out .= '</tbody></table>';
351 $out .= '<a name="fieldtypes"></a><h2>Field types</h2>';
352 $out .= '
353 <table border="1" cellspacing="0">
354 <tbody>
355 <tr>
356 <th colspan="5">Table</th>
357 </tr>
358 <tr>
359 <th>Field</th>
360 <th>Type</th><th>
361 <a href="#metatypes">Metatype</a></th>
362 <th>NOT NULL</th>
363 <th>Default</th></th>
364 </tr>';
365 ksort($GLOBALS['TYPO3_DB']->cache_fieldType);
366 foreach ($GLOBALS['TYPO3_DB']->cache_fieldType as $table => $fields) {
367 $out .= '<th colspan="5">' . $table . '</th>';
368 foreach ($fields as $field => $data) {
369 $out .= '<tr>';
370 $out .= '<td>' . $field . '</td>';
371 $out .= '<td>' . $data['type'] . '</td>';
372 $out .= '<td>' . $data['metaType'] . '</td>';
373 $out .= '<td>' . ($data['notnull'] ? 'NOT NULL' : '') . '</td>';
374 $out .= '<td>' . $data['default'] . '</td>';
375 $out .= '</tr>';
376 }
377 }
378 $out .= '</tbody></table>';
379 $out .= '<a name="metatypes"></a><h2>Metatype explanation</h2>';
380 $out .= '<pre>
381 C: Varchar, capped to 255 characters.
382 X: Larger varchar, capped to 4000 characters (to be compatible with Oracle).
383 XL: For Oracle, returns CLOB, otherwise the largest varchar size.
384
385 C2: Multibyte varchar
386 X2: Multibyte varchar (largest size)
387
388 B: BLOB (binary large object)
389
390 D: Date (some databases do not support this, and we return a datetime type)
391 T: Datetime or Timestamp
392 L: Integer field suitable for storing booleans (0 or 1)
393 I: Integer (mapped to I4)
394 I1: 1-byte integer
395 I2: 2-byte integer
396 I4: 4-byte integer
397 I8: 8-byte integer
398 F: Floating point number
399 N: Numeric or decimal number</pre>';
400 $menu = '<a href="' . $this->thisScript . '&amp;cmd=clear">CLEAR DATA</a><hr />';
401 $menu .= '<a href="#autoincrement">auto_increment</a> | <a href="#primarykeys">Primary keys</a> | <a href="#fieldtypes">Field types</a> | <a href="#metatypes">Metatype explanation</a><hr />';
402 return $menu . $out;
403 }
404
405 /**
406 * Printing the debug-log from the DBAL extension
407 *
408 * To enabled debugging, you will have to enabled it in the configuration!
409 *
410 * @return string HTML content
411 */
412 protected function printLogMgm()
413 {
414 // Disable debugging in any case...
415 $GLOBALS['TYPO3_DB']->debug = false;
416 // Get cmd:
417 $cmd = (string)GeneralUtility::_GP('cmd');
418 switch ($cmd) {
419 case 'flush':
420 $res = $GLOBALS['TYPO3_DB']->exec_TRUNCATEquery('tx_dbal_debuglog');
421 $res = $GLOBALS['TYPO3_DB']->exec_TRUNCATEquery('tx_dbal_debuglog_where');
422 $outStr = 'Log FLUSHED!';
423 break;
424 case 'joins':
425 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('table_join,exec_time,query,script', 'tx_dbal_debuglog', 'table_join!=\'\'', 'table_join,script,exec_time,query');
426 // Init vars in which to pick up the query result:
427 $tableIndex = array();
428 $tRows = array();
429 $tRows[] = '
430 <tr>
431 <td>Execution time</td>
432 <td>Table joins</td>
433 <td>Script</td>
434 <td>Query</td>
435 </tr>';
436 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
437 $tableArray = $GLOBALS['TYPO3_DB']->SQLparser->parseFromTables($row['table_join']);
438 // Create table name index:
439 foreach ($tableArray as $a) {
440 foreach ($tableArray as $b) {
441 if ($b['table'] != $a['table']) {
442 $tableIndex[$a['table']][$b['table']] = 1;
443 }
444 }
445 }
446 // Create output row
447 $tRows[] = '
448 <tr>
449 <td>' . htmlspecialchars($row['exec_time']) . '</td>
450 <td>' . htmlspecialchars($row['table_join']) . '</td>
451 <td>' . htmlspecialchars($row['script']) . '</td>
452 <td>' . htmlspecialchars($row['query']) . '</td>
453 </tr>';
454 }
455 // Printing direct joins:
456 $outStr .= '<h4>Direct joins:</h4>' . \TYPO3\CMS\Core\Utility\DebugUtility::viewArray($tableIndex);
457 // Printing total dependencies:
458 foreach ($tableIndex as $priTable => $a) {
459 foreach ($tableIndex as $tableN => $v) {
460 foreach ($v as $tableP => $vv) {
461 if ($tableP == $priTable) {
462 $tableIndex[$priTable] = array_merge($v, $a);
463 }
464 }
465 }
466 }
467 $outStr .= '<h4>Total dependencies:</h4>' . \TYPO3\CMS\Core\Utility\DebugUtility::viewArray($tableIndex);
468 // Printing data rows:
469 $outStr .= '
470 <table border="1" cellspacing="0">' . implode('', $tRows) . '
471 </table>';
472 break;
473 case 'errors':
474 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('serdata,exec_time,query,script', 'tx_dbal_debuglog', 'errorFlag>0', '', 'tstamp DESC');
475 // Init vars in which to pick up the query result:
476 $tRows = array();
477 $tRows[] = '
478 <tr>
479 <td>Execution time</td>
480 <td>Error data</td>
481 <td>Script</td>
482 <td>Query</td>
483 </tr>';
484 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
485 // Create output row
486 $tRows[] = '
487 <tr>
488 <td>' . htmlspecialchars($row['exec_time']) . '</td>
489 <td>' . \TYPO3\CMS\Core\Utility\DebugUtility::viewArray(unserialize($row['serdata'])) . '</td>
490 <td>' . htmlspecialchars($row['script']) . '</td>
491 <td>' . htmlspecialchars($row['query']) . '</td>
492 </tr>';
493 }
494 // Printing data rows:
495 $outStr .= '
496 <table border="1" cellspacing="0">' . implode('', $tRows) . '
497 </table>';
498 break;
499 case 'parsing':
500 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('query,serdata', 'tx_dbal_debuglog', 'errorFlag&2=2');
501 $tRows = array();
502 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
503 // Create output row
504 $tRows[] = '
505 <tr>
506 <td>' . htmlspecialchars($row['query']) . '</td>
507 </tr>';
508 }
509 // Printing data rows:
510 $outStr .= '
511 <table border="1" cellspacing="0">' . implode('', $tRows) . '
512 </table>';
513 break;
514 case 'where':
515 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('tstamp,script,tablename,whereclause', 'tx_dbal_debuglog_where', '', '', 'tstamp DESC');
516 $tRows = array();
517 $tRows[] = '
518 <tr>
519 <td>Time</td>
520 <td>Script</td>
521 <td>Table</td>
522 <td>WHERE clause</td>
523 </tr>';
524 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
525 $tRows[] = '
526 <tr>
527 <td>' . BackendUtility::datetime($row['tstamp']) . '</td>
528 <td>' . htmlspecialchars($row['script']) . '</td>
529 <td>' . htmlspecialchars($row['tablename']) . '</td>
530 <td>' . str_replace(array('\'\'', '""', 'IS NULL', 'IS NOT NULL'), array('<span style="background-color:#ff0000;color:#ffffff;padding:2px;font-weight:bold;">\'\'</span>', '<span style="background-color:#ff0000;color:#ffffff;padding:2px;font-weight:bold;">""</span>', '<span style="background-color:#00ff00;color:#ffffff;padding:2px;font-weight:bold;">IS NULL</span>', '<span style="background-color:#00ff00;color:#ffffff;padding:2px;font-weight:bold;">IS NOT NULL</span>'), htmlspecialchars($row['whereclause'])) . '</td>
531 </tr>';
532 }
533 $outStr = '
534 <table border="1" cellspacing="0">' . implode('', $tRows) . '
535 </table>';
536 break;
537 default:
538 // Look for request to view specific script exec:
539 $specTime = GeneralUtility::_GP('specTime');
540 if ($specTime) {
541 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('exec_time,errorFlag,table_join,serdata,query', 'tx_dbal_debuglog', 'tstamp=' . (int)$specTime);
542 $tRows = array();
543 $tRows[] = '
544 <tr>
545 <td>Execution time</td>
546 <td>Error</td>
547 <td>Table joins</td>
548 <td>Data</td>
549 <td>Query</td>
550 </tr>';
551 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
552 $tRows[] = '
553 <tr>
554 <td>' . htmlspecialchars($row['exec_time']) . '</td>
555 <td>' . ($row['errorFlag'] ? 1 : 0) . '</td>
556 <td>' . htmlspecialchars($row['table_join']) . '</td>
557 <td>' . \TYPO3\CMS\Core\Utility\DebugUtility::viewArray(unserialize($row['serdata'])) . '</td>
558 <td>' . str_replace(array('\'\'', '""', 'IS NULL', 'IS NOT NULL'), array('<span style="background-color:#ff0000;color:#ffffff;padding:2px;font-weight:bold;">\'\'</span>', '<span style="background-color:#ff0000;color:#ffffff;padding:2px;font-weight:bold;">""</span>', '<span style="background-color:#00ff00;color:#ffffff;padding:2px;font-weight:bold;">IS NULL</span>', '<span style="background-color:#00ff00;color:#ffffff;padding:2px;font-weight:bold;">IS NOT NULL</span>'), htmlspecialchars($row['query'])) . '</td>
559 </tr>';
560 }
561 } else {
562 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('tstamp,script, SUM(exec_time) as calc_sum, count(*) AS qrycount, MAX(errorFlag) as error', 'tx_dbal_debuglog', '', 'tstamp,script', 'tstamp DESC');
563 $tRows = array();
564 $tRows[] = '
565 <tr>
566 <td>Time</td>
567 <td># of queries</td>
568 <td>Error</td>
569 <td>Time (ms)</td>
570 <td>Script</td>
571 </tr>';
572 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
573 $tRows[] = '
574 <tr>
575 <td>' . BackendUtility::datetime($row['tstamp']) . '</td>
576 <td>' . htmlspecialchars($row['qrycount']) . '</td>
577 <td>' . ($row['error'] ? '<strong style="color:#f00">ERR</strong>' : '') . '</td>
578 <td>' . htmlspecialchars($row['calc_sum']) . '</td>
579 <td><a href="' . $this->thisScript . '&amp;specTime=' . (int)$row['tstamp'] . '">' . htmlspecialchars($row['script']) . '</a></td>
580 </tr>';
581 }
582 }
583 $outStr = '
584 <table border="1" cellspacing="0">' . implode('', $tRows) . '
585 </table>';
586 }
587 $menu = '
588 <a href="' . $this->thisScript . '&amp;cmd=flush">FLUSH LOG</a> -
589 <a href="' . $this->thisScript . '&amp;cmd=joins">JOINS</a> -
590 <a href="' . $this->thisScript . '&amp;cmd=errors">ERRORS</a> -
591 <a href="' . $this->thisScript . '&amp;cmd=parsing">PARSING</a> -
592 <a href="' . $this->thisScript . '">LOG</a> -
593 <a href="' . $this->thisScript . '&amp;cmd=where">WHERE</a> -
594
595 <a href="' . htmlspecialchars(GeneralUtility::linkThisScript()) . '" target="tx_debuglog">[New window]</a>
596 <hr />
597 ';
598 return $menu . $outStr;
599 }
600
601 /**
602 * Generate the ModuleMenu
603 */
604 protected function generateMenu()
605 {
606 $menu = $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
607 $menu->setIdentifier('DBALJumpMenu');
608 foreach ($this->MOD_MENU['function'] as $controller => $title) {
609 $item = $menu
610 ->makeMenuItem()
611 ->setHref(
612 BackendUtility::getModuleUrl(
613 $this->moduleName,
614 [
615 'id' => $this->id,
616 'SET' => [
617 'function' => $controller
618 ]
619 ]
620 )
621 )
622 ->setTitle($title);
623 if ($controller === $this->MOD_SETTINGS['function']) {
624 $item->setActive(true);
625 }
626 $menu->addMenuItem($item);
627 }
628 $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->addMenu($menu);
629 }
630
631 /**
632 * Returns the language service.
633 *
634 * @return \TYPO3\CMS\Lang\LanguageService
635 */
636 protected function getLanguageService()
637 {
638 return $GLOBALS['LANG'];
639 }
640
641 /**
642 * Returns the DatabaseConnection
643 *
644 * @return DatabaseConnection
645 */
646 protected function getDatabaseConnection()
647 {
648 return $GLOBALS['TYPO3_DB'];
649 }
650 }