Code Coverage |
||||||||||||||||
Lines |
Branches |
Paths |
Functions and Methods |
Classes and Traits |
||||||||||||
| Total | |
79.63% |
43 / 54 |
|
93.33% |
14 / 15 |
|
92.86% |
13 / 14 |
|
92.31% |
12 / 13 |
CRAP | |
0.00% |
0 / 1 |
| Identity | |
79.63% |
43 / 54 |
|
93.33% |
14 / 15 |
|
92.86% |
13 / 14 |
|
92.31% |
12 / 13 |
14.07 | |
0.00% |
0 / 1 |
| __construct | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| create | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| getId | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| getEmail | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| getUsername | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| getPasswordHash | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| getRoles | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| isComplete | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| getValidationToken | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| pullEvents | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| complete | |
100.00% |
13 / 13 |
|
100.00% |
3 / 3 |
|
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
| changeEmail | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| generateValidationToken | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| 1 | <?php |
| 2 | |
| 3 | namespace App\Auth\Domain\Model; |
| 4 | |
| 5 | use App\Auth\Domain\Event\EmailChangedEvent; |
| 6 | use App\Auth\Domain\Event\IdentityCompletedEvent; |
| 7 | use App\Auth\Domain\Event\IdentityCreatedEvent; |
| 8 | use App\Auth\Domain\Exception\AuthErrorCode; |
| 9 | use App\Shared\Domain\Event\DomainEvent; |
| 10 | use App\Shared\Domain\Exception\ValidationException; |
| 11 | use App\Shared\Domain\Model\EntityId; |
| 12 | use App\Shared\Domain\Model\ProducesDomainEvents; |
| 13 | use App\Shared\Domain\Validation\Validator; |
| 14 | |
| 15 | final class Identity implements ProducesDomainEvents |
| 16 | { |
| 17 | |
| 18 | /** |
| 19 | * @var array<DomainEvent> |
| 20 | */ |
| 21 | private array $events = []; |
| 22 | |
| 23 | /** |
| 24 | * @param EntityId $id |
| 25 | * @param string $email |
| 26 | * @param string $username |
| 27 | * @param string $passwordHash |
| 28 | * @param list<string> $roles |
| 29 | * @param boolean $isComplete |
| 30 | * @param string $validationToken |
| 31 | * @throws ValidationException |
| 32 | */ |
| 33 | public function __construct( |
| 34 | private readonly EntityId $id, |
| 35 | public readonly string $email, |
| 36 | private readonly string $username, |
| 37 | private readonly string $passwordHash, |
| 38 | private readonly array $roles = ['ROLE_USER'], |
| 39 | private readonly bool $isComplete = false, |
| 40 | private readonly string $validationToken = '' |
| 41 | ) { |
| 42 | $validator = new Validator(); |
| 43 | $validator |
| 44 | ->requireNotEmpty('id', $this->id) |
| 45 | ->requireNotEmpty('email', $this->email) |
| 46 | ->requireValidEmail('email', $this->email) |
| 47 | ->require('username', fn () => (bool)preg_match('/^[a-zA-Z0-9_-]+$/', $this->username), AuthErrorCode::INVALID_USERNAME) |
| 48 | ->requireNotEmpty('passwordHash', $this->passwordHash) |
| 49 | ->requireNotEmpty('roles', $this->roles) |
| 50 | ->validate(); |
| 51 | } |
| 52 | |
| 53 | /** |
| 54 | * @param EntityId $id |
| 55 | * @param string $email |
| 56 | * @param string $username |
| 57 | * @param string $passwordHash |
| 58 | * @param list<string> $roles |
| 59 | * @return self |
| 60 | */ |
| 61 | public static function create( |
| 62 | EntityId $id, |
| 63 | string $email, |
| 64 | string $username, |
| 65 | string $passwordHash, |
| 66 | array $roles = ['ROLE_USER'] |
| 67 | ): self { |
| 68 | $newUser = new self( |
| 69 | id: $id, |
| 70 | email: $email, |
| 71 | username: $username, |
| 72 | passwordHash: $passwordHash, |
| 73 | roles: $roles, |
| 74 | isComplete: false, |
| 75 | ); |
| 76 | $newUser->events = [new IdentityCreatedEvent($newUser)]; |
| 77 | return $newUser; |
| 78 | } |
| 79 | |
| 80 | public function getId(): EntityId |
| 81 | { |
| 82 | return $this->id; |
| 83 | } |
| 84 | |
| 85 | public function getEmail(): string |
| 86 | { |
| 87 | return $this->email; |
| 88 | } |
| 89 | public function getUsername(): string |
| 90 | { |
| 91 | return $this->username; |
| 92 | } |
| 93 | public function getPasswordHash(): string |
| 94 | { |
| 95 | return $this->passwordHash; |
| 96 | } |
| 97 | |
| 98 | /** |
| 99 | * @return list<string> |
| 100 | */ |
| 101 | public function getRoles(): array |
| 102 | { |
| 103 | return $this->roles; |
| 104 | } |
| 105 | |
| 106 | public function isComplete(): bool |
| 107 | { |
| 108 | return $this->isComplete; |
| 109 | } |
| 110 | |
| 111 | public function getValidationToken(): string |
| 112 | { |
| 113 | return $this->validationToken; |
| 114 | } |
| 115 | |
| 116 | /** |
| 117 | * @return array<DomainEvent> |
| 118 | */ |
| 119 | public function pullEvents(): array |
| 120 | { |
| 121 | $events = $this->events; |
| 122 | $this->events = []; |
| 123 | return $events; |
| 124 | } |
| 125 | |
| 126 | public function complete(): self |
| 127 | { |
| 128 | if ($this->isComplete()) { |
| 129 | return $this; |
| 130 | } |
| 131 | |
| 132 | $new = new self( |
| 133 | id: $this->id, |
| 134 | email: $this->email, |
| 135 | username: $this->username, |
| 136 | passwordHash: $this->passwordHash, |
| 137 | roles: $this->roles, |
| 138 | isComplete: true, |
| 139 | validationToken: $this->generateValidationToken() |
| 140 | ); |
| 141 | $new->events = [new IdentityCompletedEvent($new)]; |
| 142 | return $new; |
| 143 | } |
| 144 | |
| 145 | public function changeEmail(string $email): self |
| 146 | { |
| 147 | $new = new self( |
| 148 | id: $this->id, |
| 149 | email: $email, |
| 150 | username: $this->username, |
| 151 | passwordHash: $this->passwordHash, |
| 152 | roles: $this->roles, |
| 153 | isComplete: true, |
| 154 | validationToken: $this->generateValidationToken() |
| 155 | ); |
| 156 | $new->events = [new EmailChangedEvent($new)]; |
| 157 | return $new; |
| 158 | } |
| 159 | |
| 160 | private function generateValidationToken(): string |
| 161 | { |
| 162 | return EntityId::generate(); |
| 163 | } |
| 164 | } |
Below are the source code lines that represent each code path as identified by Xdebug. Please note a path is not
necessarily coterminous with a line, a line may contain multiple paths and therefore show up more than once.
Please also be aware that some paths may include implicit rather than explicit branches, e.g. an if statement
always has an else as part of its logical flow even if you didn't write one.
| 33 | public function __construct( |
| 34 | private readonly EntityId $id, |
| 35 | public readonly string $email, |
| 36 | private readonly string $username, |
| 37 | private readonly string $passwordHash, |
| 38 | private readonly array $roles = ['ROLE_USER'], |
| 39 | private readonly bool $isComplete = false, |
| 40 | private readonly string $validationToken = '' |
| 41 | ) { |
| 42 | $validator = new Validator(); |
| 43 | $validator |
| 44 | ->requireNotEmpty('id', $this->id) |
| 45 | ->requireNotEmpty('email', $this->email) |
| 46 | ->requireValidEmail('email', $this->email) |
| 47 | ->require('username', fn () => (bool)preg_match('/^[a-zA-Z0-9_-]+$/', $this->username), AuthErrorCode::INVALID_USERNAME) |
| 48 | ->requireNotEmpty('passwordHash', $this->passwordHash) |
| 49 | ->requireNotEmpty('roles', $this->roles) |
| 50 | ->validate(); |
| 51 | } |
| 145 | public function changeEmail(string $email): self |
| 146 | { |
| 147 | $new = new self( |
| 148 | id: $this->id, |
| 149 | email: $email, |
| 150 | username: $this->username, |
| 151 | passwordHash: $this->passwordHash, |
| 152 | roles: $this->roles, |
| 153 | isComplete: true, |
| 154 | validationToken: $this->generateValidationToken() |
| 155 | ); |
| 156 | $new->events = [new EmailChangedEvent($new)]; |
| 157 | return $new; |
| 158 | } |
| 128 | if ($this->isComplete()) { |
| 129 | return $this; |
| 128 | if ($this->isComplete()) { |
| 132 | $new = new self( |
| 133 | id: $this->id, |
| 134 | email: $this->email, |
| 135 | username: $this->username, |
| 136 | passwordHash: $this->passwordHash, |
| 137 | roles: $this->roles, |
| 138 | isComplete: true, |
| 139 | validationToken: $this->generateValidationToken() |
| 140 | ); |
| 141 | $new->events = [new IdentityCompletedEvent($new)]; |
| 142 | return $new; |
| 143 | } |
| 61 | public static function create( |
| 62 | EntityId $id, |
| 63 | string $email, |
| 64 | string $username, |
| 65 | string $passwordHash, |
| 66 | array $roles = ['ROLE_USER'] |
| 67 | ): self { |
| 68 | $newUser = new self( |
| 69 | id: $id, |
| 70 | email: $email, |
| 71 | username: $username, |
| 72 | passwordHash: $passwordHash, |
| 73 | roles: $roles, |
| 74 | isComplete: false, |
| 75 | ); |
| 76 | $newUser->events = [new IdentityCreatedEvent($newUser)]; |
| 77 | return $newUser; |
| 78 | } |
| 162 | return EntityId::generate(); |
| 163 | } |
| 87 | return $this->email; |
| 88 | } |
| 82 | return $this->id; |
| 83 | } |
| 95 | return $this->passwordHash; |
| 96 | } |
| 103 | return $this->roles; |
| 104 | } |
| 91 | return $this->username; |
| 92 | } |
| 113 | return $this->validationToken; |
| 114 | } |
| 108 | return $this->isComplete; |
| 109 | } |
| 121 | $events = $this->events; |
| 122 | $this->events = []; |
| 123 | return $events; |
| 124 | } |
| 47 | ->require('username', fn () => (bool)preg_match('/^[a-zA-Z0-9_-]+$/', $this->username), AuthErrorCode::INVALID_USERNAME) |
| 47 | ->require('username', fn () => (bool)preg_match('/^[a-zA-Z0-9_-]+$/', $this->username), AuthErrorCode::INVALID_USERNAME) |
| 47 | ->require('username', fn () => (bool)preg_match('/^[a-zA-Z0-9_-]+$/', $this->username), AuthErrorCode::INVALID_USERNAME) |
| 47 | ->require('username', fn () => (bool)preg_match('/^[a-zA-Z0-9_-]+$/', $this->username), AuthErrorCode::INVALID_USERNAME) |
| 47 | ->require('username', fn () => (bool)preg_match('/^[a-zA-Z0-9_-]+$/', $this->username), AuthErrorCode::INVALID_USERNAME) |
| 47 | ->require('username', fn () => (bool)preg_match('/^[a-zA-Z0-9_-]+$/', $this->username), AuthErrorCode::INVALID_USERNAME) |
| 3 | namespace App\Auth\Domain\Model; |
| 4 | |
| 5 | use App\Auth\Domain\Event\EmailChangedEvent; |
| 6 | use App\Auth\Domain\Event\IdentityCompletedEvent; |
| 7 | use App\Auth\Domain\Event\IdentityCreatedEvent; |
| 8 | use App\Auth\Domain\Exception\AuthErrorCode; |
| 9 | use App\Shared\Domain\Event\DomainEvent; |
| 10 | use App\Shared\Domain\Exception\ValidationException; |
| 11 | use App\Shared\Domain\Model\EntityId; |
| 12 | use App\Shared\Domain\Model\ProducesDomainEvents; |
| 13 | use App\Shared\Domain\Validation\Validator; |
| 14 | |
| 15 | final class Identity implements ProducesDomainEvents |
| 16 | { |
| 17 | |
| 18 | /** |
| 19 | * @var array<DomainEvent> |
| 20 | */ |
| 21 | private array $events = []; |
| 22 | |
| 23 | /** |
| 24 | * @param EntityId $id |
| 25 | * @param string $email |
| 26 | * @param string $username |
| 27 | * @param string $passwordHash |
| 28 | * @param list<string> $roles |
| 29 | * @param boolean $isComplete |
| 30 | * @param string $validationToken |
| 31 | * @throws ValidationException |
| 32 | */ |
| 33 | public function __construct( |
| 34 | private readonly EntityId $id, |
| 35 | public readonly string $email, |
| 36 | private readonly string $username, |
| 37 | private readonly string $passwordHash, |
| 38 | private readonly array $roles = ['ROLE_USER'], |
| 39 | private readonly bool $isComplete = false, |
| 40 | private readonly string $validationToken = '' |
| 41 | ) { |
| 42 | $validator = new Validator(); |
| 43 | $validator |
| 44 | ->requireNotEmpty('id', $this->id) |
| 45 | ->requireNotEmpty('email', $this->email) |
| 46 | ->requireValidEmail('email', $this->email) |
| 47 | ->require('username', fn () => (bool)preg_match('/^[a-zA-Z0-9_-]+$/', $this->username), AuthErrorCode::INVALID_USERNAME) |
| 48 | ->requireNotEmpty('passwordHash', $this->passwordHash) |
| 49 | ->requireNotEmpty('roles', $this->roles) |
| 50 | ->validate(); |
| 51 | } |
| 52 | |
| 53 | /** |
| 54 | * @param EntityId $id |
| 55 | * @param string $email |
| 56 | * @param string $username |
| 57 | * @param string $passwordHash |
| 58 | * @param list<string> $roles |
| 59 | * @return self |
| 60 | */ |
| 61 | public static function create( |
| 62 | EntityId $id, |
| 63 | string $email, |
| 64 | string $username, |
| 65 | string $passwordHash, |
| 66 | array $roles = ['ROLE_USER'] |
| 67 | ): self { |
| 68 | $newUser = new self( |
| 69 | id: $id, |
| 70 | email: $email, |
| 71 | username: $username, |
| 72 | passwordHash: $passwordHash, |
| 73 | roles: $roles, |
| 74 | isComplete: false, |
| 75 | ); |
| 76 | $newUser->events = [new IdentityCreatedEvent($newUser)]; |
| 77 | return $newUser; |
| 78 | } |
| 79 | |
| 80 | public function getId(): EntityId |
| 81 | { |
| 82 | return $this->id; |
| 83 | } |
| 84 | |
| 85 | public function getEmail(): string |
| 86 | { |
| 87 | return $this->email; |
| 88 | } |
| 89 | public function getUsername(): string |
| 90 | { |
| 91 | return $this->username; |
| 92 | } |
| 93 | public function getPasswordHash(): string |
| 94 | { |
| 95 | return $this->passwordHash; |
| 96 | } |
| 97 | |
| 98 | /** |
| 99 | * @return list<string> |
| 100 | */ |
| 101 | public function getRoles(): array |
| 102 | { |
| 103 | return $this->roles; |
| 104 | } |
| 105 | |
| 106 | public function isComplete(): bool |
| 107 | { |
| 108 | return $this->isComplete; |
| 109 | } |
| 110 | |
| 111 | public function getValidationToken(): string |
| 112 | { |
| 113 | return $this->validationToken; |
| 114 | } |
| 115 | |
| 116 | /** |
| 117 | * @return array<DomainEvent> |
| 118 | */ |
| 119 | public function pullEvents(): array |
| 120 | { |
| 121 | $events = $this->events; |
| 122 | $this->events = []; |
| 123 | return $events; |
| 124 | } |
| 125 | |
| 126 | public function complete(): self |
| 127 | { |
| 128 | if ($this->isComplete()) { |
| 129 | return $this; |
| 130 | } |
| 131 | |
| 132 | $new = new self( |
| 133 | id: $this->id, |
| 134 | email: $this->email, |
| 135 | username: $this->username, |
| 136 | passwordHash: $this->passwordHash, |
| 137 | roles: $this->roles, |
| 138 | isComplete: true, |
| 139 | validationToken: $this->generateValidationToken() |
| 140 | ); |
| 141 | $new->events = [new IdentityCompletedEvent($new)]; |
| 142 | return $new; |
| 143 | } |
| 144 | |
| 145 | public function changeEmail(string $email): self |
| 146 | { |
| 147 | $new = new self( |
| 148 | id: $this->id, |
| 149 | email: $email, |
| 150 | username: $this->username, |
| 151 | passwordHash: $this->passwordHash, |
| 152 | roles: $this->roles, |
| 153 | isComplete: true, |
| 154 | validationToken: $this->generateValidationToken() |
| 155 | ); |
| 156 | $new->events = [new EmailChangedEvent($new)]; |
| 157 | return $new; |
| 158 | } |
| 159 | |
| 160 | private function generateValidationToken(): string |
| 161 | { |
| 162 | return EntityId::generate(); |
| 163 | } |