Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
96.67% covered (success)
96.67%
29 / 30
80.00% covered (warning)
80.00%
4 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
InstallerConfigLoader
96.55% covered (success)
96.55%
28 / 29
80.00% covered (warning)
80.00%
4 / 5
20
0.00% covered (danger)
0.00%
0 / 1
 load
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
7
 resolveTargetDbName
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
5.93
 isClientContext
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
6
 setConfig
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 reset
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5/** @license For full copyright and license information, please see the LICENSE.md file. */
6
7namespace App\Shared\Infrastructure\Config;
8
9defined('AMMONLY_APP') || exit('Direct script access is forbidden.');
10
11/**
12 * Centralized Installer Configuration Loader.
13 *
14 * Provides thread-safe, static cached access to database installer configuration.
15 *
16 * @package App\Shared\Infrastructure\Config
17 */
18final class InstallerConfigLoader
19{
20    /** @var array<string, mixed>|null */
21    private static ?array $cachedFileConfig = null;
22
23    /** @var array<string, mixed>|null */
24    private static ?array $config = null;
25
26    /**
27     * Loads and caches installer database configuration.
28     *
29     * @return array<string, mixed> Configuration dictionary.
30     */
31    public static function load(): array
32    {
33        if (self::$config !== null) {
34            return self::$config;
35        }
36
37        $configFile = dirname(__DIR__, 4) . '/database/config/installer.php';
38        if (!file_exists($configFile)) {
39            self::$config = [];
40            return self::$config;
41        }
42
43        if (self::$cachedFileConfig !== null) {
44            self::$config = self::$cachedFileConfig;
45        } else {
46            $loaded = (static fn(string $f) => require_once $f)($configFile);
47            self::$cachedFileConfig = is_array($loaded) ? $loaded : [];
48            self::$config = self::$cachedFileConfig;
49        }
50
51        if (isset(self::$config['db']) && is_array(self::$config['db'])) {
52            self::$config['db']['dbname'] = self::resolveTargetDbName();
53        }
54
55        return self::$config;
56    }
57
58    /**
59     * Resolves target database name based on environment and request context.
60     */
61    private static function resolveTargetDbName(): string
62    {
63        if (isset($_ENV['DB_NAME']) && is_string($_ENV['DB_NAME']) && $_ENV['DB_NAME'] !== '') {
64            return $_ENV['DB_NAME'];
65        }
66
67        return self::isClientContext() ? 'ammonly_client' : 'ammonly_admin';
68    }
69
70    /**
71     * Determines whether the current context is running under client profile.
72     */
73    public static function isClientContext(): bool
74    {
75        $host = (string) ($_SERVER['HTTP_HOST'] ?? '');
76        $docRoot = (string) ($_SERVER['DOCUMENT_ROOT'] ?? '');
77        $appProfile = (string) ($_ENV['APP_PROFILE'] ?? $_SERVER['APP_PROFILE'] ?? '');
78        $dir = __DIR__;
79        $cwd = (string) (getcwd() ?: '');
80
81        return stripos($host, 'client') !== false
82            || stripos($docRoot, 'client') !== false
83            || stripos($dir, 'app-client') !== false
84            || stripos($cwd, 'app-client') !== false
85            || $appProfile === 'client';
86    }
87
88    /**
89     * Injects custom configuration dictionary (primarily for unit testing).
90     *
91     * @param array<string, mixed>|null $config Configuration array or null.
92     */
93    public static function setConfig(?array $config): void
94    {
95        self::$config = $config;
96    }
97
98    /**
99     * Resets cached config state (primarily for unit testing).
100     */
101    public static function reset(): void
102    {
103        self::$config = null;
104    }
105}