TYPO3 v15 dev-main snapshot ()

This commit is contained in:
2026-08-10 22:31:09 +02:00
commit af8cc155b5
6818 changed files with 642608 additions and 0 deletions
+342
View File
@@ -0,0 +1,342 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Core\Database\Schema;
use Doctrine\DBAL\Schema\Column;
use Doctrine\DBAL\Schema\ColumnDiff;
use Doctrine\DBAL\Schema\Exception\InvalidState;
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
use Doctrine\DBAL\Schema\Index;
use Doctrine\DBAL\Schema\Name\UnqualifiedName;
use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Schema\TableDiff as DoctrineTableDiff;
/**
* Based on the doctrine/dbal implementation restoring direct property access
* and adding further helper methods.
*
* @internal not part of public core API.
*/
class TableDiff extends DoctrineTableDiff
{
/**
* $newTableName is a TYPO3 internal addition to handle renames at a later point.
*/
public ?string $newName = null;
/**
* Constructs a TableDiff object.
*
* @param array<string, Column> $addedColumns
* @param array<string, ColumnDiff> $changedColumns
* @param array<string, Column> $droppedColumns
* @param array<string, Index> $addedIndexes
* @param array<string, Index> $modifiedIndexes
* @param array<string, Index> $droppedIndexes
* @param array<string, Index> $renamedIndexes
* @param array<ForeignKeyConstraint> $addedForeignKeys
* @param array<ForeignKeyConstraint> $modifiedForeignKeys
* @param array<ForeignKeyConstraint> $droppedForeignKeys
* @param array<int|string, mixed> $tableOptions
*
* @internal The diff can be only instantiated by a {@see Comparator}.
*
* @todo Consider to change from array to typed collections with array access support.
*/
public function __construct(
public Table $oldTable,
public array $addedColumns = [],
public array $changedColumns = [],
public array $droppedColumns = [],
public array $addedIndexes = [],
public array $modifiedIndexes = [],
public array $droppedIndexes = [],
public array $renamedIndexes = [],
public array $addedForeignKeys = [],
public array $modifiedForeignKeys = [],
public array $droppedForeignKeys = [],
public array $tableOptions = [],
) {
// NOTE: parent::__construct() not called by intention.
}
/**
* Getter for table options.
*
* @return array<int|string, mixed>
*/
public function getTableOptions(): array
{
return $this->tableOptions;
}
/**
* Setter for table options
*
* @param array<int|string, mixed> $tableOptions
*/
public function setTableOptions(array $tableOptions): self
{
$this->tableOptions = $tableOptions;
return $this;
}
/**
* Check if a table options has been set.
*/
public function hasTableOption(string $optionName): bool
{
return array_key_exists($optionName, $this->tableOptions);
}
public function getTableOption(string $optionName): string
{
if ($this->hasTableOption($optionName)) {
return (string)$this->tableOptions[$optionName];
}
return '';
}
public function getOldTable(): Table
{
return $this->oldTable;
}
/** @return array<string, Column> */
public function getAddedColumns(): array
{
return $this->addedColumns;
}
/** @return array<string, ColumnDiff> */
public function getChangedColumns(): array
{
return $this->changedColumns;
}
/** @return array<string, Column> */
public function getDroppedColumns(): array
{
return $this->droppedColumns;
}
/** @return array<string, Index> */
public function getAddedIndexes(): array
{
return $this->addedIndexes;
}
/**
* @deprecated Use {@see getAddedIndexes()} and {@see getDroppedIndexes()} instead.
*
* @return array<string, Index>
*/
public function getModifiedIndexes(): array
{
return $this->modifiedIndexes;
}
/** @return array<string, Index> */
public function getDroppedIndexes(): array
{
return $this->droppedIndexes;
}
/** @return array<string,Index> */
public function getRenamedIndexes(): array
{
return $this->renamedIndexes;
}
/** @return array<ForeignKeyConstraint> */
public function getAddedForeignKeys(): array
{
return $this->addedForeignKeys;
}
/**
* @deprecated Use {@see getAddedForeignKeys()} and {@see getDroppedForeignKeys()} instead.
*
* @return array<ForeignKeyConstraint>
*/
public function getModifiedForeignKeys(): array
{
return $this->modifiedForeignKeys;
}
/**
* @deprecated Use {@see getDroppedForeignKeyConstraintNames()}.
*
* @return array<ForeignKeyConstraint>
*/
public function getDroppedForeignKeys(): array
{
return $this->droppedForeignKeys;
}
/**
* Overridden, because the parent implementation reads the parent property directly. As the parent
* constructor is not called by intention, that property is never initialized, making the inherited
* method raise an `Error`. Reads the redeclared property here instead, keeping the parent behaviour.
*
* @return array<UnqualifiedName>
*/
public function getDroppedForeignKeyConstraintNames(): array
{
$names = [];
foreach ($this->droppedForeignKeys as $droppedForeignKey) {
$name = $droppedForeignKey->getObjectName();
if ($name === null) {
throw InvalidState::tableDiffContainsUnnamedDroppedForeignKeyConstraints();
}
$names[] = $name;
}
return $names;
}
public function isEmpty(): bool
{
return count($this->getAddedColumns()) === 0
&& count($this->getChangedColumns()) === 0
&& count($this->getDroppedColumns()) === 0
&& count($this->getAddedIndexes()) === 0
&& count($this->getModifiedIndexes()) === 0
&& count($this->getDroppedIndexes()) === 0
&& count($this->getRenamedIndexes()) === 0
&& count($this->getAddedForeignKeys()) === 0
&& count($this->getModifiedForeignKeys()) === 0
&& count($this->getDroppedForeignKeys()) === 0
// doctrine/dbal 4.x removed the newName. TYPO3 needs that to provide a rename to prefix logic before
// really dropping tables instead. Therefore, we need to add here an empty check for the reintroduced
// property.See for example: ConnectionMigrator->migrateUnprefixedRemovedTablesToRenames
&& $this->getNewName() !== null && $this->getNewName() !== ''
&& $this->getTableOptions() === [];
}
public function getNewName(): ?string
{
return $this->newName;
}
public static function ensure(DoctrineTableDiff|TableDiff $tableDiff): self
{
$diff = new self(
// oldTable
$tableDiff->getOldTable(),
// addedColumns
$tableDiff->getAddedColumns(),
// changedColumns
[],
// droppedColumns
$tableDiff->getDroppedColumns(),
// addedIndexes
$tableDiff->getAddedIndexes(),
// modifiedIndexes
[],
// droppedIndexes
$tableDiff->getDroppedIndexes(),
// renamedIndexes
$tableDiff->getRenamedIndexes(),
// addedForeignKeys
$tableDiff->getAddedForeignKeys(),
// modifiedForeignKeys
$tableDiff->getModifiedForeignKeys(),
// droppedForeignKeys
$tableDiff->getDroppedForeignKeys(),
// tableOptions
($tableDiff instanceof TableDiff ? $tableDiff->tableOptions : []),
);
// doctrine/dbal 4+ removed the column name as array index for modified column definitions,
// but we rely on it. Restore it !
// Ensure to use custom ColumnDiff instance with more data and
foreach ($tableDiff->getChangedColumns() as $changedColumn) {
$diff->changedColumns[$changedColumn->getOldColumn()->getName()] = new ColumnDiff(
// oldColumn
$changedColumn->getOldColumn(),
// newColumn
$changedColumn->getNewColumn(),
);
}
// doctrine/dbal 4+ removed the index name as array index for modified index definitions,
// but we rely on it. Restore it !.
foreach ($tableDiff->getModifiedIndexes() as $modifiedIndex) {
$diff->modifiedIndexes[$modifiedIndex->getName()] = $modifiedIndex;
}
// Accumulate modified index separated into added and dropped information to modifiedIndexes again,
// otherwise required drop action may not be executed before trying to add an existing index first.
// Required for planned doctrine/dbal 4.3.0 change (deprecation) and currently breaking with an open
// discussion to mitigate that before dbal release. We still prepare for this case to be on the safer
// side here.
// Needs to be done in a two-step strategy to avoid changing array while iterating over it.
// - https://github.com/doctrine/dbal/pull/6831
// - https://github.com/doctrine/dbal/issues/6880
/**
* @var array<int, array{added: Index, dropped: Index}> $transformIndexOperations
*/
$transformIndexOperations = [];
foreach ($diff->getAddedIndexes() as $addedIndex) {
foreach ($diff->getDroppedIndexes() as $droppedIndex) {
if ($droppedIndex->getName() === $addedIndex->getName()) {
$transformIndexOperations[] = [
'added' => $addedIndex,
'dropped' => $droppedIndex,
];
}
}
}
foreach ($transformIndexOperations as $data) {
$diff->unsetAddedIndex($data['added']);
$diff->unsetDroppedIndex($data['dropped']);
$diff->modifiedIndexes[$data['added']->getName()] = $data['added'];
}
return $diff;
}
/**
* @internal This method exists only for compatibility with the current implementation of schema managers
* that modify the diff while processing it.
*/
public function unsetAddedIndex(Index $index): void
{
$this->addedIndexes = array_filter(
$this->addedIndexes,
static function (Index $addedIndex) use ($index): bool {
return $addedIndex !== $index;
},
);
}
/**
* @internal This method exists only for compatibility with the current implementation of schema managers
* that modify the diff while processing it.
*/
public function unsetDroppedIndex(Index $index): void
{
$this->droppedIndexes = array_filter(
$this->droppedIndexes,
static function (Index $droppedIndex) use ($index): bool {
return $droppedIndex !== $index;
},
);
}
}