Skip to content

Commit 3936acf

Browse files
committed
DiffGenerator: align generated diff with ORM SchemaTool
1 parent fe0fcea commit 3936acf

File tree

3 files changed

+43
-63
lines changed

3 files changed

+43
-63
lines changed

phpstan.neon.dist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ parameters:
3535
path: src/DependencyFactory.php
3636

3737
-
38-
message: '~^Strict comparison using !== between callable\(\)\: mixed and null will always evaluate to true\.$~'
38+
message: '~^Strict comparison using === between callable\(\)\: mixed and null will always evaluate to false\.$~'
3939
path: src/Generator/DiffGenerator.php
4040

4141
-

src/Generator/DiffGenerator.php

Lines changed: 27 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@
1414

1515
use function method_exists;
1616
use function preg_match;
17-
use function strpos;
18-
use function substr;
1917

2018
/**
2119
* The DiffGenerator class is responsible for comparing two Doctrine\DBAL\Schema\Schema instances and generating a
@@ -53,16 +51,16 @@ static function ($assetName) use ($filterExpression) {
5351
$assetName = $assetName->getName();
5452
}
5553

56-
return preg_match($filterExpression, $assetName);
54+
return (bool) preg_match($filterExpression, $assetName);
5755
},
5856
);
5957
}
6058

59+
$toSchema = $this->createToSchema();
60+
6161
$fromSchema = $fromEmptySchema
6262
? $this->createEmptySchema()
63-
: $this->createFromSchema();
64-
65-
$toSchema = $this->createToSchema();
63+
: $this->createFromSchema($toSchema);
6664

6765
// prior to DBAL 4.0, the schema name was set to the first element in the search path,
6866
// which is not necessarily the default schema name
@@ -112,42 +110,37 @@ private function createEmptySchema(): Schema
112110
return $this->emptySchemaProvider->createSchema();
113111
}
114112

115-
private function createFromSchema(): Schema
116-
{
117-
return $this->schemaManager->introspectSchema();
118-
}
119-
120-
private function createToSchema(): Schema
113+
/**
114+
* Creates the schema from the database, ensuring tables from the target schema are whitelisted for comparison.
115+
*
116+
* @see https://github.com/doctrine/orm/pull/7875
117+
*/
118+
private function createFromSchema(Schema $toSchema): Schema
121119
{
122-
$toSchema = $this->schemaProvider->createSchema();
120+
// backup schema assets filter
121+
$previousFilter = $this->dbalConfiguration->getSchemaAssetsFilter();
123122

124-
$schemaAssetsFilter = $this->dbalConfiguration->getSchemaAssetsFilter();
123+
if ($previousFilter === null) {
124+
return $this->schemaManager->introspectSchema();
125+
}
125126

126-
if ($schemaAssetsFilter !== null) {
127-
foreach ($toSchema->getTables() as $table) {
128-
$tableName = $table->getName();
127+
// whitelist assets we already know about in $toSchema, use the existing filter otherwise
128+
$this->dbalConfiguration->setSchemaAssetsFilter(static function ($asset) use ($previousFilter, $toSchema): bool {
129+
$assetName = $asset instanceof AbstractAsset ? $asset->getName() : $asset;
129130

130-
if ($schemaAssetsFilter($this->resolveTableName($tableName))) {
131-
continue;
132-
}
131+
return $toSchema->hasTable($assetName) || $toSchema->hasSequence($assetName) || $previousFilter($asset);
132+
});
133133

134-
$toSchema->dropTable($tableName);
135-
}
134+
try {
135+
return $this->schemaManager->introspectSchema();
136+
} finally {
137+
// restore schema assets filter
138+
$this->dbalConfiguration->setSchemaAssetsFilter($previousFilter);
136139
}
137-
138-
return $toSchema;
139140
}
140141

141-
/**
142-
* Resolve a table name from its fully qualified name. The `$name` argument
143-
* comes from Doctrine\DBAL\Schema\Table#getName which can sometimes return
144-
* a namespaced name with the form `{namespace}.{tableName}`. This extracts
145-
* the table name from that.
146-
*/
147-
private function resolveTableName(string $name): string
142+
private function createToSchema(): Schema
148143
{
149-
$pos = strpos($name, '.');
150-
151-
return $pos === false ? $name : substr($name, $pos + 1);
144+
return $this->schemaProvider->createSchema();
152145
}
153146
}

tests/Generator/DiffGeneratorTest.php

Lines changed: 15 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -37,34 +37,21 @@ public function testGenerate(): void
3737
$fromSchema = $this->createMock(Schema::class);
3838
$toSchema = $this->createMock(Schema::class);
3939

40-
$this->dbalConfiguration->expects(self::once())
41-
->method('setSchemaAssetsFilter');
40+
// first time sets a regex check using the provided $filterExpression pattern
41+
// second time sets a function wrapping the initial one so it does not remove assets provided in $toSchema
42+
// third time reverts to the initial regex-only check
43+
$this->dbalConfiguration->expects(self::exactly(3))
44+
->method('setSchemaAssetsFilter')
45+
->willReturnCallback(function (callable $newSchemaAssetsFilter) {
46+
self::assertTrue($newSchemaAssetsFilter('table_name1'));
47+
});
4248

4349
$this->dbalConfiguration->expects(self::once())
4450
->method('getSchemaAssetsFilter')
4551
->willReturn(
4652
static fn ($name): bool => $name === 'table_name1',
4753
);
4854

49-
$table1 = $this->createMock(Table::class);
50-
$table1->expects(self::once())
51-
->method('getName')
52-
->willReturn('schema.table_name1');
53-
54-
$table2 = $this->createMock(Table::class);
55-
$table2->expects(self::once())
56-
->method('getName')
57-
->willReturn('schema.table_name2');
58-
59-
$table3 = $this->createMock(Table::class);
60-
$table3->expects(self::once())
61-
->method('getName')
62-
->willReturn('schema.table_name3');
63-
64-
$toSchema->expects(self::once())
65-
->method('getTables')
66-
->willReturn([$table1, $table2, $table3]);
67-
6855
$this->emptySchemaProvider->expects(self::never())
6956
->method('createSchema');
7057

@@ -76,9 +63,13 @@ public function testGenerate(): void
7663
->method('createSchema')
7764
->willReturn($toSchema);
7865

79-
$toSchema->expects(self::exactly(2))
80-
->method('dropTable')
81-
->willReturnSelf();
66+
$toSchema->expects(self::once())
67+
->method('hasTable')
68+
->willReturn(false);
69+
70+
$toSchema->expects(self::once())
71+
->method('hasSequence')
72+
->willReturn(false);
8273

8374
$schemaDiff = self::createStub(SchemaDiff::class);
8475

@@ -126,10 +117,6 @@ public function testGenerateFromEmptySchema(): void
126117
$this->dbalConfiguration->expects(self::never())
127118
->method('setSchemaAssetsFilter');
128119

129-
$this->dbalConfiguration->expects(self::once())
130-
->method('getSchemaAssetsFilter')
131-
->willReturn(static fn () => true);
132-
133120
$toSchema->method('getTables')
134121
->willReturn([new Table('table_name')]);
135122

0 commit comments

Comments
 (0)