UserFileReader::checkServer()   C
last analyzed

Complexity

Conditions 13
Paths 10

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 13
eloc 9
c 2
b 0
f 0
nc 10
nop 1
dl 0
loc 13
rs 6.6166

How to fix   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
namespace Lagdo\DbAdmin\Config;
4
5
use Jaxon\Config\ConfigReader;
6
use Jaxon\Config\ConfigSetter;
7
8
use function array_map;
9
use function array_filter;
10
use function array_values;
11
use function env;
0 ignored issues
show
introduced by
The function env was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
12
use function in_array;
13
use function is_array;
14
use function is_file;
15
use function is_numeric;
16
use function is_string;
17
use function preg_match;
18
19
class UserFileReader
20
{
21
    /**
22
     * @var string
23
     */
24
    private string $compareRegex = '/^env\(.*\)$/';
25
26
    /**
27
     * @var string
28
     */
29
    private string $captureRegex = '/^env\((.*)\)$/';
30
31
    /**
32
     * The constructor
33
     *
34
     * @param AuthInterface $auth
35
     */
36
    public function __construct(private AuthInterface $auth)
37
    {}
38
39
    /**
40
     * @param string $value
41
     *
42
     * @return bool
43
     */
44
    private function callsEnvVar(string $value): bool
45
    {
46
        return preg_match($this->compareRegex, $value) !== false;
47
    }
48
49
    /**
50
     * @param array $server
51
     *
52
     * @return bool
53
     */
54
    private function checkPortNumber(array $server): bool
55
    {
56
        if (!isset($server['port']) || is_numeric($server['port'])) {
57
            return true;
58
        }
59
        if (!is_string($server['port'])) {
60
            return false;
61
        }
62
        return $this->callsEnvVar($server['port']);
63
    }
64
65
    /**
66
     * @param array $server
67
     *
68
     * @return bool
69
     */
70
    private function checkServer(array $server): bool
71
    {
72
        if (!isset($server['name']) || !isset($server['driver']) ||
73
            !is_string($server['name']) || !is_string($server['driver'])) {
74
            return false;
75
        }
76
        if ($server['driver'] === 'sqlite') {
77
            return isset($server['directory']) && is_string($server['directory']);
78
        }
79
        return isset($server['username']) && isset($server['password']) &&
80
            isset($server['host']) && is_string($server['username']) &&
81
            is_string($server['password']) && is_string($server['host']) &&
82
            $this->checkPortNumber($server);
83
    }
84
85
    /**
86
     * @param array $options
87
     *
88
     * @return bool
89
     */
90
    private function checkUser(array $options): bool
91
    {
92
        $user = $options['id']['user'] ?? null;
93
        return is_string($user) && $this->auth->user() === $user;
94
    }
95
96
    /**
97
     * @param array $options
98
     *
99
     * @return bool
100
     */
101
    private function checkUsers(array $options): bool
102
    {
103
        $users = $options['id']['users'] ?? null;
104
        return is_array($users) && in_array($this->auth->user(), $users);
105
    }
106
107
    /**
108
     * @param array $options
109
     *
110
     * @return bool
111
     */
112
    private function checkRole(array $options): bool
113
    {
114
        $role = $options['id']['role'] ?? null;
115
        return is_string($role) && $this->auth->role() === $role;
116
    }
117
118
    /**
119
     * @param array $options
120
     *
121
     * @return bool
122
     */
123
    private function checkRoles(array $options): bool
124
    {
125
        $roles = $options['id']['roles'] ?? null;
126
        return is_array($roles) && in_array($this->auth->role(), $roles);
127
    }
128
129
    /**
130
     * @param array $options
131
     *
132
     * @return bool
133
     */
134
    private function userMatches(array $options): bool
135
    {
136
        return $this->checkUser($options) || $this->checkUsers($options) ||
137
            $this->checkRole($options) || $this->checkRoles($options);
138
    }
139
140
    /**
141
     * @param string $value
142
     *
143
     * @return mixed
144
     */
145
    private function getOptionValue(string $value): mixed
146
    {
147
        // We need to capture the matching string.
148
        $match = preg_match($this->captureRegex, $value, $matches);
149
        return $match === false || !isset($matches[1]) ? $value : env($matches[1]);
0 ignored issues
show
Bug introduced by
The function env was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

149
        return $match === false || !isset($matches[1]) ? $value : /** @scrutinizer ignore-call */ env($matches[1]);
Loading history...
150
    }
151
152
    /**
153
     * @param array $server
154
     *
155
     * @return array
156
     */
157
    private function getSqliteOptions(array $server): array
158
    {
159
        $server['directory'] = $this->getOptionValue($server['directory']);
160
        return $server;
161
    }
162
163
    /**
164
     * @param array $server
165
     *
166
     * @return array
167
     */
168
    private function getServerOptions(array $server): array
169
    {
170
        $server['host'] = $this->getOptionValue($server['host']);
171
        $server['username'] = $this->getOptionValue($server['username']);
172
        $server['password'] = $this->getOptionValue($server['password']);
173
        if (isset($server['port']) && is_string($server['port'])) {
174
            $server['port'] = $this->getOptionValue($server['port']);
175
        }
176
        return $server;
177
    }
178
179
    /**
180
     * Replace options with values from the .env config.
181
     *
182
     * @param array $values
183
     *
184
     * @return array
185
     */
186
    private function getOptionValues(array $values): array
187
    {
188
        // Callback to filter the servers list on valid entries.
189
        $check = fn(array $server) => $this->checkServer($server);
190
        // Callback to get the server options final values.
191
        $convert = fn(array $server) => $server['driver'] === 'sqlite' ?
192
            $this->getSqliteOptions($server) : $this->getServerOptions($server);
193
        $values['servers'] = array_map($convert,
194
            array_filter($values['servers'] ?? [], $check));
195
196
        return $values;
197
    }
198
199
    /**
200
     * Get the options for the authenticated user.
201
     *
202
     * @param string $configFile
203
     * @param array $defaultOptions
204
     *
205
     * @return array
206
     */
207
    public function getOptions(string $configFile, array $defaultOptions = []): array
208
    {
209
        // If the config file doesn't exists, return an empty array.
210
        if (!is_file($configFile)) {
211
            return [];
212
        }
213
214
        // The key to use for the user options
215
        $userKey = 'user';
216
        // Remove the provider field.
217
        unset($defaultOptions['provider']);
218
219
        $setter = new ConfigSetter();
220
        $reader = new ConfigReader($setter);
221
        $userConfig = $setter->newConfig([$userKey => $defaultOptions]);
222
223
        $config = $reader->load($setter->newConfig(), $configFile);
224
        $commonOptions = $config->getOption('common', null);
225
        if (is_array($commonOptions)) {
226
            $userConfig = $setter->setOptions($userConfig, $commonOptions, $userKey);
227
        }
228
229
        $fallbackOptions = $config->getOption('fallback', null);
230
231
        $userList = $config->getOption('users', []);
232
        $userList = array_values(array_filter($userList,
233
            fn($options) => $this->userMatches($options)));
234
        $userOptions = $userList[0] ?? $fallbackOptions;
235
236
        if (!is_array($userOptions)) {
237
            // Return nothing if no entry is found for the user.
238
            return [];
239
        }
240
241
        // Remove the id field.
242
        unset($userOptions['id']);
243
        $userConfig = $setter->setOptions($userConfig, $userOptions, $userKey);
244
245
        return $this->getOptionValues($userConfig->getOption($userKey));
0 ignored issues
show
Bug introduced by
It seems like $userConfig->getOption($userKey) can also be of type null; however, parameter $values of Lagdo\DbAdmin\Config\Use...ader::getOptionValues() 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

245
        return $this->getOptionValues(/** @scrutinizer ignore-type */ $userConfig->getOption($userKey));
Loading history...
246
    }
247
}
248