From dc8057c0c62ad654b235ad04b1d035c8a68bb049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Kr=C3=B6g?= Date: Tue, 17 Mar 2026 19:02:13 +0100 Subject: [PATCH 1/5] Fix empty string as default value for varchar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit null in a switch also matches empty string. Signed-off-by: Maximilian Krög --- libraries/classes/Table/ColumnsDefinition.php | 7 ++++--- test/classes/Table/ColumnsDefinitionTest.php | 10 ++++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/libraries/classes/Table/ColumnsDefinition.php b/libraries/classes/Table/ColumnsDefinition.php index 1fb5117ed2..f75eeb2099 100644 --- a/libraries/classes/Table/ColumnsDefinition.php +++ b/libraries/classes/Table/ColumnsDefinition.php @@ -510,10 +510,11 @@ final class ColumnsDefinition switch ($columnMeta['Default']) { case null: - if ($columnMeta['Null'] === 'YES') { - $metaDefault['DefaultType'] = 'NULL'; + // Could be null or empty string here + if ($columnMeta['Default'] === null) { + $metaDefault['DefaultType'] = $columnMeta['Null'] === 'YES' ? 'NULL' : 'NONE'; } else { - $metaDefault['DefaultType'] = 'NONE'; + $columnMeta['DefaultValue'] = $columnMeta['Default']; } break; diff --git a/test/classes/Table/ColumnsDefinitionTest.php b/test/classes/Table/ColumnsDefinitionTest.php index 455b8d1388..8b176e7255 100644 --- a/test/classes/Table/ColumnsDefinitionTest.php +++ b/test/classes/Table/ColumnsDefinitionTest.php @@ -107,6 +107,16 @@ class ColumnsDefinitionTest extends AbstractTestCase 'DefaultValue' => '"some\/thing"', ], ], + 'when varchar Default is empty string' => [ + [ + 'Default' => '', + 'Type' => 'varchar(255)', + ], + [ + 'DefaultType' => 'USER_DEFINED', + 'DefaultValue' => '', + ], + ], ]; } } From 189a3536dbc75132ad611adec330978d4e2780a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Kr=C3=B6g?= Date: Tue, 17 Mar 2026 19:05:00 +0100 Subject: [PATCH 2/5] Fix default value for text columns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For text type columns the value is quoted. The unquoted value was written to the wrong property in $columnMeta. Has to be 'DefaultValue' instead of 'Default'. Signed-off-by: Maximilian Krög --- libraries/classes/Table/ColumnsDefinition.php | 6 +++--- test/classes/Table/ColumnsDefinitionTest.php | 13 +++++++++++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/libraries/classes/Table/ColumnsDefinition.php b/libraries/classes/Table/ColumnsDefinition.php index f75eeb2099..5acf1e6b17 100644 --- a/libraries/classes/Table/ColumnsDefinition.php +++ b/libraries/classes/Table/ColumnsDefinition.php @@ -529,11 +529,11 @@ final class ColumnsDefinition break; default: - $metaDefault['DefaultValue'] = $columnMeta['Default']; - if (substr((string) $columnMeta['Type'], -4) === 'text') { $textDefault = substr($columnMeta['Default'], 1, -1); - $metaDefault['Default'] = stripcslashes($textDefault); + $metaDefault['DefaultValue'] = stripcslashes($textDefault); + } else { + $metaDefault['DefaultValue'] = $columnMeta['Default']; } break; diff --git a/test/classes/Table/ColumnsDefinitionTest.php b/test/classes/Table/ColumnsDefinitionTest.php index 8b176e7255..69d9962049 100644 --- a/test/classes/Table/ColumnsDefinitionTest.php +++ b/test/classes/Table/ColumnsDefinitionTest.php @@ -92,9 +92,8 @@ class ColumnsDefinitionTest extends AbstractTestCase 'Type' => 'text', ], [ - 'Default' => 'some/thing', 'DefaultType' => 'USER_DEFINED', - 'DefaultValue' => '"some\/thing"', + 'DefaultValue' => 'some/thing', ], ], 'when Default is anything else and Type is not text' => [ @@ -117,6 +116,16 @@ class ColumnsDefinitionTest extends AbstractTestCase 'DefaultValue' => '', ], ], + 'when longtext Default is empty string' => [ + [ + 'Default' => "''", + 'Type' => 'longtext', + ], + [ + 'DefaultType' => 'USER_DEFINED', + 'DefaultValue' => '', + ], + ], ]; } } From 9748459944b4ae3f6b30be755ef66dc8b2b4f5c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Kr=C3=B6g?= Date: Thu, 19 Mar 2026 19:17:47 +0100 Subject: [PATCH 3/5] Set all expected properties test data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maximilian Krög --- libraries/classes/Table/ColumnsDefinition.php | 5 +- phpstan-baseline.neon | 7 ++- psalm-baseline.xml | 3 ++ test/classes/Table/ColumnsDefinitionTest.php | 51 ++++++++++++++----- 4 files changed, 50 insertions(+), 16 deletions(-) diff --git a/libraries/classes/Table/ColumnsDefinition.php b/libraries/classes/Table/ColumnsDefinition.php index 5acf1e6b17..832e7c6a45 100644 --- a/libraries/classes/Table/ColumnsDefinition.php +++ b/libraries/classes/Table/ColumnsDefinition.php @@ -497,9 +497,10 @@ final class ColumnsDefinition * Set default type and default value according to the column metadata * * @param array $columnMeta Column Metadata - * @phpstan-param array $columnMeta + * @phpstan-param array{Default: string|null, Null: 'YES'|'NO', Type: string} $columnMeta * - * @return non-empty-array + * @return non-empty-array + * @psalm-return array{DefaultType: string, DefaultValue: string} */ public static function decorateColumnMetaDefault(array $columnMeta): array { diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index b90c669190..290501389e 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -35565,6 +35565,11 @@ parameters: count: 2 path: libraries/classes/Table/ColumnsDefinition.php + - + message: "#^Casting to string something that's already string\\.$#" + count: 1 + path: libraries/classes/Table/ColumnsDefinition.php + - message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" count: 2 @@ -35596,7 +35601,7 @@ parameters: path: libraries/classes/Table/ColumnsDefinition.php - - message: "#^Parameter \\#1 \\$columnMeta of static method PhpMyAdmin\\\\Table\\\\ColumnsDefinition\\:\\:decorateColumnMetaDefault\\(\\) expects array\\, mixed given\\.$#" + message: "#^Parameter \\#1 \\$columnMeta of static method PhpMyAdmin\\\\Table\\\\ColumnsDefinition\\:\\:decorateColumnMetaDefault\\(\\) expects array\\{Default\\: string\\|null, Null\\: 'NO'\\|'YES', Type\\: string\\}, mixed given\\.$#" count: 1 path: libraries/classes/Table/ColumnsDefinition.php diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 66023c51f2..5f05f2ca36 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -13459,6 +13459,9 @@ $_POST['after_field'] + + (string) $columnMeta['Type'] + diff --git a/test/classes/Table/ColumnsDefinitionTest.php b/test/classes/Table/ColumnsDefinitionTest.php index 69d9962049..ee56975fc6 100644 --- a/test/classes/Table/ColumnsDefinitionTest.php +++ b/test/classes/Table/ColumnsDefinitionTest.php @@ -17,8 +17,8 @@ class ColumnsDefinitionTest extends AbstractTestCase * * @param array $columnMeta column metadata * @param array $expected expected result - * @phpstan-param array $columnMeta - * @phpstan-param array $expected + * @phpstan-param array{Default: string|null, Null: 'YES'|'NO', Type: string} $columnMeta + * @phpstan-param array{DefaultType: string, DefaultValue: string} $expected * * @dataProvider providerColumnMetaDefault */ @@ -33,15 +33,19 @@ class ColumnsDefinitionTest extends AbstractTestCase * Data provider for testDecorateColumnMetaDefault * * @return array - * @psalm-return array, array}> + * @psalm-return array */ public static function providerColumnMetaDefault(): array { return [ 'when Default is null and Null is YES' => [ [ - 'Default' => null, + 'Type' => 'int', 'Null' => 'YES', + 'Default' => null, ], [ 'DefaultType' => 'NULL', @@ -50,8 +54,9 @@ class ColumnsDefinitionTest extends AbstractTestCase ], 'when Default is null and Null is NO' => [ [ - 'Default' => null, + 'Type' => 'int', 'Null' => 'NO', + 'Default' => null, ], [ 'DefaultType' => 'NONE', @@ -59,28 +64,44 @@ class ColumnsDefinitionTest extends AbstractTestCase ], ], 'when Default is CURRENT_TIMESTAMP' => [ - ['Default' => 'CURRENT_TIMESTAMP'], + [ + 'Type' => 'timestamp', + 'Null' => 'NO', + 'Default' => 'CURRENT_TIMESTAMP', + ], [ 'DefaultType' => 'CURRENT_TIMESTAMP', 'DefaultValue' => '', ], ], 'when Default is current_timestamp' => [ - ['Default' => 'current_timestamp()'], + [ + 'Type' => 'datetime', + 'Null' => 'NO', + 'Default' => 'current_timestamp()', + ], [ 'DefaultType' => 'CURRENT_TIMESTAMP', 'DefaultValue' => '', ], ], 'when Default is UUID' => [ - ['Default' => 'UUID'], + [ + 'Type' => 'UUID', + 'Null' => 'NO', + 'Default' => 'UUID', + ], [ 'DefaultType' => 'UUID', 'DefaultValue' => '', ], ], 'when Default is uuid()' => [ - ['Default' => 'uuid()'], + [ + 'Type' => 'UUID', + 'Null' => 'YES', + 'Default' => 'uuid()', + ], [ 'DefaultType' => 'UUID', 'DefaultValue' => '', @@ -88,8 +109,9 @@ class ColumnsDefinitionTest extends AbstractTestCase ], 'when Default is anything else and Type is text' => [ [ - 'Default' => '"some\/thing"', 'Type' => 'text', + 'Null' => 'NO', + 'Default' => '"some\/thing"', ], [ 'DefaultType' => 'USER_DEFINED', @@ -98,8 +120,9 @@ class ColumnsDefinitionTest extends AbstractTestCase ], 'when Default is anything else and Type is not text' => [ [ - 'Default' => '"some\/thing"', 'Type' => 'something', + 'Null' => 'NO', + 'Default' => '"some\/thing"', ], [ 'DefaultType' => 'USER_DEFINED', @@ -108,8 +131,9 @@ class ColumnsDefinitionTest extends AbstractTestCase ], 'when varchar Default is empty string' => [ [ - 'Default' => '', 'Type' => 'varchar(255)', + 'Null' => 'YES', + 'Default' => '', ], [ 'DefaultType' => 'USER_DEFINED', @@ -118,8 +142,9 @@ class ColumnsDefinitionTest extends AbstractTestCase ], 'when longtext Default is empty string' => [ [ - 'Default' => "''", 'Type' => 'longtext', + 'Null' => 'YES', + 'Default' => "''", ], [ 'DefaultType' => 'USER_DEFINED', From dc95d18e5cd331451761ad5a432e95dea38195f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Kr=C3=B6g?= Date: Thu, 19 Mar 2026 19:31:35 +0100 Subject: [PATCH 4/5] Add test for text column with expression default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maximilian Krög --- test/classes/Table/ColumnsDefinitionTest.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/test/classes/Table/ColumnsDefinitionTest.php b/test/classes/Table/ColumnsDefinitionTest.php index ee56975fc6..e6eb43503c 100644 --- a/test/classes/Table/ColumnsDefinitionTest.php +++ b/test/classes/Table/ColumnsDefinitionTest.php @@ -111,7 +111,7 @@ class ColumnsDefinitionTest extends AbstractTestCase [ 'Type' => 'text', 'Null' => 'NO', - 'Default' => '"some\/thing"', + 'Default' => "'some\\/thing'", ], [ 'DefaultType' => 'USER_DEFINED', @@ -151,6 +151,17 @@ class ColumnsDefinitionTest extends AbstractTestCase 'DefaultValue' => '', ], ], + 'when text type default is an expression' => [ + [ + 'Type' => 'tinytext', + 'Null' => 'YES', + 'Default' => 'unix_timestamp()', + ], + [ + 'DefaultType' => 'USER_DEFINED', + 'DefaultValue' => 'unix_timestamp()', + ], + ], ]; } } From 7e3263f9ae18d1ce8beae9f2e263629f5176d7d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Kr=C3=B6g?= Date: Thu, 19 Mar 2026 19:32:36 +0100 Subject: [PATCH 5/5] Don't remove characters from expression MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maximilian Krög --- libraries/classes/Table/ColumnsDefinition.php | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/libraries/classes/Table/ColumnsDefinition.php b/libraries/classes/Table/ColumnsDefinition.php index 832e7c6a45..0028a77b53 100644 --- a/libraries/classes/Table/ColumnsDefinition.php +++ b/libraries/classes/Table/ColumnsDefinition.php @@ -24,10 +24,13 @@ use function explode; use function in_array; use function intval; use function is_array; +use function mb_strlen; use function mb_strtoupper; use function preg_quote; use function preg_replace; use function rtrim; +use function str_ends_with; +use function str_starts_with; use function stripcslashes; use function substr; use function trim; @@ -514,8 +517,6 @@ final class ColumnsDefinition // Could be null or empty string here if ($columnMeta['Default'] === null) { $metaDefault['DefaultType'] = $columnMeta['Null'] === 'YES' ? 'NULL' : 'NONE'; - } else { - $columnMeta['DefaultValue'] = $columnMeta['Default']; } break; @@ -531,8 +532,16 @@ final class ColumnsDefinition break; default: if (substr((string) $columnMeta['Type'], -4) === 'text') { - $textDefault = substr($columnMeta['Default'], 1, -1); - $metaDefault['DefaultValue'] = stripcslashes($textDefault); + if ( + mb_strlen($columnMeta['Default']) >= 2 && + str_starts_with($columnMeta['Default'], "'") && + str_ends_with($columnMeta['Default'], "'") + ) { + $textDefault = substr($columnMeta['Default'], 1, -1); + $metaDefault['DefaultValue'] = stripcslashes($textDefault); + } else { + $metaDefault['DefaultValue'] = $columnMeta['Default']; + } } else { $metaDefault['DefaultValue'] = $columnMeta['Default']; }