19declare(strict_types=1);
55 if ($this->isDomainPartAscii && $this->isLocalPartAscii) {
56 $this->isAscii =
true;
58 $this->isAscii =
false;
99 return mb_check_encoding($str,
'ASCII');
104 $address = trim($address);
106 if (substr_count($address,
'@') !== 1) {
107 throw new \InvalidArgumentException(
"Email must contain exactly one '@' character.");
110 [$local, $domain] = explode(
'@', $address, 2);
112 if ($local ===
'' || $domain ===
'') {
113 throw new \InvalidArgumentException(
"Email must have non-empty local and domain parts.");
116 $this->addressFull = $address;
122 [, $domain] = explode(
'@', $address, 2);
124 $this->isDomainPartAscii = $this->
checkAscii($domain);
126 if ($domain ===
'localhost') {
130 if (preg_match(
'/[\p{C}\p{Z}]/u', $domain)) {
131 throw new \InvalidArgumentException(
"Domain part contains invalid characters (e.g., whitespace or control).");
134 if (str_contains($domain,
'..')) {
135 throw new \InvalidArgumentException(
"Domain part contains consecutive dots.");
140 if (substr_count($domain,
'.') < 1) {
141 throw new \InvalidArgumentException(
"Domain must contain at least one dot except for 'localhost'.");
144 if (strlen($domain) > 254) {
145 throw new \InvalidArgumentException(
"Domain part exceeds 254 character limit.");
148 $labels = explode(
'.', $domain);
149 foreach ($labels as $label) {
150 if (strlen($label) > 63) {
151 throw new \InvalidArgumentException(
"Domain label exceeds 63 character limit.");
155 throw new \InvalidArgumentException(
"Domain contains an empty label.");
158 if ($label[0] ===
'-' || $label[strlen($label) - 1] ===
'-') {
159 throw new \InvalidArgumentException(
"Domain labels must not start or end with a hyphen.");
168 [$local,] = explode(
'@', $address, 2);
170 if (!mb_check_encoding($local,
'UTF-8')) {
171 throw new \InvalidArgumentException(
"Local part is not valid UTF-8.");
174 if ($local[0] ===
'.' || str_ends_with($local,
'.')) {
175 throw new \InvalidArgumentException(
"Local part cannot start or end with a dot.");
178 if (str_contains($local,
'..')) {
179 throw new \InvalidArgumentException(
"Local part cannot contain consecutive dots.");
183 if (str_starts_with($local,
'"') && str_ends_with($local,
'"')) {
184 $local_strip_quotes = substr($local, 1, -1);
186 $local_strip_quotes = $local;
189 if (preg_match(
'/[\x00-\x1F\x7F]/', $local_strip_quotes)) {
190 throw new \InvalidArgumentException(
"Local part contains unsupported control characters or invalid escape sequences.");
194 if (preg_match(
'/^[a-zA-Z0-9!#$%&\'*+\/=?^_`{|}~\.-]+$/', $local_strip_quotes)) {
195 $this->isLocalPartAscii =
true;
197 $this->isLocalPartAscii =
false;
200 if (!self::isAllowedIntlUnicode($local_strip_quotes)) {
201 throw new \InvalidArgumentException(
"Local part is not a valid Unicode string.");
223 if (!preg_match(
'/^[\p{L}\p{N}!#$%&\'*+\/=?^_`{|}~\.-]+$/u', $string)) {
An Email Address is a common personal address for people online.
digestDomainPart(string $address)
digestFullAddress(string $address)
__construct(string $address_Full)
static isAllowedIntlUnicode(string $string)
digestLocalPart(string $address)