‪TYPO3CMS  ‪main
Comparator.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
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 
19 
20 use Doctrine\DBAL\Platforms\AbstractPlatform;
21 use Doctrine\DBAL\Platforms\MySQLPlatform;
22 use Doctrine\DBAL\Schema\Column;
23 use Doctrine\DBAL\Schema\ForeignKeyConstraint;
24 use Doctrine\DBAL\Schema\Schema;
25 use Doctrine\DBAL\Schema\SchemaDiff;
26 use Doctrine\DBAL\Schema\SchemaException;
27 use Doctrine\DBAL\Schema\Sequence;
28 use Doctrine\DBAL\Schema\Table;
29 use Doctrine\DBAL\Types\BlobType;
30 use Doctrine\DBAL\Types\TextType;
32 
38 class ‪Comparator extends \Doctrine\DBAL\Schema\Comparator
39 {
43  protected ‪$databasePlatform;
44 
48  public function ‪__construct(AbstractPlatform $platform = null)
49  {
50  $this->databasePlatform = $platform;
51  parent::__construct($platform);
52  }
53 
64  public function ‪diffTable(Table $fromTable, Table $toTable)
65  {
66  $newTableOptions = array_merge($fromTable->getOptions(), $toTable->getOptions());
67  $optionDiff = ArrayUtility::arrayDiffAssocRecursive($newTableOptions, $fromTable->getOptions());
68  $tableDifferences = parent::diffTable($fromTable, $toTable);
69 
70  // No changed table options, return parent result
71  if (count($optionDiff) === 0) {
72  return $tableDifferences;
73  }
74 
75  if ($tableDifferences === false) {
76  $tableDifferences = new ‪TableDiff($fromTable->getName());
77  $tableDifferences->fromTable = $fromTable;
78  } else {
79  $renamedColumns = $tableDifferences->renamedColumns;
80  $renamedIndexes = $tableDifferences->renamedIndexes;
81  // Rebuild TableDiff with enhanced TYPO3 TableDiff class
82  $tableDifferences = new ‪TableDiff(
83  $tableDifferences->name,
84  $tableDifferences->addedColumns,
85  $tableDifferences->changedColumns,
86  $tableDifferences->removedColumns,
87  $tableDifferences->addedIndexes,
88  $tableDifferences->changedIndexes,
89  $tableDifferences->removedIndexes,
90  $tableDifferences->fromTable
91  );
92  $tableDifferences->renamedColumns = $renamedColumns;
93  $tableDifferences->renamedIndexes = $renamedIndexes;
94  }
95 
96  // Set the table options to be parsed in the AlterTable event.
97  $tableDifferences->setTableOptions($optionDiff);
98 
99  return $tableDifferences;
100  }
101 
109  public function ‪diffColumn(Column $column1, Column $column2)
110  {
111  $changedProperties = parent::diffColumn($column1, $column2);
112 
113  // Only MySQL has variable length versions of TEXT/BLOB
114  if (!$this->databasePlatform instanceof MySQLPlatform) {
115  return $changedProperties;
116  }
117 
118  $properties1 = $column1->toArray();
119  $properties2 = $column2->toArray();
120 
121  if ($properties1['type'] instanceof BlobType || $properties1['type'] instanceof TextType) {
122  // Doctrine does not provide a length for LONGTEXT/LONGBLOB columns
123  $length1 = $properties1['length'] ?: 2147483647;
124  $length2 = $properties2['length'] ?: 2147483647;
125 
126  if ($length1 !== $length2) {
127  $changedProperties[] = 'length';
128  }
129  }
130 
131  return array_unique($changedProperties);
132  }
133 
154  public static function ‪compareSchemas(
155  Schema $fromSchema,
156  Schema $toSchema
157  ) {
158  $comparator = new self();
159  $diff = new SchemaDiff();
160  $diff->fromSchema = $fromSchema;
161 
162  $foreignKeysToTable = [];
163 
164  foreach ($toSchema->getNamespaces() as $namespace) {
165  if ($fromSchema->hasNamespace($namespace)) {
166  continue;
167  }
168 
169  $diff->newNamespaces[$namespace] = $namespace;
170  }
171 
172  foreach ($fromSchema->getNamespaces() as $namespace) {
173  if ($toSchema->hasNamespace($namespace)) {
174  continue;
175  }
176 
177  $diff->removedNamespaces[$namespace] = $namespace;
178  }
179 
180  foreach ($toSchema->getTables() as $table) {
181  $tableName = $table->getShortestName($toSchema->getName());
182  if (!$fromSchema->hasTable($tableName)) {
183  $diff->newTables[$tableName] = $toSchema->getTable($tableName);
184  } else {
185  $tableDifferences = $comparator->diffTable(
186  $fromSchema->getTable($tableName),
187  $toSchema->getTable($tableName)
188  );
189 
190  if ($tableDifferences !== false) {
191  $diff->changedTables[$tableName] = $tableDifferences;
192  }
193  }
194  }
195 
196  /* Check if there are tables removed */
197  foreach ($fromSchema->getTables() as $table) {
198  $tableName = $table->getShortestName($fromSchema->getName());
199 
200  $table = $fromSchema->getTable($tableName);
201  if (!$toSchema->hasTable($tableName)) {
202  $diff->removedTables[$tableName] = $table;
203  }
204 
205  // also remember all foreign keys that point to a specific table
206  foreach ($table->getForeignKeys() as $foreignKey) {
207  $foreignTable = strtolower($foreignKey->getForeignTableName());
208  if (!isset($foreignKeysToTable[$foreignTable])) {
209  $foreignKeysToTable[$foreignTable] = [];
210  }
211 
212  $foreignKeysToTable[$foreignTable][] = $foreignKey;
213  }
214  }
215 
216  foreach ($diff->removedTables as $tableName => $table) {
217  if (!isset($foreignKeysToTable[$tableName])) {
218  continue;
219  }
220 
221  $diff->orphanedForeignKeys = array_merge($diff->orphanedForeignKeys, $foreignKeysToTable[$tableName]);
222 
223  // deleting duplicated foreign keys present on both on the orphanedForeignKey
224  // and the removedForeignKeys from changedTables
225  foreach ($foreignKeysToTable[$tableName] as $foreignKey) {
226  // strtolower the table name to make if compatible with getShortestName
227  $localTableName = strtolower($foreignKey->getLocalTableName());
228  if (!isset($diff->changedTables[$localTableName])) {
229  continue;
230  }
231 
232  foreach ($diff->changedTables[$localTableName]->removedForeignKeys as $key => $removedForeignKey) {
233  assert($removedForeignKey instanceof ForeignKeyConstraint);
234 
235  // We check if the key is from the removed table if not we skip.
236  if ($tableName !== strtolower($removedForeignKey->getForeignTableName())) {
237  continue;
238  }
239 
240  unset($diff->changedTables[$localTableName]->removedForeignKeys[$key]);
241  }
242  }
243  }
244 
245  foreach ($toSchema->getSequences() as $sequence) {
246  $sequenceName = $sequence->getShortestName($toSchema->getName());
247  if (!$fromSchema->hasSequence($sequenceName)) {
248  if (!$comparator->isAutoIncrementSequenceInSchema($fromSchema, $sequence)) {
249  $diff->newSequences[] = $sequence;
250  }
251  } else {
252  if ($comparator->diffSequence($sequence, $fromSchema->getSequence($sequenceName))) {
253  $diff->changedSequences[] = $toSchema->getSequence($sequenceName);
254  }
255  }
256  }
257 
258  foreach ($fromSchema->getSequences() as $sequence) {
259  if ($comparator->isAutoIncrementSequenceInSchema($toSchema, $sequence)) {
260  continue;
261  }
262 
263  $sequenceName = $sequence->getShortestName($fromSchema->getName());
264 
265  if ($toSchema->hasSequence($sequenceName)) {
266  continue;
267  }
268 
269  $diff->removedSequences[] = $sequence;
270  }
271 
272  return $diff;
273  }
274 
281  private function ‪isAutoIncrementSequenceInSchema($schema, $sequence): bool
282  {
283  foreach ($schema->getTables() as $table) {
284  if ($sequence->isAutoIncrementsFor($table)) {
285  return true;
286  }
287  }
288 
289  return false;
290  }
291 }
‪TYPO3\CMS\Core\Database\Schema\Comparator\diffTable
‪false Doctrine DBAL Schema TableDiff TYPO3 CMS Core Database Schema TableDiff diffTable(Table $fromTable, Table $toTable)
Definition: Comparator.php:63
‪TYPO3\CMS\Core\Database\Schema\Comparator\__construct
‪__construct(AbstractPlatform $platform=null)
Definition: Comparator.php:47
‪TYPO3\CMS\Core\Database\Schema\Comparator\compareSchemas
‪static SchemaDiff compareSchemas(Schema $fromSchema, Schema $toSchema)
Definition: Comparator.php:153
‪TYPO3\CMS\Core\Database\Schema\Comparator
Definition: Comparator.php:39
‪TYPO3\CMS\Core\Database\Schema
Definition: Comparator.php:18
‪TYPO3\CMS\Core\Database\Schema\Comparator\$databasePlatform
‪AbstractPlatform null $databasePlatform
Definition: Comparator.php:42
‪TYPO3\CMS\Core\Utility\ArrayUtility
Definition: ArrayUtility.php:26
‪TYPO3\CMS\Core\Database\Schema\Comparator\isAutoIncrementSequenceInSchema
‪isAutoIncrementSequenceInSchema($schema, $sequence)
Definition: Comparator.php:280
‪TYPO3\CMS\Core\Database\Schema\TableDiff
Definition: TableDiff.php:27
‪TYPO3\CMS\Core\Database\Schema\Comparator\diffColumn
‪array diffColumn(Column $column1, Column $column2)
Definition: Comparator.php:108