diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 91c54862..fac01bed 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -125,7 +125,7 @@ jobs: - name: Code Coverage Report if: success() && matrix.php-version == '8.1' && matrix.db-type == 'mysql' - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v5 testsuite-windows: runs-on: windows-2022 @@ -195,7 +195,7 @@ jobs: vendor/bin/phpunit --coverage-clover=coverage.xml - name: Submit code coverage - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v5 cs-stan: uses: cakephp/.github/.github/workflows/cs-stan.yml@5.x diff --git a/.phive/phars.xml b/.phive/phars.xml index 726b7777..d311bfa5 100644 --- a/.phive/phars.xml +++ b/.phive/phars.xml @@ -1,5 +1,5 @@ - - + + diff --git a/.stickler.yml b/.stickler.yml deleted file mode 100644 index 0ec83f6f..00000000 --- a/.stickler.yml +++ /dev/null @@ -1,5 +0,0 @@ -linters: - phpcs: - standard: CakePHP -files: - ignore: ['tests/bootstrap.php', 'tests/comparisons/*'] diff --git a/README.md b/README.md index 0aecc0f0..0b396e83 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ bin/cake plugin load Migrations ``` Or you can manually add the loading statement in the **src/Application.php** file of your application: + ```php public function bootstrap(): void { @@ -37,7 +38,20 @@ public function bootstrap(): void } ``` -Additionally, you will need to configure the ``default`` database configuration in your **config/app.php** file. +### Enabling the builtin backend + +In a future release, migrations will be switching to a new backend based on the CakePHP ORM. We're aiming +to be compatible with as many existing migrations as possible, and could use your feedback. Enable the +new backend with: + +```php +// in app/config/app_local.php +$config = [ + // Other configuration + 'Migrations' => ['backend' => 'builtin'], +]; + +``` ## Documentation diff --git a/docs/en/upgrading-to-builtin-backend.rst b/docs/en/upgrading-to-builtin-backend.rst index 4731cc22..b1837263 100644 --- a/docs/en/upgrading-to-builtin-backend.rst +++ b/docs/en/upgrading-to-builtin-backend.rst @@ -2,7 +2,8 @@ Upgrading to the builtin backend ################################ As of migrations 4.3 there is a new migrations backend that uses CakePHP's -database abstractions and ORM. Longer term this will allow for phinx to be +database abstractions and ORM. In 4.4, the ``builtin`` backend became the +default backend. Longer term this will allow for phinx to be removed as a dependency. This greatly reduces the dependency footprint of migrations. @@ -42,16 +43,16 @@ Similar changes are for fetching a single row:: $stmt = $this->getAdapter()->query('SELECT * FROM articles'); $rows = $stmt->fetch('assoc'); -Enabling the new backend -======================== +Problems with the new backend? +============================== -The new backend can be enabled through application configuration. Add the +The new backend is enabled by default. If your migrations contain errors when +run with the builtin backend, please open `an issue +`_. You can also switch back +to the ``phinx`` backend through application configuration. Add the following to your ``config/app.php``:: return [ // Other configuration. - 'Migrations' => ['backend' => 'builtin'], + 'Migrations' => ['backend' => 'phinx'], ]; - -If your migrations have problems running with the builtin backend, removing this -configuration option will revert to using phinx. diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 49a54904..f5dae26c 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,71 +1,169 @@ parameters: ignoreErrors: - - message: "#^Call to an undefined method object\\:\\:loadHelper\\(\\)\\.$#" + message: '#^Call to an undefined method object\:\:loadHelper\(\)\.$#' + identifier: method.notFound count: 1 path: src/Command/BakeMigrationCommand.php - - message: "#^Call to an undefined method object\\:\\:loadHelper\\(\\)\\.$#" + message: '#^Parameter \#1 \$arguments of method Migrations\\Util\\ColumnParser\:\:parseFields\(\) expects array\, array\\|int\<1, max\>, list\\|string\> given\.$#' + identifier: argument.type + count: 1 + path: src/Command/BakeMigrationCommand.php + + - + message: '#^Parameter \#1 \$arguments of method Migrations\\Util\\ColumnParser\:\:parseIndexes\(\) expects array\, array\\|int\<1, max\>, list\\|string\> given\.$#' + identifier: argument.type + count: 1 + path: src/Command/BakeMigrationCommand.php + + - + message: '#^Parameter \#1 \$arguments of method Migrations\\Util\\ColumnParser\:\:parsePrimaryKey\(\) expects array\, array\\|int\<1, max\>, list\\|string\> given\.$#' + identifier: argument.type + count: 1 + path: src/Command/BakeMigrationCommand.php + + - + message: '#^Call to an undefined method object\:\:loadHelper\(\)\.$#' + identifier: method.notFound count: 1 path: src/Command/BakeMigrationDiffCommand.php - - message: "#^Call to an undefined method object\\:\\:loadHelper\\(\\)\\.$#" + message: '#^Call to an undefined method object\:\:loadHelper\(\)\.$#' + identifier: method.notFound count: 1 path: src/Command/BakeMigrationSnapshotCommand.php - - message: "#^Unsafe usage of new static\\(\\)\\.$#" + message: '#^PHPDoc tag @var with type string is not subtype of native type non\-falsy\-string\|true\.$#' + identifier: varTag.nativeType + count: 1 + path: src/Command/BakeSeedCommand.php + + - + message: '#^Strict comparison using \!\=\= between string and false will always evaluate to true\.$#' + identifier: notIdentical.alwaysTrue + count: 1 + path: src/Command/BakeSeedCommand.php + + - + message: '#^Call to an undefined method Cake\\Datasource\\ConnectionInterface\:\:cacheMetadata\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Command/Phinx/CacheBuild.php + + - + message: '#^Call to an undefined method Cake\\Datasource\\ConnectionInterface\:\:cacheMetadata\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/Command/Phinx/CacheClear.php + + - + message: '#^Unsafe usage of new static\(\)\.$#' + identifier: new.static count: 1 path: src/Db/Adapter/AdapterFactory.php - - message: "#^Offset 'id' on non\\-empty\\-array\\ in isset\\(\\) always exists and is not nullable\\.$#" + message: '#^Offset ''id'' on non\-empty\-array\ in isset\(\) always exists and is not nullable\.$#' + identifier: isset.offset count: 2 path: src/Db/Adapter/MysqlAdapter.php - - message: "#^Right side of && is always true\\.$#" + message: '#^Offset 3 might not exist on array\{0\: string, 1\: non\-empty\-string, 2\?\: string, 3\?\: ''''\|numeric\-string, 4\?\: string, 5\?\: ''''\|numeric\-string, 6\?\: non\-empty\-string\}\.$#' + identifier: offsetAccess.notFound count: 1 path: src/Db/Adapter/MysqlAdapter.php - - message: "#^Access to an undefined property Cake\\\\Database\\\\Connection\\:\\:\\$connection\\.$#" + message: '#^Offset 5 might not exist on array\{0\: string, 1\: non\-empty\-string, 2\?\: string, 3\?\: ''''\|numeric\-string, 4\?\: string, 5\?\: ''''\|numeric\-string, 6\?\: non\-empty\-string\}\.$#' + identifier: offsetAccess.notFound count: 1 - path: src/Db/Adapter/PdoAdapter.php + path: src/Db/Adapter/MysqlAdapter.php - - message: "#^Offset 'id' on array\\ in isset\\(\\) always exists and is not nullable\\.$#" - count: 2 - path: src/Db/Adapter/PostgresAdapter.php + message: '#^Offset 6 might not exist on array\{0\: string, 1\: non\-empty\-string, 2\?\: string, 3\?\: ''''\|numeric\-string, 4\?\: string, 5\?\: ''''\|numeric\-string, 6\?\: non\-empty\-string\}\.$#' + identifier: offsetAccess.notFound + count: 1 + path: src/Db/Adapter/MysqlAdapter.php - - message: "#^Offset 'id' on array\\ in isset\\(\\) always exists and is not nullable\\.$#" - count: 2 - path: src/Db/Adapter/SqliteAdapter.php + message: '#^Right side of && is always true\.$#' + identifier: booleanAnd.rightAlwaysTrue + count: 1 + path: src/Db/Adapter/MysqlAdapter.php - - message: "#^Offset 'id' on array\\ in isset\\(\\) always exists and is not nullable\\.$#" - count: 2 - path: src/Db/Adapter/SqlserverAdapter.php + message: '#^Right side of && is always true\.$#' + identifier: booleanAnd.rightAlwaysTrue + count: 1 + path: src/Db/Adapter/SqliteAdapter.php - - message: "#^PHPDoc tag @return with type Phinx\\\\Db\\\\Adapter\\\\AdapterInterface is not subtype of native type Migrations\\\\Db\\\\Adapter\\\\AdapterInterface\\.$#" + message: '#^PHPDoc tag @return with type Phinx\\Db\\Adapter\\AdapterInterface is not subtype of native type Migrations\\Db\\Adapter\\AdapterInterface\.$#' + identifier: return.phpDocType count: 1 path: src/Db/Adapter/SqlserverAdapter.php - - message: "#^Ternary operator condition is always true\\.$#" + message: '#^Ternary operator condition is always true\.$#' + identifier: ternary.alwaysTrue count: 2 path: src/Db/Adapter/SqlserverAdapter.php - - message: "#^Method Migrations\\\\Shim\\\\OutputAdapter\\:\\:getVerbosity\\(\\) should return 16\\|32\\|64\\|128\\|256 but returns int\\.$#" + message: '#^Parameter \#1 \$message of method Cake\\Console\\ConsoleIo\:\:verbose\(\) expects list\\|string, array\ given\.$#' + identifier: argument.type + count: 1 + path: src/Migration/Manager.php + + - + message: '#^Parameter \#1 \.\.\.\$arg1 of function max expects non\-empty\-array, array given\.$#' + identifier: argument.type + count: 1 + path: src/Migration/Manager.php + + - + message: '#^PHPDoc tag @var with type array\ is not subtype of native type mixed\.$#' + identifier: varTag.nativeType + count: 1 + path: src/Migration/ManagerFactory.php + + - + message: '#^Method Migrations\\Shim\\OutputAdapter\:\:getVerbosity\(\) should return 16\|32\|64\|128\|256 but returns int\.$#' + identifier: return.type count: 1 path: src/Shim/OutputAdapter.php - - message: "#^Possibly invalid array key type Cake\\\\Database\\\\Schema\\\\TableSchemaInterface\\|string\\.$#" + message: '#^Parameter \#1 \$message of method Cake\\Console\\ConsoleIo\:\:out\(\) expects list\\|string, array\|string given\.$#' + identifier: argument.type + count: 2 + path: src/Shim/OutputAdapter.php + + - + message: '#^Parameter \#2 \$tables of method Cake\\TestSuite\\ConnectionHelper\:\:dropTables\(\) expects list\\|null, non\-empty\-array\ given\.$#' + identifier: argument.type + count: 1 + path: src/TestSuite/Migrator.php + + - + message: '#^Parameter \#2 \$tables of method Cake\\TestSuite\\ConnectionHelper\:\:truncateTables\(\) expects list\\|null, non\-empty\-array\ given\.$#' + identifier: argument.type + count: 2 + path: src/TestSuite/Migrator.php + + - + message: '#^Offset 0 on non\-empty\-list\ in isset\(\) always exists and is not nullable\.$#' + identifier: isset.offset + count: 2 + path: src/Util/TableFinder.php + + - + message: '#^Possibly invalid array key type Cake\\Database\\Schema\\TableSchemaInterface\|string\.$#' + identifier: offsetAccess.invalidOffset count: 2 path: src/View/Helper/MigrationHelper.php diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 58309755..5b7121ab 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -59,6 +59,10 @@ + + + + @@ -82,6 +86,23 @@ + + + + + getManager()->maxNameLength]]> + + + + + + + + + + + + @@ -224,4 +245,9 @@ regexpParseField]]> + + + + + diff --git a/psalm.xml b/psalm.xml index 738d8fcd..54311675 100644 --- a/psalm.xml +++ b/psalm.xml @@ -5,7 +5,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="https://getpsalm.org/schema/config" xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd" - errorBaseline="./psalm-baseline.xml" + errorBaseline="psalm-baseline.xml" autoloader="tests/bootstrap.php" findUnusedPsalmSuppress="true" findUnusedBaselineEntry="true" diff --git a/src/Command/BakeMigrationDiffCommand.php b/src/Command/BakeMigrationDiffCommand.php index 1f076f0b..3cef914a 100644 --- a/src/Command/BakeMigrationDiffCommand.php +++ b/src/Command/BakeMigrationDiffCommand.php @@ -31,8 +31,6 @@ /** * Task class for generating migration diff files. - * - * @property \Bake\Shell\Task\TestTask $Test */ class BakeMigrationDiffCommand extends BakeSimpleMigrationCommand { diff --git a/src/Command/Phinx/Dump.php b/src/Command/Phinx/Dump.php index b4eca9f3..42336b24 100644 --- a/src/Command/Phinx/Dump.php +++ b/src/Command/Phinx/Dump.php @@ -87,8 +87,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int $this->output($output); $path = $this->getOperationsPath($input); - /** @var string $connectionName */ $connectionName = $input->getOption('connection') ?: 'default'; + assert(is_string($connectionName), 'Connection name must be a string'); $connection = ConnectionManager::get($connectionName); assert($connection instanceof Connection); $collection = $connection->getSchemaCollection(); diff --git a/src/Db/Adapter/PdoAdapter.php b/src/Db/Adapter/PdoAdapter.php index 33a29482..b1d4452d 100644 --- a/src/Db/Adapter/PdoAdapter.php +++ b/src/Db/Adapter/PdoAdapter.php @@ -173,7 +173,6 @@ public function getConnection(): Connection $this->connect(); } - /** @var \Cake\Database\Connection $this->connection */ return $this->connection; } diff --git a/src/Db/Adapter/PostgresAdapter.php b/src/Db/Adapter/PostgresAdapter.php index eb2bd4c4..a525c219 100644 --- a/src/Db/Adapter/PostgresAdapter.php +++ b/src/Db/Adapter/PostgresAdapter.php @@ -186,11 +186,11 @@ public function createTable(Table $table, array $columns = [], array $indexes = $parts = $this->getSchemaName($table->getName()); // Add the default primary key - if (!isset($options['id']) || (isset($options['id']) && $options['id'] === true)) { + if (!isset($options['id']) || $options['id'] === true) { $options['id'] = 'id'; } - if (isset($options['id']) && is_string($options['id'])) { + if (is_string($options['id'])) { // Handle id => "field_name" to support AUTO_INCREMENT $column = new Column(); $column->setName($options['id']) diff --git a/src/Db/Adapter/SqliteAdapter.php b/src/Db/Adapter/SqliteAdapter.php index 7a9df3e7..540b675f 100644 --- a/src/Db/Adapter/SqliteAdapter.php +++ b/src/Db/Adapter/SqliteAdapter.php @@ -360,11 +360,11 @@ public function createTable(Table $table, array $columns = [], array $indexes = { // Add the default primary key $options = $table->getOptions(); - if (!isset($options['id']) || (isset($options['id']) && $options['id'] === true)) { + if (!isset($options['id']) || ($options['id'] === true)) { $options['id'] = 'id'; } - if (isset($options['id']) && is_string($options['id'])) { + if (is_string($options['id'])) { // Handle id => "field_name" to support AUTO_INCREMENT $column = new Column(); $column->setName($options['id']) diff --git a/src/Db/Adapter/SqlserverAdapter.php b/src/Db/Adapter/SqlserverAdapter.php index 99f0ec4f..0e3b43c0 100644 --- a/src/Db/Adapter/SqlserverAdapter.php +++ b/src/Db/Adapter/SqlserverAdapter.php @@ -137,11 +137,11 @@ public function createTable(Table $table, array $columns = [], array $indexes = $options = $table->getOptions(); // Add the default primary key - if (!isset($options['id']) || (isset($options['id']) && $options['id'] === true)) { + if (!isset($options['id']) || $options['id'] === true) { $options['id'] = 'id'; } - if (isset($options['id']) && is_string($options['id'])) { + if (is_string($options['id'])) { // Handle id => "field_name" to support AUTO_INCREMENT $column = new Column(); $column->setName($options['id']) diff --git a/src/Migration/Environment.php b/src/Migration/Environment.php index 47ea5ee1..cd11a46e 100644 --- a/src/Migration/Environment.php +++ b/src/Migration/Environment.php @@ -155,9 +155,7 @@ public function executeSeed(SeedInterface $seed): void } // Run the seeder - if (method_exists($seed, SeedInterface::RUN)) { - $seed->{SeedInterface::RUN}(); - } + $seed->{SeedInterface::RUN}(); // commit the transaction if the adapter supports it if ($atomic) { diff --git a/src/Migration/PhinxBackend.php b/src/Migration/PhinxBackend.php index 14ad28a7..68474a8b 100644 --- a/src/Migration/PhinxBackend.php +++ b/src/Migration/PhinxBackend.php @@ -13,6 +13,7 @@ */ namespace Migrations\Migration; +use Cake\Database\Connection; use Cake\Datasource\ConnectionManager; use DateTime; use InvalidArgumentException; @@ -339,10 +340,10 @@ public function setAdapter(): void return; } - /** @var string $connectionName */ $connectionName = $this->input()->getOption('connection') ?: 'default'; - /** @var \Cake\Database\Connection $connection */ + assert(is_string($connectionName), 'Connection name should be a string'); $connection = ConnectionManager::get($connectionName); + assert($connection instanceof Connection, 'Connection should be an instance of Cake\Database\Connection'); /** @psalm-suppress PossiblyNullReference */ $env = $this->manager->getEnvironment('default'); diff --git a/src/Migrations.php b/src/Migrations.php index 71859f89..d089f618 100644 --- a/src/Migrations.php +++ b/src/Migrations.php @@ -14,6 +14,7 @@ namespace Migrations; use Cake\Core\Configure; +use Cake\Database\Connection; use Cake\Datasource\ConnectionManager; use InvalidArgumentException; use Migrations\Migration\BackendInterface; @@ -296,10 +297,10 @@ public function setAdapter(): void return; } - /** @var string $connectionName */ $connectionName = $this->input()->getOption('connection') ?: 'default'; - /** @var \Cake\Database\Connection $connection */ + assert(is_string($connectionName), 'Connection name must be a string'); $connection = ConnectionManager::get($connectionName); + assert($connection instanceof Connection, 'Connection must be an instance of \Cake\Database\Connection'); /** @psalm-suppress PossiblyNullReference */ $env = $this->manager->getEnvironment('default'); diff --git a/src/Util/SchemaTrait.php b/src/Util/SchemaTrait.php index f68a84cb..9891af7a 100644 --- a/src/Util/SchemaTrait.php +++ b/src/Util/SchemaTrait.php @@ -34,7 +34,7 @@ protected function _getSchema(InputInterface $input, OutputInterface $output): ? { /** @var string $connectionName */ $connectionName = $input->getOption('connection'); - /** @var \Cake\Database\Connection $connection */ + /** @var \Cake\Database\Connection|\Cake\Datasource\ConnectionInterface $connection */ $connection = ConnectionManager::get($connectionName); if (!method_exists($connection, 'getSchemaCollection')) {