black/email
PHP 5.4+ value object for safer email handling. Validates email format (throws on invalid), exposes recipient/domain/tld getters, array parsing, and equality checks. Note: relies on FILTER_VALIDATE_EMAIL; limited for non-ASCII and provider rules.
Installation:
composer require black/email
Verify the package is listed in composer.json under require.
First Use Case: Validate an email address in a Laravel controller or request handler:
use Email\Email;
$email = new Email('user@example.com');
If invalid, Email\Exception\InvalidEmailException is thrown.
Where to Look First:
src/Email/Email.php for the core class and validation logic.tests/ for edge-case examples (e.g., international domains, subdomains).Validation in Requests: Use in Laravel Form Requests or API validation:
public function rules()
{
return [
'email' => ['required', function ($attribute, $value, $fail) {
try {
new Email($value);
} catch (\Email\Exception\InvalidEmailException $e) {
$fail($e->getMessage());
}
}],
];
}
Domain/Recipient Extraction: Parse emails for business logic (e.g., routing):
$email = new Email('support@sub.example.com');
$domain = $email->getDomain(); // 'sub.example.com'
$tld = $email->getTld(); // 'com'
$localPart = $email->getLocalPart(); // 'support'
Normalization: Standardize emails before storage (e.g., lowercase):
$normalized = (new Email('User@Example.COM'))->getValue();
// Returns 'user@example.com'
Collection Processing: Validate batches of emails (e.g., bulk imports):
$emails = collect(['a@b.com', 'invalid']);
$validEmails = $emails->map(fn($email) => new Email($email))->filter(fn($e) => true);
Laravel Service Providers:
Bind the Email class to the container for dependency injection:
$this->app->bind(Email::class, fn() => new Email(request('email')));
API Responses: Return parsed email components in JSON:
return response()->json([
'email' => (new Email($request->email))->getValueAsArray()
]);
Testing:
Mock Email in unit tests to avoid DNS checks:
$this->partialMock(Email::class, ['validate']);
DNS Validation:
The package only validates format, not DNS records (MX/A). Avoid assuming new Email('nonexistent@domain') is "safe" to send emails to.
Exception Handling:
Uncaught InvalidEmailException will halt execution. Use try-catch or Laravel’s abort():
try {
$email = new Email($input);
} catch (InvalidEmailException $e) {
abort(422, $e->getMessage());
}
International Domains:
Supports Unicode (e.g., 用户@例子.测试), but ensure your Laravel environment (e.g., database, mail server) handles UTF-8.
Case Sensitivity:
getValue() returns lowercase, but getLocalPart()/getDomain() preserve original case. Normalize if consistency is critical.
Validation Errors:
Check the exception message for format issues (e.g., missing @, invalid TLD). Example:
try {
new Email('user@.com');
} catch (InvalidEmailException $e) {
// Throws: "The email address is invalid."
}
Edge Cases: Test with:
user@sub.domain.co.ukuser+tag@example.com"user name"@example.comCustom Validation: Extend the class to add rules (e.g., allow only specific domains):
class CustomEmail extends Email
{
public function __construct(string $email)
{
if (!str_ends_with($email, ['@gmail.com', '@yahoo.com'])) {
throw new InvalidEmailException('Only Gmail/Yahoo allowed.');
}
parent::__construct($email);
}
}
Laravel Validation: Create a custom rule:
use Illuminate\Contracts\Validation\Rule;
class ValidEmail implements Rule
{
public function passes($attribute, $value)
{
try {
new Email($value);
return true;
} catch (InvalidEmailException) {
return false;
}
}
}
Usage:
'email' => ['required', new ValidEmail],
Performance: For bulk operations, cache parsed components (e.g., TLDs) if validation is repeated.
checkdnsrr() (case-insensitive). Override if needed (e.g., for testing).How can I help you explore Laravel packages today?