[DOCS] Remove confusing "cron command" ambiguity.
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Database / ConnectionPool.php
index f4b3857..7b9a5dd 100644 (file)
@@ -1,5 +1,5 @@
 <?php
-declare (strict_types = 1);
+declare(strict_types = 1);
 namespace TYPO3\CMS\Core\Database;
 
 /*
@@ -15,9 +15,20 @@ namespace TYPO3\CMS\Core\Database;
  * The TYPO3 project - inspiring people to share!
  */
 
-use Doctrine\DBAL\Configuration;
 use Doctrine\DBAL\DriverManager;
+use Doctrine\DBAL\Events;
+use Doctrine\DBAL\Types\Type;
+use TYPO3\CMS\Core\Database\Driver\PDOMySql\Driver as PDOMySqlDriver;
+use TYPO3\CMS\Core\Database\Driver\PDOPgSql\Driver as PDOPgSqlDriver;
+use TYPO3\CMS\Core\Database\Driver\PDOSqlite\Driver as PDOSqliteDriver;
+use TYPO3\CMS\Core\Database\Driver\PDOSqlsrv\Driver as PDOSqlsrvDriver;
 use TYPO3\CMS\Core\Database\Query\QueryBuilder;
+use TYPO3\CMS\Core\Database\Schema\EventListener\SchemaAlterTableListener;
+use TYPO3\CMS\Core\Database\Schema\EventListener\SchemaColumnDefinitionListener;
+use TYPO3\CMS\Core\Database\Schema\EventListener\SchemaIndexDefinitionListener;
+use TYPO3\CMS\Core\Database\Schema\Types\EnumType;
+use TYPO3\CMS\Core\Database\Schema\Types\SetType;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
 
 /**
  * Manager that handles opening/retrieving database connections.
@@ -42,6 +53,29 @@ class ConnectionPool
     protected static $connections = [];
 
     /**
+     * @var array
+     */
+    protected $customDoctrineTypes = [
+        EnumType::TYPE => EnumType::class,
+        SetType::TYPE => SetType::class,
+    ];
+
+    /**
+     * List of custom drivers and their mappings to the driver classes.
+     *
+     * @var string[]
+     */
+    protected static $driverMap = [
+        'pdo_mysql' => PDOMySqlDriver::class,
+        'pdo_sqlite' => PDOSqliteDriver::class,
+        'pdo_pgsql' => PDOPgSqlDriver::class,
+        'pdo_sqlsrv' => PDOSqlsrvDriver::class,
+        // TODO: not supported yet, need to be checked later
+//        'pdo_oci' => PDOOCIDriver::class,
+//        'drizzle_pdo_mysql' => DrizzlePDOMySQLDriver::class,
+    ];
+
+    /**
      * Creates a connection object based on the specified table name.
      *
      * This is the official entry point to get a database connection to ensure
@@ -59,7 +93,7 @@ class ConnectionPool
             );
         }
 
-        $connectionName = ConnectionPool::DEFAULT_CONNECTION_NAME;
+        $connectionName = self::DEFAULT_CONNECTION_NAME;
         if (!empty($GLOBALS['TYPO3_CONF_VARS']['DB']['TableMapping'][$tableName])) {
             $connectionName = (string)$GLOBALS['TYPO3_CONF_VARS']['DB']['TableMapping'][$tableName];
         }
@@ -76,7 +110,6 @@ class ConnectionPool
      * @param string $connectionName
      * @return Connection
      * @throws \Doctrine\DBAL\DBALException
-     * @internal
      */
     public function getConnectionByName(string $connectionName): Connection
     {
@@ -91,16 +124,14 @@ class ConnectionPool
             return static::$connections[$connectionName];
         }
 
-        if (empty($GLOBALS['TYPO3_CONF_VARS']['DB']['Connections'][$connectionName])
-            || !is_array($GLOBALS['TYPO3_CONF_VARS']['DB']['Connections'][$connectionName])
-        ) {
+        $connectionParams = $GLOBALS['TYPO3_CONF_VARS']['DB']['Connections'][$connectionName] ?? [];
+        if (empty($connectionParams)) {
             throw new \RuntimeException(
                 'The requested database connection named "' . $connectionName . '" has not been configured.',
                 1459422492
             );
         }
 
-        $connectionParams = $GLOBALS['TYPO3_CONF_VARS']['DB']['Connections'][$connectionName];
         if (empty($connectionParams['wrapperClass'])) {
             $connectionParams['wrapperClass'] = Connection::class;
         }
@@ -119,6 +150,22 @@ class ConnectionPool
     }
 
     /**
+     * Map custom driver class for certain driver
+     *
+     * @param array $connectionParams
+     * @return array
+     */
+    protected function mapCustomDriver(array $connectionParams): array
+    {
+        // if no custom driver is provided, map TYPO3 specific drivers
+        if (!isset($connectionParams['driverClass']) && isset(static::$driverMap[$connectionParams['driver']])) {
+            $connectionParams['driverClass'] = static::$driverMap[$connectionParams['driver']];
+        }
+
+        return $connectionParams;
+    }
+
+    /**
      * Creates a connection object based on the specified parameters
      *
      * @param array $connectionParams
@@ -128,13 +175,48 @@ class ConnectionPool
     {
         // Default to UTF-8 connection charset
         if (empty($connectionParams['charset'])) {
-            $connectionParams['charset'] = 'utf-8';
+            $connectionParams['charset'] = 'utf8';
         }
+
+        $connectionParams = $this->mapCustomDriver($connectionParams);
+
         /** @var Connection $conn */
         $conn = DriverManager::getConnection($connectionParams);
         $conn->setFetchMode(\PDO::FETCH_ASSOC);
         $conn->prepareConnection($connectionParams['initCommands'] ?? '');
 
+        // Register custom data types
+        foreach ($this->customDoctrineTypes as $type => $className) {
+            if (!Type::hasType($type)) {
+                Type::addType($type, $className);
+            }
+        }
+
+        // Register all custom data types in the type mapping
+        foreach ($this->customDoctrineTypes as $type => $className) {
+            $conn->getDatabasePlatform()->registerDoctrineTypeMapping($type, $type);
+        }
+
+        // Handler for building custom data type column definitions
+        // in the SchemaManager
+        $conn->getDatabasePlatform()->getEventManager()->addEventListener(
+            Events::onSchemaColumnDefinition,
+            GeneralUtility::makeInstance(SchemaColumnDefinitionListener::class)
+        );
+
+        // Handler for enhanced index definitions in the SchemaManager
+        $conn->getDatabasePlatform()->getEventManager()->addEventListener(
+            Events::onSchemaIndexDefinition,
+            GeneralUtility::makeInstance(SchemaIndexDefinitionListener::class)
+        );
+
+        // Handler for adding custom database platform options to ALTER TABLE
+        // requests in the SchemaManager
+        $conn->getDatabasePlatform()->getEventManager()->addEventListener(
+            Events::onSchemaAlterTable,
+            GeneralUtility::makeInstance(SchemaAlterTableListener::class)
+        );
+
         return $conn;
     }
 
@@ -170,4 +252,28 @@ class ConnectionPool
     {
         return array_keys($GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']);
     }
+
+    /**
+     * Returns the list of custom Doctrine data types implemented by TYPO3.
+     * This method is needed by the Schema parser to register the types as it
+     * does not require a database connection and thus the types don't get
+     * registered automatically.
+     *
+     * @internal
+     * @return array
+     */
+    public function getCustomDoctrineTypes(): array
+    {
+        return $this->customDoctrineTypes;
+    }
+
+    /**
+     * Reset internal list of connections.
+     * Currently primarily used in functional tests to close connections and start
+     * new ones in between single tests.
+     */
+    public function resetConnections(): void
+    {
+        static::$connections = [];
+    }
 }