152 lines
4.4 KiB
PHP
152 lines
4.4 KiB
PHP
<?php
|
|
namespace Misuzu;
|
|
|
|
use Misuzu\Config\ConfigManager;
|
|
use PDO;
|
|
use InvalidArgumentException;
|
|
|
|
final class Database
|
|
{
|
|
/**
|
|
* Array of supported abstraction layers.
|
|
*/
|
|
private const SUPPORTED = [
|
|
'mysql',
|
|
'sqlite',
|
|
];
|
|
|
|
/**
|
|
* Default hostname.
|
|
*/
|
|
private const DEFAULT_HOST = '127.0.0.1';
|
|
|
|
/**
|
|
* The default port for MySQL.
|
|
*/
|
|
private const DEFAULT_PORT_MYSQL = 3306;
|
|
|
|
/**
|
|
* @var Database
|
|
*/
|
|
private static $instance;
|
|
|
|
/**
|
|
* @var PDO[]
|
|
*/
|
|
private $connections = [];
|
|
|
|
/**
|
|
* @var ConfigManager
|
|
*/
|
|
private $configManager;
|
|
|
|
/**
|
|
* @var string
|
|
*/
|
|
private $default;
|
|
|
|
public static function getInstance(): Database
|
|
{
|
|
if (!(self::$instance instanceof static)) {
|
|
throw new \UnexpectedValueException('No instance of Database exists yet.');
|
|
}
|
|
|
|
return self::$instance;
|
|
}
|
|
|
|
public function __construct(
|
|
ConfigManager $config,
|
|
string $default = 'default'
|
|
) {
|
|
if (self::$instance instanceof static) {
|
|
throw new \UnexpectedValueException('Only one instance of Database may exist.');
|
|
}
|
|
|
|
self::$instance = $this;
|
|
$this->default = $default;
|
|
$this->configManager = $config;
|
|
}
|
|
|
|
public static function connection(?string $name = null): PDO
|
|
{
|
|
return self::getInstance()->getConnection($name);
|
|
}
|
|
|
|
public function getConnection(?string $name = null): PDO
|
|
{
|
|
$name = $name ?? $this->default;
|
|
return $this->connections[$name] ?? $this->addConnection($name);
|
|
}
|
|
|
|
public function addConnection(string $name): PDO
|
|
{
|
|
$section = "Database.{$name}";
|
|
|
|
if (!$this->configManager->contains($section, 'driver')) {
|
|
throw new InvalidArgumentException('Config section not found!');
|
|
}
|
|
|
|
$driver = $this->configManager->get($section, 'driver');
|
|
|
|
if (!in_array($driver, self::SUPPORTED)) {
|
|
throw new InvalidArgumentException('Unsupported driver.');
|
|
}
|
|
|
|
$dsn = $driver . ':';
|
|
$options = [
|
|
PDO::ATTR_CASE => PDO::CASE_NATURAL,
|
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
|
PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL,
|
|
PDO::ATTR_STRINGIFY_FETCHES => false,
|
|
PDO::ATTR_EMULATE_PREPARES => false,
|
|
];
|
|
|
|
switch ($driver) {
|
|
case 'sqlite':
|
|
if ($this->configManager->get($section, 'memory', 'bool', false)) {
|
|
$dsn .= ':memory:';
|
|
} else {
|
|
$databasePath = realpath(
|
|
$this->configManager->get($section, 'database', 'string', __DIR__ . '/../store/misuzu.db')
|
|
);
|
|
|
|
if ($databasePath === false) {
|
|
throw new \UnexpectedValueException("Database does not exist.");
|
|
}
|
|
|
|
$dsn .= $databasePath . ';';
|
|
}
|
|
break;
|
|
|
|
case 'mysql':
|
|
$is_unix_socket = $this->configManager->contains($section, 'unix_socket');
|
|
|
|
if ($is_unix_socket) {
|
|
$dsn .= 'unix_socket=' . $this->configManager->get($section, 'unix_socket', 'string') . ';';
|
|
} else {
|
|
$dsn .= 'host=' . $this->configManager->get($section, 'host', 'string', self::DEFAULT_HOST) . ';';
|
|
$dsn .= 'port=' . $this->configManager->get($section, 'port', 'int', self::DEFAULT_PORT_MYSQL) . ';';
|
|
}
|
|
|
|
$dsn .= 'charset=' . (
|
|
$this->configManager->contains($section, 'charset')
|
|
? $this->configManager->get($section, 'charset', 'string')
|
|
: 'utf8mb4'
|
|
) . ';';
|
|
|
|
$dsn .= 'dbname=' . $this->configManager->get($section, 'database', 'string', 'misuzu') . ';';
|
|
|
|
$options[PDO::MYSQL_ATTR_INIT_COMMAND] = "SET SESSION sql_mode='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'";
|
|
break;
|
|
}
|
|
|
|
$connection = new PDO(
|
|
$dsn,
|
|
$this->configManager->get($section, 'username', 'string', null),
|
|
$this->configManager->get($section, 'password', 'string', null),
|
|
$options
|
|
);
|
|
|
|
return $this->connections[$name] = $connection;
|
|
}
|
|
}
|