Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
7c95aea
add using when changing column type to json (#1031)
swiffer Mar 3, 2026
a492413
Fix CI failures on MySQL/MariaDB (#1034)
dereuromark Mar 5, 2026
55ea1a0
Fix TEXT column variants not round-tripping correctly (#1032)
dereuromark Mar 5, 2026
52b91cf
Add fixed option for binary column type (#1014)
dereuromark Mar 7, 2026
9486d80
Fix misleading next steps message in upgrade command (#1037)
dereuromark Mar 7, 2026
4c4fb53
Fix upgrade command not matching plugins with slashes (#1039)
dereuromark Mar 7, 2026
b656599
Add TYPE_BIT constant to AdapterInterface (#1013)
dereuromark Mar 7, 2026
2d358ab
Bump PHPStan level +1
Feb 24, 2026
bc6d628
Make props non-nullable
Feb 24, 2026
a97592f
Add null guards for getConstraint() in BakeMigrationDiffCommand
Mar 11, 2026
5ddb790
Fix nullable return types in Column and ForeignKey
Feb 24, 2026
7a17b46
Fix null safety in Db adapters and domain classes
Feb 24, 2026
6f47568
Fix remaining PHPStan level 8 null safety issues
Mar 11, 2026
2e5c1cd
Replace assert with RuntimeException in AbstractAdapter::getConnection()
Feb 24, 2026
c93988e
Use truthiness checks for optional nullable getters
Mar 11, 2026
a5e25f3
Throw on null for required Column::getName() and FK::getReferencedTab…
Feb 24, 2026
61d71c2
Narrow ForeignKey::getColumns() return type and remove null guards
Mar 10, 2026
728ed6b
Narrow Column::getName() return type and remove dead null checks
Mar 11, 2026
55c53d6
Add null guards for column diff loop in BakeMigrationDiffCommand
Mar 11, 2026
90e0c17
Cast nullable getName() to string for drop FK/index instructions
Mar 11, 2026
c5e69fc
Fix phpcs coding standard violations
jamisonbryant Mar 11, 2026
5198c18
Merge branch '5.next' into fix/phpstan-level-8-v2
jamisonbryant Mar 11, 2026
e9045c9
Consolidate null guard conditions in BakeMigrationDiffCommand
jamisonbryant Mar 11, 2026
611e1bb
Fix inverted condition in ChangeColumn name fallback
jamisonbryant Mar 11, 2026
1d4bf94
Accept null in Column::setFixed() for snapshot compatibility
jamisonbryant Mar 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ includes:
- phpstan-baseline.neon

parameters:
level: 7
level: 8
paths:
- src/
bootstrapFiles:
Expand Down
4 changes: 2 additions & 2 deletions src/BaseSeed.php
Original file line number Diff line number Diff line change
Expand Up @@ -278,8 +278,8 @@ protected function runCall(string $seeder, array $options = []): void

$options += [
'connection' => $connection,
'plugin' => $pluginName ?? $config['plugin'],
'source' => $config['source'],
'plugin' => $pluginName ?? ($config !== null ? $config['plugin'] : null),
'source' => $config !== null ? $config['source'] : null,
];
$factory = new ManagerFactory([
'connection' => $options['connection'],
Expand Down
32 changes: 23 additions & 9 deletions src/Command/BakeMigrationDiffCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -279,18 +279,23 @@ protected function getColumns(): void
// changes in columns meta-data
foreach ($currentColumns as $columnName) {
$column = $this->safeGetColumn($currentSchema, $columnName);
if ($column === null || !in_array($columnName, $oldColumns, true)) {
continue;
}

$oldColumn = $this->safeGetColumn($this->dumpSchema[$table], $columnName);
if ($oldColumn === null) {
continue;
}

unset(
$column['collate'],
$column['fixed'],
$oldColumn['collate'],
$oldColumn['fixed'],
);

if (
in_array($columnName, $oldColumns, true) &&
$column !== $oldColumn
) {
if ($column !== $oldColumn) {
$changedAttributes = array_diff_assoc($column, $oldColumn);

foreach (['type', 'length', 'null', 'default'] as $attribute) {
Expand Down Expand Up @@ -385,9 +390,10 @@ protected function getConstraints(): void
// brand new constraints
$addedConstraints = array_diff($currentConstraints, $oldConstraints);
foreach ($addedConstraints as $constraintName) {
$this->templateData[$table]['constraints']['add'][$constraintName] =
$currentSchema->getConstraint($constraintName);
$constraint = $currentSchema->getConstraint($constraintName);
if ($constraint === null) {
continue;
}
if ($constraint['type'] === TableSchema::CONSTRAINT_FOREIGN) {
$this->templateData[$table]['constraints']['add'][$constraintName] = $constraint;
} else {
Expand All @@ -399,13 +405,18 @@ protected function getConstraints(): void
// if present in both, check if they are the same : if not, remove the old one and add the new one
foreach ($currentConstraints as $constraintName) {
$constraint = $currentSchema->getConstraint($constraintName);
if ($constraint === null) {
continue;
}

$oldConstraint = $this->dumpSchema[$table]->getConstraint($constraintName);
if (
in_array($constraintName, $oldConstraints, true) &&
$constraint !== $this->dumpSchema[$table]->getConstraint($constraintName)
$constraint !== $oldConstraint
) {
$this->templateData[$table]['constraints']['remove'][$constraintName] =
$this->dumpSchema[$table]->getConstraint($constraintName);
if ($oldConstraint !== null) {
$this->templateData[$table]['constraints']['remove'][$constraintName] = $oldConstraint;
}
$this->templateData[$table]['constraints']['add'][$constraintName] =
$constraint;
}
Expand All @@ -415,6 +426,9 @@ protected function getConstraints(): void
$removedConstraints = array_diff($oldConstraints, $currentConstraints);
foreach ($removedConstraints as $constraintName) {
$constraint = $this->dumpSchema[$table]->getConstraint($constraintName);
if ($constraint === null) {
continue;
}
if ($constraint['type'] === TableSchema::CONSTRAINT_FOREIGN) {
$this->templateData[$table]['constraints']['remove'][$constraintName] = $constraint;
} else {
Expand Down
8 changes: 4 additions & 4 deletions src/Command/BakeSimpleMigrationCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,16 +52,16 @@ abstract class BakeSimpleMigrationCommand extends SimpleBakeCommand
/**
* Console IO
*
* @var \Cake\Console\ConsoleIo|null
* @var \Cake\Console\ConsoleIo
*/
protected ?ConsoleIo $io = null;
protected ConsoleIo $io;

/**
* Arguments
*
* @var \Cake\Console\Arguments|null
* @var \Cake\Console\Arguments
*/
protected ?Arguments $args = null;
protected Arguments $args;

/**
* @inheritDoc
Expand Down
2 changes: 1 addition & 1 deletion src/Db/Action/ChangeColumn.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public function __construct(TableMetadata $table, string $columnName, Column $co
$this->column = $column;

// if the name was omitted use the existing column name
if ($column->getName() === null || strlen((string)$column->getName()) === 0) {
if (!$column->getName()) {
$column->setName($columnName);
}
}
Expand Down
13 changes: 9 additions & 4 deletions src/Db/Adapter/AbstractAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,9 @@ public function getConnection(): Connection
$this->connection = $this->getOption('connection');
$this->connect();
}
if ($this->connection === null) {
throw new RuntimeException('Unable to establish database connection. Ensure a connection is configured.');
}

return $this->connection;
}
Expand Down Expand Up @@ -1687,17 +1690,19 @@ public function executeActions(TableMetadata $table, array $actions): void

case $action instanceof DropForeignKey && $action->getForeignKey()->getName():
/** @var \Migrations\Db\Action\DropForeignKey $action */
$fkName = (string)$action->getForeignKey()->getName();
$instructions->merge($this->getDropForeignKeyInstructions(
$table->getName(),
(string)$action->getForeignKey()->getName(),
$fkName,
));
break;

case $action instanceof DropIndex && $action->getIndex()->getName():
/** @var \Migrations\Db\Action\DropIndex $action */
$indexName = (string)$action->getIndex()->getName();
$instructions->merge($this->getDropIndexByNameInstructions(
$table->getName(),
(string)$action->getIndex()->getName(),
$indexName,
));
break;

Expand All @@ -1720,15 +1725,15 @@ public function executeActions(TableMetadata $table, array $actions): void
/** @var \Migrations\Db\Action\RemoveColumn $action */
$instructions->merge($this->getDropColumnInstructions(
$table->getName(),
(string)$action->getColumn()->getName(),
$action->getColumn()->getName(),
));
break;

case $action instanceof RenameColumn:
/** @var \Migrations\Db\Action\RenameColumn $action */
$instructions->merge($this->getRenameColumnInstructions(
$table->getName(),
(string)$action->getColumn()->getName(),
$action->getColumn()->getName(),
$action->getNewName(),
));
break;
Expand Down
11 changes: 8 additions & 3 deletions src/Db/Adapter/MysqlAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -1197,8 +1197,9 @@ protected function getIndexSqlDefinition(Index $index): string
protected function getForeignKeySqlDefinition(ForeignKey $foreignKey): string
{
$def = '';
if ($foreignKey->getName()) {
$def .= ' CONSTRAINT ' . $this->quoteColumnName((string)$foreignKey->getName());
$name = $foreignKey->getName();
if ($name) {
$def .= ' CONSTRAINT ' . $this->quoteColumnName($name);
}
$columnNames = [];
foreach ($foreignKey->getColumns() as $column) {
Expand All @@ -1209,7 +1210,11 @@ protected function getForeignKeySqlDefinition(ForeignKey $foreignKey): string
foreach ($foreignKey->getReferencedColumns() as $column) {
$refColumnNames[] = $this->quoteColumnName($column);
}
$def .= ' REFERENCES ' . $this->quoteTableName($foreignKey->getReferencedTable()) . ' (' . implode(',', $refColumnNames) . ')';
$referencedTable = $foreignKey->getReferencedTable();
if ($referencedTable === null) {
throw new InvalidArgumentException('Foreign key must have a referenced table.');
}
$def .= ' REFERENCES ' . $this->quoteTableName($referencedTable) . ' (' . implode(',', $refColumnNames) . ')';
$onDelete = $foreignKey->getOnDelete();
if ($onDelete) {
$def .= ' ON DELETE ' . $onDelete;
Expand Down
40 changes: 24 additions & 16 deletions src/Db/Adapter/PostgresAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -455,9 +455,9 @@ protected function getChangeColumnInstructions(

$columnSql = $dialect->columnDefinitionSql($this->mapColumnData($newColumn->toArray()));
// Remove the column name from $columnSql
$columnType = preg_replace('/^"?(?:[^"]+)"?\s+/', '', $columnSql);
$columnType = (string)preg_replace('/^"?(?:[^"]+)"?\s+/', '', $columnSql);
// Remove generated clause
$columnType = preg_replace('/GENERATED (?:ALWAYS|BY DEFAULT) AS IDENTITY/', '', $columnType);
$columnType = (string)preg_replace('/GENERATED (?:ALWAYS|BY DEFAULT) AS IDENTITY/', '', $columnType);

$sql = sprintf(
'ALTER COLUMN %s TYPE %s',
Expand All @@ -483,10 +483,10 @@ protected function getChangeColumnInstructions(
);
}
// NULL and DEFAULT cannot be set while changing column type
$sql = preg_replace('/ NOT NULL/', '', $sql);
$sql = preg_replace('/ DEFAULT NULL/', '', $sql);
$sql = (string)preg_replace('/ NOT NULL/', '', $sql);
$sql = (string)preg_replace('/ DEFAULT NULL/', '', $sql);
// If it is set, DEFAULT is the last definition
$sql = preg_replace('/DEFAULT .*/', '', $sql);
$sql = (string)preg_replace('/DEFAULT .*/', '', $sql);
if ($newColumn->getType() === 'boolean') {
$sql .= sprintf(
' USING (CASE WHEN %s IS NULL THEN NULL WHEN %s::int=0 THEN FALSE ELSE TRUE END)',
Expand All @@ -505,11 +505,12 @@ protected function getChangeColumnInstructions(
'ALTER COLUMN %s',
$quotedColumnName,
);
if ($newColumn->isIdentity() && $newColumn->getGenerated() !== null) {
$generated = $newColumn->getGenerated();
if ($newColumn->isIdentity() && $generated !== null) {
if ($column->isIdentity()) {
$sql .= sprintf(' SET GENERATED %s', (string)$newColumn->getGenerated());
$sql .= sprintf(' SET GENERATED %s', $generated);
} else {
$sql .= sprintf(' ADD GENERATED %s AS IDENTITY', (string)$newColumn->getGenerated());
$sql .= sprintf(' ADD GENERATED %s AS IDENTITY', $generated);
}
} else {
$sql .= ' DROP IDENTITY IF EXISTS';
Expand Down Expand Up @@ -546,12 +547,13 @@ protected function getChangeColumnInstructions(
}

// rename column
if ($columnName !== $newColumn->getName()) {
$newColumnName = $newColumn->getName();
if ($columnName !== $newColumnName) {
$instructions->addPostStep(sprintf(
'ALTER TABLE %s RENAME COLUMN %s TO %s',
$this->quoteTableName($tableName),
$quotedColumnName,
$this->quoteColumnName((string)$newColumn->getName()),
$this->quoteColumnName($newColumnName),
));
}

Expand Down Expand Up @@ -873,6 +875,7 @@ public function dropDatabase($name): void
*/
protected function getColumnCommentSqlDefinition(Column $column, string $tableName): string
{
$columnName = $column->getName();
$comment = (string)$column->getComment();
// passing 'null' is to remove column comment
$comment = strcasecmp($comment, 'NULL') !== 0
Expand All @@ -882,7 +885,7 @@ protected function getColumnCommentSqlDefinition(Column $column, string $tableNa
return sprintf(
'COMMENT ON COLUMN %s.%s IS %s;',
$this->quoteTableName($tableName),
$this->quoteColumnName((string)$column->getName()),
$this->quoteColumnName($columnName),
$comment,
);
}
Expand Down Expand Up @@ -923,9 +926,10 @@ protected function getIndexSqlDefinition(Index $index, string $tableName): strin
} else {
$createIndexSentence .= '(%s)%s%s;';
}
$where = (string)$index->getWhere();
if ($where) {
$where = ' WHERE ' . $where;
$where = '';
$whereClause = $index->getWhere();
if ($whereClause) {
$where = ' WHERE ' . $whereClause;
}

return sprintf(
Expand Down Expand Up @@ -956,9 +960,13 @@ protected function getForeignKeySqlDefinition(ForeignKey $foreignKey, string $ta
);
$columnList = implode(', ', array_map($this->quoteColumnName(...), $foreignKey->getColumns()));
$refColumnList = implode(', ', array_map($this->quoteColumnName(...), $foreignKey->getReferencedColumns()));
$referencedTable = $foreignKey->getReferencedTable();
if ($referencedTable === null) {
throw new InvalidArgumentException('Foreign key must have a referenced table.');
}
$def = ' CONSTRAINT ' . $this->quoteColumnName($constraintName) .
' FOREIGN KEY (' . $columnList . ')' .
' REFERENCES ' . $this->quoteTableName($foreignKey->getReferencedTable()) . ' (' . $refColumnList . ')';
' REFERENCES ' . $this->quoteTableName($referencedTable) . ' (' . $refColumnList . ')';
if ($foreignKey->getOnDelete()) {
$def .= " ON DELETE {$foreignKey->getOnDelete()}";
}
Expand Down Expand Up @@ -1327,7 +1335,7 @@ protected function getConflictClause(
}
$quotedConflictColumns = array_map($this->quoteColumnName(...), $conflictColumns);
$updates = [];
foreach ($updateColumns as $column) {
foreach ($updateColumns ?? [] as $column) {
$quotedColumn = $this->quoteColumnName($column);
$updates[] = $quotedColumn . ' = EXCLUDED.' . $quotedColumn;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Db/Adapter/RecordingAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public function getInvertedCommands(): Intent
case $command instanceof RenameColumn:
/** @var \Migrations\Db\Action\RenameColumn $command */
$column = clone $command->getColumn();
$name = (string)$column->getName();
$name = $column->getName();
$column->setName($command->getNewName());
$inverted->addAction(new RenameColumn($command->getTable(), $column, $name));
break;
Expand Down
Loading
Loading