Test Failed
Push — main ( 82933d...ebe982 )
by Rafael
02:28
created

Config::loadConfig()   A

Complexity

Conditions 6
Paths 5

Size

Total Lines 22
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 12
nc 5
nop 1
dl 0
loc 22
rs 9.2222
c 0
b 0
f 0
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 DoliCore\Base\Constants;
0 ignored issues
show
Bug introduced by
The type DoliCore\Base\Constants was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
22
use Exception;
23
use PDO;
24
use stdClass;
25
26
/**
27
 * Manage the configuration file
28
 */
29
abstract class Config
30
{
31
    /**
32
     * Configuration filename.
33
     */
34
    private const CONFIG_FILENAME = 'config.json';
35
36
    /**
37
     * Defines the configuration file structure
38
     */
39
    private const  CONFIG_STRUCTURE = [
40
        'main' => [
41
            'path', // Path to the public folder (usually htdocs)
42
            'url',
43
            'data', // Route to the private folder that stores the documents.
44
            'theme',
45
            'language',
46
        ],
47
        'db' => [
48
            'type',
49
            'host',
50
            'user',
51
            'pass',
52
            'name',
53
            'port',
54
            'prefix',
55
            'charset',
56
            'collation',
57
            'encryption', // Pending review: If true, some database fields are encrypted.
58
            'encrypt_type', // Pending review: Encryption type ('0' if none, '1' if DES and '2' if AES)
59
        ],
60
        'security' => [
61
            'unique_id', // Unique identifier of the installation.
62
            'https', // If true, the use of https is forced (recommended)
63
        ]
64
    ];
65
66
    /**
67
     * Contains configuration file information
68
     *
69
     * @var stdClass|null
70
     */
71
    private static ?stdClass $config = null;
72
73
    /**
74
     * Checks if the connection to the database is possible with the parameters
75
     * defined in the configuration file.
76
     *
77
     * @param $data
78
     * @return bool
79
     */
80
    public static function checkDatabaseConnection($data): bool
81
    {
82
        $dsn = "$data->type:host=$data->host;dbname=$data->name;charset=$data->charset";
83
        try {
84
            $options = [
85
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
86
                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
87
                PDO::ATTR_EMULATE_PREPARES => false,
88
            ];
89
90
            $pdo = new PDO($dsn, $data->user, $data->pass, $options);
91
92
            // Run a simple query to verify the connection
93
            $pdo->query('SELECT 1');
94
        } catch (Exception $e) {
95
            // Catch errors and return false if connection fails
96
            error_log($e->getMessage());
97
            return false;
98
        }
99
        return true;
100
    }
101
102
    public static function getConfig(bool $reload = false): ?stdClass
103
    {
104
        if ($reload || !isset(self::$config)) {
105
            self::$config = self::loadConfig($reload);
106
        }
107
108
        Constants::define(self::$config);
109
110
        return self::$config;
111
    }
112
113
    /**
114
     * Add the configuration parameters received in $data in the configuration file.
115
     *
116
     * @param array $data
117
     * @return bool
118
     */
119
    public static function setConfig(array $data): bool
120
    {
121
        /**
122
         * If the configuration file is empty, we add the parameters
123
         * that we can obtain at runtime (getDefaultMainFileInfo).
124
         */
125
        if (empty(self::$config)) {
126
            self::$config = new stdClass();
127
            self::$config->main = static::getDefaultMainFileInfo();
128
        }
129
130
        foreach (self::CONFIG_STRUCTURE as $section => $values) {
131
            foreach ($values as $key) {
132
                if (!isset($data[$section])) {
133
                    error_log($section . ' is not defined!');
134
                    continue;
135
                }
136
                if (!isset($data[$section][$key])) {
137
                    error_log($key . ' is not defined in ' . $section . '!');
138
                    continue;
139
                }
140
                if (!isset(self::$config->{$section})) {
141
                    self::$config->{$section} = new stdClass();
142
                }
143
                self::$config->{$section}->{$key} = $data[$section][$key];
144
            }
145
        }
146
147
        /**
148
         * Save the configuration in the configuration file.
149
         */
150
        return self::saveConfig();
151
    }
152
153
    /**
154
     * Those configuration parameters that we can obtain at run time,
155
     * or their default values, are obtained.
156
     *
157
     * @return stdClass
158
     */
159
    public static function getDefaultMainFileInfo(): stdClass
160
    {
161
        $result = new stdClass();
162
        $result->path = constant('BASE_PATH');
163
        $result->url = constant('BASE_URL');
164
        return $result;
165
    }
166
167
    /**
168
     * Updates the configuration file with the information it has in memory.
169
     *
170
     * @return bool
171
     */
172
    private static function saveConfig(): bool
173
    {
174
        if (empty(self::$config)) {
175
            return true;
176
        }
177
        return file_put_contents(self::getConfigFilename(), json_encode(self::$config, JSON_PRETTY_PRINT)) !== false;
178
    }
179
180
    /**
181
     * Returns the config.json complete path.
182
     *
183
     * @return string
184
     */
185
    private static function getConfigFilename(): string
186
    {
187
        return realpath(BASE_PATH . '/..') . DIRECTORY_SEPARATOR . self::CONFIG_FILENAME;
0 ignored issues
show
Bug introduced by
The constant Alxarafe\Base\BASE_PATH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
188
    }
189
190
    /**
191
     * Returns a stdClass with the program configuration.
192
     * If the configuration file does not exist, is not accessible, or is not correct, returns null.
193
     * The configuration is loaded from the file only once and stored in a variable. You can set $reload
194
     * to true to force a reload of the configuration file.
195
     *
196
     * @param bool $reload
197
     * @return stdClass|null
198
     */
199
    private static function loadConfig(bool $reload = false): ?stdClass
200
    {
201
        if (!$reload && isset(self::$config)) {
202
            return self::$config;
203
        }
204
205
        $filename = self::getConfigFilename();
206
        if (!file_exists($filename)) {
207
            return null;
208
        }
209
210
        $config = file_get_contents($filename);
211
        if ($config === false) {
212
            return self::$config;
213
        }
214
215
        $result = json_decode($config);
216
        if (json_last_error() === JSON_ERROR_NONE) {
217
            self::$config = $result;
218
        }
219
220
        return $result;
221
    }
222
}
223