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

← Subscribers | Next: Webhooks →