From 836618296565f5000c59607240ad70987999e9c8 Mon Sep 17 00:00:00 2001 From: Alex Standiford Date: Sun, 2 Aug 2026 20:30:46 -0400 Subject: [PATCH] Empty database prefixes no longer produce leading underscores in table names Str::append('', '_') returns '_', so Table::getName() prepended a bare underscore for every empty global or local prefix, silently corrupting table names (billing-service observed _billing_subscriptions with an empty DB_PREFIX). Non-empty prefixes keep the exact prior format; only the empty-prefix contribution changes, from '_' to nothing. --- lib/Abstracts/Table.php | 15 +++++- tests/Unit/Abstracts/TableTest.php | 81 ++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 tests/Unit/Abstracts/TableTest.php diff --git a/lib/Abstracts/Table.php b/lib/Abstracts/Table.php index 0d2131e..608df70 100644 --- a/lib/Abstracts/Table.php +++ b/lib/Abstracts/Table.php @@ -42,11 +42,22 @@ public function __construct( */ public function getName(): string { - return Str::append($this->globalPrefixProvider->getGlobalDatabasePrefix(), '_') - . Str::append($this->localPrefixProvider->getLocalDatabasePrefix(), '_') + return $this->formatPrefix($this->globalPrefixProvider->getGlobalDatabasePrefix()) + . $this->formatPrefix($this->localPrefixProvider->getLocalDatabasePrefix()) . $this->getUnprefixedName(); } + /** + * Adds a separator to non-empty table prefixes. + * + * @param string $prefix + * @return string + */ + private function formatPrefix(string $prefix): string + { + return $prefix === '' ? '' : Str::append($prefix, '_'); + } + /** @inheritdoc */ abstract public function getUnprefixedName(): string; diff --git a/tests/Unit/Abstracts/TableTest.php b/tests/Unit/Abstracts/TableTest.php new file mode 100644 index 0000000..d9f4cd8 --- /dev/null +++ b/tests/Unit/Abstracts/TableTest.php @@ -0,0 +1,81 @@ +assertSame('glob_loc_name', $this->makeTable('glob', 'loc')->getName()); + } + + public function testGetNameOmitsAnEmptyGlobalPrefix(): void + { + $this->assertSame('loc_name', $this->makeTable('', 'loc')->getName()); + } + + public function testGetNameOmitsAnEmptyLocalPrefix(): void + { + $this->assertSame('glob_name', $this->makeTable('glob', '')->getName()); + } + + public function testGetNameOmitsBothEmptyPrefixes(): void + { + $this->assertSame('name', $this->makeTable('', '')->getName()); + } + + private function makeTable(string $globalPrefix, string $localPrefix): Table + { + $globalPrefixProvider = $this->createMock(HasGlobalDatabasePrefix::class); + $globalPrefixProvider->method('getGlobalDatabasePrefix')->willReturn($globalPrefix); + + $localPrefixProvider = $this->createMock(HasLocalDatabasePrefix::class); + $localPrefixProvider->method('getLocalDatabasePrefix')->willReturn($localPrefix); + + return new class( + $localPrefixProvider, + $globalPrefixProvider, + $this->createMock(HasCharsetProvider::class), + $this->createMock(HasCollateProvider::class), + $this->createMock(TableSchemaService::class) + ) extends Table { + public function getUnprefixedName(): string + { + return 'name'; + } + + public function getAlias(): string + { + return 'name'; + } + + public function getTableVersion(): string + { + return '1'; + } + + public function getColumns(): array + { + return []; + } + + public function getIndices(): array + { + return []; + } + + public function getSingularUnprefixedName(): string + { + return 'name'; + } + }; + } +}