‪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\MariaDBPlatform as DoctrineMariaDBPlatform;
22 use Doctrine\DBAL\Platforms\MySQLPlatform as DoctrineMySQLPlatform;
23 use Doctrine\DBAL\Schema\Column;
24 use Doctrine\DBAL\Schema\Schema;
25 use Doctrine\DBAL\Schema\Table;
26 use Doctrine\DBAL\Types\BinaryType;
27 use Doctrine\DBAL\Types\BlobType;
28 use Doctrine\DBAL\Types\DecimalType;
29 use Doctrine\DBAL\Types\GuidType;
30 use Doctrine\DBAL\Types\StringType;
31 use Doctrine\DBAL\Types\TextType;
33 
39 class ‪Comparator extends \Doctrine\DBAL\Schema\Comparator
40 {
41  protected AbstractPlatform ‪$databasePlatform;
42 
43  public function ‪__construct(protected AbstractPlatform $platform)
44  {
45  $this->databasePlatform = $platform;
46  parent::__construct($platform);
47  }
48 
49  public function ‪compareSchemas(Schema $oldSchema, Schema $newSchema): ‪SchemaDiff
50  {
51  return ‪SchemaDiff::ensure(parent::compareSchemas($oldSchema, $newSchema));
52  }
53 
59  public function ‪compareTables(Table $oldTable, Table $newTable): ‪TableDiff
60  {
61  $newTableOptions = array_merge($oldTable->getOptions(), $newTable->getOptions());
62  $optionDiff = ArrayUtility::arrayDiffAssocRecursive($newTableOptions, $oldTable->getOptions());
63  $tableDifferences = parent::compareTables($oldTable, $newTable);
64  // Rebuild TableDiff with enhanced TYPO3 TableDiff class
65  $tableDifferences = ‪TableDiff::ensure($tableDifferences);
66  // Set the table options to be parsed in the AlterTable event. Only add changed table options.
67  if (count($optionDiff) > 0) {
68  $tableDifferences->setTableOptions($optionDiff);
69  }
70  return $tableDifferences;
71  }
72 
80  protected function ‪columnsEqual(Column $column1, Column $column2): bool
81  {
82  // doctrine/dbal 4+ enforces the use of a platform and dispatches the column equal check to
83  // the used platform. That's a change in behaviour for TYPO3, therefore we reintroduce the
84  // old fallback without a set platform, the dropped `diffColumn()` methods.
85  //
86  // To avoid cloning the full `compareTables()` method code, we now override this transaction
87  // method, not dispatching to the Platform->columnsEqual() and using the preserved `diffColumn()`
88  // code chain.
89  //
90  // return parent::columnsEqual($column1, $column2);
91  return $this->‪typo3DiffColumn($column1, $column2) === [];
92  }
93 
106  public function ‪typo3DiffColumn(Column $column1, Column $column2): array
107  {
108  $changedProperties = $this->‪doctrineDbalMajorThreeDiffColumn($column1, $column2);
109  // Only MySQL has variable length versions of TEXT/BLOB
110  if (!($this->platform instanceof DoctrineMariaDBPlatform || $this->databasePlatform instanceof DoctrineMySQLPlatform)) {
111  return $changedProperties;
112  }
113  $properties1 = $column1->toArray();
114  $properties2 = $column2->toArray();
115  if ($properties1['type'] instanceof BlobType || $properties1['type'] instanceof TextType) {
116  // Doctrine does not provide a length for LONGTEXT/LONGBLOB columns
117  $length1 = $properties1['length'] ?: 2147483647;
118  $length2 = $properties2['length'] ?: 2147483647;
119 
120  if ($length1 !== $length2) {
121  $changedProperties[] = 'length';
122  }
123  }
124  return array_unique($changedProperties);
125  }
126 
139  public function ‪doctrineDbalMajorThreeDiffColumn(Column $column1, Column $column2): array
140  {
141  $properties1 = $column1->toArray();
142  $properties2 = $column2->toArray();
143 
144  $changedProperties = [];
145 
146  if (get_class($properties1['type']) !== get_class($properties2['type'])) {
147  $changedProperties[] = 'type';
148  }
149 
150  foreach (['notnull', 'unsigned', 'autoincrement'] as $property) {
151  if ($properties1[$property] === $properties2[$property]) {
152  continue;
153  }
154 
155  $changedProperties[] = $property;
156  }
157 
158  // Null values need to be checked additionally as they tell whether to create or drop a default value.
159  // null != 0, null != false, null != '' etc. This affects platform's table alteration SQL generation.
160  if (
161  ($properties1['default'] === null) !== ($properties2['default'] === null)
162  || $properties1['default'] != $properties2['default']
163  ) {
164  $changedProperties[] = 'default';
165  }
166 
167  if (
168  ($properties1['type'] instanceof StringType && !$properties1['type'] instanceof GuidType) ||
169  $properties1['type'] instanceof BinaryType
170  ) {
171  // check if value of length is set at all, default value assumed otherwise.
172  $length1 = $properties1['length'] ?? 255;
173  $length2 = $properties2['length'] ?? 255;
174  if ($length1 !== $length2) {
175  $changedProperties[] = 'length';
176  }
177 
178  if ($properties1['fixed'] !== $properties2['fixed']) {
179  $changedProperties[] = 'fixed';
180  }
181  } elseif ($properties1['type'] instanceof DecimalType) {
182  if (($properties1['precision'] ?? 10) !== ($properties2['precision'] ?? 10)) {
183  $changedProperties[] = 'precision';
184  }
185 
186  if ($properties1['scale'] !== $properties2['scale']) {
187  $changedProperties[] = 'scale';
188  }
189  }
190 
191  // A null value and an empty string are actually equal for a comment so they should not trigger a change.
192  if (
193  $properties1['comment'] !== $properties2['comment'] &&
194  !($properties1['comment'] === null && $properties2['comment'] === '') &&
195  !($properties2['comment'] === null && $properties1['comment'] === '')
196  ) {
197  $changedProperties[] = 'comment';
198  }
199 
200  $platformOptions1 = $column1->getPlatformOptions();
201  $platformOptions2 = $column2->getPlatformOptions();
202 
203  // NOTE: This is an important point, as only the overlapping platform option keys are compared. Doctrine DBAL
204  // 4.x comparison using the `columnsEqual()` method generate the create table statement type string for
205  // the column and comparing these strings. That means, that we cannot replace this old compare with the
206  // new one. At least this needs additional work and mainly for all extended platforms.
207  // @todo Invest time to evaluate this in Doctrine DBAL directly and getting a fix for it (3.x + 4.x)
208  foreach (array_keys(array_intersect_key($platformOptions1, $platformOptions2)) as $key) {
209  if ($properties1[$key] === $properties2[$key]) {
210  continue;
211  }
212 
213  $changedProperties[] = $key;
214  }
215 
216  return array_unique($changedProperties);
217  }
218 }
‪TYPO3\CMS\Core\Database\Schema\Comparator\doctrineDbalMajorThreeDiffColumn
‪string[] doctrineDbalMajorThreeDiffColumn(Column $column1, Column $column2)
Definition: Comparator.php:139
‪TYPO3\CMS\Core\Database\Schema\Comparator\$databasePlatform
‪AbstractPlatform $databasePlatform
Definition: Comparator.php:41
‪TYPO3\CMS\Core\Database\Schema\Comparator\compareTables
‪compareTables(Table $oldTable, Table $newTable)
Definition: Comparator.php:59
‪TYPO3\CMS\Core\Database\Schema\Comparator
Definition: Comparator.php:40
‪TYPO3\CMS\Core\Database\Schema
Definition: ColumnDiff.php:18
‪TYPO3\CMS\Core\Database\Schema\SchemaDiff\ensure
‪static ensure(SchemaDiff|DoctrineSchemaDiff $schemaDiff)
Definition: SchemaDiff.php:127
‪TYPO3\CMS\Core\Database\Schema\TableDiff\ensure
‪static ensure(DoctrineTableDiff|TableDiff $tableDiff)
Definition: TableDiff.php:213
‪TYPO3\CMS\Core\Database\Schema\Comparator\typo3DiffColumn
‪typo3DiffColumn(Column $column1, Column $column2)
Definition: Comparator.php:106
‪TYPO3\CMS\Core\Database\Schema\Comparator\__construct
‪__construct(protected AbstractPlatform $platform)
Definition: Comparator.php:43
‪TYPO3\CMS\Core\Database\Schema\Comparator\columnsEqual
‪columnsEqual(Column $column1, Column $column2)
Definition: Comparator.php:80
‪TYPO3\CMS\Core\Database\Schema\Comparator\compareSchemas
‪compareSchemas(Schema $oldSchema, Schema $newSchema)
Definition: Comparator.php:49
‪TYPO3\CMS\Core\Utility\ArrayUtility
Definition: ArrayUtility.php:26
‪TYPO3\CMS\Core\Database\Schema\SchemaDiff
Definition: SchemaDiff.php:33
‪TYPO3\CMS\Core\Database\Schema\TableDiff
Definition: TableDiff.php:33