Passed
Push — main ( 4c9f29...3d651d )
by Rafael
05:37
created

Config::addNewMessage()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
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
    private static array $messages = [];
73
74
    /**
75
     * Checks if the connection to the database is possible with the parameters
76
     * defined in the configuration file.
77
     *
78
     * @param $data
79
     * @return bool
80
     */
81
    public static function checkDatabaseConnection($data): bool
82
    {
83
        $dsn = "$data->type:host=$data->host;dbname=$data->name;charset=$data->charset";
84
        try {
85
            $options = [
86
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
87
                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
88
                PDO::ATTR_EMULATE_PREPARES => false,
89
            ];
90
91
            $pdo = new PDO($dsn, $data->user, $data->pass, $options);
92
93
            // Run a simple query to verify the connection
94
            $pdo->query('SELECT 1');
95
        } catch (Exception $e) {
96
            // Catch errors and return false if connection fails
97
            $message = $e->getMessage();
98
            static::addNewMessage($message);
99
            error_log($message);
100
            dump(Config::getMessages());
101
            return false;
102
        }
103
        return true;
104
    }
105
106
    /**
107
     * Adds a new message that will then be returned by getMessages.
108
     *
109
     * @param $message
110
     * @return void
111
     */
112
    private static function addNewMessage($message)
113
    {
114
        static::$messages[] = $message;
0 ignored issues
show
Bug introduced by
Since $messages is declared private, accessing it with static will lead to errors in possible sub-classes; you can either use self, or increase the visibility of $messages to at least protected.
Loading history...
115
    }
116
117
    /**
118
     * Gets the information defined in the configuration file.
119
     * To reload the configuration file, set $reload to true.
120
     *
121
     * @param bool $reload
122
     * @return stdClass|null
123
     */
124
    public static function getConfig(bool $reload = false): ?stdClass
125
    {
126
        if ($reload || !isset(self::$config)) {
127
            self::$config = self::loadConfig($reload);
128
        }
129
130
        return self::$config;
131
    }
132
133
    /**
134
     * Add the configuration parameters received in $data in the configuration file.
135
     *
136
     * @param array $data
137
     * @return bool
138
     */
139
    public static function setConfig(array $data): bool
140
    {
141
        /**
142
         * If the configuration file is empty, we add the parameters
143
         * that we can obtain at runtime (getDefaultMainFileInfo).
144
         */
145
        if (empty(self::$config)) {
146
            self::$config = new stdClass();
147
            self::$config->main = static::getDefaultMainFileInfo();
148
        }
149
150
        foreach (self::CONFIG_STRUCTURE as $section => $values) {
151
            foreach ($values as $key) {
152
                if (!isset($data[$section])) {
153
                    error_log($section . ' is not defined!');
154
                    continue;
155
                }
156
                if (!isset($data[$section][$key])) {
157
                    error_log($key . ' is not defined in ' . $section . '!');
158
                    continue;
159
                }
160
                if (!isset(self::$config->{$section})) {
161
                    self::$config->{$section} = new stdClass();
162
                }
163
                self::$config->{$section}->{$key} = $data[$section][$key];
164
            }
165
        }
166
167
        /**
168
         * Save the configuration in the configuration file.
169
         */
170
        return self::saveConfig();
171
    }
172
173
    /**
174
     * Returns a stdClass with the program configuration.
175
     * If the configuration file does not exist, is not accessible, or is not correct, returns null.
176
     * The configuration is loaded from the file only once and stored in a variable. You can set $reload
177
     * to true to force a reload of the configuration file.
178
     *
179
     * @param bool $reload
180
     * @return stdClass|null
181
     */
182
    private static function loadConfig(bool $reload = false): ?stdClass
183
    {
184
        if (!$reload && isset(self::$config)) {
185
            return self::$config;
186
        }
187
188
        $filename = self::getConfigFilename();
189
        if (!file_exists($filename)) {
190
            return null;
191
        }
192
193
        $config = file_get_contents($filename);
194
        if ($config === false) {
195
            return self::$config;
196
        }
197
198
        $result = json_decode($config);
199
        if (json_last_error() === JSON_ERROR_NONE) {
200
            self::$config = $result;
201
        }
202
203
        return $result;
204
    }
205
206
    /**
207
     * Returns the config.json complete path.
208
     *
209
     * @return string
210
     */
211
    private static function getConfigFilename(): string
212
    {
213
        return realpath(constant('BASE_PATH') . '/..') . DIRECTORY_SEPARATOR . self::CONFIG_FILENAME;
214
    }
215
216
    /**
217
     * Those configuration parameters that we can obtain at run time,
218
     * or their default values, are obtained.
219
     *
220
     * @return stdClass
221
     */
222
    public static function getDefaultMainFileInfo(): stdClass
223
    {
224
        $result = new stdClass();
225
        $result->path = constant('BASE_PATH');
226
        $result->url = constant('BASE_URL');
227
        return $result;
228
    }
229
230
    /**
231
     * Updates the configuration file with the information it has in memory.
232
     *
233
     * @return bool
234
     */
235
    private static function saveConfig(): bool
236
    {
237
        if (empty(self::$config)) {
238
            return true;
239
        }
240
        return file_put_contents(self::getConfigFilename(), json_encode(self::$config, JSON_PRETTY_PRINT)) !== false;
241
    }
242
243
    /**
244
     * Returns an array with the messages accumulated since the last call.
245
     *
246
     * @return array
247
     */
248
    public static function getMessages()
249
    {
250
        $result = static::$messages;
0 ignored issues
show
Bug introduced by
Since $messages is declared private, accessing it with static will lead to errors in possible sub-classes; you can either use self, or increase the visibility of $messages to at least protected.
Loading history...
251
        static::$messages = [];
252
        return $result;
253
    }
254
}
255