diff --git a/js/src/drag_drop_import.ts b/js/src/drag_drop_import.ts index 9f5171533c..3923186003 100644 --- a/js/src/drag_drop_import.ts +++ b/js/src/drag_drop_import.ts @@ -329,8 +329,6 @@ var DragDropImport = { fd.append('token', CommonParams.get('token')); fd.append('import_type', 'database'); // todo: method to find the value below - fd.append('MAX_FILE_SIZE', '4194304'); - // todo: method to find the value below fd.append('charset_of_file', 'utf-8'); // todo: method to find the value below fd.append('allow_interrupt', 'yes'); diff --git a/libraries/classes/Controllers/Import/ImportController.php b/libraries/classes/Controllers/Import/ImportController.php index e71e0b04e5..37abdc6b99 100644 --- a/libraries/classes/Controllers/Import/ImportController.php +++ b/libraries/classes/Controllers/Import/ImportController.php @@ -101,7 +101,6 @@ final class ImportController extends AbstractController $GLOBALS['format'] = $request->getParsedBodyParam('format', ''); $GLOBALS['import_type'] = $request->getParsedBodyParam('import_type'); $GLOBALS['is_js_confirmed'] = $request->getParsedBodyParam('is_js_confirmed'); - $GLOBALS['MAX_FILE_SIZE'] = $request->getParsedBodyParam('MAX_FILE_SIZE'); $GLOBALS['message_to_show'] = $request->getParsedBodyParam('message_to_show'); $GLOBALS['noplugin'] = $request->getParsedBodyParam('noplugin'); $GLOBALS['skip_queries'] = $request->getParsedBodyParam('skip_queries'); diff --git a/libraries/classes/Controllers/Table/ChangeController.php b/libraries/classes/Controllers/Table/ChangeController.php index 14cb266685..cff2199672 100644 --- a/libraries/classes/Controllers/Table/ChangeController.php +++ b/libraries/classes/Controllers/Table/ChangeController.php @@ -24,6 +24,7 @@ use function is_string; use function str_contains; use function strlen; use function strpos; +use function trim; /** * Displays form for editing and inserting new table rows. @@ -61,7 +62,6 @@ class ChangeController extends AbstractController $GLOBALS['tabindex'] ??= null; $GLOBALS['tabindex_for_value'] ??= null; $GLOBALS['o_rows'] ??= null; - $GLOBALS['biggest_max_file_size'] ??= null; $GLOBALS['has_blob_field'] ??= null; $GLOBALS['jsvkey'] ??= null; $GLOBALS['vkey'] ??= null; @@ -182,14 +182,10 @@ class ChangeController extends AbstractController $GLOBALS['tabindex'] = 0; $GLOBALS['tabindex_for_value'] = 0; $GLOBALS['o_rows'] = 0; - $GLOBALS['biggest_max_file_size'] = 0; $GLOBALS['urlParams']['db'] = $GLOBALS['db']; $GLOBALS['urlParams']['table'] = $GLOBALS['table']; - $GLOBALS['urlParams'] = $this->insertEdit->urlParamsInEditMode( - $GLOBALS['urlParams'], - $GLOBALS['where_clause_array'], - ); + $GLOBALS['urlParams'] = $this->urlParamsInEditMode($GLOBALS['urlParams'], $GLOBALS['where_clause_array']); $GLOBALS['has_blob_field'] = false; foreach ($GLOBALS['table_columns'] as $column) { @@ -263,7 +259,6 @@ class ChangeController extends AbstractController $GLOBALS['table'], $GLOBALS['db'], $rowId, - $GLOBALS['biggest_max_file_size'], $GLOBALS['text_dir'], $GLOBALS['repopulate'], $GLOBALS['where_clause_array'], @@ -286,11 +281,6 @@ class ChangeController extends AbstractController 'is_numeric' => $isNumeric, ]); - if ($GLOBALS['biggest_max_file_size'] > 0) { - $htmlOutput .= '' . "\n"; - } - $htmlOutput .= ''; $htmlOutput .= $this->insertEdit->getHtmlForGisEditor(); @@ -308,4 +298,27 @@ class ChangeController extends AbstractController $this->response->addHTML($htmlOutput); } + + /** + * Add some url parameters + * + * @param mixed[] $urlParams containing $db and $table as url parameters + * @param mixed[] $whereClauseArray where clauses array + * + * @return mixed[] Add some url parameters to $url_params array and return it + */ + public function urlParamsInEditMode( + array $urlParams, + array $whereClauseArray, + ): array { + foreach ($whereClauseArray as $whereClause) { + $urlParams['where_clause'] = trim($whereClause); + } + + if (! empty($_POST['sql_query'])) { + $urlParams['sql_query'] = $_POST['sql_query']; + } + + return $urlParams; + } } diff --git a/libraries/classes/Controllers/Table/ReplaceController.php b/libraries/classes/Controllers/Table/ReplaceController.php index ccacb56c30..6f1df84fa7 100644 --- a/libraries/classes/Controllers/Table/ReplaceController.php +++ b/libraries/classes/Controllers/Table/ReplaceController.php @@ -25,6 +25,7 @@ use PhpMyAdmin\Transformations; use PhpMyAdmin\Util; use function __; +use function array_keys; use function array_values; use function class_exists; use function implode; @@ -104,7 +105,9 @@ final class ReplaceController extends AbstractController /** * Prepares the update/insert of a row */ - [$loopArray, $usingKey, $isInsert, $isInsertignore] = $this->insertEdit->getParamsForUpdateOrInsert(); + [$loopArray, $usingKey, $isInsert] = $this->getParamsForUpdateOrInsert(); + + $isInsertignore = isset($_POST['submit_type']) && $_POST['submit_type'] === 'insertignore'; $GLOBALS['query'] = []; $valueSets = []; @@ -283,7 +286,12 @@ final class ReplaceController extends AbstractController // Builds the sql query if ($isInsert && $valueSets !== []) { - $GLOBALS['query'] = $this->insertEdit->buildSqlQuery($isInsertignore, $queryFields, $valueSets); + $GLOBALS['query'] = (array) $this->insertEdit->buildInsertSqlQuery( + $GLOBALS['table'], + $isInsertignore, + $queryFields, + $valueSets, + ); } elseif (empty($GLOBALS['query']) && ! isset($_POST['preview_sql']) && ! $rowSkipped) { // No change -> move back to the calling script // @@ -504,4 +512,36 @@ final class ReplaceController extends AbstractController /** @psalm-suppress UnresolvableInclude */ require ROOT_PATH . Core::securePath($gotoInclude); } + + /** + * Prepares the update/insert of a row + * + * @return mixed[] $loop_array, $using_key, $is_insert + * @psalm-return array{array, bool, bool} + */ + private function getParamsForUpdateOrInsert(): array + { + if (isset($_POST['where_clause'])) { + // we were editing something => use the WHERE clause + $loopArray = is_array($_POST['where_clause']) + ? $_POST['where_clause'] + : [$_POST['where_clause']]; + $usingKey = true; + $isInsert = isset($_POST['submit_type']) + && ($_POST['submit_type'] === 'insert' + || $_POST['submit_type'] === 'showinsert' + || $_POST['submit_type'] === 'insertignore'); + } else { + // new row => use indexes + $loopArray = []; + if (! empty($_POST['fields'])) { + $loopArray = array_keys($_POST['fields']['multi_edit']); + } + + $usingKey = false; + $isInsert = true; + } + + return [$loopArray, $usingKey, $isInsert]; + } } diff --git a/libraries/classes/InsertEdit.php b/libraries/classes/InsertEdit.php index 8dfdf059c1..6da47a7622 100644 --- a/libraries/classes/InsertEdit.php +++ b/libraries/classes/InsertEdit.php @@ -13,7 +13,6 @@ use PhpMyAdmin\Utils\Gis; use function __; use function array_fill; use function array_key_exists; -use function array_keys; use function array_merge; use function array_values; use function bin2hex; @@ -132,26 +131,6 @@ class InsertEdit return $formParams; } - /** - * Creates array of where clauses - * - * @param string[]|string|null $whereClause where clause - * - * @return string[] whereClauseArray array of where clauses - */ - private function getWhereClauseArray(array|string|null $whereClause): array - { - if ($whereClause === null) { - return []; - } - - if (is_array($whereClause)) { - return $whereClause; - } - - return [$whereClause]; - } - /** * Analysing where clauses array * @@ -244,46 +223,20 @@ class InsertEdit /** * No primary key given, just load first row - * - * @param string $table name of the table - * @param string $db name of the database - * - * @return array - * @phpstan-return array{ResultInterface, false[]} */ - private function loadFirstRow(string $table, string $db): array + private function loadFirstRow(string $table, string $db): ResultInterface { - $result = $this->dbi->query( + return $this->dbi->query( 'SELECT * FROM ' . Util::backquote($db) . '.' . Util::backquote($table) . ' LIMIT 1;', ); - // Can be a string on some old configuration storage settings - $rows = array_fill(0, (int) $GLOBALS['cfg']['InsertRows'], false); - - return [$result, $rows]; } - /** - * Add some url parameters - * - * @param mixed[] $urlParams containing $db and $table as url parameters - * @param mixed[] $whereClauseArray where clauses array - * - * @return mixed[] Add some url parameters to $url_params array and return it - */ - public function urlParamsInEditMode( - array $urlParams, - array $whereClauseArray, - ): array { - foreach ($whereClauseArray as $whereClause) { - $urlParams['where_clause'] = trim($whereClause); - } - - if (! empty($_POST['sql_query'])) { - $urlParams['sql_query'] = $_POST['sql_query']; - } - - return $urlParams; + /** @return false[] */ + private function getInsertRows(): array + { + // Can be a string on some old configuration storage settings + return array_fill(0, (int) $GLOBALS['cfg']['InsertRows'], false); } /** @@ -375,11 +328,18 @@ class InsertEdit ['char', 'varchar'], ); - [ - $column['pma_type'], - $column['wrap'], - $column['first_timestamp'], - ] = $this->getEnumSetAndTimestampColumns($column, $timestampSeen); + $column['pma_type'] = match ($column['True_Type']) { + 'set', 'enum' => $column['True_Type'], + default => $column['Type'] + }; + + $column['wrap'] = match ($column['True_Type']) { + 'set', 'enum' => '', + default => ' text-nowrap' + }; + + // can only occur once per table + $column['first_timestamp'] = $column['True_Type'] === 'timestamp' ? ! $timestampSeen : false; return $column; } @@ -422,29 +382,6 @@ class InsertEdit return false; } - /** - * Retrieve set, enum, timestamp table columns - * - * @param mixed[] $column description of column in given table - * @param bool $timestampSeen whether a timestamp has been seen - * - * @return mixed[] $column['pma_type'], $column['wrap'], $column['first_timestamp'] - * @psalm-return array{0: mixed, 1: string, 2: bool} - */ - private function getEnumSetAndTimestampColumns(array $column, bool $timestampSeen): array - { - return match ($column['True_Type']) { - 'set' => ['set', '', false], - 'enum' => ['enum', '', false], - 'timestamp' => [ - $column['Type'], - ' text-nowrap', - ! $timestampSeen, // can only occur once per table - ], - default => [$column['Type'], ' text-nowrap', false], - }; - } - /** * Retrieve the nullify code for the null column * @@ -542,48 +479,6 @@ class InsertEdit . ''; } - /** - * Get column values - * - * @param string[] $enumSetValues - * - * @return mixed[] column values as an associative array - * @psalm-return list - */ - private function getColumnEnumValues(array $enumSetValues): array - { - $values = []; - foreach ($enumSetValues as $val) { - $values[] = ['plain' => $val, 'html' => htmlspecialchars($val)]; - } - - return $values; - } - - /** - * Retrieve column 'set' value and select size - * - * @param mixed[] $column description of column in given table - * @param string[] $enumSetValues - * - * @return mixed[] $column['values'], $column['select_size'] - */ - private function getColumnSetValueAndSelectSize( - array $column, - array $enumSetValues, - ): array { - if (! isset($column['values'])) { - $column['values'] = []; - foreach ($enumSetValues as $val) { - $column['values'][] = ['plain' => $val, 'html' => htmlspecialchars($val)]; - } - - $column['select_size'] = min(4, count($column['values'])); - } - - return [$column['values'], $column['select_size']]; - } - /** * Get HTML input type * @@ -682,40 +577,24 @@ class InsertEdit /** * Retrieve the maximum upload file size - * - * @param string $pmaType column type - * @param int $biggestMaxFileSize biggest max file size for uploading - * - * @return mixed[] an html snippet and $biggest_max_file_size - * @psalm-return array{non-empty-string, int} */ - private function getMaxUploadSize(string $pmaType, int $biggestMaxFileSize): array + private function getMaxUploadSize(string $pmaType): string { // find maximum upload size, based on field type /** * @todo with functions this is not so easy, as you can basically * process any data with function like MD5 */ - $maxFieldSizes = [ + $maxFieldSize = match ($pmaType) { 'tinyblob' => 256, 'blob' => 65536, 'mediumblob' => 16777216, 'longblob' => 4294967296,// yeah, really - ]; + }; $thisFieldMaxSize = (int) $GLOBALS['config']->get('max_upload_size'); // from PHP max - if ($thisFieldMaxSize > $maxFieldSizes[$pmaType]) { - $thisFieldMaxSize = $maxFieldSizes[$pmaType]; - } - $htmlOutput = Util::getFormattedMaximumUploadSize($thisFieldMaxSize) . "\n"; - // do not generate here the MAX_FILE_SIZE, because we should - // put only one in the form to accommodate the biggest field - if ($thisFieldMaxSize > $biggestMaxFileSize) { - $biggestMaxFileSize = $thisFieldMaxSize; - } - - return [$htmlOutput, $biggestMaxFileSize]; + return Util::getFormattedMaximumUploadSize(min($thisFieldMaxSize, $maxFieldSize)) . "\n"; } /** @@ -931,6 +810,7 @@ class InsertEdit * * @return mixed[] $real_null_value, $data, $special_chars, $backup_field, * $special_chars_encoded + * @psalm-return array{bool, string, string, string, string} */ private function getSpecialCharsAndBackupFieldForExistingRow( array $currentRow, @@ -1013,17 +893,12 @@ class InsertEdit /** * display default values - * - * @return mixed[] $real_null_value, $data, $special_chars, $special_chars_encoded - * @psalm-return array{bool, string, string, string} */ - private function getSpecialCharsAndBackupFieldForInsertingMode( + private function getSpecialCharsForInsertingMode( string|null $defaultValue, string $trueType, - ): array { - $realNullValue = false; + ): string { if ($defaultValue === null) { - $realNullValue = true; $defaultValue = ''; } @@ -1040,44 +915,7 @@ class InsertEdit $specialChars = htmlspecialchars($defaultValue); } - $specialCharsEncoded = Util::duplicateFirstNewline($specialChars); - - return [$realNullValue, $defaultValue, $specialChars, $specialCharsEncoded]; - } - - /** - * Prepares the update/insert of a row - * - * @return mixed[] $loop_array, $using_key, $is_insert, $is_insertignore - * @psalm-return array{array, bool, bool, bool} - */ - public function getParamsForUpdateOrInsert(): array - { - if (isset($_POST['where_clause'])) { - // we were editing something => use the WHERE clause - $loopArray = is_array($_POST['where_clause']) - ? $_POST['where_clause'] - : [$_POST['where_clause']]; - $usingKey = true; - $isInsert = isset($_POST['submit_type']) - && ($_POST['submit_type'] === 'insert' - || $_POST['submit_type'] === 'showinsert' - || $_POST['submit_type'] === 'insertignore'); - } else { - // new row => use indexes - $loopArray = []; - if (! empty($_POST['fields'])) { - $loopArray = array_keys($_POST['fields']['multi_edit']); - } - - $usingKey = false; - $isInsert = true; - } - - $isInsertIgnore = isset($_POST['submit_type']) - && $_POST['submit_type'] === 'insertignore'; - - return [$loopArray, $usingKey, $isInsert, $isInsertIgnore]; + return $specialChars; } /** @@ -1170,29 +1008,24 @@ class InsertEdit } /** - * Builds the sql query + * Builds the SQL insert query * - * @param bool $isInsertIgnore $_POST['submit_type'] === 'insertignore' - * @param mixed[] $queryFields column names array - * @param mixed[] $valueSets array of query values + * @param bool $isInsertIgnore $_POST['submit_type'] === 'insertignore' + * @param string[] $queryFields column names array + * @param string[] $valueSets array of query values * - * @return mixed[] of query - * @psalm-return array{string} + * @todo move this to Query generator class */ - public function buildSqlQuery(bool $isInsertIgnore, array $queryFields, array $valueSets): array - { - if ($isInsertIgnore) { - $insertCommand = 'INSERT IGNORE '; - } else { - $insertCommand = 'INSERT '; - } - - return [ - $insertCommand . 'INTO ' - . Util::backquote($GLOBALS['table']) + public function buildInsertSqlQuery( + string $table, + bool $isInsertIgnore, + array $queryFields, + array $valueSets, + ): string { + return ($isInsertIgnore ? 'INSERT IGNORE ' : 'INSERT ') . 'INTO ' + . Util::backquote($table) . ' (' . implode(', ', $queryFields) . ') VALUES (' - . implode('), (', $valueSets) . ')', - ]; + . implode('), (', $valueSets) . ')'; } /** @@ -1760,7 +1593,7 @@ class InsertEdit if (isset($whereClause)) { // we are editing $insertMode = false; - $whereClauseArray = $this->getWhereClauseArray($whereClause); + $whereClauseArray = (array) $whereClause; [$whereClauses, $result, $rows, $foundUniqueKey] = $this->analyzeWhereClauses( $whereClauseArray, $table, @@ -1770,7 +1603,8 @@ class InsertEdit // we are inserting $insertMode = true; $whereClause = null; - [$result, $rows] = $this->loadFirstRow($table, $db); + $result = $this->loadFirstRow($table, $db); + $rows = $this->getInsertRows(); $whereClauses = null; $whereClauseArray = []; $foundUniqueKey = false; @@ -1874,7 +1708,6 @@ class InsertEdit * @param string $table table * @param string $db database * @param int $rowId row id - * @param int $biggestMaxFileSize biggest max file size * @param string $defaultCharEditing default char editing mode which is stored in the config.inc.php script * @param string $textDir text direction * @param mixed[] $repopulate the data to be repopulated @@ -1900,7 +1733,6 @@ class InsertEdit string $table, string $db, int $rowId, - int $biggestMaxFileSize, string $defaultCharEditing, string $textDir, array $repopulate, @@ -1966,19 +1798,16 @@ class InsertEdit } else { // (we are inserting) // display default values - $tmp = $column; + $defaultValue = $column['Default'] ?? null; if (isset($repopulate[$fieldHashMd5])) { - $tmp['Default'] = $repopulate[$fieldHashMd5]; + $defaultValue = $repopulate[$fieldHashMd5]; } - [ - $realNullValue, - $data, - $specialChars, - $specialCharsEncoded, - ] = $this->getSpecialCharsAndBackupFieldForInsertingMode($tmp['Default'] ?? null, $tmp['True_Type']); + $realNullValue = $defaultValue === null; + $data = (string) $defaultValue; + $specialChars = $this->getSpecialCharsForInsertingMode($defaultValue, $column['True_Type']); + $specialCharsEncoded = Util::duplicateFirstNewline($specialChars); $backupField = ''; - unset($tmp); } $idindex = ($oRows * $columnsCnt) + $columnNumber + 1; @@ -2098,30 +1927,28 @@ class InsertEdit } if ($column['pma_type'] === 'enum') { - if (! isset($column['values'])) { - $column['values'] = $this->getColumnEnumValues($extractedColumnspec['enum_set_values']); - } + $column['values'] ??= $extractedColumnspec['enum_set_values']; foreach ($column['values'] as $enumValue) { if ( - $data == $enumValue['plain'] || ($data == '' + $data == $enumValue || ($data == '' && (! isset($_POST['where_clause']) || $column['Null'] !== 'YES') - && isset($column['Default']) && $enumValue['plain'] == $column['Default']) + && isset($column['Default']) && $enumValue == $column['Default']) ) { - $enumSelectedValue = $enumValue['plain']; + $enumSelectedValue = $enumValue; break; } } } elseif ($column['pma_type'] === 'set') { - [$columnSetValues, $setSelectSize] = $this->getColumnSetValueAndSelectSize( - $column, - $extractedColumnspec['enum_set_values'], - ); + $columnSetValues = $column['values'] ?? $extractedColumnspec['enum_set_values']; + $setSelectSize = ! isset($column['values']) + ? min(4, count($extractedColumnspec['enum_set_values'])) + : $column['select_size']; } elseif ($column['is_binary'] || $column['is_blob']) { $isColumnProtectedBlob = ($GLOBALS['cfg']['ProtectBinary'] === 'blob' && $column['is_blob']) || ($GLOBALS['cfg']['ProtectBinary'] === 'all') || ($GLOBALS['cfg']['ProtectBinary'] === 'noblob' && ! $column['is_blob']); - if ($isColumnProtectedBlob && isset($data)) { + if ($isColumnProtectedBlob) { $blobSize = Util::formatByteDown(mb_strlen(stripslashes($data)), 3, 1); if ($blobSize !== null) { [$blobValue, $blobValueUnit] = $blobSize; @@ -2129,7 +1956,7 @@ class InsertEdit } if ($isUpload && $column['is_blob']) { - [$maxUploadSize] = $this->getMaxUploadSize($column['pma_type'], $biggestMaxFileSize); + $maxUploadSize = $this->getMaxUploadSize($column['pma_type']); } if (! empty($GLOBALS['cfg']['UploadDir'])) { @@ -2228,28 +2055,27 @@ class InsertEdit /** * Function to get html for each insert/edit row * - * @param mixed[] $urlParams url parameters - * @param mixed[][] $tableColumns table columns - * @param mixed[] $commentsMap comments map - * @param bool $timestampSeen whether timestamp seen - * @param ResultInterface $currentResult current result - * @param string $jsvkey javascript validation key - * @param string $vkey validation key - * @param bool $insertMode whether insert mode - * @param mixed[] $currentRow current row - * @param int $oRows row offset - * @param int $tabindex tab index - * @param int $columnsCnt columns count - * @param bool $isUpload whether upload - * @param mixed[] $foreigners foreigners - * @param int $tabindexForValue tab index offset for value - * @param string $table table - * @param string $db database - * @param int $rowId row id - * @param int $biggestMaxFileSize biggest max file size - * @param string $textDir text direction - * @param mixed[] $repopulate the data to be repopulated - * @param mixed[] $whereClauseArray the array of where clauses + * @param mixed[] $urlParams url parameters + * @param mixed[][] $tableColumns table columns + * @param mixed[] $commentsMap comments map + * @param bool $timestampSeen whether timestamp seen + * @param ResultInterface $currentResult current result + * @param string $jsvkey javascript validation key + * @param string $vkey validation key + * @param bool $insertMode whether insert mode + * @param mixed[] $currentRow current row + * @param int $oRows row offset + * @param int $tabindex tab index + * @param int $columnsCnt columns count + * @param bool $isUpload whether upload + * @param mixed[] $foreigners foreigners + * @param int $tabindexForValue tab index offset for value + * @param string $table table + * @param string $db database + * @param int $rowId row id + * @param string $textDir text direction + * @param mixed[] $repopulate the data to be repopulated + * @param mixed[] $whereClauseArray the array of where clauses */ public function getHtmlForInsertEditRow( array $urlParams, @@ -2270,7 +2096,6 @@ class InsertEdit string $table, string $db, int $rowId, - int $biggestMaxFileSize, string $textDir, array $repopulate, array $whereClauseArray, @@ -2317,7 +2142,6 @@ class InsertEdit $table, $db, $rowId, - $biggestMaxFileSize, $defaultCharEditing, $textDir, $repopulate, diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 4801b729d4..5c2d8d27b4 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -2085,6 +2085,11 @@ parameters: count: 1 path: libraries/classes/Controllers/Table/ChangeController.php + - + message: "#^Parameter \\#1 \\$string of function trim expects string, mixed given\\.$#" + count: 1 + path: libraries/classes/Controllers/Table/ChangeController.php + - message: "#^Parameter \\#2 \\$signature of static method PhpMyAdmin\\\\Core\\:\\:checkSqlQuerySignature\\(\\) expects string, mixed given\\.$#" count: 1 @@ -2215,6 +2220,11 @@ parameters: count: 1 path: libraries/classes/Controllers/Table/ReplaceController.php + - + message: "#^Method PhpMyAdmin\\\\Controllers\\\\Table\\\\ReplaceController\\:\\:getParamsForUpdateOrInsert\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: libraries/classes/Controllers/Table/ReplaceController.php + - message: "#^PHPDoc tag @var for variable \\$relationFields has no value type specified in iterable type array\\.$#" count: 1 @@ -4741,12 +4751,12 @@ parameters: path: libraries/classes/InsertEdit.php - - message: "#^Method PhpMyAdmin\\\\InsertEdit\\:\\:getFormParametersForInsertForm\\(\\) has parameter \\$whereClauses with no value type specified in iterable type array\\.$#" + message: "#^Match expression does not handle remaining value\\: string$#" count: 1 path: libraries/classes/InsertEdit.php - - message: "#^Method PhpMyAdmin\\\\InsertEdit\\:\\:getParamsForUpdateOrInsert\\(\\) return type has no value type specified in iterable type array\\.$#" + message: "#^Method PhpMyAdmin\\\\InsertEdit\\:\\:getFormParametersForInsertForm\\(\\) has parameter \\$whereClauses with no value type specified in iterable type array\\.$#" count: 1 path: libraries/classes/InsertEdit.php @@ -4760,11 +4770,6 @@ parameters: count: 1 path: libraries/classes/InsertEdit.php - - - message: "#^Parameter \\#1 \\$enumSetValues of method PhpMyAdmin\\\\InsertEdit\\:\\:getColumnEnumValues\\(\\) expects array\\, mixed given\\.$#" - count: 1 - path: libraries/classes/InsertEdit.php - - message: "#^Parameter \\#1 \\$haystack of function mb_stripos expects string, mixed given\\.$#" count: 1 @@ -4810,11 +4815,6 @@ parameters: count: 1 path: libraries/classes/InsertEdit.php - - - message: "#^Parameter \\#1 \\$string of function stripslashes expects string, mixed given\\.$#" - count: 1 - path: libraries/classes/InsertEdit.php - - message: "#^Parameter \\#1 \\$string of function substr expects string, mixed given\\.$#" count: 1 @@ -4822,7 +4822,7 @@ parameters: - message: "#^Parameter \\#1 \\$string of function trim expects string, mixed given\\.$#" - count: 2 + count: 1 path: libraries/classes/InsertEdit.php - @@ -4835,31 +4835,21 @@ parameters: count: 1 path: libraries/classes/InsertEdit.php + - + message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, mixed given\\.$#" + count: 1 + path: libraries/classes/InsertEdit.php + - message: "#^Parameter \\#1 \\$value of static method PhpMyAdmin\\\\Util\\:\\:addMicroseconds\\(\\) expects string, mixed given\\.$#" count: 1 path: libraries/classes/InsertEdit.php - - - message: "#^Parameter \\#11 \\$specialCharsEncoded of method PhpMyAdmin\\\\InsertEdit\\:\\:getValueColumnForOtherDatatypes\\(\\) expects string, mixed given\\.$#" - count: 1 - path: libraries/classes/InsertEdit.php - - - - message: "#^Parameter \\#12 \\$data of method PhpMyAdmin\\\\InsertEdit\\:\\:getValueColumnForOtherDatatypes\\(\\) expects string, mixed given\\.$#" - count: 1 - path: libraries/classes/InsertEdit.php - - message: "#^Parameter \\#2 \\$column of method PhpMyAdmin\\\\ConfigStorage\\\\Relation\\:\\:searchColumnInForeigners\\(\\) expects string, mixed given\\.$#" count: 1 path: libraries/classes/InsertEdit.php - - - message: "#^Parameter \\#2 \\$enumSetValues of method PhpMyAdmin\\\\InsertEdit\\:\\:getColumnSetValueAndSelectSize\\(\\) expects array\\, mixed given\\.$#" - count: 1 - path: libraries/classes/InsertEdit.php - - message: "#^Parameter \\#2 \\$foreignField of method PhpMyAdmin\\\\ConfigStorage\\\\Relation\\:\\:foreignDropdown\\(\\) expects string, mixed given\\.$#" count: 1 @@ -4871,12 +4861,7 @@ parameters: path: libraries/classes/InsertEdit.php - - message: "#^Parameter \\#24 \\$whereClause of method PhpMyAdmin\\\\InsertEdit\\:\\:getHtmlForInsertEditFormColumn\\(\\) expects string, mixed given\\.$#" - count: 1 - path: libraries/classes/InsertEdit.php - - - - message: "#^Parameter \\#3 \\$backupField of method PhpMyAdmin\\\\InsertEdit\\:\\:getValueColumnForOtherDatatypes\\(\\) expects string, mixed given\\.$#" + message: "#^Parameter \\#23 \\$whereClause of method PhpMyAdmin\\\\InsertEdit\\:\\:getHtmlForInsertEditFormColumn\\(\\) expects string, mixed given\\.$#" count: 1 path: libraries/classes/InsertEdit.php @@ -4885,21 +4870,6 @@ parameters: count: 1 path: libraries/classes/InsertEdit.php - - - message: "#^Parameter \\#3 \\$specialChars of method PhpMyAdmin\\\\InsertEdit\\:\\:getHtmlInput\\(\\) expects string, mixed given\\.$#" - count: 1 - path: libraries/classes/InsertEdit.php - - - - message: "#^Parameter \\#4 \\$data of method PhpMyAdmin\\\\ConfigStorage\\\\Relation\\:\\:foreignDropdown\\(\\) expects string, mixed given\\.$#" - count: 1 - path: libraries/classes/InsertEdit.php - - - - message: "#^Parameter \\#7 \\$specialChars of method PhpMyAdmin\\\\InsertEdit\\:\\:getValueColumnForOtherDatatypes\\(\\) expects string, mixed given\\.$#" - count: 1 - path: libraries/classes/InsertEdit.php - - message: "#^Binary operation \"\\-\" between string and 1 results in an error\\.$#" count: 1 @@ -9335,6 +9305,11 @@ parameters: count: 7 path: test/classes/Controllers/Server/VariablesControllerTest.php + - + message: "#^PHPDoc tag @var for variable \\$result has no value type specified in iterable type array\\.$#" + count: 2 + path: test/classes/Controllers/Table/ReplaceControllerTest.php + - message: "#^Cannot cast mixed to string\\.$#" count: 3 diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 8d73a2f27e..13345003f9 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -2174,7 +2174,6 @@ - @@ -3271,7 +3270,6 @@ - @@ -3305,6 +3303,7 @@ $isUpload getQueryParam('where_clause_signature')]]> $whereClause + $whereClause $rowId @@ -3312,7 +3311,6 @@ - @@ -3339,6 +3337,7 @@ $isUpload $whereClause + $whereClause @@ -3347,9 +3346,6 @@ $isUpload - - 0]]> - @@ -3841,6 +3837,7 @@ + $columnName $columnName $columnName @@ -3922,6 +3919,7 @@ + $multiEditColumnsPrev[$key] $multiEditColumnsType[$key] @@ -7917,8 +7915,6 @@ ] - - $backupField @@ -7932,6 +7928,7 @@ + @@ -7953,10 +7950,7 @@ - $data - $data - $data - + $defaultValue @@ -7971,33 +7965,22 @@ $rows[$keyId] $singleQuery $singleQuery - $specialChars - $specialChars - $specialCharsEncoded - - getScripts()]]> $whereClause $whereClause - $whereClause - $whereClause $query - $queryFields $thisUrlParams $thisUrlParams $urlParams - $valueSets + $whereClauseArray $currCellEditedValues[$columnName] - - - $editedValues[$cellIndex][$columnName] @@ -8032,7 +8015,9 @@ + $columnSetValues + $currCellEditedValues @@ -8040,6 +8025,8 @@ $currentValue $data + $defaultValue + $defaultValue $enumSelectedValue $enumValue $fieldsize @@ -8047,14 +8034,13 @@ $isUnsigned $maxlength $maxlength + $setSelectSize $singleQuery $specialChars - $transformedHtml $whereClause $whereClause $whereClause - $whereClause int @@ -8093,11 +8079,7 @@ $whereClause - $whereClause - - - @@ -8108,7 +8090,6 @@ - @@ -14895,8 +14876,6 @@ $result $result $result - $result - $result diff --git a/templates/import.twig b/templates/import.twig index dc63c91c06..1abfd6d0ec 100644 --- a/templates/import.twig +++ b/templates/import.twig @@ -42,7 +42,6 @@
-
@@ -69,7 +68,6 @@
{% elseif is_upload %} -
diff --git a/templates/preferences/manage/main.twig b/templates/preferences/manage/main.twig index ebe6a4a6b8..6bbbd1d224 100644 --- a/templates/preferences/manage/main.twig +++ b/templates/preferences/manage/main.twig @@ -14,7 +14,6 @@
{{ get_hidden_inputs() }} - diff --git a/templates/table/insert/column_row.twig b/templates/table/insert/column_row.twig index c5e4d761b5..1dcdeb1c1a 100644 --- a/templates/table/insert/column_row.twig +++ b/templates/table/insert/column_row.twig @@ -71,13 +71,13 @@ {% else %} {% for enum_value in column.values %} - - + + {% endfor %} {% endif %} {% elseif column.pma_type == 'set' %} @@ -85,7 +85,7 @@ {% elseif column.is_binary or column.is_blob %} diff --git a/test/classes/Controllers/Table/ChangeControllerTest.php b/test/classes/Controllers/Table/ChangeControllerTest.php index bb90395d48..8d74f722f7 100644 --- a/test/classes/Controllers/Table/ChangeControllerTest.php +++ b/test/classes/Controllers/Table/ChangeControllerTest.php @@ -79,4 +79,27 @@ class ChangeControllerTest extends AbstractTestCase $actual, ); } + + /** + * Test for urlParamsInEditMode + */ + public function testUrlParamsInEditMode(): void + { + $changeController = new ChangeController( + $this->createStub(ResponseRenderer::class), + $this->createStub(Template::class), + $this->createStub(InsertEdit::class), + $this->createStub(Relation::class), + ); + + $whereClauseArray = ['foo=1', 'bar=2']; + $_POST['sql_query'] = 'SELECT 1'; + + $result = $changeController->urlParamsInEditMode([1], $whereClauseArray); + + $this->assertEquals( + ['0' => 1, 'where_clause' => 'bar=2', 'sql_query' => 'SELECT 1'], + $result, + ); + } } diff --git a/test/classes/Controllers/Table/ReplaceControllerTest.php b/test/classes/Controllers/Table/ReplaceControllerTest.php index 185ba90e90..17dc5a51c5 100644 --- a/test/classes/Controllers/Table/ReplaceControllerTest.php +++ b/test/classes/Controllers/Table/ReplaceControllerTest.php @@ -187,4 +187,52 @@ class ReplaceControllerTest extends AbstractTestCase $output, ); } + + /** + * Test for getParamsForUpdateOrInsert + */ + public function testGetParamsForUpdateOrInsert(): void + { + $_POST['where_clause'] = 'LIMIT 1'; + $_POST['submit_type'] = 'showinsert'; + + $replaceController = new ReplaceController( + $this->createStub(ResponseRenderer::class), + $this->createStub(Template::class), + $this->createStub(InsertEdit::class), + $this->createStub(Transformations::class), + $this->createStub(Relation::class), + $this->createStub(DatabaseInterface::class), + ); + + /** @var array $result */ + $result = $this->callFunction( + $replaceController, + ReplaceController::class, + 'getParamsForUpdateOrInsert', + [], + ); + + $this->assertEquals( + [['LIMIT 1'], true, true], + $result, + ); + + // case 2 (else) + unset($_POST['where_clause']); + $_POST['fields']['multi_edit'] = ['a' => 'b', 'c' => 'd']; + + /** @var array $result */ + $result = $this->callFunction( + $replaceController, + ReplaceController::class, + 'getParamsForUpdateOrInsert', + [], + ); + + $this->assertEquals( + [['a', 'c'], false, true], + $result, + ); + } } diff --git a/test/classes/InsertEditTest.php b/test/classes/InsertEditTest.php index 59f750fafd..8fea1fc02c 100644 --- a/test/classes/InsertEditTest.php +++ b/test/classes/InsertEditTest.php @@ -181,42 +181,6 @@ class InsertEditTest extends AbstractTestCase ); } - /** - * Test for getWhereClauseArray - */ - public function testGetWhereClauseArray(): void - { - $this->assertEquals( - [], - $this->callFunction( - $this->insertEdit, - InsertEdit::class, - 'getWhereClauseArray', - [null], - ), - ); - - $this->assertEquals( - [1, 2, 3], - $this->callFunction( - $this->insertEdit, - InsertEdit::class, - 'getWhereClauseArray', - [[1, 2, 3]], - ), - ); - - $this->assertEquals( - ['clause'], - $this->callFunction( - $this->insertEdit, - InsertEdit::class, - 'getWhereClauseArray', - ['clause'], - ), - ); - } - /** * Test for analyzeWhereClauses */ @@ -336,23 +300,8 @@ class InsertEditTest extends AbstractTestCase $this->assertFalse($result); } - /** @return list}> */ - public static function dataProviderConfigValueInsertRows(): array + public function testLoadFirstRow(): void { - return [[2, [false, false]], ['2', [false, false]], [3, [false, false, false]], ['3', [false, false, false]]]; - } - - /** - * Test for loadFirstRow - * - * @param array $rowsValue - * - * @dataProvider dataProviderConfigValueInsertRows - */ - public function testLoadFirstRow(string|int $configValue, array $rowsValue): void - { - $GLOBALS['cfg']['InsertRows'] = $configValue; - $resultStub = $this->createMock(DummyResult::class); $dbi = $this->getMockBuilder(DatabaseInterface::class) @@ -380,26 +329,34 @@ class InsertEditTest extends AbstractTestCase ['table', 'db'], ); - $this->assertEquals( - [$resultStub, $rowsValue], - $result, - ); + $this->assertEquals($resultStub, $result); + } + + /** @return list}> */ + public static function dataProviderConfigValueInsertRows(): array + { + return [[2, [false, false]], ['2', [false, false]], [3, [false, false, false]], ['3', [false, false, false]]]; } /** - * Test for urlParamsInEditMode + * Test for loadFirstRow + * + * @param array $rowsValue + * + * @dataProvider dataProviderConfigValueInsertRows */ - public function testUrlParamsInEditMode(): void + public function testGetInsertRows(string|int $configValue, array $rowsValue): void { - $whereClauseArray = ['foo=1', 'bar=2']; - $_POST['sql_query'] = 'SELECT 1'; + $GLOBALS['cfg']['InsertRows'] = $configValue; - $result = $this->insertEdit->urlParamsInEditMode([1], $whereClauseArray); - - $this->assertEquals( - ['0' => 1, 'where_clause' => 'bar=2', 'sql_query' => 'SELECT 1'], - $result, + $result = $this->callFunction( + $this->insertEdit, + InsertEdit::class, + 'getInsertRows', + [], ); + + $this->assertEquals($rowsValue, $result); } /** @@ -573,83 +530,6 @@ class InsertEditTest extends AbstractTestCase $this->assertFalse($this->insertEdit->isColumn($column, $types)); } - /** - * Test for getEnumSetAndTimestampColumns - */ - public function testGetEnumAndTimestampColumns(): void - { - $column = []; - $column['True_Type'] = 'set'; - $this->assertEquals( - ['set', '', false], - $this->callFunction( - $this->insertEdit, - InsertEdit::class, - 'getEnumSetAndTimestampColumns', - [$column, false], - ), - ); - - $column['True_Type'] = 'enum'; - $this->assertEquals( - ['enum', '', false], - $this->callFunction( - $this->insertEdit, - InsertEdit::class, - 'getEnumSetAndTimestampColumns', - [$column, false], - ), - ); - - $column['True_Type'] = 'timestamp'; - $column['Type'] = 'date'; - $this->assertEquals( - ['date', ' text-nowrap', true], - $this->callFunction( - $this->insertEdit, - InsertEdit::class, - 'getEnumSetAndTimestampColumns', - [$column, false], - ), - ); - - $column['True_Type'] = 'timestamp'; - $column['Type'] = 'date'; - $this->assertEquals( - ['date', ' text-nowrap', false], - $this->callFunction( - $this->insertEdit, - InsertEdit::class, - 'getEnumSetAndTimestampColumns', - [$column, true], - ), - ); - - $column['True_Type'] = 'SET'; - $column['Type'] = 'num'; - $this->assertEquals( - ['num', ' text-nowrap', false], - $this->callFunction( - $this->insertEdit, - InsertEdit::class, - 'getEnumSetAndTimestampColumns', - [$column, false], - ), - ); - - $column['True_Type'] = ''; - $column['Type'] = 'num'; - $this->assertEquals( - ['num', ' text-nowrap', false], - $this->callFunction( - $this->insertEdit, - InsertEdit::class, - 'getEnumSetAndTimestampColumns', - [$column, false], - ), - ); - } - /** * Test for getNullifyCodeForNullColumn */ @@ -739,59 +619,6 @@ class InsertEditTest extends AbstractTestCase ); } - /** - * Test for getColumnEnumValues - */ - public function testGetColumnEnumValues(): void - { - $enumSetValues = ['', '"foo"']; - - $result = $this->callFunction( - $this->insertEdit, - InsertEdit::class, - 'getColumnEnumValues', - [$enumSetValues], - ); - $this->assertEquals( - [['plain' => '', 'html' => '<abc>'], ['plain' => '"foo"', 'html' => '"foo"']], - $result, - ); - } - - /** - * Test for getColumnSetValueAndSelectSize - */ - public function testGetColumnSetValueAndSelectSize(): void - { - $column = []; - $enumSetValues = ['a', '<']; - $result = $this->callFunction( - $this->insertEdit, - InsertEdit::class, - 'getColumnSetValueAndSelectSize', - [[], $enumSetValues], - ); - - $this->assertEquals( - [[['plain' => 'a', 'html' => 'a'], ['plain' => '<', 'html' => '<']], 2], - $result, - ); - - $column['values'] = [1, 2]; - $column['select_size'] = 3; - $result = $this->callFunction( - $this->insertEdit, - InsertEdit::class, - 'getColumnSetValueAndSelectSize', - [$column, $enumSetValues], - ); - - $this->assertEquals( - [[1, 2], 3], - $result, - ); - } - /** * Test for getHtmlInput */ @@ -872,13 +699,10 @@ class InsertEditTest extends AbstractTestCase $this->insertEdit, InsertEdit::class, 'getMaxUploadSize', - [$pmaType, 256], + [$pmaType], ); - $this->assertEquals( - ["(Max: 256B)\n", 256], - $result, - ); + $this->assertEquals("(Max: 256B)\n", $result); // case 2 $GLOBALS['config']->set('max_upload_size', 250); @@ -887,13 +711,10 @@ class InsertEditTest extends AbstractTestCase $this->insertEdit, InsertEdit::class, 'getMaxUploadSize', - [$pmaType, 20], + [$pmaType], ); - $this->assertEquals( - ["(Max: 250B)\n", 250], - $result, - ); + $this->assertEquals("(Max: 250B)\n", $result); } /** @@ -1341,26 +1162,26 @@ class InsertEditTest extends AbstractTestCase } /** - * Test for getSpecialCharsAndBackupFieldForInsertingMode + * Test for getSpecialCharsForInsertingMode * - * @param array $column Column parameters - * @param array $expected Expected result + * @param array $column Column parameters + * @param string $expected Expected result * @psalm-param array $column - * @psalm-param array $expected * - * @dataProvider providerForTestGetSpecialCharsAndBackupFieldForInsertingMode + * @dataProvider providerForTestGetSpecialCharsForInsertingMode */ - public function testGetSpecialCharsAndBackupFieldForInsertingMode( + public function testGetSpecialCharsForInsertingMode( array $column, - array $expected, + string $expected, ): void { $GLOBALS['cfg']['ProtectBinary'] = false; $GLOBALS['cfg']['ShowFunctionFields'] = true; - $result = (array) $this->callFunction( + /** @var string $result */ + $result = $this->callFunction( $this->insertEdit, InsertEdit::class, - 'getSpecialCharsAndBackupFieldForInsertingMode', + 'getSpecialCharsForInsertingMode', [$column['Default'] ?? null, $column['True_Type']], ); @@ -1368,76 +1189,45 @@ class InsertEditTest extends AbstractTestCase } /** - * Data provider for test getSpecialCharsAndBackupFieldForInsertingMode() + * Data provider for test getSpecialCharsForInsertingMode() * - * @return array, array}> + * @return array, string}> */ - public static function providerForTestGetSpecialCharsAndBackupFieldForInsertingMode(): array + public static function providerForTestGetSpecialCharsForInsertingMode(): array { return [ 'bit' => [ ['True_Type' => 'bit', 'Default' => 'b\'101\'', 'is_binary' => true], - [false, 'b\'101\'', '101', '101'], + '101', ], - 'char' => [['True_Type' => 'char', 'is_binary' => true], [true, '', '', '']], + 'char' => [['True_Type' => 'char', 'is_binary' => true], ''], 'time with CURRENT_TIMESTAMP value' => [ ['True_Type' => 'time', 'Default' => 'CURRENT_TIMESTAMP'], - [false, 'CURRENT_TIMESTAMP', 'CURRENT_TIMESTAMP', 'CURRENT_TIMESTAMP'], + 'CURRENT_TIMESTAMP', ], 'time with current_timestamp() value' => [ ['True_Type' => 'time', 'Default' => 'current_timestamp()'], - [false, 'current_timestamp()', 'current_timestamp()', 'current_timestamp()'], + 'current_timestamp()', ], 'time with no dot value' => [ ['True_Type' => 'time', 'Default' => '10'], - [false, '10', '10.000000', '10.000000'], + '10.000000', ], 'time with dot value' => [ ['True_Type' => 'time', 'Default' => '10.08'], - [false, '10.08', '10.080000', '10.080000'], + '10.080000', ], 'any text with escape text default' => [ ['True_Type' => 'text', 'Default' => '"lorem\"ipsem"'], - [false, '"lorem\"ipsem"', 'lorem"ipsem', 'lorem"ipsem'], + 'lorem"ipsem', ], 'varchar with html special chars' => [ ['True_Type' => 'varchar', 'Default' => 'hello world
lorem ipsem'], - [ - false, - 'hello world
lorem ipsem', - 'hello world<br><b>lorem</b> ipsem', - 'hello world<br><b>lorem</b> ipsem', - ], + 'hello world<br><b>lorem</b> ipsem', ], ]; } - /** - * Test for getParamsForUpdateOrInsert - */ - public function testGetParamsForUpdateOrInsert(): void - { - $_POST['where_clause'] = 'LIMIT 1'; - $_POST['submit_type'] = 'showinsert'; - - $result = $this->insertEdit->getParamsForUpdateOrInsert(); - - $this->assertEquals( - [['LIMIT 1'], true, true, false], - $result, - ); - - // case 2 (else) - unset($_POST['where_clause']); - $_POST['fields']['multi_edit'] = ['a' => 'b', 'c' => 'd']; - $result = $this->insertEdit->getParamsForUpdateOrInsert(); - - $this->assertEquals( - [['a', 'c'], false, true, false], - $result, - ); - } - /** * Test for setSessionForEditNext */ @@ -1546,19 +1336,17 @@ class InsertEditTest extends AbstractTestCase */ public function testBuildSqlQuery(): void { - $GLOBALS['db'] = 'db'; - $GLOBALS['table'] = 'table'; $queryFields = ['a', 'b']; - $valueSets = [1, 2]; + $valueSets = ['1', '2']; $this->assertEquals( - ['INSERT IGNORE INTO `table` (a, b) VALUES (1), (2)'], - $this->insertEdit->buildSqlQuery(true, $queryFields, $valueSets), + 'INSERT IGNORE INTO `table` (a, b) VALUES (1), (2)', + $this->insertEdit->buildInsertSqlQuery('table', true, $queryFields, $valueSets), ); $this->assertEquals( - ['INSERT INTO `table` (a, b) VALUES (1), (2)'], - $this->insertEdit->buildSqlQuery(false, $queryFields, $valueSets), + 'INSERT INTO `table` (a, b) VALUES (1), (2)', + $this->insertEdit->buildInsertSqlQuery('table', false, $queryFields, $valueSets), ); } @@ -2661,7 +2449,6 @@ class InsertEditTest extends AbstractTestCase 'table', 'db', 0, - 0, '', '', $repopulate, @@ -2721,7 +2508,6 @@ class InsertEditTest extends AbstractTestCase 'table', 'db', 0, - 0, '', '', $repopulate, @@ -2829,7 +2615,6 @@ class InsertEditTest extends AbstractTestCase 'table', 'db', 0, - 0, 'ltr', [], ['wc'], @@ -2908,7 +2693,6 @@ class InsertEditTest extends AbstractTestCase 'table', 'db', 0, - 0, '', [], ['wc'], @@ -2968,7 +2752,6 @@ class InsertEditTest extends AbstractTestCase 'table', 'db', 0, - 0, '', [], ['wc'],