diff --git a/libraries/classes/Table/ColumnsDefinition.php b/libraries/classes/Table/ColumnsDefinition.php index 1fb5117ed2..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; @@ -497,9 +500,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 { @@ -510,10 +514,9 @@ final class ColumnsDefinition switch ($columnMeta['Default']) { case null: - if ($columnMeta['Null'] === 'YES') { - $metaDefault['DefaultType'] = 'NULL'; - } else { - $metaDefault['DefaultType'] = 'NONE'; + // Could be null or empty string here + if ($columnMeta['Default'] === null) { + $metaDefault['DefaultType'] = $columnMeta['Null'] === 'YES' ? 'NULL' : 'NONE'; } break; @@ -528,11 +531,19 @@ 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); + 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']; } break; diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index ac006880f9..df6fb90a2d 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -35545,6 +35545,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 @@ -35576,7 +35581,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 d3178fdab2..c0559ce0a5 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -13457,6 +13457,9 @@ $_POST['after_field'] + + (string) $columnMeta['Type'] + diff --git a/test/classes/Table/ColumnsDefinitionTest.php b/test/classes/Table/ColumnsDefinitionTest.php index 455b8d1388..e6eb43503c 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,25 +109,59 @@ class ColumnsDefinitionTest extends AbstractTestCase ], 'when Default is anything else and Type is text' => [ [ - 'Default' => '"some\/thing"', 'Type' => 'text', + 'Null' => 'NO', + 'Default' => "'some\\/thing'", ], [ - 'Default' => 'some/thing', 'DefaultType' => 'USER_DEFINED', - 'DefaultValue' => '"some\/thing"', + 'DefaultValue' => 'some/thing', ], ], 'when Default is anything else and Type is not text' => [ [ - 'Default' => '"some\/thing"', 'Type' => 'something', + 'Null' => 'NO', + 'Default' => '"some\/thing"', ], [ 'DefaultType' => 'USER_DEFINED', 'DefaultValue' => '"some\/thing"', ], ], + 'when varchar Default is empty string' => [ + [ + 'Type' => 'varchar(255)', + 'Null' => 'YES', + 'Default' => '', + ], + [ + 'DefaultType' => 'USER_DEFINED', + 'DefaultValue' => '', + ], + ], + 'when longtext Default is empty string' => [ + [ + 'Type' => 'longtext', + 'Null' => 'YES', + 'Default' => "''", + ], + [ + 'DefaultType' => 'USER_DEFINED', + 'DefaultValue' => '', + ], + ], + 'when text type default is an expression' => [ + [ + 'Type' => 'tinytext', + 'Null' => 'YES', + 'Default' => 'unix_timestamp()', + ], + [ + 'DefaultType' => 'USER_DEFINED', + 'DefaultValue' => 'unix_timestamp()', + ], + ], ]; } }