Passed
Push — 2.x ( 364dd8...03335a )
by Terry
01:58
created

BaseController::_()   B

Complexity

Conditions 10
Paths 7

Size

Total Lines 54
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 39
nc 7
nop 2
dl 0
loc 54
rs 7.6666
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
11
declare(strict_types=1);
12
13
namespace Shieldon\Firewall\Panel;
14
15
use Psr\Http\Message\ResponseInterface;
16
use Shieldon\Firewall\Firewall;
17
use Shieldon\Firewall\FirewallTrait;
18
use Shieldon\Firewall\Panel\DemoModeTrait;
19
use Shieldon\Firewall\Panel\ConfigMethodsTrait;
20
use Shieldon\Firewall\Utils\Container;
21
use Shieldon\Firewall\Log\ActionLogParser;
22
use function Shieldon\Firewall\__;
23
use function Shieldon\Firewall\get_request;
24
use function Shieldon\Firewall\get_response;
25
use function Shieldon\Firewall\get_session;
26
use function Shieldon\Firewall\unset_superglobal;
27
28
use RuntimeException;
29
use function array_push;
30
use function define;
31
use function defined;
32
use function extract;
33
use function file_exists;
34
use function file_put_contents;
35
use function is_array;
36
use function is_numeric;
37
use function is_string;
38
use function json_encode;
39
use function ob_end_clean;
40
use function ob_get_contents;
41
use function ob_start;
42
use function trim;
43
44
/**
45
 * User
46
 */
47
class BaseController
48
{
49
    use FirewallTrait;
50
    use DemoModeTrait;
51
    use ConfigMethodsTrait;
52
53
    /**
54
     * LogPaeser instance.
55
     *
56
     * @var object
57
     */
58
    protected $parser;
59
60
    /**
61
     * Messages.
62
     *
63
     * @var array
64
     */
65
    protected $messages = [];
66
67
    /**
68
     * Check page availability.
69
     *
70
     * @var array
71
     */
72
    protected $pageAvailability = [
73
74
        // Need to implement Action Logger to make it true.
75
        'logs' => false,
76
    ];
77
78
    /**
79
     * see $this->csrf()
80
     *
81
     * @var array
82
     */
83
    protected $csrfField = [];
84
85
    /**
86
     * Language code.
87
     *
88
     * @var string
89
     */
90
    protected $locate = 'en';
91
92
    /**
93
     * Captcha modules.
94
     *
95
     * @var Interface
0 ignored issues
show
Bug introduced by
The type Shieldon\Firewall\Panel\Interface 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...
96
     */
97
    protected $captcha = [];
98
99
    /**
100
     * The base URL of the firewall panel.
101
     *
102
     * @var string
103
     */
104
    public $base = '';
105
106
    /**
107
     * Firewall panel base controller.                  
108
     */
109
    public function __construct() 
110
    {
111
        $firewall = Container::get('firewall');
112
113
        if (!($firewall instanceof Firewall)) {
114
            throw new RuntimeException(
115
                'The Firewall instance should be initialized first.'
116
            );
117
        }
118
119
        $this->mode          = 'managed';
120
        $this->kernel        = $firewall->getKernel();
121
        $this->configuration = $firewall->getConfiguration();
122
        $this->directory     = $firewall->getDirectory();
123
        $this->filename      = $firewall->getFilename();
124
        $this->base          = SHIELDON_PANEL_BASE;
125
126
        if (!empty($this->kernel->logger)) {
127
128
            // We need to know where the logs stored in.
129
            $logDirectory = $this->kernel->logger->getDirectory();
130
131
            // Load ActionLogParser for parsing log files.
132
            $this->parser = new ActionLogParser($logDirectory);
133
134
            $this->pageAvailability['logs'] = true;
135
        }
136
137
        $flashMessage = get_session()->get('flash_messages');
138
139
        // Flash message, use it when redirecting page.
140
        if (!empty($flashMessage)) {
141
            $this->messages = $flashMessage;
0 ignored issues
show
Documentation Bug introduced by
It seems like $flashMessage can also be of type string. However, the property $messages is declared as type array. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
142
            get_session()->remove('flash_messages');
143
        }
144
145
        $this->locate = 'en';
146
147
        $sessionLang = get_session()->get('shieldon_panel_lang');
148
149
        if (!empty($sessionLang)) {
150
            $this->locate = $sessionLang;
151
        }
152
    }
153
154
    /**
155
     * Load view file.
156
     *
157
     * @param string $page The page type. (filename)
158
     * @param array  $data The variables passed to that page.
159
     *
160
     * @return string
161
     */
162
    protected function loadView(string $page, array $data = []): string
163
    {
164
        if (!defined('SHIELDON_VIEW')) {
165
            define('SHIELDON_VIEW', true);
166
        }
167
168
        $viewFilePath =  __DIR__ . '/../../../templates/' . $page . '.php';
169
    
170
        if (!empty($data)) {
171
            extract($data);
172
        }
173
174
        $output = '';
175
    
176
        if (file_exists($viewFilePath)) {
177
            ob_start();
178
            require $viewFilePath;
179
            $output = ob_get_contents();
180
            ob_end_clean();
181
        }
182
183
        return $output;
184
    }
185
186
    /**
187
     * Render the web page with full layout.
188
     *
189
     * @param string $page The page type. (filename)
190
     * @param array  $data The variables passed to that page.
191
     *
192
     * @return ResponseInterface
193
     */
194
    protected function renderPage(string $page, array $data): ResponseInterface
195
    {
196
        $channelName = $this->kernel->driver->getChannel();
197
        $body = [];
198
199
        if (empty($channelName)) {
200
            $channelName = 'default';
201
        }
202
203
        $body['channel_name'] = $channelName;
204
        $body['mode_name'] = $this->mode;
205
        $body['page_url'] = $this->url();
206
        $body['content'] = $this->loadView($page, $data);
207
        $body['title'] = $data['title'] ?? '';
208
209
        $body['title'] .= ' - ' . __('panel', 'title_site_wide', 'Shieldon Firewall');
210
        $body['title'] .= ' v' . SHIELDON_FIREWALL_VERSION;
211
212
        $page = $this->loadView('panel/template', $body);
213
214
        return $this->respond($page);
215
    }
216
217
    /**
218
     * Return the response instance.
219
     *
220
     * @param string $body The content body.
221
     *
222
     * @return ResponseInterface
223
     */
224
    protected function respond(string $body): ResponseInterface
225
    {
226
        $response = get_response();
227
        $stream = $response->getBody();
228
        $stream->write($body);
229
        $stream->rewind();
230
231
        return $response->withBody($stream);
232
    }
233
234
    /**
235
     * Include a view file.
236
     *
237
     * @param string $page The page type. (filename)
238
     * @param array  $data The variables passed to that page.
239
     *
240
     * @return void
241
     */
242
    protected function _include(string $page, array $data = []): void
243
    {
244
        if (!defined('SHIELDON_VIEW')) {
245
            define('SHIELDON_VIEW', true);
246
        }
247
248
        foreach ($data as $k => $v) {
249
            ${$k} = $v;
250
        }
251
252
        require __DIR__ . '/../../../templates/' . $page . '.php';
253
    }
254
255
    /**
256
     * Response message to front.
257
     *
258
     * @param string $type The message status type. error|success
259
     * @param string $text The message body.
260
     *
261
     * @return void
262
     */
263
    protected function pushMessage(string $type, string $text): void
264
    {
265
        $class = $type;
266
267
        if ($type == 'error') {
268
            $class = 'danger';
269
        }
270
271
        array_push($this->messages, [
272
            'type' => $type,
273
            'text' => $text,
274
            'class' => $class,
275
        ]);
276
    }
277
278
    /**
279
     * Return the relative URL.
280
     *
281
     * @param string $path The page's path.
282
     * @param string $tab  Tab.
283
     *
284
     * @return string
285
     */
286
    protected function url(string $path = '', string $tab = ''): string
287
    {
288
        $query = !empty($tab) ? '?tab=' . $tab : '';
289
290
        return '/' . trim($this->base, '/') . '/' . $path . '/' . $query;
291
    }
292
293
    /**
294
     * Output HTML input element with CSRF token.
295
     *
296
     * @return void
297
     */
298
    public function _csrf(): void
299
    {
300
        if (!empty($this->csrfField)) {
301
            foreach ($this->csrfField as $value) {
302
                echo '<input type="hidden" name="' . $value['name'] . '" value="' . $value['value'] . '" id="csrf-field">';
303
            }
304
        }
305
    }
306
307
    /**
308
     * Save the configuration settings to the JSON file.
309
     *
310
     * @return void
311
     */
312
    protected function saveConfig(): void
313
    {
314
        $postParams = get_request()->getParsedBody();
315
316
        $configFilePath = $this->directory . '/' . $this->filename;
317
318
        foreach ($this->csrfField as $csrfInfo) {
319
            if (!empty($csrfInfo['name'])) {
320
                unset_superglobal($csrfInfo['name'], 'post');
321
            }
322
        }
323
324
        if (empty($postParams) || !is_array($postParams) || 'managed' !== $this->mode) {
325
            return;
326
        }
327
328
        $this->saveConfigPrepareSettings($postParams);
329
330
        //  Start checking the availibility of the data driver settings.
331
        $result = true;
332
        $result = $this->saveConfigCheckDataDriver($result);
333
        $result = $this->saveConfigCheckActionLogger($result);
334
        $result = $this->saveConfigCheckIptables($result);
335
336
        // Only update settings while data driver is correctly connected.
337
        if ($result) {
338
            file_put_contents($configFilePath, json_encode($this->configuration));
339
340
            $this->pushMessage('success',
341
                __(
342
                    'panel',
343
                    'success_settings_saved',
344
                    'Settings saved.'
345
                )
346
            );
347
        }
348
    }
349
350
    /**
351
     * Echo the setting string to the template.
352
     *
353
     * @param string $field   Field.
354
     * @param mixed  $defailt Default value.
355
     *
356
     * @return void
357
     */
358
    protected function _(string $field, $default = ''): void
359
    {
360
        if (is_string($this->getConfig($field)) || is_numeric($this->getConfig($field))) {
361
362
            if ('demo' === $this->mode) {
363
364
                // Hide sensitive data because of security concerns.
365
                $hiddenForDemo = [
366
                    'drivers.redis.auth',
367
                    'drivers.file.directory_path',
368
                    'drivers.sqlite.directory_path',
369
                    'drivers.mysql.dbname',
370
                    'drivers.mysql.user',
371
                    'drivers.mysql.pass',
372
                    'captcha_modules.recaptcha.config.site_key',
373
                    'captcha_modules.recaptcha.config.secret_key',
374
                    'loggers.action.config.directory_path',
375
                    'admin.user',
376
                    'admin.pass',
377
                    'admin.last_modified',
378
                    'messengers.telegram.config.api_key',
379
                    'messengers.telegram.config.channel',
380
                    'messengers.sendgrid.config.api_key',
381
                    'messengers.sendgrid.config.sender',
382
                    'messengers.sendgrid.config.recipients',
383
                    'messengers.line_notify.config.access_token',
384
                    'iptables.config.watching_folder',
385
                    'ip6tables.config.watching_folder',
386
                ];
387
388
                if (in_array($field, $hiddenForDemo)) {
389
                    echo __('panel', 'field_not_visible', 'Cannot view this field in demo mode.');
390
                } else {
391
                    echo (!empty($this->getConfig($field))) ? $this->getConfig($field) : $default;
392
                }
393
394
            } else {
395
                echo (!empty($this->getConfig($field))) ? $this->getConfig($field) : $default;
396
            }
397
        } elseif (is_array($this->getConfig($field))) {
398
399
            if ('demo' === $this->mode) {
400
                $hiddenForDemo = [
401
                    'messengers.sendgrid.config.recipients'
402
                ];
403
404
                if (in_array($field, $hiddenForDemo)) {
405
                    echo __('panel', 'field_not_visible', 'Cannot view this field in demo mode.');
406
                } else {
407
                    echo implode("\n", $this->getConfig($field));
0 ignored issues
show
Bug introduced by
It seems like $this->getConfig($field) can also be of type string; however, parameter $pieces of implode() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

407
                    echo implode("\n", /** @scrutinizer ignore-type */ $this->getConfig($field));
Loading history...
408
                }
409
410
            } else {
411
                echo implode("\n", $this->getConfig($field));
412
            }
413
        }
414
    }
415
416
    /**
417
     * Use on HTML checkbox and radio elements.
418
     *
419
     * @param string $value
420
     * @param mixed  $valueChecked
421
     * @param bool   $isConfig
422
     *
423
     * @return void
424
     */
425
    protected function checked(string $value, $valueChecked, bool $isConfig = true): void
426
    {
427
        if ($isConfig) {
428
            if ($this->getConfig($value) === $valueChecked) {
429
                echo 'checked';
430
            } else {
431
                echo '';
432
            }
433
        } else {
434
            if ($value === $valueChecked) {
435
                echo 'checked';
436
            } else {
437
                echo '';
438
            }
439
        }
440
    }
441
442
    /**
443
     * Echo correspondence string on Messenger setting page.
444
     *
445
     * @param string $moduleName
446
     * @param string $echoType
447
     *
448
     * @return void
449
     */
450
    protected function _m(string $moduleName, string $echoType = 'css'): void
451
    {
452
        if ('css' === $echoType) {
453
            echo $this->getConfig('messengers.' . $moduleName . '.confirm_test') ? 'success' : '';
454
        }
455
456
        if ('icon' === $echoType) {
457
            echo $this->getConfig('messengers.' . $moduleName . '.confirm_test') ? '<i class="fas fa-check"></i>' : '<i class="fas fa-exclamation"></i>';
458
        }
459
    }
460
461
    /**
462
     * Use on HTML select elemets.
463
     *
464
     * @param string $value
465
     * @param mixed  $valueChecked
466
     *
467
     * @return void
468
     */
469
    protected function selected(string $value, $valueChecked): void
470
    {
471
        if ($this->getConfig($value) === $valueChecked) {
472
            echo 'selected';
473
        } else {
474
            echo '';
475
        }
476
    }
477
}
478
479