TYPO3  7.6
SqlSchemaMigrationService.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Install\Service;
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 
22 {
27 
31  protected $deletedPrefixKey = 'zzz_deleted_';
32 
36  protected $character_sets = array();
37 
43  public function setDeletedPrefixKey($prefix)
44  {
45  $this->deletedPrefixKey = $prefix;
46  }
47 
53  public function getDeletedPrefixKey()
54  {
56  }
57 
64  public function getFieldDefinitions_fileContent($fileContent)
65  {
66  $lines = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(LF, $fileContent, true);
67  $table = '';
68  $total = array();
69  foreach ($lines as $value) {
70  if ($value[0] === '#') {
71  // Ignore comments
72  continue;
73  }
74  if ($table === '') {
75  $parts = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(' ', $value, true);
76  if (strtoupper($parts[0]) === 'CREATE' && strtoupper($parts[1]) === 'TABLE') {
77  $table = str_replace('`', '', $parts[2]);
78  // tablenames are always lowercase on windows!
79  if (TYPO3_OS == 'WIN') {
80  $table = strtolower($table);
81  }
82  }
83  } else {
84  if ($value[0] === ')' && substr($value, -1) === ';') {
85  $ttype = array();
86  if (preg_match('/(ENGINE|TYPE)[ ]*=[ ]*([a-zA-Z]*)/', $value, $ttype)) {
87  $total[$table]['extra']['ENGINE'] = $ttype[2];
88  }
89  // Otherwise, just do nothing: If table engine is not defined, just accept the system default.
90  // Set the collation, if specified
91  if (preg_match('/(COLLATE)[ ]*=[ ]*([a-zA-z0-9_-]+)/', $value, $tcollation)) {
92  $total[$table]['extra']['COLLATE'] = $tcollation[2];
93  } else {
94  // Otherwise, get the CHARACTER SET and try to find the default collation for it as returned by "SHOW CHARACTER SET" query (for details, see http://dev.mysql.com/doc/refman/5.1/en/charset-table.html)
95  if (preg_match('/(CHARSET|CHARACTER SET)[ ]*=[ ]*([a-zA-z0-9_-]+)/', $value, $tcharset)) {
96  // Note: Keywords "DEFAULT CHARSET" and "CHARSET" are the same, so "DEFAULT" can just be ignored
97  $charset = $tcharset[2];
98  } else {
99  $charset = $this->getDatabaseConnection()->default_charset;
100  }
101  $total[$table]['extra']['COLLATE'] = $this->getCollationForCharset($charset);
102  }
103  // Remove table marker and start looking for the next "CREATE TABLE" statement
104  $table = '';
105  } else {
106  // Strip trailing commas
107  $lineV = preg_replace('/,$/', '', $value);
108  $lineV = str_replace('`', '', $lineV);
109  // Reduce multiple blanks and tabs except newline
110  $lineV = preg_replace('/\h+/', ' ', $lineV);
111  $parts = explode(' ', $lineV, 2);
112  // Field definition
113  if (!preg_match('/(PRIMARY|UNIQUE|FULLTEXT|SPATIAL|INDEX|KEY)/', $parts[0])) {
114  // Make sure there is no default value when auto_increment is set
115  if (stristr($parts[1], 'auto_increment')) {
116  $parts[1] = preg_replace('/ default \'0\'/i', '', $parts[1]);
117  }
118  // "default" is always lower-case
119  if (stristr($parts[1], ' DEFAULT ')) {
120  $parts[1] = str_ireplace(' DEFAULT ', ' default ', $parts[1]);
121  }
122  // Change order of "default" and "NULL" statements
123  $parts[1] = preg_replace('/(.*) (default .*) (NOT NULL)/', '$1 $3 $2', $parts[1]);
124  $parts[1] = preg_replace('/(.*) (default .*) (NULL)/', '$1 $3 $2', $parts[1]);
125  $key = $parts[0];
126  $total[$table]['fields'][$key] = $parts[1];
127  } else {
128  // Key definition
129  $search = array('/UNIQUE (INDEX|KEY)/', '/FULLTEXT (INDEX|KEY)/', '/SPATIAL (INDEX|KEY)/', '/INDEX/');
130  $replace = array('UNIQUE', 'FULLTEXT', 'SPATIAL', 'KEY');
131  $lineV = preg_replace($search, $replace, $lineV);
132  if (preg_match('/PRIMARY|UNIQUE|FULLTEXT|SPATIAL/', $parts[0])) {
133  $parts[1] = preg_replace('/^(KEY|INDEX) /', '', $parts[1]);
134  }
135  $newParts = explode(' ', $parts[1], 2);
136  $key = $parts[0] == 'PRIMARY' ? $parts[0] : $newParts[0];
137  $total[$table]['keys'][$key] = $lineV;
138  // This is a protection against doing something stupid: Only allow clearing of cache_* and index_* tables.
139  if (preg_match('/^(cache|index)_/', $table)) {
140  // Suggest to truncate (clear) this table
141  $total[$table]['extra']['CLEAR'] = 1;
142  }
143  }
144  }
145  }
146  }
147  return $total;
148  }
149 
156  public function getCollationForCharset($charset)
157  {
158  // Load character sets, if not cached already
159  if (empty($this->character_sets)) {
160  $databaseConnection = $this->getDatabaseConnection();
161  if (method_exists($databaseConnection, 'admin_get_charsets')) {
162  $this->character_sets = $databaseConnection->admin_get_charsets();
163  } else {
164  // Add empty element to avoid that the check will be repeated
165  $this->character_sets[$charset] = array();
166  }
167  }
168  $collation = '';
169  if (isset($this->character_sets[$charset]['Default collation'])) {
170  $collation = $this->character_sets[$charset]['Default collation'];
171  }
172  return $collation;
173  }
174 
181  {
182  $total = array();
183  $tempKeys = array();
184  $tempKeysPrefix = array();
185  $databaseConnection = $this->getDatabaseConnection();
186  $databaseConnection->connectDB();
187  echo $databaseConnection->sql_error();
188  $tables = $databaseConnection->admin_get_tables();
189  foreach ($tables as $tableName => $tableStatus) {
190  // Fields
191  $fieldInformation = $databaseConnection->admin_get_fields($tableName);
192  foreach ($fieldInformation as $fN => $fieldRow) {
193  $total[$tableName]['fields'][$fN] = $this->assembleFieldDefinition($fieldRow);
194  }
195  // Keys
196  $keyInformation = $databaseConnection->admin_get_keys($tableName);
197  foreach ($keyInformation as $keyRow) {
198  $keyName = $keyRow['Key_name'];
199  $colName = $keyRow['Column_name'];
200  if ($keyRow['Sub_part'] && $keyRow['Index_type'] !== 'SPATIAL') {
201  $colName .= '(' . $keyRow['Sub_part'] . ')';
202  }
203  $tempKeys[$tableName][$keyName][$keyRow['Seq_in_index']] = $colName;
204  if ($keyName == 'PRIMARY') {
205  $prefix = 'PRIMARY KEY';
206  } else {
207  if ($keyRow['Index_type'] === 'FULLTEXT') {
208  $prefix = 'FULLTEXT';
209  } elseif ($keyRow['Index_type'] === 'SPATIAL') {
210  $prefix = 'SPATIAL';
211  } elseif ($keyRow['Non_unique']) {
212  $prefix = 'KEY';
213  } else {
214  $prefix = 'UNIQUE';
215  }
216  $prefix .= ' ' . $keyName;
217  }
218  $tempKeysPrefix[$tableName][$keyName] = $prefix;
219  }
220  // Table status (storage engine, collaction, etc.)
221  if (is_array($tableStatus)) {
222  $tableExtraFields = array(
223  'Engine' => 'ENGINE',
224  'Collation' => 'COLLATE'
225  );
226  foreach ($tableExtraFields as $mysqlKey => $internalKey) {
227  if (isset($tableStatus[$mysqlKey])) {
228  $total[$tableName]['extra'][$internalKey] = $tableStatus[$mysqlKey];
229  }
230  }
231  }
232  }
233  // Compile key information:
234  if (!empty($tempKeys)) {
235  foreach ($tempKeys as $table => $keyInf) {
236  foreach ($keyInf as $kName => $index) {
237  ksort($index);
238  $total[$table]['keys'][$kName] = $tempKeysPrefix[$table][$kName] . ' (' . implode(',', $index) . ')';
239  }
240  }
241  }
242  return $total;
243  }
244 
255  public function getDatabaseExtra($FDsrc, $FDcomp, $onlyTableList = '', $ignoreNotNullWhenComparing = false)
256  {
257  $extraArr = array();
258  $diffArr = array();
259  if (is_array($FDsrc)) {
260  foreach ($FDsrc as $table => $info) {
261  if ($onlyTableList === '' || \TYPO3\CMS\Core\Utility\GeneralUtility::inList($onlyTableList, $table)) {
262  if (!isset($FDcomp[$table])) {
263  // If the table was not in the FDcomp-array, the result array is loaded with that table.
264  $extraArr[$table] = $info;
265  $extraArr[$table]['whole_table'] = 1;
266  } else {
267  $keyTypes = explode(',', 'extra,fields,keys');
268  foreach ($keyTypes as $theKey) {
269  if (is_array($info[$theKey])) {
270  foreach ($info[$theKey] as $fieldN => $fieldC) {
271  $fieldN = str_replace('`', '', $fieldN);
272  if ($this->isDbalEnabled() && $fieldN === 'ENGINE') {
273  continue;
274  }
275  if ($fieldN == 'COLLATE') {
276  // @todo collation support is currently disabled (needs more testing)
277  continue;
278  }
279  if (!isset($FDcomp[$table][$theKey][$fieldN])) {
280  $extraArr[$table][$theKey][$fieldN] = $fieldC;
281  } else {
282  $fieldC = trim($fieldC);
283 
284  // Lowercase the field type to surround false-positive schema changes to be
285  // reported just because of different caseing of characters
286  // The regex does just trigger for the first word followed by parentheses
287  // that contain a length. It does not trigger for e.g. "PRIMARY KEY" because
288  // "PRIMARY KEY" is being returned from the DB in upper case.
289  $fieldC = preg_replace_callback(
290  '/^([a-zA-Z0-9]+)(\([^)]*\)\s.*)/',
291  function ($matches) { return strtolower($matches[1]) . $matches[2]; },
292  $fieldC
293  );
294 
295  if ($this->isDbalEnabled()) {
296  // Ignore nonstandard MySQL numeric field attributes UNSIGNED and ZEROFILL
297  if (preg_match('/^(TINYINT|SMALLINT|MEDIUMINT|INT|INTEGER|BIGINT|REAL|DOUBLE|FLOAT|DECIMAL|NUMERIC)\([^\)]+\)\s+(UNSIGNED|ZEROFILL)/i', $fieldC)) {
298  $fieldC = str_ireplace(array(' UNSIGNED', ' ZEROFILL'), '', $fieldC);
299  $FDcomp[$table][$theKey][$fieldN] = str_ireplace(array(' UNSIGNED', ' ZEROFILL'), '', $FDcomp[$table][$theKey][$fieldN]);
300  }
301 
302  // Replace field and index definitions with functionally equivalent statements
303  if ($fieldC !== $FDcomp[$table][$theKey][$fieldN]) {
304  switch ($theKey) {
305  case 'fields':
306  $fieldC = $this->getDatabaseConnection()->getEquivalentFieldDefinition($fieldC);
307  break;
308  case 'keys':
309  $fieldC = $this->getDatabaseConnection()->getEquivalentIndexDefinition($fieldC);
310  break;
311  }
312  }
313  }
314  if ($ignoreNotNullWhenComparing) {
315  $fieldC = str_replace(' NOT NULL', '', $fieldC);
316  $FDcomp[$table][$theKey][$fieldN] = str_replace(' NOT NULL', '', $FDcomp[$table][$theKey][$fieldN]);
317  }
318  if ($fieldC !== $FDcomp[$table][$theKey][$fieldN]) {
319  $diffArr[$table][$theKey][$fieldN] = $fieldC;
320  $diffArr_cur[$table][$theKey][$fieldN] = $FDcomp[$table][$theKey][$fieldN];
321  }
322  }
323  }
324  }
325  }
326  }
327  }
328  }
329  }
330  $output = array(
331  'extra' => $extraArr,
332  'diff' => $diffArr,
333  'diff_currentValues' => $diffArr_cur
334  );
335  return $output;
336  }
337 
345  public function getUpdateSuggestions($diffArr, $keyList = 'extra,diff')
346  {
347  $statements = array();
349  $deletedPrefixLength = strlen($deletedPrefixKey);
350  $remove = 0;
351  if ($keyList == 'remove') {
352  $remove = 1;
353  $keyList = 'extra';
354  }
355  $keyList = explode(',', $keyList);
356  foreach ($keyList as $theKey) {
357  if (is_array($diffArr[$theKey])) {
358  foreach ($diffArr[$theKey] as $table => $info) {
359  $whole_table = array();
360  if (isset($info['keys']) && is_array($info['keys'])) {
361  foreach ($info['keys'] as $fN => $fV) {
362  if (!$info['whole_table'] && $theKey === 'extra' && $remove) {
363  $statement = 'ALTER TABLE ' . $table . ($fN === 'PRIMARY' ? ' DROP PRIMARY KEY' : ' DROP KEY ' . $fN) . ';';
364  $statements['drop'][md5($statement)] = $statement;
365  }
366  }
367  }
368  if (is_array($info['fields'])) {
369  foreach ($info['fields'] as $fN => $fV) {
370  if ($info['whole_table']) {
371  $whole_table[] = $fN . ' ' . $fV;
372  } else {
373  // Special case to work around MySQL problems when adding auto_increment fields:
374  if (stristr($fV, 'auto_increment')) {
375  // The field can only be set "auto_increment" if there exists a PRIMARY key of that field already.
376  // The check does not look up which field is primary but just assumes it must be the field with the auto_increment value...
377  if (isset($info['keys']['PRIMARY'])) {
378  if (!$this->isDbalEnabled()) {
379  // Combine adding the field and the primary key into a single statement
380  $fV .= ', ADD PRIMARY KEY (' . $fN . ')';
381  unset($info['keys']['PRIMARY']);
382  }
383  } else {
384  // In the next step, attempt to clear the table once again (2 = force)
385  $info['extra']['CLEAR'] = 2;
386  }
387  }
388  if ($theKey == 'extra') {
389  if ($remove) {
390  if (substr($fN, 0, $deletedPrefixLength) !== $deletedPrefixKey) {
391  // we've to make sure we don't exceed the maximal length
392  $prefixedFieldName = $deletedPrefixKey . substr($fN, ($deletedPrefixLength - self::MYSQL_MAXIMUM_FIELD_WIDTH));
393  $statement = 'ALTER TABLE ' . $table . ' CHANGE ' . $fN . ' ' . $prefixedFieldName . ' ' . $fV . ';';
394  $statements['change'][md5($statement)] = $statement;
395  } else {
396  $statement = 'ALTER TABLE ' . $table . ' DROP ' . $fN . ';';
397  $statements['drop'][md5($statement)] = $statement;
398  }
399  } else {
400  $statement = 'ALTER TABLE ' . $table . ' ADD ' . $fN . ' ' . $fV . ';';
401  $statements['add'][md5($statement)] = $statement;
402  }
403  } elseif ($theKey == 'diff') {
404  $statement = 'ALTER TABLE ' . $table . ' CHANGE ' . $fN . ' ' . $fN . ' ' . $fV . ';';
405  $statements['change'][md5($statement)] = $statement;
406  $statements['change_currentValue'][md5($statement)] = $diffArr['diff_currentValues'][$table]['fields'][$fN];
407  }
408  }
409  }
410  }
411  if (is_array($info['keys'])) {
412  foreach ($info['keys'] as $fN => $fV) {
413  if ($info['whole_table']) {
414  $whole_table[] = $fV;
415  } else {
416  if ($theKey == 'extra') {
417  if (!$remove) {
418  $statement = 'ALTER TABLE ' . $table . ' ADD ' . $fV . ';';
419  $statements['add'][md5($statement)] = $statement;
420  }
421  } elseif ($theKey == 'diff') {
422  $statement = 'ALTER TABLE ' . $table . ($fN == 'PRIMARY' ? ' DROP PRIMARY KEY' : ' DROP KEY ' . $fN) . ';';
423  $statements['change'][md5($statement)] = $statement;
424  $statement = 'ALTER TABLE ' . $table . ' ADD ' . $fV . ';';
425  $statements['change'][md5($statement)] = $statement;
426  }
427  }
428  }
429  }
430  if (is_array($info['extra'])) {
431  $extras = array();
432  $extras_currentValue = array();
433  $clear_table = false;
434  foreach ($info['extra'] as $fN => $fV) {
435  // Only consider statements which are missing in the database but don't remove existing properties
436  if (!$remove) {
437  if (!$info['whole_table']) {
438  // If the whole table is created at once, we take care of this later by imploding all elements of $info['extra']
439  if ($fN == 'CLEAR') {
440  // Truncate table must happen later, not now
441  // Valid values for CLEAR: 1=only clear if keys are missing, 2=clear anyway (force)
442  if (!empty($info['keys']) || $fV == 2) {
443  $clear_table = true;
444  }
445  continue;
446  } else {
447  $extras[] = $fN . '=' . $fV;
448  $extras_currentValue[] = $fN . '=' . $diffArr['diff_currentValues'][$table]['extra'][$fN];
449  }
450  }
451  }
452  }
453  if ($clear_table) {
454  $statement = 'TRUNCATE TABLE ' . $table . ';';
455  $statements['clear_table'][md5($statement)] = $statement;
456  }
457  if (!empty($extras)) {
458  $statement = 'ALTER TABLE ' . $table . ' ' . implode(' ', $extras) . ';';
459  $statements['change'][md5($statement)] = $statement;
460  $statements['change_currentValue'][md5($statement)] = implode(' ', $extras_currentValue);
461  }
462  }
463  if ($info['whole_table']) {
464  if ($remove) {
465  if (substr($table, 0, $deletedPrefixLength) !== $deletedPrefixKey) {
466  // we've to make sure we don't exceed the maximal length
467  $prefixedTableName = $deletedPrefixKey . substr($table, ($deletedPrefixLength - self::MYSQL_MAXIMUM_FIELD_WIDTH));
468  $statement = 'ALTER TABLE ' . $table . ' RENAME ' . $prefixedTableName . ';';
469  $statements['change_table'][md5($statement)] = $statement;
470  } else {
471  $statement = 'DROP TABLE ' . $table . ';';
472  $statements['drop_table'][md5($statement)] = $statement;
473  }
474  // Count
475  $count = $this->getDatabaseConnection()->exec_SELECTcountRows('*', $table);
476  $statements['tables_count'][md5($statement)] = $count ? 'Records in table: ' . $count : '';
477  } else {
478  $statement = 'CREATE TABLE ' . $table . ' (
479 ' . implode(',
480 ', $whole_table) . '
481 )';
482  if ($info['extra']) {
483  foreach ($info['extra'] as $k => $v) {
484  if ($k == 'COLLATE' || $k == 'CLEAR') {
485  // Skip these special statements.
486  // @todo collation support is currently disabled (needs more testing)
487  continue;
488  }
489  // Add extra attributes like ENGINE, CHARSET, etc.
490  $statement .= ' ' . $k . '=' . $v;
491  }
492  }
493  $statement .= ';';
494  $statements['create_table'][md5($statement)] = $statement;
495  }
496  }
497  }
498  }
499  }
500  return $statements;
501  }
502 
509  public function assembleFieldDefinition($row)
510  {
511  $field = array($row['Type']);
512  if ($row['Null'] == 'NO') {
513  $field[] = 'NOT NULL';
514  }
515  if (!strstr($row['Type'], 'blob') && !strstr($row['Type'], 'text')) {
516  // Add a default value if the field is not auto-incremented (these fields never have a default definition)
517  if (!stristr($row['Extra'], 'auto_increment')) {
518  if ($row['Default'] === null) {
519  $field[] = 'default NULL';
520  } else {
521  $field[] = 'default \'' . addslashes($row['Default']) . '\'';
522  }
523  }
524  }
525  if ($row['Extra']) {
526  $field[] = $row['Extra'];
527  }
528  if (trim($row['Comment']) !== '') {
529  $field[] = "COMMENT '" . $row['Comment'] . "'";
530  }
531  return implode(' ', $field);
532  }
533 
542  public function getStatementArray($sqlcode, $removeNonSQL = false, $query_regex = '')
543  {
544  $sqlcodeArr = explode(LF, $sqlcode);
545  // Based on the assumption that the sql-dump has
546  $statementArray = array();
547  $statementArrayPointer = 0;
548  foreach ($sqlcodeArr as $line => $lineContent) {
549  $lineContent = trim($lineContent);
550  $is_set = 0;
551  // Auto_increment fields cannot have a default value!
552  if (stristr($lineContent, 'auto_increment')) {
553  $lineContent = preg_replace('/ default \'0\'/i', '', $lineContent);
554  }
555  if (!$removeNonSQL || $lineContent !== '' && $lineContent[0] !== '#' && substr($lineContent, 0, 2) !== '--') {
556  // '--' is seen as mysqldump comments from server version 3.23.49
557  $statementArray[$statementArrayPointer] .= $lineContent;
558  $is_set = 1;
559  }
560  if (substr($lineContent, -1) === ';') {
561  if (isset($statementArray[$statementArrayPointer])) {
562  if (!trim($statementArray[$statementArrayPointer]) || $query_regex && !preg_match(('/' . $query_regex . '/i'), trim($statementArray[$statementArrayPointer]))) {
563  unset($statementArray[$statementArrayPointer]);
564  }
565  }
566  $statementArrayPointer++;
567  } elseif ($is_set) {
568  $statementArray[$statementArrayPointer] .= LF;
569  }
570  }
571  return $statementArray;
572  }
573 
581  public function getCreateTables($statements, $insertCountFlag = false)
582  {
583  $crTables = array();
584  $insertCount = array();
585  foreach ($statements as $line => $lineContent) {
586  $reg = array();
587  if (preg_match('/^create[[:space:]]*table[[:space:]]*[`]?([[:alnum:]_]*)[`]?/i', substr($lineContent, 0, 100), $reg)) {
588  $table = trim($reg[1]);
589  if ($table) {
590  // Table names are always lowercase on Windows!
591  if (TYPO3_OS == 'WIN') {
592  $table = strtolower($table);
593  }
594  $sqlLines = explode(LF, $lineContent);
595  foreach ($sqlLines as $k => $v) {
596  if (stristr($v, 'auto_increment')) {
597  $sqlLines[$k] = preg_replace('/ default \'0\'/i', '', $v);
598  }
599  }
600  $lineContent = implode(LF, $sqlLines);
601  $crTables[$table] = $lineContent;
602  }
603  } elseif ($insertCountFlag && preg_match('/^insert[[:space:]]*into[[:space:]]*[`]?([[:alnum:]_]*)[`]?/i', substr($lineContent, 0, 100), $reg)) {
604  $nTable = trim($reg[1]);
605  $insertCount[$nTable]++;
606  }
607  }
608  return array($crTables, $insertCount);
609  }
610 
618  public function getTableInsertStatements($statements, $table)
619  {
620  $outStatements = array();
621  foreach ($statements as $line => $lineContent) {
622  $reg = array();
623  if (preg_match('/^insert[[:space:]]*into[[:space:]]*[`]?([[:alnum:]_]*)[`]?/i', substr($lineContent, 0, 100), $reg)) {
624  $nTable = trim($reg[1]);
625  if ($nTable && $table === $nTable) {
626  $outStatements[] = $lineContent;
627  }
628  }
629  }
630  return $outStatements;
631  }
632 
640  public function performUpdateQueries($arr, $keyArr)
641  {
642  $result = array();
643  if (is_array($arr)) {
644  $databaseConnection = $this->getDatabaseConnection();
645  foreach ($arr as $key => $string) {
646  if (isset($keyArr[$key]) && $keyArr[$key]) {
647  $res = $databaseConnection->admin_query($string);
648  if ($res === false) {
649  $result[$key] = $databaseConnection->sql_error();
650  } elseif (is_resource($res) || is_a($res, '\\mysqli_result')) {
651  $databaseConnection->sql_free_result($res);
652  }
653  }
654  }
655  }
656  if (!empty($result)) {
657  return $result;
658  } else {
659  return true;
660  }
661  }
662 
669  public function getListOfTables()
670  {
671  $whichTables = $this->getDatabaseConnection()->admin_get_tables(TYPO3_db);
672  foreach ($whichTables as $key => &$value) {
673  $value = $key;
674  }
675  unset($value);
676  return $whichTables;
677  }
678 
684  protected function isDbalEnabled()
685  {
686  return \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('dbal');
687  }
688 
692  protected function getDatabaseConnection()
693  {
694  return $GLOBALS['TYPO3_DB'];
695  }
696 }