3 * MySQL charset metadata and manipulations
6 declare(strict_types
=1);
10 use PhpMyAdmin\Charsets\Charset
;
11 use PhpMyAdmin\Charsets\Collation
;
12 use PhpMyAdmin\Dbal\DatabaseInterface
;
15 use function array_keys
;
17 use function is_string
;
20 use const SORT_STRING
;
23 * Class used to manage MySQL charsets
30 * @var array<string, string>
32 public static array $mysqlCharsetMap = [
39 'iso-8859-1' => 'latin1',
40 'iso-8859-2' => 'latin2',
41 'iso-8859-7' => 'greek',
42 'iso-8859-8' => 'hebrew',
43 'iso-8859-8-i' => 'hebrew',
44 'iso-8859-9' => 'latin5',
45 'iso-8859-13' => 'latin7',
46 'iso-8859-15' => 'latin1',
48 'shift_jis' => 'sjis',
49 'tis-620' => 'tis620',
51 'windows-1250' => 'cp1250',
52 'windows-1251' => 'cp1251',
53 'windows-1252' => 'latin1',
54 'windows-1256' => 'cp1256',
55 'windows-1257' => 'cp1257',
59 * The charset for the server
61 private static Charset|
null $serverCharset = null;
63 /** @var array<string, Charset> */
64 private static array $charsets = [];
66 /** @var array<string, array<string, Collation>> */
67 private static array $collations = [];
70 * Loads charset data from the server
72 * @param bool $disableIs Disable use of INFORMATION_SCHEMA
74 private static function loadCharsets(DatabaseInterface
$dbi, bool $disableIs): void
76 /* Data already loaded */
77 if (self
::$charsets !== []) {
81 $sql = 'SELECT `CHARACTER_SET_NAME` AS `Charset`,'
82 . ' `DEFAULT_COLLATE_NAME` AS `Default collation`,'
83 . ' `DESCRIPTION` AS `Description`,'
84 . ' `MAXLEN` AS `Maxlen`'
85 . ' FROM `information_schema`.`CHARACTER_SETS`';
88 $sql = 'SHOW CHARACTER SET';
91 $res = $dbi->query($sql);
94 foreach ($res as $row) {
95 self
::$charsets[$row['Charset']] = Charset
::fromServer($row);
98 ksort(self
::$charsets, SORT_STRING
);
102 * Loads collation data from the server
104 * @param bool $disableIs Disable use of INFORMATION_SCHEMA
106 private static function loadCollations(DatabaseInterface
$dbi, bool $disableIs): void
108 /* Data already loaded */
109 if (self
::$collations !== []) {
113 if ($dbi->isMariaDB() && $dbi->getVersion() >= 101000) {
114 /* Use query to accommodate new structure of MariaDB collations.
115 Note, that SHOW COLLATION command is not applicable at the time of writing.
116 Refer https://jira.mariadb.org/browse/MDEV-27009 */
117 $sql = 'SELECT `collapp`.`FULL_COLLATION_NAME` AS `Collation`,'
118 . ' `collapp`.`CHARACTER_SET_NAME` AS `Charset`,'
119 . ' `collapp`.`ID` AS `Id`,'
120 . ' `collapp`.`IS_DEFAULT` AS `Default`,'
121 . ' `coll`.`IS_COMPILED` AS `Compiled`,'
122 . ' `coll`.`SORTLEN` AS `Sortlen`'
123 . ' FROM `information_schema`.`COLLATION_CHARACTER_SET_APPLICABILITY` `collapp`'
124 . ' LEFT JOIN `information_schema`.`COLLATIONS` `coll`'
125 . ' ON `collapp`.`COLLATION_NAME`=`coll`.`COLLATION_NAME`';
127 $sql = 'SELECT `COLLATION_NAME` AS `Collation`,'
128 . ' `CHARACTER_SET_NAME` AS `Charset`,'
130 . ' `IS_DEFAULT` AS `Default`,'
131 . ' `IS_COMPILED` AS `Compiled`,'
132 . ' `SORTLEN` AS `Sortlen`'
133 . ' FROM `information_schema`.`COLLATIONS`';
136 $sql = 'SHOW COLLATION';
140 $res = $dbi->query($sql);
142 self
::$collations = [];
143 foreach ($res as $row) {
144 self
::$collations[$row['Charset']][$row['Collation']] = Collation
::fromServer($row);
147 foreach (array_keys(self
::$collations) as $charset) {
148 ksort(self
::$collations[$charset], SORT_STRING
);
153 * Get current server charset
155 * @param bool $disableIs Disable use of INFORMATION_SCHEMA
157 public static function getServerCharset(DatabaseInterface
$dbi, bool $disableIs): Charset
159 if (self
::$serverCharset !== null) {
160 return self
::$serverCharset;
163 self
::loadCharsets($dbi, $disableIs);
164 $serverCharset = $dbi->getVariable('character_set_server');
165 if (! is_string($serverCharset)) {// MySQL 5.7.8 fallback, issue #15614
166 $serverCharset = $dbi->fetchValue('SELECT @@character_set_server;');
169 self
::$serverCharset = self
::$charsets[$serverCharset] ??
null;
171 // MySQL 8.0.11+ fallback, issue #16931
172 if (self
::$serverCharset === null && $serverCharset === 'utf8mb3') {
173 // See: https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-11.html#mysqld-8-0-11-charset
174 // The utf8mb3 character set will be replaced by utf8mb4 in a future MySQL version.
175 // The utf8 character set is currently an alias for utf8mb3,
176 // but will at that point become a reference to utf8mb4.
177 // To avoid ambiguity about the meaning of utf8,
178 // consider specifying utf8mb4 explicitly for character set references instead of utf8.
179 // Warning: #3719 'utf8' is currently an alias for the character set UTF8MB3 [...]
180 return self
::$charsets['utf8'];
183 return self
::$serverCharset ?? Charset
::fromServer(
184 ['Charset' => __('Unknown'), 'Description' => __('Unknown')],
189 * Get all server charsets
191 * @param bool $disableIs Disable use of INFORMATION_SCHEMA
193 * @return array<string, Charset>
195 public static function getCharsets(DatabaseInterface
$dbi, bool $disableIs): array
197 self
::loadCharsets($dbi, $disableIs);
199 return self
::$charsets;
203 * Get all server collations
205 * @param bool $disableIs Disable use of INFORMATION_SCHEMA
207 * @return array<string, array<string, Collation>>
209 public static function getCollations(DatabaseInterface
$dbi, bool $disableIs): array
211 self
::loadCollations($dbi, $disableIs);
213 return self
::$collations;
217 * @param bool $disableIs Disable use of INFORMATION_SCHEMA
218 * @param string $name Collation name
220 public static function findCollationByName(
221 DatabaseInterface
$dbi,
225 $charset = explode('_', $name)[0];
226 $collations = self
::getCollations($dbi, $disableIs);
228 return $collations[$charset][$name] ??
null;