2 namespace TYPO3\CMS\Dbal\Controller
;
5 * This file is part of the TYPO3 CMS project.
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.
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
14 * The TYPO3 project - inspiring people to share!
17 use TYPO3\CMS\Backend\Utility\BackendUtility
;
20 * Script class; Backend module for DBAL extension
22 * @author Kasper Skårhøj <kasper@typo3.com>
23 * @author Karsten Dambekalns <karsten@typo3.org>
25 class ModuleController
extends \TYPO3\CMS\Backend\Module\BaseScriptClass
{
30 protected $thisScript;
33 * The name of the module
37 protected $moduleName = 'tools_txdbalM1';
40 * Initializes this module.
44 public function init() {
46 'name' => $this->moduleName
,
48 $this->getLanguageService()->includeLLFile('EXT:dbal/mod1/locallang.xlf');
53 * Adds items to the ->MOD_MENU array. Used for the function menu selector.
57 public function menuConfig() {
58 $languageService = $this->getLanguageService();
59 $this->MOD_MENU
= array(
61 0 => $languageService->getLL('Debug_log'),
62 'info' => $languageService->getLL('Cached_info'),
63 'sqlcheck' => $languageService->getLL('SQL_check')
70 * Main function of the module. Write the content to $this->content
74 public function main() {
75 $languageService = $this->getLanguageService();
76 $this->thisScript
= BackendUtility
::getModuleUrl($this->MCONF
['name']);
78 $this->MOD_SETTINGS
= BackendUtility
::getModuleData($this->MOD_MENU
, \TYPO3\CMS\Core\Utility\GeneralUtility
::_GP('SET'), $this->MCONF
['name']);
80 $this->doc
= \TYPO3\CMS\Core\Utility\GeneralUtility
::makeInstance(\TYPO3\CMS\Backend\Template\DocumentTemplate
::class);
81 $this->doc
->backPath
= $GLOBALS['BACK_PATH'];
82 $this->doc
->form
= '<form action="" method="post">';
84 $this->content
.= $this->doc
->startPage($languageService->getLL('title'));
85 $this->content
.= $this->doc
->header($languageService->getLL('title'));
86 $this->content
.= $this->doc
->spacer(5);
87 $this->content
.= $this->doc
->section('', $this->doc
->funcMenu('', BackendUtility
::getFuncMenu(0, 'SET[function]', $this->MOD_SETTINGS
['function'], $this->MOD_MENU
['function'])));
89 switch ($this->MOD_SETTINGS
['function']) {
91 $this->content
.= $this->doc
->section($languageService->getLL('Cached_info'), $this->printCachedInfo());
94 $this->content
.= $this->doc
->section($languageService->getLL('SQL_check'), $this->printSqlCheck());
97 $this->content
.= $this->doc
->section($languageService->getLL('Debug_log'), $this->printLogMgm());
101 if ($GLOBALS['BE_USER']->mayMakeShortcut()) {
102 $this->content
.= $this->doc
->spacer(20) . $this->doc
->section('', $this->doc
->makeShortcutIcon('id', implode(',', array_keys($this->MOD_MENU
)), $this->MCONF
['name']));
104 $this->content
.= $this->doc
->spacer(10);
108 * Prints out the module HTML
110 * @return string HTML output
112 public function printContent() {
113 $this->content
.= $this->doc
->endPage();
118 * Displays a form to check DBAL SQL methods and parse raw SQL.
120 * @return string HTML output
122 protected function printSqlCheck() {
123 $input = \TYPO3\CMS\Core\Utility\GeneralUtility
::_GP('tx_dbal');
125 <form name="sql_check" action="' . $this->thisScript
. '" method="post" enctype="' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['form_enctype'] . '">
126 <script type="text/javascript">
128 function updateQryForm(s) {
129 document.getElementById(\'tx-dbal-result\').style.display = \'none\';
132 document.getElementById(\'tx-dbal-qryupdate\').style.display = \'none\';
133 document.getElementById(\'tx-dbal-qryfields\').style.display = \'table-row\';
134 document.getElementById(\'tx-dbal-qryinsertvalues\').style.display = \'none\';
135 document.getElementById(\'tx-dbal-qryupdatevalues\').style.display = \'none\';
136 document.getElementById(\'tx-dbal-qryfrom\').style.display = \'table-row\';
137 document.getElementById(\'tx-dbal-qryinto\').style.display = \'none\';
138 document.getElementById(\'tx-dbal-qrywhere\').style.display = \'table-row\';
139 document.getElementById(\'tx-dbal-qrygroup\').style.display = \'table-row\';
140 document.getElementById(\'tx-dbal-qryorder\').style.display = \'table-row\';
141 document.getElementById(\'tx-dbal-qrylimit\').style.display = \'table-row\';
144 document.getElementById(\'tx-dbal-qryupdate\').style.display = \'none\';
145 document.getElementById(\'tx-dbal-qryfields\').style.display = \'none\';
146 document.getElementById(\'tx-dbal-qryinsertvalues\').style.display = \'table-row\';
147 document.getElementById(\'tx-dbal-qryupdatevalues\').style.display = \'none\';
148 document.getElementById(\'tx-dbal-qryfrom\').style.display = \'none\';
149 document.getElementById(\'tx-dbal-qryinto\').style.display = \'table-row\';
150 document.getElementById(\'tx-dbal-qrywhere\').style.display = \'table-row\';
151 document.getElementById(\'tx-dbal-qrygroup\').style.display = \'table-row\';
152 document.getElementById(\'tx-dbal-qryorder\').style.display = \'table-row\';
153 document.getElementById(\'tx-dbal-qrylimit\').style.display = \'table-row\';
156 document.getElementById(\'tx-dbal-qryupdate\').style.display = \'table-row\';
157 document.getElementById(\'tx-dbal-qryfields\').style.display = \'none\';
158 document.getElementById(\'tx-dbal-qryinsertvalues\').style.display = \'none\';
159 document.getElementById(\'tx-dbal-qryupdatevalues\').style.display = \'table-row\';
160 document.getElementById(\'tx-dbal-qryfrom\').style.display = \'none\';
161 document.getElementById(\'tx-dbal-qryinto\').style.display = \'none\';
162 document.getElementById(\'tx-dbal-qryupdate\').style.display = \'table-row\';
163 document.getElementById(\'tx-dbal-qrywhere\').style.display = \'table-row\';
164 document.getElementById(\'tx-dbal-qrygroup\').style.display = \'none\';
165 document.getElementById(\'tx-dbal-qryorder\').style.display = \'none\';
166 document.getElementById(\'tx-dbal-qrylimit\').style.display = \'none\';
169 document.getElementById(\'tx-dbal-qryupdate\').style.display = \'none\';
170 document.getElementById(\'tx-dbal-qryfields\').style.display = \'none\';
171 document.getElementById(\'tx-dbal-qryinsertvalues\').style.display = \'none\';
172 document.getElementById(\'tx-dbal-qryupdatevalues\').style.display = \'none\';
173 document.getElementById(\'tx-dbal-qryfrom\').style.display = \'table-row\';
174 document.getElementById(\'tx-dbal-qryinto\').style.display = \'none\';
175 document.getElementById(\'tx-dbal-qrywhere\').style.display = \'table-row\';
176 document.getElementById(\'tx-dbal-qrygroup\').style.display = \'none\';
177 document.getElementById(\'tx-dbal-qryorder\').style.display = \'none\';
178 document.getElementById(\'tx-dbal-qrylimit\').style.display = \'none\';
185 <tr class="tableheader bgColor5"><th colspan="2">Easy SQL check</th></tr>
187 <select name="tx_dbal[QUERY]"size="1" onchange="updateQryForm(this.options[this.selectedIndex].value)">
188 <option value="SELECT" ' . ($input['QUERY'] === 'SELECT' ?
'selected="selected"' : '') . '>SELECT</option>
189 <option value="INSERT" ' . ($input['QUERY'] === 'INSERT' ?
'selected="selected"' : '') . '>INSERT</option>
190 <option value="UPDATE" ' . ($input['QUERY'] === 'UPDATE' ?
'selected="selected"' : '') . '>UPDATE</option>
191 <option value="DELETE" ' . ($input['QUERY'] === 'DELETE' ?
'selected="selected"' : '') . '>DELETE</option>
194 <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>
195 <tr id="tx-dbal-qryfields"><td></td><td><input name="tx_dbal[FIELDS]" value="' . $input['FIELDS'] . '" type="text" size="30" maxsize="100" /></td></tr>
196 <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>
197 <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>
198 <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>
199 <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>
200 <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>
201 <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>
202 <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>
203 <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>
206 <td style="text-align:right;">
207 <input class="btn btn-default" type="submit" value="CHECK" />
210 <script type="text/javascript">
212 updateQryForm(\'' . $input['QUERY'] . '\');
216 $out .= '<tr id="tx-dbal-result" class="bgColor4"><th>Result:</th><td>';
217 switch ($input['QUERY']) {
219 $qry = $GLOBALS['TYPO3_DB']->SELECTquery($input['FIELDS'], $input['FROM'], $input['WHERE'], $input['GROUP'], $input['ORDER'], $input['LIMIT']);
222 $qry = $GLOBALS['TYPO3_DB']->INSERTquery($input['INTO'], $this->createFieldsValuesArray($input['INSERTVALUES']));
225 $qry = $GLOBALS['TYPO3_DB']->UPDATEquery($input['UPDATE'], $input['WHERE'], $this->createFieldsValuesArray($input['UPDATEVALUES']));
228 $qry = $GLOBALS['TYPO3_DB']->DELETEquery($input['FROM'], $input['WHERE']);
231 $out .= '<pre>' . htmlspecialchars($qry) . '</pre></td></tr>';
233 <tr class="tableheader bgColor5">
234 <th colspan="2">RAW SQL check</th>
237 <td colspan="2" style="text-align:right;">
238 <textarea name="tx_dbal[RAWSQL]" cols="60" rows="5">' . $input['RAWSQL'] . '</textarea>
240 <input class="btn btn-default" type="submit" value="CHECK" />
243 if (!empty($input['RAWSQL'])) {
244 $out .= '<tr class="bgColor4">';
245 $parseResult = $GLOBALS['TYPO3_DB']->SQLparser
->parseSQL($input['RAWSQL']);
246 if (is_array($parseResult)) {
247 $newQuery = $GLOBALS['TYPO3_DB']->SQLparser
->compileSQL($parseResult);
248 $testResult = $GLOBALS['TYPO3_DB']->SQLparser
->debug_parseSQLpartCompare($input['RAWSQL'], $newQuery);
249 if (!is_array($testResult)) {
250 $out .= '<td colspan="2">' . $newQuery;
252 $out .= '<td colspan="2">' . htmlspecialchars($testResult[0]) . '</td></tr>
253 <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]);
256 $out .= '<th>Result:</th><td style="border:2px solid #f00;">' . $parseResult;
258 $out .= '</td></tr>';
260 $out .= '</table></form>';
265 * Parses a very simple text format into an array.
267 * Each line is seen as a key/value pair that is exploded at =. This is used
268 * in the simple SQL check to input values for INSERT and UPDATE statements.
270 * @param string $in String to parse into key/value array.
271 * @return array Array created from the input string.
273 protected function createFieldsValuesArray($in) {
275 $in = explode(LF
, $in);
276 foreach ($in as $v) {
277 $fv = explode('=', $v);
278 $ret[$fv[0]] = $fv[1];
284 * Prints out the cached information about the database.
286 * The DBAL caches a lot of information, e.g. about auto increment fields,
287 * field types and primary keys. This method formats all this into a HTML
288 * table to display in the BE.
290 * @return string HTML output
292 protected function printCachedInfo() {
294 if ((string)\TYPO3\CMS\Core\Utility\GeneralUtility
::_GP('cmd') === 'clear') {
295 $GLOBALS['TYPO3_DB']->clearCachedFieldInfo();
296 $GLOBALS['TYPO3_DB']->cacheFieldInfo();
298 $out = '<a name="autoincrement"></a><h2>auto_increment</h2>';
299 $out .= '<table border="1" cellspacing="0"><tbody><tr><th>Table</th><th>Field</th></tr>';
300 ksort($GLOBALS['TYPO3_DB']->cache_autoIncFields
);
301 foreach ($GLOBALS['TYPO3_DB']->cache_autoIncFields
as $table => $field) {
303 $out .= '<td>' . $table . '</td>';
304 $out .= '<td>' . $field . '</td>';
307 $out .= '</tbody></table>';
308 $out .= $this->doc
->spacer(5);
309 $out .= '<a name="primarykeys"></a><h2>Primary keys</h2>';
310 $out .= '<table border="1" cellspacing="0"><tbody><tr><th>Table</th><th>Field(s)</th></tr>';
311 ksort($GLOBALS['TYPO3_DB']->cache_primaryKeys
);
312 foreach ($GLOBALS['TYPO3_DB']->cache_primaryKeys
as $table => $field) {
314 $out .= '<td>' . $table . '</td>';
315 $out .= '<td>' . $field . '</td>';
318 $out .= '</tbody></table>';
319 $out .= $this->doc
->spacer(5);
320 $out .= '<a name="fieldtypes"></a><h2>Field types</h2>';
321 $out .= '<table border="1" cellspacing="0"><tbody><tr><th colspan="5">Table</th></tr><tr><th>Field</th><th>Type</th><th><a href="#metatypes">Metatype</a></th><th>NOT NULL</th><th>Default</th></th></tr>';
322 ksort($GLOBALS['TYPO3_DB']->cache_fieldType
);
323 foreach ($GLOBALS['TYPO3_DB']->cache_fieldType
as $table => $fields) {
324 $out .= '<th colspan="5">' . $table . '</th>';
325 foreach ($fields as $field => $data) {
327 $out .= '<td>' . $field . '</td>';
328 $out .= '<td>' . $data['type'] . '</td>';
329 $out .= '<td>' . $data['metaType'] . '</td>';
330 $out .= '<td>' . ($data['notnull'] ?
'NOT NULL' : '') . '</td>';
331 $out .= '<td>' . $data['default'] . '</td>';
335 $out .= '</tbody></table>';
336 $out .= $this->doc
->spacer(5);
337 $out .= '<a name="metatypes"></a><h2>Metatype explanation</h2>';
339 C: Varchar, capped to 255 characters.
340 X: Larger varchar, capped to 4000 characters (to be compatible with Oracle).
341 XL: For Oracle, returns CLOB, otherwise the largest varchar size.
343 C2: Multibyte varchar
344 X2: Multibyte varchar (largest size)
346 B: BLOB (binary large object)
348 D: Date (some databases do not support this, and we return a datetime type)
349 T: Datetime or Timestamp
350 L: Integer field suitable for storing booleans (0 or 1)
351 I: Integer (mapped to I4)
356 F: Floating point number
357 N: Numeric or decimal number</pre>';
358 $menu = '<a href="' . $this->thisScript
. '&cmd=clear">CLEAR DATA</a><hr />';
359 $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 />';
364 * Printing the debug-log from the DBAL extension
366 * To enabled debugging, you will have to enabled it in the configuration!
368 * @return string HTML content
370 protected function printLogMgm() {
371 // Disable debugging in any case...
372 $GLOBALS['TYPO3_DB']->debug
= FALSE;
374 $cmd = (string)\TYPO3\CMS\Core\Utility\GeneralUtility
::_GP('cmd');
377 $res = $GLOBALS['TYPO3_DB']->exec_TRUNCATEquery('tx_dbal_debuglog');
378 $res = $GLOBALS['TYPO3_DB']->exec_TRUNCATEquery('tx_dbal_debuglog_where');
379 $outStr = 'Log FLUSHED!';
382 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('table_join,exec_time,query,script', 'tx_dbal_debuglog', 'table_join!=\'\'', 'table_join,script,exec_time,query');
383 // Init vars in which to pick up the query result:
384 $tableIndex = array();
388 <td>Execution time</td>
393 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
394 $tableArray = $GLOBALS['TYPO3_DB']->SQLparser
->parseFromTables($row['table_join']);
395 // Create table name index:
396 foreach ($tableArray as $a) {
397 foreach ($tableArray as $b) {
398 if ($b['table'] != $a['table']) {
399 $tableIndex[$a['table']][$b['table']] = 1;
406 <td>' . htmlspecialchars($row['exec_time']) . '</td>
407 <td>' . htmlspecialchars($row['table_join']) . '</td>
408 <td>' . htmlspecialchars($row['script']) . '</td>
409 <td>' . htmlspecialchars($row['query']) . '</td>
412 // Printing direct joins:
413 $outStr .= '<h4>Direct joins:</h4>' . \TYPO3\CMS\Core\Utility\DebugUtility
::viewArray($tableIndex);
414 // Printing total dependencies:
415 foreach ($tableIndex as $priTable => $a) {
416 foreach ($tableIndex as $tableN => $v) {
417 foreach ($v as $tableP => $vv) {
418 if ($tableP == $priTable) {
419 $tableIndex[$priTable] = array_merge($v, $a);
424 $outStr .= '<h4>Total dependencies:</h4>' . \TYPO3\CMS\Core\Utility\DebugUtility
::viewArray($tableIndex);
425 // Printing data rows:
427 <table border="1" cellspacing="0">' . implode('', $tRows) . '
431 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('serdata,exec_time,query,script', 'tx_dbal_debuglog', 'errorFlag>0', '', 'tstamp DESC');
432 // Init vars in which to pick up the query result:
436 <td>Execution time</td>
441 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
445 <td>' . htmlspecialchars($row['exec_time']) . '</td>
446 <td>' . \TYPO3\CMS\Core\Utility\DebugUtility
::viewArray(unserialize($row['serdata'])) . '</td>
447 <td>' . htmlspecialchars($row['script']) . '</td>
448 <td>' . htmlspecialchars($row['query']) . '</td>
451 // Printing data rows:
453 <table border="1" cellspacing="0">' . implode('', $tRows) . '
457 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('query,serdata', 'tx_dbal_debuglog', 'errorFlag&2=2');
459 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
463 <td>' . htmlspecialchars($row['query']) . '</td>
466 // Printing data rows:
468 <table border="1" cellspacing="0">' . implode('', $tRows) . '
472 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('tstamp,script,tablename,whereclause', 'tx_dbal_debuglog_where', '', '', 'tstamp DESC');
479 <td>WHERE clause</td>
481 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
484 <td>' . BackendUtility
::datetime($row['tstamp']) . '</td>
485 <td>' . htmlspecialchars($row['script']) . '</td>
486 <td>' . htmlspecialchars($row['tablename']) . '</td>
487 <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>
491 <table border="1" cellspacing="0">' . implode('', $tRows) . '
495 // Look for request to view specific script exec:
496 $specTime = \TYPO3\CMS\Core\Utility\GeneralUtility
::_GP('specTime');
498 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('exec_time,errorFlag,table_join,serdata,query', 'tx_dbal_debuglog', 'tstamp=' . (int)$specTime);
502 <td>Execution time</td>
508 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
511 <td>' . htmlspecialchars($row['exec_time']) . '</td>
512 <td>' . ($row['errorFlag'] ?
1 : 0) . '</td>
513 <td>' . htmlspecialchars($row['table_join']) . '</td>
514 <td>' . \TYPO3\CMS\Core\Utility\DebugUtility
::viewArray(unserialize($row['serdata'])) . '</td>
515 <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>
519 $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');
524 <td># of queries</td>
529 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
532 <td>' . BackendUtility
::datetime($row['tstamp']) . '</td>
533 <td>' . htmlspecialchars($row['qrycount']) . '</td>
534 <td>' . ($row['error'] ?
'<strong style="color:#f00">ERR</strong>' : '') . '</td>
535 <td>' . htmlspecialchars($row['calc_sum']) . '</td>
536 <td><a href="' . $this->thisScript
. '&specTime=' . (int)$row['tstamp'] . '">' . htmlspecialchars($row['script']) . '</a></td>
541 <table border="1" cellspacing="0">' . implode('', $tRows) . '
545 <a href="' . $this->thisScript
. '&cmd=flush">FLUSH LOG</a> -
546 <a href="' . $this->thisScript
. '&cmd=joins">JOINS</a> -
547 <a href="' . $this->thisScript
. '&cmd=errors">ERRORS</a> -
548 <a href="' . $this->thisScript
. '&cmd=parsing">PARSING</a> -
549 <a href="' . $this->thisScript
. '">LOG</a> -
550 <a href="' . $this->thisScript
. '&cmd=where">WHERE</a> -
552 <a href="' . htmlspecialchars(\TYPO3\CMS\Core\Utility\GeneralUtility
::linkThisScript()) . '" target="tx_debuglog">[New window]</a>
555 return $menu . $outStr;
559 * Returns the language service.
561 * @return \TYPO3\CMS\Lang\LanguageService
563 protected function getLanguageService() {
564 return $GLOBALS['LANG'];