Passed
Push — 2.x ( f11ac1...7015c1 )
by Terry
02:01
created

ConfigMethodsTrait   A

Complexity

Total Complexity 42

Size/Duplication

Total Lines 464
Duplicated Lines 0 %

Importance

Changes 7
Bugs 0 Features 0
Metric Value
eloc 189
c 7
b 0
f 0
dl 0
loc 464
rs 9.0399
wmc 42

8 Methods

Rating   Name   Duplication   Size   Complexity  
B saveConfigCheckActionLogger() 0 42 6
A saveConfigCheckDataDriver() 0 19 2
B saveConfigPrepareSettings() 0 43 11
A saveCofigCheckDataDriverMySql() 0 48 3
B saveCofigCheckDataDriverSqlLite() 0 73 7
A saveCofigCheckDataDriverRedis() 0 34 3
A saveCofigCheckDataDriverFile() 0 34 4
B saveConfigCheckiptables() 0 53 6

How to fix   Complexity   

Complex Class

Complex classes like ConfigMethodsTrait often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ConfigMethodsTrait, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * This file is part of the Shieldon package.
4
 *
5
 * (c) Terry L. <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 * 
10
 * php version 7.1.0
11
 * 
12
 * @category  Web-security
13
 * @package   Shieldon
14
 * @author    Terry Lin <[email protected]>
15
 * @copyright 2019 terrylinooo
16
 * @license   https://github.com/terrylinooo/shieldon/blob/2.x/LICENSE MIT
17
 * @link      https://github.com/terrylinooo/shieldon
18
 * @see       https://shieldon.io
19
 */
20
21
declare(strict_types=1);
22
23
namespace Shieldon\Firewall\Panel;
24
25
use function Shieldon\Firewall\__;
26
27
use PDO;
28
use PDOException;
29
use Redis;
30
use Exception;
31
use function class_exists;
32
use function file_exists;
33
use function is_numeric;
34
use function is_string;
35
use function is_writable;
36
use function mkdir;
37
use function password_hash;
38
use function preg_split;
39
use function rtrim;
40
use function str_replace;
41
use function umask;
42
43
/*
44
 * @since 2.0.0
45
 */
46
trait ConfigMethodsTrait
47
{
48
    /**
49
     *   Public methods       | Desctiotion
50
     *  ----------------------|---------------------------------------------
51
     *                        | No public methods.
52
     *  ----------------------|---------------------------------------------
53
     */
54
55
    /**
56
     * Get a variable from configuration.
57
     *
58
     * @param string $field The field of the configuration.
59
     *
60
     * @return mixed
61
     */
62
    abstract public function getConfig(string $field);
63
64
    /**
65
     * Set a variable to the configuration.
66
     *
67
     * @param string $field The field of the configuration.
68
     * @param mixed  $value The vale of a field in the configuration.
69
     *
70
     * @return void
71
     */
72
    abstract public function setConfig(string $field, $value): void;
73
74
    /**
75
     * Response message to front.
76
     *
77
     * @param string $type The message status type. error|success
78
     * @param string $text The message body.
79
     *
80
     * @return void
81
     */
82
    abstract protected function pushMessage(string $type, string $text): void;
83
84
    /**
85
     * Parse the POST fields and set them into configuration data structure.
86
     * Used for saveConfig method only.
87
     *
88
     * @param array $postParams The PSR-7 variable of $_POST
89
     *
90
     * @return void
91
     */
92
    protected function saveConfigPrepareSettings(array $postParams): void
93
    {
94
        foreach ($postParams as $postKey => $postData) {
95
96
            if (is_string($postData)) {
97
                if ($postData === 'on') {
98
                    $this->setConfig(str_replace('__', '.', $postKey), true);
99
100
                } elseif ($postData === 'off') {
101
                    $this->setConfig(str_replace('__', '.', $postKey), false);
102
103
                } elseif ($postKey === 'ip_variable_source') {
104
                    $this->setConfig('ip_variable_source.REMOTE_ADDR', false);
105
                    $this->setConfig('ip_variable_source.HTTP_CF_CONNECTING_IP', false);
106
                    $this->setConfig('ip_variable_source.HTTP_X_FORWARDED_FOR', false);
107
                    $this->setConfig('ip_variable_source.HTTP_X_FORWARDED_HOST', false);
108
                    $this->setConfig('ip_variable_source.' . $postData, true);
109
110
                } elseif ($postKey === 'dialog_ui__shadow_opacity') {
111
                    $this->setConfig('dialog_ui.shadow_opacity', (string) $postData);
112
113
                } elseif ($postKey === 'admin__pass') {
114
                    if (strlen($postParams['admin__pass']) < 58) {
115
                        $this->setConfig('admin.pass', password_hash($postData, PASSWORD_BCRYPT));
116
                    }
117
118
                } else if (strpos($postKey, 'config__recipients') !== false) {
119
                    // For example: 
120
                    // messengers__sendgrid__config__recipients
121
                    // => messengers.sendgrid.config.recipients
122
                    $this->setConfig(
123
                        str_replace('__', '.', $postKey),
124
                        preg_split(
125
                            '/\r\n|[\r\n]/',
126
                            $postData
127
                        )
128
                    );
129
130
                } elseif (is_numeric($postData)) {
131
                    $this->setConfig(str_replace('__', '.', $postKey), (int) $postData);
132
133
                } else {
134
                    $this->setConfig(str_replace('__', '.', $postKey), $postData);
135
                }
136
            }
137
        }
138
    }
139
140
    /**
141
     * Check the settings of Action Logger.
142
     * 
143
     * @param bool $result The result passed from previous check.
144
     *
145
     * @return bool
146
     */
147
    protected function saveConfigCheckActionLogger(bool $result): bool
148
    {
149
        if (!$result) {
150
            return false;
151
        }
152
153
        // Check Action Logger settings.
154
        $enableActionLogger = $this->getConfig('loggers.action.enable');
155
156
        $actionLogDir = rtrim($this->getConfig('loggers.action.config.directory_path'), '\\/ ');
157
158
        if ($enableActionLogger) {
159
            if (empty($actionLogDir)) {
160
                $actionLogDir = $this->directory . '/action_logs';
161
            }
162
163
            $this->setConfig('loggers.action.config.directory_path', $actionLogDir);
164
165
            if (!is_dir($actionLogDir)) {
166
                // @codeCoverageIgnoreStart
167
                $originalUmask = umask(0);
168
                mkdir($actionLogDir, 0777, true);
169
                umask($originalUmask);
170
                // @codeCoverageIgnoreEnd
171
            }
172
173
            if (!is_writable($actionLogDir)) {
174
                // @codeCoverageIgnoreStart
175
                $result = false;
176
                $this->pushMessage(
177
                    'error',
178
                    __(
179
                        'panel',
180
                        'error_logger_directory_not_writable',
181
                        'Action Logger requires the storage directory writable.'
182
                    )
183
                );
184
                // @codeCoverageIgnoreEnd
185
            }
186
        }
187
188
        return $result;
189
    }
190
191
    /**
192
     * Check the settings of iptables.
193
     * 
194
     * @param bool $result The result passed from previous check.
195
     *
196
     * @return bool
197
     */
198
    protected function saveConfigCheckiptables(bool $result): bool
199
    {
200
        if (!$result) {
201
            return false;
202
        }
203
204
        // System firewall.
205
        $enableiptables = $this->getConfig('iptables.enable');
206
        $iptablesWatchingFolder = rtrim($this->getConfig('iptables.config.watching_folder'), '\\/ ');
207
208
        if ($enableiptables) {
209
            if (empty($iptablesWatchingFolder)) {
210
                // @codeCoverageIgnoreStart
211
                $iptablesWatchingFolder = $this->directory . '/iptables';
212
                // @codeCoverageIgnoreEnd
213
            }
214
215
            $this->setConfig('iptables.config.watching_folder', $iptablesWatchingFolder);
216
217
            if (!is_dir($iptablesWatchingFolder)) {
218
                // @codeCoverageIgnoreStart
219
                $originalUmask = umask(0);
220
                mkdir($iptablesWatchingFolder, 0777, true);
221
                umask($originalUmask);
222
                // @codeCoverageIgnoreEnd
223
            }
224
    
225
            // Create default log files.
226
            if (is_writable($iptablesWatchingFolder)) {
227
                fopen($iptablesWatchingFolder . '/iptables_queue.log', 'w+');
228
                fopen($iptablesWatchingFolder . '/ipv4_status.log',    'w+');
229
                fopen($iptablesWatchingFolder . '/ipv6_status.log',    'w+');
230
                fopen($iptablesWatchingFolder . '/ipv4_command.log',   'w+');
231
                fopen($iptablesWatchingFolder . '/ipv6_command.log',   'w+');
232
233
                return $result;
234
            }
235
236
            // @codeCoverageIgnoreStart
237
            $result = false;
238
239
            $this->pushMessage(
240
                'error',
241
                __(
242
                    'panel',
243
                    'error_ip6tables_directory_not_writable',
244
                    'iptables watching folder requires the storage directory writable.'
245
                )
246
            );
247
            // @codeCoverageIgnoreEnd
248
        }
249
250
        return $result;
251
    }
252
253
    /**
254
     * Check the settings of Data drivers.
255
     *
256
     * @param bool $result The result passed from previous check.
257
     *
258
     * @return bool
259
     */
260
    protected function saveConfigCheckDataDriver(bool $result): bool
261
    {
262
        if (!$result) {
263
            return false;
264
        }
265
266
        $type = $this->getConfig('driver_type');
267
268
        $methods = [
269
            'mysql'  => 'saveCofigCheckDataDriverMySql',
270
            'sqlite' => 'saveCofigCheckDataDriverSqlLite',
271
            'redis'  => 'saveCofigCheckDataDriverRedis',
272
            'file'   => 'saveCofigCheckDataDriverFile',
273
        ];
274
275
        $method = $methods[$type];
276
        $result = $this->{$method}($result);
277
278
        return $result;
279
    }
280
281
    /**
282
     * Check the settings of Data drivers.
283
     *
284
     * @param bool $result The result passed from previous check.
285
     *
286
     * @return bool
287
     */
288
    protected function saveCofigCheckDataDriverMySql(bool $result): bool
289
    {
290
        if (class_exists('PDO')) {
291
292
            $db = [
293
                'host'    => $this->getConfig('drivers.mysql.host'),
294
                'dbname'  => $this->getConfig('drivers.mysql.dbname'),
295
                'user'    => $this->getConfig('drivers.mysql.user'),
296
                'pass'    => $this->getConfig('drivers.mysql.pass'),
297
                'charset' => $this->getConfig('drivers.mysql.charset'),
298
            ];
299
300
            try {
301
                new PDO(
302
                    'mysql:host=' . $db['host'] . ';dbname=' . $db['dbname'] . ';charset=' . $db['charset'],
303
                    (string) $db['user'],
304
                    (string) $db['pass']
305
                );
306
            } catch (PDOException $e) {
307
308
                $result = false;
309
310
                $this->pushMessage(
311
                    'error', 
312
                    __(
313
                        'panel',
314
                        'error_mysql_connection',
315
                        'Cannot access to your MySQL database, please check your settings.'
316
                    )
317
                );
318
            }
319
            return $result;
320
        }
321
322
        // @codeCoverageIgnoreStart
323
324
        $result = false;
325
326
        $this->pushMessage(
327
            'error',
328
            __(
329
                'panel',
330
                'error_mysql_driver_not_supported',
331
                'Your system doesn’t support MySQL driver.'
332
            )
333
        );
334
335
        return $result;
336
337
        // @codeCoverageIgnoreEnd
338
    }
339
340
    /**
341
     * Check the settings of Data drivers.
342
     *
343
     * @param bool $result The result passed from previous check.
344
     *
345
     * @return bool
346
     */
347
    protected function saveCofigCheckDataDriverSqlLite(bool $result): bool
348
    {
349
        $fileDir = rtrim($this->getConfig('drivers.file.directory_path'), '\\/ ');
350
351
        if (empty($fileDir)) {
352
            $fileDir = $this->directory . '/data_driver_file';
353
            $this->setConfig('drivers.file.directory_path', $fileDir);
354
        }
355
356
        $this->setConfig('drivers.file.directory_path', $fileDir);
357
358
        $sqliteDir = rtrim($this->getConfig('drivers.sqlite.directory_path'), '\\/ ');
359
360
        if (empty($sqliteDir)) {
361
            $sqliteDir = $this->directory . '/data_driver_sqlite';
362
            $this->setConfig('drivers.sqlite.directory_path', $sqliteDir);
363
        }
364
365
        $sqliteFilePath = $sqliteDir . '/shieldon.sqlite3';
366
367
        if (!is_dir($sqliteDir)) {
368
            // @codeCoverageIgnoreStart
369
            $originalUmask = umask(0);
370
            mkdir($sqliteDir, 0777, true);
371
            umask($originalUmask);
372
            // @codeCoverageIgnoreEnd
373
        }
374
375
        if (class_exists('PDO')) {
376
377
            try {
378
                new PDO('sqlite:' . $sqliteFilePath);
379
380
                // @codeCoverageIgnoreStart
381
            } catch (PDOException $e) { 
382
                $this->pushMessage('error', $e->getMessage());
383
                $result = false;
384
            }
385
386
            // @codeCoverageIgnoreEnd
387
388
            if (!is_writable($sqliteFilePath)) {
389
390
                // @codeCoverageIgnoreStart
391
                $this->pushMessage(
392
                    'error',
393
                    __(
394
                        'panel',
395
                        'error_sqlite_directory_not_writable',
396
                        'SQLite data driver requires the storage directory writable.'
397
                    )
398
                );
399
                $result = false;
400
                // @codeCoverageIgnoreEnd
401
            }
402
403
            return $result;
404
        }
405
406
        // @codeCoverageIgnoreStart
407
408
        $result = false;
409
410
        $this->pushMessage(
411
            'error',
412
            __(
413
                'panel',
414
                'error_sqlite_driver_not_supported',
415
                'Your system doesn’t support SQLite driver.'
416
            )
417
        );
418
419
        return $result;
420
421
        // @codeCoverageIgnoreEnd
422
    }
423
424
    /**
425
     * Check the settings of Data drivers.
426
     *
427
     * @param bool $result The result passed from previous check.
428
     *
429
     * @return bool
430
     */
431
    protected function saveCofigCheckDataDriverRedis(bool $result): bool
432
    {
433
        if (class_exists('Redis')) {
434
435
            try {
436
                $redis = new Redis();
437
                $redis->connect(
438
                    (string) $this->getConfig('drivers.redis.host'), 
439
                    (int)    $this->getConfig('drivers.redis.port')
440
                );
441
                unset($redis);
442
443
            } catch (Exception $e) {
444
                $this->pushMessage('error', $e->getMessage());
445
                $result = false;
446
            }
447
448
            return $result;
449
        }
450
451
        // @codeCoverageIgnoreStart
452
453
        $result = false;
454
455
        $this->pushMessage(
456
            'error',
457
            __(
458
                'panel',
459
                'error_redis_driver_not_supported',
460
                'Your system doesn’t support Redis driver.'
461
            )
462
        );
463
464
        return $result;
465
466
        // @codeCoverageIgnoreEnd
467
    }
468
469
    /**
470
     * Check the settings of Data drivers.
471
     *
472
     * @param bool $result The result passed from previous check.
473
     *
474
     * @return bool
475
     */
476
    protected function saveCofigCheckDataDriverFile(bool $result): bool
477
    {
478
        $fileDir = rtrim($this->getConfig('drivers.file.directory_path'), '\\/ ');
479
480
        if (empty($fileDir)) {
481
            $fileDir = $this->directory . '/data_driver_file';
482
            $this->setConfig('drivers.file.directory_path', $fileDir);
483
        }
484
485
        $this->setConfig('drivers.file.directory_path', $fileDir);
486
487
        if (!is_dir($fileDir)) {
488
            // @codeCoverageIgnoreStart
489
            $originalUmask = umask(0);
490
            mkdir($fileDir, 0777, true);
491
            umask($originalUmask);
492
            // @codeCoverageIgnoreEnd
493
        }
494
495
        if (!is_writable($fileDir)) {
496
            // @codeCoverageIgnoreStart
497
            $result = false;
498
            $this->pushMessage(
499
                'error',
500
                __(
501
                    'panel',
502
                    'error_file_directory_not_writable',
503
                    'File data driver requires the storage directory writable.'
504
                )
505
            );
506
            // @codeCoverageIgnoreEnd
507
        }
508
509
        return $result;
510
    }
511
}
512