Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
62.89% |
61 / 97 |
|
60.00% |
3 / 5 |
CRAP | |
0.00% |
0 / 1 |
| Migration320Beta | |
62.89% |
61 / 97 |
|
60.00% |
3 / 5 |
24.02 | |
0.00% |
0 / 1 |
| getVersion | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| getDependencies | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| getDescription | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| up | |
47.46% |
28 / 59 |
|
0.00% |
0 / 1 |
14.11 | |||
| rebuildTableWithoutColumns | |
85.71% |
30 / 35 |
|
0.00% |
0 / 1 |
4.05 | |||
| 1 | <?php |
| 2 | |
| 3 | /** |
| 4 | * Migration for phpMyFAQ 3.2.0-beta. |
| 5 | * |
| 6 | * This Source Code Form is subject to the terms of the Mozilla Public License, |
| 7 | * v. 2.0. If a copy of the MPL was not distributed with this file, You can |
| 8 | * obtain one at https://mozilla.org/MPL/2.0/. |
| 9 | * |
| 10 | * @package phpMyFAQ |
| 11 | * @author Thorsten Rinne <thorsten@phpmyfaq.de> |
| 12 | * @copyright 2023-2026 phpMyFAQ Team |
| 13 | * @license https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0 |
| 14 | * @link https://www.phpmyfaq.de |
| 15 | * @since 2026-01-25 |
| 16 | */ |
| 17 | |
| 18 | declare(strict_types=1); |
| 19 | |
| 20 | namespace phpMyFAQ\Setup\Migration\Versions; |
| 21 | |
| 22 | use phpMyFAQ\Setup\Migration\AbstractMigration; |
| 23 | use phpMyFAQ\Setup\Migration\Operations\OperationRecorder; |
| 24 | |
| 25 | readonly class Migration320Beta extends AbstractMigration |
| 26 | { |
| 27 | public function getVersion(): string |
| 28 | { |
| 29 | return '3.2.0-beta'; |
| 30 | } |
| 31 | |
| 32 | public function getDependencies(): array |
| 33 | { |
| 34 | return ['3.2.0-alpha']; |
| 35 | } |
| 36 | |
| 37 | public function getDescription(): string |
| 38 | { |
| 39 | return 'SMTP TLS config, remove link verification, config value as TEXT column'; |
| 40 | } |
| 41 | |
| 42 | public function up(OperationRecorder $recorder): void |
| 43 | { |
| 44 | $recorder->addConfig('mail.remoteSMTPDisableTLSPeerVerification', false); |
| 45 | $recorder->deleteConfig('main.enableLinkVerification'); |
| 46 | |
| 47 | // Delete link verification columns - use portable syntax |
| 48 | if ($this->isSqlite()) { |
| 49 | // SQLite requires table rebuild for dropping columns |
| 50 | $this->rebuildTableWithoutColumns($recorder, 'faqdata'); |
| 51 | $this->rebuildTableWithoutColumns($recorder, 'faqdata_revisions'); |
| 52 | } |
| 53 | if (!$this->isSqlite()) { |
| 54 | // MySQL, PostgreSQL, SQL Server - use separate DROP COLUMN statements |
| 55 | $recorder->addSql( |
| 56 | sprintf('ALTER TABLE %sfaqdata DROP COLUMN links_state', $this->tablePrefix), |
| 57 | 'Remove links_state column from faqdata', |
| 58 | ); |
| 59 | $recorder->addSql( |
| 60 | sprintf('ALTER TABLE %sfaqdata DROP COLUMN links_check_date', $this->tablePrefix), |
| 61 | 'Remove links_check_date column from faqdata', |
| 62 | ); |
| 63 | |
| 64 | $recorder->addSql( |
| 65 | sprintf('ALTER TABLE %sfaqdata_revisions DROP COLUMN links_state', $this->tablePrefix), |
| 66 | 'Remove links_state column from faqdata_revisions', |
| 67 | ); |
| 68 | $recorder->addSql( |
| 69 | sprintf('ALTER TABLE %sfaqdata_revisions DROP COLUMN links_check_date', $this->tablePrefix), |
| 70 | 'Remove links_check_date column from faqdata_revisions', |
| 71 | ); |
| 72 | } |
| 73 | |
| 74 | // Configuration values in a TEXT column |
| 75 | if ($this->isMySql()) { |
| 76 | $recorder->addSql( |
| 77 | sprintf('ALTER TABLE %sfaqconfig MODIFY config_value TEXT DEFAULT NULL', $this->tablePrefix), |
| 78 | 'Change faqconfig.config_value to TEXT (MySQL)', |
| 79 | ); |
| 80 | return; |
| 81 | } |
| 82 | |
| 83 | if ($this->isPostgreSql()) { |
| 84 | $recorder->addSql( |
| 85 | sprintf('ALTER TABLE %sfaqconfig ALTER COLUMN config_value TYPE TEXT', $this->tablePrefix), |
| 86 | 'Change faqconfig.config_value to TEXT (PostgreSQL)', |
| 87 | ); |
| 88 | return; |
| 89 | } |
| 90 | |
| 91 | if ($this->isSqlite()) { |
| 92 | // SQLite requires table rebuild |
| 93 | $recorder->addSql(sprintf('CREATE TABLE %sfaqconfig_new ( |
| 94 | config_name VARCHAR(255) NOT NULL default \'\', |
| 95 | config_value TEXT DEFAULT NULL, PRIMARY KEY (config_name) |
| 96 | )', $this->tablePrefix), 'Create new faqconfig table (SQLite)'); |
| 97 | |
| 98 | $recorder->addSql( |
| 99 | sprintf( |
| 100 | 'INSERT INTO %sfaqconfig_new SELECT config_name, config_value FROM %sfaqconfig', |
| 101 | $this->tablePrefix, |
| 102 | $this->tablePrefix, |
| 103 | ), |
| 104 | 'Copy data to new faqconfig table (SQLite)', |
| 105 | ); |
| 106 | |
| 107 | $recorder->addSql( |
| 108 | sprintf('DROP TABLE %sfaqconfig', $this->tablePrefix), |
| 109 | 'Drop old faqconfig table (SQLite)', |
| 110 | ); |
| 111 | |
| 112 | $recorder->addSql( |
| 113 | sprintf('ALTER TABLE %sfaqconfig_new RENAME TO %sfaqconfig', $this->tablePrefix, $this->tablePrefix), |
| 114 | 'Rename new faqconfig table (SQLite)', |
| 115 | ); |
| 116 | return; |
| 117 | } |
| 118 | |
| 119 | if ($this->isSqlServer()) { |
| 120 | $recorder->addSql( |
| 121 | sprintf('ALTER TABLE %sfaqconfig ALTER COLUMN config_value NVARCHAR(MAX)', $this->tablePrefix), |
| 122 | 'Change faqconfig.config_value to NVARCHAR(MAX) (SQL Server)', |
| 123 | ); |
| 124 | } |
| 125 | } |
| 126 | |
| 127 | /** |
| 128 | * Rebuilds a table without specified columns (for SQLite). |
| 129 | * |
| 130 | * Note: This method uses hardcoded schema definitions for faqdata and faqdata_revisions |
| 131 | * tables due to SQLite limitations with ALTER TABLE DROP COLUMN. |
| 132 | */ |
| 133 | private function rebuildTableWithoutColumns(OperationRecorder $recorder, string $tableName): void |
| 134 | { |
| 135 | $allowedTables = ['faqdata', 'faqdata_revisions']; |
| 136 | if (!in_array($tableName, $allowedTables, strict: true)) { |
| 137 | throw new \LogicException(sprintf( |
| 138 | 'rebuildTableWithoutColumns() only supports [%s], got "%s"', |
| 139 | implode(', ', $allowedTables), |
| 140 | $tableName, |
| 141 | )); |
| 142 | } |
| 143 | |
| 144 | $fullTableName = $this->tablePrefix . $tableName; |
| 145 | |
| 146 | // For faqdata and faqdata_revisions, we need to define the schema without the removed columns |
| 147 | if ($tableName === 'faqdata') { |
| 148 | $recorder->addSql( |
| 149 | sprintf('CREATE TABLE %s_new ( |
| 150 | id INTEGER NOT NULL, |
| 151 | lang VARCHAR(5) NOT NULL, |
| 152 | solution_id INTEGER NOT NULL, |
| 153 | revision_id INTEGER NOT NULL DEFAULT 0, |
| 154 | active char(3) NOT NULL, |
| 155 | sticky INTEGER NOT NULL, |
| 156 | keywords text DEFAULT NULL, |
| 157 | thema text NOT NULL, |
| 158 | content text DEFAULT NULL, |
| 159 | author VARCHAR(255) NOT NULL, |
| 160 | email VARCHAR(255) NOT NULL, |
| 161 | comment char(1) default \'y\', |
| 162 | updated VARCHAR(15) NOT NULL, |
| 163 | date_start VARCHAR(14) NOT NULL DEFAULT \'00000000000000\', |
| 164 | date_end VARCHAR(14) NOT NULL DEFAULT \'99991231235959\', |
| 165 | created DATETIME DEFAULT CURRENT_TIMESTAMP, |
| 166 | notes text DEFAULT NULL, |
| 167 | sticky_order INTEGER DEFAULT NULL, |
| 168 | PRIMARY KEY (id, lang) |
| 169 | )', $fullTableName), |
| 170 | sprintf('Create new %s table without link verification columns (SQLite)', $tableName), |
| 171 | ); |
| 172 | |
| 173 | $recorder->addSql( |
| 174 | sprintf('INSERT INTO %s_new |
| 175 | SELECT id, lang, solution_id, revision_id, active, sticky, keywords, thema, content, |
| 176 | author, email, comment, updated, date_start, date_end, created, notes, sticky_order |
| 177 | FROM %s', $fullTableName, $fullTableName), |
| 178 | sprintf('Copy data to new %s table (SQLite)', $tableName), |
| 179 | ); |
| 180 | } |
| 181 | |
| 182 | if ($tableName === 'faqdata_revisions') { |
| 183 | $recorder->addSql( |
| 184 | sprintf('CREATE TABLE %s_new ( |
| 185 | id INTEGER NOT NULL, |
| 186 | lang VARCHAR(5) NOT NULL, |
| 187 | solution_id INTEGER NOT NULL, |
| 188 | revision_id INTEGER NOT NULL DEFAULT 0, |
| 189 | active char(3) NOT NULL, |
| 190 | sticky INTEGER NOT NULL, |
| 191 | keywords text DEFAULT NULL, |
| 192 | thema text NOT NULL, |
| 193 | content text DEFAULT NULL, |
| 194 | author VARCHAR(255) NOT NULL, |
| 195 | email VARCHAR(255) NOT NULL, |
| 196 | comment char(1) default \'y\', |
| 197 | updated VARCHAR(15) NOT NULL, |
| 198 | date_start VARCHAR(14) NOT NULL DEFAULT \'00000000000000\', |
| 199 | date_end VARCHAR(14) NOT NULL DEFAULT \'99991231235959\', |
| 200 | created DATETIME DEFAULT CURRENT_TIMESTAMP, |
| 201 | notes text DEFAULT NULL, |
| 202 | sticky_order INTEGER DEFAULT NULL, |
| 203 | PRIMARY KEY (id, lang, solution_id, revision_id) |
| 204 | )', $fullTableName), |
| 205 | sprintf('Create new %s table without link verification columns (SQLite)', $tableName), |
| 206 | ); |
| 207 | |
| 208 | $recorder->addSql( |
| 209 | sprintf('INSERT INTO %s_new |
| 210 | SELECT id, lang, solution_id, revision_id, active, sticky, keywords, thema, content, |
| 211 | author, email, comment, updated, date_start, date_end, created, notes, sticky_order |
| 212 | FROM %s', $fullTableName, $fullTableName), |
| 213 | sprintf('Copy data to new %s table (SQLite)', $tableName), |
| 214 | ); |
| 215 | } |
| 216 | |
| 217 | $recorder->addSql(sprintf('DROP TABLE %s', $fullTableName), sprintf('Drop old %s table (SQLite)', $tableName)); |
| 218 | |
| 219 | $recorder->addSql( |
| 220 | sprintf('ALTER TABLE %s_new RENAME TO %s', $fullTableName, $fullTableName), |
| 221 | sprintf('Rename new %s table (SQLite)', $tableName), |
| 222 | ); |
| 223 | } |
| 224 | } |