[TASK][CONF] Accept other settings in [SYS][setDBinit] 69/15369/21
authorAlexander Stehlik <alexander.stehlik@googlemail.com>
Tue, 14 Jan 2014 17:12:10 +0000 (18:12 +0100)
committerMarkus Klein <klein.t3@mfc-linz.at>
Wed, 15 Jan 2014 13:10:08 +0000 (14:10 +0100)
Since the mysqli interface recommends setting the charset using the
mysqli API the utf8 charset will be initialized when establishing the
database connection using mysqli_set_charset().

Additionally [SYS][setDBinit] can now be set to any value or can totally
be removed. To make sure the database connection still uses the correct
encoding an additional check is added to the database connection process
that checks the MySQL character set session variables.

Finally the old default value of [SYS][setDBinit] will automatically be
removed if it is set to the old default value.

Resolves: #41596
Releases: 6.2
Change-Id: I8d0a9eba50495d52accb59627147c1c87b6a9bb5
Reviewed-on: https://review.typo3.org/15369
Reviewed-by: Wouter Wolters
Reviewed-by: Alexander Opitz
Tested-by: Alexander Opitz
Reviewed-by: Michael Stucki
Tested-by: Michael Stucki
Reviewed-by: Markus Klein
Tested-by: Markus Klein
typo3/sysext/core/Classes/Core/Bootstrap.php
typo3/sysext/core/Classes/Database/DatabaseConnection.php
typo3/sysext/core/Configuration/DefaultConfiguration.php
typo3/sysext/core/Configuration/FactoryConfiguration.php
typo3/sysext/install/Classes/Service/SilentConfigurationUpgradeService.php

index a431086..99fe760 100644 (file)
@@ -238,7 +238,6 @@ class Bootstrap {
                $this->defineDatabaseConstants()
                        ->defineUserAgentConstant()
                        ->registerExtDirectComponents()
-                       ->checkUtf8DatabaseSettingsOrDie()
                        ->transferDeprecatedCurlSettings()
                        ->setCacheHashOptions()
                        ->setDefaultTimezone()
@@ -488,35 +487,6 @@ class Bootstrap {
        }
 
        /**
-        * Checking for UTF-8 in the settings since TYPO3 4.5
-        *
-        * Since TYPO3 4.5, everything other than UTF-8 is deprecated.
-        *
-        * [SYS][setDBinit] is used to set the DB connection
-        * and both settings need to be adjusted for UTF-8 in order to work properly
-        *
-        * @return Bootstrap
-        */
-       protected function checkUtf8DatabaseSettingsOrDie() {
-               if (isset($GLOBALS['TYPO3_CONF_VARS']['SYS']['setDBinit']) &&
-                       $GLOBALS['TYPO3_CONF_VARS']['SYS']['setDBinit'] !== '-1' &&
-                       preg_match('/SET NAMES [\'"]?utf8[\'"]?/i', $GLOBALS['TYPO3_CONF_VARS']['SYS']['setDBinit']) === FALSE &&
-                       TYPO3_enterInstallScript !== '1') {
-
-                       // Only accept "SET NAMES utf8" for this setting, otherwise die with a nice error
-                       die('This TYPO3 installation is using the $GLOBALS[\'TYPO3_CONF_VARS\'][\'SYS\'][\'setDBinit\'] property with the following value:' . chr(10) .
-                               $GLOBALS['TYPO3_CONF_VARS']['SYS']['setDBinit'] . chr(10) . chr(10) .
-                               'It looks like UTF-8 is not used for this connection.' . chr(10) . chr(10) .
-                               'Everything other than UTF-8 is unsupported since TYPO3 4.7.' . chr(10) .
-                               'The DB, its connection and TYPO3 should be migrated to UTF-8 therefore. Please check your setup.'
-                       );
-               } else {
-                       $GLOBALS['TYPO3_CONF_VARS']['SYS']['setDBinit'] = 'SET NAMES utf8;';
-               }
-               return $this;
-       }
-
-       /**
         * Parse old curl options and set new http ones instead
         *
         * @TODO: This code segment must still be finished
index 640c2c3..f23c109 100644 (file)
@@ -137,6 +137,14 @@ class DatabaseConnection {
        protected $connectionCompression = FALSE;
 
        /**
+        * The charset for the connection; will be passed on to
+        * mysqli_set_charset during connection initialization.
+        *
+        * @var string
+        */
+       protected $connectionCharset = 'utf8';
+
+       /**
         * @var array List of commands executed after connection was established
         */
        protected $initializeCommandsAfterConnect = array();
@@ -1194,6 +1202,15 @@ class DatabaseConnection {
 
                if ($connected) {
                        $this->isConnected = TRUE;
+
+                       if ($this->link->set_charset($this->connectionCharset) === FALSE) {
+                               \TYPO3\CMS\Core\Utility\GeneralUtility::sysLog(
+                                       'Error setting connection charset to "' . $this->connectionCharset . '"',
+                                       'Core',
+                                       \TYPO3\CMS\Core\Utility\GeneralUtility::SYSLOG_SEVERITY_ERROR
+                               );
+                       }
+
                        foreach ($this->initializeCommandsAfterConnect as $command) {
                                if ($this->link->query($command) === FALSE) {
                                        \TYPO3\CMS\Core\Utility\GeneralUtility::sysLog(
@@ -1204,6 +1221,7 @@ class DatabaseConnection {
                                }
                        }
                        $this->setSqlMode();
+                       $this->checkConnectionCharset();
                } else {
                        // @TODO: This should raise an exception. Would be useful especially to work during installation.
                        $error_msg = $this->link->connect_error;
@@ -1524,6 +1542,20 @@ class DatabaseConnection {
        }
 
        /**
+        * Set the charset that should be used for the MySQL connection.
+        * The given value will be passed on to mysqli_set_charset().
+        *
+        * The default value of this setting is utf8.
+        *
+        * @param string $connectionCharset The connection charset that will be passed on to mysqli_set_charset() when connecting the database. Default is utf8.
+        * @return void
+        */
+       public function setConnectionCharset($connectionCharset = 'utf8') {
+               $this->disconnectIfConnected();
+               $this->connectionCharset = $connectionCharset;
+       }
+
+       /**
         * Connects to database for TYPO3 sites:
         *
         * @param string $host Deprecated since 6.1, will be removed in two versions Database. host IP/domain[:port]
@@ -1601,6 +1633,82 @@ class DatabaseConnection {
        }
 
        /**
+        * Checks if the current connection character set has the same value
+        * as the connectionCharset variable.
+        *
+        * To determine the character set these MySQL session variables are
+        * checked: character_set_client, character_set_results and
+        * character_set_connection.
+        *
+        * If the character set does not match or if the session variables
+        * can not be read a RuntimeException is thrown.
+        *
+        * @return void
+        * @throws \RuntimeException
+        */
+       protected function checkConnectionCharset() {
+               $sessionResult = $this->sql_query('SHOW SESSION VARIABLES LIKE \'character_set%\'');
+
+               if ($sessionResult === FALSE) {
+                       \TYPO3\CMS\Core\Utility\GeneralUtility::sysLog(
+                               'Error while retrieving the current charset session variables from the database: ' . $this->sql_error(),
+                               'Core',
+                               \TYPO3\CMS\Core\Utility\GeneralUtility::SYSLOG_SEVERITY_ERROR
+                       );
+                       throw new \RuntimeException(
+                               'TYPO3 Fatal Error: Could not determine the current charset of the database.',
+                               1381847136
+                       );
+               }
+
+               $charsetVariables = array();
+               while (($row = $this->sql_fetch_row($sessionResult)) !== FALSE) {
+                       $variableName = $row[0];
+                       $variableValue = $row[1];
+                       $charsetVariables[$variableName] = $variableValue;
+               }
+               $this->sql_free_result($sessionResult);
+
+               // These variables are set with the "Set names" command which was
+               // used in the past. This is why we check them.
+               $charsetRequiredVariables = array(
+                       'character_set_client',
+                       'character_set_results',
+                       'character_set_connection',
+               );
+
+               $hasValidCharset = TRUE;
+               foreach ($charsetRequiredVariables as $variableName) {
+                       if (empty($charsetVariables[$variableName])) {
+                               \TYPO3\CMS\Core\Utility\GeneralUtility::sysLog(
+                                       'A required session variable is missing in the current MySQL connection: ' . $variableName,
+                                       'Core',
+                                       \TYPO3\CMS\Core\Utility\GeneralUtility::SYSLOG_SEVERITY_ERROR
+                               );
+                               throw new \RuntimeException(
+                                       'TYPO3 Fatal Error: Could not determine the value of the database session variable: ' . $variableName,
+                                       1381847779
+                               );
+                       }
+
+                       if ($charsetVariables[$variableName] !== $this->connectionCharset) {
+                               $hasValidCharset = FALSE;
+                               break;
+                       }
+               }
+
+               if (!$hasValidCharset) {
+                       throw new \RuntimeException(
+                               'It looks like the character set ' . $this->connectionCharset . ' is not used for this connection even though it is configured as connection charset. ' .
+                               'This TYPO3 installation is using the $GLOBALS[\'TYPO3_CONF_VARS\'][\'SYS\'][\'setDBinit\'] property with the following value: "' .
+                               $GLOBALS['TYPO3_CONF_VARS']['SYS']['setDBinit'] . '". Please make sure that this command does not overwrite the configured charset. ' .
+                               'Please note that for the TYPO3 database everything other than utf8 is unsupported since version 4.7.',
+                               1389697515
+                       );
+               }
+       }
+
+       /**
         * Disconnect from database if connected
         *
         * @return void
index 2c6f3ac..0586978 100644 (file)
@@ -110,6 +110,7 @@ return array(
                't3lib_cs_utils' => '',                                 // String (values: "iconv", "mbstring", default is homemade PHP-code). Defines which of these PHP-features to use for various charset processing functions in t3lib_cs. Will speed up charset functions radically.
                'no_pconnect' => TRUE,                                  // Boolean: If TRUE, "connect" is used to connect to the database. If FALSE, a persistent connection using "pconnect" will be established!
                'dbClientCompress' => FALSE,                    // Boolean: if TRUE, data exchange between TYPO3 and database server will be compressed. This may improve performance if (1) database serever is on the different server and (2) network connection speed to database server is 100mbps or less. CPU usage will be higher if this option is used but database operations will be executed faster due to much less (up to 3 times) database network traffic. This option has no effect if MySQL server is localhost.
+               'setDBinit' => '',                                              // String (textarea): These commands are executed after the database connection was established. Hint: The previous default "SET NAMES utf8;" is not required any more and will be removed automatically if set!
                'setMemoryLimit' => 0,                                  // Integer: memory_limit in MB: If more than 16, TYPO3 will try to use ini_set() to set the memory limit of PHP to the value. This works only if the function ini_set() is not disabled by your sysadmin.
                'serverTimeZone' => 1,                                  // Integer: GMT offset of servers time (from time()). Default is "1" which is "GMT+1" (central european time). This value can be used in extensions that are GMT aware and wants to convert times to/from other timezones.
                'phpTimeZone' => '',                                    // String: timezone to force for all date() and mktime() functions. A list of supported values can be found at <a href="http://php.net/manual/en/timezones.php" target="_blank">php.net</a>. If this is not set, a valid fallback will be searched for by PHP (php.ini's <a href="http://www.php.net/manual/en/datetime.configuration.php#ini.date.timezone" target="_blank">date.timezone</a> setting, server defaults, etc); and if no fallback is found, the value of "UTC" is used instead.
index 910c24e..0a1f313 100644 (file)
@@ -77,7 +77,6 @@ return array(
        'SYS' => array(
                'compat_version' => '6.2',
                'isInitialInstallationInProgress' => TRUE,
-               'setDBinit' => 'SET NAMES utf8;',
                'sitename' => 'New TYPO3 site',
        ),
 );
index 27702e7..532cd87 100644 (file)
@@ -141,6 +141,14 @@ class SilentConfigurationUpgradeService {
         */
        protected function removeObsoleteLocalConfigurationSettings() {
                $removed = $this->configurationManager->removeLocalConfigurationKeysByPath($this->obsoleteLocalConfigurationSettings);
+
+               // The old default value is not needed anymore. So if the user
+               // did not set a different value we can remove it.
+               $currentSetDbInitValue = $this->configurationManager->getConfigurationValueByPath('SYS/setDBinit');
+               if (preg_match('/^\s*SET\s+NAMES\s+[\'"]?utf8[\'"]?\s*[;]?\s*$/i', $currentSetDbInitValue) === 1) {
+                       $removed = $removed || $this->configurationManager->removeLocalConfigurationKeysByPath(array('SYS/setDBinit'));
+               }
+
                // If something was changed: Trigger a reload to have new values in next request
                if ($removed) {
                        $this->throwRedirectException();