Failed Conditions
Push — master ( c24b3a...b512e9 )
by Florent
14:42
created

Client   B

Complexity

Total Complexity 42

Size/Duplication

Total Lines 370
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 11

Importance

Changes 0
Metric Value
wmc 42
lcom 1
cbo 11
dl 0
loc 370
rs 8.295
c 0
b 0
f 0

27 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A getSchema() 0 4 1
A createEmpty() 0 4 1
A create() 0 12 1
A getOwnerId() 0 4 1
A withOwnerId() 0 13 2
A withParameters() 0 9 1
A markAsDeleted() 0 9 1
A isDeleted() 0 4 1
A isGrantTypeAllowed() 0 9 3
A isResponseTypeAllowed() 0 9 3
A isPublic() 0 4 1
A getTokenEndpointAuthenticationMethod() 0 8 2
A getClientCredentialsExpiresAt() 0 8 2
A areClientCredentialsExpired() 0 8 2
A getPublicId() 0 8 2
A has() 0 4 1
A get() 0 8 2
A all() 0 7 1
A jsonSerialize() 0 13 2
A createFromJson() 0 15 2
A apply() 0 13 4
A getEventMap() 0 9 1
A applyClientCreatedEvent() 0 9 1
A applyClientOwnerChangedEvent() 0 7 1
A applyClientDeletedEvent() 0 7 1
A applyClientParametersUpdatedEvent() 0 7 1

How to fix   Complexity   

Complex Class

Complex classes like Client 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 Client, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * The MIT License (MIT)
7
 *
8
 * Copyright (c) 2014-2018 Spomky-Labs
9
 *
10
 * This software may be modified and distributed under the terms
11
 * of the MIT license.  See the LICENSE file for details.
12
 */
13
14
namespace OAuth2Framework\Component\Core\Client;
15
16
use OAuth2Framework\Component\Core\Client\Event as ClientEvent;
17
use OAuth2Framework\Component\Core\DataBag\DataBag;
18
use OAuth2Framework\Component\Core\Event\Event;
19
use OAuth2Framework\Component\Core\ResourceOwner\ResourceOwnerId;
20
use OAuth2Framework\Component\Core\ResourceOwner\ResourceOwner;
21
use OAuth2Framework\Component\Core\UserAccount\UserAccountId;
22
use OAuth2Framework\Component\Core\Domain\DomainObject;
23
use SimpleBus\Message\Recorder\ContainsRecordedMessages;
24
use SimpleBus\Message\Recorder\PrivateMessageRecorderCapabilities;
25
26
/**
27
 * Class ClientCredentials.
28
 *
29
 * This class is used for every client types.
30
 * A client is a resource owner with a set of allowed grant types and can perform requests against
31
 * available endpoints.
32
 */
33
class Client implements ResourceOwner, ContainsRecordedMessages, DomainObject
0 ignored issues
show
Coding Style introduced by
Since you have declared the constructor as private, maybe you should also declare the class as final.
Loading history...
34
{
35
    use PrivateMessageRecorderCapabilities;
36
37
    /**
38
     * @var bool
39
     */
40
    private $deleted = false;
41
42
    /**
43
     * @var UserAccountId|null
44
     */
45
    private $ownerId = null;
46
47
    /**
48
     * @var ClientId|null
49
     */
50
    private $clientId = null;
51
52
    /**
53
     * @var DataBag
54
     */
55
    protected $parameters;
56
57
    /**
58
     * ClientCredentials constructor.
59
     */
60
    private function __construct()
61
    {
62
        $this->parameters = DataBag::create([]);
63
    }
64
65
    /**
66
     * {@inheritdoc}
67
     */
68
    public static function getSchema(): string
69
    {
70
        return 'https://oauth2-framework.spomky-labs.com/schemas/model/client/1.0/schema';
71
    }
72
73
    /**
74
     * @return Client
75
     */
76
    public static function createEmpty(): self
77
    {
78
        return new self();
79
    }
80
81
    /**
82
     * @param ClientId           $clientId
83
     * @param DataBag            $parameters
84
     * @param UserAccountId|null $ownerId
85
     *
86
     * @return Client
87
     */
88
    public function create(ClientId $clientId, DataBag $parameters, ? UserAccountId $ownerId): self
89
    {
90
        $clone = clone $this;
91
        $clone->clientId = $clientId;
92
        $clone->parameters = $parameters;
93
        $clone->ownerId = $ownerId;
94
95
        $event = ClientEvent\ClientCreatedEvent::create($clone->clientId, $parameters, $ownerId);
96
        $clone->record($event);
97
98
        return $clone;
99
    }
100
101
    /**
102
     * @return UserAccountId|null
103
     */
104
    public function getOwnerId(): ? UserAccountId
105
    {
106
        return $this->ownerId;
107
    }
108
109
    /**
110
     * @param UserAccountId $ownerId
111
     *
112
     * @return Client
113
     */
114
    public function withOwnerId(UserAccountId $ownerId): self
115
    {
116
        if ($this->getOwnerId()->getValue() === $ownerId->getValue()) {
117
            return $this;
118
        }
119
120
        $clone = clone $this;
121
        $clone->ownerId = $ownerId;
122
        $event = ClientEvent\ClientOwnerChangedEvent::create($clone->getPublicId(), $ownerId);
123
        $clone->record($event);
124
125
        return $clone;
126
    }
127
128
    /**
129
     * @param DataBag $parameters
130
     *
131
     * @return Client
132
     */
133
    public function withParameters(DataBag $parameters): self
134
    {
135
        $clone = clone $this;
136
        $clone->parameters = $parameters;
137
        $event = ClientEvent\ClientParametersUpdatedEvent::create($clone->getPublicId(), $parameters);
138
        $clone->record($event);
139
140
        return $clone;
141
    }
142
143
    /**
144
     * @return Client
145
     */
146
    public function markAsDeleted(): self
147
    {
148
        $clone = clone $this;
149
        $clone->deleted = true;
150
        $event = ClientEvent\ClientDeletedEvent::create($clone->getPublicId());
151
        $clone->record($event);
152
153
        return $clone;
154
    }
155
156
    /**
157
     * @return bool
158
     */
159
    public function isDeleted(): bool
160
    {
161
        return $this->deleted;
162
    }
163
164
    /**
165
     * @param string $grant_type
166
     *
167
     * @return bool
168
     */
169
    public function isGrantTypeAllowed(string $grant_type): bool
170
    {
171
        $grant_types = $this->has('grant_types') ? $this->get('grant_types') : [];
172
        if (!is_array($grant_types)) {
173
            throw new \InvalidArgumentException('The metadata "grant_types" must be an array.');
174
        }
175
176
        return in_array($grant_type, $grant_types);
177
    }
178
179
    /**
180
     * @param string $response_type
181
     *
182
     * @return bool
183
     */
184
    public function isResponseTypeAllowed(string $response_type): bool
185
    {
186
        $response_types = $this->has('response_types') ? $this->get('response_types') : [];
187
        if (!is_array($response_types)) {
188
            throw new \InvalidArgumentException('The metadata "response_types" must be an array.');
189
        }
190
191
        return in_array($response_type, $response_types);
192
    }
193
194
    /**
195
     * @return bool
196
     */
197
    public function isPublic(): bool
198
    {
199
        return 'none' === $this->getTokenEndpointAuthenticationMethod();
200
    }
201
202
    /**
203
     * @return string
204
     */
205
    public function getTokenEndpointAuthenticationMethod(): string
206
    {
207
        if ($this->has('token_endpoint_auth_method')) {
208
            return $this->get('token_endpoint_auth_method');
209
        }
210
211
        return 'client_secret_basic';
212
    }
213
214
    /**
215
     * @return int
216
     */
217
    public function getClientCredentialsExpiresAt(): int
218
    {
219
        if ($this->has('client_secret_expires_at')) {
220
            return $this->get('client_secret_expires_at');
221
        }
222
223
        return 0;
224
    }
225
226
    /**
227
     * @return bool
228
     */
229
    public function areClientCredentialsExpired(): bool
230
    {
231
        if (0 === $this->getClientCredentialsExpiresAt()) {
232
            return false;
233
        }
234
235
        return time() > $this->getClientCredentialsExpiresAt();
236
    }
237
238
    /**
239
     * {@inheritdoc}
240
     */
241
    public function getPublicId(): ResourceOwnerId
242
    {
243
        if (null === $this->clientId) {
244
            throw new \RuntimeException('Client not initialized.');
245
        }
246
247
        return $this->clientId;
248
    }
249
250
    /**
251
     * {@inheritdoc}
252
     */
253
    public function has(string $key): bool
254
    {
255
        return $this->parameters->has($key);
256
    }
257
258
    /**
259
     * {@inheritdoc}
260
     */
261
    public function get(string $key)
262
    {
263
        if (!$this->has($key)) {
264
            throw new \InvalidArgumentException(sprintf('Configuration value with key "%s" does not exist.', $key));
265
        }
266
267
        return $this->parameters->get($key);
268
    }
269
270
    /**
271
     * @return array
272
     */
273
    public function all(): array
274
    {
275
        $all = $this->parameters->all();
276
        $all['client_id'] = $this->getPublicId()->getValue();
277
278
        return $all;
279
    }
280
281
    /**
282
     * {@inheritdoc}
283
     */
284
    public function jsonSerialize()
285
    {
286
        $data = [
287
            '$schema' => $this->getSchema(),
288
            'type' => get_class($this),
289
            'client_id' => $this->getPublicId()->getValue(),
290
            'owner_id' => $this->getOwnerId() ? $this->getOwnerId()->getValue() : null,
291
            'parameters' => (object) $this->all(),
292
            'is_deleted' => $this->isDeleted(),
293
        ];
294
295
        return $data;
296
    }
297
298
    /**
299
     * {@inheritdoc}
300
     */
301
    public static function createFromJson(\stdClass $json): DomainObject
302
    {
303
        $clientId = ClientId::create($json->client_id);
304
        $ownerId = null !== $json->owner_id ? UserAccountId::create($json->owner_id) : null;
305
        $parameters = DataBag::create((array) $json->parameters);
306
        $deleted = $json->is_deleted;
307
308
        $client = new self();
309
        $client->clientId = $clientId;
310
        $client->ownerId = $ownerId;
311
        $client->parameters = $parameters;
312
        $client->deleted = $deleted;
313
314
        return $client;
315
    }
316
317
    /**
318
     * @param Event $event
319
     *
320
     * @return Client
321
     */
322
    public function apply(Event $event): self
323
    {
324
        $map = $this->getEventMap();
325
        if (!array_key_exists($event->getType(), $map)) {
326
            throw new \InvalidArgumentException('Unsupported event.');
327
        }
328
        if (null !== $this->clientId && $this->clientId->getValue() !== $event->getDomainId()->getValue()) {
329
            throw new \InvalidArgumentException('Event not applicable for this client.');
330
        }
331
        $method = $map[$event->getType()];
332
333
        return $this->$method($event);
334
    }
335
336
    /**
337
     * @return array
338
     */
339
    private function getEventMap(): array
340
    {
341
        return [
342
            ClientEvent\ClientCreatedEvent::class => 'applyClientCreatedEvent',
343
            ClientEvent\ClientOwnerChangedEvent::class => 'applyClientOwnerChangedEvent',
344
            ClientEvent\ClientDeletedEvent::class => 'applyClientDeletedEvent',
345
            ClientEvent\ClientParametersUpdatedEvent::class => 'applyClientParametersUpdatedEvent',
346
        ];
347
    }
348
349
    /**
350
     * @param ClientEvent\ClientCreatedEvent $event
351
     *
352
     * @return Client
353
     */
354
    protected function applyClientCreatedEvent(ClientEvent\ClientCreatedEvent $event): self
355
    {
356
        $clone = clone $this;
357
        $clone->clientId = $event->getClientId();
358
        $clone->ownerId = $event->getOwnerId();
359
        $clone->parameters = $event->getParameters();
360
361
        return $clone;
362
    }
363
364
    /**
365
     * @param ClientEvent\ClientOwnerChangedEvent $event
366
     *
367
     * @return Client
368
     */
369
    protected function applyClientOwnerChangedEvent(ClientEvent\ClientOwnerChangedEvent $event): self
370
    {
371
        $clone = clone $this;
372
        $clone->ownerId = $event->getNewOwnerId();
373
374
        return $clone;
375
    }
376
377
    /**
378
     * @param ClientEvent\ClientDeletedEvent $event
379
     *
380
     * @return Client
381
     */
382
    protected function applyClientDeletedEvent(ClientEvent\ClientDeletedEvent $event): self
383
    {
384
        $clone = clone $this;
385
        $clone->deleted = true;
386
387
        return $clone;
388
    }
389
390
    /**
391
     * @param ClientEvent\ClientParametersUpdatedEvent $event
392
     *
393
     * @return Client
394
     */
395
    protected function applyClientParametersUpdatedEvent(ClientEvent\ClientParametersUpdatedEvent $event): self
396
    {
397
        $clone = clone $this;
398
        $clone->parameters = $event->getParameters();
399
400
        return $clone;
401
    }
402
}
403