Completed
Push — master ( bbd2b1...f08c3b )
by Kamil
31:47 queued 09:37
created

User   C

Complexity

Total Complexity 56

Size/Duplication

Total Lines 528
Duplicated Lines 0 %

Coupling/Cohesion

Components 7
Dependencies 4

Importance

Changes 0
Metric Value
wmc 56
lcom 7
cbo 4
dl 0
loc 528
rs 5.5199
c 0
b 0
f 0

49 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 1
A __toString() 0 4 1
A getId() 0 4 1
A getEmail() 0 4 1
A setEmail() 0 4 1
A getEmailCanonical() 0 4 1
A setEmailCanonical() 0 4 1
A getUsername() 0 4 1
A setUsername() 0 4 1
A getUsernameCanonical() 0 4 1
A setUsernameCanonical() 0 4 1
A getSalt() 0 4 1
A getPlainPassword() 0 4 1
A setPlainPassword() 0 4 1
A getPassword() 0 4 1
A setPassword() 0 4 1
A getExpiresAt() 0 4 1
A setExpiresAt() 0 4 1
A getCredentialsExpireAt() 0 4 1
A setCredentialsExpireAt() 0 4 1
A getLastLogin() 0 4 1
A setLastLogin() 0 4 1
A getEmailVerificationToken() 0 4 1
A setEmailVerificationToken() 0 4 1
A getPasswordResetToken() 0 4 1
A setPasswordResetToken() 0 4 1
A isCredentialsNonExpired() 0 4 1
A isAccountNonExpired() 0 4 1
A setLocked() 0 4 1
A isAccountNonLocked() 0 4 1
A hasRole() 0 4 1
A addRole() 0 7 2
A removeRole() 0 7 2
A getRoles() 0 4 1
A isPasswordRequestNonExpired() 0 11 2
A getPasswordRequestedAt() 0 4 1
A setPasswordRequestedAt() 0 4 1
A isVerified() 0 4 1
A getVerifiedAt() 0 4 1
A setVerifiedAt() 0 4 1
A eraseCredentials() 0 4 1
A getOAuthAccounts() 0 4 1
A getOAuthAccount() 0 16 3
A addOAuthAccount() 0 7 2
A getEncoderName() 0 4 1
A setEncoderName() 0 4 1
A serialize() 0 13 1
A unserialize() 0 18 1
A hasExpired() 0 4 2

How to fix   Complexity   

Complex Class

Complex classes like User 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 User, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * This file is part of the Sylius package.
5
 *
6
 * (c) Paweł Jędrzejewski
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace Sylius\Component\User\Model;
15
16
use Doctrine\Common\Collections\ArrayCollection;
17
use Doctrine\Common\Collections\Collection;
18
use Sylius\Component\Resource\Model\TimestampableTrait;
19
use Sylius\Component\Resource\Model\ToggleableTrait;
20
21
class User implements UserInterface
22
{
23
    use TimestampableTrait, ToggleableTrait;
24
25
    /** @var mixed */
26
    protected $id;
27
28
    /** @var string|null */
29
    protected $username;
30
31
    /**
32
     * Normalized representation of a username.
33
     *
34
     * @var string|null
35
     */
36
    protected $usernameCanonical;
37
38
    /**
39
     * Random data that is used as an additional input to a function that hashes a password.
40
     *
41
     * @var string
42
     */
43
    protected $salt;
44
45
    /**
46
     * Encrypted password. Must be persisted.
47
     *
48
     * @var string|null
49
     */
50
    protected $password;
51
52
    /**
53
     * Password before encryption. Used for model validation. Must not be persisted.
54
     *
55
     * @var string|null
56
     */
57
    protected $plainPassword;
58
59
    /** @var \DateTimeInterface|null */
60
    protected $lastLogin;
61
62
    /**
63
     * Random string sent to the user email address in order to verify it
64
     *
65
     * @var string|null
66
     */
67
    protected $emailVerificationToken;
68
69
    /**
70
     * Random string sent to the user email address in order to verify the password resetting request
71
     *
72
     * @var string|null
73
     */
74
    protected $passwordResetToken;
75
76
    /** @var \DateTimeInterface|null */
77
    protected $passwordRequestedAt;
78
79
    /** @var \DateTimeInterface|null */
80
    protected $verifiedAt;
81
82
    /** @var bool */
83
    protected $locked = false;
84
85
    /** @var \DateTimeInterface|null */
86
    protected $expiresAt;
87
88
    /** @var \DateTimeInterface|null */
89
    protected $credentialsExpireAt;
90
91
    /**
92
     * We need at least one role to be able to authenticate
93
     *
94
     * @var array
95
     */
96
    protected $roles = [UserInterface::DEFAULT_ROLE];
97
98
    /** @var Collection|UserOAuth[] */
99
    protected $oauthAccounts;
100
101
    /** @var string|null */
102
    protected $email;
103
104
    /** @var string|null */
105
    protected $emailCanonical;
106
107
    /** @var string|null */
108
    protected $encoderName;
109
110
    public function __construct()
111
    {
112
        $this->salt = base_convert(bin2hex(random_bytes(20)), 16, 36);
113
        $this->oauthAccounts = new ArrayCollection();
114
        $this->createdAt = new \DateTime();
115
116
        // Set here to overwrite default value from trait
117
        $this->enabled = false;
118
    }
119
120
    public function __toString(): string
121
    {
122
        return (string) $this->getUsername();
123
    }
124
125
    /**
126
     * {@inheritdoc}
127
     */
128
    public function getId()
129
    {
130
        return $this->id;
131
    }
132
133
    /**
134
     * {@inheritdoc}
135
     */
136
    public function getEmail(): ?string
137
    {
138
        return $this->email;
139
    }
140
141
    /**
142
     * {@inheritdoc}
143
     */
144
    public function setEmail(?string $email): void
145
    {
146
        $this->email = $email;
147
    }
148
149
    /**
150
     * {@inheritdoc}
151
     */
152
    public function getEmailCanonical(): ?string
153
    {
154
        return $this->emailCanonical;
155
    }
156
157
    /**
158
     * {@inheritdoc}
159
     */
160
    public function setEmailCanonical(?string $emailCanonical): void
161
    {
162
        $this->emailCanonical = $emailCanonical;
163
    }
164
165
    /**
166
     * {@inheritdoc}
167
     */
168
    public function getUsername(): ?string
169
    {
170
        return $this->username;
171
    }
172
173
    /**
174
     * {@inheritdoc}
175
     */
176
    public function setUsername(?string $username): void
177
    {
178
        $this->username = $username;
179
    }
180
181
    /**
182
     * {@inheritdoc}
183
     */
184
    public function getUsernameCanonical(): ?string
185
    {
186
        return $this->usernameCanonical;
187
    }
188
189
    /**
190
     * {@inheritdoc}
191
     */
192
    public function setUsernameCanonical(?string $usernameCanonical): void
193
    {
194
        $this->usernameCanonical = $usernameCanonical;
195
    }
196
197
    /**
198
     * {@inheritdoc}
199
     */
200
    public function getSalt(): string
201
    {
202
        return $this->salt;
203
    }
204
205
    /**
206
     * {@inheritdoc}
207
     */
208
    public function getPlainPassword(): ?string
209
    {
210
        return $this->plainPassword;
211
    }
212
213
    /**
214
     * {@inheritdoc}
215
     */
216
    public function setPlainPassword(?string $password): void
217
    {
218
        $this->plainPassword = $password;
219
    }
220
221
    /**
222
     * {@inheritdoc}
223
     */
224
    public function getPassword(): ?string
225
    {
226
        return $this->password;
227
    }
228
229
    /**
230
     * {@inheritdoc}
231
     */
232
    public function setPassword(?string $password): void
233
    {
234
        $this->password = $password;
235
    }
236
237
    /**
238
     * {@inheritdoc}
239
     */
240
    public function getExpiresAt(): ?\DateTimeInterface
241
    {
242
        return $this->expiresAt;
243
    }
244
245
    /**
246
     * {@inheritdoc}
247
     */
248
    public function setExpiresAt(?\DateTimeInterface $date): void
249
    {
250
        $this->expiresAt = $date;
251
    }
252
253
    /**
254
     * {@inheritdoc}
255
     */
256
    public function getCredentialsExpireAt(): ?\DateTimeInterface
257
    {
258
        return $this->credentialsExpireAt;
259
    }
260
261
    /**
262
     * {@inheritdoc}
263
     */
264
    public function setCredentialsExpireAt(?\DateTimeInterface $date): void
265
    {
266
        $this->credentialsExpireAt = $date;
267
    }
268
269
    /**
270
     * {@inheritdoc}
271
     */
272
    public function getLastLogin(): ?\DateTimeInterface
273
    {
274
        return $this->lastLogin;
275
    }
276
277
    /**
278
     * {@inheritdoc}
279
     */
280
    public function setLastLogin(?\DateTimeInterface $time): void
281
    {
282
        $this->lastLogin = $time;
283
    }
284
285
    /**
286
     * {@inheritdoc}
287
     */
288
    public function getEmailVerificationToken(): ?string
289
    {
290
        return $this->emailVerificationToken;
291
    }
292
293
    /**
294
     * {@inheritdoc}
295
     */
296
    public function setEmailVerificationToken(?string $verificationToken): void
297
    {
298
        $this->emailVerificationToken = $verificationToken;
299
    }
300
301
    /**
302
     * {@inheritdoc}
303
     */
304
    public function getPasswordResetToken(): ?string
305
    {
306
        return $this->passwordResetToken;
307
    }
308
309
    /**
310
     * {@inheritdoc}
311
     */
312
    public function setPasswordResetToken(?string $passwordResetToken): void
313
    {
314
        $this->passwordResetToken = $passwordResetToken;
315
    }
316
317
    /**
318
     * {@inheritdoc}
319
     */
320
    public function isCredentialsNonExpired(): bool
321
    {
322
        return !$this->hasExpired($this->credentialsExpireAt);
323
    }
324
325
    /**
326
     * {@inheritdoc}
327
     */
328
    public function isAccountNonExpired(): bool
329
    {
330
        return !$this->hasExpired($this->expiresAt);
331
    }
332
333
    /**
334
     * {@inheritdoc}
335
     */
336
    public function setLocked(bool $locked): void
337
    {
338
        $this->locked = $locked;
339
    }
340
341
    /**
342
     * {@inheritdoc}
343
     */
344
    public function isAccountNonLocked(): bool
345
    {
346
        return !$this->locked;
347
    }
348
349
    /**
350
     * {@inheritdoc}
351
     */
352
    public function hasRole(string $role): bool
353
    {
354
        return in_array(strtoupper($role), $this->getRoles(), true);
355
    }
356
357
    /**
358
     * {@inheritdoc}
359
     */
360
    public function addRole(string $role): void
361
    {
362
        $role = strtoupper($role);
363
        if (!in_array($role, $this->roles, true)) {
364
            $this->roles[] = $role;
365
        }
366
    }
367
368
    /**
369
     * {@inheritdoc}
370
     */
371
    public function removeRole(string $role): void
372
    {
373
        if (false !== $key = array_search(strtoupper($role), $this->roles, true)) {
374
            unset($this->roles[$key]);
375
            $this->roles = array_values($this->roles);
376
        }
377
    }
378
379
    /**
380
     * {@inheritdoc}
381
     */
382
    public function getRoles(): array
383
    {
384
        return $this->roles;
385
    }
386
387
    /**
388
     * {@inheritdoc}
389
     */
390
    public function isPasswordRequestNonExpired(\DateInterval $ttl): bool
391
    {
392
        if (null === $this->passwordRequestedAt) {
393
            return false;
394
        }
395
396
        $threshold = new \DateTime();
397
        $threshold->sub($ttl);
398
399
        return $threshold <= $this->passwordRequestedAt;
400
    }
401
402
    /**
403
     * {@inheritdoc}
404
     */
405
    public function getPasswordRequestedAt(): ?\DateTimeInterface
406
    {
407
        return $this->passwordRequestedAt;
408
    }
409
410
    /**
411
     * {@inheritdoc}
412
     */
413
    public function setPasswordRequestedAt(?\DateTimeInterface $date): void
414
    {
415
        $this->passwordRequestedAt = $date;
416
    }
417
418
    /**
419
     * {@inheritdoc}
420
     */
421
    public function isVerified(): bool
422
    {
423
        return null !== $this->verifiedAt;
424
    }
425
426
    /**
427
     * {@inheritdoc}
428
     */
429
    public function getVerifiedAt(): ?\DateTimeInterface
430
    {
431
        return $this->verifiedAt;
432
    }
433
434
    /**
435
     * {@inheritdoc}
436
     */
437
    public function setVerifiedAt(?\DateTimeInterface $verifiedAt): void
438
    {
439
        $this->verifiedAt = $verifiedAt;
440
    }
441
442
    /**
443
     * {@inheritdoc}
444
     */
445
    public function eraseCredentials(): void
446
    {
447
        $this->plainPassword = null;
448
    }
449
450
    /**
451
     * {@inheritdoc}
452
     */
453
    public function getOAuthAccounts(): Collection
454
    {
455
        return $this->oauthAccounts;
456
    }
457
458
    /**
459
     * {@inheritdoc}
460
     */
461
    public function getOAuthAccount(string $provider): ?UserOAuthInterface
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
462
    {
463
        if ($this->oauthAccounts->isEmpty()) {
464
            return null;
465
        }
466
467
        $filtered = $this->oauthAccounts->filter(function (UserOAuthInterface $oauth) use ($provider): bool {
468
            return $provider === $oauth->getProvider();
469
        });
470
471
        if ($filtered->isEmpty()) {
472
            return null;
473
        }
474
475
        return $filtered->current();
476
    }
477
478
    /**
479
     * {@inheritdoc}
480
     */
481
    public function addOAuthAccount(UserOAuthInterface $oauth): void
482
    {
483
        if (!$this->oauthAccounts->contains($oauth)) {
484
            $this->oauthAccounts->add($oauth);
485
            $oauth->setUser($this);
486
        }
487
    }
488
489
    /**
490
     * {@inheritdoc}
491
     */
492
    public function getEncoderName(): ?string
493
    {
494
        return $this->encoderName;
495
    }
496
497
    /**
498
     * {@inheritdoc}
499
     */
500
    public function setEncoderName(?string $encoderName): void
501
    {
502
        $this->encoderName = $encoderName;
503
    }
504
505
    /**
506
     * The serialized data have to contain the fields used by the equals method and the username.
507
     */
508
    public function serialize(): string
509
    {
510
        return serialize([
511
            $this->password,
512
            $this->salt,
513
            $this->usernameCanonical,
514
            $this->username,
515
            $this->locked,
516
            $this->enabled,
517
            $this->id,
518
            $this->encoderName,
519
        ]);
520
    }
521
522
    /**
523
     * @param string $serialized
524
     */
525
    public function unserialize($serialized): void
526
    {
527
        $data = unserialize($serialized);
528
        // add a few extra elements in the array to ensure that we have enough keys when unserializing
529
        // older data which does not include all properties.
530
        $data = array_merge($data, array_fill(0, 2, null));
531
532
        [
533
            $this->password,
534
            $this->salt,
535
            $this->usernameCanonical,
536
            $this->username,
537
            $this->locked,
538
            $this->enabled,
539
            $this->id,
540
            $this->encoderName,
541
        ] = $data;
542
    }
543
544
    protected function hasExpired(?\DateTimeInterface $date): bool
545
    {
546
        return null !== $date && new \DateTime() >= $date;
547
    }
548
}
549