From 8fa2e36f5c71833a9d53e05d5a084b76983ed0ab Mon Sep 17 00:00:00 2001 From: William Desportes Date: Tue, 23 Feb 2021 16:53:23 +0100 Subject: [PATCH 1/2] Update baselines Signed-off-by: William Desportes --- phpstan-baseline.neon | 5 ----- psalm-baseline.xml | 34 ++++++++++++++++++++-------------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 49fc4bff67..1cfc2aa806 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1830,11 +1830,6 @@ parameters: count: 4 path: libraries/classes/Plugins/Schema/Svg/TableStatsSvg.php - - - message: "#^Cannot access property \\$type on stdClass\\|null\\.$#" - count: 1 - path: libraries/classes/Plugins/Transformations/Abs/DateFormatTransformationsPlugin.php - - message: "#^Binary operation \"\\-\\=\" between int\\<0, max\\>\\|string\\|false and int results in an error\\.$#" count: 1 diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 66c11ed458..43b7b348da 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -485,6 +485,9 @@ $this->dbi->fetchSingleRow($sql_query) + + (int) $fields_meta[$i]->length + @@ -538,6 +541,9 @@ $dataLabel $key + + (int) $fields_meta[$i]->length + @@ -682,15 +688,11 @@ - - array|bool - string|false - $mysqli->connect_errno $mysqli->connect_error - + $mysqli $mysqli $mysqli @@ -714,7 +716,6 @@ $result $result $result - $result $databaseName @@ -808,6 +809,14 @@ (string) '1' (string) '1' + + (int) $meta->length + (string) $fields_meta->name + (string) $fields_meta->name + (string) $fields_meta->name + (string) $fields_meta[$i]->name + (string) $fields_meta[$i]->name + $column === null $column === null @@ -1487,7 +1496,8 @@ $view_alias $view_alias - + + (int) $fields_meta[$j]->length (string) $create_query (string) $table @@ -2206,9 +2216,6 @@ $timestamp - - $meta->type - (string) $buffer (string) $buffer @@ -2416,9 +2423,7 @@ save - - $fields_meta - $fields_meta + $result $result $result @@ -2693,7 +2698,8 @@ (string) strftime($string) - + + (int) $meta->length (int) $timestamp (int) $timestamp (int) $timestamp From c5155ecae9650fd0c5e2ff38f7f1a1d20ab60477 Mon Sep 17 00:00:00 2001 From: William Desportes Date: Sun, 14 Feb 2021 23:51:02 +0100 Subject: [PATCH 2/2] Refactor field flags to use an object an not strpos Signed-off-by: William Desportes --- .../Controllers/Table/ChartController.php | 14 +- .../Table/GisVisualizationController.php | 4 +- .../Controllers/Table/SearchController.php | 2 +- .../Table/ZoomSearchController.php | 6 +- libraries/classes/Database/Routines.php | 3 +- libraries/classes/DatabaseInterface.php | 20 +- libraries/classes/Dbal/DbalInterface.php | 15 +- libraries/classes/Dbal/DbiExtension.php | 16 +- libraries/classes/Dbal/DbiMysqli.php | 181 +------ libraries/classes/Display/Results.php | 411 ++++++++-------- libraries/classes/FieldMetadata.php | 456 ++++++++++++++++++ libraries/classes/InsertEdit.php | 14 +- .../classes/Plugins/Export/ExportJson.php | 8 +- .../classes/Plugins/Export/ExportOds.php | 28 +- .../classes/Plugins/Export/ExportOdt.php | 20 +- .../classes/Plugins/Export/ExportSql.php | 23 +- .../classes/Plugins/Export/Helpers/Pdf.php | 45 +- .../Abs/Bool2TextTransformationsPlugin.php | 10 +- .../CodeMirrorEditorTransformationPlugin.php | 10 +- .../Abs/DateFormatTransformationsPlugin.php | 12 +- .../Abs/DownloadTransformationsPlugin.php | 10 +- .../Abs/ExternalTransformationsPlugin.php | 10 +- .../Abs/FormattedTransformationsPlugin.php | 10 +- .../Abs/HexTransformationsPlugin.php | 10 +- .../Abs/ImageLinkTransformationsPlugin.php | 10 +- .../Abs/ImageUploadTransformationsPlugin.php | 10 +- .../Abs/InlineTransformationsPlugin.php | 10 +- .../Abs/LongToIPv4TransformationsPlugin.php | 10 +- .../Abs/PreApPendTransformationsPlugin.php | 10 +- .../RegexValidationTransformationsPlugin.php | 10 +- .../Abs/SQLTransformationsPlugin.php | 10 +- .../Abs/SubstringTransformationsPlugin.php | 10 +- .../TextFileUploadTransformationsPlugin.php | 10 +- .../TextImageLinkTransformationsPlugin.php | 10 +- .../Abs/TextLinkTransformationsPlugin.php | 10 +- .../Input/Text_Plain_Iptobinary.php | 14 +- .../Input/Text_Plain_Iptolong.php | 14 +- .../Output/Text_Plain_Binarytoip.php | 14 +- .../Output/Text_Plain_Json.php | 10 +- .../Transformations/Output/Text_Plain_Xml.php | 10 +- .../Plugins/Transformations/TEMPLATE_ABSTRACT | 8 +- .../classes/Plugins/TransformationsPlugin.php | 10 +- libraries/classes/Sql.php | 19 +- libraries/classes/Util.php | 45 +- .../display/results/comment_for_row.twig | 12 +- templates/display/results/null_display.twig | 4 +- templates/table/chart/tbl_chart.twig | 7 +- .../Table/SearchControllerTest.php | 11 +- test/classes/Display/ResultsTest.php | 53 +- test/classes/FieldMetadataTest.php | 136 ++++++ test/classes/InsertEditTest.php | 25 +- .../classes/Plugins/Export/ExportJsonTest.php | 7 +- test/classes/Plugins/Export/ExportOdsTest.php | 117 ++--- test/classes/Plugins/Export/ExportOdtTest.php | 84 ++-- test/classes/Plugins/Export/ExportSqlTest.php | 65 +-- .../TransformationPluginsTest.php | 11 +- test/classes/Stubs/DbiDummy.php | 19 +- test/classes/TableTest.php | 4 +- test/classes/UtilTest.php | 88 +--- 59 files changed, 1234 insertions(+), 1001 deletions(-) create mode 100644 libraries/classes/FieldMetadata.php create mode 100644 test/classes/FieldMetadataTest.php diff --git a/libraries/classes/Controllers/Table/ChartController.php b/libraries/classes/Controllers/Table/ChartController.php index 6da1dea342..ede10df354 100644 --- a/libraries/classes/Controllers/Table/ChartController.php +++ b/libraries/classes/Controllers/Table/ChartController.php @@ -6,6 +6,7 @@ namespace PhpMyAdmin\Controllers\Table; use PhpMyAdmin\DatabaseInterface; use PhpMyAdmin\DbTableExists; +use PhpMyAdmin\FieldMetadata; use PhpMyAdmin\Message; use PhpMyAdmin\Response; use PhpMyAdmin\SqlParser\Components\Limit; @@ -16,7 +17,6 @@ use PhpMyAdmin\Url; use PhpMyAdmin\Util; use function array_keys; use function htmlspecialchars; -use function in_array; use function json_encode; use function min; use function strlen; @@ -125,20 +125,19 @@ class ChartController extends AbstractController $data = []; $result = $this->dbi->tryQuery($sql_query); - $fields_meta = $this->dbi->getFieldsMeta($result); + $fields_meta = $this->dbi->getFieldsMeta($result) ?? []; while ($row = $this->dbi->fetchAssoc($result)) { $data[] = $row; } $keys = array_keys($data[0]); - $numeric_types = [ - 'int', - 'real', - ]; $numeric_column_count = 0; foreach ($keys as $idx => $key) { - if (! in_array($fields_meta[$idx]->type, $numeric_types)) { + if (isset($fields_meta[$idx]) && ( + $fields_meta[$idx]->isNotType(FieldMetadata::TYPE_INT) + || $fields_meta[$idx]->isNotType(FieldMetadata::TYPE_REAL) + )) { continue; } @@ -165,7 +164,6 @@ class ChartController extends AbstractController 'url_params' => $url_params, 'keys' => $keys, 'fields_meta' => $fields_meta, - 'numeric_types' => $numeric_types, 'numeric_column_count' => $numeric_column_count, 'sql_query' => $sql_query, ]); diff --git a/libraries/classes/Controllers/Table/GisVisualizationController.php b/libraries/classes/Controllers/Table/GisVisualizationController.php index 18435a855b..1bf16fc6c6 100644 --- a/libraries/classes/Controllers/Table/GisVisualizationController.php +++ b/libraries/classes/Controllers/Table/GisVisualizationController.php @@ -73,13 +73,13 @@ final class GisVisualizationController extends AbstractController // Execute the query and return the result $result = $this->dbi->tryQuery($sqlQuery); // Get the meta data of results - $meta = $this->dbi->getFieldsMeta($result); + $meta = $this->dbi->getFieldsMeta($result) ?? []; // Find the candidate fields for label column and spatial column $labelCandidates = []; $spatialCandidates = []; foreach ($meta as $column_meta) { - if ($column_meta->type === 'geometry') { + if ($column_meta->isMappedTypeGeometry) { $spatialCandidates[] = $column_meta->name; } else { $labelCandidates[] = $column_meta->name; diff --git a/libraries/classes/Controllers/Table/SearchController.php b/libraries/classes/Controllers/Table/SearchController.php index 829a01fce4..592069143b 100644 --- a/libraries/classes/Controllers/Table/SearchController.php +++ b/libraries/classes/Controllers/Table/SearchController.php @@ -249,7 +249,7 @@ class SearchController extends AbstractController // for bit fields we need to convert them to printable form $i = 0; foreach ($row as $col => $val) { - if ($fields_meta[$i]->type === 'bit') { + if (isset($fields_meta[$i]) && $fields_meta[$i]->isMappedTypeBit) { $row[$col] = Util::printableBitValue( (int) $val, (int) $fields_meta[$i]->length diff --git a/libraries/classes/Controllers/Table/ZoomSearchController.php b/libraries/classes/Controllers/Table/ZoomSearchController.php index dc389744f9..4b5aedabfe 100644 --- a/libraries/classes/Controllers/Table/ZoomSearchController.php +++ b/libraries/classes/Controllers/Table/ZoomSearchController.php @@ -294,12 +294,12 @@ class ZoomSearchController extends AbstractController DatabaseInterface::CONNECT_USER, DatabaseInterface::QUERY_STORE ); - $fields_meta = $this->dbi->getFieldsMeta($result); + $fields_meta = $this->dbi->getFieldsMeta($result) ?? []; while ($row = $this->dbi->fetchAssoc($result)) { // for bit fields we need to convert them to printable form $i = 0; foreach ($row as $col => $val) { - if ($fields_meta[$i]->type === 'bit') { + if ($fields_meta[$i]->isMappedTypeBit) { $row[$col] = Util::printableBitValue( (int) $val, (int) $fields_meta[$i]->length @@ -363,7 +363,7 @@ class ZoomSearchController extends AbstractController DatabaseInterface::CONNECT_USER, DatabaseInterface::QUERY_STORE ); - $fields_meta = $this->dbi->getFieldsMeta($result); + $fields_meta = $this->dbi->getFieldsMeta($result) ?? []; $data = []; while ($row = $this->dbi->fetchAssoc($result)) { //Need a row with indexes as 0,1,2 for the getUniqueCondition diff --git a/libraries/classes/Database/Routines.php b/libraries/classes/Database/Routines.php index a73d1642bd..21570be550 100644 --- a/libraries/classes/Database/Routines.php +++ b/libraries/classes/Database/Routines.php @@ -1226,7 +1226,8 @@ class Routines if (($result !== false) && ($num_rows > 0)) { $output .= ''; - foreach ($this->dbi->getFieldsMeta($result) as $field) { + $fieldsMeta = $this->dbi->getFieldsMeta($result) ?? []; + foreach ($fieldsMeta as $field) { $output .= ''; diff --git a/libraries/classes/DatabaseInterface.php b/libraries/classes/DatabaseInterface.php index 9e006a49a3..ea94e65873 100644 --- a/libraries/classes/DatabaseInterface.php +++ b/libraries/classes/DatabaseInterface.php @@ -772,6 +772,7 @@ class DatabaseInterface implements DbalInterface return []; } + /** @var FieldMetadata[] $meta */ $meta = $this->getFieldsMeta( $result ); @@ -2219,13 +2220,13 @@ class DatabaseInterface implements DbalInterface * * @param object $result result set identifier * - * @return mixed meta info for fields in $result + * @return FieldMetadata[]|null meta info for fields in $result */ - public function getFieldsMeta($result) + public function getFieldsMeta($result): ?array { $result = $this->extension->getFieldsMeta($result); - if ($this->getLowerCaseNames() === '2') { + if ($result !== null && $this->getLowerCaseNames() === '2') { /** * Fixup orgtable for lower_case_table_names = 2 * @@ -2285,19 +2286,6 @@ class DatabaseInterface implements DbalInterface return $this->extension->fieldName($result, $i); } - /** - * returns concatenated string of human readable field flags - * - * @param object $result result set identifier - * @param int $i field - * - * @return string field flags - */ - public function fieldFlags($result, $i): string - { - return $this->extension->fieldFlags($result, $i); - } - /** * returns properly escaped string for use in MySQL queries * diff --git a/libraries/classes/Dbal/DbalInterface.php b/libraries/classes/Dbal/DbalInterface.php index c0da4c1f8e..7de78b4b14 100644 --- a/libraries/classes/Dbal/DbalInterface.php +++ b/libraries/classes/Dbal/DbalInterface.php @@ -6,6 +6,7 @@ namespace PhpMyAdmin\Dbal; use mysqli_result; use PhpMyAdmin\DatabaseInterface; +use PhpMyAdmin\FieldMetadata; use PhpMyAdmin\SystemDatabase; use PhpMyAdmin\Table; @@ -670,9 +671,9 @@ interface DbalInterface * * @param object $result result set identifier * - * @return mixed meta info for fields in $result + * @return FieldMetadata[]|null meta info for fields in $result */ - public function getFieldsMeta($result); + public function getFieldsMeta($result): ?array; /** * return number of fields in given $result @@ -703,16 +704,6 @@ interface DbalInterface */ public function fieldName($result, int $i): string; - /** - * returns concatenated string of human readable field flags - * - * @param object $result result set identifier - * @param int $i field - * - * @return string field flags - */ - public function fieldFlags($result, $i): string; - /** * returns properly escaped string for use in MySQL queries * diff --git a/libraries/classes/Dbal/DbiExtension.php b/libraries/classes/Dbal/DbiExtension.php index 102294680b..63a732a14a 100644 --- a/libraries/classes/Dbal/DbiExtension.php +++ b/libraries/classes/Dbal/DbiExtension.php @@ -7,6 +7,8 @@ declare(strict_types=1); namespace PhpMyAdmin\Dbal; +use PhpMyAdmin\FieldMetadata; + /** * Contract for every database extension supported by phpMyAdmin */ @@ -184,9 +186,9 @@ interface DbiExtension * * @param object $result result set identifier * - * @return array meta info for fields in $result + * @return FieldMetadata[]|null meta info for fields in $result */ - public function getFieldsMeta($result); + public function getFieldsMeta($result): ?array; /** * return number of fields in given $result @@ -217,16 +219,6 @@ interface DbiExtension */ public function fieldName($result, $i); - /** - * returns concatenated string of human readable field flags - * - * @param object $result result set identifier - * @param int $i field - * - * @return string field flags - */ - public function fieldFlags($result, $i); - /** * returns properly escaped string for use in MySQL queries * diff --git a/libraries/classes/Dbal/DbiMysqli.php b/libraries/classes/Dbal/DbiMysqli.php index 546a4c2d20..814baeb2c0 100644 --- a/libraries/classes/Dbal/DbiMysqli.php +++ b/libraries/classes/Dbal/DbiMysqli.php @@ -11,64 +11,23 @@ use mysqli; use mysqli_result; use mysqli_stmt; use PhpMyAdmin\DatabaseInterface; +use PhpMyAdmin\FieldMetadata; use PhpMyAdmin\Query\Utilities; use stdClass; use function mysqli_report; use const E_USER_WARNING; use const MYSQLI_ASSOC; -use const MYSQLI_AUTO_INCREMENT_FLAG; -use const MYSQLI_BLOB_FLAG; use const MYSQLI_BOTH; use const MYSQLI_CLIENT_COMPRESS; use const MYSQLI_CLIENT_SSL; use const MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT; -use const MYSQLI_ENUM_FLAG; -use const MYSQLI_MULTIPLE_KEY_FLAG; -use const MYSQLI_NOT_NULL_FLAG; use const MYSQLI_NUM; -use const MYSQLI_NUM_FLAG; use const MYSQLI_OPT_LOCAL_INFILE; use const MYSQLI_OPT_SSL_VERIFY_SERVER_CERT; -use const MYSQLI_PART_KEY_FLAG; -use const MYSQLI_PRI_KEY_FLAG; use const MYSQLI_REPORT_OFF; -use const MYSQLI_SET_FLAG; use const MYSQLI_STORE_RESULT; -use const MYSQLI_TIMESTAMP_FLAG; -use const MYSQLI_TYPE_BIT; -use const MYSQLI_TYPE_BLOB; -use const MYSQLI_TYPE_DATE; -use const MYSQLI_TYPE_DATETIME; -use const MYSQLI_TYPE_DECIMAL; -use const MYSQLI_TYPE_DOUBLE; -use const MYSQLI_TYPE_ENUM; -use const MYSQLI_TYPE_FLOAT; -use const MYSQLI_TYPE_GEOMETRY; -use const MYSQLI_TYPE_INT24; -use const MYSQLI_TYPE_JSON; -use const MYSQLI_TYPE_LONG; -use const MYSQLI_TYPE_LONG_BLOB; -use const MYSQLI_TYPE_LONGLONG; -use const MYSQLI_TYPE_MEDIUM_BLOB; -use const MYSQLI_TYPE_NEWDATE; -use const MYSQLI_TYPE_NEWDECIMAL; -use const MYSQLI_TYPE_NULL; -use const MYSQLI_TYPE_SET; -use const MYSQLI_TYPE_SHORT; -use const MYSQLI_TYPE_STRING; -use const MYSQLI_TYPE_TIME; -use const MYSQLI_TYPE_TIMESTAMP; -use const MYSQLI_TYPE_TINY; -use const MYSQLI_TYPE_TINY_BLOB; -use const MYSQLI_TYPE_VAR_STRING; -use const MYSQLI_TYPE_YEAR; -use const MYSQLI_UNIQUE_KEY_FLAG; -use const MYSQLI_UNSIGNED_FLAG; use const MYSQLI_USE_RESULT; -use const MYSQLI_ZEROFILL_FLAG; -use function define; use function defined; -use function implode; use function is_array; use function is_bool; use function mysqli_init; @@ -80,23 +39,6 @@ use function trigger_error; */ class DbiMysqli implements DbiExtension { - /** @var array */ - private static $flagNames = [ - MYSQLI_NUM_FLAG => 'num', - MYSQLI_PART_KEY_FLAG => 'part_key', - MYSQLI_SET_FLAG => 'set', - MYSQLI_TIMESTAMP_FLAG => 'timestamp', - MYSQLI_AUTO_INCREMENT_FLAG => 'auto_increment', - MYSQLI_ENUM_FLAG => 'enum', - MYSQLI_ZEROFILL_FLAG => 'zerofill', - MYSQLI_UNSIGNED_FLAG => 'unsigned', - MYSQLI_BLOB_FLAG => 'blob', - MYSQLI_MULTIPLE_KEY_FLAG => 'multiple_key', - MYSQLI_UNIQUE_KEY_FLAG => 'unique_key', - MYSQLI_PRI_KEY_FLAG => 'primary_key', - MYSQLI_NOT_NULL_FLAG => 'not_null', - ]; - /** * connects to the database server * @@ -463,84 +405,20 @@ class DbiMysqli implements DbiExtension * * @param mysqli_result $result result set identifier * - * @return array|bool meta info for fields in $result + * @return FieldMetadata[]|null meta info for fields in $result */ - public function getFieldsMeta($result) + public function getFieldsMeta($result): ?array { if (! $result instanceof mysqli_result) { - return false; + return null; } - // Issue #16043 - client API mysqlnd seem not to have MYSQLI_TYPE_JSON defined - if (! defined('MYSQLI_TYPE_JSON')) { - define('MYSQLI_TYPE_JSON', 245); - } - // Build an associative array for a type look up - $typeAr = []; - $typeAr[MYSQLI_TYPE_DECIMAL] = 'real'; - $typeAr[MYSQLI_TYPE_NEWDECIMAL] = 'real'; - $typeAr[MYSQLI_TYPE_BIT] = 'int'; - $typeAr[MYSQLI_TYPE_TINY] = 'int'; - $typeAr[MYSQLI_TYPE_SHORT] = 'int'; - $typeAr[MYSQLI_TYPE_LONG] = 'int'; - $typeAr[MYSQLI_TYPE_FLOAT] = 'real'; - $typeAr[MYSQLI_TYPE_DOUBLE] = 'real'; - $typeAr[MYSQLI_TYPE_NULL] = 'null'; - $typeAr[MYSQLI_TYPE_TIMESTAMP] = 'timestamp'; - $typeAr[MYSQLI_TYPE_LONGLONG] = 'int'; - $typeAr[MYSQLI_TYPE_INT24] = 'int'; - $typeAr[MYSQLI_TYPE_DATE] = 'date'; - $typeAr[MYSQLI_TYPE_TIME] = 'time'; - $typeAr[MYSQLI_TYPE_DATETIME] = 'datetime'; - $typeAr[MYSQLI_TYPE_YEAR] = 'year'; - $typeAr[MYSQLI_TYPE_NEWDATE] = 'date'; - $typeAr[MYSQLI_TYPE_ENUM] = 'unknown'; - $typeAr[MYSQLI_TYPE_SET] = 'unknown'; - $typeAr[MYSQLI_TYPE_TINY_BLOB] = 'blob'; - $typeAr[MYSQLI_TYPE_MEDIUM_BLOB] = 'blob'; - $typeAr[MYSQLI_TYPE_LONG_BLOB] = 'blob'; - $typeAr[MYSQLI_TYPE_BLOB] = 'blob'; - $typeAr[MYSQLI_TYPE_VAR_STRING] = 'string'; - $typeAr[MYSQLI_TYPE_STRING] = 'string'; - // MySQL returns MYSQLI_TYPE_STRING for CHAR - // and MYSQLI_TYPE_CHAR === MYSQLI_TYPE_TINY - // so this would override TINYINT and mark all TINYINT as string - // see https://github.com/phpmyadmin/phpmyadmin/issues/8569 - //$typeAr[MYSQLI_TYPE_CHAR] = 'string'; - $typeAr[MYSQLI_TYPE_GEOMETRY] = 'geometry'; - $typeAr[MYSQLI_TYPE_BIT] = 'bit'; - $typeAr[MYSQLI_TYPE_JSON] = 'json'; - $fields = $result->fetch_fields(); - if (! is_array($fields)) { - return false; + return null; } foreach ($fields as $k => $field) { - $fields[$k]->_type = $field->type; - $fields[$k]->type = $typeAr[$field->type]; - $fields[$k]->_flags = $field->flags; - $fields[$k]->flags = $this->fieldFlags($result, $k); - - // Enhance the field objects for mysql-extension compatibility - //$flags = explode(' ', $fields[$k]->flags); - //array_unshift($flags, 'dummy'); - $fields[$k]->multiple_key - = (int) (bool) ($fields[$k]->_flags & MYSQLI_MULTIPLE_KEY_FLAG); - $fields[$k]->primary_key - = (int) (bool) ($fields[$k]->_flags & MYSQLI_PRI_KEY_FLAG); - $fields[$k]->unique_key - = (int) (bool) ($fields[$k]->_flags & MYSQLI_UNIQUE_KEY_FLAG); - $fields[$k]->not_null - = (int) (bool) ($fields[$k]->_flags & MYSQLI_NOT_NULL_FLAG); - $fields[$k]->unsigned - = (int) (bool) ($fields[$k]->_flags & MYSQLI_UNSIGNED_FLAG); - $fields[$k]->zerofill - = (int) (bool) ($fields[$k]->_flags & MYSQLI_ZEROFILL_FLAG); - $fields[$k]->numeric - = (int) (bool) ($fields[$k]->_flags & MYSQLI_NUM_FLAG); - $fields[$k]->blob - = (int) (bool) ($fields[$k]->_flags & MYSQLI_BLOB_FLAG); + $fields[$k] = new FieldMetadata($field->type, $field->flags, $field); } return $fields; @@ -602,53 +480,6 @@ class DbiMysqli implements DbiExtension return ''; } - /** - * returns concatenated string of human readable field flags - * - * @param mysqli_result $result result set identifier - * @param int $i field - * - * @return string|false field flags - */ - public function fieldFlags($result, $i) - { - if ($i >= $this->numFields($result)) { - return false; - } - /** @var stdClass|false $fieldDefinition */ - $fieldDefinition = $result->fetch_field_direct($i); - if ($fieldDefinition === false) { - return ''; - } - - $type = $fieldDefinition->type; - $charsetNumber = $fieldDefinition->charsetnr; - $fieldDefinitionFlags = $fieldDefinition->flags; - $flags = []; - foreach (self::$flagNames as $flag => $name) { - if (! ($fieldDefinitionFlags & $flag)) { - continue; - } - - $flags[] = $name; - } - // See https://dev.mysql.com/doc/refman/6.0/en/c-api-datatypes.html: - // to determine if a string is binary, we should not use MYSQLI_BINARY_FLAG - // but instead the charsetnr member of the MYSQL_FIELD - // structure. Watch out: some types like DATE returns 63 in charsetnr - // so we have to check also the type. - // Unfortunately there is no equivalent in the mysql extension. - if (($type == MYSQLI_TYPE_TINY_BLOB || $type == MYSQLI_TYPE_BLOB - || $type == MYSQLI_TYPE_MEDIUM_BLOB || $type == MYSQLI_TYPE_LONG_BLOB - || $type == MYSQLI_TYPE_VAR_STRING || $type == MYSQLI_TYPE_STRING) - && $charsetNumber == 63 - ) { - $flags[] = 'binary'; - } - - return implode(' ', $flags); - } - /** * returns properly escaped string for use in MySQL queries * diff --git a/libraries/classes/Display/Results.php b/libraries/classes/Display/Results.php index d3851ff875..1ff4d44f2a 100644 --- a/libraries/classes/Display/Results.php +++ b/libraries/classes/Display/Results.php @@ -7,6 +7,7 @@ namespace PhpMyAdmin\Display; use PhpMyAdmin\Config\SpecialSchemaLinks; use PhpMyAdmin\Core; use PhpMyAdmin\DatabaseInterface; +use PhpMyAdmin\FieldMetadata; use PhpMyAdmin\Html\Generator; use PhpMyAdmin\Index; use PhpMyAdmin\Message; @@ -27,8 +28,6 @@ use PhpMyAdmin\Theme; use PhpMyAdmin\Transformations; use PhpMyAdmin\Url; use PhpMyAdmin\Util; -use stdClass; -use const MYSQLI_TYPE_BIT; use function array_filter; use function array_keys; use function array_merge; @@ -93,15 +92,6 @@ class Results public const HEADER_FLIP_TYPE_CSS = 'css'; public const HEADER_FLIP_TYPE_FAKE = 'fake'; - public const DATE_FIELD = 'date'; - public const DATETIME_FIELD = 'datetime'; - public const TIMESTAMP_FIELD = 'timestamp'; - public const TIME_FIELD = 'time'; - public const STRING_FIELD = 'string'; - public const GEOMETRY_FIELD = 'geometry'; - public const BLOB_FIELD = 'BLOB'; - public const BINARY_FIELD = 'BINARY'; - public const RELATIONAL_KEY = 'K'; public const RELATIONAL_DISPLAY_COLUMN = 'D'; @@ -361,31 +351,31 @@ class Results /** * Set properties which were not initialized at the constructor * - * @param int $unlim_num_rows the total number of rows returned by - * the SQL query without any appended - * "LIMIT" clause programmatically - * @param stdClass $fields_meta meta information about fields - * @param bool $is_count statement is SELECT COUNT - * @param int $is_export statement contains INTO OUTFILE - * @param bool $is_func statement contains a function like SUM() - * @param int $is_analyse statement contains PROCEDURE ANALYSE - * @param int $num_rows total no. of rows returned by SQL query - * @param int $fields_cnt total no.of fields returned by SQL query - * @param double $querytime time taken for execute the SQL query - * @param string $text_dir text direction - * @param bool $is_maint statement contains a maintenance command - * @param bool $is_explain statement contains EXPLAIN - * @param bool $is_show statement contains SHOW - * @param array $showtable table definitions - * @param string $printview print view was requested - * @param bool $editable whether the results set is editable - * @param bool $is_browse_dist whether browsing distinct values + * @param int $unlim_num_rows the total number of rows returned by + * the SQL query without any appended + * "LIMIT" clause programmatically + * @param array $fields_meta meta information about fields + * @param bool $is_count statement is SELECT COUNT + * @param int $is_export statement contains INTO OUTFILE + * @param bool $is_func statement contains a function like SUM() + * @param int $is_analyse statement contains PROCEDURE ANALYSE + * @param int $num_rows total no. of rows returned by SQL query + * @param int $fields_cnt total no.of fields returned by SQL query + * @param double $querytime time taken for execute the SQL query + * @param string $text_dir text direction + * @param bool $is_maint statement contains a maintenance command + * @param bool $is_explain statement contains EXPLAIN + * @param bool $is_show statement contains SHOW + * @param array $showtable table definitions + * @param string $printview print view was requested + * @param bool $editable whether the results set is editable + * @param bool $is_browse_dist whether browsing distinct values * * @return void */ public function setProperties( $unlim_num_rows, - $fields_meta, + array $fields_meta, $is_count, $is_export, $is_func, @@ -537,6 +527,7 @@ class Results // $displayParts['edit_lnk'], $displayParts['del_lnk'] and // $displayParts['text_btn'] (keeps other default values) + /** @var FieldMetadata[] $fields_meta */ $fields_meta = $this->properties['fields_meta']; $prev_table = ''; $displayParts['text_btn'] = (string) '1'; @@ -1016,6 +1007,7 @@ class Results // Following variable are needed for use in isset/empty or // use with array indexes/safe use in the for loop $highlight_columns = $this->properties['highlight_columns']; + /** @var FieldMetadata[] $fields_meta */ $fields_meta = $this->properties['fields_meta']; // Prepare Display column comments if enabled @@ -1557,18 +1549,19 @@ class Results * * @see getTableHeaders() * - * @param array $commentsMap comments array - * @param array $fieldsMeta set of field properties + * @param array $commentsMap comments array + * @param FieldMetadata $fieldsMeta set of field properties * * @return string html content * * @access private */ - private function getCommentForRow(array $commentsMap, $fieldsMeta) + private function getCommentForRow(array $commentsMap, FieldMetadata $fieldsMeta) { return $this->template->render('display/results/comment_for_row', [ 'comments_map' => $commentsMap, - 'fields_meta' => $fieldsMeta, + 'column_name' => $fieldsMeta->name, + 'table_name' => $fieldsMeta->table, 'limit_chars' => $GLOBALS['cfg']['LimitChars'], ]); } @@ -1578,23 +1571,23 @@ class Results * * @see getTableHeaders() * - * @param stdClass $fields_meta set of field properties - * @param array $sort_expression sort expression - * @param array $sort_expression_nodirection sort expression without direction - * @param int $column_index the index of the column - * @param string $unsorted_sql_query the unsorted sql query - * @param int $session_max_rows maximum rows resulted by sql - * @param string $comments comment for row - * @param array $sort_direction sort direction - * @param bool $col_visib column is visible(false) or column isn't visible(string array) - * @param string $col_visib_j element of $col_visib array + * @param FieldMetadata $fields_meta set of field properties + * @param array $sort_expression sort expression + * @param array $sort_expression_nodirection sort expression without direction + * @param int $column_index the index of the column + * @param string $unsorted_sql_query the unsorted sql query + * @param int $session_max_rows maximum rows resulted by sql + * @param string $comments comment for row + * @param array $sort_direction sort direction + * @param bool $col_visib column is visible(false) or column isn't visible(string array) + * @param string $col_visib_j element of $col_visib array * * @return array 2 element array - $order_link, $sorted_header_html * * @access private */ private function getOrderLinkAndSortedHeaderHtml( - $fields_meta, + FieldMetadata $fields_meta, array $sort_expression, array $sort_expression_nodirection, $column_index, @@ -1696,14 +1689,14 @@ class Results * * @see getOrderLinkAndSortedHeaderHtml() * - * @param array $sort_expression sort expression - * @param array $sort_expression_nodirection sort expression without direction - * @param string $sort_tbl The name of the table to which - * the current column belongs to - * @param string $name_to_use_in_sort The current column under - * consideration - * @param array $sort_direction sort direction - * @param stdClass $fields_meta set of field properties + * @param array $sort_expression sort expression + * @param array $sort_expression_nodirection sort expression without direction + * @param string $sort_tbl The name of the table to which + * the current column belongs to + * @param string $name_to_use_in_sort The current column under + * consideration + * @param array $sort_direction sort direction + * @param FieldMetadata $fields_meta set of field properties * * @return array 3 element array - $single_sort_order, $sort_order, $order_img * @@ -1715,7 +1708,7 @@ class Results $sort_tbl, $name_to_use_in_sort, array $sort_direction, - $fields_meta + FieldMetadata $fields_meta ) { $sort_order = ''; // Check if the current column is in the order by clause @@ -1734,10 +1727,9 @@ class Results = Util::backquote( $current_name ); - $sort_direction[$special_index] = preg_match( - '@time|date@i', - $fields_meta->type ?? '' - ) ? self::DESCENDING_SORT_DIR : self::ASCENDING_SORT_DIR; + $isTimeOrDate = $fields_meta->isType(FieldMetadata::TYPE_TIME) + || $fields_meta->isType(FieldMetadata::TYPE_DATE); + $sort_direction[$special_index] = $isTimeOrDate ? self::DESCENDING_SORT_DIR : self::ASCENDING_SORT_DIR; } $sort_expression_nodirection = array_filter($sort_expression_nodirection); @@ -1979,10 +1971,10 @@ class Results * * @see getTableHeaders() * - * @param string $order_img the sort order image - * @param stdClass $fields_meta set of field properties - * @param string $order_url the url for sort - * @param string $multi_order_url the url for sort + * @param string $order_img the sort order image + * @param FieldMetadata $fields_meta set of field properties + * @param string $order_url the url for sort + * @param string $multi_order_url the url for sort * * @return string the sort order link * @@ -1990,7 +1982,7 @@ class Results */ private function getSortOrderLink( $order_img, - $fields_meta, + FieldMetadata $fields_meta, $order_url, $multi_order_url ) { @@ -2013,21 +2005,22 @@ class Results * * @see getDraggableClassForSortableColumns() * - * @param stdClass $fields_meta set of field properties - * @param array $th_class array containing classes + * @param FieldMetadata $fields_meta set of field properties + * @param array $th_class array containing classes * * @return void */ - private function getClassForNumericColumnType($fields_meta, array &$th_class) + private function getClassForNumericColumnType(FieldMetadata $fields_meta, array &$th_class) { - if (! preg_match( - '@int|decimal|float|double|real|bit|boolean|serial@i', - (string) $fields_meta->type - )) { + // This was defined in commit b661cd7c9b31f8bc564d2f9a1b8527e0eb966de8 + // For issue https://github.com/phpmyadmin/phpmyadmin/issues/4746 + if (! $fields_meta->isType(FieldMetadata::TYPE_REAL) + && ! $fields_meta->isMappedTypeBit + && ! $fields_meta->isType(FieldMetadata::TYPE_INT)) { return; } - $th_class[] = 'text-end'; + $th_class[] = 'text-end'; } /** @@ -2035,12 +2028,12 @@ class Results * * @see getTableHeaders() * - * @param bool $col_visib the column is visible (false) - * array the column is not visible (string array) - * @param string $col_visib_j element of $col_visib array - * @param stdClass $fields_meta set of field properties - * @param string $order_link the order link - * @param string $comments the comment for the column + * @param bool $col_visib the column is visible (false) + * array the column is not visible (string array) + * @param string $col_visib_j element of $col_visib array + * @param FieldMetadata $fields_meta set of field properties + * @param string $order_link the order link + * @param string $comments the comment for the column * * @return string html content * @@ -2049,7 +2042,7 @@ class Results private function getDraggableClassForSortableColumns( $col_visib, $col_visib_j, - $fields_meta, + FieldMetadata $fields_meta, $order_link, $comments ) { @@ -2084,12 +2077,12 @@ class Results * * @see getTableHeaders() * - * @param bool $col_visib the column is visible (false) - * array the column is not visible (string array) - * @param string $col_visib_j element of $col_visib array - * @param bool $condition_field whether to add CSS class condition - * @param stdClass $fields_meta set of field properties - * @param string $comments the comment for the column + * @param bool $col_visib the column is visible (false) + * array the column is not visible (string array) + * @param string $col_visib_j element of $col_visib array + * @param bool $condition_field whether to add CSS class condition + * @param FieldMetadata $fields_meta set of field properties + * @param string $comments the comment for the column * * @return string html content * @@ -2099,7 +2092,7 @@ class Results $col_visib, $col_visib_j, $condition_field, - $fields_meta, + FieldMetadata $fields_meta, $comments ) { $draggable_html = 'addClass($class, $conditionField, $meta, ''); return $this->template->render('display/results/null_display', [ 'align' => $align, - 'meta' => $meta, + 'data_decimals' => $meta->decimals ?? -1, + 'data_type' => $meta->getMappedType(), 'classes' => $classes, ]); } @@ -2243,16 +2237,16 @@ class Results * getDataCellForGeometryColumns(), * getDataCellForNonNumericColumns() * - * @param string $class class of table cell - * @param bool $conditionField whether to add CSS class condition - * @param stdClass $meta the meta-information about this field - * @param string $align cell alignment + * @param string $class class of table cell + * @param bool $conditionField whether to add CSS class condition + * @param FieldMetadata $meta the meta-information about this field + * @param string $align cell alignment * * @return string the td * * @access private */ - private function buildEmptyDisplay($class, $conditionField, $meta, $align = '') + private function buildEmptyDisplay($class, $conditionField, FieldMetadata $meta, $align = '') { $classes = $this->addClass($class, $conditionField, $meta, 'text-nowrap'); @@ -2270,7 +2264,7 @@ class Results * @param string $class class of table cell * @param bool $condition_field whether to add CSS class * condition - * @param stdClass $meta the meta-information about the + * @param FieldMetadata $meta the meta-information about the * field * @param string $nowrap avoid wrapping * @param bool $is_field_truncated is field truncated (display ...) @@ -2286,7 +2280,7 @@ class Results private function addClass( $class, $condition_field, - $meta, + FieldMetadata $meta, $nowrap, $is_field_truncated = false, $transformation_plugin = '', @@ -2297,8 +2291,8 @@ class Results $nowrap, ]; - if (isset($meta->mimetype)) { - $classes[] = preg_replace('/\//', '_', $meta->mimetype); + if (isset($meta->internalMediaType)) { + $classes[] = preg_replace('/\//', '_', $meta->internalMediaType); } if ($condition_field) { @@ -2319,24 +2313,23 @@ class Results } // Define classes to be added to this data field based on the type of data - $matches = [ - 'enum' => 'enum', - 'set' => 'set', - 'binary' => 'hex', - ]; - foreach ($matches as $key => $value) { - if (mb_strpos($meta->flags, $key) === false) { - continue; - } - - $classes[] = $value; + if ($meta->isEnum()) { + $classes[] = 'enum'; } - if (mb_strpos($meta->type, 'bit') !== false) { + if ($meta->isSet()) { + $classes[] = 'set'; + } + + if ($meta->isMappedTypeBit) { $classes[] = 'bit'; } + if ($meta->isBinary()) { + $classes[] = 'hex'; + } + return implode(' ', $classes); } @@ -2607,6 +2600,7 @@ class Results */ private function setMimeMap() { + /** @var FieldMetadata[] $fields_meta */ $fields_meta = $this->properties['fields_meta']; $mimeMap = []; $added = []; @@ -2710,6 +2704,7 @@ class Results // Following variable are needed for use in isset/empty or // use with array indexes/safe use in foreach $sql_query = $this->properties['sql_query']; + /** @var FieldMetadata[] $fields_meta */ $fields_meta = $this->properties['fields_meta']; $highlight_columns = $this->properties['highlight_columns']; $mime_map = $this->properties['mime_map']; @@ -2731,7 +2726,7 @@ class Results $orgFullColName = $this->properties['db'] . '.' . $meta->orgtable . '.' . $meta->orgname; - $not_null_class = $meta->not_null ? 'not_null' : ''; + $not_null_class = $meta->isNotNull() ? 'not_null' : ''; $relation_class = isset($map[$meta->name]) ? 'relation' : ''; $hide_class = is_array($col_visib) && isset($col_visib[$currentColumn]) && ! $col_visib[$currentColumn] ? 'hide' @@ -2740,7 +2735,7 @@ class Results // handle datetime-related class, for grid editing $field_type_class - = $this->getClassForDateTimeRelatedFields($meta->type); + = $this->getClassForDateTimeRelatedFields($meta); $is_field_truncated = false; // combine all the classes applicable to this column's value @@ -2788,7 +2783,7 @@ class Results $mime_map[$orgFullColName]['transformation_options'] ?? '' ); - $meta->mimetype = str_replace( + $meta->internalMediaType = str_replace( '_', '/', $mime_map[$orgFullColName]['mimetype'] @@ -2818,7 +2813,7 @@ class Results $orgTable = mb_strtolower($meta->orgtable); $orgName = mb_strtolower($meta->orgname); - $meta->mimetype = str_replace( + $meta->internalMediaType = str_replace( '_', '/', $this->transformationInfo[$dbLower][$orgTable][$orgName][2] @@ -2840,7 +2835,7 @@ class Results 2 => true, ]; - $meta->mimetype = str_replace( + $meta->internalMediaType = str_replace( '_', '/', 'Text/Plain' @@ -2896,7 +2891,8 @@ class Results // even for a string type // for decimal numeric is returning 1 // have to improve logic - if (($meta->numeric == 1 && $meta->type !== 'string') || $meta->type === 'real') { + if (($meta->isNumeric && $meta->isNotType(FieldMetadata::TYPE_STRING)) + || $meta->isType(FieldMetadata::TYPE_REAL)) { // n u m e r i c $display_params['data'][$row_no][$i] @@ -2912,7 +2908,7 @@ class Results $default_function, $transform_options ); - } elseif ($meta->type === self::GEOMETRY_FIELD) { + } elseif ($meta->isMappedTypeGeometry) { // g e o m e t r y // Remove 'grid_edit' from $class as we do not allow to @@ -2984,7 +2980,7 @@ class Results array $specialSchemaLinks, $column_value, array $row_info, - $field_name + string $field_name ) { $linking_url_params = []; $db = mb_strtolower($this->properties['db']); @@ -3042,6 +3038,7 @@ class Results private function getRowInfoForSpecialLinks(array $row, $col_order) { $row_info = []; + /** @var FieldMetadata[] $fields_meta */ $fields_meta = $this->properties['fields_meta']; for ($n = 0; $n < $this->properties['fields_cnt']; ++$n) { @@ -3361,25 +3358,25 @@ class Results * * @see getTableBody() * - * @param string $grid_edit_class the class for all editable columns - * @param string $not_null_class the class for not null columns - * @param string $relation_class the class for relations in a column - * @param string $hide_class the class for visibility of a column - * @param string $field_type_class the class related to type of the field + * @param string $gridEditClass the class for all editable columns + * @param string $notNullClass the class for not null columns + * @param string $relationClass the class for relations in a column + * @param string $hideClass the class for visibility of a column + * @param string $fieldTypeClass the class related to type of the field * * @return string the combined classes * * @access private */ private function getClassesForColumn( - $grid_edit_class, - $not_null_class, - $relation_class, - $hide_class, - $field_type_class + string $gridEditClass, + string $notNullClass, + string $relationClass, + string $hideClass, + string $fieldTypeClass ) { - return 'data ' . $grid_edit_class . ' ' . $not_null_class . ' ' - . $relation_class . ' ' . $hide_class . ' ' . $field_type_class; + return 'data ' . $gridEditClass . ' ' . $notNullClass . ' ' + . $relationClass . ' ' . $hideClass . ' ' . $fieldTypeClass; } /** @@ -3387,29 +3384,29 @@ class Results * * @see getTableBody() * - * @param string $type the type of the column field + * @param FieldMetadata $meta the type of the column field * * @return string the class for the column * * @access private */ - private function getClassForDateTimeRelatedFields($type) + private function getClassForDateTimeRelatedFields(FieldMetadata $meta): string { - if ((substr($type, 0, 9) === self::TIMESTAMP_FIELD) - || ($type === self::DATETIME_FIELD) + $fieldTypeClass = ''; + + if ($meta->isMappedTypeTimestamp + || $meta->isType(FieldMetadata::TYPE_DATETIME) ) { - $field_type_class = 'datetimefield'; - } elseif ($type === self::DATE_FIELD) { - $field_type_class = 'datefield'; - } elseif ($type === self::TIME_FIELD) { - $field_type_class = 'timefield'; - } elseif ($type === self::STRING_FIELD) { - $field_type_class = 'text'; - } else { - $field_type_class = ''; + $fieldTypeClass = 'datetimefield'; + } elseif ($meta->isType(FieldMetadata::TYPE_DATE)) { + $fieldTypeClass = 'datefield'; + } elseif ($meta->isType(FieldMetadata::TYPE_TIME)) { + $fieldTypeClass = 'timefield'; + } elseif ($meta->isType(FieldMetadata::TYPE_STRING)) { + $fieldTypeClass = 'text'; } - return $field_type_class; + return $fieldTypeClass; } /** @@ -3421,8 +3418,8 @@ class Results * @param string $class the html class for column * @param bool $condition_field the column should highlighted * or not - * @param stdClass $meta the meta-information about this - * field + * @param FieldMetadata $meta the meta-information about this + * field * @param array $map the list of relations * @param bool $is_field_truncated the condition for blob data * replacements @@ -3440,7 +3437,7 @@ class Results ?string $column, $class, $condition_field, - $meta, + FieldMetadata $meta, array $map, $is_field_truncated, array $analyzed_sql_results, @@ -3494,8 +3491,8 @@ class Results * * @param string|null $column the relevant column in data row * @param string $class the html class for column - * @param stdClass $meta the meta-information about - * this field + * @param FieldMetadata $meta the meta-information about + * this field * @param array $map the list of relations * @param array $_url_params the parameters for generate url * @param bool $condition_field the column should highlighted @@ -3514,7 +3511,7 @@ class Results private function getDataCellForGeometryColumns( ?string $column, $class, - $meta, + FieldMetadata $meta, array $map, array $_url_params, $condition_field, @@ -3534,7 +3531,7 @@ class Results // Display as [GEOMETRY - (size)] if ($_SESSION['tmpval']['geoOption'] === self::GEOMETRY_DISP_GEOM) { $geometry_text = $this->handleNonPrintableContents( - strtoupper(self::GEOMETRY_FIELD), + 'GEOMETRY', $column, $transformation_plugin, $transform_options, @@ -3611,7 +3608,7 @@ class Results } $wkbval = $this->handleNonPrintableContents( - self::BINARY_FIELD, + 'BINARY', $column, $transformation_plugin, $transform_options, @@ -3634,8 +3631,8 @@ class Results * * @param string|null $column the relevant column in data row * @param string $class the html class for column - * @param stdClass $meta the meta-information about - * the field + * @param FieldMetadata $meta the meta-information about + * the field * @param array $map the list of relations * @param array $_url_params the parameters for generate * url @@ -3661,7 +3658,7 @@ class Results private function getDataCellForNonNumericColumns( ?string $column, $class, - $meta, + FieldMetadata $meta, array $map, array $_url_params, $condition_field, @@ -3678,7 +3675,6 @@ class Results $original_length = 0; $is_analyse = $this->properties['is_analyse']; - $field_flags = $dbi->fieldFlags($dt_result, $col_index); $bIsText = is_object($transformation_plugin) && strpos($transformation_plugin->getMIMEType(), 'Text') @@ -3688,13 +3684,15 @@ class Results // if binary fields are protected // or transformation plugin is of non text type // such as image - if ((stripos($field_flags, self::BINARY_FIELD) !== false - && ($GLOBALS['cfg']['ProtectBinary'] === 'all' - || ($GLOBALS['cfg']['ProtectBinary'] === 'noblob' - && stripos($meta->type, self::BLOB_FIELD) === false) - || ($GLOBALS['cfg']['ProtectBinary'] === 'blob' - && stripos($meta->type, self::BLOB_FIELD) !== false))) - || $bIsText + $isTypeBlob = $meta->isType(FieldMetadata::TYPE_BLOB); + $cfgProtectBinary = $GLOBALS['cfg']['ProtectBinary']; + if (($meta->isBinary() + && ( + $cfgProtectBinary === 'all' + || ($cfgProtectBinary === 'noblob' && ! $isTypeBlob) + || ($cfgProtectBinary === 'blob' && $isTypeBlob) + ) + ) || $bIsText ) { $class = str_replace('grid_edit', '', $class); } @@ -3712,7 +3710,7 @@ class Results $displayedColumn = $column; if (! (is_object($transformation_plugin) && strpos($transformation_plugin->getName(), 'Link') !== false) - && stripos($field_flags, self::BINARY_FIELD) === false + && ! $meta->isBinary() ) { [ $is_field_truncated, @@ -3722,7 +3720,7 @@ class Results } $formatted = false; - if (isset($meta->_type) && $meta->_type === MYSQLI_TYPE_BIT) { + if ($meta->isMappedTypeBit) { $displayedColumn = Util::printableBitValue( (int) $displayedColumn, (int) $meta->length @@ -3731,14 +3729,14 @@ class Results // some results of PROCEDURE ANALYSE() are reported as // being BINARY but they are quite readable, // so don't treat them as BINARY - } elseif (stripos($field_flags, self::BINARY_FIELD) !== false + } elseif ($meta->isBinary() && ! (isset($is_analyse) && $is_analyse) ) { // we show the BINARY or BLOB message and field's size // (or maybe use a transformation) - $binary_or_blob = self::BLOB_FIELD; - if ($meta->type === self::STRING_FIELD) { - $binary_or_blob = self::BINARY_FIELD; + $binary_or_blob = 'BLOB'; + if ($meta->isType(FieldMetadata::TYPE_STRING)) { + $binary_or_blob = 'BINARY'; } $displayedColumn = $this->handleNonPrintableContents( $binary_or_blob, @@ -3786,8 +3784,7 @@ class Results // do not wrap if date field type or if no-wrapping enabled by transform functions // otherwise, preserve whitespaces and wrap - $nowrap = preg_match('@DATE|TIME@i', $meta->type) - || $bool_nowrap ? 'text-nowrap' : 'pre_wrap'; + $nowrap = $meta->isDateTimeType() || $bool_nowrap ? 'text-nowrap' : 'pre_wrap'; $where_comparison = ' = \'' . $dbi->escapeString($column) @@ -3999,6 +3996,7 @@ class Results // Following variable are needed for use in isset/empty or // use with array indexes/safe use in foreach + /** @var FieldMetadata[] $fields_meta */ $fields_meta = $this->properties['fields_meta']; $showtable = $this->properties['showtable']; $printview = $this->properties['printview']; @@ -4263,6 +4261,7 @@ class Results ) { global $dbi; + /** @var FieldMetadata[] $fields_meta */ $fields_meta = $this->properties['fields_meta']; // To use array indexes if (empty($sort_expression_nodirection)) { @@ -4309,11 +4308,12 @@ class Results // check for non printable sorted row data $meta = $fields_meta[$sorted_column_index]; - if (stripos($meta->type, self::BLOB_FIELD) !== false - || ($meta->type === self::GEOMETRY_FIELD) - ) { + $isBlobOrGeometry = $meta->isType(FieldMetadata::TYPE_BLOB) + || $meta->isMappedTypeGeometry; + + if ($isBlobOrGeometry) { $column_for_first_row = $this->handleNonPrintableContents( - $meta->type, + $meta->getMappedType(), $row[$sorted_column_index], $transformation_plugin, $transform_options, @@ -4341,11 +4341,9 @@ class Results // check for non printable sorted row data $meta = $fields_meta[$sorted_column_index]; - if (stripos($meta->type, self::BLOB_FIELD) !== false - || ($meta->type === self::GEOMETRY_FIELD) - ) { + if ($isBlobOrGeometry) { $column_for_last_row = $this->handleNonPrintableContents( - $meta->type, + $meta->getMappedType(), $row[$sorted_column_index], $transformation_plugin, $transform_options, @@ -4671,9 +4669,10 @@ class Results } } + /** @var FieldMetadata[] $fields_meta */ $fields_meta = $this->properties['fields_meta']; foreach ($fields_meta as $meta) { - if ($meta->type === self::GEOMETRY_FIELD) { + if ($meta->isMappedTypeGeometry) { $geometry_found = true; break; } @@ -4695,18 +4694,18 @@ class Results * * @see getDataCellForGeometryColumns(), getDataCellForNonNumericColumns(), getSortedColumnMessage() * - * @param string $category BLOB|BINARY|GEOMETRY - * @param string|null $content the binary content - * @param mixed $transformation_plugin transformation plugin. - * Can also be the - * default function: - * Core::mimeDefaultFunction - * @param string $transform_options transformation parameters - * @param string $default_function default transformation function - * @param stdClass $meta the meta-information about the field - * @param array $url_params parameters that should go to the - * download link - * @param bool $is_truncated the result is truncated or not + * @param string $category BLOB|BINARY|GEOMETRY + * @param string|null $content the binary content + * @param mixed $transformation_plugin transformation plugin. + * Can also be the + * default function: + * Core::mimeDefaultFunction + * @param string $transform_options transformation parameters + * @param string $default_function default transformation function + * @param FieldMetadata $meta the meta-information about the field + * @param array $url_params parameters that should go to the + * download link + * @param bool $is_truncated the result is truncated or not * * @return mixed string or float * @@ -4718,7 +4717,7 @@ class Results $transformation_plugin, $transform_options, $default_function, - $meta, + FieldMetadata $meta, array $url_params = [], &$is_truncated = null ) { @@ -4769,9 +4768,9 @@ class Results $result = $default_function($result, [], $meta); if (($_SESSION['tmpval']['display_binary'] - && $meta->type === self::STRING_FIELD) + && $meta->isType(FieldMetadata::TYPE_STRING)) || ($_SESSION['tmpval']['display_blob'] - && stripos($meta->type, self::BLOB_FIELD) !== false) + && $meta->isType(FieldMetadata::TYPE_BLOB)) ) { // in this case, restart from the original $content if (mb_check_encoding($content, 'utf-8') @@ -4809,15 +4808,15 @@ class Results /** * Retrieves the associated foreign key info for a data cell * - * @param array $map the list of relations - * @param stdClass $meta the meta-information about the field - * @param string $where_comparison data for the where clause + * @param array $map the list of relations + * @param FieldMetadata $meta the meta-information about the field + * @param string $where_comparison data for the where clause * * @return string|null formatted data * * @access private */ - private function getFromForeign(array $map, $meta, $where_comparison) + private function getFromForeign(array $map, FieldMetadata $meta, $where_comparison) { global $dbi; @@ -4859,8 +4858,8 @@ class Results * @param bool $condition_field whether the column is a part of * the where clause * @param array $analyzed_sql_results the analyzed query - * @param stdClass $meta the meta-information about the - * field + * @param FieldMetadata $meta the meta-information about the + * field * @param array $map the list of relations * @param string $data data * @param string $displayedData data that will be displayed (maybe be chunked) @@ -4883,7 +4882,7 @@ class Results $class, $condition_field, array $analyzed_sql_results, - $meta, + FieldMetadata $meta, array $map, $data, $displayedData, @@ -4899,7 +4898,7 @@ class Results $printview = $this->properties['printview']; $decimals = $meta->decimals ?? '-1'; $result = '
'; $output .= htmlspecialchars($field->name); $output .= 'isMultipleKey + = (bool) ($fieldFlags & MYSQLI_MULTIPLE_KEY_FLAG); + $this->isPrimaryKey + = (bool) ($fieldFlags & MYSQLI_PRI_KEY_FLAG); + $this->isUniqueKey + = (bool) ($fieldFlags & MYSQLI_UNIQUE_KEY_FLAG); + $this->isNotNull + = (bool) ($fieldFlags & MYSQLI_NOT_NULL_FLAG); + $this->isUnsigned + = (bool) ($fieldFlags & MYSQLI_UNSIGNED_FLAG); + $this->isZerofill + = (bool) ($fieldFlags & MYSQLI_ZEROFILL_FLAG); + $this->isNumeric + = (bool) ($fieldFlags & MYSQLI_NUM_FLAG); + $this->isBlob + = (bool) ($fieldFlags & MYSQLI_BLOB_FLAG); + $this->isEnum + = (bool) ($fieldFlags & MYSQLI_ENUM_FLAG); + $this->isSet + = (bool) ($fieldFlags & MYSQLI_SET_FLAG); + + /* + MYSQLI_PART_KEY_FLAG => 'part_key', + MYSQLI_TIMESTAMP_FLAG => 'timestamp', + MYSQLI_AUTO_INCREMENT_FLAG => 'auto_increment', + */ + + $this->mappedType = $this->getTypeMap()[$fieldType] ?? null; + + $this->isMappedTypeBit = $this->isType(self::TYPE_BIT); + $this->isMappedTypeGeometry = $this->isType(self::TYPE_GEOMETRY); + $this->isMappedTypeTimestamp = $this->isType(self::TYPE_TIMESTAMP); + + $this->name = property_exists($field, 'name') ? $field->name : ''; + $this->orgname = property_exists($field, 'orgname') ? $field->orgname : ''; + $this->table = property_exists($field, 'table') ? $field->table : ''; + $this->orgtable = property_exists($field, 'orgtable') ? $field->orgtable : ''; + $this->charsetnr = property_exists($field, 'charsetnr') ? $field->charsetnr : -1; + $this->decimals = property_exists($field, 'decimals') ? $field->decimals : 0; + $this->length = property_exists($field, 'length') ? $field->length : 0; + + // 63 is the number for the MySQL charset "binary" + $this->isBinary = ( + $fieldType === MYSQLI_TYPE_TINY_BLOB || $fieldType === MYSQLI_TYPE_BLOB + || $fieldType === MYSQLI_TYPE_MEDIUM_BLOB || $fieldType === MYSQLI_TYPE_LONG_BLOB + || $fieldType === MYSQLI_TYPE_VAR_STRING || $fieldType === MYSQLI_TYPE_STRING + ) && $this->charsetnr == 63; + } + + /** + * @see https://dev.mysql.com/doc/connectors/en/apis-php-mysqli.constants.html + */ + private function getTypeMap(): array + { + // Issue #16043 - client API mysqlnd seem not to have MYSQLI_TYPE_JSON defined + if (! defined('MYSQLI_TYPE_JSON')) { + define('MYSQLI_TYPE_JSON', 245); + } + // Build an associative array for a type look up + $typeAr = []; + $typeAr[MYSQLI_TYPE_DECIMAL] = self::TYPE_REAL; + $typeAr[MYSQLI_TYPE_NEWDECIMAL] = self::TYPE_REAL; + $typeAr[MYSQLI_TYPE_BIT] = self::TYPE_INT; + $typeAr[MYSQLI_TYPE_TINY] = self::TYPE_INT; + $typeAr[MYSQLI_TYPE_SHORT] = self::TYPE_INT; + $typeAr[MYSQLI_TYPE_LONG] = self::TYPE_INT; + $typeAr[MYSQLI_TYPE_FLOAT] = self::TYPE_REAL; + $typeAr[MYSQLI_TYPE_DOUBLE] = self::TYPE_REAL; + $typeAr[MYSQLI_TYPE_NULL] = self::TYPE_NULL; + $typeAr[MYSQLI_TYPE_TIMESTAMP] = self::TYPE_TIMESTAMP; + $typeAr[MYSQLI_TYPE_LONGLONG] = self::TYPE_INT; + $typeAr[MYSQLI_TYPE_INT24] = self::TYPE_INT; + $typeAr[MYSQLI_TYPE_DATE] = self::TYPE_DATE; + $typeAr[MYSQLI_TYPE_TIME] = self::TYPE_TIME; + $typeAr[MYSQLI_TYPE_DATETIME] = self::TYPE_DATETIME; + $typeAr[MYSQLI_TYPE_YEAR] = self::TYPE_YEAR; + $typeAr[MYSQLI_TYPE_NEWDATE] = self::TYPE_DATE; + $typeAr[MYSQLI_TYPE_ENUM] = self::TYPE_UNKNOWN; + $typeAr[MYSQLI_TYPE_SET] = self::TYPE_UNKNOWN; + $typeAr[MYSQLI_TYPE_TINY_BLOB] = self::TYPE_BLOB; + $typeAr[MYSQLI_TYPE_MEDIUM_BLOB] = self::TYPE_BLOB; + $typeAr[MYSQLI_TYPE_LONG_BLOB] = self::TYPE_BLOB; + $typeAr[MYSQLI_TYPE_BLOB] = self::TYPE_BLOB; + $typeAr[MYSQLI_TYPE_VAR_STRING] = self::TYPE_STRING; + $typeAr[MYSQLI_TYPE_STRING] = self::TYPE_STRING; + // MySQL returns MYSQLI_TYPE_STRING for CHAR + // and MYSQLI_TYPE_CHAR === MYSQLI_TYPE_TINY + // so this would override TINYINT and mark all TINYINT as string + // see https://github.com/phpmyadmin/phpmyadmin/issues/8569 + //$typeAr[MYSQLI_TYPE_CHAR] = self::TYPE_STRING; + $typeAr[MYSQLI_TYPE_GEOMETRY] = self::TYPE_GEOMETRY; + $typeAr[MYSQLI_TYPE_BIT] = self::TYPE_BIT; + $typeAr[MYSQLI_TYPE_JSON] = self::TYPE_JSON; + + return $typeAr; + } + + public function isNotNull(): bool + { + return $this->isNotNull; + } + + public function isNumeric(): bool + { + return $this->isNumeric; + } + + public function isBinary(): bool + { + return $this->isBinary; + } + + public function isBlob(): bool + { + return $this->isBlob; + } + + public function isPrimaryKey(): bool + { + return $this->isPrimaryKey; + } + + public function isUniqueKey(): bool + { + return $this->isUniqueKey; + } + + public function isMultipleKey(): bool + { + return $this->isMultipleKey; + } + + public function isUnsigned(): bool + { + return $this->isUnsigned; + } + + public function isZerofill(): bool + { + return $this->isZerofill; + } + + public function isEnum(): bool + { + return $this->isEnum; + } + + public function isSet(): bool + { + return $this->isSet; + } + + /** + * Checks that it is type INT or type REAL + */ + public function isNumericType(): bool + { + return $this->isType(self::TYPE_INT) || $this->isType(self::TYPE_REAL); + } + + /** + * Checks that it is type DATE/TIME/DATETIME + */ + public function isDateTimeType(): bool + { + return $this->isType(self::TYPE_DATE) + || $this->isType(self::TYPE_TIME) + || $this->isType(self::TYPE_DATETIME); + } + + /** + * Checks that it contains time + * A "DATE" field returns false for example + */ + public function isTimeType(): bool + { + return $this->isType(self::TYPE_TIME) + || $this->isType(self::TYPE_TIMESTAMP) + || $this->isType(self::TYPE_DATETIME); + } + + /** + * Get the mapped type as a string + * + * @return string Empty when nothing could be matched + */ + public function getMappedType(): string + { + $types = [ + self::TYPE_GEOMETRY => 'geometry', + self::TYPE_BIT => 'bit', + self::TYPE_JSON => 'json', + self::TYPE_REAL => 'real', + self::TYPE_INT => 'int', + self::TYPE_BLOB => 'blob', + self::TYPE_UNKNOWN => 'unknown', + self::TYPE_NULL => 'null', + self::TYPE_STRING => 'string', + self::TYPE_DATE => 'date', + self::TYPE_TIME => 'time', + self::TYPE_TIMESTAMP => 'timestamp', + self::TYPE_DATETIME => 'datetime', + self::TYPE_YEAR => 'year', + ]; + + return $types[$this->mappedType] ?? ''; + } + + /** + * Check if it is the mapped type + * + * @phpstan-param self::TYPE_* $type + */ + public function isType(int $type): bool + { + return $this->mappedType === $type; + } + + /** + * Check if it is NOT the mapped type + * + * @phpstan-param self::TYPE_* $type + */ + public function isNotType(int $type): bool + { + return $this->mappedType !== $type; + } +} diff --git a/libraries/classes/InsertEdit.php b/libraries/classes/InsertEdit.php index ddb6e6d200..a9e63ec667 100644 --- a/libraries/classes/InsertEdit.php +++ b/libraries/classes/InsertEdit.php @@ -231,7 +231,7 @@ class InsertEdit * exit if we want the message to be displayed */ } else {// end if (no row returned) - $meta = $this->dbi->getFieldsMeta($result[$key_id]); + $meta = $this->dbi->getFieldsMeta($result[$key_id]) ?? []; [$unique_condition, $tmp_clause_is_unique] = Util::getUniqueCondition( $result[$key_id], @@ -2265,7 +2265,7 @@ class InsertEdit $res = $this->dbi->query($local_query); $row = $this->dbi->fetchRow($res); - $meta = $this->dbi->getFieldsMeta($res); + $meta = $this->dbi->getFieldsMeta($res) ?? []; // must find a unique condition based on unique key, // not a combination of all fields [$unique_condition, $clause_is_unique] = Util::getUniqueCondition( @@ -2954,18 +2954,16 @@ class InsertEdit . ' WHERE ' . $_POST['where_clause'][0]; $result = $this->dbi->tryQuery($sql_for_real_value); - $fields_meta = $this->dbi->getFieldsMeta($result); + $fields_meta = $this->dbi->getFieldsMeta($result) ?? []; + /** @var FieldMetadata $meta */ $meta = $fields_meta[0]; $row = $this->dbi->fetchRow($result); if ($row) { $new_value = $row[0]; - if ((substr($meta->type, 0, 9) === 'timestamp') - || ($meta->type === 'datetime') - || ($meta->type === 'time') - ) { + if ($meta->isTimeType()) { $new_value = Util::addMicroseconds($new_value); - } elseif (mb_strpos($meta->flags, 'binary') !== false) { + } elseif ($meta->isBinary()) { $new_value = '0x' . bin2hex($new_value); } $extra_data['isNeedToRecheck'] = true; diff --git a/libraries/classes/Plugins/Export/ExportJson.php b/libraries/classes/Plugins/Export/ExportJson.php index 8dec8a0eb9..5f5efea00a 100644 --- a/libraries/classes/Plugins/Export/ExportJson.php +++ b/libraries/classes/Plugins/Export/ExportJson.php @@ -8,6 +8,7 @@ declare(strict_types=1); namespace PhpMyAdmin\Plugins\Export; use PhpMyAdmin\DatabaseInterface; +use PhpMyAdmin\FieldMetadata; use PhpMyAdmin\Plugins\ExportPlugin; use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyMainGroup; use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyRootGroup; @@ -20,7 +21,6 @@ use function bin2hex; use function explode; use function json_encode; use function stripslashes; -use function strpos; /** * Handles the export for the JSON format @@ -210,6 +210,7 @@ class ExportJson extends ExportPlugin $sql_query, array $aliases = [] ) { + /** @var DatabaseInterface $dbi */ global $dbi; $db_alias = $db; @@ -244,7 +245,7 @@ class ExportJson extends ExportPlugin DatabaseInterface::QUERY_UNBUFFERED ); $columns_cnt = $dbi->numFields($result); - $fieldsMeta = $dbi->getFieldsMeta($result); + $fieldsMeta = $dbi->getFieldsMeta($result) ?? []; $columns = []; for ($i = 0; $i < $columns_cnt; $i++) { @@ -269,7 +270,8 @@ class ExportJson extends ExportPlugin $data = []; for ($i = 0; $i < $columns_cnt; $i++) { - if ($fieldsMeta[$i]->type === 'geometry' || strpos($fieldsMeta[$i]->type, 'blob') !== false) { + if ($fieldsMeta[$i]->isMappedTypeGeometry + || $fieldsMeta[$i]->isType(FieldMetadata::TYPE_BLOB)) { // export GIS and blob types as hex $record[$i] = '0x' . bin2hex($record[$i]); } diff --git a/libraries/classes/Plugins/Export/ExportOds.php b/libraries/classes/Plugins/Export/ExportOds.php index f5d461ac57..91dcc9a2d6 100644 --- a/libraries/classes/Plugins/Export/ExportOds.php +++ b/libraries/classes/Plugins/Export/ExportOds.php @@ -8,6 +8,7 @@ declare(strict_types=1); namespace PhpMyAdmin\Plugins\Export; use PhpMyAdmin\DatabaseInterface; +use PhpMyAdmin\FieldMetadata; use PhpMyAdmin\OpenDocument; use PhpMyAdmin\Plugins\ExportPlugin; use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyMainGroup; @@ -19,7 +20,6 @@ use PhpMyAdmin\Properties\Plugins\ExportPluginProperties; use function bin2hex; use function date; use function htmlspecialchars; -use function stripos; use function stripslashes; use function strtotime; @@ -215,6 +215,7 @@ class ExportOds extends ExportPlugin $sql_query, array $aliases = [] ) { + /** @var DatabaseInterface $dbi */ global $what, $dbi; $db_alias = $db; @@ -227,11 +228,8 @@ class ExportOds extends ExportPlugin DatabaseInterface::QUERY_UNBUFFERED ); $fields_cnt = $dbi->numFields($result); + /** @var FieldMetadata[] $fields_meta */ $fields_meta = $dbi->getFieldsMeta($result); - $field_flags = []; - for ($j = 0; $j < $fields_cnt; $j++) { - $field_flags[$j] = $dbi->fieldFlags($result, $j); - } $GLOBALS['ods_buffer'] .= ''; @@ -260,7 +258,7 @@ class ExportOds extends ExportPlugin while ($row = $dbi->fetchRow($result)) { $GLOBALS['ods_buffer'] .= ''; for ($j = 0; $j < $fields_cnt; $j++) { - if ($fields_meta[$j]->type === 'geometry') { + if ($fields_meta[$j]->isMappedTypeGeometry) { // export GIS types as hex $row[$j] = '0x' . bin2hex($row[$j]); } @@ -271,15 +269,15 @@ class ExportOds extends ExportPlugin . htmlspecialchars($GLOBALS[$what . '_null']) . '' . ''; - } elseif (stripos($field_flags[$j], 'BINARY') !== false - && $fields_meta[$j]->blob + } elseif ($fields_meta[$j]->isBinary + && $fields_meta[$j]->isBlob ) { // ignore BLOB $GLOBALS['ods_buffer'] .= '' . '' . ''; - } elseif ($fields_meta[$j]->type === 'date') { + } elseif ($fields_meta[$j]->isType(FieldMetadata::TYPE_DATE)) { $GLOBALS['ods_buffer'] .= 'numFields($result); + /** @var FieldMetadata[] $fields_meta */ $fields_meta = $dbi->getFieldsMeta($result); - $field_flags = []; - for ($j = 0; $j < $fields_cnt; $j++) { - $field_flags[$j] = $dbi->fieldFlags($result, $j); - } $GLOBALS['odt_buffer'] .= 'fetchRow($result)) { $GLOBALS['odt_buffer'] .= ''; for ($j = 0; $j < $fields_cnt; $j++) { - if ($fields_meta[$j]->type === 'geometry') { + if ($fields_meta[$j]->isMappedTypeGeometry) { // export GIS types as hex $row[$j] = '0x' . bin2hex($row[$j]); } @@ -307,17 +305,17 @@ class ExportOdt extends ExportPlugin . htmlspecialchars($GLOBALS[$what . '_null']) . '' . ''; - } elseif (stripos($field_flags[$j], 'BINARY') !== false - && $fields_meta[$j]->blob + } elseif ($fields_meta[$j]->isBinary + && $fields_meta[$j]->isBlob ) { // ignore BLOB $GLOBALS['odt_buffer'] .= '' . '' . ''; - } elseif ($fields_meta[$j]->numeric - && $fields_meta[$j]->type !== 'timestamp' - && ! $fields_meta[$j]->blob + } elseif ($fields_meta[$j]->isNumeric + && ! $fields_meta[$j]->isMappedTypeTimestamp + && ! $fields_meta[$j]->isBlob ) { $GLOBALS['odt_buffer'] .= 'freeResult($result); + /** @var mixed $result */ + $dbi->freeResult($result);// This makes no sense return true; } @@ -2349,11 +2351,8 @@ class ExportSql extends ExportPlugin $fields_cnt = $dbi->numFields($result); // Get field information + /** @var FieldMetadata[] $fields_meta */ $fields_meta = $dbi->getFieldsMeta($result); - $field_flags = []; - for ($j = 0; $j < $fields_cnt; $j++) { - $field_flags[$j] = $dbi->fieldFlags($result, $j); - } $field_set = []; for ($j = 0; $j < $fields_cnt; $j++) { @@ -2502,15 +2501,15 @@ class ExportSql extends ExportPlugin // NULL if (! isset($row[$j]) || $row[$j] === null) { $values[] = 'NULL'; - } elseif ($fields_meta[$j]->numeric - && $fields_meta[$j]->type !== 'timestamp' - && ! $fields_meta[$j]->blob + } elseif ($fields_meta[$j]->isNumeric + && ! $fields_meta[$j]->isMappedTypeTimestamp + && ! $fields_meta[$j]->isBlob ) { // a number // timestamp is numeric on some MySQL 4.1, BLOBs are // sometimes numeric $values[] = $row[$j]; - } elseif (stripos($field_flags[$j], 'BINARY') !== false + } elseif ($fields_meta[$j]->isBinary && isset($GLOBALS['sql_hex_for_binary']) ) { // a true BLOB @@ -2527,7 +2526,7 @@ class ExportSql extends ExportPlugin } else { $values[] = '0x' . bin2hex($row[$j]); } - } elseif ($fields_meta[$j]->type === 'bit') { + } elseif ($fields_meta[$j]->isMappedTypeBit) { // detection of 'bit' works only on mysqli extension $values[] = "b'" . $dbi->escapeString( Util::printableBitValue( @@ -2536,7 +2535,7 @@ class ExportSql extends ExportPlugin ) ) . "'"; - } elseif ($fields_meta[$j]->type === 'geometry') { + } elseif ($fields_meta[$j]->isMappedTypeGeometry) { // export GIS types as hex $values[] = '0x' . bin2hex($row[$j]); } elseif (! empty($GLOBALS['exporting_metadata']) diff --git a/libraries/classes/Plugins/Export/Helpers/Pdf.php b/libraries/classes/Plugins/Export/Helpers/Pdf.php index 10ad4db33b..22d0e7e7b5 100644 --- a/libraries/classes/Plugins/Export/Helpers/Pdf.php +++ b/libraries/classes/Plugins/Export/Helpers/Pdf.php @@ -8,6 +8,7 @@ declare(strict_types=1); namespace PhpMyAdmin\Plugins\Export\Helpers; use PhpMyAdmin\DatabaseInterface; +use PhpMyAdmin\FieldMetadata; use PhpMyAdmin\Pdf as PdfLib; use PhpMyAdmin\Relation; use PhpMyAdmin\Transformations; @@ -16,7 +17,6 @@ use TCPDF_STATIC; use function array_key_exists; use function count; use function ksort; -use function stripos; /** * Adapted from a LGPL script by Philip Clarke @@ -71,7 +71,7 @@ class Pdf extends PdfLib /** @var int */ private $numFields; - /** @var array */ + /** @var FieldMetadata[] */ private $fields; /** @var int|float */ @@ -736,6 +736,7 @@ class Pdf extends PdfLib */ public function mysqlReport($query) { + /** @var DatabaseInterface $dbi */ global $dbi; unset( @@ -756,7 +757,7 @@ class Pdf extends PdfLib DatabaseInterface::QUERY_UNBUFFERED ); $this->numFields = $dbi->numFields($this->results); - $this->fields = $dbi->getFieldsMeta($this->results); + $this->fields = $dbi->getFieldsMeta($this->results) ?? []; // sColWidth = starting col width (an average size width) $availableWidth = $this->w - $this->lMargin - $this->rMargin; @@ -789,27 +790,25 @@ class Pdf extends PdfLib $this->colTitles[$i] = $col_as; $this->displayColumn[$i] = true; - switch ($this->fields[$i]->type) { - case 'int': - $this->colAlign[$i] = 'R'; - break; - case 'blob': - case 'tinyblob': - case 'mediumblob': - case 'longblob': - /** - * @todo do not deactivate completely the display - * but show the field's name and [BLOB] - */ - if (stripos($this->fields[$i]->flags, 'BINARY') !== false) { - $this->displayColumn[$i] = false; - unset($this->colTitles[$i]); - } - $this->colAlign[$i] = 'L'; - break; - default: - $this->colAlign[$i] = 'L'; + $this->colAlign[$i] = 'L'; + + if ($this->fields[$i]->isType(FieldMetadata::TYPE_INT)) { + $this->colAlign[$i] = 'R'; } + + if (! $this->fields[$i]->isType(FieldMetadata::TYPE_BLOB)) { + continue; + } + + /** + * @todo do not deactivate completely the display + * but show the field's name and [BLOB] + */ + if ($this->fields[$i]->isBinary()) { + $this->displayColumn[$i] = false; + unset($this->colTitles[$i]); + } + $this->colAlign[$i] = 'L'; } // title width verification diff --git a/libraries/classes/Plugins/Transformations/Abs/Bool2TextTransformationsPlugin.php b/libraries/classes/Plugins/Transformations/Abs/Bool2TextTransformationsPlugin.php index 8eb0386aaa..e714daeeb6 100644 --- a/libraries/classes/Plugins/Transformations/Abs/Bool2TextTransformationsPlugin.php +++ b/libraries/classes/Plugins/Transformations/Abs/Bool2TextTransformationsPlugin.php @@ -7,8 +7,8 @@ declare(strict_types=1); namespace PhpMyAdmin\Plugins\Transformations\Abs; +use PhpMyAdmin\FieldMetadata; use PhpMyAdmin\Plugins\TransformationsPlugin; -use stdClass; /** * Provides common methods for all of the Bool2Text transformations plugins. @@ -31,13 +31,13 @@ abstract class Bool2TextTransformationsPlugin extends TransformationsPlugin /** * Does the actual work of each specific transformations plugin. * - * @param string $buffer text to be transformed - * @param array $options transformation options - * @param stdClass|null $meta meta information + * @param string $buffer text to be transformed + * @param array $options transformation options + * @param FieldMetadata|null $meta meta information * * @return string */ - public function applyTransformation($buffer, array $options = [], ?stdClass $meta = null) + public function applyTransformation($buffer, array $options = [], ?FieldMetadata $meta = null) { $cfg = $GLOBALS['cfg']; $options = $this->getOptions($options, $cfg['DefaultTransformations']['Bool2Text']); diff --git a/libraries/classes/Plugins/Transformations/Abs/CodeMirrorEditorTransformationPlugin.php b/libraries/classes/Plugins/Transformations/Abs/CodeMirrorEditorTransformationPlugin.php index 3c7212b27a..04d236f564 100644 --- a/libraries/classes/Plugins/Transformations/Abs/CodeMirrorEditorTransformationPlugin.php +++ b/libraries/classes/Plugins/Transformations/Abs/CodeMirrorEditorTransformationPlugin.php @@ -7,8 +7,8 @@ declare(strict_types=1); namespace PhpMyAdmin\Plugins\Transformations\Abs; +use PhpMyAdmin\FieldMetadata; use PhpMyAdmin\Plugins\IOTransformationsPlugin; -use stdClass; use function htmlspecialchars; use function strtolower; @@ -20,13 +20,13 @@ abstract class CodeMirrorEditorTransformationPlugin extends IOTransformationsPlu /** * Does the actual work of each specific transformations plugin. * - * @param string $buffer text to be transformed - * @param array $options transformation options - * @param stdClass|null $meta meta information + * @param string $buffer text to be transformed + * @param array $options transformation options + * @param FieldMetadata|null $meta meta information * * @return string */ - public function applyTransformation($buffer, array $options = [], ?stdClass $meta = null) + public function applyTransformation($buffer, array $options = [], ?FieldMetadata $meta = null) { return $buffer; } diff --git a/libraries/classes/Plugins/Transformations/Abs/DateFormatTransformationsPlugin.php b/libraries/classes/Plugins/Transformations/Abs/DateFormatTransformationsPlugin.php index c0c664b390..badb7bba25 100644 --- a/libraries/classes/Plugins/Transformations/Abs/DateFormatTransformationsPlugin.php +++ b/libraries/classes/Plugins/Transformations/Abs/DateFormatTransformationsPlugin.php @@ -7,10 +7,10 @@ declare(strict_types=1); namespace PhpMyAdmin\Plugins\Transformations\Abs; +use PhpMyAdmin\FieldMetadata; use PhpMyAdmin\Plugins\TransformationsPlugin; use PhpMyAdmin\Sanitize; use PhpMyAdmin\Util; -use stdClass; use function checkdate; use function gmdate; use function htmlspecialchars; @@ -49,13 +49,13 @@ abstract class DateFormatTransformationsPlugin extends TransformationsPlugin /** * Does the actual work of each specific transformations plugin. * - * @param string $buffer text to be transformed - * @param array $options transformation options - * @param stdClass|null $meta meta information + * @param string $buffer text to be transformed + * @param array $options transformation options + * @param FieldMetadata|null $meta meta information * * @return string */ - public function applyTransformation($buffer, array $options = [], ?stdClass $meta = null) + public function applyTransformation($buffer, array $options = [], ?FieldMetadata $meta = null) { $buffer = (string) $buffer; // possibly use a global transform and feed it with special options @@ -78,7 +78,7 @@ abstract class DateFormatTransformationsPlugin extends TransformationsPlugin // INT columns will be treated as UNIX timestamps // and need to be detected before the verification for // MySQL TIMESTAMP - if ($meta->type === 'int') { + if ($meta !== null && $meta->isType(FieldMetadata::TYPE_INT)) { $timestamp = $buffer; // Detect TIMESTAMP(6 | 8 | 10 | 12 | 14) diff --git a/libraries/classes/Plugins/Transformations/Abs/DownloadTransformationsPlugin.php b/libraries/classes/Plugins/Transformations/Abs/DownloadTransformationsPlugin.php index 7ce4ffb6b6..c7d0fe92e2 100644 --- a/libraries/classes/Plugins/Transformations/Abs/DownloadTransformationsPlugin.php +++ b/libraries/classes/Plugins/Transformations/Abs/DownloadTransformationsPlugin.php @@ -7,9 +7,9 @@ declare(strict_types=1); namespace PhpMyAdmin\Plugins\Transformations\Abs; +use PhpMyAdmin\FieldMetadata; use PhpMyAdmin\Plugins\TransformationsPlugin; use PhpMyAdmin\Url; -use stdClass; use function array_merge; use function htmlspecialchars; @@ -37,13 +37,13 @@ abstract class DownloadTransformationsPlugin extends TransformationsPlugin /** * Does the actual work of each specific transformations plugin. * - * @param string $buffer text to be transformed - * @param array $options transformation options - * @param stdClass|null $meta meta information + * @param string $buffer text to be transformed + * @param array $options transformation options + * @param FieldMetadata|null $meta meta information * * @return string */ - public function applyTransformation($buffer, array $options = [], ?stdClass $meta = null) + public function applyTransformation($buffer, array $options = [], ?FieldMetadata $meta = null) { global $row, $fields_meta; diff --git a/libraries/classes/Plugins/Transformations/Abs/ExternalTransformationsPlugin.php b/libraries/classes/Plugins/Transformations/Abs/ExternalTransformationsPlugin.php index ea97b61545..954a5c006b 100644 --- a/libraries/classes/Plugins/Transformations/Abs/ExternalTransformationsPlugin.php +++ b/libraries/classes/Plugins/Transformations/Abs/ExternalTransformationsPlugin.php @@ -7,8 +7,8 @@ declare(strict_types=1); namespace PhpMyAdmin\Plugins\Transformations\Abs; +use PhpMyAdmin\FieldMetadata; use PhpMyAdmin\Plugins\TransformationsPlugin; -use stdClass; use const E_USER_DEPRECATED; use function count; use function fclose; @@ -74,13 +74,13 @@ abstract class ExternalTransformationsPlugin extends TransformationsPlugin /** * Does the actual work of each specific transformations plugin. * - * @param string $buffer text to be transformed - * @param array $options transformation options - * @param stdClass|null $meta meta information + * @param string $buffer text to be transformed + * @param array $options transformation options + * @param FieldMetadata|null $meta meta information * * @return string */ - public function applyTransformation($buffer, array $options = [], ?stdClass $meta = null) + public function applyTransformation($buffer, array $options = [], ?FieldMetadata $meta = null) { // possibly use a global transform and feed it with special options diff --git a/libraries/classes/Plugins/Transformations/Abs/FormattedTransformationsPlugin.php b/libraries/classes/Plugins/Transformations/Abs/FormattedTransformationsPlugin.php index 65aaecbaf5..4a00b17e29 100644 --- a/libraries/classes/Plugins/Transformations/Abs/FormattedTransformationsPlugin.php +++ b/libraries/classes/Plugins/Transformations/Abs/FormattedTransformationsPlugin.php @@ -7,8 +7,8 @@ declare(strict_types=1); namespace PhpMyAdmin\Plugins\Transformations\Abs; +use PhpMyAdmin\FieldMetadata; use PhpMyAdmin\Plugins\TransformationsPlugin; -use stdClass; use function strtr; /** @@ -33,13 +33,13 @@ abstract class FormattedTransformationsPlugin extends TransformationsPlugin /** * Does the actual work of each specific transformations plugin. * - * @param string $buffer text to be transformed - * @param array $options transformation options - * @param stdClass|null $meta meta information + * @param string $buffer text to be transformed + * @param array $options transformation options + * @param FieldMetadata|null $meta meta information * * @return string */ - public function applyTransformation($buffer, array $options = [], ?stdClass $meta = null) + public function applyTransformation($buffer, array $options = [], ?FieldMetadata $meta = null) { return '