diff --git a/phpBB/config/default/container/services.yml b/phpBB/config/default/container/services.yml index 3402d78dea..ba78673211 100644 --- a/phpBB/config/default/container/services.yml +++ b/phpBB/config/default/container/services.yml @@ -7,7 +7,6 @@ imports: - { resource: services_content.yml } - { resource: services_cron.yml } - { resource: services_db.yml } - - { resource: services_doctrine.yml } - { resource: services_event.yml } - { resource: services_extensions.yml } - { resource: services_feed.yml } diff --git a/phpBB/config/default/container/services_db.yml b/phpBB/config/default/container/services_db.yml index fe7d42937d..4b0e49dddb 100644 --- a/phpBB/config/default/container/services_db.yml +++ b/phpBB/config/default/container/services_db.yml @@ -7,7 +7,10 @@ services: dbal.conn.driver: synthetic: true -# ----- DB Tools ----- + dbal.conn.doctrine: + synthetic: true + + # ----- DB Tools ----- dbal.tools.factory: class: phpbb\db\tools\factory @@ -15,7 +18,7 @@ services: class: phpbb\db\tools\tools_interface factory: ['@dbal.tools.factory', get] arguments: - - '@dbal.conn.driver' + - '@dbal.conn.doctrine' # ----- DB Extractor ----- dbal.extractor.factory: diff --git a/phpBB/config/default/container/services_doctrine.yml b/phpBB/config/default/container/services_doctrine.yml deleted file mode 100644 index 1511c830fd..0000000000 --- a/phpBB/config/default/container/services_doctrine.yml +++ /dev/null @@ -1,5 +0,0 @@ -services: - doctrine.connection: - class: Doctrine\DBAL\Connection - factory: ['phpbb\db\doctrine\connection_factory', 'get_connection'] - arguments: ['@config.php'] diff --git a/phpBB/develop/create_schema_files.php b/phpBB/develop/create_schema_files.php index 5288a84d6d..e7c44a7f34 100644 --- a/phpBB/develop/create_schema_files.php +++ b/phpBB/develop/create_schema_files.php @@ -49,8 +49,15 @@ $classes = $finder->core_path('phpbb/') ->get_classes(); $db = new \phpbb\db\driver\sqlite3(); + +// The database is not used by db\tools when we generate the schema but it requires a doctrine DBAL object +// which always tries to connect to the database in the constructor. Which means if we want a valid doctrine +// Connection object that is not connected to any database we have to do that. +$ref = new ReflectionClass(\Doctrine\DBAL\Connection::class); +$db_doctrine = $ref->newInstanceWithoutConstructor(); + $factory = new \phpbb\db\tools\factory(); -$db_tools = $factory->get($db, true); +$db_tools = $factory->get($db_doctrine, true); $tables_data = \Symfony\Component\Yaml\Yaml::parseFile($phpbb_root_path . '/config/default/container/tables.yml'); $tables = []; diff --git a/phpBB/includes/functions_compatibility.php b/phpBB/includes/functions_compatibility.php index 64eb2333ce..5846559f0f 100644 --- a/phpBB/includes/functions_compatibility.php +++ b/phpBB/includes/functions_compatibility.php @@ -365,10 +365,7 @@ function request_var($var_name, $default, $multibyte = false, $cookie = false, $ */ function get_tables($db) { - $db_tools_factory = new \phpbb\db\tools\factory(); - $db_tools = $db_tools_factory->get($db); - - return $db_tools->sql_list_tables(); + throw new BadFunctionCallException('function removed from phpBB core, use db_tools service instead.'); } /** diff --git a/phpBB/install/convert/controller/convertor.php b/phpBB/install/convert/controller/convertor.php index 9b44832dfe..aa388204cd 100644 --- a/phpBB/install/convert/controller/convertor.php +++ b/phpBB/install/convert/controller/convertor.php @@ -14,6 +14,7 @@ namespace phpbb\convert\controller; use phpbb\cache\driver\driver_interface; +use phpbb\db\doctrine\connection_factory; use phpbb\exception\http_exception; use phpbb\install\controller\helper; use phpbb\install\helper\container_factory; @@ -25,6 +26,7 @@ use phpbb\install\helper\navigation\navigation_provider; use phpbb\language\language; use phpbb\request\request_interface; use phpbb\template\template; +use PHPUnit\DbUnit\Database\Connection; use Symfony\Component\HttpFoundation\StreamedResponse; /** @@ -76,6 +78,11 @@ class convertor */ protected $db; + /** + * @var Connection + */ + protected $db_doctrine; + /** * @var install_helper */ @@ -165,10 +172,11 @@ class convertor $this->controller_helper->handle_language_select(); - $this->cache = $container->get('cache.driver'); - $this->config = $container->get('config'); + $this->cache = $container->get('cache.driver'); + $this->config = $container->get('config'); $this->config_php_file = new \phpbb\config_php_file($this->phpbb_root_path, $this->php_ext); - $this->db = $container->get('dbal.conn.driver'); + $this->db = $container->get('dbal.conn.driver'); + $this->db_doctrine = $container->get('dbal.conn.doctrine'); $this->config_table = $container->get_parameter('tables.config'); $this->session_keys_table = $container->get_parameter('tables.sessions_keys'); @@ -507,11 +515,13 @@ class convertor /** @var \phpbb\db\driver\driver_interface $src_db */ $src_db = new $src_dbms(); $src_db->sql_connect($src_dbhost, $src_dbuser, htmlspecialchars_decode($src_dbpasswd, ENT_COMPAT), $src_dbname, $src_dbport, false, true); + $src_db_doctrine = connection_factory::get_connection_from_params($src_dbms, $src_dbhost, $src_dbuser, htmlspecialchars_decode($src_dbpasswd, ENT_COMPAT), $src_dbname, $src_dbport); $same_db = false; } else { $src_db = $this->db; + $src_db_doctrine = $this->db_doctrine; $same_db = true; } @@ -526,7 +536,7 @@ class convertor $prefixes = array(); $db_tools_factory = new \phpbb\db\tools\factory(); - $db_tools = $db_tools_factory->get($src_db); + $db_tools = $db_tools_factory->get($src_db_doctrine); $tables_existing = $db_tools->sql_list_tables(); $tables_existing = array_map('strtolower', $tables_existing); foreach ($tables_existing as $table_name) diff --git a/phpBB/install/convertors/functions_phpbb20.php b/phpBB/install/convertors/functions_phpbb20.php index ae67fa1458..aed6c3aece 100644 --- a/phpBB/install/convertors/functions_phpbb20.php +++ b/phpBB/install/convertors/functions_phpbb20.php @@ -1876,11 +1876,7 @@ function phpbb_check_username_collisions() function phpbb_convert_timezone($timezone) { - global $config, $db, $phpbb_root_path, $phpEx, $table_prefix; - - $factory = new \phpbb\db\tools\factory(); - $timezone_migration = new \phpbb\db\migration\data\v310\timezone($config, $db, $factory->get($db), $phpbb_root_path, $phpEx, $table_prefix); - return $timezone_migration->convert_phpbb30_timezone($timezone, 0); + return \phpbb\db\migration\data\v310\timezone::convert_phpbb30_timezone($timezone, 0); } function phpbb_add_notification_options($user_notify_pm) diff --git a/phpBB/phpbb/db/doctrine/connection_factory.php b/phpBB/phpbb/db/doctrine/connection_factory.php index b88006a1d5..4d2c2f55e6 100644 --- a/phpBB/phpbb/db/doctrine/connection_factory.php +++ b/phpBB/phpbb/db/doctrine/connection_factory.php @@ -4,7 +4,7 @@ * This file is part of the phpBB Forum Software package. * * @copyright (c) phpBB Limited - * @license GNU General Public License, version 2 (GPL-2.0) + * @license GNU General Public License, version 2 (GPL-2.0) * * For full copyright and license information, please see * the docs/CREDITS.txt file. @@ -33,8 +33,8 @@ class connection_factory * * @return Connection Doctrine DBAL connection. * - * @throws runtime_exception If the database connection could not be established. - * @throws InvalidArgumentException If the provided driver name is not a valid phpBB database driver. + * @throws runtime_exception If the database connection could not be established. + * @throws InvalidArgumentException If the provided driver name is not a valid phpBB database driver. */ public static function get_connection(config_php_file $config): Connection { @@ -58,17 +58,17 @@ class connection_factory /** * Creates a database connection from the specified parameters. * - * @param string $driver Driver name. - * @param string $host Hostname. - * @param string|null $user Username. - * @param string|null $password Password. - * @param string|null $name Database name. - * @param string|null $port Database port. + * @param string $driver Driver name. + * @param string $host Hostname. + * @param string|null $user Username. + * @param string|null $password Password. + * @param string|null $name Database name. + * @param string|null $port Database port. * * @return Connection Doctrine DBAL connection. * - * @throws runtime_exception If the database connection could not be established. - * @throws InvalidArgumentException If $driver is not a valid phpBB database driver. + * @throws runtime_exception If the database connection could not be established. + * @throws InvalidArgumentException If $driver is not a valid phpBB database driver. */ public static function get_connection_from_params( string $driver, @@ -97,13 +97,16 @@ class connection_factory try { $connection = DriverManager::getConnection($params); - Type::addType(case_insensitive_string::CASE_INSENSITIVE_STRING, case_insensitive_string::class); + if (!Type::hasType(case_insensitive_string::CASE_INSENSITIVE_STRING)) + { + Type::addType(case_insensitive_string::CASE_INSENSITIVE_STRING, case_insensitive_string::class); + } $connection->getDatabasePlatform()->registerDoctrineTypeMapping('varchar_ci', case_insensitive_string::CASE_INSENSITIVE_STRING); return $connection; } catch (Exception $e) { - throw new runtime_exception('DB_CONNECTION_FAILED'); + throw new runtime_exception('DB_CONNECTION_FAILED', [], $e); } } diff --git a/phpBB/phpbb/db/doctrine/connection_parameter_factory.php b/phpBB/phpbb/db/doctrine/connection_parameter_factory.php index a87c6360ce..bf0a18f5e8 100644 --- a/phpBB/phpbb/db/doctrine/connection_parameter_factory.php +++ b/phpBB/phpbb/db/doctrine/connection_parameter_factory.php @@ -14,6 +14,7 @@ namespace phpbb\db\doctrine; use InvalidArgumentException; +use phpbb\db\doctrine\oci8\driver as oci8_driver; /** * Helper class to generate Doctrine DBAL configuration. @@ -152,9 +153,14 @@ class connection_parameter_factory 'oci8' => [ 'charset' => 'UTF8', 'platform' => new oracle_platform(), + 'driverClass' => oci8_driver::class, ], 'pdo_pgsql' => [ 'charset' => 'UTF8', + 'platform' => new postgresql_platform(), + ], + 'pdo_sqlsrv' => [ + 'platform' => new sqlsrv_platform(), ], ]; diff --git a/phpBB/phpbb/db/doctrine/table_helper.php b/phpBB/phpbb/db/doctrine/table_helper.php index 297481743d..422c71a240 100644 --- a/phpBB/phpbb/db/doctrine/table_helper.php +++ b/phpBB/phpbb/db/doctrine/table_helper.php @@ -4,7 +4,7 @@ * This file is part of the phpBB Forum Software package. * * @copyright (c) phpBB Limited - * @license GNU General Public License, version 2 (GPL-2.0) + * @license GNU General Public License, version 2 (GPL-2.0) * * For full copyright and license information, please see * the docs/CREDITS.txt file. @@ -27,7 +27,7 @@ class table_helper public static function convert_column_data(array $column_data, string $dbms_layer): array { $options = self::resolve_dbms_specific_options($column_data, $dbms_layer); - list($type, $opts) = type_converter::convert($column_data[0]); + list($type, $opts) = type_converter::convert($column_data[0], $dbms_layer); $options = array_merge($options, $opts); return [$type, $options]; } @@ -35,8 +35,8 @@ class table_helper /** * Resolve DBMS specific options in column data. * - * @param array $column_data Original column data. - * @param string $dbms_layer DBMS layer name. + * @param array $column_data Original column data. + * @param string $dbms_layer DBMS layer name. * * @return array Doctrine column options. */ @@ -54,12 +54,19 @@ class table_helper $doctrine_options['default'] = $column_data[1]; $doctrine_options['notnull'] = true; } + else + { + $doctrine_options['notnull'] = false; + } $non_string_pattern = '/^[a-z]*(?:int|decimal|bool|timestamp)(?::[0-9]+)?$/'; - if ($dbms_layer === 'oracle' && !preg_match($non_string_pattern, strtolower($column_data[0])) - && array_key_exists('default', $doctrine_options[0]) && $doctrine_options[0]['default'] === '') + if ($dbms_layer === 'oracle' + && !preg_match($non_string_pattern, strtolower($column_data[0])) + && array_key_exists('default', $doctrine_options) + && $doctrine_options['default'] === '') { - unset($doctrine_options['notnull']); + // Not null is true by default and Oracle does not allow empty strings in not null columns + $doctrine_options['notnull'] = false; } if (isset($column_data[2])) @@ -80,8 +87,8 @@ class table_helper /** * Returns the DBMS specific default value for a column definition. * - * @param array $default_options Database specific default value options. - * @param string $dbms_layer Name of the DBMS layer. + * @param array $default_options Database specific default value options. + * @param string $dbms_layer Name of the DBMS layer. * * @return mixed Default value for the current DBMS. * diff --git a/phpBB/phpbb/db/doctrine/type_converter.php b/phpBB/phpbb/db/doctrine/type_converter.php index c339cb7c9e..c03c3bd2c8 100644 --- a/phpBB/phpbb/db/doctrine/type_converter.php +++ b/phpBB/phpbb/db/doctrine/type_converter.php @@ -24,6 +24,7 @@ class type_converter * @var array */ private const TYPE_MAP = [ + 'INT' => ['integer', []], 'BINT' => ['bigint', []], 'ULINT' => ['integer', ['unsigned' => true]], 'UINT' => ['integer', ['unsigned' => true]], @@ -41,8 +42,8 @@ class type_converter 'MTEXT' => ['text', ['length' => ((1 << 24) - 1)]], 'MTEXT_UNI' => ['text', ['length' => ((1 << 24) - 1)]], 'TIMESTAMP' => ['integer', ['unsigned' => true]], - 'DECIMAL' => ['integer', ['precision' => 5, 'scale' => 2]], - 'PDECIMAL' => ['integer', ['precision' => 6, 'scale' => 3]], + 'DECIMAL' => ['decimal', ['precision' => 5, 'scale' => 2]], + 'PDECIMAL' => ['decimal', ['precision' => 6, 'scale' => 3]], 'VCHAR_UNI' => ['string', ['length' => 255]], 'VCHAR_CI' => ['string_ci', ['length' => 255]], 'VARBINARY' => ['binary', ['length' => 255]], @@ -55,7 +56,7 @@ class type_converter * * @return array Pair of type name and options. */ - public static function convert(string $type): array + public static function convert(string $type, string $dbms): array { if (strpos($type, ':') !== false) { @@ -63,7 +64,7 @@ class type_converter return self::mapWithLength($typename, (int) $length); } - return self::mapType($type); + return self::mapType($type, $dbms); } /** @@ -108,13 +109,40 @@ class type_converter * * @return array Pair of type name and an array of options. */ - private static function mapType(string $type): array + private static function mapType(string $type, string $dbms): array { - if (!in_array($type, self::TYPE_MAP, true)) + if (!array_key_exists($type, self::TYPE_MAP)) { throw new \InvalidArgumentException("Database type is undefined."); } + // Historically, on mssql varbinary fields were stored as varchar. + // For compatibility reasons we have to keep it (because when + // querying the database, mssql does not convert strings to their + // binary representation automatically like the other dbms. + if ($type === 'VARBINARY' && $dbms === 'mssql') + { + return self::TYPE_MAP['VCHAR']; + } + + // Historically, on mssql bool fields were stored as integer. + // For compatibility reasons we have to keep it because is + // some queries we are using MIN() to these columns which + // is forbidden by MSSQL for bool (bit) columns. + if ($type === 'BOOL' && $dbms === 'mssql') + { + return self::TYPE_MAP['TINT']; + } + + // Historically, on postgres bool fields were stored as integer. + // For compatibility reasons we have to keep it because when + // querying the database, postgres does not convert automatically + // 0 and 1 to their boolean representation like the other dbms. + if ($type === 'BOOL' && $dbms === 'postgresql') + { + return self::TYPE_MAP['TINT']; + } + return self::TYPE_MAP[$type]; } } diff --git a/phpBB/phpbb/db/migration/data/v310/timezone.php b/phpBB/phpbb/db/migration/data/v310/timezone.php index d596b52837..f9cfe718fe 100644 --- a/phpBB/phpbb/db/migration/data/v310/timezone.php +++ b/phpBB/phpbb/db/migration/data/v310/timezone.php @@ -72,7 +72,7 @@ class timezone extends \phpbb\db\migration\migration foreach ($update_blocks as $timezone => $user_ids) { $timezone = explode(':', $timezone); - $converted_timezone = $this->convert_phpbb30_timezone($timezone[0], $timezone[1]); + $converted_timezone = static::convert_phpbb30_timezone($timezone[0], $timezone[1]); $sql = 'UPDATE ' . $this->table_prefix . "users SET user_timezone = '" . $this->db->sql_escape($converted_timezone) . "' @@ -88,7 +88,7 @@ class timezone extends \phpbb\db\migration\migration // Update board default timezone $sql = 'UPDATE ' . $this->table_prefix . "config - SET config_value = '" . $this->convert_phpbb30_timezone($this->config['board_timezone'], $this->config['board_dst']) . "' + SET config_value = '" . static::convert_phpbb30_timezone($this->config['board_timezone'], $this->config['board_dst']) . "' WHERE config_name = 'board_timezone'"; $this->sql_query($sql); } @@ -101,7 +101,7 @@ class timezone extends \phpbb\db\migration\migration * @param $dst int Users daylight saving time * @return string Users new php Timezone which is used since 3.1 */ - public function convert_phpbb30_timezone($timezone, $dst) + public static function convert_phpbb30_timezone($timezone, $dst) { $offset = (float) $timezone + (int) $dst; diff --git a/phpBB/phpbb/db/tools/doctrine.php b/phpBB/phpbb/db/tools/doctrine.php index 279e07018d..631e4dc811 100644 --- a/phpBB/phpbb/db/tools/doctrine.php +++ b/phpBB/phpbb/db/tools/doctrine.php @@ -4,7 +4,7 @@ * This file is part of the phpBB Forum Software package. * * @copyright (c) phpBB Limited - * @license GNU General Public License, version 2 (GPL-2.0) + * @license GNU General Public License, version 2 (GPL-2.0) * * For full copyright and license information, please see * the docs/CREDITS.txt file. @@ -16,9 +16,14 @@ namespace phpbb\db\tools; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Exception; use Doctrine\DBAL\Schema\AbstractAsset; -use Doctrine\DBAL\Schema\Comparator; +use Doctrine\DBAL\Schema\AbstractSchemaManager; use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\SchemaException; +use Doctrine\DBAL\Schema\Sequence; use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Types\Type; +use phpbb\db\doctrine\comparator; use phpbb\db\doctrine\table_helper; /** @@ -26,41 +31,59 @@ use phpbb\db\doctrine\table_helper; * * In general, it is recommended to use Doctrine directly instead of this class as this * implementation is only a BC layer. - * - * In the 3.3.x version branch this class could return SQL statements instead of - * performing changes. This functionality has been removed. */ -class doctrine implements tools_interface +class doctrine implements tools_interface, tools { /** - * @var Comparator - */ - private $comparator; - - /** - * @var \Doctrine\DBAL\Schema\AbstractSchemaManager + * @var AbstractSchemaManager */ private $schema_manager; + /** + * @var Connection + */ + private $connection; + + /** + * @var bool + */ + private $return_statements; + /** * Database tools constructors. * * @param Connection $connection - * - * @throws Exception If the schema manager cannot be created. + * @param bool $return_statements */ - public function __construct(Connection $connection) + public function __construct(Connection $connection, bool $return_statements = false) { - $this->comparator = new Comparator(); - $this->schema_manager = $connection->createSchemaManager(); + $this->return_statements = $return_statements; + $this->connection = $connection; } /** - * {@inheritDoc} + * @return AbstractSchemaManager + * + * @throws Exception */ - public function perform_schema_changes(array $schema_changes): void + protected function get_schema_manager(): AbstractSchemaManager { - // @todo + if ($this->schema_manager == null) + { + $this->schema_manager = $this->connection->createSchemaManager(); + } + + return $this->schema_manager; + } + + /** + * @return Schema + * + * @throws Exception + */ + protected function get_schema(): Schema + { + return $this->get_schema_manager()->createSchema(); } /** @@ -70,7 +93,8 @@ class doctrine implements tools_interface { try { - return array_map('strtolower', $this->schema_manager->listTableNames()); + $tables = array_map('strtolower', $this->get_schema_manager()->listTableNames()); + return array_combine($tables, $tables); } catch (Exception $e) { @@ -85,86 +109,7 @@ class doctrine implements tools_interface { try { - return $this->schema_manager->tablesExist([$table_name]); - } - catch (Exception $e) - { - return false; - } - } - - /** - * {@inheritDoc} - */ - public function sql_create_table(string $table_name, array $table_data): bool - { - if ($this->sql_table_exists($table_name)) - { - return false; - } - - try - { - $table = new Table($table_name); - $dbms_name = $this->schema_manager->getDatabasePlatform()->getName(); - - foreach ($table_data['COLUMNS'] as $column_name => $column_data) - { - list($type, $options) = table_helper::convert_column_data( - $column_data, - $dbms_name - ); - $table->addColumn($column_name, $type, $options); - } - - $table_data['PRIMARY_KEY'] = (!is_array($table_data['PRIMARY_KEY'])) - ? [$table_data['PRIMARY_KEY']] - : $table_data['PRIMARY_KEY']; - - $table->setPrimaryKey($table_data['PRIMARY_KEY']); - - if (array_key_exists('KEYS', $table_data)) - { - foreach ($table_data['KEYS'] as $key_name => $key_data) - { - $columns = (is_array($key_data[1])) ? $key_data[1] : [$key_data[1]]; - if ($key_data[0] === 'UNIQUE') - { - $table->addUniqueIndex($columns, $key_name); - } - else - { - $table->addIndex($columns, $key_name); - } - } - } - - switch ($dbms_name) - { - case 'mysql': - $table->addOption('collate', 'utf8_bin'); - break; - } - - $this->schema_manager->createTable($table); - } - catch (Exception $e) - { - return false; - } - - return true; - } - - /** - * {@inheritDoc} - */ - public function sql_table_drop(string $table_name): bool - { - try - { - $this->schema_manager->dropTable($table_name); - return true; + return $this->get_schema_manager()->tablesExist([$table_name]); } catch (Exception $e) { @@ -179,7 +124,7 @@ class doctrine implements tools_interface { try { - return $this->get_asset_names($this->schema_manager->listTableColumns($table_name)); + return $this->get_asset_names($this->get_schema_manager()->listTableColumns($table_name)); } catch (Exception $e) { @@ -194,7 +139,7 @@ class doctrine implements tools_interface { try { - return $this->asset_exists($column_name, $this->schema_manager->listTableColumns($table_name)); + return $this->asset_exists($column_name, $this->get_schema_manager()->listTableColumns($table_name)); } catch (Exception $e) { @@ -202,52 +147,6 @@ class doctrine implements tools_interface } } - /** - * {@inheritDoc} - */ - public function sql_column_add(string $table_name, string $column_name, array $column_data): bool - { - $dbms_name = $this->schema_manager->getDatabasePlatform()->getName(); - return $this->alter_table( - $table_name, - function (Table $table) use ($column_name, $column_data, $dbms_name) { - list($type, $options) = table_helper::convert_column_data($column_data, $dbms_name); - return $table->addColumn($column_name, $type, $options); - } - ); - } - - /** - * {@inheritDoc} - */ - public function sql_column_change(string $table_name, string $column_name, array $column_data): bool - { - // @todo: index handling. - return $this->alter_table( - $table_name, - function (Table $table) use ($column_name, $column_data) { - // @todo type maps to options['type'] - //$table->dropColumn($column_name); - //list($type, $options) = table_helper::convert_column_data($column_data); - //return $table->addColumn($column_name, $type, $options); - } - ); - } - - /** - * {@inheritDoc} - */ - public function sql_column_remove(string $table_name, string $column_name): bool - { - // @todo: index handling. - return $this->alter_table( - $table_name, - function (Table $table) use ($column_name) { - return $table->dropColumn($column_name); - } - ); - } - /** * {@inheritDoc} */ @@ -264,41 +163,6 @@ class doctrine implements tools_interface return $this->asset_exists($index_name, $this->get_filtered_index_list($table_name, true)); } - /** - * {@inheritDoc} - */ - public function sql_create_index(string $table_name, string $index_name, $column): bool - { - $column = (is_array($column)) ? $column : [$column]; - $index = new Index($index_name, $column); - try - { - $this->schema_manager->createIndex($index, $table_name); - } - catch (Exception $e) - { - return false; - } - - return true; - } - - /** - * {@inheritDoc} - */ - public function sql_index_drop(string $table_name, string $index_name): bool - { - try - { - $this->schema_manager->dropIndex($index_name, $table_name); - return true; - } - catch (Exception $e) - { - return false; - } - } - /** * {@inheritDoc} */ @@ -310,54 +174,231 @@ class doctrine implements tools_interface /** * {@inheritDoc} */ - public function sql_create_unique_index(string $table_name, string $index_name, $column): bool + public function perform_schema_changes(array $schema_changes) { - $column = (is_array($column)) ? $column : [$column]; - $index = new Index($index_name, $column, true); - try + if (empty($schema_changes)) { - $this->schema_manager->createIndex($index, $table_name); - } - catch (Exception $e) - { - return false; + return; } - return true; + return $this->_alter_schema( + function (Schema $schema) use($schema_changes): void + { + $mapping = [ + 'drop_tables' => [ + 'method' => '_schema_drop_table', + 'use_key' => false, + ], + 'add_tables' => [ + 'method' => '_schema_create_table', + 'use_key' => true, + ], + 'change_columns' => [ + 'method' => '_schema_column_change_add', + 'use_key' => true, + 'per_table' => true, + ], + 'add_columns' => [ + 'method' => '_schema_column_add', + 'use_key' => true, + 'per_table' => true, + ], + 'drop_columns' => [ + 'method' => '_schema_column_remove', + 'use_key' => false, + 'per_table' => true, + ], + 'drop_keys' => [ + 'method' => '_schema_index_drop', + 'use_key' => false, + 'per_table' => true, + ], + 'add_primary_keys' => [ + 'method' => '_schema_create_primary_key', + 'use_key' => true, + ], + 'add_unique_index' => [ + 'method' => '_schema_create_unique_index', + 'use_key' => true, + 'per_table' => true, + ], + 'add_index' => [ + 'method' => '_schema_create_index', + 'use_key' => true, + 'per_table' => true, + ], + ]; + + foreach ($mapping as $action => $params) + { + if (array_key_exists($action, $schema_changes)) + { + foreach ($schema_changes[$action] as $key => $data) + { + if (array_key_exists('per_table', $params) && $params['per_table']) + { + $table_name = $key; + $table_data = $data; + foreach ($table_data as $key => $data) + { + if ($params['use_key'] == false) + { + $this->{$params['method']}($schema, $table_name, $data, true); + } + else + { + $this->{$params['method']}($schema, $table_name, $key, $data, true); + } + } + } + else + { + if ($params['use_key'] == false) + { + $this->{$params['method']}($schema, $data, true); + } + else + { + $this->{$params['method']}($schema, $key, $data, true); + } + } + } + } + } + } + ); } /** * {@inheritDoc} */ - public function sql_create_primary_key(string $table_name, $column): bool + public function sql_create_table(string $table_name, array $table_data) { - $column = (is_array($column)) ? $column : [$column]; - $index = new Index('primary', $column, true, true); - try - { - $this->schema_manager->createIndex($index, $table_name); - } - catch (Exception $e) - { - return false; - } + return $this->_alter_schema( + function (Schema $schema) use ($table_name, $table_data): void + { + $this->_schema_create_table($schema, $table_name, $table_data, true); + } + ); + } - return true; + /** + * {@inheritDoc} + */ + public function sql_table_drop(string $table_name) + { + return $this->_alter_schema( + function (Schema $schema) use ($table_name): void + { + $this->_schema_drop_table($schema, $table_name, true); + } + ); + } + + /** + * {@inheritDoc} + */ + public function sql_column_add(string $table_name, string $column_name, array $column_data) + { + return $this->_alter_schema( + function (Schema $schema) use ($table_name, $column_name, $column_data): void + { + $this->_schema_column_add($schema, $table_name, $column_name, $column_data); + } + ); + } + + /** + * {@inheritDoc} + */ + public function sql_column_change(string $table_name, string $column_name, array $column_data) + { + return $this->_alter_schema( + function (Schema $schema) use ($table_name, $column_name, $column_data): void + { + $this->_schema_column_change($schema, $table_name, $column_name, $column_data); + } + ); + } + + /** + * {@inheritDoc} + */ + public function sql_column_remove(string $table_name, string $column_name) + { + return $this->_alter_schema( + function (Schema $schema) use ($table_name, $column_name): void + { + $this->_schema_column_remove($schema, $table_name, $column_name); + } + ); + } + + /** + * {@inheritDoc} + */ + public function sql_create_index(string $table_name, string $index_name, $column) + { + return $this->_alter_schema( + function (Schema $schema) use ($table_name, $index_name, $column): void + { + $this->_schema_create_index($column, $schema, $table_name, $index_name); + } + ); + } + + /** + * {@inheritDoc} + */ + public function sql_index_drop(string $table_name, string $index_name) + { + return $this->_alter_schema( + function (Schema $schema) use ($table_name, $index_name): void + { + $this->_schema_index_drop($schema, $table_name, $index_name); + } + ); + } + + /** + * {@inheritDoc} + */ + public function sql_create_unique_index(string $table_name, string $index_name, $column) + { + return $this->_alter_schema( + function (Schema $schema) use ($table_name, $index_name, $column): void + { + $this->_schema_create_unique_index($column, $schema, $table_name, $index_name); + } + ); + } + + /** + * {@inheritDoc} + */ + public function sql_create_primary_key(string $table_name, $column) + { + return $this->_alter_schema( + function (Schema $schema) use ($table_name, $column): void + { + $this->_schema_create_primary_key($schema, $column, $table_name); + } + ); } /** * Returns an array of indices for either unique and primary keys, or simple indices. * - * @param string $table_name The name of the table. - * @param bool $is_non_unique Whether to return simple indices or primary and unique ones. + * @param string $table_name The name of the table. + * @param bool $is_non_unique Whether to return simple indices or primary and unique ones. * * @return array The filtered index array. */ - private function get_filtered_index_list(string $table_name, bool $is_non_unique): array + protected function get_filtered_index_list(string $table_name, bool $is_non_unique): array { try { - $indices = $this->schema_manager->listTableIndexes($table_name); + $indices = $this->get_schema_manager()->listTableIndexes($table_name); } catch (Exception $e) { @@ -366,12 +407,14 @@ class doctrine implements tools_interface if ($is_non_unique) { - return array_filter($indices, function(Index $index) { + return array_filter($indices, function (Index $index) + { return $index->isSimpleIndex(); }); } - return array_filter($indices, function(Index $index) { + return array_filter($indices, function (Index $index) + { return !$index->isSimpleIndex(); }); } @@ -383,10 +426,11 @@ class doctrine implements tools_interface * * @return array An array of lowercase asset names. */ - private function get_asset_names(array $assets): array + protected function get_asset_names(array $assets): array { return array_map( - function(AbstractAsset $asset) { + function (AbstractAsset $asset) + { return strtolower($asset->getName()); }, $assets @@ -396,44 +440,470 @@ class doctrine implements tools_interface /** * Returns whether an asset name exists in a list of assets (case insensitive). * - * @param string $needle The asset name to search for. - * @param array $assets The array of assets. + * @param string $needle The asset name to search for. + * @param array $assets The array of assets. * * @return bool Whether the asset name exists in a list of assets. */ - private function asset_exists(string $needle, array $assets): bool + protected function asset_exists(string $needle, array $assets): bool { return in_array(strtolower($needle), $this->get_asset_names($assets), true); } /** - * Alter table. + * Alter the current database representation using a callback and execute the changes. + * Returns false in case of error. * - * @param string $table_name Table name. - * @param callable $callback Callback function to modify the table. + * @param callable $callback Callback taking the schema as parameters and returning it altered (or null in case of error) * - * @return bool True if the changes were applied successfully, false otherwise. + * @return bool|string[] */ - private function alter_table(string $table_name, callable $callback): bool + protected function _alter_schema(callable $callback) { try { - $table = $this->schema_manager->listTableDetails($table_name); - $altered_table = clone $table; - $altered_table = call_user_func($callback, $altered_table); - $diff = $this->comparator->diffTable($table, $altered_table); - if ($diff === false) + $current_schema = $this->get_schema(); + $new_schema = clone $current_schema; + call_user_func($callback, $new_schema); + + $comparator = new comparator(); + $schemaDiff = $comparator->compare($current_schema, $new_schema); + $queries = $schemaDiff->toSql($this->get_schema_manager()->getDatabasePlatform()); + + if ($this->return_statements) { - return true; + return $queries; } - $this->schema_manager->alterTable($diff); + foreach ($queries as $query) + { + // executeQuery() must be used here because $query might return a result set, for instance REPAIR does + $this->connection->executeQuery($query); + } + + return true; } catch (Exception $e) { return false; } + } - return true; + /** + * Alter table. + * + * @param string $table_name Table name. + * @param callable $callback Callback function to modify the table. + * + * @throws SchemaException + */ + protected function alter_table(Schema $schema, string $table_name, callable $callback): void + { + $table = $schema->getTable($table_name); + call_user_func($callback, $table); + } + + /** + * Update the schema representation with a new table. + * Returns null in case of errors + * + * @param Schema $schema + * @param string $table_name + * @param array $table_data + * @param bool $safe_check + * + * @throws SchemaException + */ + protected function _schema_create_table(Schema $schema, string $table_name, array $table_data, bool $safe_check = false): void + { + if ($safe_check && $this->sql_table_exists($table_name)) + { + return; + } + + $table = $schema->createTable($table_name); + $dbms_name = $this->get_schema_manager()->getDatabasePlatform()->getName(); + + foreach ($table_data['COLUMNS'] as $column_name => $column_data) + { + list($type, $options) = table_helper::convert_column_data( + $column_data, + $dbms_name + ); + $table->addColumn($column_name, $type, $options); + } + + if (array_key_exists('PRIMARY_KEY', $table_data)) + { + $table_data['PRIMARY_KEY'] = (!is_array($table_data['PRIMARY_KEY'])) + ? [$table_data['PRIMARY_KEY']] + : $table_data['PRIMARY_KEY']; + + $table->setPrimaryKey($table_data['PRIMARY_KEY']); + } + + if (array_key_exists('KEYS', $table_data)) + { + foreach ($table_data['KEYS'] as $key_name => $key_data) + { + $columns = (is_array($key_data[1])) ? $key_data[1] : [$key_data[1]]; + + // Supports key columns defined with there length + $columns = array_map(function (string $column) + { + if (strpos($column, ':') !== false) + { + $parts = explode(':', $column, 2); + return $parts[0]; + } + return $column; + }, $columns); + + if ($key_data[0] === 'UNIQUE') + { + $table->addUniqueIndex($columns, $key_name); + } + else + { + $table->addIndex($columns, $key_name); + } + } + } + + switch ($dbms_name) + { + case 'mysql': + $table->addOption('collate', 'utf8_bin'); + break; + } + } + + /** + * @param Schema $schema + * @param string $table_name + * @param bool $safe_check + * + * @throws SchemaException + */ + protected function _schema_drop_table(Schema $schema, string $table_name, bool $safe_check = false): void + { + if ($safe_check && !$schema->hasTable($table_name)) + { + return; + } + + $schema->dropTable($table_name); + } + + /** + * @param Schema $schema + * @param string $table_name + * @param string $column_name + * @param array $column_data + * @param bool $safe_check + * + * @throws SchemaException + */ + protected function _schema_column_add(Schema $schema, string $table_name, string $column_name, array $column_data, bool $safe_check = false): void + { + $this->alter_table( + $schema, + $table_name, + function (Table $table) use ($column_name, $column_data, $safe_check) + { + if ($safe_check && $table->hasColumn($column_name)) + { + return; + } + + $dbms_name = $this->get_schema_manager()->getDatabasePlatform()->getName(); + + list($type, $options) = table_helper::convert_column_data($column_data, $dbms_name); + $table->addColumn($column_name, $type, $options); + return $table; + } + ); + } + + /** + * @param Schema $schema + * @param string $table_name + * @param string $column_name + * @param array $column_data + * @param bool $safe_check + * + * @throws SchemaException + */ + protected function _schema_column_change(Schema $schema, string $table_name, string $column_name, array $column_data, bool $safe_check = false): void + { + $this->alter_table( + $schema, + $table_name, + function (Table $table) use ($column_name, $column_data, $safe_check): void + { + if ($safe_check && !$table->hasColumn($column_name)) + { + return; + } + + $dbms_name = $this->get_schema_manager()->getDatabasePlatform()->getName(); + + list($type, $options) = table_helper::convert_column_data($column_data, $dbms_name); + $options['type'] = Type::getType($type); + $table->changeColumn($column_name, $options); + + // Re-create the indices using this column + // TODO: not sure it will works the way we want. It is possible that doctrine does not detect any changes on the indices level + foreach ($table->getIndexes() as $index) + { + $index_columns = array_map('strtolower', $index->getUnquotedColumns()); + if (array_search($column_name, $index_columns, true) !== false) + { + $this->_recreate_index($table, $index, $index_columns); + } + } + } + ); + } + + /** + * @param Schema $schema + * @param string $table_name + * @param string $column_name + * @param array $column_data + * @param bool $safe_check + * + * @throws SchemaException + */ + protected function _schema_column_change_add(Schema $schema, string $table_name, string $column_name, array $column_data, bool $safe_check = false): void + { + $table = $schema->getTable($table_name); + if ($table->hasColumn($column_name)) + { + $this->_schema_column_change($schema, $table_name, $column_name, $column_data, $safe_check); + } + else + { + $this->_schema_column_add($schema, $table_name, $column_name, $column_data, $safe_check); + } + } + + /** + * @param Schema $schema + * @param string $table_name + * @param string $column_name + * @param bool $safe_check + * + * @throws SchemaException + */ + protected function _schema_column_remove(Schema $schema, string $table_name, string $column_name, bool $safe_check = false): void + { + $this->alter_table( + $schema, + $table_name, + function (Table $table) use ($schema, $table_name, $column_name, $safe_check): void + { + if ($safe_check && !$table->hasColumn($column_name)) + { + return; + } + + /* + * As our sequences does not have the same name as these generated + * by default by doctrine or the DBMS, we have to manage them ourselves. + */ + if ($table->getColumn($column_name)->getAutoincrement()) + { + foreach ($schema->getSequences() as $sequence) + { + if ($this->isSequenceAutoIncrementsFor($sequence, $table)) + { + $schema->dropSequence($sequence->getName()); + } + } + } + + // Re-create / delete the indices using this column + foreach ($table->getIndexes() as $index) + { + $index_columns = array_map('strtolower', $index->getUnquotedColumns()); + $key = array_search($column_name, $index_columns, true); + if ($key !== false) + { + unset($index_columns[$key]); + $this->_recreate_index($table, $index, $index_columns); + } + } + $table->dropColumn($column_name); + } + ); + } + + /** + * @param $column + * @param Schema $schema + * @param string $table_name + * @param string $index_name + * @param bool $safe_check + * + * @throws SchemaException + */ + protected function _schema_create_index($column, Schema $schema, string $table_name, string $index_name, bool $safe_check = false): void + { + $columns = (is_array($column)) ? $column : [$column]; + $table = $schema->getTable($table_name); + + if ($safe_check && $table->hasIndex($index_name)) + { + return; + } + + $table->addIndex($columns, $index_name); + } + + /** + * @param $column + * @param Schema $schema + * @param string $table_name + * @param string $index_name + * @param bool $safe_check + * + * @throws SchemaException + */ + protected function _schema_create_unique_index($column, Schema $schema, string $table_name, string $index_name, bool $safe_check = false): void + { + $columns = (is_array($column)) ? $column : [$column]; + $table = $schema->getTable($table_name); + + if ($safe_check && $table->hasIndex($index_name)) + { + return; + } + + $table->addUniqueIndex($columns, $index_name); + } + + /** + * @param Schema $schema + * @param string $table_name + * @param string $index_name + * @param bool $safe_check + * + * @throws SchemaException + */ + protected function _schema_index_drop(Schema $schema, string $table_name, string $index_name, bool $safe_check = false): void + { + $table = $schema->getTable($table_name); + + if ($safe_check && !$table->hasIndex($index_name)) + { + return; + } + + $table->dropIndex($index_name); + } + + /** + * @param $column + * @param Schema $schema + * @param string $table_name + * @param bool $safe_check + * + * @throws SchemaException + */ + protected function _schema_create_primary_key(Schema $schema, $column, string $table_name, bool $safe_check = false): void + { + $columns = (is_array($column)) ? $column : [$column]; + $table = $schema->getTable($table_name); + $table->dropPrimaryKey(); + $table->setPrimaryKey($columns); + } + + /** + * Recreate an index of a table + * + * @param Table $table + * @param Index $index + * @param array Columns to use in the new (recreated) index + * + * @throws SchemaException + */ + protected function _recreate_index(Table $table, Index $index, array $new_columns): void + { + if ($index->isPrimary()) + { + $table->dropPrimaryKey(); + } + else + { + $table->dropIndex($index->getName()); + } + + if (count($new_columns) > 0) + { + if ($index->isPrimary()) + { + $table->setPrimaryKey( + $new_columns, + $index->getName(), + ); + } + else if ($index->isUnique()) + { + $table->addUniqueIndex( + $new_columns, + $index->getName(), + $index->getOptions(), + ); + } + else + { + $table->addIndex( + $new_columns, + $index->getName(), + $index->getFlags(), + $index->getOptions(), + ); + } + } + } + + /** + * @param Sequence $sequence + * @param Table $table + * + * @return bool + * @throws SchemaException + * + * @see Sequence + */ + private function isSequenceAutoIncrementsFor(Sequence $sequence, Table $table): bool + { + $primaryKey = $table->getPrimaryKey(); + + if ($primaryKey === null) + { + return false; + } + + $pkColumns = $primaryKey->getColumns(); + + if (count($pkColumns) !== 1) + { + return false; + } + + $column = $table->getColumn($pkColumns[0]); + + if (! $column->getAutoincrement()) + { + return false; + } + + $sequenceName = $sequence->getShortestName($table->getNamespaceName()); + $tableName = $table->getShortestName($table->getNamespaceName()); + $tableSequenceName = sprintf('%s_seq', $tableName); + + return $tableSequenceName === $sequenceName; } } diff --git a/phpBB/phpbb/db/tools/factory.php b/phpBB/phpbb/db/tools/factory.php index 44671a764a..ff3d69f83e 100644 --- a/phpBB/phpbb/db/tools/factory.php +++ b/phpBB/phpbb/db/tools/factory.php @@ -13,32 +13,18 @@ namespace phpbb\db\tools; +use Doctrine\DBAL\Connection; + /** * A factory which serves the suitable tools instance for the given dbal */ class factory { /** - * @param mixed $db_driver - * @param bool $return_statements - * @return \phpbb\db\tools\tools_interface + * @return tools_interface */ - public function get($db_driver, $return_statements = false) + public function get(Connection $connection, $return_statements = false) { - // @todo: only create the doctrine tools object. - if ($db_driver instanceof \phpbb\db\driver\mssql_base) - { - return new \phpbb\db\tools\mssql($db_driver, $return_statements); - } - else if ($db_driver instanceof \phpbb\db\driver\postgres) - { - return new \phpbb\db\tools\postgres($db_driver, $return_statements); - } - else if ($db_driver instanceof \phpbb\db\driver\driver_interface) - { - return new \phpbb\db\tools\tools($db_driver, $return_statements); - } - - throw new \InvalidArgumentException('Invalid database driver given'); + return new doctrine($connection, $return_statements); } } diff --git a/phpBB/phpbb/db/tools/tools.php b/phpBB/phpbb/db/tools/tools.php index 5036f78dc4..822df4d6ca 100644 --- a/phpBB/phpbb/db/tools/tools.php +++ b/phpBB/phpbb/db/tools/tools.php @@ -19,6 +19,6 @@ namespace phpbb\db\tools; * * @deprecated 4.0.0-a1 */ -class tools extends doctrine +interface tools extends tools_interface { } diff --git a/phpBB/phpbb/db/tools/tools_interface.php b/phpBB/phpbb/db/tools/tools_interface.php index 259b6497b2..717f2c5a4e 100644 --- a/phpBB/phpbb/db/tools/tools_interface.php +++ b/phpBB/phpbb/db/tools/tools_interface.php @@ -1,15 +1,15 @@ -* @license GNU General Public License, version 2 (GPL-2.0) -* -* For full copyright and license information, please see -* the docs/CREDITS.txt file. -* -*/ + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ namespace phpbb\db\tools; @@ -22,38 +22,41 @@ interface tools_interface * Handle passed database update array. * Expected structure... * Key being one of the following - * drop_tables: Drop tables - * add_tables: Add tables - * change_columns: Column changes (only type, not name) - * add_columns: Add columns to a table - * drop_keys: Dropping keys - * drop_columns: Removing/Dropping columns - * add_primary_keys: adding primary keys - * add_unique_index: adding an unique index - * add_index: adding an index (can be column:index_size if you need to provide size) + * drop_tables: Drop tables + * add_tables: Add tables + * change_columns: Column changes (only type, not name) + * add_columns: Add columns to a table + * drop_keys: Dropping keys + * drop_columns: Removing/Dropping columns + * add_primary_keys: adding primary keys + * add_unique_index: adding an unique index + * add_index: adding an index (can be column:index_size if you need to provide size) * * The values are in this format: - * {TABLE NAME} => array( - * {COLUMN NAME} => array({COLUMN TYPE}, {DEFAULT VALUE}, {OPTIONAL VARIABLES}), - * {KEY/INDEX NAME} => array({COLUMN NAMES}), - * ) + * {TABLE NAME} => array( + * {COLUMN NAME} => array({COLUMN TYPE}, {DEFAULT VALUE}, {OPTIONAL VARIABLES}), + * {KEY/INDEX NAME} => array({COLUMN NAMES}), + * ) * * * @param array $schema_changes + * + * @return bool|string[] */ - public function perform_schema_changes(array $schema_changes): void; + public function perform_schema_changes(array $schema_changes); /** * Gets a list of tables in the database. * - * @return array Array of table names (all lower case) + * @return array Array of table names (all lower case) */ public function sql_list_tables(): array; /** * Check if table exists * - * @param string $table_name The table name to check for + * @param string $table_name The table name to check for + * * @return bool True if table exists, else false */ public function sql_table_exists(string $table_name): bool; @@ -61,68 +64,72 @@ interface tools_interface /** * Create SQL Table * - * @param string $table_name The table name to create - * @param array $table_data Array containing table data. - * @return bool True if the statements have been executed + * @param string $table_name The table name to create + * @param array $table_data Array containing table data. + * + * @return bool|string[] True if the statements have been executed */ - public function sql_create_table(string $table_name, array $table_data): bool; + public function sql_create_table(string $table_name, array $table_data); /** * Drop Table * - * @param string $table_name The table name to drop - * @return bool True if the statements have been executed + * @param string $table_name The table name to drop + * + * @return bool|string[] True if the statements have been executed */ - public function sql_table_drop(string $table_name): bool; + public function sql_table_drop(string $table_name); /** * Gets a list of columns of a table. * - * @param string $table_name Table name - * @return array Array of column names (all lower case) + * @param string $table_name Table name + * + * @return array Array of column names (all lower case) */ public function sql_list_columns(string $table_name): array; /** * Check whether a specified column exist in a table * - * @param string $table_name Table to check - * @param string $column_name Column to check - * @return bool True if column exists, false otherwise + * @param string $table_name Table to check + * @param string $column_name Column to check + * + * @return bool True if column exists, false otherwise */ public function sql_column_exists(string $table_name, string $column_name): bool; /** * Add new column * - * @param string $table_name Table to modify - * @param string $column_name Name of the column to add - * @param array $column_data Column data + * @param string $table_name Table to modify + * @param string $column_name Name of the column to add + * @param array $column_data Column data * - * @return bool True if the statements have been executed + * @return bool|string[] True if the statements have been executed */ - public function sql_column_add(string $table_name, string $column_name, array $column_data): bool; + public function sql_column_add(string $table_name, string $column_name, array $column_data); /** * Change column type (not name!) * - * @param string $table_name Table to modify - * @param string $column_name Name of the column to modify - * @param array $column_data Column data + * @param string $table_name Table to modify + * @param string $column_name Name of the column to modify + * @param array $column_data Column data * - * @return bool True if the statements have been executed + * @return bool|string[] True if the statements have been executed */ - public function sql_column_change(string $table_name, string $column_name, array $column_data): bool; + public function sql_column_change(string $table_name, string $column_name, array $column_data); /** * Drop column * - * @param string $table_name Table to modify - * @param string $column_name Name of the column to drop + * @param string $table_name Table to modify + * @param string $column_name Name of the column to drop * - * @return bool True if the statements have been executed + * @return bool|string[] True if the statements have been executed */ - public function sql_column_remove(string $table_name, string $column_name): bool; + public function sql_column_remove(string $table_name, string $column_name); /** * List all of the indices that belong to a table @@ -131,66 +138,73 @@ interface tools_interface * - UNIQUE indices * - PRIMARY keys * - * @param string $table_name Table to check - * @return array Array with index names + * @param string $table_name Table to check + * + * @return array Array with index names */ public function sql_list_index(string $table_name): array; /** * Check if a specified index exists in table. Does not return PRIMARY KEY and UNIQUE indexes. * - * @param string $table_name Table to check the index at - * @param string $index_name The index name to check - * @return bool True if index exists, else false + * @param string $table_name Table to check the index at + * @param string $index_name The index name to check + * + * @return bool True if index exists, else false */ public function sql_index_exists(string $table_name, string $index_name): bool; /** * Add index * - * @param string $table_name Table to modify - * @param string $index_name Name of the index to create - * @param string|array $column Either a string with a column name, or an array with columns - * @return bool True if the statements have been executed + * @param string $table_name Table to modify + * @param string $index_name Name of the index to create + * @param string|array $column Either a string with a column name, or an array with columns + * + * @return bool|string[] True if the statements have been executed */ - public function sql_create_index(string $table_name, string $index_name, $column): bool; + public function sql_create_index(string $table_name, string $index_name, $column); /** * Drop Index * - * @param string $table_name Table to modify - * @param string $index_name Name of the index to delete - * @return bool True if the statements have been executed + * @param string $table_name Table to modify + * @param string $index_name Name of the index to delete + * + * @return bool|string[] True if the statements have been executed */ - public function sql_index_drop(string $table_name, string $index_name): bool; + public function sql_index_drop(string $table_name, string $index_name); /** * Check if a specified index exists in table. * * NOTE: Does not return normal and PRIMARY KEY indexes * - * @param string $table_name Table to check the index at - * @param string $index_name The index name to check - * @return bool True if index exists, else false + * @param string $table_name Table to check the index at + * @param string $index_name The index name to check + * + * @return bool|string[] True if index exists, else false */ - public function sql_unique_index_exists(string $table_name, string $index_name): bool; + public function sql_unique_index_exists(string $table_name, string $index_name); /** * Add unique index * - * @param string $table_name Table to modify - * @param string $index_name Name of the unique index to create - * @param string|array $column Either a string with a column name, or an array with columns - * @return bool True if the statements have been executed + * @param string $table_name Table to modify + * @param string $index_name Name of the unique index to create + * @param string|array $column Either a string with a column name, or an array with columns + * + * @return bool|string[] True if the statements have been executed */ - public function sql_create_unique_index(string $table_name, string $index_name, $column): bool; + public function sql_create_unique_index(string $table_name, string $index_name, $column); /** * Add primary key * - * @param string $table_name Table to modify - * @param string|array $column Either a string with a column name, or an array with columns - * @return bool True if the statements have been executed + * @param string $table_name Table to modify + * @param string|array $column Either a string with a column name, or an array with columns + * + * @return bool|string[] True if the statements have been executed */ - public function sql_create_primary_key(string $table_name, $column): bool; + public function sql_create_primary_key(string $table_name, $column); } diff --git a/phpBB/phpbb/di/container_builder.php b/phpBB/phpbb/di/container_builder.php index 415a2e9202..6631800a8a 100644 --- a/phpBB/phpbb/di/container_builder.php +++ b/phpBB/phpbb/di/container_builder.php @@ -13,6 +13,7 @@ namespace phpbb\di; +use phpbb\db\doctrine\connection_factory; use Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper; use Symfony\Component\Config\ConfigCache; use Symfony\Component\Config\FileLocator; @@ -55,6 +56,11 @@ class container_builder */ protected $dbal_connection = null; + /** + * @var \Doctrine\DBAL\Connection + */ + private $dbal_connection_doctrine; + /** * Indicates whether extensions should be used (default to true). * @@ -587,8 +593,10 @@ class container_builder false, defined('PHPBB_DB_NEW_LINK') && PHPBB_DB_NEW_LINK ); + $this->dbal_connection_doctrine = connection_factory::get_connection($this->config_php_file); } $this->container->set('dbal.conn.driver', $this->dbal_connection); + $this->container->set('dbal.conn.doctrine', $this->dbal_connection_doctrine); } } diff --git a/phpBB/phpbb/install/helper/database.php b/phpBB/phpbb/install/helper/database.php index 04d4bbb9c9..55a938de9e 100644 --- a/phpBB/phpbb/install/helper/database.php +++ b/phpBB/phpbb/install/helper/database.php @@ -13,6 +13,7 @@ namespace phpbb\install\helper; +use phpbb\db\doctrine\connection_factory; use phpbb\install\exception\invalid_dbms_exception; use phpbb\filesystem\helper as filesystem_helper; @@ -389,8 +390,9 @@ class database $temp_prefix . 'users', ); + $doctrine_db = connection_factory::get_connection_from_params($dbms, $dbhost, $dbuser, $dbpass, $dbname, $dbport); $db_tools_factory = new \phpbb\db\tools\factory(); - $db_tools = $db_tools_factory->get($db); + $db_tools = $db_tools_factory->get($doctrine_db); $tables = $db_tools->sql_list_tables(); $tables = array_map('strtolower', $tables); $table_intersect = array_intersect($tables, $table_ary); diff --git a/phpBB/phpbb/install/module/install_database/task/add_tables.php b/phpBB/phpbb/install/module/install_database/task/add_tables.php index c994e32ab4..0d7e81b9e8 100644 --- a/phpBB/phpbb/install/module/install_database/task/add_tables.php +++ b/phpBB/phpbb/install/module/install_database/task/add_tables.php @@ -13,6 +13,7 @@ namespace phpbb\install\module\install_database\task; +use phpbb\db\doctrine\connection_factory; use phpbb\db\driver\driver_interface; use phpbb\db\tools\tools_interface; use phpbb\install\helper\config; @@ -83,8 +84,17 @@ class add_tables extends task_base false ); + $doctrine_db = connection_factory::get_connection_from_params( + $config->get('dbms'), + $config->get('dbhost'), + $config->get('dbuser'), + $config->get('dbpasswd'), + $config->get('dbname'), + $config->get('dbport') + ); + $this->config = $config; - $this->db_tools = $factory->get($this->db); + $this->db_tools = $factory->get($doctrine_db); $this->schema_file_path = $phpbb_root_path . 'store/schema.json'; $this->table_prefix = $this->config->get('table_prefix'); $this->change_prefix = $this->config->get('change_table_prefix', true); diff --git a/phpBB/phpbb/install/module/install_database/task/create_schema_file.php b/phpBB/phpbb/install/module/install_database/task/create_schema_file.php index 984ca3461e..59a40e605f 100644 --- a/phpBB/phpbb/install/module/install_database/task/create_schema_file.php +++ b/phpBB/phpbb/install/module/install_database/task/create_schema_file.php @@ -13,6 +13,7 @@ namespace phpbb\install\module\install_database\task; +use phpbb\db\doctrine\connection_factory; use phpbb\install\exception\resource_limit_reached_exception; /** @@ -30,6 +31,11 @@ class create_schema_file extends \phpbb\install\task_base */ protected $db; + /** + * @var \Doctrine\DBAL\Connection + */ + protected $db_doctrine; + /** * @var \phpbb\filesystem\filesystem_interface */ @@ -81,6 +87,15 @@ class create_schema_file extends \phpbb\install\task_base false ); + $this->db_doctrine = connection_factory::get_connection_from_params( + $config->get('dbms'), + $config->get('dbhost'), + $config->get('dbuser'), + $config->get('dbpasswd'), + $config->get('dbname'), + $config->get('dbport') + ); + $this->config = $config; $this->filesystem = $filesystem; $this->phpbb_root_path = $phpbb_root_path; @@ -129,7 +144,7 @@ class create_schema_file extends \phpbb\install\task_base $finder = $finder_factory->get(); $migrator_classes = $finder->core_path('phpbb/db/migration/data/')->get_classes(); $factory = new \phpbb\db\tools\factory(); - $db_tools = $factory->get($this->db, true); + $db_tools = $factory->get($this->db_doctrine, true); $tables_data = \Symfony\Component\Yaml\Yaml::parseFile($this->phpbb_root_path . '/config/default/container/tables.yml'); $tables = []; foreach ($tables_data['parameters'] as $parameter => $table)