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