From e36c2131a6a90b5ccca1e361bb8dca2b5799d6fc Mon Sep 17 00:00:00 2001 From: Ryan Rigby Date: Thu, 7 Oct 2021 14:16:55 +1000 Subject: [PATCH 01/26] Add support for renaming columns via the change command. --- src/Operation/ColumnOperation.php | 1 + src/Operation/TableOperation.php | 13 +++++++++++++ src/Statement/MysqlStatementBuilder.php | 17 ++++++++++++++++- src/TableMigration.php | 22 ++++++++++++++++++++++ 4 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/Operation/ColumnOperation.php b/src/Operation/ColumnOperation.php index dc8d6af..c159940 100644 --- a/src/Operation/ColumnOperation.php +++ b/src/Operation/ColumnOperation.php @@ -6,6 +6,7 @@ final class ColumnOperation extends AbstractOperation { const ADD = 'add'; const MODIFY = 'modify'; + const CHANGE = 'change'; const DROP = 'drop'; /** diff --git a/src/Operation/TableOperation.php b/src/Operation/TableOperation.php index 6e73aa4..ae47b40 100644 --- a/src/Operation/TableOperation.php +++ b/src/Operation/TableOperation.php @@ -104,6 +104,18 @@ public function reverse(?ReversibleOperationInterface $originalOperation = null) $originalColumn->getOptions() ); } + + if ($columnOperation->getOperation() === ColumnOperation::CHANGE) { + if (!$originalColumn) { + throw new LogicException('Cannot revert a column that does not exist.'); + } + + $columnOperations[] = new ColumnOperation( + $columnOperation->getName(), + ColumnOperation::CHANGE, + $originalColumn->getOptions() + ); + } } $indexOperations = []; @@ -274,6 +286,7 @@ public function apply(ReducibleOperationInterface $operation): ?ReducibleOperati } break; case ColumnOperation::MODIFY: + case ColumnOperation::CHANGE: $columns[] = new ColumnOperation( $columnOperation->getName(), $originalOperation, diff --git a/src/Statement/MysqlStatementBuilder.php b/src/Statement/MysqlStatementBuilder.php index 0fb7b9a..6cbba3e 100644 --- a/src/Statement/MysqlStatementBuilder.php +++ b/src/Statement/MysqlStatementBuilder.php @@ -126,6 +126,12 @@ public function buildTable(TableOperation $operation): string $this->buildColumn($columnOperation->getName(), $columnOperation->getOptions()) ); break; + case ColumnOperation::CHANGE: + $specifications[] = sprintf( + 'CHANGE COLUMN %s', + $this->buildColumn($columnOperation->getName(), $columnOperation->getOptions()) + ); + break; case ColumnOperation::DROP: $specifications[] = sprintf( 'DROP COLUMN %s', @@ -344,7 +350,16 @@ protected function buildType(array $options): string */ protected function buildColumn(string $column, array $options): string { - $definition = sprintf('%s %s', $this->buildIdentifier($column), $this->buildType($options)); + if ($options['name'] ?? null) { + $definition = sprintf( + '%s %s %s', + $this->buildIdentifier($column), + $this->buildIdentifier($options['name']), + $this->buildType($options) + ); + } else { + $definition = sprintf('%s %s', $this->buildIdentifier($column), $this->buildType($options)); + } if (!($options['null'] ?? true)) { $definition .= ' NOT NULL'; diff --git a/src/TableMigration.php b/src/TableMigration.php index 3795c48..fd744a9 100644 --- a/src/TableMigration.php +++ b/src/TableMigration.php @@ -114,6 +114,28 @@ public function modifyColumn(string $column, array $options): TableMigration return $this; } + /** + * Pushes a new change column operation. + * + * @param string $column + * @param array $options + * @return TableMigration + */ + public function changeColumn(string $column, array $options): TableMigration + { + if ($this->operation === TableOperation::CREATE) { + throw new LogicException('Cannot change columns in a create migration.'); + } + + if ($this->operation === TableOperation::DROP) { + throw new LogicException('Cannot change columns in a drop migration.'); + } + + $this->columnOperations[] = new ColumnOperation($column, ColumnOperation::CHANGE, $options); + + return $this; + } + /** * Pushes a new drop column operation. * From 564929c8a0709df11377240e6d16999d265d68f4 Mon Sep 17 00:00:00 2001 From: Ryan Rigby Date: Thu, 7 Oct 2021 14:17:02 +1000 Subject: [PATCH 02/26] Add tests. --- tests/Operation/TableOperationTest.php | 58 +++++++++++++++++++ tests/Statement/MysqlStatementBuilderTest.php | 6 ++ tests/TableMigrationTest.php | 3 +- 3 files changed, 66 insertions(+), 1 deletion(-) diff --git a/tests/Operation/TableOperationTest.php b/tests/Operation/TableOperationTest.php index c1d65b8..e52e77e 100644 --- a/tests/Operation/TableOperationTest.php +++ b/tests/Operation/TableOperationTest.php @@ -86,6 +86,39 @@ public function testApplyAlterToAlter() $this->assertEquals(['unique' => true], $operation->getIndexOperations()[0]->getOptions()); } + public function testApplyAlterToAlterWithColumnChange() + { + $base = new TableOperation('users', TableOperation::ALTER, [ + new ColumnOperation('id', ColumnOperation::DROP, []), + new ColumnOperation('email', ColumnOperation::ADD, ['type' => 'string', 'length' => 255, 'first' => true]), + new ColumnOperation('username', ColumnOperation::ADD, ['type' => 'string', 'length' => 255]) + ], [ + new IndexOperation('email_username', ColumnOperation::ADD, ['email', 'username'], ['unique' => true]) + ]); + + $operation = $base->apply(new TableOperation('users', TableOperation::ALTER, [ + new ColumnOperation('email', ColumnOperation::DROP, []), + new ColumnOperation('username', ColumnOperation::CHANGE, ['type' => 'string', 'length' => 255, 'name' => 'email']) + ], [])); + + $this->assertEquals('users', $operation->getName()); + $this->assertEquals(TableOperation::ALTER, $operation->getOperation()); + $this->assertCount(2, $operation->getColumnOperations()); + $this->assertCount(1, $operation->getIndexOperations()); + + $this->assertEquals('id', $operation->getColumnOperations()[0]->getName()); + $this->assertEquals(ColumnOperation::DROP, $operation->getColumnOperations()[0]->getOperation()); + + $this->assertEquals('username', $operation->getColumnOperations()[1]->getName()); + $this->assertEquals(ColumnOperation::ADD, $operation->getColumnOperations()[1]->getOperation()); + $this->assertEquals(['type' => 'string', 'length' => 255, 'name' => 'email'], $operation->getColumnOperations()[1]->getOptions()); + + $this->assertEquals('email_username', $operation->getIndexOperations()[0]->getName()); + $this->assertEquals(IndexOperation::ADD, $operation->getIndexOperations()[0]->getOperation()); + $this->assertEquals(['username'], $operation->getIndexOperations()[0]->getColumns()); + $this->assertEquals(['unique' => true], $operation->getIndexOperations()[0]->getOptions()); + } + public function testApplyDropToAlter() { $base = new TableOperation('users', TableOperation::ALTER, [ @@ -158,6 +191,31 @@ public function testReverseAlter() $this->assertEquals(IndexOperation::DROP, $operation->getIndexOperations()[1]->getOperation()); } + public function testReverseAlterWithColumnChange() + { + $base = new TableOperation('users', TableOperation::ALTER, [ + new ColumnOperation('username', ColumnOperation::CHANGE, ['name' => 'email', 'type' => 'string', 'length' => 255]) + ], []); + + $create = new TableOperation('users', TableOperation::CREATE, [ + new ColumnOperation('id', ColumnOperation::ADD, ['type' => 'uuid']), + new ColumnOperation('username', ColumnOperation::ADD, ['type' => 'string', 'length' => 64]) + ], [ + new IndexOperation('username', IndexOperation::ADD, ['username'], ['unique' => true]) + ]); + + $operation = $base->reverse($create); + + $this->assertEquals('users', $operation->getName()); + $this->assertEquals(TableOperation::ALTER, $operation->getOperation()); + + $this->assertEquals('username', $operation->getColumnOperations()[0]->getName()); + $this->assertEquals(ColumnOperation::CHANGE, $operation->getColumnOperations()[0]->getOperation()); + $this->assertEquals(['type' => 'string', 'length' => 64], $operation->getColumnOperations()[0]->getOptions()); + + $this->assertCount(0, $operation->getIndexOperations()); + } + public function testReverseDrop() { $base = new TableOperation('users', TableOperation::DROP, [], []); diff --git a/tests/Statement/MysqlStatementBuilderTest.php b/tests/Statement/MysqlStatementBuilderTest.php index 73a18c8..b790b0a 100644 --- a/tests/Statement/MysqlStatementBuilderTest.php +++ b/tests/Statement/MysqlStatementBuilderTest.php @@ -101,6 +101,12 @@ public function provider() 'MODIFY COLUMN `username` VARCHAR(255), ' . 'DROP COLUMN `created_at`, ADD UNIQUE INDEX `meta` (`meta`), DROP INDEX `username`;' ], + [ + new TableOperation('users', TableOperation::ALTER, [ + new ColumnOperation('meta', ColumnOperation::CHANGE, ['name' => 'metadata', 'type' => 'json']), + ], []), + 'ALTER TABLE `users` CHANGE COLUMN `meta` `metadata` JSON;' + ], [ new TableOperation('users', TableOperation::DROP, [], []), 'DROP TABLE `users`;' diff --git a/tests/TableMigrationTest.php b/tests/TableMigrationTest.php index 0ddce0a..6a36e68 100644 --- a/tests/TableMigrationTest.php +++ b/tests/TableMigrationTest.php @@ -25,11 +25,12 @@ public function testAlterMigration() ->addColumn('email', ['type' => 'string', 'length' => 255]) ->modifyColumn('password', ['type' => 'string', 'length' => 255]) ->dropColumn('username') + ->changeColumn('id', ['name' => 'uid', 'type' => 'string']) ->getOperation(); $this->assertEquals('users', $operation->getName()); $this->assertEquals(TableOperation::ALTER, $operation->getOperation()); - $this->assertCount(3, $operation->getColumnOperations()); + $this->assertCount(4, $operation->getColumnOperations()); } public function testDropMigration() From 4cc5d0f3bda002a65be66671c5246272b015d19c Mon Sep 17 00:00:00 2001 From: Ryan Rigby Date: Tue, 12 Oct 2021 16:01:48 +1000 Subject: [PATCH 03/26] Update TableOperation.php --- src/Operation/TableOperation.php | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/src/Operation/TableOperation.php b/src/Operation/TableOperation.php index ae47b40..41e2bc0 100644 --- a/src/Operation/TableOperation.php +++ b/src/Operation/TableOperation.php @@ -93,26 +93,17 @@ public function reverse(?ReversibleOperationInterface $originalOperation = null) $columnOperations[] = $originalColumn; } - if ($columnOperation->getOperation() === ColumnOperation::MODIFY) { + if ( + $columnOperation->getOperation() === ColumnOperation::MODIFY || + $columnOperation->getOperation() === ColumnOperation::CHANGE + ) { if (!$originalColumn) { throw new LogicException('Cannot revert a column that does not exist.'); } $columnOperations[] = new ColumnOperation( $columnOperation->getName(), - ColumnOperation::MODIFY, - $originalColumn->getOptions() - ); - } - - if ($columnOperation->getOperation() === ColumnOperation::CHANGE) { - if (!$originalColumn) { - throw new LogicException('Cannot revert a column that does not exist.'); - } - - $columnOperations[] = new ColumnOperation( - $columnOperation->getName(), - ColumnOperation::CHANGE, + $columnOperation->getOperation(), $originalColumn->getOptions() ); } From 6f896e1949cb39d72ae35e856f3daac73e98ae9b Mon Sep 17 00:00:00 2001 From: Ryan Rigby Date: Tue, 12 Oct 2021 20:41:06 +1000 Subject: [PATCH 04/26] Make recommended adjustments to building query string. --- src/Statement/MysqlStatementBuilder.php | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/Statement/MysqlStatementBuilder.php b/src/Statement/MysqlStatementBuilder.php index 6cbba3e..a7ab050 100644 --- a/src/Statement/MysqlStatementBuilder.php +++ b/src/Statement/MysqlStatementBuilder.php @@ -128,8 +128,12 @@ public function buildTable(TableOperation $operation): string break; case ColumnOperation::CHANGE: $specifications[] = sprintf( - 'CHANGE COLUMN %s', - $this->buildColumn($columnOperation->getName(), $columnOperation->getOptions()) + 'CHANGE COLUMN %s %s', + $this->buildIdentifier($columnOperation->getName()), + $this->buildColumn( + $columnOperation->getOptions()['new_name'], + $columnOperation->getOptions() + ) ); break; case ColumnOperation::DROP: @@ -350,16 +354,7 @@ protected function buildType(array $options): string */ protected function buildColumn(string $column, array $options): string { - if ($options['name'] ?? null) { - $definition = sprintf( - '%s %s %s', - $this->buildIdentifier($column), - $this->buildIdentifier($options['name']), - $this->buildType($options) - ); - } else { - $definition = sprintf('%s %s', $this->buildIdentifier($column), $this->buildType($options)); - } + $definition = sprintf('%s %s', $this->buildIdentifier($column), $this->buildType($options)); if (!($options['null'] ?? true)) { $definition .= ' NOT NULL'; From 15a82fecef3bad28366b4565a431ab53c52bb330 Mon Sep 17 00:00:00 2001 From: Ryan Rigby Date: Tue, 12 Oct 2021 21:28:21 +1000 Subject: [PATCH 05/26] Update table operations for Change to operate on the new column. --- src/Operation/TableOperation.php | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/Operation/TableOperation.php b/src/Operation/TableOperation.php index 41e2bc0..e7b6069 100644 --- a/src/Operation/TableOperation.php +++ b/src/Operation/TableOperation.php @@ -216,6 +216,15 @@ public function apply(ReducibleOperationInterface $operation): ?ReducibleOperati $options ); + array_splice($columns, $offset, 0, [$addOperation]); + break; + case ColumnOperation::CHANGE: + $addOperation = new ColumnOperation( + $columnOperation->getOptions()['new_name'], + ColumnOperation::ADD, + $options + ); + array_splice($columns, $offset, 0, [$addOperation]); break; } @@ -277,13 +286,19 @@ public function apply(ReducibleOperationInterface $operation): ?ReducibleOperati } break; case ColumnOperation::MODIFY: - case ColumnOperation::CHANGE: $columns[] = new ColumnOperation( $columnOperation->getName(), $originalOperation, $columnOperation->getOptions() ); break; + case ColumnOperation::CHANGE: + $columns[] = new ColumnOperation( + $columnOperation->getOptions()['new_name'], + $originalOperation, + $columnOperation->getOptions() + ); + break; } } From e95270483cbbc4c960248d527c5da2aa0ba0640a Mon Sep 17 00:00:00 2001 From: Ryan Rigby Date: Tue, 12 Oct 2021 21:29:15 +1000 Subject: [PATCH 06/26] make the changeColumn method more dev friendly by accepting a new column arg. --- src/TableMigration.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/TableMigration.php b/src/TableMigration.php index fd744a9..f473616 100644 --- a/src/TableMigration.php +++ b/src/TableMigration.php @@ -117,11 +117,12 @@ public function modifyColumn(string $column, array $options): TableMigration /** * Pushes a new change column operation. * - * @param string $column + * @param string $oldColumn + * @param string $newColumn * @param array $options * @return TableMigration */ - public function changeColumn(string $column, array $options): TableMigration + public function changeColumn(string $oldColumn, string $newColumn, array $options): TableMigration { if ($this->operation === TableOperation::CREATE) { throw new LogicException('Cannot change columns in a create migration.'); @@ -131,7 +132,12 @@ public function changeColumn(string $column, array $options): TableMigration throw new LogicException('Cannot change columns in a drop migration.'); } - $this->columnOperations[] = new ColumnOperation($column, ColumnOperation::CHANGE, $options); + if ($oldColumn === $newColumn) { + $this->columnOperations[] = new ColumnOperation($oldColumn, ColumnOperation::MODIFY, $options); + } else { + $options['new_name'] = $newColumn; + $this->columnOperations[] = new ColumnOperation($oldColumn, ColumnOperation::CHANGE, $options); + } return $this; } From 8d96cd803ffacd8240db94b98af2913c389f2450 Mon Sep 17 00:00:00 2001 From: Ryan Rigby Date: Tue, 12 Oct 2021 21:29:24 +1000 Subject: [PATCH 07/26] Get tests to working state. --- tests/Operation/TableOperationTest.php | 10 +++++----- tests/Statement/MysqlStatementBuilderTest.php | 2 +- tests/TableMigrationTest.php | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/Operation/TableOperationTest.php b/tests/Operation/TableOperationTest.php index e52e77e..12b79af 100644 --- a/tests/Operation/TableOperationTest.php +++ b/tests/Operation/TableOperationTest.php @@ -98,7 +98,7 @@ public function testApplyAlterToAlterWithColumnChange() $operation = $base->apply(new TableOperation('users', TableOperation::ALTER, [ new ColumnOperation('email', ColumnOperation::DROP, []), - new ColumnOperation('username', ColumnOperation::CHANGE, ['type' => 'string', 'length' => 255, 'name' => 'email']) + new ColumnOperation('username', ColumnOperation::CHANGE, ['type' => 'string', 'length' => 255, 'new_name' => 'email']) ], [])); $this->assertEquals('users', $operation->getName()); @@ -109,13 +109,13 @@ public function testApplyAlterToAlterWithColumnChange() $this->assertEquals('id', $operation->getColumnOperations()[0]->getName()); $this->assertEquals(ColumnOperation::DROP, $operation->getColumnOperations()[0]->getOperation()); - $this->assertEquals('username', $operation->getColumnOperations()[1]->getName()); + $this->assertEquals('email', $operation->getColumnOperations()[1]->getName()); $this->assertEquals(ColumnOperation::ADD, $operation->getColumnOperations()[1]->getOperation()); - $this->assertEquals(['type' => 'string', 'length' => 255, 'name' => 'email'], $operation->getColumnOperations()[1]->getOptions()); + $this->assertEquals(['type' => 'string', 'length' => 255, 'new_name' => 'email'], $operation->getColumnOperations()[1]->getOptions()); $this->assertEquals('email_username', $operation->getIndexOperations()[0]->getName()); $this->assertEquals(IndexOperation::ADD, $operation->getIndexOperations()[0]->getOperation()); - $this->assertEquals(['username'], $operation->getIndexOperations()[0]->getColumns()); + $this->assertEquals(['email'], $operation->getIndexOperations()[0]->getColumns()); $this->assertEquals(['unique' => true], $operation->getIndexOperations()[0]->getOptions()); } @@ -194,7 +194,7 @@ public function testReverseAlter() public function testReverseAlterWithColumnChange() { $base = new TableOperation('users', TableOperation::ALTER, [ - new ColumnOperation('username', ColumnOperation::CHANGE, ['name' => 'email', 'type' => 'string', 'length' => 255]) + new ColumnOperation('username', ColumnOperation::CHANGE, ['new_name' => 'email', 'type' => 'string', 'length' => 255]) ], []); $create = new TableOperation('users', TableOperation::CREATE, [ diff --git a/tests/Statement/MysqlStatementBuilderTest.php b/tests/Statement/MysqlStatementBuilderTest.php index b790b0a..aee7a47 100644 --- a/tests/Statement/MysqlStatementBuilderTest.php +++ b/tests/Statement/MysqlStatementBuilderTest.php @@ -103,7 +103,7 @@ public function provider() ], [ new TableOperation('users', TableOperation::ALTER, [ - new ColumnOperation('meta', ColumnOperation::CHANGE, ['name' => 'metadata', 'type' => 'json']), + new ColumnOperation('meta', ColumnOperation::CHANGE, ['new_name' => 'metadata', 'type' => 'json']), ], []), 'ALTER TABLE `users` CHANGE COLUMN `meta` `metadata` JSON;' ], diff --git a/tests/TableMigrationTest.php b/tests/TableMigrationTest.php index 6a36e68..64025ea 100644 --- a/tests/TableMigrationTest.php +++ b/tests/TableMigrationTest.php @@ -25,7 +25,7 @@ public function testAlterMigration() ->addColumn('email', ['type' => 'string', 'length' => 255]) ->modifyColumn('password', ['type' => 'string', 'length' => 255]) ->dropColumn('username') - ->changeColumn('id', ['name' => 'uid', 'type' => 'string']) + ->changeColumn('id', 'uid', ['type' => 'string']) ->getOperation(); $this->assertEquals('users', $operation->getName()); From 7881e024b4abea32fd88ce33aa2ce445c38d4c97 Mon Sep 17 00:00:00 2001 From: Ryan Rigby Date: Tue, 12 Oct 2021 21:59:03 +1000 Subject: [PATCH 08/26] Add proper reverse method for change. --- src/Operation/TableOperation.php | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/Operation/TableOperation.php b/src/Operation/TableOperation.php index e7b6069..ebbcd68 100644 --- a/src/Operation/TableOperation.php +++ b/src/Operation/TableOperation.php @@ -93,10 +93,7 @@ public function reverse(?ReversibleOperationInterface $originalOperation = null) $columnOperations[] = $originalColumn; } - if ( - $columnOperation->getOperation() === ColumnOperation::MODIFY || - $columnOperation->getOperation() === ColumnOperation::CHANGE - ) { + if ($columnOperation->getOperation() === ColumnOperation::MODIFY) { if (!$originalColumn) { throw new LogicException('Cannot revert a column that does not exist.'); } @@ -107,6 +104,20 @@ public function reverse(?ReversibleOperationInterface $originalOperation = null) $originalColumn->getOptions() ); } + + if ($columnOperation->getOperation() === ColumnOperation::CHANGE) { + if (!$originalColumn) { + throw new LogicException('Cannot revert a column that does not exist.'); + } + + $options = $originalColumn->getOptions(); + $options['new_name'] = $columnOperation->getName(); + $columnOperations[] = new ColumnOperation( + $columnOperation->getOptions()['new_name'], + $columnOperation->getOperation(), + $options + ); + } } $indexOperations = []; From c4d4c260a493518f841e478474dfd696b1069192 Mon Sep 17 00:00:00 2001 From: Ryan Rigby Date: Tue, 12 Oct 2021 22:16:05 +1000 Subject: [PATCH 09/26] Update TableOperationTest.php --- tests/Operation/TableOperationTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Operation/TableOperationTest.php b/tests/Operation/TableOperationTest.php index 12b79af..c6c6fca 100644 --- a/tests/Operation/TableOperationTest.php +++ b/tests/Operation/TableOperationTest.php @@ -209,9 +209,9 @@ public function testReverseAlterWithColumnChange() $this->assertEquals('users', $operation->getName()); $this->assertEquals(TableOperation::ALTER, $operation->getOperation()); - $this->assertEquals('username', $operation->getColumnOperations()[0]->getName()); + $this->assertEquals('email', $operation->getColumnOperations()[0]->getName()); $this->assertEquals(ColumnOperation::CHANGE, $operation->getColumnOperations()[0]->getOperation()); - $this->assertEquals(['type' => 'string', 'length' => 64], $operation->getColumnOperations()[0]->getOptions()); + $this->assertEquals(['new_name' => 'username', 'type' => 'string', 'length' => 64], $operation->getColumnOperations()[0]->getOptions()); $this->assertCount(0, $operation->getIndexOperations()); } From 938d4adc3c5842691d899df9bb89a6804a638b49 Mon Sep 17 00:00:00 2001 From: Ryan Rigby Date: Fri, 15 Oct 2021 14:36:35 +1000 Subject: [PATCH 10/26] Add tests to test applying and rewinding changes. --- .../TestChange/20211015_create_users.php | 4 + .../20211016_change_username_to_email.php | 4 + .../20211017_add_column_username.php | 4 + .../20211018_change_username_to_user_id.php | 4 + tests/MigrationIntegrationTest.php | 82 +++++++++++++++++++ 5 files changed, 98 insertions(+) create mode 100644 tests/Fixtures/TestChange/20211015_create_users.php create mode 100644 tests/Fixtures/TestChange/20211016_change_username_to_email.php create mode 100644 tests/Fixtures/TestChange/20211017_add_column_username.php create mode 100644 tests/Fixtures/TestChange/20211018_change_username_to_user_id.php create mode 100644 tests/MigrationIntegrationTest.php diff --git a/tests/Fixtures/TestChange/20211015_create_users.php b/tests/Fixtures/TestChange/20211015_create_users.php new file mode 100644 index 0000000..3479ca3 --- /dev/null +++ b/tests/Fixtures/TestChange/20211015_create_users.php @@ -0,0 +1,4 @@ +addColumn('username', ['type' => 'string']); diff --git a/tests/Fixtures/TestChange/20211016_change_username_to_email.php b/tests/Fixtures/TestChange/20211016_change_username_to_email.php new file mode 100644 index 0000000..e717179 --- /dev/null +++ b/tests/Fixtures/TestChange/20211016_change_username_to_email.php @@ -0,0 +1,4 @@ +changeColumn('username', 'email', ['type' => 'string']); diff --git a/tests/Fixtures/TestChange/20211017_add_column_username.php b/tests/Fixtures/TestChange/20211017_add_column_username.php new file mode 100644 index 0000000..be3b18a --- /dev/null +++ b/tests/Fixtures/TestChange/20211017_add_column_username.php @@ -0,0 +1,4 @@ +addColumn('username', ['type' => 'string']); diff --git a/tests/Fixtures/TestChange/20211018_change_username_to_user_id.php b/tests/Fixtures/TestChange/20211018_change_username_to_user_id.php new file mode 100644 index 0000000..3311883 --- /dev/null +++ b/tests/Fixtures/TestChange/20211018_change_username_to_user_id.php @@ -0,0 +1,4 @@ +changeColumn('username', 'user_id', ['type' => 'integer']); diff --git a/tests/MigrationIntegrationTest.php b/tests/MigrationIntegrationTest.php new file mode 100644 index 0000000..c502084 --- /dev/null +++ b/tests/MigrationIntegrationTest.php @@ -0,0 +1,82 @@ +fromPath(__DIR__ . '/Fixtures/TestChange'); + $operations = $history->play('20211015_create_users', '20211018_change_username_to_user_id', true); + + $this->assertCount(1, $operations); + + $operation = $operations[0]; + + $this->assertSame('users', $operation->getName()); + $this->assertSame(TableOperation::CREATE, $operation->getOperation()); + $this->assertCount(2, $operation->getColumnOperations()); + + list($operation1, $operation2) = $operation->getColumnOperations(); + + $this->assertSame('email', $operation1->getName()); + $this->assertSame(ColumnOperation::ADD, $operation1->getOperation()); + $this->assertSame('string', $operation1->getOptions()['type']); + + $this->assertSame('user_id', $operation2->getName()); + $this->assertSame(ColumnOperation::ADD, $operation2->getOperation()); + $this->assertSame('integer', $operation2->getOptions()['type']); + } + + public function testRewindMigrationsWithChange() + { + $finder = new Finder([]); + $history = $finder->fromPath(__DIR__ . '/Fixtures/TestChange'); + $operations = $history->rewind('20211018_change_username_to_user_id', '20211017_add_column_username', false); + $this->assertCount(2, $operations); + + list($operation1, $operation2) = $operations; + + $this->assertSame('users', $operation1->getName()); + $this->assertSame(TableOperation::ALTER, $operation1->getOperation()); + $this->assertCount(1, $operation1->getColumnOperations()); + $this->assertSame('user_id', $operation1->getColumnOperations()[0]->getName()); + $this->assertSame(ColumnOperation::CHANGE, $operation1->getColumnOperations()[0]->getOperation()); + $this->assertSame('username', $operation1->getColumnOperations()[0]->getOptions()['new_name']); + + $this->assertSame('users', $operation2->getName()); + $this->assertSame(TableOperation::ALTER, $operation2->getOperation()); + $this->assertCount(1, $operation2->getColumnOperations()); + $this->assertSame('username', $operation2->getColumnOperations()[0]->getName()); + $this->assertSame(ColumnOperation::DROP, $operation2->getColumnOperations()[0]->getOperation()); + } + + public function testReduceRewindMigrationsWithChange() + { + $finder = new Finder([]); + $history = $finder->fromPath(__DIR__ . '/Fixtures/TestChange'); + $operations = $history->rewind('20211018_change_username_to_user_id', '20211017_add_column_username', true); + $this->assertCount(1, $operations); + + $operation = $operations[0]; + + $this->assertSame('users', $operation->getName()); + $this->assertSame(TableOperation::ALTER, $operation->getOperation()); + $this->assertCount(2, $operation->getColumnOperations()); + + list($operation1, $operation2) = $operation->getColumnOperations(); + + $this->assertSame('user_id', $operation1->getName()); + $this->assertSame(ColumnOperation::CHANGE, $operation1->getOperation()); + $this->assertSame('username', $operation1->getOptions()['new_name']); + + $this->assertSame('username', $operation2->getName()); + $this->assertSame(ColumnOperation::DROP, $operation2->getOperation()); + } +} From 5f8f565225df496c594e7e2fa2d1e5f4d1eee9ff Mon Sep 17 00:00:00 2001 From: Ryan Rigby Date: Fri, 15 Oct 2021 14:58:15 +1000 Subject: [PATCH 11/26] Added proper integration test parts where we run the migrations on the Db and confirm it works. --- tests/MigrationIntegrationTest.php | 69 ++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/tests/MigrationIntegrationTest.php b/tests/MigrationIntegrationTest.php index c502084..5599325 100644 --- a/tests/MigrationIntegrationTest.php +++ b/tests/MigrationIntegrationTest.php @@ -2,13 +2,40 @@ namespace Exo\Tests; +use Exo\Handler; +use PDO; use Exo\Operation\ColumnOperation; use Exo\Operation\TableOperation; +use Exo\Tests\Traits\UsesYamlConfig; use Exo\Util\Finder; use PHPUnit\Framework\TestCase; class MigrationIntegrationTest extends TestCase { + use UsesYamlConfig; + + /** + * @var PDO|null + */ + private ?PDO $pdo; + + public function setUp(): void + { + $mysql = self::yaml('handlers.mysql'); + + $this->pdo = new PDO( + sprintf('mysql:dbname=%s;host=%s;port=%s', $mysql['name'], $mysql['host'], $mysql['port']), + $mysql['user'], + $mysql['pass'] + ); + $this->pdo->exec('DROP TABLE IF EXISTS users;'); + } + + public function tearDown(): void + { + $this->pdo = null; + } + public function testReduceMigrationsWithChange() { $finder = new Finder([]); @@ -79,4 +106,46 @@ public function testReduceRewindMigrationsWithChange() $this->assertSame('username', $operation2->getName()); $this->assertSame(ColumnOperation::DROP, $operation2->getOperation()); } + + public function testMigrateMigrationsWithMysql() + { + $finder = new Finder([]); + $history = $finder->fromPath(__DIR__ . '/Fixtures/TestChange'); + $handler = new Handler($this->pdo, $history); + + $handler->migrate([], null, true); + + $usersTable = $this->pdo->query('DESCRIBE users')->fetchAll(); + + $this->assertSame('email', $usersTable[0]['Field']); + $this->contains('varchar', $usersTable[0]['Type']); + $this->assertSame('user_id', $usersTable[1]['Field']); + $this->contains('int', $usersTable[1]['Type']); + } + + public function testRollbackMigrationsWithMysql() + { + $finder = new Finder([]); + $history = $finder->fromPath(__DIR__ . '/Fixtures/TestChange'); + $handler = new Handler($this->pdo, $history); + $handler->migrate([], null, true); + + $handler->rollback( + [ + '20211015_create_users', + '20211016_change_username_to_email', + '20211017_add_column_username', + '20211018_change_username_to_user_id' + ], + '20211017_add_column_username', + true + ); + + $usersTable = $this->pdo->query('DESCRIBE users')->fetchAll(); + + $this->assertSame('email', $usersTable[0]['Field']); + $this->contains('varchar', $usersTable[0]['Type']); + $this->assertSame('username', $usersTable[1]['Field']); + $this->contains('varchar', $usersTable[1]['Type']); + } } From a991b4e2229cb12ed12b351d83907a19f24df845 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 23:21:08 +0000 Subject: [PATCH 12/26] Add PHP 8.1-8.4 support - Update composer.json to support PHP versions 7.4 through 8.4 - Update CI workflow to test against PHP 7.4, 8.0, 8.1, 8.2, 8.3, 8.4 - Update composer.lock to reflect new PHP version constraints Co-Authored-By: Ben Sinclair --- .github/workflows/tests.yml | 4 ++-- composer.json | 2 +- composer.lock | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d7adcf6..6ffad59 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -14,7 +14,7 @@ jobs: timeout-minutes: 10 strategy: matrix: - php-versions: [ '7.4', '8.0' ] + php-versions: [ '7.4', '8.0', '8.1', '8.2', '8.3', '8.4' ] postgresql-versions: [ '12', '14' ] name: PHP ${{ matrix.php-versions }} with postgreSQL ${{ matrix.postgresql-versions }} @@ -73,7 +73,7 @@ jobs: timeout-minutes: 10 strategy: matrix: - php-versions: [ '7.4', '8.0' ] + php-versions: [ '7.4', '8.0', '8.1', '8.2', '8.3', '8.4' ] mysql-versions: [ '5.7', '8.0' ] name: PHP ${{ matrix.php-versions }} with MySQL ${{ matrix.mysql-versions }} diff --git a/composer.json b/composer.json index 3c15012..88ee793 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,7 @@ "type": "library", "license": "MIT", "require": { - "php": ">=7.4", + "php": ">=7.4 <8.5", "ext-pdo": "*" }, "require-dev": { diff --git a/composer.lock b/composer.lock index c6e3bae..7de15f1 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f4eb836aa389b5ed2b1d1d035b61c22d", + "content-hash": "f012672abd9b2ab08d08654d9e0d303e", "packages": [], "packages-dev": [ { @@ -2245,13 +2245,13 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=7.4", + "php": ">=7.4 <8.5", "ext-pdo": "*" }, - "platform-dev": [], - "plugin-api-version": "2.0.0" + "platform-dev": {}, + "plugin-api-version": "2.6.0" } From eaa65bcd96cc2621cea60a27966dbd2389cbae61 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 23:25:16 +0000 Subject: [PATCH 13/26] Fix deprecated GitHub Actions versions - Update actions/checkout from v2 to v4 - Update actions/cache from v2 to v4 - Resolves CI failure due to deprecated action versions Co-Authored-By: Ben Sinclair --- .github/workflows/tests.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6ffad59..cd775dc 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -34,7 +34,7 @@ jobs: --health-retries 5 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 name: Check Out Code - name: Setup PHP @@ -48,7 +48,7 @@ jobs: - name: Cache Composer packages id: composer-cache - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: vendor key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} @@ -89,7 +89,7 @@ jobs: options: --health-cmd "mysqladmin ping" --health-interval 10s --health-timeout 5s --health-retries 10 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 name: Check Out Code - name: Setup PHP @@ -103,7 +103,7 @@ jobs: - name: Cache Composer packages id: composer-cache - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: vendor key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} From 46ee98f9a80bd9a1d43ddb4e751429bfb3c84fe5 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 23:29:36 +0000 Subject: [PATCH 14/26] Drop PHP 7.4 support, require PHP >=8.0 for dependency compatibility Co-Authored-By: Ben Sinclair --- .github/workflows/tests.yml | 4 ++-- composer.json | 2 +- composer.lock | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index cd775dc..6c2db61 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -14,7 +14,7 @@ jobs: timeout-minutes: 10 strategy: matrix: - php-versions: [ '7.4', '8.0', '8.1', '8.2', '8.3', '8.4' ] + php-versions: [ '8.0', '8.1', '8.2', '8.3', '8.4' ] postgresql-versions: [ '12', '14' ] name: PHP ${{ matrix.php-versions }} with postgreSQL ${{ matrix.postgresql-versions }} @@ -73,7 +73,7 @@ jobs: timeout-minutes: 10 strategy: matrix: - php-versions: [ '7.4', '8.0', '8.1', '8.2', '8.3', '8.4' ] + php-versions: [ '8.0', '8.1', '8.2', '8.3', '8.4' ] mysql-versions: [ '5.7', '8.0' ] name: PHP ${{ matrix.php-versions }} with MySQL ${{ matrix.mysql-versions }} diff --git a/composer.json b/composer.json index 88ee793..92730a7 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,7 @@ "type": "library", "license": "MIT", "require": { - "php": ">=7.4 <8.5", + "php": ">=8.0 <8.5", "ext-pdo": "*" }, "require-dev": { diff --git a/composer.lock b/composer.lock index 7de15f1..d0c397d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f012672abd9b2ab08d08654d9e0d303e", + "content-hash": "818deb88d1b484f20f0f29563872d88e", "packages": [], "packages-dev": [ { @@ -2249,7 +2249,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=7.4 <8.5", + "php": ">=8.0 <8.5", "ext-pdo": "*" }, "platform-dev": {}, From 68e3ecb2abaf0cfec616ef73ec62b7eaa49bfb2d Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 23:30:13 +0000 Subject: [PATCH 15/26] Implement before/after name concepts for ColumnOperation to fix reduction logic - Add beforeName and afterName properties to ColumnOperation - Update TableOperation apply method to use before/after name matching - Fix reduction scenarios where CHANGE operations are followed by operations on renamed columns - Addresses feedback from joshmcrae in PR #22 This resolves the issue where column operations couldn't be properly matched during reduction when CHANGE operations were involved. The solution introduces explicit before/after name tracking so operations can be matched based on the relationship between the old name (beforeName) and new name (afterName). Co-Authored-By: Ben Sinclair --- src/Operation/ColumnOperation.php | 38 +++++++++++++++++++++++++++++++ src/Operation/TableOperation.php | 16 ++++++------- 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/src/Operation/ColumnOperation.php b/src/Operation/ColumnOperation.php index c159940..a77bcb6 100644 --- a/src/Operation/ColumnOperation.php +++ b/src/Operation/ColumnOperation.php @@ -14,6 +14,16 @@ final class ColumnOperation extends AbstractOperation */ private string $operation; + /** + * @var string + */ + private string $beforeName; + + /** + * @var string + */ + private string $afterName; + /** * @var array */ @@ -31,6 +41,14 @@ public function __construct(string $name, string $operation, array $options) $this->name = $name; $this->operation = $operation; $this->options = $options; + + if ($operation === self::CHANGE && isset($options['new_name'])) { + $this->beforeName = $name; + $this->afterName = $options['new_name']; + } else { + $this->beforeName = $name; + $this->afterName = $name; + } } /** @@ -52,4 +70,24 @@ public function getOptions(): array { return $this->options; } + + /** + * Returns the before name (original name for CHANGE operations). + * + * @return string + */ + public function getBeforeName(): string + { + return $this->beforeName; + } + + /** + * Returns the after name (new name for CHANGE operations). + * + * @return string + */ + public function getAfterName(): string + { + return $this->afterName; + } } diff --git a/src/Operation/TableOperation.php b/src/Operation/TableOperation.php index ebbcd68..3d7773c 100644 --- a/src/Operation/TableOperation.php +++ b/src/Operation/TableOperation.php @@ -111,9 +111,9 @@ public function reverse(?ReversibleOperationInterface $originalOperation = null) } $options = $originalColumn->getOptions(); - $options['new_name'] = $columnOperation->getName(); + $options['new_name'] = $columnOperation->getBeforeName(); $columnOperations[] = new ColumnOperation( - $columnOperation->getOptions()['new_name'], + $columnOperation->getAfterName(), $columnOperation->getOperation(), $options ); @@ -206,9 +206,9 @@ public function apply(ReducibleOperationInterface $operation): ?ReducibleOperati $offset = array_search($options['after'], array_keys($columns)) + 1; } - // Remove existing operation for the column + // Remove existing operation for the column using before/after name matching foreach ($columns as $existing => $column) { - if ($column->getName() === $columnOperation->getName()) { + if ($column->getAfterName() === $columnOperation->getBeforeName()) { unset($columns[$existing]); break; } @@ -231,7 +231,7 @@ public function apply(ReducibleOperationInterface $operation): ?ReducibleOperati break; case ColumnOperation::CHANGE: $addOperation = new ColumnOperation( - $columnOperation->getOptions()['new_name'], + $columnOperation->getAfterName(), ColumnOperation::ADD, $options ); @@ -277,9 +277,9 @@ public function apply(ReducibleOperationInterface $operation): ?ReducibleOperati foreach ($operation->getColumnOperations() as $columnOperation) { $originalOperation = $columnOperation->getOperation(); - // Remove existing operation for the column + // Remove existing operation for the column using before/after name matching foreach ($columns as $existing => $column) { - if ($column->getName() === $columnOperation->getName()) { + if ($column->getAfterName() === $columnOperation->getBeforeName()) { unset($columns[$existing]); $originalOperation = $column->getOperation(); break; @@ -305,7 +305,7 @@ public function apply(ReducibleOperationInterface $operation): ?ReducibleOperati break; case ColumnOperation::CHANGE: $columns[] = new ColumnOperation( - $columnOperation->getOptions()['new_name'], + $columnOperation->getAfterName(), $originalOperation, $columnOperation->getOptions() ); From 0e2019ceaa90bfd4e07212509a3bbcd8605d0a7d Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 23:32:39 +0000 Subject: [PATCH 16/26] Update phpspec/prophecy to 1.22.0 for PHP 8.2-8.4 compatibility Co-Authored-By: Ben Sinclair --- composer.json | 3 ++- composer.lock | 27 +++++++++++++++------------ 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/composer.json b/composer.json index 92730a7..935c786 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,8 @@ }, "require-dev": { "phpunit/phpunit": "^9.4", - "symfony/yaml": "^5.0" + "symfony/yaml": "^5.0", + "phpspec/prophecy": "^1.22.0" }, "scripts":{ "test": "./vendor/bin/phpunit -c phpunit.xml" diff --git a/composer.lock b/composer.lock index d0c397d..aa3eeeb 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "818deb88d1b484f20f0f29563872d88e", + "content-hash": "d975bb62eb5a45c054f1f8f280ed56dd", "packages": [], "packages-dev": [ { @@ -462,28 +462,30 @@ }, { "name": "phpspec/prophecy", - "version": "1.14.0", + "version": "v1.22.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e" + "reference": "35f1adb388946d92e6edab2aa2cb2b60e132ebd5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", - "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/35f1adb388946d92e6edab2aa2cb2b60e132ebd5", + "reference": "35f1adb388946d92e6edab2aa2cb2b60e132ebd5", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.2", - "php": "^7.2 || ~8.0, <8.2", + "doctrine/instantiator": "^1.2 || ^2.0", + "php": "^7.4 || 8.0.* || 8.1.* || 8.2.* || 8.3.* || 8.4.*", "phpdocumentor/reflection-docblock": "^5.2", - "sebastian/comparator": "^3.0 || ^4.0", - "sebastian/recursion-context": "^3.0 || ^4.0" + "sebastian/comparator": "^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0", + "sebastian/recursion-context": "^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0" }, "require-dev": { + "friendsofphp/php-cs-fixer": "^3.40", "phpspec/phpspec": "^6.0 || ^7.0", - "phpunit/phpunit": "^8.0 || ^9.0" + "phpstan/phpstan": "^2.1.13", + "phpunit/phpunit": "^8.0 || ^9.0 || ^10.0" }, "type": "library", "extra": { @@ -516,6 +518,7 @@ "keywords": [ "Double", "Dummy", + "dev", "fake", "mock", "spy", @@ -523,9 +526,9 @@ ], "support": { "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/1.14.0" + "source": "https://github.com/phpspec/prophecy/tree/v1.22.0" }, - "time": "2021-09-10T09:02:12+00:00" + "time": "2025-04-29T14:58:06+00:00" }, { "name": "phpunit/php-code-coverage", From 67bbe0379cc53e29249091ce56d1754adf722e04 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 23:25:16 +0000 Subject: [PATCH 17/26] Fix deprecated GitHub Actions versions - Update actions/checkout from v2 to v4 - Update actions/cache from v2 to v4 - Resolves CI failure due to deprecated action versions Co-Authored-By: Ben Sinclair --- .github/workflows/tests.yml | 123 ++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..cd775dc --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,123 @@ +name: Run Tests + +on: + push: + branches: [ master ] + pull_request: + branches: '*' + +jobs: + + postgresql_tests: + + runs-on: ubuntu-latest + timeout-minutes: 10 + strategy: + matrix: + php-versions: [ '7.4', '8.0', '8.1', '8.2', '8.3', '8.4' ] + postgresql-versions: [ '12', '14' ] + name: PHP ${{ matrix.php-versions }} with postgreSQL ${{ matrix.postgresql-versions }} + + services: + postgres: + image: postgres:${{ matrix.postgresql-versions }} + env: + POSTGRES_USER: root + POSTGRES_PASSWORD: rootpassword + POSTGRES_DB: test + ports: + [ '5432:5432' ] + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + + steps: + - uses: actions/checkout@v4 + name: Check Out Code + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + extensions: pdo_pgsql + + - name: Validate composer.json and composer.lock + run: composer validate + + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v4 + with: + path: vendor + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-composer- + + - name: Install dependencies + if: steps.composer-cache.outputs.cache-hit != 'true' + run: composer install --prefer-dist --no-progress --no-suggest + + - name: Setup Database + run: | + sudo apt update + cp tests/db.yml.example tests/db.yml + + - name: Run test suite + run: ./vendor/bin/phpunit + + mysql_tests: + + runs-on: ubuntu-latest + timeout-minutes: 10 + strategy: + matrix: + php-versions: [ '7.4', '8.0', '8.1', '8.2', '8.3', '8.4' ] + mysql-versions: [ '5.7', '8.0' ] + name: PHP ${{ matrix.php-versions }} with MySQL ${{ matrix.mysql-versions }} + + services: + mysql: + image: mysql:${{ matrix.mysql-versions }} + env: + MYSQL_DATABASE: test + MYSQL_ROOT_PASSWORD: rootpassword + MYSQL_HOST: 127.0.0.1 + ports: + [ '3306:3306' ] + options: --health-cmd "mysqladmin ping" --health-interval 10s --health-timeout 5s --health-retries 10 + + steps: + - uses: actions/checkout@v4 + name: Check Out Code + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + extensions: pdo_mysql + + - name: Validate composer.json and composer.lock + run: composer validate + + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v4 + with: + path: vendor + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-composer- + + - name: Install dependencies + if: steps.composer-cache.outputs.cache-hit != 'true' + run: composer install --prefer-dist --no-progress --no-suggest + + - name: Setup Database + run: | + sudo apt update + cp tests/db.yml.example tests/db.yml + + - name: Run test suite + run: ./vendor/bin/phpunit From 3ebbec92d8ae742052f305cfa8af38f821c27dd2 Mon Sep 17 00:00:00 2001 From: Ben Sinclair Date: Tue, 29 Jul 2025 09:38:21 +1000 Subject: [PATCH 18/26] Remove explicit upper PHP version --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 935c786..9f2d573 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,7 @@ "type": "library", "license": "MIT", "require": { - "php": ">=8.0 <8.5", + "php": ">=8.0", "ext-pdo": "*" }, "require-dev": { From 10013a4fb8afc39b137b8331aa447dbc1fd9ceac Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 23:42:37 +0000 Subject: [PATCH 19/26] Update composer.lock after removing upper PHP version constraint Co-Authored-By: Ben Sinclair --- composer.lock | 644 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 387 insertions(+), 257 deletions(-) diff --git a/composer.lock b/composer.lock index aa3eeeb..f89d9d9 100644 --- a/composer.lock +++ b/composer.lock @@ -4,34 +4,83 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d975bb62eb5a45c054f1f8f280ed56dd", + "content-hash": "2ef951c4b9bc3b0d5de2f265f789494c", "packages": [], "packages-dev": [ + { + "name": "doctrine/deprecations", + "version": "1.1.5", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", + "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "phpunit/phpunit": "<=7.5 || >=13" + }, + "require-dev": { + "doctrine/coding-standard": "^9 || ^12 || ^13", + "phpstan/phpstan": "1.4.10 || 2.1.11", + "phpstan/phpstan-phpunit": "^1.0 || ^2", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12", + "psr/log": "^1 || ^2 || ^3" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.5" + }, + "time": "2025-04-07T20:06:18+00:00" + }, { "name": "doctrine/instantiator", - "version": "1.4.0", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b" + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/d56bf6102915de5702778fe20f2de3b2fe570b5b", - "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" + "php": "^8.1" }, "require-dev": { - "doctrine/coding-standard": "^8.0", + "doctrine/coding-standard": "^11", "ext-pdo": "*", "ext-phar": "*", - "phpbench/phpbench": "^0.13 || 1.0.0-alpha2", - "phpstan/phpstan": "^0.12", - "phpstan/phpstan-phpunit": "^0.12", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^1.9.4", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^9.5.27", + "vimeo/psalm": "^5.4" }, "type": "library", "autoload": { @@ -58,7 +107,7 @@ ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.4.0" + "source": "https://github.com/doctrine/instantiator/tree/2.0.0" }, "funding": [ { @@ -74,41 +123,43 @@ "type": "tidelift" } ], - "time": "2020-11-10T18:47:58+00:00" + "time": "2022-12-30T00:23:10+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.10.2", + "version": "1.13.3", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220" + "reference": "faed855a7b5f4d4637717c2b3863e277116beb36" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/776f831124e9c62e1a2c601ecc52e776d8bb7220", - "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/faed855a7b5f4d4637717c2b3863e277116beb36", + "reference": "faed855a7b5f4d4637717c2b3863e277116beb36", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, - "replace": { - "myclabs/deep-copy": "self.version" + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" }, "require-dev": { - "doctrine/collections": "^1.0", - "doctrine/common": "^2.6", - "phpunit/phpunit": "^7.1" + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, "type": "library", "autoload": { - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - }, "files": [ "src/DeepCopy/deep_copy.php" - ] + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -124,7 +175,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.10.2" + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.3" }, "funding": [ { @@ -132,29 +183,31 @@ "type": "tidelift" } ], - "time": "2020-11-13T09:40:50+00:00" + "time": "2025-07-05T12:25:42+00:00" }, { "name": "nikic/php-parser", - "version": "v4.13.0", + "version": "v5.6.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "50953a2691a922aa1769461637869a0a2faa3f53" + "reference": "221b0d0fdf1369c71047ad1d18bb5880017bbc56" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/50953a2691a922aa1769461637869a0a2faa3f53", - "reference": "50953a2691a922aa1769461637869a0a2faa3f53", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/221b0d0fdf1369c71047ad1d18bb5880017bbc56", + "reference": "221b0d0fdf1369c71047ad1d18bb5880017bbc56", "shasum": "" }, "require": { + "ext-ctype": "*", + "ext-json": "*", "ext-tokenizer": "*", - "php": ">=7.0" + "php": ">=7.4" }, "require-dev": { "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "^9.0" }, "bin": [ "bin/php-parse" @@ -162,7 +215,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.9-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -186,26 +239,27 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.13.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.0" }, - "time": "2021-09-20T12:20:58+00:00" + "time": "2025-07-27T20:03:57+00:00" }, { "name": "phar-io/manifest", - "version": "2.0.3", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + "reference": "54750ef60c58e43759730615a392c31c80e23176" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", "shasum": "" }, "require": { "ext-dom": "*", + "ext-libxml": "*", "ext-phar": "*", "ext-xmlwriter": "*", "phar-io/version": "^3.0.1", @@ -246,22 +300,28 @@ "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", "support": { "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.3" + "source": "https://github.com/phar-io/manifest/tree/2.0.4" }, - "time": "2021-07-20T11:28:43+00:00" + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" }, { "name": "phar-io/version", - "version": "3.1.0", + "version": "3.2.1", "source": { "type": "git", "url": "https://github.com/phar-io/version.git", - "reference": "bae7c545bef187884426f042434e561ab1ddb182" + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/bae7c545bef187884426f042434e561ab1ddb182", - "reference": "bae7c545bef187884426f042434e561ab1ddb182", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", "shasum": "" }, "require": { @@ -297,9 +357,9 @@ "description": "Library for handling version information and constraints", "support": { "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/3.1.0" + "source": "https://github.com/phar-io/version/tree/3.2.1" }, - "time": "2021-02-23T14:00:09+00:00" + "time": "2022-02-21T01:04:05+00:00" }, { "name": "phpdocumentor/reflection-common", @@ -356,27 +416,35 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.2.2", + "version": "5.6.2", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556" + "reference": "92dde6a5919e34835c506ac8c523ef095a95ed62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/069a785b2141f5bcf49f3e353548dc1cce6df556", - "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/92dde6a5919e34835c506ac8c523ef095a95ed62", + "reference": "92dde6a5919e34835c506ac8c523ef095a95ed62", "shasum": "" }, "require": { + "doctrine/deprecations": "^1.1", "ext-filter": "*", - "php": "^7.2 || ^8.0", + "php": "^7.4 || ^8.0", "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.3", + "phpdocumentor/type-resolver": "^1.7", + "phpstan/phpdoc-parser": "^1.7|^2.0", "webmozart/assert": "^1.9.1" }, "require-dev": { - "mockery/mockery": "~1.3.2" + "mockery/mockery": "~1.3.5 || ~1.6.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-webmozart-assert": "^1.2", + "phpunit/phpunit": "^9.5", + "psalm/phar": "^5.26" }, "type": "library", "extra": { @@ -400,37 +468,45 @@ }, { "name": "Jaap van Otterdijk", - "email": "account@ijaap.nl" + "email": "opensource@ijaap.nl" } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "support": { "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/master" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.2" }, - "time": "2020-09-03T19:13:55+00:00" + "time": "2025-04-13T19:20:35+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "1.5.1", + "version": "1.10.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "a12f7e301eb7258bb68acd89d4aefa05c2906cae" + "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/a12f7e301eb7258bb68acd89d4aefa05c2906cae", - "reference": "a12f7e301eb7258bb68acd89d4aefa05c2906cae", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/679e3ce485b99e84c775d28e2e96fade9a7fb50a", + "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.0" + "doctrine/deprecations": "^1.0", + "php": "^7.3 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.18|^2.0" }, "require-dev": { "ext-tokenizer": "*", - "psalm/phar": "^4.8" + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^9.5", + "rector/rector": "^0.13.9", + "vimeo/psalm": "^4.25" }, "type": "library", "extra": { @@ -456,9 +532,9 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.5.1" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.10.0" }, - "time": "2021-10-02T14:08:47+00:00" + "time": "2024-11-09T15:12:26+00:00" }, { "name": "phpspec/prophecy", @@ -530,46 +606,93 @@ }, "time": "2025-04-29T14:58:06+00:00" }, + { + "name": "phpstan/phpdoc-parser", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "b9e61a61e39e02dd90944e9115241c7f7e76bfd8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/b9e61a61e39e02dd90944e9115241c7f7e76bfd8", + "reference": "b9e61a61e39e02dd90944e9115241c7f7e76bfd8", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^5.3.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.2.0" + }, + "time": "2025-07-13T07:04:09+00:00" + }, { "name": "phpunit/php-code-coverage", - "version": "9.2.7", + "version": "9.2.32", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "d4c798ed8d51506800b441f7a13ecb0f76f12218" + "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/d4c798ed8d51506800b441f7a13ecb0f76f12218", - "reference": "d4c798ed8d51506800b441f7a13ecb0f76f12218", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/85402a822d1ecf1db1096959413d35e1c37cf1a5", + "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.12.0", + "nikic/php-parser": "^4.19.1 || ^5.1.0", "php": ">=7.3", - "phpunit/php-file-iterator": "^3.0.3", - "phpunit/php-text-template": "^2.0.2", - "sebastian/code-unit-reverse-lookup": "^2.0.2", - "sebastian/complexity": "^2.0", - "sebastian/environment": "^5.1.2", - "sebastian/lines-of-code": "^1.0.3", - "sebastian/version": "^3.0.1", - "theseer/tokenizer": "^1.2.0" + "phpunit/php-file-iterator": "^3.0.6", + "phpunit/php-text-template": "^2.0.4", + "sebastian/code-unit-reverse-lookup": "^2.0.3", + "sebastian/complexity": "^2.0.3", + "sebastian/environment": "^5.1.5", + "sebastian/lines-of-code": "^1.0.4", + "sebastian/version": "^3.0.2", + "theseer/tokenizer": "^1.2.3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^9.6" }, "suggest": { - "ext-pcov": "*", - "ext-xdebug": "*" + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "9.2-dev" + "dev-main": "9.2.x-dev" } }, "autoload": { @@ -597,7 +720,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.7" + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.32" }, "funding": [ { @@ -605,20 +729,20 @@ "type": "github" } ], - "time": "2021-09-17T05:39:03+00:00" + "time": "2024-08-22T04:23:01+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "3.0.5", + "version": "3.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8" + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/aa4be8575f26070b100fccb67faabb28f21f66f8", - "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", "shasum": "" }, "require": { @@ -657,7 +781,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.5" + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" }, "funding": [ { @@ -665,7 +789,7 @@ "type": "github" } ], - "time": "2020-09-28T05:57:25+00:00" + "time": "2021-12-02T12:48:52+00:00" }, { "name": "phpunit/php-invoker", @@ -850,55 +974,50 @@ }, { "name": "phpunit/phpunit", - "version": "9.5.10", + "version": "9.6.23", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "c814a05837f2edb0d1471d6e3f4ab3501ca3899a" + "reference": "43d2cb18d0675c38bd44982a5d1d88f6d53d8d95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c814a05837f2edb0d1471d6e3f4ab3501ca3899a", - "reference": "c814a05837f2edb0d1471d6e3f4ab3501ca3899a", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/43d2cb18d0675c38bd44982a5d1d88f6d53d8d95", + "reference": "43d2cb18d0675c38bd44982a5d1d88f6d53d8d95", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.3.1", + "doctrine/instantiator": "^1.5.0 || ^2", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", + "myclabs/deep-copy": "^1.13.1", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", "php": ">=7.3", - "phpspec/prophecy": "^1.12.1", - "phpunit/php-code-coverage": "^9.2.7", - "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-code-coverage": "^9.2.32", + "phpunit/php-file-iterator": "^3.0.6", "phpunit/php-invoker": "^3.1.1", - "phpunit/php-text-template": "^2.0.3", - "phpunit/php-timer": "^5.0.2", - "sebastian/cli-parser": "^1.0.1", - "sebastian/code-unit": "^1.0.6", - "sebastian/comparator": "^4.0.5", - "sebastian/diff": "^4.0.3", - "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.3", - "sebastian/global-state": "^5.0.1", - "sebastian/object-enumerator": "^4.0.3", - "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^2.3.4", + "phpunit/php-text-template": "^2.0.4", + "phpunit/php-timer": "^5.0.3", + "sebastian/cli-parser": "^1.0.2", + "sebastian/code-unit": "^1.0.8", + "sebastian/comparator": "^4.0.8", + "sebastian/diff": "^4.0.6", + "sebastian/environment": "^5.1.5", + "sebastian/exporter": "^4.0.6", + "sebastian/global-state": "^5.0.7", + "sebastian/object-enumerator": "^4.0.4", + "sebastian/resource-operations": "^3.0.4", + "sebastian/type": "^3.2.1", "sebastian/version": "^3.0.2" }, - "require-dev": { - "ext-pdo": "*", - "phpspec/prophecy-phpunit": "^2.0.1" - }, "suggest": { - "ext-soap": "*", - "ext-xdebug": "*" + "ext-soap": "To be able to generate mocks based on WSDL files", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" }, "bin": [ "phpunit" @@ -906,15 +1025,15 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.5-dev" + "dev-master": "9.6-dev" } }, "autoload": { - "classmap": [ - "src/" - ], "files": [ "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -937,32 +1056,45 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.10" + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.23" }, "funding": [ { - "url": "https://phpunit.de/donate.html", + "url": "https://phpunit.de/sponsors.html", "type": "custom" }, { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" } ], - "time": "2021-09-25T07:38:51+00:00" + "time": "2025-05-02T06:40:34+00:00" }, { "name": "sebastian/cli-parser", - "version": "1.0.1", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/2b56bea83a09de3ac06bb18b92f068e60cc6f50b", + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b", "shasum": "" }, "require": { @@ -997,7 +1129,7 @@ "homepage": "https://github.com/sebastianbergmann/cli-parser", "support": { "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.2" }, "funding": [ { @@ -1005,7 +1137,7 @@ "type": "github" } ], - "time": "2020-09-28T06:08:49+00:00" + "time": "2024-03-02T06:27:43+00:00" }, { "name": "sebastian/code-unit", @@ -1120,16 +1252,16 @@ }, { "name": "sebastian/comparator", - "version": "4.0.6", + "version": "4.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "55f4261989e546dc112258c7a75935a81a7ce382" + "reference": "fa0f136dd2334583309d32b62544682ee972b51a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55f4261989e546dc112258c7a75935a81a7ce382", - "reference": "55f4261989e546dc112258c7a75935a81a7ce382", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a", "shasum": "" }, "require": { @@ -1182,7 +1314,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.6" + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" }, "funding": [ { @@ -1190,24 +1322,24 @@ "type": "github" } ], - "time": "2020-10-26T15:49:45+00:00" + "time": "2022-09-14T12:41:17+00:00" }, { "name": "sebastian/complexity", - "version": "2.0.2", + "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a", + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a", "shasum": "" }, "require": { - "nikic/php-parser": "^4.7", + "nikic/php-parser": "^4.18 || ^5.0", "php": ">=7.3" }, "require-dev": { @@ -1239,7 +1371,7 @@ "homepage": "https://github.com/sebastianbergmann/complexity", "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", - "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3" }, "funding": [ { @@ -1247,20 +1379,20 @@ "type": "github" } ], - "time": "2020-10-26T15:52:27+00:00" + "time": "2023-12-22T06:19:30+00:00" }, { "name": "sebastian/diff", - "version": "4.0.4", + "version": "4.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc", + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc", "shasum": "" }, "require": { @@ -1305,7 +1437,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.6" }, "funding": [ { @@ -1313,20 +1445,20 @@ "type": "github" } ], - "time": "2020-10-26T13:10:38+00:00" + "time": "2024-03-02T06:30:58+00:00" }, { "name": "sebastian/environment", - "version": "5.1.3", + "version": "5.1.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "388b6ced16caa751030f6a69e588299fa09200ac" + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/388b6ced16caa751030f6a69e588299fa09200ac", - "reference": "388b6ced16caa751030f6a69e588299fa09200ac", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", "shasum": "" }, "require": { @@ -1368,7 +1500,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.3" + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" }, "funding": [ { @@ -1376,20 +1508,20 @@ "type": "github" } ], - "time": "2020-09-28T05:52:38+00:00" + "time": "2023-02-03T06:03:51+00:00" }, { "name": "sebastian/exporter", - "version": "4.0.3", + "version": "4.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "d89cc98761b8cb5a1a235a6b703ae50d34080e65" + "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/d89cc98761b8cb5a1a235a6b703ae50d34080e65", - "reference": "d89cc98761b8cb5a1a235a6b703ae50d34080e65", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/78c00df8f170e02473b682df15bfcdacc3d32d72", + "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72", "shasum": "" }, "require": { @@ -1438,14 +1570,14 @@ } ], "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "http://www.github.com/sebastianbergmann/exporter", + "homepage": "https://www.github.com/sebastianbergmann/exporter", "keywords": [ "export", "exporter" ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.3" + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.6" }, "funding": [ { @@ -1453,20 +1585,20 @@ "type": "github" } ], - "time": "2020-09-28T05:24:23+00:00" + "time": "2024-03-02T06:33:00+00:00" }, { "name": "sebastian/global-state", - "version": "5.0.3", + "version": "5.0.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "23bd5951f7ff26f12d4e3242864df3e08dec4e49" + "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/23bd5951f7ff26f12d4e3242864df3e08dec4e49", - "reference": "23bd5951f7ff26f12d4e3242864df3e08dec4e49", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", + "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", "shasum": "" }, "require": { @@ -1509,7 +1641,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.3" + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.7" }, "funding": [ { @@ -1517,24 +1649,24 @@ "type": "github" } ], - "time": "2021-06-11T13:31:12+00:00" + "time": "2024-03-02T06:35:11+00:00" }, { "name": "sebastian/lines-of-code", - "version": "1.0.3", + "version": "1.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5", "shasum": "" }, "require": { - "nikic/php-parser": "^4.6", + "nikic/php-parser": "^4.18 || ^5.0", "php": ">=7.3" }, "require-dev": { @@ -1566,7 +1698,7 @@ "homepage": "https://github.com/sebastianbergmann/lines-of-code", "support": { "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4" }, "funding": [ { @@ -1574,7 +1706,7 @@ "type": "github" } ], - "time": "2020-11-28T06:42:11+00:00" + "time": "2023-12-22T06:20:34+00:00" }, { "name": "sebastian/object-enumerator", @@ -1690,16 +1822,16 @@ }, { "name": "sebastian/recursion-context", - "version": "4.0.4", + "version": "4.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172" + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172", - "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", "shasum": "" }, "require": { @@ -1738,10 +1870,10 @@ } ], "description": "Provides functionality to recursively process PHP variables", - "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" }, "funding": [ { @@ -1749,20 +1881,20 @@ "type": "github" } ], - "time": "2020-10-26T13:17:30+00:00" + "time": "2023-02-03T06:07:39+00:00" }, { "name": "sebastian/resource-operations", - "version": "3.0.3", + "version": "3.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e", "shasum": "" }, "require": { @@ -1774,7 +1906,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -1795,8 +1927,7 @@ "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", "support": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4" }, "funding": [ { @@ -1804,33 +1935,32 @@ "type": "github" } ], - "abandoned": true, - "time": "2020-09-28T06:45:17+00:00" + "time": "2024-03-14T16:00:52+00:00" }, { "name": "sebastian/type", - "version": "2.3.4", + "version": "3.2.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914" + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/b8cd8a1c753c90bc1a0f5372170e3e489136f914", - "reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", "shasum": "" }, "require": { "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^9.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.3-dev" + "dev-master": "3.2-dev" } }, "autoload": { @@ -1853,7 +1983,7 @@ "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/2.3.4" + "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" }, "funding": [ { @@ -1861,7 +1991,7 @@ "type": "github" } ], - "time": "2021-06-15T12:49:02+00:00" + "time": "2023-02-03T06:13:03+00:00" }, { "name": "sebastian/version", @@ -1918,29 +2048,29 @@ }, { "name": "symfony/deprecation-contracts", - "version": "v2.4.0", + "version": "v3.6.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627" + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5f38c8804a9e97d23e0c8d63341088cd8a22d627", - "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=8.1" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "2.4-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" } }, "autoload": { @@ -1965,7 +2095,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v2.4.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" }, "funding": [ { @@ -1981,45 +2111,45 @@ "type": "tidelift" } ], - "time": "2021-03-23T23:28:01+00:00" + "time": "2024-09-25T14:21:43+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.23.0", + "version": "v1.32.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce" + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/46cd95797e9df938fdd2b03693b5fca5e64b01ce", - "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" }, "suggest": { "ext-ctype": "For best performance" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - }, "files": [ "bootstrap.php" - ] + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2044,7 +2174,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.23.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0" }, "funding": [ { @@ -2060,32 +2190,32 @@ "type": "tidelift" } ], - "time": "2021-02-19T12:13:01+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/yaml", - "version": "v5.3.6", + "version": "v5.4.45", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "4500fe63dc9c6ffc32d3b1cb0448c329f9c814b7" + "reference": "a454d47278cc16a5db371fe73ae66a78a633371e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/4500fe63dc9c6ffc32d3b1cb0448c329f9c814b7", - "reference": "4500fe63dc9c6ffc32d3b1cb0448c329f9c814b7", + "url": "https://api.github.com/repos/symfony/yaml/zipball/a454d47278cc16a5db371fe73ae66a78a633371e", + "reference": "a454d47278cc16a5db371fe73ae66a78a633371e", "shasum": "" }, "require": { "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1", - "symfony/polyfill-ctype": "~1.8" + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-ctype": "^1.8" }, "conflict": { - "symfony/console": "<4.4" + "symfony/console": "<5.3" }, "require-dev": { - "symfony/console": "^4.4|^5.0" + "symfony/console": "^5.3|^6.0" }, "suggest": { "symfony/console": "For validating YAML files using the lint command" @@ -2119,7 +2249,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v5.3.6" + "source": "https://github.com/symfony/yaml/tree/v5.4.45" }, "funding": [ { @@ -2135,20 +2265,20 @@ "type": "tidelift" } ], - "time": "2021-07-29T06:20:01+00:00" + "time": "2024-09-25T14:11:13+00:00" }, { "name": "theseer/tokenizer", - "version": "1.2.1", + "version": "1.2.3", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", "shasum": "" }, "require": { @@ -2177,7 +2307,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + "source": "https://github.com/theseer/tokenizer/tree/1.2.3" }, "funding": [ { @@ -2185,25 +2315,25 @@ "type": "github" } ], - "time": "2021-07-28T10:34:58+00:00" + "time": "2024-03-03T12:36:25+00:00" }, { "name": "webmozart/assert", - "version": "1.10.0", + "version": "1.11.0", "source": { "type": "git", "url": "https://github.com/webmozarts/assert.git", - "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25" + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/6964c76c7804814a842473e0c8fd15bab0f18e25", - "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0", - "symfony/polyfill-ctype": "^1.8" + "ext-ctype": "*", + "php": "^7.2 || ^8.0" }, "conflict": { "phpstan/phpstan": "<0.12.20", @@ -2241,9 +2371,9 @@ ], "support": { "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.10.0" + "source": "https://github.com/webmozarts/assert/tree/1.11.0" }, - "time": "2021-03-09T10:59:23+00:00" + "time": "2022-06-03T18:03:27+00:00" } ], "aliases": [], @@ -2252,7 +2382,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=8.0 <8.5", + "php": ">=8.0", "ext-pdo": "*" }, "platform-dev": {}, From 79f7f05292781ff85644a98f7e30f81126d00734 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 23:46:26 +0000 Subject: [PATCH 20/26] Fix PHP 8.0 compatibility by constraining doctrine/instantiator and symfony/deprecation-contracts Co-Authored-By: Ben Sinclair --- composer.json | 4 +++- composer.lock | 44 ++++++++++++++++++++++---------------------- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/composer.json b/composer.json index 9f2d573..38ccacf 100644 --- a/composer.json +++ b/composer.json @@ -10,7 +10,9 @@ "require-dev": { "phpunit/phpunit": "^9.4", "symfony/yaml": "^5.0", - "phpspec/prophecy": "^1.22.0" + "phpspec/prophecy": "^1.22.0", + "doctrine/instantiator": "^1.5", + "symfony/deprecation-contracts": "^2.4" }, "scripts":{ "test": "./vendor/bin/phpunit -c phpunit.xml" diff --git a/composer.lock b/composer.lock index f89d9d9..3c6b5f5 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "2ef951c4b9bc3b0d5de2f265f789494c", + "content-hash": "73e103ce6c073e02a467617792310203", "packages": [], "packages-dev": [ { @@ -57,30 +57,30 @@ }, { "name": "doctrine/instantiator", - "version": "2.0.0", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", - "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", "shasum": "" }, "require": { - "php": "^8.1" + "php": "^7.1 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^11", + "doctrine/coding-standard": "^9 || ^11", "ext-pdo": "*", "ext-phar": "*", - "phpbench/phpbench": "^1.2", - "phpstan/phpstan": "^1.9.4", - "phpstan/phpstan-phpunit": "^1.3", - "phpunit/phpunit": "^9.5.27", - "vimeo/psalm": "^5.4" + "phpbench/phpbench": "^0.16 || ^1", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-phpunit": "^1", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.30 || ^5.4" }, "type": "library", "autoload": { @@ -107,7 +107,7 @@ ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/2.0.0" + "source": "https://github.com/doctrine/instantiator/tree/1.5.0" }, "funding": [ { @@ -123,7 +123,7 @@ "type": "tidelift" } ], - "time": "2022-12-30T00:23:10+00:00" + "time": "2022-12-30T00:15:36+00:00" }, { "name": "myclabs/deep-copy", @@ -2048,20 +2048,20 @@ }, { "name": "symfony/deprecation-contracts", - "version": "v3.6.0", + "version": "v2.5.4", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" + "reference": "605389f2a7e5625f273b53960dc46aeaf9c62918" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", - "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/605389f2a7e5625f273b53960dc46aeaf9c62918", + "reference": "605389f2a7e5625f273b53960dc46aeaf9c62918", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=7.1" }, "type": "library", "extra": { @@ -2070,7 +2070,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "2.5-dev" } }, "autoload": { @@ -2095,7 +2095,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.4" }, "funding": [ { @@ -2111,7 +2111,7 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:21:43+00:00" + "time": "2024-09-25T14:11:13+00:00" }, { "name": "symfony/polyfill-ctype", From 4200f2f302122d23eedf4f04d795abeb12649c6c Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 23:52:28 +0000 Subject: [PATCH 21/26] Update dependencies for PHP 8.3/8.4 compatibility - Update phpspec/prophecy to v1.22.0 to support PHP 8.3/8.4 - Update PHPUnit and related packages for compatibility - Fixes CI failures on extended PHP version matrix Co-Authored-By: Ben Sinclair --- composer.lock | 79 ++++++++++++++++++++++++++------------------------- 1 file changed, 41 insertions(+), 38 deletions(-) diff --git a/composer.lock b/composer.lock index c6e3bae..8f549ff 100644 --- a/composer.lock +++ b/composer.lock @@ -462,28 +462,30 @@ }, { "name": "phpspec/prophecy", - "version": "1.14.0", + "version": "v1.22.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e" + "reference": "35f1adb388946d92e6edab2aa2cb2b60e132ebd5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", - "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/35f1adb388946d92e6edab2aa2cb2b60e132ebd5", + "reference": "35f1adb388946d92e6edab2aa2cb2b60e132ebd5", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.2", - "php": "^7.2 || ~8.0, <8.2", + "doctrine/instantiator": "^1.2 || ^2.0", + "php": "^7.4 || 8.0.* || 8.1.* || 8.2.* || 8.3.* || 8.4.*", "phpdocumentor/reflection-docblock": "^5.2", - "sebastian/comparator": "^3.0 || ^4.0", - "sebastian/recursion-context": "^3.0 || ^4.0" + "sebastian/comparator": "^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0", + "sebastian/recursion-context": "^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0" }, "require-dev": { + "friendsofphp/php-cs-fixer": "^3.40", "phpspec/phpspec": "^6.0 || ^7.0", - "phpunit/phpunit": "^8.0 || ^9.0" + "phpstan/phpstan": "^2.1.13", + "phpunit/phpunit": "^8.0 || ^9.0 || ^10.0" }, "type": "library", "extra": { @@ -516,6 +518,7 @@ "keywords": [ "Double", "Dummy", + "dev", "fake", "mock", "spy", @@ -523,29 +526,29 @@ ], "support": { "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/1.14.0" + "source": "https://github.com/phpspec/prophecy/tree/v1.22.0" }, - "time": "2021-09-10T09:02:12+00:00" + "time": "2025-04-29T14:58:06+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "9.2.7", + "version": "9.2.15", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "d4c798ed8d51506800b441f7a13ecb0f76f12218" + "reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/d4c798ed8d51506800b441f7a13ecb0f76f12218", - "reference": "d4c798ed8d51506800b441f7a13ecb0f76f12218", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2e9da11878c4202f97915c1cb4bb1ca318a63f5f", + "reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.12.0", + "nikic/php-parser": "^4.13.0", "php": ">=7.3", "phpunit/php-file-iterator": "^3.0.3", "phpunit/php-text-template": "^2.0.2", @@ -594,7 +597,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.7" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.15" }, "funding": [ { @@ -602,20 +605,20 @@ "type": "github" } ], - "time": "2021-09-17T05:39:03+00:00" + "time": "2022-03-07T09:28:20+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "3.0.5", + "version": "3.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8" + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/aa4be8575f26070b100fccb67faabb28f21f66f8", - "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", "shasum": "" }, "require": { @@ -654,7 +657,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.5" + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" }, "funding": [ { @@ -662,7 +665,7 @@ "type": "github" } ], - "time": "2020-09-28T05:57:25+00:00" + "time": "2021-12-02T12:48:52+00:00" }, { "name": "phpunit/php-invoker", @@ -847,16 +850,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.5.10", + "version": "9.5.18", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "c814a05837f2edb0d1471d6e3f4ab3501ca3899a" + "reference": "1b5856028273bfd855e60a887278857d872ec67a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c814a05837f2edb0d1471d6e3f4ab3501ca3899a", - "reference": "c814a05837f2edb0d1471d6e3f4ab3501ca3899a", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1b5856028273bfd855e60a887278857d872ec67a", + "reference": "1b5856028273bfd855e60a887278857d872ec67a", "shasum": "" }, "require": { @@ -872,7 +875,7 @@ "phar-io/version": "^3.0.2", "php": ">=7.3", "phpspec/prophecy": "^1.12.1", - "phpunit/php-code-coverage": "^9.2.7", + "phpunit/php-code-coverage": "^9.2.13", "phpunit/php-file-iterator": "^3.0.5", "phpunit/php-invoker": "^3.1.1", "phpunit/php-text-template": "^2.0.3", @@ -907,11 +910,11 @@ } }, "autoload": { - "classmap": [ - "src/" - ], "files": [ "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -934,11 +937,11 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.10" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.18" }, "funding": [ { - "url": "https://phpunit.de/donate.html", + "url": "https://phpunit.de/sponsors.html", "type": "custom" }, { @@ -946,7 +949,7 @@ "type": "github" } ], - "time": "2021-09-25T07:38:51+00:00" + "time": "2022-03-08T06:52:28+00:00" }, { "name": "sebastian/cli-parser", @@ -2245,13 +2248,13 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { "php": ">=7.4", "ext-pdo": "*" }, - "platform-dev": [], - "plugin-api-version": "2.0.0" + "platform-dev": {}, + "plugin-api-version": "2.6.0" } From 0abc14f9f86ddc69946269f16779751bf1254402 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 23:59:23 +0000 Subject: [PATCH 22/26] Fix PHPUnit compatibility issues for PHP 8.2 - Replace deprecated contains() with assertStringContainsString() - Resolves 'Call to undefined method' errors in MigrationIntegrationTest - Addresses CI failures on PHP 8.2 with MySQL 5.7 and PostgreSQL 14 Co-Authored-By: Ben Sinclair --- tests/MigrationIntegrationTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/MigrationIntegrationTest.php b/tests/MigrationIntegrationTest.php index 5599325..b5892b4 100644 --- a/tests/MigrationIntegrationTest.php +++ b/tests/MigrationIntegrationTest.php @@ -118,9 +118,9 @@ public function testMigrateMigrationsWithMysql() $usersTable = $this->pdo->query('DESCRIBE users')->fetchAll(); $this->assertSame('email', $usersTable[0]['Field']); - $this->contains('varchar', $usersTable[0]['Type']); + $this->assertStringContainsString('varchar', $usersTable[0]['Type']); $this->assertSame('user_id', $usersTable[1]['Field']); - $this->contains('int', $usersTable[1]['Type']); + $this->assertStringContainsString('int', $usersTable[1]['Type']); } public function testRollbackMigrationsWithMysql() @@ -144,8 +144,8 @@ public function testRollbackMigrationsWithMysql() $usersTable = $this->pdo->query('DESCRIBE users')->fetchAll(); $this->assertSame('email', $usersTable[0]['Field']); - $this->contains('varchar', $usersTable[0]['Type']); + $this->assertStringContainsString('varchar', $usersTable[0]['Type']); $this->assertSame('username', $usersTable[1]['Field']); - $this->contains('varchar', $usersTable[1]['Type']); + $this->assertStringContainsString('varchar', $usersTable[1]['Type']); } } From d0ce3c266e92983f0d120859a59e81ab8b2b70f9 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 00:04:56 +0000 Subject: [PATCH 23/26] Fix column matching logic in TableOperation.apply() for proper reduction - Use appropriate matching logic for CHANGE vs other operations - CHANGE operations match by before/after names - Other operations match by column name - Resolves testReduceRewindMigrationsWithChange failure Co-Authored-By: Ben Sinclair --- src/Operation/TableOperation.php | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/Operation/TableOperation.php b/src/Operation/TableOperation.php index 3d7773c..394f2cf 100644 --- a/src/Operation/TableOperation.php +++ b/src/Operation/TableOperation.php @@ -206,9 +206,17 @@ public function apply(ReducibleOperationInterface $operation): ?ReducibleOperati $offset = array_search($options['after'], array_keys($columns)) + 1; } - // Remove existing operation for the column using before/after name matching + // Remove existing operation for the column using proper name matching foreach ($columns as $existing => $column) { - if ($column->getAfterName() === $columnOperation->getBeforeName()) { + $shouldRemove = false; + + if ($columnOperation->getOperation() === ColumnOperation::CHANGE) { + $shouldRemove = ($column->getAfterName() === $columnOperation->getBeforeName()); + } else { + $shouldRemove = ($column->getName() === $columnOperation->getName()); + } + + if ($shouldRemove) { unset($columns[$existing]); break; } @@ -277,9 +285,17 @@ public function apply(ReducibleOperationInterface $operation): ?ReducibleOperati foreach ($operation->getColumnOperations() as $columnOperation) { $originalOperation = $columnOperation->getOperation(); - // Remove existing operation for the column using before/after name matching + // Remove existing operation for the column using proper name matching foreach ($columns as $existing => $column) { - if ($column->getAfterName() === $columnOperation->getBeforeName()) { + $shouldRemove = false; + + if ($columnOperation->getOperation() === ColumnOperation::CHANGE) { + $shouldRemove = ($column->getAfterName() === $columnOperation->getBeforeName()); + } else { + $shouldRemove = ($column->getName() === $columnOperation->getName()); + } + + if ($shouldRemove) { unset($columns[$existing]); $originalOperation = $column->getOperation(); break; From 370634aa12337b81b63bfda5928d0cf14eed3fde Mon Sep 17 00:00:00 2001 From: Ben Sinclair Date: Tue, 29 Jul 2025 10:53:25 +1000 Subject: [PATCH 24/26] Combine MODIFY and CHANGE conditions Resolves https://github.com/tithely/exo/pull/22/files#r724598094 --- src/Operation/TableOperation.php | 34 ++++++++++++++------------------ 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/src/Operation/TableOperation.php b/src/Operation/TableOperation.php index 394f2cf..3b2041f 100644 --- a/src/Operation/TableOperation.php +++ b/src/Operation/TableOperation.php @@ -93,30 +93,26 @@ public function reverse(?ReversibleOperationInterface $originalOperation = null) $columnOperations[] = $originalColumn; } - if ($columnOperation->getOperation() === ColumnOperation::MODIFY) { + if ($columnOperation->getOperation() === ColumnOperation::MODIFY || $columnOperation->getOperation() === ColumnOperation::CHANGE) { if (!$originalColumn) { throw new LogicException('Cannot revert a column that does not exist.'); } - $columnOperations[] = new ColumnOperation( - $columnOperation->getName(), - $columnOperation->getOperation(), - $originalColumn->getOptions() - ); - } - - if ($columnOperation->getOperation() === ColumnOperation::CHANGE) { - if (!$originalColumn) { - throw new LogicException('Cannot revert a column that does not exist.'); + if ($columnOperation->getOperation() === ColumnOperation::MODIFY) { + $columnOperations[] = new ColumnOperation( + $columnOperation->getName(), + $columnOperation->getOperation(), + $originalColumn->getOptions() + ); + } else { + $options = $originalColumn->getOptions(); + $options['new_name'] = $columnOperation->getBeforeName(); + $columnOperations[] = new ColumnOperation( + $columnOperation->getAfterName(), + $columnOperation->getOperation(), + $options + ); } - - $options = $originalColumn->getOptions(); - $options['new_name'] = $columnOperation->getBeforeName(); - $columnOperations[] = new ColumnOperation( - $columnOperation->getAfterName(), - $columnOperation->getOperation(), - $options - ); } } From 973264a4184a42cb99ff8b23683565ae4cb24318 Mon Sep 17 00:00:00 2001 From: Ben Sinclair Date: Tue, 29 Jul 2025 11:04:41 +1000 Subject: [PATCH 25/26] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 63b887c..c23f70a 100644 --- a/README.md +++ b/README.md @@ -15,3 +15,4 @@ $ composer require tithely/exo ## Documentation - [Introduction](doc/01-introduction.md) - [Usage](doc/02-usage.md) +- [Testing](doc/03-testing.md) From 0b5666514eed3309da8171c6e9faa7c489790f4b Mon Sep 17 00:00:00 2001 From: Ben Sinclair Date: Tue, 29 Jul 2025 11:05:02 +1000 Subject: [PATCH 26/26] Fix compatibility issues with newer PHP versions --- src/ExecMigration.php | 2 +- src/FunctionMigration.php | 4 ++-- src/History.php | 2 +- src/Operation/ExecOperation.php | 2 +- src/Operation/FunctionOperation.php | 4 ++-- src/Operation/ProcedureOperation.php | 2 +- src/Operation/ViewOperation.php | 2 +- src/ProcedureMigration.php | 2 +- src/ViewMigration.php | 2 +- .../20200605_alter_user_counts_view-with_context.php | 2 +- tests/Traits/UsesYamlConfig.php | 2 +- 11 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/ExecMigration.php b/src/ExecMigration.php index 45cb35e..9c5ba77 100644 --- a/src/ExecMigration.php +++ b/src/ExecMigration.php @@ -33,7 +33,7 @@ public static function create(string $name): ExecMigration * @param string $name * @param string|null $body */ - private function __construct(string $name, string $body = null) + private function __construct(string $name, ?string $body = null) { $this->name = $name; $this->body = $body; diff --git a/src/FunctionMigration.php b/src/FunctionMigration.php index 200fd11..0cf50ec 100644 --- a/src/FunctionMigration.php +++ b/src/FunctionMigration.php @@ -104,13 +104,13 @@ public static function drop(string $name): FunctionMigration private function __construct( string $name, string $operation, - ReturnTypeOperation $returnType = null, + ?ReturnTypeOperation $returnType = null, bool $deterministic = false, string $dataUse = 'READS SQL DATA', string $language = 'plpgsql', array $parameterOperations = [], array $variableOperations = [], - string $body = null + ?string $body = null ) { $this->name = $name; $this->operation = $operation; diff --git a/src/History.php b/src/History.php index d51c614..7346709 100644 --- a/src/History.php +++ b/src/History.php @@ -64,7 +64,7 @@ public function add(string $version, MigrationInterface $migrationOrView) * @param array|null $versions * @return History */ - public function clone(array $versions = null): History + public function clone(?array $versions = null): History { $history = new History(); diff --git a/src/Operation/ExecOperation.php b/src/Operation/ExecOperation.php index d604250..11fe946 100644 --- a/src/Operation/ExecOperation.php +++ b/src/Operation/ExecOperation.php @@ -15,7 +15,7 @@ final class ExecOperation extends AbstractOperation * @param string $name * @param ?string $body */ - public function __construct(string $name, string $body = null) { + public function __construct(string $name, ?string $body = null) { $this->name = $name; $this->body = $body; } diff --git a/src/Operation/FunctionOperation.php b/src/Operation/FunctionOperation.php index ced0f31..7ab24e8 100644 --- a/src/Operation/FunctionOperation.php +++ b/src/Operation/FunctionOperation.php @@ -66,13 +66,13 @@ final class FunctionOperation extends AbstractOperation implements ReversibleOpe public function __construct( string $name, string $operation, - ReturnTypeOperation $returnType = null, + ?ReturnTypeOperation $returnType = null, bool $deterministic = false, string $dataUse = 'READS SQL DATA', string $language = 'plpgsql', array $parameterOperations = [], array $variableOperations = [], - string $body = null + ?string $body = null ) { $this->name = $name; $this->operation = $operation; diff --git a/src/Operation/ProcedureOperation.php b/src/Operation/ProcedureOperation.php index e92242b..f8f3df4 100644 --- a/src/Operation/ProcedureOperation.php +++ b/src/Operation/ProcedureOperation.php @@ -64,7 +64,7 @@ public function __construct( string $language = 'plpgsql', array $inParameterOperations = [], array $outParameterOperations = [], - string $body = null + ?string $body = null ) { $this->name = $name; $this->operation = $operation; diff --git a/src/Operation/ViewOperation.php b/src/Operation/ViewOperation.php index 39de8c0..175b74f 100644 --- a/src/Operation/ViewOperation.php +++ b/src/Operation/ViewOperation.php @@ -27,7 +27,7 @@ final class ViewOperation extends AbstractOperation implements ReversibleOperati * @param string $operation * @param string|null $body */ - public function __construct(string $name, string $operation, string $body = null) + public function __construct(string $name, string $operation, ?string $body = null) { $this->name = $name; $this->operation = $operation; diff --git a/src/ProcedureMigration.php b/src/ProcedureMigration.php index 84aff22..6eaff87 100644 --- a/src/ProcedureMigration.php +++ b/src/ProcedureMigration.php @@ -90,7 +90,7 @@ private function __construct( string $language = 'plpgsql', array $inParameterOperations = [], array $outParameterOperations = [], - string $body = null + ?string $body = null ) { $this->name = $name; $this->operation = $operation; diff --git a/src/ViewMigration.php b/src/ViewMigration.php index a3497bc..b82859a 100644 --- a/src/ViewMigration.php +++ b/src/ViewMigration.php @@ -61,7 +61,7 @@ public static function drop(string $name) * @param string $operation * @param string|null $body */ - private function __construct(string $name, string $operation, string $body = null) + private function __construct(string $name, string $operation, ?string $body = null) { $this->name = $name; $this->operation = $operation; diff --git a/tests/Migrations/20200605_alter_user_counts_view-with_context.php b/tests/Migrations/20200605_alter_user_counts_view-with_context.php index df3bc8e..5483db4 100644 --- a/tests/Migrations/20200605_alter_user_counts_view-with_context.php +++ b/tests/Migrations/20200605_alter_user_counts_view-with_context.php @@ -4,4 +4,4 @@ $tenant_database_name = $tenant_database_name ?? 'undefined'; return Exo\ViewMigration::alter('user_counts') - ->withBody("SELECT COUNT(id) AS user_count FROM ${tenant_database_name}.users"); + ->withBody("SELECT COUNT(id) AS user_count FROM {$tenant_database_name}.users"); diff --git a/tests/Traits/UsesYamlConfig.php b/tests/Traits/UsesYamlConfig.php index 51d4bc3..9fd7f3b 100644 --- a/tests/Traits/UsesYamlConfig.php +++ b/tests/Traits/UsesYamlConfig.php @@ -16,7 +16,7 @@ trait UsesYamlConfig * When the value cannot be found by the given key, we default to null. * @throws Exception */ - protected static function yaml(string $keys = null) + protected static function yaml(?string $keys = null) { $yaml = self::readYamlFile();