Diff Git HTML — Migration PHP 5 → PHP 8.x
Suppression Ajout Contexte
diff --git a/UserAccount.php b/UserAccount.php
--- a/UserAccount.php
+++ b/UserAccount.php
@@ -1,180 +1,170 @@
1
-<?php
1
+<?php
2
+
3
+declare(strict_types=1);
4
5
-/**
6
- * Classe PHP 5 "complète"
7
- * Compatible style PHP 5.x
8
- */
5
+/**
6
+ * PHP 8.x moderne
7
+ *
8
+ * Équivalent conceptuel de la classe PHP 5 ci-dessus,
9
+ * mais avec les apports modernes introduits progressivement
10
+ * entre PHP 7.4 et PHP 8.x.
11
+ */
12
+enum UserStatus: string
13
+{
14
+ case Pending = 'pending';
15
+ case Active = 'active';
16
+ case Suspended = 'suspended';
17
+}
18
+
19
+final readonly class UserId
20
+{
21
+ public function __construct(
22
+ public int $value
23
+ ) {
24
+ if ($this->value <= 0) {
25
+ throw new InvalidArgumentException('L\'identifiant doit être positif.');
26
+ }
27
+ }
28
+}
29
30
-class UserAccount
30
+final class UserAccount
31
{
32
- /**
33
- * @var int
34
- */
35
- private $id;
36
- /**
37
- * @var string
38
- */
39
- private $email;
40
- /**
41
- * @var string
42
- */
43
- private $passwordHash;
44
- /**
45
- * @var string
46
- */
47
- private $status;
48
- /**
49
- * @var array
50
- */
51
- private $roles = array();
52
- /**
53
- * @var DateTime
54
- */
55
- private $createdAt;
56
- /**
57
- * @var DateTime|null
58
- */
59
- private $updatedAt;
32
+ public function __construct(
33
+ private readonly UserId $id,
34
+ private string $email,
35
+ private string $passwordHash,
36
+ private UserStatus $status = UserStatus::Pending,
37
+ private array $roles = [],
38
+ private readonly DateTimeImmutable $createdAt = new DateTimeImmutable(),
39
+ private ?DateTimeImmutable $updatedAt = null,
40
+ ) {
41
+ $this->email = self::normalizeEmail($this->email);
42
+
43
+ if (!password_get_info($this->passwordHash)['algo']) {
44
+ throw new InvalidArgumentException('Le hash du mot de passe est invalide.');
45
+ }
46
+
47
+ $this->roles = array_values(array_unique(array_map('strval', $this->roles)));
48
+ }
60
- public function __construct($id, $email, $plainPassword, array $roles)
61
- {
62
- $this->id = (int) $id;
63
- $this->setEmail($email);
64
- $this->setPassword($plainPassword);
65
- $this->roles = $roles;
66
- $this->status = 'pending';
67
- $this->createdAt = new DateTime();
68
- $this->updatedAt = null;
69
- }
49
+ public static function register(
50
+ int $id,
51
+ string $email,
52
+ string $plainPassword,
53
+ array $roles = ['ROLE_USER'],
54
+ ): self {
55
+ return new self(
56
+ id: new UserId($id),
57
+ email: $email,
58
+ passwordHash: password_hash($plainPassword, PASSWORD_DEFAULT),
59
+ roles: $roles,
60
+ );
61
+ }
70
- public function getId()
62
+ public function id(): int
63
{
71
- return $this->id;
64
+ return $this->id->value;
65
}
72
- public function getEmail()
66
+ public function email(): string
67
{
68
return $this->email;
69
}
73
- public function setPassword($plainPassword)
70
+ public function changePassword(string $plainPassword): void
71
{
74
- if (!is_string($plainPassword) || strlen($plainPassword) < 8) {
72
+ if (mb_strlen($plainPassword) < 8) {
73
throw new InvalidArgumentException('Le mot de passe doit contenir au moins 8 caractères.');
74
}
75
- $this->passwordHash = sha1($plainPassword);
75
+ $this->passwordHash = password_hash($plainPassword, PASSWORD_DEFAULT);
76
$this->touch();
76
- return $this;
77
}
77
- public function checkPassword($plainPassword)
78
+ public function verifyPassword(string $plainPassword): bool
79
{
78
- return $this->passwordHash === sha1($plainPassword);
80
+ return password_verify($plainPassword, $this->passwordHash);
81
}
79
- public function getStatus()
82
+ public function status(): UserStatus
83
{
80
- return $this->status;
84
+ return $this->status;
85
}
81
- public function activate()
86
+ public function activate(): void
87
{
82
- if ($this->status === 'active') {
88
+ if ($this->status === UserStatus::Active) {
83
- return $this;
89
+ return;
90
}
84
- $this->status = 'active';
91
+ $this->status = UserStatus::Active;
92
$this->touch();
85
- return $this;
93
}
86
- public function suspend()
94
+ public function suspend(): void
95
{
87
- $this->status = 'suspended';
96
+ if ($this->status === UserStatus::Suspended) {
97
+ return;
98
+ }
99
+ $this->status = UserStatus::Suspended;
100
$this->touch();
88
- return $this;
101
}
89
- public function addRole($role)
102
+ public function addRole(string $role): void
103
{
104
+ $role = strtoupper(trim($role));
105
+
106
+ if ($role === '') {
107
+ throw new InvalidArgumentException('Le rôle ne peut pas être vide.');
108
+ }
109
110
if (!in_array($role, $this->roles, true)) {
90
- $this->roles[] = $role;
111
+ $this->roles[] = $role;
112
+ sort($this->roles);
113
$this->touch();
114
}
91
- return $this;
115
}
92
- public function getRoles()
116
+ public function roles(): array
117
{
118
return $this->roles;
119
}
93
- public function getCreatedAt()
120
+ public function createdAt(): DateTimeImmutable
121
{
122
return $this->createdAt;
123
}
94
- public function getUpdatedAt()
124
+ public function updatedAt(): ?DateTimeImmutable
125
{
126
return $this->updatedAt;
127
}
95
- public function toArray()
128
+ public function toArray(): array
129
{
96
- return array(
130
+ return [
97
- 'id' => $this->id,
131
+ 'id' => $this->id(),
132
'email' => $this->email,
98
- 'status' => $this->status,
133
+ 'status' => $this->status->value,
134
'roles' => $this->roles,
99
- 'created_at' => $this->createdAt->format('Y-m-d H:i:s'),
135
+ 'created_at' => $this->createdAt->format(DateTimeInterface::ATOM),
100
- 'updated_at' => $this->updatedAt ? $this->updatedAt->format('Y-m-d H:i:s') : null,
136
+ 'updated_at' => $this->updatedAt?->format(DateTimeInterface::ATOM),
101
- );
137
+ ];
138
}
139
+ public function jsonSerialize(): array
140
+ {
141
+ return $this->toArray();
142
+ }
102
- private function touch()
143
+ private function touch(): void
144
{
103
- $this->updatedAt = new DateTime();
145
+ $this->updatedAt = new DateTimeImmutable();
146
}
147
+ private static function normalizeEmail(string $email): string
148
+ {
149
+ $email = strtolower(trim($email));
150
+
151
+ if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
152
+ throw new InvalidArgumentException('Adresse email invalide.');
153
+ }
154
+
155
+ return $email;
156
+ }
157
}
Ce diff met surtout en avant : strict_types, enum, readonly, typed properties, constructor property promotion, DateTimeImmutable, password_hash/password_verify, nullsafe operator et signatures modernes.