<?php /** * This file is part of the CodeIgniter 4 framework. * * (c) CodeIgniter Foundation <admin@codeigniter.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace CodeIgniter\Database; use InvalidArgumentException; /** * Database Connection Factory * * Creates and returns an instance of the appropriate DatabaseConnection */ class Database { /** * Maintains an array of the instances of all connections that have * been created. * * Helps to keep track of all open connections for performance * monitoring, logging, etc. * * @var array */ protected $connections = []; //-------------------------------------------------------------------- /** * Parses the connection binds and returns an instance of the driver * ready to go. * * @param array $params * @param string $alias * * @return mixed * * @throws InvalidArgumentException * * @internal param bool $useBuilder */ public function load(array $params = [], string $alias = '') { if ($alias === '') { throw new InvalidArgumentException('You must supply the parameter: alias.'); } // Handle universal DSN connection string if (! empty($params['DSN']) && strpos($params['DSN'], '://') !== false) { $params = $this->parseDSN($params); } // No DB specified? Beat them senseless... if (empty($params['DBDriver'])) { throw new InvalidArgumentException('You have not selected a database type to connect to.'); } // Store the connection $this->connections[$alias] = $this->initDriver($params['DBDriver'], 'Connection', $params); return $this->connections[$alias]; } //-------------------------------------------------------------------- /** * Creates a Forge instance for the current database type. * * @param ConnectionInterface $db * * @return object */ public function loadForge(ConnectionInterface $db): object { // Initialize database connection if not exists. if (! $db->connID) { $db->initialize(); } return $this->initDriver($db->DBDriver, 'Forge', $db); } //-------------------------------------------------------------------- /** * Creates a Utils instance for the current database type. * * @param ConnectionInterface $db * * @return object */ public function loadUtils(ConnectionInterface $db): object { // Initialize database connection if not exists. if (! $db->connID) { $db->initialize(); } return $this->initDriver($db->DBDriver, 'Utils', $db); } //-------------------------------------------------------------------- /** * Parse universal DSN string * * @param array $params * * @return array * * @throws InvalidArgumentException */ protected function parseDSN(array $params): array { $dsn = parse_url($params['DSN']); if (! $dsn) { throw new InvalidArgumentException('Your DSN connection string is invalid.'); } $dsnParams = [ 'DSN' => '', 'DBDriver' => $dsn['scheme'], 'hostname' => isset($dsn['host']) ? rawurldecode($dsn['host']) : '', 'port' => isset($dsn['port']) ? rawurldecode((string) $dsn['port']) : '', 'username' => isset($dsn['user']) ? rawurldecode($dsn['user']) : '', 'password' => isset($dsn['pass']) ? rawurldecode($dsn['pass']) : '', 'database' => isset($dsn['path']) ? rawurldecode(substr($dsn['path'], 1)) : '', ]; // Do we have additional config items set? if (! empty($dsn['query'])) { parse_str($dsn['query'], $extra); foreach ($extra as $key => $val) { if (is_string($val) && in_array(strtolower($val), ['true', 'false', 'null'], true)) { $val = $val === 'null' ? null : filter_var($val, FILTER_VALIDATE_BOOLEAN); } $dsnParams[$key] = $val; } } return array_merge($params, $dsnParams); } //-------------------------------------------------------------------- /** * Initialize database driver. * * @param string $driver Database driver name (e.g. 'MySQLi') * @param string $class Database class name (e.g. 'Forge') * @param array|object $argument * * @return object */ protected function initDriver(string $driver, string $class, $argument): object { $class = $driver . '\\' . $class; if (strpos($driver, '\\') === false) { $class = "CodeIgniter\Database\\{$class}"; } return new $class($argument); } }