Lines
48.67%
110 / 226
Methods
60.00%
18 / 30
Classes
0.00%
0 / 1
| Name | Lines | Methods | CRAP | ||||
|---|---|---|---|---|---|---|---|
| getVersion | 100.00% | 1 / 1 | 100.00% | 1 / 1 | 1 | ||
| getDescription | 100.00% | 1 / 1 | 100.00% | 1 / 1 | 1 | ||
| up | 40.50% | 32 / 79 | 0.00% | 0 / 1 | 17.32 | ||
| [phpMyFAQ\Setup\Migration\AbstractMigration] __construct | 100.00% | 2 / 2 | 100.00% | 1 / 1 | 1 | ||
| [phpMyFAQ\Setup\Migration\AbstractMigration] getDependencies | 100.00% | 1 / 1 | 100.00% | 1 / 1 | 1 | ||
| [phpMyFAQ\Setup\Migration\AbstractMigration] isReversible | 100.00% | 1 / 1 | 100.00% | 1 / 1 | 1 | ||
| [phpMyFAQ\Setup\Migration\AbstractMigration] down | 100.00% | 1 / 1 | 100.00% | 1 / 1 | 1 | ||
| [phpMyFAQ\Setup\Migration\AbstractMigration] table | 100.00% | 1 / 1 | 100.00% | 1 / 1 | 1 | ||
| [phpMyFAQ\Setup\Migration\AbstractMigration] isDbType | 100.00% | 2 / 2 | 100.00% | 1 / 1 | 1 | ||
| [phpMyFAQ\Setup\Migration\AbstractMigration] isMySql | 100.00% | 1 / 1 | 100.00% | 1 / 1 | 1 | ||
| [phpMyFAQ\Setup\Migration\AbstractMigration] isPostgreSql | 100.00% | 1 / 1 | 100.00% | 1 / 1 | 1 | ||
| [phpMyFAQ\Setup\Migration\AbstractMigration] isSqlite | 100.00% | 1 / 1 | 100.00% | 1 / 1 | 1 | ||
| [phpMyFAQ\Setup\Migration\AbstractMigration] isSqlServer | 100.00% | 1 / 1 | 100.00% | 1 / 1 | 1 | ||
| [phpMyFAQ\Setup\Migration\AbstractMigration] addColumn | 62.50% | 5 / 8 | 0.00% | 0 / 1 | 4.84 | ||
| [phpMyFAQ\Setup\Migration\AbstractMigration] dropColumn | 100.00% | 2 / 2 | 100.00% | 1 / 1 | 1 | ||
| [phpMyFAQ\Setup\Migration\AbstractMigration] dropColumns | 100.00% | 6 / 6 | 100.00% | 1 / 1 | 1 | ||
| [phpMyFAQ\Setup\Migration\AbstractMigration] createIndex | 33.33% | 5 / 15 | 0.00% | 0 / 1 | 8.74 | ||
| [phpMyFAQ\Setup\Migration\AbstractMigration] createUniqueIndex | 28.57% | 6 / 21 | 0.00% | 0 / 1 | 6.28 | ||
| [phpMyFAQ\Setup\Migration\AbstractMigration] indexExists | 40.62% | 13 / 32 | 0.00% | 0 / 1 | 10.23 | ||
| [phpMyFAQ\Setup\Migration\AbstractMigration] dropTable | 100.00% | 1 / 1 | 100.00% | 1 / 1 | 1 | ||
| [phpMyFAQ\Setup\Migration\AbstractMigration] dropTableIfExists | 100.00% | 1 / 1 | 100.00% | 1 / 1 | 1 | ||
| [phpMyFAQ\Setup\Migration\AbstractMigration] updateLanguageCode | 100.00% | 11 / 11 | 100.00% | 1 / 1 | 1 | ||
| [phpMyFAQ\Setup\Migration\AbstractMigration] integerType | 66.66% | 2 / 3 | 0.00% | 0 / 1 | 3.33 | ||
| [phpMyFAQ\Setup\Migration\AbstractMigration] textType | 66.66% | 2 / 3 | 0.00% | 0 / 1 | 3.33 | ||
| [phpMyFAQ\Setup\Migration\AbstractMigration] varcharType | 0.00% | 0 / 3 | 0.00% | 0 / 1 | 12 | ||
| [phpMyFAQ\Setup\Migration\AbstractMigration] timestampType | 40.00% | 4 / 10 | 0.00% | 0 / 1 | 21.82 | ||
| [phpMyFAQ\Setup\Migration\AbstractMigration] booleanType | 0.00% | 0 / 4 | 0.00% | 0 / 1 | 20 | ||
| [phpMyFAQ\Setup\Migration\AbstractMigration] autoIncrementColumn | 0.00% | 0 / 6 | 0.00% | 0 / 1 | 42 | ||
| [phpMyFAQ\Setup\Migration\AbstractMigration] getConfig | 0.00% | 0 / 1 | 0.00% | 0 / 1 | 2 | ||
| [phpMyFAQ\Setup\Migration\AbstractMigration] getChecksum | 100.00% | 6 / 6 | 100.00% | 1 / 1 | 1 | ||
| 25 | readonly class Migration320Alpha extends AbstractMigration | |
| 26 | { | |
| 27 | public function getVersion(): string | |
| 28 | { | |
| 29 | return '3.2.0-alpha'; | |
| 30 | } | |
| 31 | ||
| 32 | public function getDescription(): string | |
| 33 | { | |
| 34 | return 'Microsoft Entra ID support, 2FA support, backup table, Google ReCAPTCHA v2, remove section tables'; | |
| 35 | } | |
| 36 | ||
| 37 | public function up(OperationRecorder $recorder): void | |
| 38 | { | |
| 39 | // Microsoft Entra ID support and 2FA-support | |
| 40 | $recorder->addConfig('security.enableSignInWithMicrosoft', false); | |
| 41 | ||
| 42 | // Add columns for OAuth tokens | |
| 43 | if ($this->isSqlite()) { | |
| 44 | $recorder->addSql( | |
| 45 | sprintf('ALTER TABLE %sfaquser ADD COLUMN refresh_token TEXT NULL DEFAULT NULL', $this->tablePrefix), | |
| 46 | 'Add OAuth refresh_token to faquser (SQLite)', | |
| 47 | ); | |
| 48 | $recorder->addSql( | |
| 49 | sprintf('ALTER TABLE %sfaquser ADD COLUMN access_token TEXT NULL DEFAULT NULL', $this->tablePrefix), | |
| 50 | 'Add OAuth access_token to faquser (SQLite)', | |
| 51 | ); | |
| 52 | $recorder->addSql( | |
| 53 | sprintf( | |
| 54 | 'ALTER TABLE %sfaquser ADD COLUMN code_verifier VARCHAR(255) NULL DEFAULT NULL', | |
| 55 | $this->tablePrefix, | |
| 56 | ), | |
| 57 | 'Add OAuth code_verifier to faquser (SQLite)', | |
| 58 | ); | |
| 59 | $recorder->addSql( | |
| 60 | sprintf('ALTER TABLE %sfaquser ADD COLUMN jwt TEXT NULL DEFAULT NULL', $this->tablePrefix), | |
| 61 | 'Add OAuth jwt to faquser (SQLite)', | |
| 62 | ); | |
| 63 | ||
| 64 | $recorder->addSql( | |
| 65 | sprintf( | |
| 66 | 'ALTER TABLE %sfaquserdata ADD COLUMN twofactor_enabled INT(1) NULL DEFAULT 0', | |
| 67 | $this->tablePrefix, | |
| 68 | ), | |
| 69 | 'Add 2FA twofactor_enabled to faquserdata (SQLite)', | |
| 70 | ); | |
| 71 | $recorder->addSql( | |
| 72 | sprintf( | |
| 73 | 'ALTER TABLE %sfaquserdata ADD COLUMN secret VARCHAR(128) NULL DEFAULT NULL', | |
| 74 | $this->tablePrefix, | |
| 75 | ), | |
| 76 | 'Add 2FA secret to faquserdata (SQLite)', | |
| 77 | ); | |
| 78 | } | |
| 79 | ||
| 80 | if ($this->isPostgreSql()) { | |
| 81 | // "IF NOT EXISTS" keeps a re-run alive after a previously failed update | |
| 82 | // already applied this statement - the version number is only updated | |
| 83 | // at the very end, so a failed run starts over from the old version. | |
| 84 | $recorder->addSql(sprintf( | |
| 85 | 'ALTER TABLE %sfaquser | |
| 86 | ADD COLUMN IF NOT EXISTS refresh_token TEXT NULL DEFAULT NULL, | |
| 87 | ADD COLUMN IF NOT EXISTS access_token TEXT NULL DEFAULT NULL, | |
| 88 | ADD COLUMN IF NOT EXISTS code_verifier VARCHAR(255) NULL DEFAULT NULL, | |
| 89 | ADD COLUMN IF NOT EXISTS jwt TEXT NULL DEFAULT NULL', | |
| 90 | $this->tablePrefix, | |
| 91 | ), 'Add OAuth token columns to faquser'); | |
| 92 | ||
| 93 | $recorder->addSql(sprintf( | |
| 94 | 'ALTER TABLE %sfaquserdata | |
| 95 | ADD COLUMN IF NOT EXISTS twofactor_enabled INT NULL DEFAULT 0, | |
| 96 | ADD COLUMN IF NOT EXISTS secret VARCHAR(128) NULL DEFAULT NULL', | |
| 97 | $this->tablePrefix, | |
| 98 | ), 'Add 2FA columns to faquserdata'); | |
| 99 | } | |
| 100 | ||
| 101 | if (!$this->isSqlite() && !$this->isPostgreSql()) { | |
| 102 | $recorder->addSql(sprintf( | |
| 103 | 'ALTER TABLE %sfaquser | |
| 104 | ADD refresh_token TEXT NULL DEFAULT NULL, | |
| 105 | ADD access_token TEXT NULL DEFAULT NULL, | |
| 106 | ADD code_verifier VARCHAR(255) NULL DEFAULT NULL, | |
| 107 | ADD jwt TEXT NULL DEFAULT NULL', | |
| 108 | $this->tablePrefix, | |
| 109 | ), 'Add OAuth token columns to faquser'); | |
| 110 | ||
| 111 | $recorder->addSql(sprintf( | |
| 112 | 'ALTER TABLE %sfaquserdata | |
| 113 | ADD twofactor_enabled INT NULL DEFAULT 0, | |
| 114 | ADD secret VARCHAR(128) NULL DEFAULT NULL', | |
| 115 | $this->tablePrefix, | |
| 116 | ), 'Add 2FA columns to faquserdata'); | |
| 117 | } | |
| 118 | ||
| 119 | // New backup table | |
| 120 | // SQL Server has no "CREATE TABLE IF NOT EXISTS" | |
| 121 | $createTable = $this->isSqlServer() ? 'CREATE TABLE' : 'CREATE TABLE IF NOT EXISTS'; | |
| 122 | $timestampType = $this->timestampType(false); | |
| 123 | $recorder->addSql( | |
| 124 | sprintf('%s %sfaqbackup ( | |
| 125 | id INT NOT NULL, | |
| 126 | filename VARCHAR(255) NOT NULL, | |
| 127 | authkey VARCHAR(255) NOT NULL, | |
| 128 | authcode VARCHAR(255) NOT NULL, | |
| 129 | created %s NOT NULL, | |
| 130 | PRIMARY KEY (id))', $createTable, $this->tablePrefix, $timestampType), | |
| 131 | 'Create backup table', | |
| 132 | ); | |
| 133 | ||
| 134 | // Migrate MySQL from MyISAM to InnoDB | |
| 135 | if ($this->isMySql()) { | |
| 136 | $recorder->addSql( | |
| 137 | sprintf('ALTER TABLE %sfaqdata ENGINE=INNODB', $this->tablePrefix), | |
| 138 | 'Migrate faqdata to InnoDB', | |
| 139 | ); | |
| 140 | } | |
| 141 | ||
| 142 | // New options | |
| 143 | $recorder->addConfig('main.enableAskQuestions', true); | |
| 144 | $recorder->addConfig('main.enableNotifications', true); | |
| 145 | ||
| 146 | // Update options | |
| 147 | $recorder->renameConfig('security.loginWithEmailAddress', 'security.loginWithEmailAddress'); | |
| 148 | ||
| 149 | // Handle permLevel migration - note: actual update requires reading current value | |
| 150 | // This is handled by the configuration update operation | |
| 151 | ||
| 152 | // Google ReCAPTCHAv3 support | |
| 153 | $recorder->addConfig('security.enableGoogleReCaptchaV2', false); | |
| 154 | $recorder->addConfig('security.googleReCaptchaV2SiteKey', ''); | |
| 155 | $recorder->addConfig('security.googleReCaptchaV2SecretKey', ''); | |
| 156 | ||
| 157 | // Remove section tables ("IF EXISTS" is supported by all databases, SQL Server since 2016) | |
| 158 | $recorder->addSql($this->dropTableIfExists('faqsections'), 'Drop faqsections table'); | |
| 159 | $recorder->addSql($this->dropTableIfExists('faqsection_category'), 'Drop faqsection_category table'); | |
| 160 | $recorder->addSql($this->dropTableIfExists('faqsection_group'), 'Drop faqsection_group table'); | |
| 161 | $recorder->addSql($this->dropTableIfExists('faqsection_news'), 'Drop faqsection_news table'); | |
| 162 | } | |
| 163 | } |
Inherited from phpMyFAQ\Setup\Migration\AbstractMigration
| 32 | public function __construct( | |
| 33 | protected Configuration $configuration, | |
| 34 | ) { | |
| 35 | $this->tablePrefix = Database::getTablePrefix(); | |
| 36 | $this->dbType = Database::getType(); | |
| 37 | } |
| 44 | public function getDependencies(): array | |
| 45 | { | |
| 46 | return []; | |
| 47 | } |
| 52 | public function isReversible(): bool | |
| 53 | { | |
| 54 | return false; | |
| 55 | } |
| 60 | public function down(OperationRecorder $recorder): void | |
| 61 | { | |
| 62 | // Override in subclass if migration is reversible | |
| 63 | } |
| 68 | protected function table(string $name): string | |
| 69 | { | |
| 70 | return $this->tablePrefix . $name; | |
| 71 | } |
| 78 | protected function isDbType(string|array $types): bool | |
| 79 | { | |
| 80 | $types = (array) $types; | |
| 81 | return in_array($this->dbType, $types, strict: true); | |
| 82 | } |
| 87 | protected function isMySql(): bool | |
| 88 | { | |
| 89 | return $this->isDbType(['mysqli', 'pdo_mysql']); | |
| 90 | } |
| 95 | protected function isPostgreSql(): bool | |
| 96 | { | |
| 97 | return $this->isDbType(['pgsql', 'pdo_pgsql']); | |
| 98 | } |
| 103 | protected function isSqlite(): bool | |
| 104 | { | |
| 105 | return $this->isDbType(['sqlite3', 'pdo_sqlite']); | |
| 106 | } |
| 111 | protected function isSqlServer(): bool | |
| 112 | { | |
| 113 | return $this->isDbType(['sqlsrv', 'pdo_sqlsrv']); | |
| 114 | } |
| 120 | protected function addColumn(string $table, string $column, string $type, ?string $default = null): string | |
| 121 | { | |
| 122 | $tableName = $this->table($table); | |
| 123 | $defaultClause = ''; | |
| 124 | if ($default !== null) { | |
| 125 | $defaultClause = " DEFAULT {$default}"; | |
| 126 | } | |
| 127 | ||
| 128 | if ($this->isSqlite()) { | |
| 129 | return sprintf('ALTER TABLE %s ADD COLUMN %s %s%s', $tableName, $column, $type, $defaultClause); | |
| 130 | } | |
| 131 | ||
| 132 | // MySQL, PostgreSQL, SQL Server | |
| 133 | $columnKeyword = $this->isSqlServer() ? '' : 'COLUMN '; | |
| 134 | return sprintf('ALTER TABLE %s ADD %s%s %s%s', $tableName, $columnKeyword, $column, $type, $defaultClause); | |
| 135 | } |
| 140 | protected function dropColumn(string $table, string $column): string | |
| 141 | { | |
| 142 | $tableName = $this->table($table); | |
| 143 | return sprintf('ALTER TABLE %s DROP COLUMN %s', $tableName, $column); | |
| 144 | } |
| 153 | protected function dropColumns(string $table, array $columns): array | |
| 154 | { | |
| 155 | $tableName = $this->table($table); | |
| 156 | return array_map(static fn(string $col): string => sprintf( | |
| 157 | 'ALTER TABLE %s DROP COLUMN %s', | |
| 158 | $tableName, | |
| 159 | $col, | |
| 160 | ), $columns); | |
| 161 | } |
| 168 | protected function createIndex(string $table, string $indexName, string|array $columns): string | |
| 169 | { | |
| 170 | $tableName = $this->table($table); | |
| 171 | $columnList = is_array($columns) ? implode(', ', $columns) : $columns; | |
| 172 | ||
| 173 | if ($this->isSqlServer()) { | |
| 174 | return sprintf( | |
| 175 | "IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = '%s' AND object_id = OBJECT_ID('%s')) " | |
| 176 | . 'CREATE INDEX %s ON %s (%s)', | |
| 177 | $indexName, | |
| 178 | $tableName, | |
| 179 | $indexName, | |
| 180 | $tableName, | |
| 181 | $columnList, | |
| 182 | ); | |
| 183 | } | |
| 184 | ||
| 185 | // MySQL/MariaDB don't support IF NOT EXISTS in older versions | |
| 186 | // Use conditional execution or manual check instead | |
| 187 | if ($this->isMySql()) { | |
| 188 | return sprintf('CREATE INDEX %s ON %s (%s)', $indexName, $tableName, $columnList); | |
| 189 | } | |
| 190 | ||
| 191 | // PostgreSQL and SQLite support IF NOT EXISTS | |
| 192 | return sprintf('CREATE INDEX IF NOT EXISTS %s ON %s (%s)', $indexName, $tableName, $columnList); | |
| 193 | } |
| 204 | protected function createUniqueIndex(string $table, string $indexName, string|array $columns): string | |
| 205 | { | |
| 206 | $tableName = $this->table($table); | |
| 207 | $columns = (array) $columns; | |
| 208 | $columnList = implode(', ', $columns); | |
| 209 | ||
| 210 | if ($this->isSqlServer()) { | |
| 211 | $whereClause = implode(' AND ', array_map(static fn(string $col): string => sprintf( | |
| 212 | '%s IS NOT NULL', | |
| 213 | $col, | |
| 214 | ), $columns)); | |
| 215 | return sprintf( | |
| 216 | "IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = '%s' AND object_id = OBJECT_ID('%s')) " | |
| 217 | . 'CREATE UNIQUE INDEX %s ON %s (%s) WHERE %s', | |
| 218 | $indexName, | |
| 219 | $tableName, | |
| 220 | $indexName, | |
| 221 | $tableName, | |
| 222 | $columnList, | |
| 223 | $whereClause, | |
| 224 | ); | |
| 225 | } | |
| 226 | ||
| 227 | if ($this->isMySql()) { | |
| 228 | return sprintf('CREATE UNIQUE INDEX %s ON %s (%s)', $indexName, $tableName, $columnList); | |
| 229 | } | |
| 230 | ||
| 231 | return sprintf('CREATE UNIQUE INDEX IF NOT EXISTS %s ON %s (%s)', $indexName, $tableName, $columnList); | |
| 232 | } |
| 238 | protected function indexExists(string $table, string $indexName): string | |
| 239 | { | |
| 240 | $db = $this->configuration->getDb(); | |
| 241 | $tableName = $this->table($table); | |
| 242 | $escapedTableName = $db->escape($tableName); | |
| 243 | $escapedIndexName = $db->escape($indexName); | |
| 244 | ||
| 245 | if ($this->isMySql()) { | |
| 246 | return sprintf( | |
| 247 | 'SELECT COUNT(*) as idx_count FROM information_schema.STATISTICS ' | |
| 248 | . "WHERE table_schema = DATABASE() AND table_name = '%s' AND index_name = '%s'", | |
| 249 | $escapedTableName, | |
| 250 | $escapedIndexName, | |
| 251 | ); | |
| 252 | } | |
| 253 | ||
| 254 | if ($this->isPostgreSql()) { | |
| 255 | return sprintf( | |
| 256 | 'SELECT COUNT(*) as idx_count FROM pg_indexes ' | |
| 257 | . "WHERE schemaname = 'public' AND tablename = '%s' AND indexname = '%s'", | |
| 258 | $escapedTableName, | |
| 259 | $escapedIndexName, | |
| 260 | ); | |
| 261 | } | |
| 262 | ||
| 263 | if ($this->isSqlite()) { | |
| 264 | return sprintf( | |
| 265 | 'SELECT COUNT(*) as idx_count FROM sqlite_master ' | |
| 266 | . "WHERE type = 'index' AND name = '%s' AND tbl_name = '%s'", | |
| 267 | $escapedIndexName, | |
| 268 | $escapedTableName, | |
| 269 | ); | |
| 270 | } | |
| 271 | ||
| 272 | if ($this->isSqlServer()) { | |
| 273 | return sprintf( | |
| 274 | 'SELECT COUNT(*) as idx_count FROM sys.indexes ' . "WHERE name = '%s' AND object_id = OBJECT_ID('%s')", | |
| 275 | $escapedIndexName, | |
| 276 | $escapedTableName, | |
| 277 | ); | |
| 278 | } | |
| 279 | ||
| 280 | throw new \RuntimeException("Unsupported database type for index existence check: {$this->dbType}"); | |
| 281 | } |
| 286 | protected function dropTable(string $table): string | |
| 287 | { | |
| 288 | return sprintf('DROP TABLE %s', $this->table($table)); | |
| 289 | } |
| 294 | protected function dropTableIfExists(string $table): string | |
| 295 | { | |
| 296 | return sprintf('DROP TABLE IF EXISTS %s', $this->table($table)); | |
| 297 | } |
| 302 | protected function updateLanguageCode(string $table, string $column, string $oldCode, string $newCode): string | |
| 303 | { | |
| 304 | $db = $this->configuration->getDb(); | |
| 305 | $escapedOldCode = $db->escape($oldCode); | |
| 306 | $escapedNewCode = $db->escape($newCode); | |
| 307 | ||
| 308 | return sprintf( | |
| 309 | "UPDATE %s SET %s='%s' WHERE %s='%s'", | |
| 310 | $this->table($table), | |
| 311 | $column, | |
| 312 | $escapedNewCode, | |
| 313 | $column, | |
| 314 | $escapedOldCode, | |
| 315 | ); | |
| 316 | } |
| 321 | protected function integerType(): string | |
| 322 | { | |
| 323 | return match ($this->dbType) { | |
| 324 | 'mysqli', 'pdo_mysql' => 'INT', | |
| 325 | default => 'INTEGER', | |
| 326 | }; | |
| 327 | } |
| 332 | protected function textType(): string | |
| 333 | { | |
| 334 | return match ($this->dbType) { | |
| 335 | 'sqlsrv', 'pdo_sqlsrv' => 'NVARCHAR(MAX)', | |
| 336 | default => 'TEXT', | |
| 337 | }; | |
| 338 | } |
| 343 | protected function varcharType(int $length): string | |
| 344 | { | |
| 345 | return match ($this->dbType) { | |
| 346 | 'sqlsrv', 'pdo_sqlsrv' => "NVARCHAR({$length})", | |
| 347 | default => "VARCHAR({$length})", | |
| 348 | }; | |
| 349 | } |
| 354 | protected function timestampType(bool $withDefault = true): string | |
| 355 | { | |
| 356 | $type = match ($this->dbType) { | |
| 357 | 'mysqli', 'pdo_mysql' => 'TIMESTAMP', | |
| 358 | 'sqlsrv', 'pdo_sqlsrv' => 'DATETIME', | |
| 359 | default => 'TIMESTAMP', | |
| 360 | }; | |
| 361 | ||
| 362 | if (!$withDefault) { | |
| 363 | return $type; | |
| 364 | } | |
| 365 | ||
| 366 | return match ($this->dbType) { | |
| 367 | 'sqlsrv', 'pdo_sqlsrv' => $type . ' NOT NULL DEFAULT GETDATE()', | |
| 368 | 'sqlite3', 'pdo_sqlite' => 'DATETIME DEFAULT CURRENT_TIMESTAMP', | |
| 369 | default => $type . ' NOT NULL DEFAULT CURRENT_TIMESTAMP', | |
| 370 | }; | |
| 371 | } |
| 376 | protected function booleanType(): string | |
| 377 | { | |
| 378 | return match ($this->dbType) { | |
| 379 | 'mysqli', 'pdo_mysql' => 'TINYINT(1)', | |
| 380 | 'sqlsrv', 'pdo_sqlsrv' => 'TINYINT', | |
| 381 | default => 'INTEGER', | |
| 382 | }; | |
| 383 | } |
| 388 | protected function autoIncrementColumn(string $columnName = 'id'): string | |
| 389 | { | |
| 390 | return match ($this->dbType) { | |
| 391 | 'mysqli', 'pdo_mysql' => "{$columnName} INT NOT NULL AUTO_INCREMENT", | |
| 392 | 'pgsql', 'pdo_pgsql' => "{$columnName} SERIAL NOT NULL", | |
| 393 | 'sqlite3', 'pdo_sqlite' => "{$columnName} INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT", | |
| 394 | 'sqlsrv', 'pdo_sqlsrv' => "{$columnName} INT IDENTITY(1,1) NOT NULL", | |
| 395 | default => "{$columnName} INTEGER NOT NULL", | |
| 396 | }; | |
| 397 | } |
| 402 | protected function getConfig(string $key): mixed | |
| 403 | { | |
| 404 | return $this->configuration->get(item: $key); | |
| 405 | } |
| 410 | public function getChecksum(): string | |
| 411 | { | |
| 412 | $data = [ | |
| 413 | 'version' => $this->getVersion(), | |
| 414 | 'description' => $this->getDescription(), | |
| 415 | 'class' => static::class, | |
| 416 | ]; | |
| 417 | return hash('sha256', (string) json_encode($data)); | |
| 418 | } |