Test Failed
Push — main ( 894963...0f30b5 )
by Rafael
05:41
created

Config::runMigrations()   B

Complexity

Conditions 6
Paths 10

Size

Total Lines 31
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 20
dl 0
loc 31
rs 8.9777
c 0
b 0
f 0
cc 6
nc 10
nop 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 Alxarafe\Lib\Routes;
22
use Alxarafe\Lib\Trans;
23
use Alxarafe\Tools\Debug;
24
use CoreModules\Admin\Model\Migration;
25
use DebugBar\DebugBarException;
26
use stdClass;
27
28
/**
29
 * Manage the configuration file
30
 */
31
abstract class Config
32
{
33
    /**
34
     * Configuration filename.
35
     */
36
    private const CONFIG_FILENAME = 'config.json';
37
38
    /**
39
     * Defines the configuration file structure
40
     */
41
    public const  CONFIG_STRUCTURE = [
42
        'main' => [
43
            'path', // Path to the public folder (usually htdocs)
44
            'url',
45
            'data', // Route to the private folder that stores the documents.
46
            'theme',
47
            'language',
48
        ],
49
        'db' => [
50
            'type',
51
            'host',
52
            'user',
53
            'pass',
54
            'name',
55
            'port',
56
            'prefix',
57
            'charset',
58
            'collation',
59
            'encryption', // Pending review: If true, some database fields are encrypted.
60
            'encrypt_type', // Pending review: Encryption type ('0' if none, '1' if DES and '2' if AES)
61
        ],
62
        'security' => [
63
            'debug',
64
            'unique_id', // Unique identifier of the installation.
65
            'https', // If true, the use of https is forced (recommended)
66
            'jwt_secret_key',
67
        ]
68
    ];
69
70
    /**
71
     * Contains configuration file information
72
     *
73
     * @var stdClass|null
74
     */
75
    private static ?stdClass $config = null;
76
77
    private static array $messages = [];
78
79
    /**
80
     * Returns an array with the messages accumulated since the last call.
81
     *
82
     * @return array
83
     */
84
    public static function getMessages()
85
    {
86
        $result = self::$messages;
87
        self::$messages = [];
88
        return $result;
89
    }
90
91
    /**
92
     * Gets the information defined in the configuration file.
93
     * To reload the configuration file, set $reload to true.
94
     *
95
     * @param bool $reload
96
     * @return stdClass|null
97
     */
98
    public static function getConfig(bool $reload = false): ?stdClass
99
    {
100
        if ($reload || !isset(self::$config)) {
101
            self::$config = self::loadConfig($reload);
102
        }
103
104
        return self::$config;
105
    }
106
107
    /**
108
     * Add the configuration parameters received in $data in the configuration file.
109
     *
110
     * @param stdClass $data
111
     * @return bool
112
     * @throws DebugBarException
113
     */
114
    public static function setConfig(stdClass $data): bool
115
    {
116
        /**
117
         * If the configuration file is empty, we add the parameters
118
         * that we can obtain at runtime (getDefaultMainFileInfo).
119
         */
120
        if (empty(self::$config)) {
121
            self::$config = new stdClass();
122
            self::$config->main = static::getDefaultMainFileInfo();
123
        }
124
125
        foreach (self::CONFIG_STRUCTURE as $section => $values) {
126
            foreach ($values as $key) {
127
                if (!isset($data->$section)) {
128
                    error_log($section . ' is not defined!');
129
                    continue;
130
                }
131
                if (!isset($data->$section->$key)) {
132
                    error_log($key . ' is not defined in ' . $section . '!');
133
                    continue;
134
                }
135
                if (!isset(self::$config->$section)) {
136
                    self::$config->$section = new stdClass();
137
                }
138
                self::$config->$section->$key = $data->$section->$key;
139
            }
140
        }
141
142
        /**
143
         * Save the configuration in the configuration file.
144
         */
145
        Trans::setLang(self::$config->main->language ?? Trans::FALLBACK_LANG);
146
        $ok = self::saveConfig();
147
        self::getConfig(true);
148
        Debug::initialize(true);
149
        return $ok;
150
    }
151
152
    /**
153
     * Those configuration parameters that we can obtain at run time,
154
     * or their default values, are obtained.
155
     *
156
     * @return stdClass
157
     */
158
    public static function getDefaultMainFileInfo(): stdClass
159
    {
160
        $result = new stdClass();
161
        $result->path = constant('BASE_PATH');
162
        $result->url = constant('BASE_URL');
163
        return $result;
164
    }
165
166
    /**
167
     * Updates the configuration file with the information it has in memory.
168
     *
169
     * @return bool
170
     */
171
    private static function saveConfig(): bool
172
    {
173
        if (empty(self::$config)) {
174
            return true;
175
        }
176
        return file_put_contents(self::getConfigFilename(), json_encode(self::$config, JSON_PRETTY_PRINT)) !== false;
177
    }
178
179
    /**
180
     * Returns the config.json complete path.
181
     *
182
     * @return string
183
     */
184
    private static function getConfigFilename(): string
185
    {
186
        return realpath(constant('BASE_PATH') . '/..') . DIRECTORY_SEPARATOR . self::CONFIG_FILENAME;
187
    }
188
189
    public static function runMigrations(): void
190
    {
191
        $routes = Routes::getAllRoutes();
192
        if (empty($routes['Migrations'])) {
193
            return;
194
        }
195
196
        $migrations = $routes['Migrations'];
197
198
        $result = [];
199
        foreach ($migrations as $module => $data) {
200
            foreach ($data as $filename => $migration) {
201
                $route_array = explode('|', $migration);
202
                $filepath = $route_array[1];
203
204
                $result[$filename . '@' . $module] = $filepath;
205
            }
206
        }
207
        ksort($result);
208
209
        $batch = 1 + Migration::getLastBatch();
210
        foreach ($result as $filename => $filepath) {
211
            if (Migration::where(['migration' => $filename])->first()) {
212
                continue;
213
            }
214
215
            $migration = require_once $filepath;
216
            $migration->up();
217
            Migration::create([
218
                'migration' => $filename,
219
                'batch' => $batch,
220
            ]);
221
        }
222
    }
223
224
    public static function runSeeders()
225
    {
226
        $routes = Routes::getAllRoutes();
227
        if (empty($routes['Seeders'])) {
228
            return;
229
        }
230
231
        $seeders = $routes['Seeders'];
232
233
        $result = [];
0 ignored issues
show
Unused Code introduced by
The assignment to $result is dead and can be removed.
Loading history...
234
        foreach ($seeders as $data) {
235
            foreach ($data as $seeder) {
236
                $route_array = explode('|', $seeder);
237
                $classname = $route_array[0];
238
                new $classname();
239
            }
240
        }
241
242
    }
243
244
    /**
245
     * Adds a new message that will then be returned by getMessages.
246
     *
247
     * @param $message
248
     * @return void
249
     */
250
    private static function addNewMessage($message)
0 ignored issues
show
Unused Code introduced by
The method addNewMessage() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
251
    {
252
        self::$messages[] = $message;
253
    }
254
255
    /**
256
     * Returns a stdClass with the program configuration.
257
     * If the configuration file does not exist, is not accessible, or is not correct, returns null.
258
     * The configuration is loaded from the file only once and stored in a variable. You can set $reload
259
     * to true to force a reload of the configuration file.
260
     *
261
     * @param bool $reload
262
     * @return stdClass|null
263
     */
264
    private static function loadConfig(bool $reload = false): ?stdClass
265
    {
266
        if (!$reload && isset(self::$config)) {
267
            return self::$config;
268
        }
269
270
        $filename = self::getConfigFilename();
271
        if (!file_exists($filename)) {
272
            return null;
273
        }
274
275
        $config = file_get_contents($filename);
276
        if ($config === false) {
277
            return self::$config;
278
        }
279
280
        $result = json_decode($config);
281
        if (json_last_error() === JSON_ERROR_NONE) {
282
            self::$config = $result;
283
        }
284
285
        return $result;
286
    }
287
}
288