Merge remote-tracking branch 'upstream/develop' into 4.4

Conflicts:
	user_guide_src/source/database/configuration.rst
This commit is contained in:
kenjis 2023-06-12 08:01:48 +09:00
commit d97fded2c8
No known key found for this signature in database
GPG Key ID: BD254878922AF198
16 changed files with 279 additions and 104 deletions

View File

@ -37,7 +37,7 @@ class Config extends BaseConfig
protected static $factory;
/**
* Creates the default
* Returns the database connection
*
* @param array|BaseConnection|string|null $group The name of the connection group to use,
* or an array of configuration settings.

View File

@ -16,7 +16,7 @@ use InvalidArgumentException;
/**
* Database Connection Factory
*
* Creates and returns an instance of the appropriate DatabaseConnection
* Creates and returns an instance of the appropriate Database Connection.
*/
class Database
{
@ -32,8 +32,7 @@ class Database
protected $connections = [];
/**
* Parses the connection binds and returns an instance of the driver
* ready to go.
* Parses the connection binds and creates a Database Connection instance.
*
* @return BaseConnection
*
@ -83,7 +82,7 @@ class Database
}
/**
* Parse universal DSN string
* Parses universal DSN string
*
* @throws InvalidArgumentException
*/
@ -121,21 +120,20 @@ class Database
}
/**
* Initialize database driver.
* Creates a database object.
*
* @param string $driver Driver name. FQCN can be used.
* @param array|object $argument
* @param string $class 'Connection'|'Forge'|'Utils'
* @param array|object $argument The constructor parameter.
*
* @return BaseConnection|BaseUtils|Forge
*/
protected function initDriver(string $driver, string $class, $argument): object
{
$class = $driver . '\\' . $class;
$classname = (strpos($driver, '\\') === false)
? "CodeIgniter\\Database\\{$driver}\\{$class}"
: $driver . '\\' . $class;
if (strpos($driver, '\\') === false) {
$class = "CodeIgniter\\Database\\{$class}";
}
return new $class($argument);
return new $classname($argument);
}
}

View File

@ -64,14 +64,11 @@ class Connection extends BaseConnection
$this->buildDSN();
}
// Strip pgsql if exists
// Convert DSN string
if (mb_strpos($this->DSN, 'pgsql:') === 0) {
$this->DSN = mb_substr($this->DSN, 6);
$this->convertDSN();
}
// Convert semicolons to spaces.
$this->DSN = str_replace(';', ' ', $this->DSN);
$this->connID = $persistent === true ? pg_pconnect($this->DSN) : pg_connect($this->DSN);
if ($this->connID !== false) {
@ -92,6 +89,44 @@ class Connection extends BaseConnection
return $this->connID;
}
/**
* Converts the DSN with semicolon syntax.
*/
private function convertDSN()
{
// Strip pgsql
$this->DSN = mb_substr($this->DSN, 6);
// Convert semicolons to spaces in DSN format like:
// pgsql:host=localhost;port=5432;dbname=database_name
// https://www.php.net/manual/en/function.pg-connect.php
$allowedParams = ['host', 'port', 'dbname', 'user', 'password', 'connect_timeout', 'options', 'sslmode', 'service'];
$parameters = explode(';', $this->DSN);
$output = '';
$previousParameter = '';
foreach ($parameters as $parameter) {
[$key, $value] = explode('=', $parameter, 2);
if (in_array($key, $allowedParams, true)) {
if ($previousParameter !== '') {
if (array_search($key, $allowedParams, true) < array_search($previousParameter, $allowedParams, true)) {
$output .= ';';
} else {
$output .= ' ';
}
}
$output .= $parameter;
$previousParameter = $key;
} else {
$output .= ';' . $parameter;
}
}
$this->DSN = $output;
}
/**
* Keep or establish the connection if no queries have been sent for
* a length of time exceeding the server's idle timeout.

View File

@ -33,7 +33,7 @@ class Registrar
'DBDriver' => 'MySQLi',
'DBPrefix' => 'db_',
'pConnect' => false,
'DBDebug' => (ENVIRONMENT !== 'production'),
'DBDebug' => true,
'charset' => 'utf8',
'DBCollat' => 'utf8_general_ci',
'swapPre' => '',
@ -52,7 +52,7 @@ class Registrar
'DBDriver' => 'Postgre',
'DBPrefix' => 'db_',
'pConnect' => false,
'DBDebug' => (ENVIRONMENT !== 'production'),
'DBDebug' => true,
'charset' => 'utf8',
'DBCollat' => 'utf8_general_ci',
'swapPre' => '',
@ -71,7 +71,7 @@ class Registrar
'DBDriver' => 'SQLite3',
'DBPrefix' => 'db_',
'pConnect' => false,
'DBDebug' => (ENVIRONMENT !== 'production'),
'DBDebug' => true,
'charset' => 'utf8',
'DBCollat' => 'utf8_general_ci',
'swapPre' => '',
@ -91,7 +91,7 @@ class Registrar
'DBDriver' => 'SQLSRV',
'DBPrefix' => 'db_',
'pConnect' => false,
'DBDebug' => (ENVIRONMENT !== 'production'),
'DBDebug' => true,
'charset' => 'utf8',
'DBCollat' => 'utf8_general_ci',
'swapPre' => '',
@ -110,8 +110,8 @@ class Registrar
'DBDriver' => 'OCI8',
'DBPrefix' => 'db_',
'pConnect' => false,
'DBDebug' => (ENVIRONMENT !== 'production'),
'charset' => 'utf8',
'DBDebug' => true,
'charset' => 'AL32UTF8',
'DBCollat' => 'utf8_general_ci',
'swapPre' => '',
'encrypt' => false,

View File

@ -13,6 +13,7 @@ namespace CodeIgniter\Database;
use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\Test\ReflectionHelper;
use Generator;
/**
* @internal
@ -190,4 +191,47 @@ final class ConfigTest extends CIUnitTestCase
$this->assertTrue($this->getPrivateProperty($conn, 'strictOn'));
$this->assertSame([], $this->getPrivateProperty($conn, 'failover'));
}
/**
* @dataProvider convertDSNProvider
*
* @see https://github.com/codeigniter4/CodeIgniter4/issues/7550
*/
public function testConvertDSN(string $input, string $expected)
{
$this->dsnGroupPostgreNative['DSN'] = $input;
$conn = Config::connect($this->dsnGroupPostgreNative, false);
$this->assertInstanceOf(BaseConnection::class, $conn);
$method = $this->getPrivateMethodInvoker($conn, 'convertDSN');
$method();
$this->assertSame($expected, $this->getPrivateProperty($conn, 'DSN'));
}
public function convertDSNProvider(): Generator
{
yield from [
[
'pgsql:host=localhost;port=5432;dbname=database_name;user=username;password=password',
'host=localhost port=5432 dbname=database_name user=username password=password',
],
[
'pgsql:host=localhost;port=5432;dbname=database_name;user=username;password=we;port=we',
'host=localhost port=5432 dbname=database_name user=username password=we;port=we',
],
[
'pgsql:host=localhost;port=5432;dbname=database_name',
'host=localhost port=5432 dbname=database_name',
],
[
"pgsql:host=localhost;port=5432;dbname=database_name;options='--client_encoding=UTF8'",
"host=localhost port=5432 dbname=database_name options='--client_encoding=UTF8'",
],
[
'pgsql:host=localhost;port=5432;dbname=database_name;something=stupid',
'host=localhost port=5432 dbname=database_name;something=stupid',
],
];
}
}

View File

@ -50,6 +50,7 @@ Bugs Fixed
the value of a placeholder.
- **Validation:** Fixed a bug that ``check()`` cannot specify non-default
database group.
- **Database:** Fixed a bug where semicolon character (``;``) in one of the Postgre connection parameters would break the DSN string.
See the repo's
`CHANGELOG.md <https://github.com/codeigniter4/CodeIgniter4/blob/develop/CHANGELOG.md>`_

View File

@ -4,7 +4,7 @@ Database Configuration
.. contents::
:local:
:depth: 2
:depth: 3
.. note::
See :ref:`requirements-supported-databases` for currently supported database drivers.
@ -18,6 +18,9 @@ connection values (username, password, database name, etc.). The config
file is located at **app/Config/Database.php**. You can also set
database connection values in the **.env** file. See below for more details.
Setting Default Database
========================
The config settings are stored in a class property that is an array with this
prototype:
@ -26,37 +29,45 @@ prototype:
The name of the class property is the connection name, and can be used
while connecting to specify a group name.
.. note:: The default location of the SQLite3 database is in the ``writable`` folder.
.. note:: The default location of the SQLite3 database is in the **writable** folder.
If you want to change the location, you must set the full path to the new folder.
DSN
===
---
Some database drivers (such as PDO, PostgreSQL, Oracle, ODBC) might
require a full DSN string to be provided. If that is the case, you
should use the 'DSN' configuration setting, as if you're using the
driver's underlying native PHP extension, like this:
Some database drivers (such as Postgre, OCI8) requires a full DSN string to connect.
But if you do not specify a DSN string for a driver that requires it, CodeIgniter
will try to build it with the rest of the provided settings.
If you specify a DSN, you should use the ``'DSN'`` configuration setting, as if
you're using the driver's underlying native PHP extension, like this:
.. literalinclude:: configuration/002.php
:lines: 11-15
.. note:: If you do not specify a DSN string for a driver that requires it, CodeIgniter
will try to build it with the rest of the provided settings.
DSN in Universal Manner
^^^^^^^^^^^^^^^^^^^^^^^
You can also set a Data Source Name in universal manner (URL like). In that case DSNs must have this prototype:
.. literalinclude:: configuration/003.php
:lines: 11-14
To override default config values when connecting with a universal version of the DSN string,
add the config variables as a query string:
.. literalinclude:: configuration/004.php
:lines: 11-15
.. literalinclude:: configuration/010.php
:lines: 11-15
.. note:: If you provide a DSN string and it is missing some valid settings (e.g., the
database character set), which are present in the rest of the configuration
fields, CodeIgniter will append them.
Failovers
=========
---------
You can also specify failovers for the situation when the main connection cannot connect for some reason.
These failovers can be specified by setting the failover for a connection like this:
@ -65,6 +76,9 @@ These failovers can be specified by setting the failover for a connection like t
You can specify as many failovers as you like.
Setting Multiple Databases
==========================
You may optionally store multiple sets of connection
values. If, for example, you run multiple environments (development,
production, test, etc.) under a single installation, you can set up a
@ -78,15 +92,15 @@ variable located in the config file:
.. literalinclude:: configuration/007.php
.. note:: The name 'test' is arbitrary. It can be anything you want. By
default we've used the word "default" for the primary connection,
.. note:: The name ``test`` is arbitrary. It can be anything you want. By
default we've used the word ``default`` for the primary connection,
but it too can be renamed to something more relevant to your project.
defaultGroup
============
Changing Databases Automatically
================================
You could modify the config file to detect the environment and automatically
update the `defaultGroup` value to the correct one by adding the required logic
update the ``defaultGroup`` value to the correct one by adding the required logic
within the class' constructor:
.. literalinclude:: configuration/008.php
@ -127,7 +141,7 @@ Explanation of Values:
================ ===========================================================================================================
Name Config Description
================ ===========================================================================================================
**dsn** The DSN connect string (an all-in-one configuration sequence).
**DSN** The DSN connect string (an all-in-one configuration sequence).
**hostname** The hostname of your database server. Often this is 'localhost'.
**username** The username used to connect to the database. (``SQLite3`` does not use this.)
**password** The password used to connect to the database. (``SQLite3`` does not use this.)

View File

@ -6,7 +6,9 @@ use CodeIgniter\Database\Config;
class Database extends Config
{
public $default = [
// ...
public array $default = [
'DSN' => '',
'hostname' => 'localhost',
'username' => 'root',
@ -14,7 +16,7 @@ class Database extends Config
'database' => 'database_name',
'DBDriver' => 'MySQLi',
'DBPrefix' => '',
'pConnect' => true,
'pConnect' => false,
'DBDebug' => true,
'charset' => 'utf8',
'DBCollat' => 'utf8_general_ci',

View File

@ -1,13 +1,18 @@
<?php
// PDO
$default = [
'DSN' => 'pgsql:host=localhost;port=5432;dbname=database_name',
// ...
];
namespace Config;
// Oracle
$default = [
'DSN' => '//localhost/XE',
use CodeIgniter\Database\Config;
class Database extends Config
{
// ...
];
// OCI8
public array $default = [
'DSN' => '//localhost/XE',
// ...
];
// ...
}

View File

@ -1,6 +1,17 @@
<?php
$default = [
'DSN' => 'DBDriver://username:password@hostname:port/database',
namespace Config;
use CodeIgniter\Database\Config;
class Database extends Config
{
// ...
];
public array $default = [
'DSN' => 'DBDriver://username:password@hostname:port/database',
// ...
];
// ...
}

View File

@ -1,13 +1,18 @@
<?php
// MySQLi
$default = [
'DSN' => 'MySQLi://username:password@hostname:3306/database?charset=utf8&DBCollat=utf8_general_ci',
// ...
];
namespace Config;
// Postgre
$default = [
'DSN' => 'Postgre://username:password@hostname:5432/database?charset=utf8&connect_timeout=5&sslmode=1',
use CodeIgniter\Database\Config;
class Database extends Config
{
// ...
];
// MySQLi
public array $default = [
'DSN' => 'MySQLi://username:password@hostname:3306/database?charset=utf8&DBCollat=utf8_general_ci',
// ...
];
// ...
}

View File

@ -1,36 +1,51 @@
<?php
$default['failover'] = [
[
'hostname' => 'localhost1',
'username' => '',
'password' => '',
'database' => '',
'DBDriver' => 'MySQLi',
'DBPrefix' => '',
'pConnect' => true,
'DBDebug' => true,
'charset' => 'utf8',
'DBCollat' => 'utf8_general_ci',
'swapPre' => '',
'encrypt' => false,
'compress' => false,
'strictOn' => false,
],
[
'hostname' => 'localhost2',
'username' => '',
'password' => '',
'database' => '',
'DBDriver' => 'MySQLi',
'DBPrefix' => '',
'pConnect' => true,
'DBDebug' => true,
'charset' => 'utf8',
'DBCollat' => 'utf8_general_ci',
'swapPre' => '',
'encrypt' => false,
'compress' => false,
'strictOn' => false,
],
];
namespace Config;
use CodeIgniter\Database\Config;
class Database extends Config
{
// ...
public array $default = [
// ...
'failover' => [
[
'hostname' => 'localhost1',
'username' => '',
'password' => '',
'database' => '',
'DBDriver' => 'MySQLi',
'DBPrefix' => '',
'pConnect' => true,
'DBDebug' => true,
'charset' => 'utf8',
'DBCollat' => 'utf8_general_ci',
'swapPre' => '',
'encrypt' => false,
'compress' => false,
'strictOn' => false,
],
[
'hostname' => 'localhost2',
'username' => '',
'password' => '',
'database' => '',
'DBDriver' => 'MySQLi',
'DBPrefix' => '',
'pConnect' => true,
'DBDebug' => true,
'charset' => 'utf8',
'DBCollat' => 'utf8_general_ci',
'swapPre' => '',
'encrypt' => false,
'compress' => false,
'strictOn' => false,
],
],
// ...
];
// ...
}

View File

@ -6,7 +6,9 @@ use CodeIgniter\Database\Config;
class Database extends Config
{
public $test = [
// ...
public array $test = [
'DSN' => '',
'hostname' => 'localhost',
'username' => 'root',

View File

@ -1,3 +1,14 @@
<?php
$defaultGroup = 'test';
namespace Config;
use CodeIgniter\Database\Config;
class Database extends Config
{
// ...
public string $defaultGroup = 'test';
// ...
}

View File

@ -0,0 +1,18 @@
<?php
namespace Config;
use CodeIgniter\Database\Config;
class Database extends Config
{
// ...
// Postgre
public array $default = [
'DSN' => 'Postgre://username:password@hostname:5432/database?charset=utf8&connect_timeout=5&sslmode=1',
// ...
];
// ...
}

View File

@ -6,11 +6,18 @@ Connecting to your Database
:local:
:depth: 2
Connecting to a Database
========================
Connecting to the Default Group
-------------------------------
You can connect to your database by adding this line of code in any
function where it is needed, or in your class constructor to make the
database available globally in that class.
.. literalinclude:: connecting/001.php
:lines: 2-
If the above function does **not** contain any information in the first
parameter, it will connect to the default group specified in your database config
@ -20,18 +27,19 @@ A convenience method exists that is purely a wrapper around the above line
and is provided for your convenience:
.. literalinclude:: connecting/002.php
:lines: 2-
Available Parameters
--------------------
**\\Config\\Database::connect($group = null, bool $getShared = true): BaseConnection**
#. ``$group``: The database group name, a string that must match the config class' property name. Default value is ``$config->defaultGroup``.
#. ``$group``: The database group name, a string that must match the config class' property name. Default value is ``Config\Database::$defaultGroup``.
#. ``$getShared``: true/false (boolean). Whether to return the shared connection (see
Connecting to Multiple Databases below).
Manually Connecting to a Database
---------------------------------
Connecting to Specific Group
----------------------------
The first parameter of this function can **optionally** be used to
specify a particular database group from your config file. Examples:
@ -39,8 +47,9 @@ specify a particular database group from your config file. Examples:
To choose a specific group from your config file you can do this:
.. literalinclude:: connecting/003.php
:lines: 2-
Where group_name is the name of the connection group from your config
Where ``group_name`` is the name of the connection group from your config
file.
Multiple Connections to Same Database
@ -51,6 +60,7 @@ database connection every time. If you need to have a separate connection
to the same database, send ``false`` as the second parameter:
.. literalinclude:: connecting/004.php
:lines: 2-
Connecting to Multiple Databases
================================
@ -59,6 +69,7 @@ If you need to connect to more than one database simultaneously you can
do so as follows:
.. literalinclude:: connecting/005.php
:lines: 2-
Note: Change the words ``group_one`` and ``group_two`` to the specific
group names you are connecting to.
@ -76,6 +87,7 @@ a connection that uses your custom settings. The array passed in must be
the same format as the groups are defined in the configuration file:
.. literalinclude:: connecting/006.php
:lines: 2-
Reconnecting / Keeping the Connection Alive
===========================================
@ -90,11 +102,13 @@ or re-establish it.
does not ping the server but it closes the connection then connects again.
.. literalinclude:: connecting/007.php
:lines: 2-
Manually closing the Connection
Manually Closing the Connection
===============================
While CodeIgniter intelligently takes care of closing your database
connections, you can explicitly close the connection.
.. literalinclude:: connecting/008.php
:lines: 2-