Passed
Push — main ( 7af4bb...131f20 )
by Rafael
58:13
created

Config::setDbConfig()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 5
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 8
rs 10
1
<?php
2
3
/* Copyright (C) 2024       Rafael San José         <[email protected]>
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 3 of the License, or
8
 * any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17
 */
18
19
namespace Alxarafe\Base;
20
21
use Exception;
22
use PDO;
23
use stdClass;
24
25
/**
26
 * Manage the configuration file
27
 */
28
abstract class Config
29
{
30
    /**
31
     * Configuration filename.
32
     */
33
    private const CONFIG_FILENAME = 'config.json';
34
35
    /**
36
     * Defines the configuration file structure
37
     */
38
    private const  CONFIG_STRUCTURE = [
39
        'main' => [
40
            'path', // Path to the public folder (usually htdocs)
41
            'url',
42
            'data', // Route to the private folder that stores the documents.
43
            'theme',
44
            'language',
45
        ],
46
        'db' => [
47
            'type',
48
            'host',
49
            'user',
50
            'pass',
51
            'name',
52
            'port',
53
            'prefix',
54
            'charset',
55
            'collation',
56
            'encryption', // Pending review: If true, some database fields are encrypted.
57
            'encrypt_type', // Pending review: Encryption type ('0' if none, '1' if DES and '2' if AES)
58
        ],
59
        'security' => [
60
            'unique_id', // Unique identifier of the installation.
61
            'https', // If true, the use of https is forced (recommended)
62
        ]
63
    ];
64
65
    /**
66
     * Contains configuration file information
67
     *
68
     * @var stdClass|null
69
     */
70
    private static ?stdClass $config = null;
71
72
    /**
73
     * Checks if the connection to the database is possible with the parameters
74
     * defined in the configuration file.
75
     *
76
     * @param $data
77
     * @return bool
78
     */
79
    public static function checkDatabaseConnection($data): bool
80
    {
81
        $dsn = "$data->type:host=$data->host;dbname=$data->name;charset=$data->charset";
82
        try {
83
            $options = [
84
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
85
                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
86
                PDO::ATTR_EMULATE_PREPARES => false,
87
            ];
88
89
            $pdo = new PDO($dsn, $data->user, $data->pass, $options);
90
91
            // Run a simple query to verify the connection
92
            $pdo->query('SELECT 1');
93
        } catch (Exception $e) {
94
            // Catch errors and return false if connection fails
95
            error_log($e->getMessage());
96
            return false;
97
        }
98
        return true;
99
    }
100
101
    /**
102
     * Add the configuration parameters received in $data in the configuration file.
103
     *
104
     * @param array $data
105
     * @return bool
106
     */
107
    public static function setConfig(array $data): bool
108
    {
109
        /**
110
         * If the configuration file is empty, we add the parameters
111
         * that we can obtain at runtime (getDefaultMainFileInfo).
112
         */
113
        if (empty(self::$config)) {
114
            self::$config = new stdClass();
115
            self::$config->main = static::getDefaultMainFileInfo();
116
        }
117
118
        foreach (self::CONFIG_STRUCTURE as $section => $values) {
119
            foreach ($values as $key) {
120
                if (!isset($data[$section])) {
121
                    error_log($section . ' is not defined!');
122
                    continue;
123
                }
124
                if (!isset($data[$section][$key])) {
125
                    error_log($key . ' is not defined in ' . $section . '!');
126
                    continue;
127
                }
128
                if (!isset(self::$config->{$section})) {
129
                    self::$config->{$section} = new stdClass();
130
                }
131
                self::$config->{$section}->{$key} = $data[$section][$key];
132
            }
133
        }
134
135
        /**
136
         * Save the configuration in the configuration file.
137
         */
138
        return self::saveConfig();
139
    }
140
141
    /**
142
     * Those configuration parameters that we can obtain at run time,
143
     * or their default values, are obtained.
144
     *
145
     * @return stdClass
146
     */
147
    public static function getDefaultMainFileInfo(): stdClass
148
    {
149
        $result = new stdClass();
150
        $result->path = constant('BASE_PATH');
151
        $result->url = constant('BASE_URL');
152
        return $result;
153
    }
154
155
    /**
156
     * Updates the configuration file with the information it has in memory.
157
     *
158
     * @return bool
159
     */
160
    private static function saveConfig(): bool
161
    {
162
        if (empty(self::$config)) {
163
            return true;
164
        }
165
        return file_put_contents(self::getConfigFilename(), json_encode(self::$config, JSON_PRETTY_PRINT)) !== false;
166
    }
167
168
    /**
169
     * Returns the config.json complete path.
170
     *
171
     * @return string
172
     */
173
    private static function getConfigFilename(): string
174
    {
175
        return realpath(BASE_PATH . '/..') . DIRECTORY_SEPARATOR . self::CONFIG_FILENAME;
176
    }
177
178
    /**
179
     * Returns a stdClass with the program configuration.
180
     * If the configuration file does not exist, is not accessible, or is not correct, returns null.
181
     * The configuration is loaded from the file only once and stored in a variable. You can set $reload
182
     * to true to force a reload of the configuration file.
183
     *
184
     * @param bool $reload
185
     * @return stdClass|null
186
     */
187
    public static function loadConfig(bool $reload = false): ?stdClass
188
    {
189
        if (!$reload && isset(self::$config)) {
190
            return self::$config;
191
        }
192
193
        $filename = self::getConfigFilename();
194
        if (!file_exists($filename)) {
195
            return null;
196
        }
197
198
        $config = file_get_contents($filename);
199
        if ($config === false) {
200
            return self::$config;
201
        }
202
203
        $result = json_decode($config);
204
        if (json_last_error() === JSON_ERROR_NONE) {
205
            self::$config = $result;
206
        }
207
208
        return $result;
209
    }
210
}
211