webo3 / smtp-email-validation
Comprehensive email validation: syntax, disposable domain detection, DNS checks, and SMTP verification with scoring.
Installs: 6
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 1
Forks: 0
Open Issues: 0
pkg:composer/webo3/smtp-email-validation
Requires
- php: >=8.0
Requires (Dev)
- phpunit/phpunit: ^10.0
README
Comprehensive PHP email validation library with syntax checking, disposable domain detection, DNS verification, SMTP mailbox validation, and a scoring system.
Requires: PHP 8.0+ with fsockopen, getmxrr, dns_get_record and ability to connect to remote port 25.
Installation
composer require webo3/smtp-email-validation
Usage
Full Validation with Scoring (Recommended)
use webO3\SmtpEmailValidation\EmailValidator; $validator = new EmailValidator( fromEmail: 'noreply@yourdomain.com', validThreshold: 60, // Score >= 60 considered valid timeout: 30, ); $result = $validator->validate('user@example.com'); echo $result->score; // 0-100 echo $result->isValid; // true/false echo $result->email; // 'user@example.com' // Per-check breakdown foreach ($result->checks as $check) { echo "{$check->name}: {$check->status->value} (score: {$check->score})\n"; } // Get a specific check $smtpCheck = $result->getCheck('smtp'); // Convert to array (useful for JSON APIs) $array = $result->toArray();
Quick Static Method
use webO3\SmtpEmailValidation\Validator; // Full validation with scoring $result = Validator::validate('user@example.com', from: 'noreply@yourdomain.com'); echo $result->score; // 0-100
Custom Check Configuration
Run only specific checks or adjust settings:
use webO3\SmtpEmailValidation\EmailValidator; use webO3\SmtpEmailValidation\Check\SyntaxCheck; use webO3\SmtpEmailValidation\Check\DisposableDomainCheck; use webO3\SmtpEmailValidation\Check\DnsCheck; // Lightweight validation without SMTP (no port 25 needed) $validator = new EmailValidator( validThreshold: 50, checks: [ new SyntaxCheck(), new DisposableDomainCheck(cacheTtl: 3600), // refresh list every hour new DnsCheck(), ], ); $result = $validator->validate('user@example.com');
Legacy SMTP-Only Check (Backward Compatible)
use webO3\SmtpEmailValidation\Validator; $response = Validator::test('noreply@yourdomain.com', 'user@example.com'); // Returns: ['code' => 250, 'success' => true, 'msg' => 'Email is valid', 'debug' => [...]]
Examples
Runnable example scripts are provided in the examples/ directory:
# Full validation with score breakdown php examples/basic-validation.php user@example.com # Syntax-only check (no network calls) php examples/syntax-only.php user@example.com # Batch validation from arguments or file php examples/batch-validation.php user1@gmail.com user2@mailinator.com php examples/batch-validation.php --file emails.txt # Custom checks (skip SMTP, JSON output) php examples/custom-checks.php user@example.com
Validation Checks
The library runs four checks in sequence:
| Check | Weight | Description |
|---|---|---|
| Syntax | 25% | RFC 5322 format validation (local-part length, domain structure, etc.) |
| Disposable | 15% | Checks against a regularly updated list of disposable email providers |
| DNS | 25% | MX records, A-record fallback, SPF and DMARC presence |
| SMTP | 35% | Connects to mail server and verifies the mailbox exists |
If syntax validation fails, all subsequent checks are skipped and the score is 0.
Disposable Domain List
The disposable domain check automatically fetches the latest list from the disposable-email-domains community project and caches it locally (default: 24 hours). If the fetch fails, a bundled fallback list is used.
You can configure the cache TTL and location:
new DisposableDomainCheck( cachePath: '/path/to/cache.json', // default: sys_get_temp_dir() cacheTtl: 3600, // refresh every hour (default: 86400) );
Scoring System
Each check produces a score from 0-100, weighted by importance:
- Overall score = weighted average of non-skipped checks (0-100)
- isValid = score >= threshold (default: 60)
Example Scores
| Scenario | Score |
|---|---|
| Valid email, all checks pass | 100 |
| Valid email, disposable domain | ~85 |
| Valid email, SMTP greylisting (451/452) | ~79 |
| Valid email, SMTP rejected | ~65 |
| Invalid syntax | 0 |
DNS Sub-Scoring
| DNS Component | Points |
|---|---|
| MX records found | 40 |
| A-record fallback (no MX) | 20 |
| SPF record present | 30 |
| DMARC record present | 30 |
SMTP Sub-Scoring
| SMTP Stage | Points |
|---|---|
| Connected to MX server | 20 |
| Valid 220 greeting | 20 |
| RCPT TO accepted (250) | 60 |
| Greylisting (451/452) | 40 |
Legacy Response Codes
When using Validator::test():
| Code | Success | Description |
|---|---|---|
| 10 | false | No MX records found for domain |
| 15 | false | Could not connect to any MX servers |
| 20 | false | Connection timeout |
| 25 | false | Server did not return 220 greeting |
| 250 | true | Email is valid |
| 451/452 | true | Temporary rejection (greylisting) |
| Other | false | Email rejected |
Testing
composer install vendor/bin/phpunit
Warning
- Many hosting providers and ISPs block direct connections to remote port 25.
- Use a valid FROM address — some blacklists may block specific sender addresses.