diff --git a/includes/database/mysql/database.inc b/includes/database/mysql/database.inc index 5e7315d..b44bc65 100644 --- a/includes/database/mysql/database.inc +++ b/includes/database/mysql/database.inc @@ -19,6 +19,28 @@ class DatabaseConnection_mysql extends DatabaseConnection { */ protected $shutdownRegistered = FALSE; + /** + * Flag to indicate our charset. It can either be utf8 or utf8mb4 (adds + * support for 4-byte UTF8 characters). See settings.default.php to learn more + * about utf8mb4. This is used in combination with the __get method below to + * make charset a read-only property of this object. + * + * @var string + */ + protected $charset = 'utf8'; + + /** + * This is used to fetch readonly variables. You can not read the registry + * instance reference through here. + * + * @param string $var + * @return string + */ + public function __get($property) { + // Only allow access to the charset property. + if ($property == 'charset') return $this->$property; + } + public function __construct(array $connection_options = array()) { // This driver defaults to transaction support, except if explicitly passed FALSE. $this->transactionSupport = !isset($connection_options['transactions']) || ($connection_options['transactions'] !== FALSE); @@ -46,17 +68,16 @@ class DatabaseConnection_mysql extends DatabaseConnection { PDO::ATTR_CASE => PDO::CASE_LOWER, )); - // Force MySQL to use the UTF-8 character set. Also set the collation, if a - // certain one has been set; otherwise, MySQL defaults to 'utf8_general_ci' - // for UTF-8. - if (!empty($connection_options['charset'])) $charset = $connection_options['charset']; - else $charset = 'utf8'; - + // Default Drupal to use the UTF-8 character set, but allow the use of + // utf8mb4 for better internationalization and full 4-byte UTF-8 support if + // set. Also set the collation, if a certain one has been set; otherwise, + // MySQL defaults to 'utf8_general_ci' for UTF-8. + $this->charset = (isset($connection_options['charset']) ? ($connection_options['charset'] == 'utf8mb4' ? 'utf8mb4' : 'utf8') : 'utf8'); if (!empty($connection_options['collation'])) { - $this->exec('SET NAMES '.$charset.' COLLATE ' . $connection_options['collation']); + $this->exec('SET NAMES ' . $this->charset . ' COLLATE ' . $connection_options['collation']); } else { - $this->exec('SET NAMES '.$charset); + $this->exec('SET NAMES ' . $this->charset); } // Force MySQL's behavior to conform more closely to SQL standards. diff --git a/includes/database/mysql/install.inc b/includes/database/mysql/install.inc index 75f2ae3..973a196 100644 --- a/includes/database/mysql/install.inc +++ b/includes/database/mysql/install.inc @@ -29,5 +29,22 @@ class DatabaseTasks_mysql extends DatabaseTasks { public function minimumVersion() { return '5.0.15'; } + + /** + * Validates settings, specifically the character set. + */ + public function validateDatabaseSettings($database) { + // Perform standard validation. + $errors = parent::validateDatabaseSettings($database); + + // If we are using utf8mb4 charset, make sure the database supports it. + if (isset($database['charset']) && $database['charset'] == 'utf8mb4') { + if (!db_query("SHOW CHARACTER SET WHERE Charset = 'utf8mb4'")->rowCount()) { + $errors['mysql_charset'] = st('Your database does not support the utf8mb4 character set'); + } + } + + return $errors; + } } diff --git a/includes/database/mysql/schema.inc b/includes/database/mysql/schema.inc index 4e88fa1..f372f77 100644 --- a/includes/database/mysql/schema.inc +++ b/includes/database/mysql/schema.inc @@ -141,7 +141,13 @@ class DatabaseSchema_mysql extends DatabaseSchema { if (!empty($spec['unsigned'])) { $sql .= ' unsigned'; } - + + // If it's a text field, check to see if we should use utf8mb4 (4-byte UTF8) + // as the character set. + if (in_array($spec['mysql_type'], array('TINYTEXT', 'MEDIUMTEXT', 'LONGTEXT', 'TEXT')) && Database::getConnection()->charset == 'utf8mb4') { + $sql .= ' CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci'; + } + if (isset($spec['not null'])) { if ($spec['not null']) { $sql .= ' NOT NULL';