[BUGFIX] Move doctrine initalization to connect() method 32/54432/4
authorAlexander Stehlik <alexander.stehlik@gmail.com>
Wed, 18 Oct 2017 02:40:16 +0000 (04:40 +0200)
committerJigal van Hemert <jigal.van.hemert@typo3.org>
Tue, 31 Oct 2017 15:39:22 +0000 (16:39 +0100)
The event and custom type initalization of doctrine is moved
to an overwritten connect() method in the Connection class.

This prevents a premature establishment of a database connection
by calls to getDatabasePlatform().

This fixes the page unavailable during the connectToDB() in
the Frontend request handling.

Change-Id: Id6087002c7cbb9ed3938c4627d63b77515a7108f
Releases: master, 8.7
Resolves: #82795
Reviewed-on: https://review.typo3.org/54432
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Morton Jonuschat <m.jonuschat@mojocode.de>
Tested-by: Morton Jonuschat <m.jonuschat@mojocode.de>
Reviewed-by: Jigal van Hemert <jigal.van.hemert@typo3.org>
Tested-by: Jigal van Hemert <jigal.van.hemert@typo3.org>
typo3/sysext/core/Classes/Database/Connection.php
typo3/sysext/core/Classes/Database/ConnectionPool.php
typo3/sysext/core/Classes/Database/Schema/Parser/TableBuilder.php

index de7aead..1af7874 100644 (file)
@@ -19,11 +19,18 @@ use Doctrine\Common\EventManager;
 use Doctrine\DBAL\Configuration;
 use Doctrine\DBAL\Driver;
 use Doctrine\DBAL\Driver\Statement;
+use Doctrine\DBAL\Events;
 use Doctrine\DBAL\Platforms\PostgreSqlPlatform;
+use Doctrine\DBAL\Types\Type;
 use Psr\Log\LoggerAwareInterface;
 use Psr\Log\LoggerAwareTrait;
 use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder;
 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;
 
 class Connection extends \Doctrine\DBAL\Connection implements LoggerAwareInterface
@@ -61,6 +68,31 @@ class Connection extends \Doctrine\DBAL\Connection implements LoggerAwareInterfa
     const PARAM_BOOL = \PDO::PARAM_BOOL; // 5
 
     /**
+     * Prevents the duplicate registration of event handlers and types for this connection.
+     * If true the events will not be initialized any more in the connect() method.
+     *
+     * @var bool
+     */
+    private $customTypesAndEventsInitialized = false;
+
+    /**
+     * 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 static function getCustomDoctrineTypes(): array
+    {
+        return [
+            EnumType::TYPE => EnumType::class,
+            SetType::TYPE => SetType::class,
+        ];
+    }
+
+    /**
      * Initializes a new instance of the Connection class.
      *
      * @param array $params The connection parameters.
@@ -77,6 +109,53 @@ class Connection extends \Doctrine\DBAL\Connection implements LoggerAwareInterfa
     }
 
     /**
+     * Establishes the connection with the database.
+     *
+     * @return bool true if the connection was successfully established, false if the connection is already open.
+     */
+    public function connect(): bool
+    {
+        // Early return if the connection is already open and custom setup has been done.
+        if (!parent::connect() || $this->customTypesAndEventsInitialized) {
+            return false;
+        }
+
+        // Register custom data types
+        foreach (static::getCustomDoctrineTypes() as $type => $className) {
+            if (!Type::hasType($type)) {
+                Type::addType($type, $className);
+            }
+        }
+
+        // Register all custom data types in the type mapping
+        foreach (static::getCustomDoctrineTypes() as $type => $className) {
+            $this->getDatabasePlatform()->registerDoctrineTypeMapping($type, $type);
+        }
+
+        // Handler for building custom data type column definitions in the SchemaManager
+        $this->getDatabasePlatform()->getEventManager()->addEventListener(
+            Events::onSchemaColumnDefinition,
+            GeneralUtility::makeInstance(SchemaColumnDefinitionListener::class)
+        );
+
+        // Handler for enhanced index definitions in the SchemaManager
+        $this->getDatabasePlatform()->getEventManager()->addEventListener(
+            Events::onSchemaIndexDefinition,
+            GeneralUtility::makeInstance(SchemaIndexDefinitionListener::class)
+        );
+
+        // Handler for adding custom database platform options to ALTER TABLE requests in the SchemaManager
+        $this->getDatabasePlatform()->getEventManager()->addEventListener(
+            Events::onSchemaAlterTable,
+            GeneralUtility::makeInstance(SchemaAlterTableListener::class)
+        );
+
+        $this->customTypesAndEventsInitialized = true;
+
+        return true;
+    }
+
+    /**
      * Creates a new instance of a SQL query builder.
      *
      * @return \TYPO3\CMS\Core\Database\Query\QueryBuilder
index f32ba70..e501a3b 100644 (file)
@@ -16,15 +16,7 @@ namespace TYPO3\CMS\Core\Database;
  */
 
 use Doctrine\DBAL\DriverManager;
-use Doctrine\DBAL\Events;
-use Doctrine\DBAL\Types\Type;
 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.
@@ -49,14 +41,6 @@ class ConnectionPool
     protected static $connections = [];
 
     /**
-     * @var array
-     */
-    protected $customDoctrineTypes = [
-        EnumType::TYPE => EnumType::class,
-        SetType::TYPE => SetType::class,
-    ];
-
-    /**
      * Creates a connection object based on the specified table name.
      *
      * This is the official entry point to get a database connection to ensure
@@ -157,38 +141,6 @@ class ConnectionPool
         $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;
     }
 
@@ -224,18 +176,4 @@ 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;
-    }
 }
index 5c7540a..64ae6bf 100644 (file)
@@ -22,7 +22,7 @@ use Doctrine\DBAL\Schema\Column;
 use Doctrine\DBAL\Schema\Index;
 use Doctrine\DBAL\Schema\Table;
 use Doctrine\DBAL\Types\Type;
-use TYPO3\CMS\Core\Database\ConnectionPool;
+use TYPO3\CMS\Core\Database\Connection;
 use TYPO3\CMS\Core\Database\Schema\Parser\AST\CreateColumnDefinitionItem;
 use TYPO3\CMS\Core\Database\Schema\Parser\AST\CreateForeignKeyDefinitionItem;
 use TYPO3\CMS\Core\Database\Schema\Parser\AST\CreateIndexDefinitionItem;
@@ -59,12 +59,7 @@ class TableBuilder
      */
     public function __construct(AbstractPlatform $platform = null)
     {
-        // Register custom data types as no connection might have
-        // been established yet so the types would not be available
-        // when building tables/columns.
-        $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
-
-        foreach ($connectionPool->getCustomDoctrineTypes() as $type => $className) {
+        foreach (Connection::getCustomDoctrineTypes() as $type => $className) {
             if (!Type::hasType($type)) {
                 Type::addType($type, $className);
             }