Passed
Push — master ( efd5d0...5a212a )
by Alexander
03:17
created

BaseClient::setUserAttributes()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\AuthClient;
6
7
use function is_array;
8
use function is_callable;
9
use Psr\Http\Client\ClientInterface as PsrClientInterface;
10
use Psr\Http\Message\RequestFactoryInterface;
11
use Psr\Http\Message\RequestInterface;
12
use Psr\Http\Message\ResponseInterface;
13
14
use Yiisoft\Yii\AuthClient\Exception\InvalidConfigException;
15
use Yiisoft\Yii\AuthClient\StateStorage\StateStorageInterface;
16
17
/**
18
 * BaseClient is a base Auth Client class.
19
 *
20
 * @see ClientInterface
21
 */
22
abstract class BaseClient implements ClientInterface
23
{
24
    /**
25
     * @var array authenticated user attributes.
26
     */
27
    protected array $userAttributes = [];
28
    /**
29
     * @var array map used to normalize user attributes fetched from external auth service
30
     * in format: normalizedAttributeName => sourceSpecification
31
     * 'sourceSpecification' can be:
32
     * - string, raw attribute name
33
     * - array, pass to raw attribute value
34
     * - callable, PHP callback, which should accept array of raw attributes and return normalized value.
35
     *
36
     * For example:
37
     *
38
     * ```php
39
     * 'normalizeUserAttributeMap' => [
40
     *      'about' => 'bio',
41
     *      'language' => ['languages', 0, 'name'],
42
     *      'fullName' => function ($attributes) {
43
     *          return $attributes['firstName'] . ' ' . $attributes['lastName'];
44
     *      },
45
     *  ],
46
     * ```
47
     */
48
    protected array $normalizeUserAttributeMap = [];
49
    /**
50
     * @var array view options in format: optionName => optionValue
51
     */
52
    protected array $viewOptions;
53
54
    protected PsrClientInterface $httpClient;
55
56
    protected RequestFactoryInterface $requestFactory;
57
58
    /**
59
     * @var StateStorageInterface state storage to be used.
60
     */
61
    private StateStorageInterface $stateStorage;
62
63 22
    public function __construct(
64
        PsrClientInterface $httpClient,
65
        RequestFactoryInterface $requestFactory,
66
        StateStorageInterface $stateStorage
67
    ) {
68 22
        $this->httpClient = $httpClient;
69 22
        $this->requestFactory = $requestFactory;
70 22
        $this->stateStorage = $stateStorage;
71 22
    }
72
73
    /**
74
     * @throws InvalidConfigException
75
     *
76
     * @return array list of user attributes
77
     */
78 6
    public function getUserAttributes(): array
79
    {
80 6
        if ($this->userAttributes === null) {
81
            $this->userAttributes = $this->normalizeUserAttributes($this->initUserAttributes());
82
        }
83
84 6
        return $this->userAttributes;
85
    }
86
87
    /**
88
     * @param array $userAttributes list of user attributes
89
     *
90
     * @throws InvalidConfigException
91
     */
92 6
    public function setUserAttributes(array $userAttributes): void
93
    {
94 6
        $this->userAttributes = $this->normalizeUserAttributes($userAttributes);
95 6
    }
96
97
    /**
98
     * Normalize given user attributes according to {@see normalizeUserAttributeMap}.
99
     *
100
     * @param array $attributes raw attributes.
101
     *
102
     * @throws InvalidConfigException on incorrect normalize attribute map.
103
     *
104
     * @return array normalized attributes.
105
     */
106 6
    protected function normalizeUserAttributes(array $attributes): array
107
    {
108 6
        foreach ($this->getNormalizeUserAttributeMap() as $normalizedName => $actualName) {
109 5
            if (is_scalar($actualName)) {
110 2
                if (array_key_exists($actualName, $attributes)) {
111 2
                    $attributes[$normalizedName] = $attributes[$actualName];
112
                }
113 3
            } elseif (is_callable($actualName)) {
114 1
                $attributes[$normalizedName] = $actualName($attributes);
115 2
            } elseif (is_array($actualName)) {
116 2
                $haystack = $attributes;
117 2
                $searchKeys = $actualName;
118 2
                $isFound = true;
119 2
                while (($key = array_shift($searchKeys)) !== null) {
120 2
                    if (is_array($haystack) && array_key_exists($key, $haystack)) {
121 2
                        $haystack = $haystack[$key];
122
                    } else {
123 1
                        $isFound = false;
124 1
                        break;
125
                    }
126
                }
127 2
                if ($isFound) {
128 2
                    $attributes[$normalizedName] = $haystack;
129
                }
130
            } else {
131
                throw new InvalidConfigException(
132
                    'Invalid actual name "' . gettype($actualName) . '" specified at "' . static::class
133
134
                     . '::normalizeUserAttributeMap"'
135
                );
136
            }
137
        }
138
139 6
        return $attributes;
140
    }
141
142
    /**
143
     * @return array normalize user attribute map.
144
     */
145 6
    public function getNormalizeUserAttributeMap(): array
146
    {
147 6
        if ($this->normalizeUserAttributeMap === null) {
148
            $this->normalizeUserAttributeMap = $this->defaultNormalizeUserAttributeMap();
149
        }
150
151 6
        return $this->normalizeUserAttributeMap;
152
    }
153
154
    /**
155
     * @param array $normalizeUserAttributeMap normalize user attribute map.
156
     */
157 6
    public function setNormalizeUserAttributeMap(array $normalizeUserAttributeMap): void
158
    {
159 6
        $this->normalizeUserAttributeMap = $normalizeUserAttributeMap;
160 6
    }
161
162
    /**
163
     * Returns the default {@see normalizeUserAttributeMap} value.
164
     * Particular client may override this method in order to provide specific default map.
165
     *
166
     * @return array normalize attribute map.
167
     */
168
    protected function defaultNormalizeUserAttributeMap(): array
169
    {
170
        return [];
171
    }
172
173
    /**
174
     * Initializes authenticated user attributes.
175
     *
176
     * @return array auth user attributes.
177
     */
178
    abstract protected function initUserAttributes(): array;
179
180
    /**
181
     * @return array view options in format: optionName => optionValue
182
     */
183 1
    public function getViewOptions(): array
184
    {
185 1
        if ($this->viewOptions === null) {
186
            $this->viewOptions = $this->defaultViewOptions();
187
        }
188
189 1
        return $this->viewOptions;
190
    }
191
192
    /**
193
     * @param array $viewOptions view options in format: optionName => optionValue
194
     */
195 1
    public function setViewOptions(array $viewOptions): void
196
    {
197 1
        $this->viewOptions = $viewOptions;
198 1
    }
199
200
    /**
201
     * Returns the default {@see viewOptions} value.
202
     * Particular client may override this method in order to provide specific default view options.
203
     *
204
     * @return array list of default {@see viewOptions}
205
     */
206
    protected function defaultViewOptions(): array
207
    {
208
        return [];
209
    }
210
211 4
    public function createRequest(string $method, string $uri): RequestInterface
212
    {
213 4
        return $this->requestFactory->createRequest($method, $uri);
214
    }
215
216
    /**
217
     * Sets persistent state.
218
     *
219
     * @param string $key state key.
220
     * @param mixed $value state value
221
     *
222
     * @return $this the object itself
223
     */
224 5
    protected function setState(string $key, $value): self
225
    {
226 5
        $this->stateStorage->set($this->getStateKeyPrefix() . $key, $value);
227 5
        return $this;
228
    }
229
230
    /**
231
     * Returns session key prefix, which is used to store internal states.
232
     *
233
     * @return string session key prefix.
234
     */
235 7
    protected function getStateKeyPrefix(): string
236
    {
237 7
        return static::class . '_' . $this->getName() . '_';
238
    }
239
240
    /**
241
     * Returns persistent state value.
242
     *
243
     * @param string $key state key.
244
     *
245
     * @return mixed state value.
246
     */
247 3
    protected function getState(string $key)
248
    {
249 3
        return $this->stateStorage->get($this->getStateKeyPrefix() . $key);
250
    }
251
252
    /**
253
     * Removes persistent state value.
254
     *
255
     * @param string $key state key.
256
     *
257
     * @return bool success.
258
     */
259
    protected function removeState(string $key): bool
260
    {
261
        return $this->stateStorage->remove($this->getStateKeyPrefix() . $key);
262
    }
263
264
    protected function sendRequest(RequestInterface $request): ResponseInterface
265
    {
266
        return $this->httpClient->sendRequest($request);
267
    }
268
}
269