248 lines
6.3 KiB
PHP
248 lines
6.3 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
const DB_INSTALLER_CONFIG_FILE = __DIR__ . '/installer-config.php';
|
|
|
|
function db_normalize_settings(array $settings): array
|
|
{
|
|
$host = trim((string)($settings['DB_HOST'] ?? ''));
|
|
$port = trim((string)($settings['DB_PORT'] ?? ''));
|
|
$name = trim((string)($settings['DB_NAME'] ?? ''));
|
|
$user = trim((string)($settings['DB_USER'] ?? ''));
|
|
$password = isset($settings['DB_PASS']) ? (string)$settings['DB_PASS'] : '';
|
|
|
|
return [
|
|
'DB_HOST' => $host !== '' ? $host : 'localhost',
|
|
'DB_PORT' => $port !== '' ? $port : '3306',
|
|
'DB_NAME' => $name,
|
|
'DB_USER' => $user,
|
|
'DB_PASS' => $password,
|
|
];
|
|
}
|
|
|
|
function db_env_settings(): array
|
|
{
|
|
$settings = [];
|
|
|
|
foreach (['DB_HOST', 'DB_PORT', 'DB_NAME', 'DB_USER', 'DB_PASS'] as $key) {
|
|
$value = getenv($key);
|
|
if ($value !== false) {
|
|
$settings[$key] = $value;
|
|
}
|
|
}
|
|
|
|
return db_normalize_settings($settings);
|
|
}
|
|
|
|
function db_local_settings(): array
|
|
{
|
|
static $settings = null;
|
|
|
|
if (is_array($settings)) {
|
|
return $settings;
|
|
}
|
|
|
|
if (!is_file(DB_INSTALLER_CONFIG_FILE)) {
|
|
$settings = db_normalize_settings([]);
|
|
return $settings;
|
|
}
|
|
|
|
$loaded = include DB_INSTALLER_CONFIG_FILE;
|
|
$settings = is_array($loaded) ? db_normalize_settings($loaded) : db_normalize_settings([]);
|
|
|
|
return $settings;
|
|
}
|
|
|
|
function db_settings_complete(array $settings): bool
|
|
{
|
|
return $settings['DB_HOST'] !== ''
|
|
&& $settings['DB_NAME'] !== ''
|
|
&& $settings['DB_USER'] !== '';
|
|
}
|
|
|
|
function db_resolved_settings(): array
|
|
{
|
|
static $settings = null;
|
|
|
|
if (is_array($settings)) {
|
|
return $settings;
|
|
}
|
|
|
|
$env = db_env_settings();
|
|
$local = db_local_settings();
|
|
$envComplete = db_settings_complete($env);
|
|
$localComplete = db_settings_complete($local);
|
|
|
|
if ($envComplete && $localComplete) {
|
|
$error = null;
|
|
if (db_test_connection($env, true, $error)) {
|
|
$env['__source'] = 'env';
|
|
$settings = $env;
|
|
return $settings;
|
|
}
|
|
|
|
$local['__source'] = 'file';
|
|
$settings = $local;
|
|
return $settings;
|
|
}
|
|
|
|
if ($envComplete) {
|
|
$env['__source'] = 'env';
|
|
$settings = $env;
|
|
return $settings;
|
|
}
|
|
|
|
if ($localComplete) {
|
|
$local['__source'] = 'file';
|
|
$settings = $local;
|
|
return $settings;
|
|
}
|
|
|
|
$settings = db_normalize_settings([]);
|
|
$settings['__source'] = 'missing';
|
|
|
|
return $settings;
|
|
}
|
|
|
|
$resolvedDbSettings = db_resolved_settings();
|
|
define('DB_HOST', $resolvedDbSettings['DB_HOST']);
|
|
define('DB_PORT', $resolvedDbSettings['DB_PORT']);
|
|
define('DB_NAME', $resolvedDbSettings['DB_NAME']);
|
|
define('DB_USER', $resolvedDbSettings['DB_USER']);
|
|
define('DB_PASS', $resolvedDbSettings['DB_PASS']);
|
|
unset($resolvedDbSettings);
|
|
|
|
function db_config_source(): string
|
|
{
|
|
return db_resolved_settings()['__source'] ?? 'missing';
|
|
}
|
|
|
|
function db_has_required_config(): bool
|
|
{
|
|
return db_settings_complete(db_resolved_settings());
|
|
}
|
|
|
|
function db_build_dsn(array $settings, bool $withDatabase = true): string
|
|
{
|
|
$settings = db_normalize_settings($settings);
|
|
$dsn = 'mysql:host=' . $settings['DB_HOST'] . ';port=' . $settings['DB_PORT'] . ';charset=utf8mb4';
|
|
|
|
if ($withDatabase && $settings['DB_NAME'] !== '') {
|
|
$dsn .= ';dbname=' . $settings['DB_NAME'];
|
|
}
|
|
|
|
return $dsn;
|
|
}
|
|
|
|
function db_create_pdo(array $settings, bool $withDatabase = true): PDO
|
|
{
|
|
$settings = db_normalize_settings($settings);
|
|
|
|
return new PDO(
|
|
db_build_dsn($settings, $withDatabase),
|
|
$settings['DB_USER'],
|
|
$settings['DB_PASS'],
|
|
[
|
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
|
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
|
PDO::ATTR_TIMEOUT => 5,
|
|
]
|
|
);
|
|
}
|
|
|
|
function db_test_connection(array $settings, bool $withDatabase = true, ?string &$error = null): bool
|
|
{
|
|
try {
|
|
db_create_pdo($settings, $withDatabase)->query('SELECT 1');
|
|
$error = null;
|
|
return true;
|
|
} catch (Throwable $exception) {
|
|
$error = $exception->getMessage();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function db_connection_error(?array $settings = null): ?string
|
|
{
|
|
$settings = $settings !== null ? db_normalize_settings($settings) : db_resolved_settings();
|
|
|
|
if (!db_settings_complete($settings)) {
|
|
return 'Database belum dikonfigurasi lengkap.';
|
|
}
|
|
|
|
$error = null;
|
|
db_test_connection($settings, true, $error);
|
|
|
|
return $error;
|
|
}
|
|
|
|
function db(): PDO
|
|
{
|
|
static $pdo = null;
|
|
|
|
if ($pdo instanceof PDO) {
|
|
return $pdo;
|
|
}
|
|
|
|
if (!db_has_required_config()) {
|
|
throw new RuntimeException('Database belum dikonfigurasi.');
|
|
}
|
|
|
|
$pdo = db_create_pdo(db_resolved_settings(), true);
|
|
return $pdo;
|
|
}
|
|
|
|
function db_error_is_missing_database(string $message): bool
|
|
{
|
|
return stripos($message, 'Unknown database') !== false
|
|
|| str_contains($message, '[1049]');
|
|
}
|
|
|
|
function db_quote_identifier(string $identifier): string
|
|
{
|
|
$identifier = trim($identifier);
|
|
if ($identifier === '' || preg_match('/[[:cntrl:]]/', $identifier)) {
|
|
throw new RuntimeException('Nama database tidak valid.');
|
|
}
|
|
|
|
return '`' . str_replace('`', '``', $identifier) . '`';
|
|
}
|
|
|
|
function db_create_database_if_missing(array $settings): void
|
|
{
|
|
$settings = db_normalize_settings($settings);
|
|
|
|
if ($settings['DB_NAME'] === '') {
|
|
throw new RuntimeException('Nama database wajib diisi.');
|
|
}
|
|
|
|
$pdo = db_create_pdo($settings, false);
|
|
$pdo->exec(
|
|
'CREATE DATABASE IF NOT EXISTS ' . db_quote_identifier($settings['DB_NAME']) .
|
|
' CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci'
|
|
);
|
|
}
|
|
|
|
function db_save_local_settings(array $settings): void
|
|
{
|
|
$settings = db_normalize_settings($settings);
|
|
$payload = "<?php
|
|
";
|
|
$payload .= "declare(strict_types=1);
|
|
|
|
";
|
|
$payload .= 'return ' . var_export([
|
|
'DB_HOST' => $settings['DB_HOST'],
|
|
'DB_PORT' => $settings['DB_PORT'],
|
|
'DB_NAME' => $settings['DB_NAME'],
|
|
'DB_USER' => $settings['DB_USER'],
|
|
'DB_PASS' => $settings['DB_PASS'],
|
|
], true) . ";
|
|
";
|
|
|
|
$bytes = file_put_contents(DB_INSTALLER_CONFIG_FILE, $payload, LOCK_EX);
|
|
if ($bytes === false) {
|
|
throw new RuntimeException('Gagal menyimpan file konfigurasi database. Pastikan folder db bisa ditulis.');
|
|
}
|
|
}
|