immutable readonly. trims whitespace, splits on @, lowercases the domain (local-part case preserved per RFC 5321), validates with FILTER_VALIDATE_EMAIL after normalization. throws InvalidArgumentException on empty / missing-@ / malformed input. exposes value(), getDomain(), equals(), __toString(). all 7 EmailAddressTest cases green; 9 tests total pass.
53 lines
1.2 KiB
PHP
53 lines
1.2 KiB
PHP
<?php
|
|
|
|
namespace App\Shared\ValueObject;
|
|
|
|
use InvalidArgumentException;
|
|
|
|
final readonly class EmailAddress
|
|
{
|
|
private string $normalized;
|
|
|
|
private string $domain;
|
|
|
|
private const ERROR_MESSAGE = 'Invalid email address:';
|
|
|
|
public function __construct(string $email)
|
|
{
|
|
$trimmed = trim($email);
|
|
|
|
if ($trimmed === '' || ! str_contains($trimmed, '@')) {
|
|
throw new InvalidArgumentException(self::ERROR_MESSAGE." $email");
|
|
}
|
|
|
|
[$local, $domain] = explode('@', $trimmed, 2);
|
|
$this->domain = mb_strtolower($domain);
|
|
$normalized = $local.'@'.$this->domain;
|
|
|
|
if (filter_var($normalized, FILTER_VALIDATE_EMAIL) === false) {
|
|
throw new InvalidArgumentException(self::ERROR_MESSAGE." $email");
|
|
}
|
|
|
|
$this->normalized = $normalized;
|
|
}
|
|
|
|
public function value(): string
|
|
{
|
|
return $this->normalized;
|
|
}
|
|
|
|
public function equals(self $other): bool
|
|
{
|
|
return $this->normalized === $other->normalized;
|
|
}
|
|
|
|
public function getDomain(): string
|
|
{
|
|
return $this->domain;
|
|
}
|
|
|
|
public function __toString(): string
|
|
{
|
|
return $this->normalized;
|
|
}
|
|
}
|