918451ed3ab3eeae1b7902e82a36fcbe6680cae6
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Database / Schema / SqlReader.php
1 <?php
2 declare(strict_types = 1);
3 namespace TYPO3\CMS\Core\Database\Schema;
4
5 /*
6 * This file is part of the TYPO3 CMS project.
7 *
8 * It is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, either version 2
10 * of the License, or any later version.
11 *
12 * For the full copyright and license information, please read the
13 * LICENSE.txt file that was distributed with this source code.
14 *
15 * The TYPO3 project - inspiring people to share!
16 */
17
18 use TYPO3\CMS\Core\Utility\GeneralUtility;
19 use TYPO3\CMS\Extbase\SignalSlot\Dispatcher;
20
21 /**
22 * Helper methods to handle raw SQL input and transform it into individual statements
23 * for further processing.
24 *
25 * @internal
26 */
27 class SqlReader
28 {
29 /**
30 * @var \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
31 */
32 protected $signalSlotDispatcher;
33
34 /**
35 * @param Dispatcher $signalSlotDispatcher
36 * @throws \InvalidArgumentException
37 */
38 public function __construct(Dispatcher $signalSlotDispatcher = null)
39 {
40 $this->signalSlotDispatcher = $signalSlotDispatcher ?: GeneralUtility::makeInstance(Dispatcher::class);
41 }
42
43 /**
44 * Cycle through all loaded extensions and get full table definitions as concatenated string
45 *
46 * @param bool $withStatic TRUE if sql from ext_tables_static+adt.sql should be loaded, too.
47 * @return string Concatenated SQL of loaded extensions ext_tables.sql
48 * @throws \TYPO3\CMS\Core\Database\Schema\Exception\UnexpectedSignalReturnValueTypeException
49 * @throws \TYPO3\CMS\Extbase\SignalSlot\Exception\InvalidSlotReturnException
50 * @throws \TYPO3\CMS\Extbase\SignalSlot\Exception\InvalidSlotException
51 */
52 public function getTablesDefinitionString(bool $withStatic = false): string
53 {
54 $sqlString = [];
55
56 // Find all ext_tables.sql of loaded extensions
57 foreach ((array)$GLOBALS['TYPO3_LOADED_EXT'] as $extensionConfiguration) {
58 if (!is_array($extensionConfiguration) && !$extensionConfiguration instanceof \ArrayAccess) {
59 continue;
60 }
61 if ($extensionConfiguration['ext_tables.sql']) {
62 $sqlString[] = file_get_contents($extensionConfiguration['ext_tables.sql']);
63 }
64 if ($withStatic && $extensionConfiguration['ext_tables_static+adt.sql']) {
65 $sqlString[] = file_get_contents($extensionConfiguration['ext_tables_static+adt.sql']);
66 }
67 }
68
69 $sqlString = $this->emitTablesDefinitionIsBeingBuiltSignal($sqlString);
70
71 return implode(LF . LF, $sqlString);
72 }
73
74 /**
75 * Returns an array where every entry is a single SQL-statement.
76 * Input must be formatted like an ordinary MySQL dump file. Every statements needs to be terminated by a ';'
77 * and there may only be one statement (or partial statement) per line.
78 *
79 * @param string $dumpContent The SQL dump content.
80 * @param string $queryRegex Regex to select which statements to return.
81 * @return array Array of SQL statements
82 */
83 public function getStatementArray(string $dumpContent, string $queryRegex = null): array
84 {
85 $statementArray = [];
86 $statementArrayPointer = 0;
87 foreach (explode(LF, $dumpContent) as $lineContent) {
88 $lineContent = trim($lineContent);
89
90 // Skip empty lines and comments
91 if ($lineContent === '' || $lineContent[0] === '#' || strpos($lineContent, '--') === 0) {
92 continue;
93 }
94
95 $statementArray[$statementArrayPointer] = ($statementArray[$statementArrayPointer] ?? '') . $lineContent;
96
97 if (substr($lineContent, -1) === ';') {
98 $statement = trim($statementArray[$statementArrayPointer]);
99 if (!$statement || ($queryRegex && !preg_match('/' . $queryRegex . '/i', $statement))) {
100 unset($statementArray[$statementArrayPointer]);
101 }
102 $statementArrayPointer++;
103 } else {
104 $statementArray[$statementArrayPointer] .= ' ';
105 }
106 }
107
108 return $statementArray;
109 }
110
111 /**
112 * Extract only INSERT statements from SQL dump
113 *
114 * @param string $dumpContent
115 * @return array
116 */
117 public function getInsertStatementArray(string $dumpContent): array
118 {
119 return $this->getStatementArray($dumpContent, '^INSERT');
120 }
121
122 /**
123 * Extract only CREATE TABLE statements from SQL dump
124 *
125 * @param string $dumpContent
126 * @return array
127 */
128 public function getCreateTableStatementArray(string $dumpContent): array
129 {
130 return $this->getStatementArray($dumpContent, '^CREATE TABLE');
131 }
132
133 /**
134 * Emits a signal to manipulate the tables definitions
135 *
136 * @param array $sqlString
137 * @return array
138 * @throws \TYPO3\CMS\Core\Database\Schema\Exception\UnexpectedSignalReturnValueTypeException
139 * @throws \TYPO3\CMS\Extbase\SignalSlot\Exception\InvalidSlotReturnException
140 * @throws \TYPO3\CMS\Extbase\SignalSlot\Exception\InvalidSlotException
141 */
142 protected function emitTablesDefinitionIsBeingBuiltSignal(array $sqlString): array
143 {
144 // Using the old class name from the install tool here to keep backwards compatibility.
145 $signalReturn = $this->signalSlotDispatcher->dispatch(
146 'TYPO3\\CMS\\Install\\Service\\SqlExpectedSchemaService',
147 'tablesDefinitionIsBeingBuilt',
148 [$sqlString]
149 );
150
151 // This is important to support old associated returns
152 $signalReturn = array_values($signalReturn);
153 $sqlString = $signalReturn[0];
154 if (!is_array($sqlString)) {
155 throw new Exception\UnexpectedSignalReturnValueTypeException(
156 sprintf(
157 'The signal %s of class %s returned a value of type %s, but array was expected.',
158 'tablesDefinitionIsBeingBuilt',
159 __CLASS__,
160 gettype($sqlString)
161 ),
162 1382351456
163 );
164 }
165
166 return $sqlString;
167 }
168 }