Files
kaffeekasse-saas/scripts/check-db-table-prefix.php
T

97 lines
3.9 KiB
PHP

<?php
declare(strict_types=1);
require __DIR__ . '/../app/prefixed-pdo.php';
$assertSame = static function (string $expected, string $actual, string $message): void {
if ($expected !== $actual) {
fwrite(STDERR, "FEHLER: {$message}\nErwartet: {$expected}\nTatsächlich: {$actual}\n");
exit(1);
}
};
$assertSame('', app_validate_db_table_prefix(''), 'Ein leerer Prefix muss für die einmalige Datenübernahme erlaubt sein.');
$assertSame('test_', app_validate_db_table_prefix('test_'), 'Der Test-Prefix muss gültig sein.');
$assertSame('prod_', app_validate_db_table_prefix('prod_'), 'Der Produktions-Prefix muss gültig sein.');
foreach (['Test_', '1test_', 'test', 'test-'] as $invalidPrefix) {
try {
app_validate_db_table_prefix($invalidPrefix);
fwrite(STDERR, "FEHLER: Ungültiger Prefix {$invalidPrefix} wurde akzeptiert.\n");
exit(1);
} catch (RuntimeException) {
// Erwartet.
}
}
$previousAppEnv = getenv('APP_ENV');
$previousDbPrefix = getenv('DB_TABLE_PREFIX');
putenv('APP_ENV=prod');
putenv('DB_TABLE_PREFIX');
try {
app_db_table_prefix();
fwrite(STDERR, "FEHLER: Produktion wurde ohne DB_TABLE_PREFIX akzeptiert.\n");
exit(1);
} catch (RuntimeException) {
// Erwartet: Außerhalb von Dev gilt fail-closed.
} finally {
$previousAppEnv === false ? putenv('APP_ENV') : putenv('APP_ENV=' . $previousAppEnv);
$previousDbPrefix === false ? putenv('DB_TABLE_PREFIX') : putenv('DB_TABLE_PREFIX=' . $previousDbPrefix);
}
$assertSame(
'SELECT * FROM test_users JOIN test_tenants ON test_tenants.id = test_users.tenant_id',
app_prefix_database_sql('SELECT * FROM users JOIN tenants ON tenants.id = users.tenant_id', 'test_'),
'Normale Laufzeit-Abfragen müssen vollständig umgeschrieben werden.'
);
$assertSame(
'SELECT * FROM test_users',
app_prefix_database_sql('SELECT * FROM test_users', 'test_'),
'Bereits umgeschriebene Tabellennamen dürfen keinen doppelten Prefix erhalten.'
);
$assertSame(
"ALTER TABLE test_users ADD CONSTRAINT test_fk_users_tenant FOREIGN KEY (tenant_id) REFERENCES test_tenants(id)",
app_prefix_database_sql(
'ALTER TABLE users ADD CONSTRAINT fk_users_tenant FOREIGN KEY (tenant_id) REFERENCES tenants(id)',
'test_'
),
'Tabellen und Fremdschlüssel müssen gemeinsam umgeschrieben werden.'
);
$assertSame(
"WHERE TABLE_NAME = 'test_users' AND CONSTRAINT_NAME = 'test_fk_users_tenant'",
app_prefix_database_sql("WHERE TABLE_NAME = 'users' AND CONSTRAINT_NAME = 'fk_users_tenant'", 'test_'),
'Dynamische Migrationen müssen auch INFORMATION_SCHEMA-Vergleiche umschreiben.'
);
$migrationFiles = glob(__DIR__ . '/../database/migrations/*.sql') ?: [];
if (count($migrationFiles) !== 30) {
fwrite(STDERR, 'FEHLER: Erwartet wurden 30 Migrationen, gefunden wurden ' . count($migrationFiles) . ".\n");
exit(1);
}
$allMigrationSql = '';
foreach ($migrationFiles as $migrationFile) {
$contents = file_get_contents($migrationFile);
if ($contents === false) {
fwrite(STDERR, "FEHLER: Migration {$migrationFile} konnte nicht gelesen werden.\n");
exit(1);
}
$allMigrationSql .= "\n" . $contents;
}
$prefixedMigrationSql = app_prefix_database_sql($allMigrationSql, 'test_');
foreach (app_database_table_names() as $table) {
$rawPattern = '/(?<![A-Za-z0-9_])' . preg_quote($table, '/') . '(?![A-Za-z0-9_])/i';
if (preg_match($rawPattern, $allMigrationSql) === 1 && preg_match($rawPattern, $prefixedMigrationSql) === 1) {
fwrite(STDERR, "FEHLER: Tabelle {$table} blieb in den umgeschriebenen Migrationen ohne Prefix.\n");
exit(1);
}
}
if (preg_match('/(?<![A-Za-z0-9_])fk_[A-Za-z0-9_]+(?![A-Za-z0-9_])/i', $prefixedMigrationSql) === 1) {
fwrite(STDERR, "FEHLER: Mindestens ein Fremdschlüssel blieb in den Migrationen ohne Prefix.\n");
exit(1);
}
echo "DB-Prefix-Prüfung bestanden: Produktion ist fail-closed; Laufzeit-SQL und alle 30 Migrationen werden getrennt umgeschrieben.\n";