ConnectionFactory.php 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. <?php
  2. namespace Doctrine\Bundle\DoctrineBundle;
  3. use Doctrine\Common\EventManager;
  4. use Doctrine\DBAL\Configuration;
  5. use Doctrine\DBAL\Connection;
  6. use Doctrine\DBAL\DBALException;
  7. use Doctrine\DBAL\Driver\AbstractMySQLDriver;
  8. use Doctrine\DBAL\DriverManager;
  9. use Doctrine\DBAL\Exception;
  10. use Doctrine\DBAL\Exception\DriverException;
  11. use Doctrine\DBAL\Platforms\AbstractPlatform;
  12. use Doctrine\DBAL\Types\Type;
  13. use function array_merge;
  14. use function class_exists;
  15. use function is_subclass_of;
  16. use const PHP_EOL;
  17. /**
  18. * @psalm-import-type Params from DriverManager
  19. */
  20. class ConnectionFactory
  21. {
  22. /** @var mixed[][] */
  23. private $typesConfig = [];
  24. /** @var bool */
  25. private $initialized = false;
  26. /**
  27. * @param mixed[][] $typesConfig
  28. */
  29. public function __construct(array $typesConfig)
  30. {
  31. $this->typesConfig = $typesConfig;
  32. }
  33. /**
  34. * Create a connection by name.
  35. *
  36. * @param mixed[] $params
  37. * @param array<string, string> $mappingTypes
  38. *
  39. * @return Connection
  40. *
  41. * @psalm-param Params $params
  42. */
  43. public function createConnection(array $params, ?Configuration $config = null, ?EventManager $eventManager = null, array $mappingTypes = [])
  44. {
  45. if (! $this->initialized) {
  46. $this->initializeTypes();
  47. }
  48. $overriddenOptions = $params['connection_override_options'] ?? [];
  49. unset($params['connection_override_options']);
  50. if (! isset($params['pdo']) && (! isset($params['charset']) || $overriddenOptions)) {
  51. $wrapperClass = null;
  52. if (isset($params['wrapperClass'])) {
  53. if (! is_subclass_of($params['wrapperClass'], Connection::class)) {
  54. if (class_exists(DBALException::class)) {
  55. throw DBALException::invalidWrapperClass($params['wrapperClass']);
  56. }
  57. throw Exception::invalidWrapperClass($params['wrapperClass']);
  58. }
  59. $wrapperClass = $params['wrapperClass'];
  60. $params['wrapperClass'] = null;
  61. }
  62. $connection = DriverManager::getConnection($params, $config, $eventManager);
  63. $params = array_merge($connection->getParams(), $overriddenOptions);
  64. $driver = $connection->getDriver();
  65. if ($driver instanceof AbstractMySQLDriver) {
  66. $params['charset'] = 'utf8mb4';
  67. if (! isset($params['defaultTableOptions']['collate'])) {
  68. $params['defaultTableOptions']['collate'] = 'utf8mb4_unicode_ci';
  69. }
  70. } else {
  71. $params['charset'] = 'utf8';
  72. }
  73. if ($wrapperClass !== null) {
  74. $params['wrapperClass'] = $wrapperClass;
  75. } else {
  76. $wrapperClass = Connection::class;
  77. }
  78. $connection = new $wrapperClass($params, $driver, $config, $eventManager);
  79. } else {
  80. $connection = DriverManager::getConnection($params, $config, $eventManager);
  81. }
  82. if (! empty($mappingTypes)) {
  83. $platform = $this->getDatabasePlatform($connection);
  84. foreach ($mappingTypes as $dbType => $doctrineType) {
  85. $platform->registerDoctrineTypeMapping($dbType, $doctrineType);
  86. }
  87. }
  88. return $connection;
  89. }
  90. /**
  91. * Try to get the database platform.
  92. *
  93. * This could fail if types should be registered to an predefined/unused connection
  94. * and the platform version is unknown.
  95. * For details have a look at DoctrineBundle issue #673.
  96. *
  97. * @throws DBALException
  98. * @throws Exception
  99. */
  100. private function getDatabasePlatform(Connection $connection): AbstractPlatform
  101. {
  102. try {
  103. return $connection->getDatabasePlatform();
  104. } catch (DriverException $driverException) {
  105. $exceptionClass = class_exists(DBALException::class) ? DBALException::class : Exception::class;
  106. throw new $exceptionClass(
  107. 'An exception occurred while establishing a connection to figure out your platform version.' . PHP_EOL .
  108. "You can circumvent this by setting a 'server_version' configuration value" . PHP_EOL . PHP_EOL .
  109. 'For further information have a look at:' . PHP_EOL .
  110. 'https://github.com/doctrine/DoctrineBundle/issues/673',
  111. 0,
  112. $driverException
  113. );
  114. }
  115. }
  116. /**
  117. * initialize the types
  118. */
  119. private function initializeTypes(): void
  120. {
  121. foreach ($this->typesConfig as $typeName => $typeConfig) {
  122. if (Type::hasType($typeName)) {
  123. Type::overrideType($typeName, $typeConfig['class']);
  124. } else {
  125. Type::addType($typeName, $typeConfig['class']);
  126. }
  127. }
  128. $this->initialized = true;
  129. }
  130. }