User   F
last analyzed

Complexity

Total Complexity 81

Size/Duplication

Total Lines 666
Duplicated Lines 2.7 %

Coupling/Cohesion

Components 7
Dependencies 1

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 81
c 3
b 0
f 0
lcom 7
cbo 1
dl 18
loc 666
rs 1.3705

55 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 1
A addRole() 0 13 3
A serialize() 0 14 1
A unserialize() 0 19 1
A eraseCredentials() 0 4 1
A getId() 0 4 1
A getUsername() 0 4 1
A getUsernameCanonical() 0 4 1
A getSalt() 0 4 1
A getEmail() 0 4 1
A getEmailCanonical() 0 4 1
A getPassword() 0 4 1
A getPlainPassword() 0 4 1
A getLastLogin() 0 4 1
A getConfirmationToken() 0 4 1
A getRoles() 0 13 2
A hasRole() 0 4 1
A isAccountNonExpired() 0 12 4
A isAccountNonLocked() 0 4 1
A isCredentialsNonExpired() 0 12 4
A isCredentialsExpired() 0 4 1
A isEnabled() 0 4 1
A isExpired() 0 4 1
A isLocked() 0 4 1
A isSuperAdmin() 0 4 1
A isUser() 0 4 2
A removeRole() 0 9 2
A setUsername() 0 6 1
A setUsernameCanonical() 0 6 1
A setCredentialsExpireAt() 0 6 1
A setCredentialsExpired() 0 6 1
A setEmail() 0 6 1
A setEmailCanonical() 0 6 1
A setEnabled() 0 6 1
A setExpired() 0 6 1
A setExpiresAt() 0 6 1
A setPassword() 0 6 1
A setSuperAdmin() 0 10 2
A setPlainPassword() 0 6 1
A setLastLogin() 0 6 1
A setLocked() 0 6 1
A setConfirmationToken() 0 6 1
A setPasswordRequestedAt() 0 6 1
A getPasswordRequestedAt() 0 4 1
A isPasswordRequestNonExpired() 0 5 2
A setRoles() 0 10 2
A getGroup() 0 4 1
A setGroup() 0 6 1
A setCreatedAt() 0 6 1
A getCreatedAt() 0 4 1
B setAdmin() 9 9 5
A isAdmin() 0 4 1
B setUserAdmin() 9 9 5
B validateGroup() 0 15 5
A __toString() 0 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

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 Dedipanel project
5
 *
6
 * (c) 2010-2015 Dedipanel <http://www.dedicated-panel.net>
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
namespace DP\Core\UserBundle\Entity;
13
14
use Doctrine\ORM\Mapping as ORM;
15
use FOS\UserBundle\Model\UserInterface;
16
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
17
use Symfony\Component\Validator\Constraints as Assert;
18
use Symfony\Component\Validator\Context\ExecutionContextInterface;
19
20
/**
21
 * User
22
 *
23
 * @ORM\Table(name="user_table")
24
 * @ORM\Entity(repositoryClass="DP\Core\UserBundle\Entity\UserRepository")
25
 * @UniqueEntity(fields="usernameCanonical", errorPath="username", message="fos_user.username.already_used", groups={"Registration", "Profile"})
26
 * @UniqueEntity(fields="emailCanonical", errorPath="email", message="fos_user.email.already_used", groups={"Registration", "Profile"})
27
 */
28
class User implements UserInterface
29
{
30
    const ROLE_ADMIN = 'ROLE_ADMIN';
31
32
    /**
33
     * @var integer
34
     *
35
     * @ORM\Column(name="id", type="integer")
36
     * @ORM\Id
37
     * @ORM\GeneratedValue(strategy="AUTO")
38
     */
39
    protected $id;
40
41
    /**
42
     * @var string
43
     *
44
     * @ORM\Column(name="username", type="string", length=255)
45
     * @Assert\NotBlank(message="user_admin.assert.username.empty", groups={"Profile","Adding","Editing"})
46
     * @Assert\Length(min=2, minMessage="fos_user.username.short", max=255, maxMessage="fos_user.username.long", groups={"Profile","Adding","Editing"})
47
     */
48
    protected $username;
49
50
    /**
51
     * @var string
52
     *
53
     * @ORM\Column(name="username_canonical", type="string", length=255, unique=true)
54
     */
55
    protected $usernameCanonical;
56
57
    /**
58
     * @var string
59
     *
60
     * @ORM\Column(name="email", type="string", length=255)
61
     * @Assert\NotBlank(message="user_admin.assert.email.empty", groups={"Adding","Editing","Profile"})
62
     * @Assert\Length(min=2, minMessage="fos_user.email.short", max=255, maxMessage="fos_user.email.long", groups={"Adding","Editing","Profile"})
63
     * @Assert\Email(message="user_admin.assert.email.valid", groups={"Adding","Editing","Profile"})
64
     */
65
    protected $email;
66
67
    /**
68
     * @var string
69
     *
70
     * @ORM\Column(name="email_canonical", type="string", length=255, unique=true)
71
     */
72
    protected $emailCanonical;
73
74
    /**
75
     * @var boolean
76
     *
77
     * @ORM\Column(name="enabled", type="boolean")
78
     */
79
    protected $enabled;
80
81
    /**
82
     * The salt to use for hashing
83
     *
84
     * @var string
85
     *
86
     * @ORM\Column(name="salt", type="string")
87
     */
88
    protected $salt;
89
90
    /**
91
     * Encrypted password. Must be persisted.
92
     *
93
     * @var string
94
     *
95
     * @ORM\Column(name="password", type="string")
96
     */
97
    protected $password;
98
99
    /**
100
     * Plain password. Used for model validation. Must not be persisted.
101
     *
102
     * @var string
103
     *
104
     * @Assert\NotBlank(message="user_admin.assert.password.empty", groups={"Adding"})
105
     * @Assert\Length(min=2, minMessage="user_admin.assert.password.short", max=4096, groups={"Adding","Profile"})
106
     */
107
    protected $plainPassword;
108
109
    /**
110
     * @var \DateTime
111
     *
112
     * @ORM\Column(name="last_login", type="datetime", nullable=true)
113
     */
114
    protected $lastLogin;
115
116
    /**
117
     * Random string sent to the user email address in order to verify it
118
     *
119
     * @var string
120
     *
121
     * @ORM\Column(name="confirmation_token", type="string", nullable=true)
122
     */
123
    protected $confirmationToken;
124
125
    /**
126
     * @var \DateTime
127
     *
128
     * @ORM\Column(name="password_requested_at", type="datetime", nullable=true)
129
     */
130
    protected $passwordRequestedAt;
131
132
    /**
133
     * @var Group
134
     *
135
     * @ORM\ManyToOne(targetEntity="DP\Core\UserBundle\Entity\Group", inversedBy="users")
136
     * @ORM\JoinColumn(name="group_id", referencedColumnName="id")
137
     */
138
    protected $group;
139
140
    /**
141
     * @var boolean
142
     *
143
     * @ORM\Column(name="locked", type="boolean")
144
     */
145
    protected $locked;
146
147
    /**
148
     * @var boolean
149
     *
150
     * @ORM\Column(name="expired", type="boolean")
151
     */
152
    protected $expired;
153
154
    /**
155
     * @var \DateTime
156
     *
157
     * @ORM\Column(name="expires_at", type="datetime", nullable=true)
158
     */
159
    protected $expiresAt;
160
161
    /**
162
     * @var array
163
     *
164
     * @ORM\Column(name="roles", type="array")
165
     */
166
    protected $roles;
167
168
    /**
169
     * @var boolean
170
     *
171
     * @ORM\Column(name="credentials_expired", type="boolean")
172
     */
173
    protected $credentialsExpired;
174
175
    /**
176
     * @var \DateTime
177
     *
178
     * @ORM\Column(name="credentials_expire_at", type="datetime", nullable=true)
179
     */
180
    protected $credentialsExpireAt;
181
182
    /**
183
     * @var \DateTime
184
     *
185
     * @ORM\Column(name="createdAt", type="datetime", nullable=true)
186
     */
187
    protected $createdAt;
188
189
    public function __construct()
190
    {
191
        $this->salt = base_convert(sha1(uniqid(mt_rand(), true)), 16, 36);
192
        $this->enabled = false;
193
        $this->locked = false;
194
        $this->expired = false;
195
        $this->roles = array();
196
        $this->credentialsExpired = false;
197
    }
198
199
    public function addRole($role)
200
    {
201
        $role = strtoupper($role);
202
        if ($role === static::ROLE_DEFAULT) {
203
            return $this;
204
        }
205
206
        if (!in_array($role, $this->roles, true)) {
207
            $this->roles[] = $role;
208
        }
209
210
        return $this;
211
    }
212
213
    /**
214
     * Serializes the user.
215
     *
216
     * The serialized data have to contain the fields used by the equals method and the username.
217
     *
218
     * @return string
219
     */
220
    public function serialize()
221
    {
222
        return serialize(array(
223
            $this->password,
224
            $this->salt,
225
            $this->usernameCanonical,
226
            $this->username,
227
            $this->expired,
228
            $this->locked,
229
            $this->credentialsExpired,
230
            $this->enabled,
231
            $this->id,
232
        ));
233
    }
234
235
    /**
236
     * Unserializes the user.
237
     *
238
     * @param string $serialized
239
     */
240
    public function unserialize($serialized)
241
    {
242
        $data = unserialize($serialized);
243
        // add a few extra elements in the array to ensure that we have enough keys when unserializing
244
        // older data which does not include all properties.
245
        $data = array_merge($data, array_fill(0, 2, null));
246
247
        list(
248
            $this->password,
249
            $this->salt,
250
            $this->usernameCanonical,
251
            $this->username,
252
            $this->expired,
253
            $this->locked,
254
            $this->credentialsExpired,
255
            $this->enabled,
256
            $this->id
257
            ) = $data;
258
    }
259
260
    /**
261
     * Removes sensitive data from the user.
262
     */
263
    public function eraseCredentials()
264
    {
265
        $this->plainPassword = null;
266
    }
267
268
    /**
269
     * Returns the user unique id.
270
     *
271
     * @return mixed
272
     */
273
    public function getId()
274
    {
275
        return $this->id;
276
    }
277
278
    public function getUsername()
279
    {
280
        return $this->username;
281
    }
282
283
    public function getUsernameCanonical()
284
    {
285
        return $this->usernameCanonical;
286
    }
287
288
    public function getSalt()
289
    {
290
        return $this->salt;
291
    }
292
293
    public function getEmail()
294
    {
295
        return $this->email;
296
    }
297
298
    public function getEmailCanonical()
299
    {
300
        return $this->emailCanonical;
301
    }
302
303
    /**
304
     * Gets the encrypted password.
305
     *
306
     * @return string
307
     */
308
    public function getPassword()
309
    {
310
        return $this->password;
311
    }
312
313
    public function getPlainPassword()
314
    {
315
        return $this->plainPassword;
316
    }
317
318
    /**
319
     * Gets the last login time.
320
     *
321
     * @return \DateTime
322
     */
323
    public function getLastLogin()
324
    {
325
        return $this->lastLogin;
326
    }
327
328
    public function getConfirmationToken()
329
    {
330
        return $this->confirmationToken;
331
    }
332
333
    /**
334
     * Returns the user roles
335
     *
336
     * @return array The roles
337
     */
338
    public function getRoles()
339
    {
340
        $roles = $this->roles;
341
342
        if ($this->getGroup() !== null) {
343
            $roles = array_merge($roles, $this->getGroup()->getRoles());
344
        }
345
346
        // we need to make sure to have at least one role
347
        $roles[] = static::ROLE_DEFAULT;
348
349
        return array_unique($roles);
350
    }
351
352
    /**
353
     * Never use this to check if this user has access to anything!
354
     *
355
     * Use the SecurityContext, or an implementation of AccessDecisionManager
356
     * instead, e.g.
357
     *
358
     *         $securityContext->isGranted('ROLE_USER');
359
     *
360
     * @param string $role
361
     *
362
     * @return boolean
363
     */
364
    public function hasRole($role)
365
    {
366
        return in_array(strtoupper($role), $this->getRoles(), true);
367
    }
368
369
    public function isAccountNonExpired()
370
    {
371
        if (true === $this->expired) {
372
            return false;
373
        }
374
375
        if (null !== $this->expiresAt && $this->expiresAt->getTimestamp() < time()) {
376
            return false;
377
        }
378
379
        return true;
380
    }
381
382
    public function isAccountNonLocked()
383
    {
384
        return !$this->locked;
385
    }
386
387
    public function isCredentialsNonExpired()
388
    {
389
        if (true === $this->credentialsExpired) {
390
            return false;
391
        }
392
393
        if (null !== $this->credentialsExpireAt && $this->credentialsExpireAt->getTimestamp() < time()) {
394
            return false;
395
        }
396
397
        return true;
398
    }
399
400
    public function isCredentialsExpired()
401
    {
402
        return !$this->isCredentialsNonExpired();
403
    }
404
405
    public function isEnabled()
406
    {
407
        return $this->enabled;
408
    }
409
410
    public function isExpired()
411
    {
412
        return !$this->isAccountNonExpired();
413
    }
414
415
    public function isLocked()
416
    {
417
        return !$this->isAccountNonLocked();
418
    }
419
420
    public function isSuperAdmin()
421
    {
422
        return $this->hasRole(static::ROLE_SUPER_ADMIN);
423
    }
424
425
    public function isUser(UserInterface $user = null)
426
    {
427
        return null !== $user && $this->getId() === $user->getId();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface FOS\UserBundle\Model\UserInterface as the method getId() does only exist in the following implementations of said interface: DP\Core\UserBundle\Entity\User, FOS\UserBundle\Document\User, FOS\UserBundle\Entity\User, FOS\UserBundle\Model\User, FOS\UserBundle\Tests\Doctrine\DummyUser, FOS\UserBundle\Tests\TestUser.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
428
    }
429
430
    public function removeRole($role)
431
    {
432
        if (false !== $key = array_search(strtoupper($role), $this->roles, true)) {
433
            unset($this->roles[$key]);
434
            $this->roles = array_values($this->roles);
435
        }
436
437
        return $this;
438
    }
439
440
    public function setUsername($username)
441
    {
442
        $this->username = $username;
443
444
        return $this;
445
    }
446
447
    public function setUsernameCanonical($usernameCanonical)
448
    {
449
        $this->usernameCanonical = $usernameCanonical;
450
451
        return $this;
452
    }
453
454
    /**
455
     * @param \DateTime $date
456
     *
457
     * @return User
458
     */
459
    public function setCredentialsExpireAt(\DateTime $date)
460
    {
461
        $this->credentialsExpireAt = $date;
462
463
        return $this;
464
    }
465
466
    /**
467
     * @param boolean $boolean
468
     *
469
     * @return User
470
     */
471
    public function setCredentialsExpired($boolean)
472
    {
473
        $this->credentialsExpired = $boolean;
474
475
        return $this;
476
    }
477
478
    public function setEmail($email)
479
    {
480
        $this->email = $email;
481
482
        return $this;
483
    }
484
485
    public function setEmailCanonical($emailCanonical)
486
    {
487
        $this->emailCanonical = $emailCanonical;
488
489
        return $this;
490
    }
491
492
    public function setEnabled($boolean)
493
    {
494
        $this->enabled = (Boolean) $boolean;
495
496
        return $this;
497
    }
498
499
    /**
500
     * Sets this user to expired.
501
     *
502
     * @param Boolean $boolean
503
     *
504
     * @return User
505
     */
506
    public function setExpired($boolean)
507
    {
508
        $this->expired = (Boolean) $boolean;
509
510
        return $this;
511
    }
512
513
    /**
514
     * @param \DateTime $date
515
     *
516
     * @return User
517
     */
518
    public function setExpiresAt(\DateTime $date)
519
    {
520
        $this->expiresAt = $date;
521
522
        return $this;
523
    }
524
525
    public function setPassword($password)
526
    {
527
        $this->password = $password;
528
529
        return $this;
530
    }
531
532
    public function setSuperAdmin($boolean)
533
    {
534
        if (true === $boolean) {
535
            $this->addRole(static::ROLE_SUPER_ADMIN);
536
        } else {
537
            $this->removeRole(static::ROLE_SUPER_ADMIN);
538
        }
539
540
        return $this;
541
    }
542
543
    public function setPlainPassword($password)
544
    {
545
        $this->plainPassword = $password;
546
547
        return $this;
548
    }
549
550
    public function setLastLogin(\DateTime $time)
551
    {
552
        $this->lastLogin = $time;
553
554
        return $this;
555
    }
556
557
    public function setLocked($boolean)
558
    {
559
        $this->locked = $boolean;
560
561
        return $this;
562
    }
563
564
    public function setConfirmationToken($confirmationToken)
565
    {
566
        $this->confirmationToken = $confirmationToken;
567
568
        return $this;
569
    }
570
571
    public function setPasswordRequestedAt(\DateTime $date = null)
572
    {
573
        $this->passwordRequestedAt = $date;
574
575
        return $this;
576
    }
577
578
    /**
579
     * Gets the timestamp that the user requested a password reset.
580
     *
581
     * @return null|\DateTime
582
     */
583
    public function getPasswordRequestedAt()
584
    {
585
        return $this->passwordRequestedAt;
586
    }
587
588
    public function isPasswordRequestNonExpired($ttl)
589
    {
590
        return $this->getPasswordRequestedAt() instanceof \DateTime &&
591
        $this->getPasswordRequestedAt()->getTimestamp() + $ttl > time();
592
    }
593
594
    public function setRoles(array $roles)
595
    {
596
        $this->roles = array();
597
598
        foreach ($roles as $role) {
599
            $this->addRole($role);
600
        }
601
602
        return $this;
603
    }
604
605
    /**
606
     * Gets the group assigned to the user.
607
     *
608
     * @return Group
609
     */
610
    public function getGroup()
611
    {
612
        return $this->group;
613
    }
614
615
    public function setGroup(Group $group)
616
    {
617
        $this->group = $group;
618
619
        return $this;
620
    }
621
622
    public function setCreatedAt(\DateTime $date)
623
    {
624
        $this->createdAt = $date;
625
626
        return $this;
627
    }
628
629
    public function getCreatedAt()
630
    {
631
        return $this->createdAt;
632
    }
633
634
    /**
635
     * @param bool $admin
636
     */
637 View Code Duplication
    public function setAdmin($admin)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
638
    {
639
        if ($admin == true && !$this->isAdmin()) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
640
            $this->addRole(static::ROLE_ADMIN);
641
        }
642
        elseif ($admin == false && $this->isAdmin()) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
643
            $this->removeRole(static::ROLE_ADMIN);
644
        }
645
    }
646
647
    /**
648
     * Is current user an admin ?
649
     *
650
     * @return bool
651
     */
652
    public function isAdmin()
653
    {
654
        return $this->hasRole(static::ROLE_ADMIN);
655
    }
656
657
    /**
658
     * @param bool $userAdmin
659
     */
660 View Code Duplication
    public function setUserAdmin($userAdmin)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
661
    {
662
        if ($userAdmin == true && !$this->isSuperAdmin()) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
663
            $this->addRole(static::ROLE_SUPER_ADMIN);
664
        }
665
        elseif ($userAdmin == false && $this->isSuperAdmin()) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
666
            $this->removeRole(static::ROLE_SUPER_ADMIN);
667
        }
668
    }
669
670
    /**
671
     * @Assert\Callback(groups={"Adding","Editing"})
672
     */
673
    public function validateGroup(ExecutionContextInterface $context)
674
    {
675
        if ($this->isSuperAdmin() && $this->getGroup() !== null) {
676
            $context->addViolationAt(
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Valida...rface::addViolationAt() has been deprecated with message: Deprecated since version 2.5, to be removed in Symfony 3.0. Use {@link Context\ExecutionContextInterface::buildViolation()} instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
677
                'group',
678
                'user_admin.assert.group.super_admin'
679
            );
680
        }
681
        elseif (!$this->isSuperAdmin() && $this->getGroup() === null) {
682
            $context->addViolationAt(
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Valida...rface::addViolationAt() has been deprecated with message: Deprecated since version 2.5, to be removed in Symfony 3.0. Use {@link Context\ExecutionContextInterface::buildViolation()} instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
683
                'group',
684
                'user_admin.assert.group.empty'
685
            );
686
        }
687
    }
688
689
    public function __toString()
690
    {
691
        return (string) $this->getUsername();
692
    }
693
}
694