Email Validation #
Advanced email validation system with disposable detection and syntax checking.
Overview #
The validation system provides:
- Syntax Validation: RFC 5322 compliant email format checking
- Disposable Detection: Block temporary/throwaway email services
- MX Record Verification: Verify domain has mail servers
- Statistics Tracking: Monitor validation results
- Customizable Rules: Configure validation strictness
EmailValidator Class #
Location: Classes/EmailValidator.php
Basic Usage #
use Albrightlabs\Campaign\Classes\EmailValidator;
$validator = new EmailValidator();
$result = $validator->validate('user@example.com');
if ($result['is_valid']) {
// Email is valid
} else {
// Show error: $result['reason']
}
Validation Levels #
// 1. Syntax only (fast)
if ($validator->validateSyntax($email)) {
echo "Valid format";
}
// 2. Check disposable
if ($validator->isDisposable($email)) {
echo "Disposable email detected";
}
// 3. MX records
if ($validator->hasMxRecords($email)) {
echo "Domain has mail servers";
}
// 4. Full validation
$result = $validator->validate($email);
Validation Result #
[
'is_valid' => true|false,
'reason' => 'Error message if invalid',
'checks' => [
'syntax' => true|false,
'disposable' => true|false,
'mx_records' => true|false
]
]
DisposableEmailChecker #
Location: Classes/DisposableEmailChecker.php
Features #
- 29,000+ known disposable domains
- Automatic list updates
- Fast hash-based lookup
- Configurable update interval
Usage #
use Albrightlabs\Campaign\Classes\DisposableEmailChecker;
$checker = new DisposableEmailChecker();
if ($checker->isDisposable('temp@mailinator.com')) {
throw new ValidationException([
'email' => 'Disposable email addresses not allowed'
]);
}
Domain List Management #
// Force update disposable list
$checker->updateDisposableList();
// Get list stats
$count = count($checker->getDisposableDomains());
echo "Tracking {$count} disposable domains";
ValidationStatistics #
Location: Classes/ValidationStatistics.php
Tracking Results #
use Albrightlabs\Campaign\Classes\ValidationStatistics;
$stats = new ValidationStatistics();
// Record validation
$stats->recordValidation([
'email' => 'user@example.com',
'is_valid' => true,
'reason' => null,
'organization_id' => $org->id
]);
Getting Statistics #
// Overall stats
$overall = $stats->getOverallStats($org->id);
/*
[
'total' => 1500,
'valid' => 1350,
'invalid' => 150,
'success_rate' => 90.0
]
*/
// Failure reasons
$reasons = $stats->getFailureReasons($org->id);
/*
[
'disposable' => 85,
'invalid_syntax' => 45,
'no_mx_records' => 20
]
*/
Validation Rules #
Model Validation #
public $rules = [
'email' => [
'required',
'email:rfc,dns', // Laravel built-in
function($attribute, $value, $fail) {
$validator = new EmailValidator();
$result = $validator->validate($value);
if (!$result['is_valid']) {
$fail($result['reason']);
}
}
]
];
Custom Validation Rule #
// Create reusable rule
use Illuminate\Contracts\Validation\Rule;
class ValidEmailRule implements Rule
{
public function passes($attribute, $value)
{
$validator = new EmailValidator();
$result = $validator->validate($value);
return $result['is_valid'];
}
public function message()
{
return 'The email address is not valid.';
}
}
// Usage
public $rules = [
'email' => ['required', new ValidEmailRule]
];
Configuration #
Validation Settings #
// config/campaign.php
return [
'validation' => [
'enabled' => true,
'check_syntax' => true,
'check_disposable' => true,
'check_mx_records' => false, // Slower
'block_role_emails' => false, // admin@, noreply@
'allow_subaddress' => true, // user+tag@domain.com
'disposable_update_interval' => 30 // days
]
];
MX Record Verification #
private function checkMxRecords($email)
{
$domain = substr(strrchr($email, "@"), 1);
// Get MX records
$mxRecords = [];
if (getmxrr($domain, $mxRecords)) {
return true;
}
// Fallback to A record
return checkdnsrr($domain, 'A');
}
Common Validation Patterns #
Signup Form #
public function onSubscribe()
{
$email = post('email');
// Validate
$validator = new EmailValidator();
$result = $validator->validate($email);
if (!$result['is_valid']) {
throw new ValidationException([
'email' => $result['reason']
]);
}
// Create subscriber
Subscriber::create([
'email' => $email,
'organization_id' => $org->id
]);
}
Import Validation #
$validator = new EmailValidator();
$errors = [];
foreach ($csvRows as $index => $row) {
$result = $validator->validate($row['email']);
if (!$result['is_valid']) {
$errors[] = "Row {$index}: {$result['reason']}";
}
}
if (!empty($errors)) {
// Show errors to user
}
Error Messages #
Standard Messages #
const ERROR_INVALID_SYNTAX = 'Invalid email format';
const ERROR_DISPOSABLE = 'Disposable email addresses not allowed';
const ERROR_NO_MX_RECORDS = 'Email domain has no mail servers';
const ERROR_ROLE_EMAIL = 'Role-based email addresses not allowed';
const ERROR_BLOCKED_DOMAIN = 'Email domain is blocked';
Custom Messages #
public function getErrorMessage($reason)
{
return match($reason) {
'disposable' => 'Please use a permanent email address',
'invalid_syntax' => 'Email format is incorrect',
'no_mx_records' => 'Email domain appears invalid',
default => 'Email validation failed'
};
}
Performance Considerations #
Caching #
// Cache MX record checks
Cache::remember("mx_{$domain}", 3600, function() use ($domain) {
return $this->checkMxRecords($domain);
});
// Cache disposable list
Cache::remember('disposable_domains', 86400, function() {
return $this->loadDisposableList();
});
Batch Validation #
$validator = new EmailValidator();
$results = [];
foreach ($emails as $email) {
$results[$email] = $validator->validate($email);
}
Troubleshooting #
False Positives #
// Email marked disposable but isn't
// Check disposable list
$checker = new DisposableEmailChecker();
$domains = $checker->getDisposableDomains();
if (in_array($domain, $domains)) {
// Remove from list or whitelist
}
MX Check Failures #
// DNS resolution issues
// Disable MX checking
config(['campaign.validation.check_mx_records' => false]);
// Or use external service
// getmxrr() may fail on some hosts