User   C
last analyzed

Complexity

Total Complexity 54

Size/Duplication

Total Lines 539
Duplicated Lines 0 %

Coupling/Cohesion

Components 7
Dependencies 3

Importance

Changes 0
Metric Value
wmc 54
lcom 7
cbo 3
dl 0
loc 539
rs 6.4799
c 0
b 0
f 0

47 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 serialize() 0 12 1
A unserialize() 0 17 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
    /**
26
     * @var mixed
27
     */
28
    protected $id;
29
30
    /**
31
     * @var string|null
32
     */
33
    protected $username;
34
35
    /**
36
     * Normalized representation of a username.
37
     *
38
     * @var string|null
39
     */
40
    protected $usernameCanonical;
41
42
    /**
43
     * Random data that is used as an additional input to a function that hashes a password.
44
     *
45
     * @var string
46
     */
47
    protected $salt;
48
49
    /**
50
     * Encrypted password. Must be persisted.
51
     *
52
     * @var string|null
53
     */
54
    protected $password;
55
56
    /**
57
     * Password before encryption. Used for model validation. Must not be persisted.
58
     *
59
     * @var string|null
60
     */
61
    protected $plainPassword;
62
63
    /**
64
     * @var \DateTimeInterface|null
65
     */
66
    protected $lastLogin;
67
68
    /**
69
     * Random string sent to the user email address in order to verify it
70
     *
71
     * @var string|null
72
     */
73
    protected $emailVerificationToken;
74
75
    /**
76
     * Random string sent to the user email address in order to verify the password resetting request
77
     *
78
     * @var string|null
79
     */
80
    protected $passwordResetToken;
81
82
    /**
83
     * @var \DateTimeInterface|null
84
     */
85
    protected $passwordRequestedAt;
86
87
    /**
88
     * @var \DateTimeInterface|null
89
     */
90
    protected $verifiedAt;
91
92
    /**
93
     * @var bool
94
     */
95
    protected $locked = false;
96
97
    /**
98
     * @var \DateTimeInterface|null
99
     */
100
    protected $expiresAt;
101
102
    /**
103
     * @var \DateTimeInterface|null
104
     */
105
    protected $credentialsExpireAt;
106
107
    /**
108
     * We need at least one role to be able to authenticate
109
     *
110
     * @var array
111
     */
112
    protected $roles = [UserInterface::DEFAULT_ROLE];
113
114
    /**
115
     * @var Collection|UserOAuth[]
116
     */
117
    protected $oauthAccounts;
118
119
    /**
120
     * @var string|null
121
     */
122
    protected $email;
123
124
    /**
125
     * @var string|null
126
     */
127
    protected $emailCanonical;
128
129
    public function __construct()
130
    {
131
        $this->salt = base_convert(bin2hex(random_bytes(20)), 16, 36);
132
        $this->oauthAccounts = new ArrayCollection();
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Doctrine\Common\Collections\ArrayCollection() of type object<Doctrine\Common\C...ctions\ArrayCollection> is incompatible with the declared type object<Doctrine\Common\C...\User\Model\UserOAuth>> of property $oauthAccounts.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
133
        $this->createdAt = new \DateTime();
134
135
        // Set here to overwrite default value from trait
136
        $this->enabled = false;
137
    }
138
139
    /**
140
     * @return string
141
     */
142
    public function __toString(): string
143
    {
144
        return (string) $this->getUsername();
145
    }
146
147
    /**
148
     * {@inheritdoc}
149
     */
150
    public function getId()
151
    {
152
        return $this->id;
153
    }
154
155
    /**
156
     * {@inheritdoc}
157
     */
158
    public function getEmail(): ?string
159
    {
160
        return $this->email;
161
    }
162
163
    /**
164
     * {@inheritdoc}
165
     */
166
    public function setEmail(?string $email): void
167
    {
168
        $this->email = $email;
169
    }
170
171
    /**
172
     * {@inheritdoc}
173
     */
174
    public function getEmailCanonical(): ?string
175
    {
176
        return $this->emailCanonical;
177
    }
178
179
    /**
180
     * {@inheritdoc}
181
     */
182
    public function setEmailCanonical(?string $emailCanonical): void
183
    {
184
        $this->emailCanonical = $emailCanonical;
185
    }
186
187
    /**
188
     * {@inheritdoc}
189
     */
190
    public function getUsername(): ?string
191
    {
192
        return $this->username;
193
    }
194
195
    /**
196
     * {@inheritdoc}
197
     */
198
    public function setUsername(?string $username): void
199
    {
200
        $this->username = $username;
201
    }
202
203
    /**
204
     * {@inheritdoc}
205
     */
206
    public function getUsernameCanonical(): ?string
207
    {
208
        return $this->usernameCanonical;
209
    }
210
211
    /**
212
     * {@inheritdoc}
213
     */
214
    public function setUsernameCanonical(?string $usernameCanonical): void
215
    {
216
        $this->usernameCanonical = $usernameCanonical;
217
    }
218
219
    /**
220
     * {@inheritdoc}
221
     */
222
    public function getSalt(): string
223
    {
224
        return $this->salt;
225
    }
226
227
    /**
228
     * {@inheritdoc}
229
     */
230
    public function getPlainPassword(): ?string
231
    {
232
        return $this->plainPassword;
233
    }
234
235
    /**
236
     * {@inheritdoc}
237
     */
238
    public function setPlainPassword(?string $password): void
239
    {
240
        $this->plainPassword = $password;
241
    }
242
243
    /**
244
     * {@inheritdoc}
245
     */
246
    public function getPassword(): ?string
247
    {
248
        return $this->password;
249
    }
250
251
    /**
252
     * {@inheritdoc}
253
     */
254
    public function setPassword(?string $password): void
255
    {
256
        $this->password = $password;
257
    }
258
259
    /**
260
     * {@inheritdoc}
261
     */
262
    public function getExpiresAt(): ?\DateTimeInterface
263
    {
264
        return $this->expiresAt;
265
    }
266
267
    /**
268
     * {@inheritdoc}
269
     */
270
    public function setExpiresAt(?\DateTimeInterface $date): void
271
    {
272
        $this->expiresAt = $date;
273
    }
274
275
    /**
276
     * {@inheritdoc}
277
     */
278
    public function getCredentialsExpireAt(): ?\DateTimeInterface
279
    {
280
        return $this->credentialsExpireAt;
281
    }
282
283
    /**
284
     * {@inheritdoc}
285
     */
286
    public function setCredentialsExpireAt(?\DateTimeInterface $date): void
287
    {
288
        $this->credentialsExpireAt = $date;
289
    }
290
291
    /**
292
     * {@inheritdoc}
293
     */
294
    public function getLastLogin(): ?\DateTimeInterface
295
    {
296
        return $this->lastLogin;
297
    }
298
299
    /**
300
     * {@inheritdoc}
301
     */
302
    public function setLastLogin(?\DateTimeInterface $time): void
303
    {
304
        $this->lastLogin = $time;
305
    }
306
307
    /**
308
     * {@inheritdoc}
309
     */
310
    public function getEmailVerificationToken(): ?string
311
    {
312
        return $this->emailVerificationToken;
313
    }
314
315
    /**
316
     * {@inheritdoc}
317
     */
318
    public function setEmailVerificationToken(?string $verificationToken): void
319
    {
320
        $this->emailVerificationToken = $verificationToken;
321
    }
322
323
    /**
324
     * {@inheritdoc}
325
     */
326
    public function getPasswordResetToken(): ?string
327
    {
328
        return $this->passwordResetToken;
329
    }
330
331
    /**
332
     * {@inheritdoc}
333
     */
334
    public function setPasswordResetToken(?string $passwordResetToken): void
335
    {
336
        $this->passwordResetToken = $passwordResetToken;
337
    }
338
339
    /**
340
     * {@inheritdoc}
341
     */
342
    public function isCredentialsNonExpired(): bool
343
    {
344
        return !$this->hasExpired($this->credentialsExpireAt);
345
    }
346
347
    /**
348
     * {@inheritdoc}
349
     */
350
    public function isAccountNonExpired(): bool
351
    {
352
        return !$this->hasExpired($this->expiresAt);
353
    }
354
355
    /**
356
     * {@inheritdoc}
357
     */
358
    public function setLocked(bool $locked): void
359
    {
360
        $this->locked = $locked;
361
    }
362
363
    /**
364
     * {@inheritdoc}
365
     */
366
    public function isAccountNonLocked(): bool
367
    {
368
        return !$this->locked;
369
    }
370
371
    /**
372
     * {@inheritdoc}
373
     */
374
    public function hasRole(string $role): bool
375
    {
376
        return in_array(strtoupper($role), $this->getRoles(), true);
377
    }
378
379
    /**
380
     * {@inheritdoc}
381
     */
382
    public function addRole(string $role): void
383
    {
384
        $role = strtoupper($role);
385
        if (!in_array($role, $this->roles, true)) {
386
            $this->roles[] = $role;
387
        }
388
    }
389
390
    /**
391
     * {@inheritdoc}
392
     */
393
    public function removeRole(string $role): void
394
    {
395
        if (false !== $key = array_search(strtoupper($role), $this->roles, true)) {
396
            unset($this->roles[$key]);
397
            $this->roles = array_values($this->roles);
398
        }
399
    }
400
401
    /**
402
     * {@inheritdoc}
403
     */
404
    public function getRoles(): array
405
    {
406
        return $this->roles;
407
    }
408
409
    /**
410
     * {@inheritdoc}
411
     */
412
    public function isPasswordRequestNonExpired(\DateInterval $ttl): bool
413
    {
414
        if (null === $this->passwordRequestedAt) {
415
            return false;
416
        }
417
418
        $threshold = new \DateTime();
419
        $threshold->sub($ttl);
420
421
        return $threshold <= $this->passwordRequestedAt;
422
    }
423
424
    /**
425
     * {@inheritdoc}
426
     */
427
    public function getPasswordRequestedAt(): ?\DateTimeInterface
428
    {
429
        return $this->passwordRequestedAt;
430
    }
431
432
    /**
433
     * {@inheritdoc}
434
     */
435
    public function setPasswordRequestedAt(?\DateTimeInterface $date): void
436
    {
437
        $this->passwordRequestedAt = $date;
438
    }
439
440
    /**
441
     * {@inheritdoc}
442
     */
443
    public function isVerified(): bool
444
    {
445
        return null !== $this->verifiedAt;
446
    }
447
448
    /**
449
     * {@inheritdoc}
450
     */
451
    public function getVerifiedAt(): ?\DateTimeInterface
452
    {
453
        return $this->verifiedAt;
454
    }
455
456
    /**
457
     * {@inheritdoc}
458
     */
459
    public function setVerifiedAt(?\DateTimeInterface $verifiedAt): void
460
    {
461
        $this->verifiedAt = $verifiedAt;
462
    }
463
464
    /**
465
     * {@inheritdoc}
466
     */
467
    public function eraseCredentials(): void
468
    {
469
        $this->plainPassword = null;
470
    }
471
472
    /**
473
     * {@inheritdoc}
474
     */
475
    public function getOAuthAccounts(): Collection
476
    {
477
        return $this->oauthAccounts;
478
    }
479
480
    /**
481
     * {@inheritdoc}
482
     */
483
    public function getOAuthAccount(string $provider): ?UserOAuthInterface
484
    {
485
        if ($this->oauthAccounts->isEmpty()) {
486
            return null;
487
        }
488
489
        $filtered = $this->oauthAccounts->filter(function (UserOAuthInterface $oauth) use ($provider): bool {
490
            return $provider === $oauth->getProvider();
491
        });
492
493
        if ($filtered->isEmpty()) {
494
            return null;
495
        }
496
497
        return $filtered->current();
498
    }
499
500
    /**
501
     * {@inheritdoc}
502
     */
503
    public function addOAuthAccount(UserOAuthInterface $oauth): void
504
    {
505
        if (!$this->oauthAccounts->contains($oauth)) {
506
            $this->oauthAccounts->add($oauth);
507
            $oauth->setUser($this);
508
        }
509
    }
510
511
    /**
512
     * The serialized data have to contain the fields used by the equals method and the username.
513
     *
514
     * @return string
515
     */
516
    public function serialize(): string
517
    {
518
        return serialize([
519
            $this->password,
520
            $this->salt,
521
            $this->usernameCanonical,
522
            $this->username,
523
            $this->locked,
524
            $this->enabled,
525
            $this->id,
526
        ]);
527
    }
528
529
    /**
530
     * @param string $serialized
531
     */
532
    public function unserialize($serialized): void
533
    {
534
        $data = unserialize($serialized);
535
        // add a few extra elements in the array to ensure that we have enough keys when unserializing
536
        // older data which does not include all properties.
537
        $data = array_merge($data, array_fill(0, 2, null));
538
539
        [
540
            $this->password,
541
            $this->salt,
542
            $this->usernameCanonical,
543
            $this->username,
544
            $this->locked,
545
            $this->enabled,
546
            $this->id
547
        ] = $data;
548
    }
549
550
    /**
551
     * @param \DateTimeInterface|null $date
552
     *
553
     * @return bool
554
     */
555
    protected function hasExpired(?\DateTimeInterface $date): bool
556
    {
557
        return null !== $date && new \DateTime() >= $date;
558
    }
559
}
560