dartcafe / email-validator
Lightweight email address validation library with optional REST API.
Installs: 12
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/dartcafe/email-validator
Requires
- php: >=8.1
Requires (Dev)
- ext-intl: *
- friendsofphp/php-cs-fixer: ^3.68
- phpunit/phpunit: ^10.5
- psalm/plugin-phpunit: ^0.19.0
- symfony/yaml: ^6.4 || ^7.0
- vimeo/psalm: ^5.25
- zircote/swagger-php: ^4
Suggests
- ext-intl: IDN normalization for internationalized domains
README
A small, framework‑agnostic PHP library to validate email addresses with.
Separates format validity from deliverability, supports domain typo suggestions, and lets you plug in custom providers for lists and DNS. File/INI list handling is offered via a small adapter.
Features
- Format-only validity (
valid) — syntax & RFC length checks plus basic domain shape. - Deliverability prediction (
sendable) — DNS: domain existence + MX records (via pluggable resolver). - Domain suggestions with typo‑distance scoring (Levenshtein or Damerau–Levenshtein)
- Pluggable list checks — allow/deny by domain or full address via a
ListProvider. - Clear result model — typed getters for PHP and compact JSON for APIs.
- A clean, typed
ValidationResultDTO and an optional OpenAPI spec for a tiny REST endpoint - PHP 8.1+, Psalm-typed, PHPUnit-tested, PSR-12 styled.
Installation
composer require dartcafe/email-validator
Requires PHP 8.1+. For Internationalized Domain Names (IDN) support, enable
ext-intl(recommended).
Quick start
<?php use Dartcafe\EmailValidator\EmailValidator; use Dartcafe\EmailValidator\Suggestion\TextDomainSuggestionProvider; // Create a validator with default providers $validator = new EmailValidator( suggestions: TextDomainSuggestionProvider::default() // common domains bundled ); // Validate an address $result = $validator->validate('ceo@gamil.com'); // Access the structured result $result->isValid(); // format-only validity (bool) $result->isSendable(); // domain has A/AAAA and MX (bool) $result->getReasons(); // list<string> format/DNS reasons $result->getWarnings(); // list<string> (e.g. deny_list:<name>) $result->getNormalized(); // ascii-lower-cased domain, same local part $result->getSuggestion(); // suggested corrected address or null $result->getSuggestionScore(); // 0.0–1.0 confidence score or null $result->getDomainExists(); // ?bool $result->getHasMx(); // ?bool $result->getLists(); // list<ListOutcome>
Example JSON (via json_encode($result)):
{
"query": "ceo@gamil.com",
"corrections": {
"normalized": "ceo@gmail.com",
"suggestion": "ceo@gmail.com",
"suggestionScore": 0.92
},
"simpleResults": {
"formatValid": true,
"isSendable": true,
"hasWarnings": false
},
"reasons": [],
"warnings": [],
"dns": { "domainExists": true, "hasMx": true },
"lists": []
}
Domain suggestions & distance metrics
By default the validator uses a curated set of popular domains and Levenshtein distance. You can provide your own list and choose Damerau–Levenshtein:
use Dartcafe\EmailValidator\Suggestion\ArrayDomainSuggestionProvider; use Dartcafe\EmailValidator\Suggestion\Distance; // Provide your own candidate domains: $domains = ['gmail.com', 'yahoo.com', 'outlook.com']; // Choose the metric (LEVENSHTEIN | DAMERAU_LEVENSHTEIN) $suggestions = ArrayDomainSuggestionProvider::fromArray($domains, Distance::DAMERAU_LEVENSHTEIN); $validator = new EmailValidator(suggestions: $suggestions); $res = $validator->validate('user@gmil.com'); $res->getSuggestion(); // "user@gmail.com" $res->getSuggestionScore(); // e.g. 0.93
The score is a normalized similarity in [0.0, 1.0] (1.0 = identical, ~0.9 strong typo‑fix candidate, <0.5 usually weak).
Deliverability (DNS)
Deliverability is heuristic: the validator checks MX first, then A/AAAA fallback.
isSendable()istrueonly if domain resolves and MX exists.reasonsmay containdomain_not_foundorno_mx.
Custom DNS is possible by implementing:
namespace Dartcafe\EmailValidator\Contracts; interface DnsResolver { /** @return array{0:?bool,1:?bool} [domainExists, hasMx] */ public function check(string $asciiLowerDomain): array; }
and passing it into the validator’s constructor.
Lists: allow/deny with a pluggable provider
The library defines a minimal interface:
namespace Dartcafe\EmailValidator\Contracts; use Dartcafe\EmailValidator\Value\ListOutcome; interface ListProvider { /** * @return list<ListOutcome> */ public function evaluate(string $normalizedAddress, string $normalizedDomain): array; }
Return ListOutcome objects (type: allow|deny, checkType: address|domain, matched, …).
Deny matches are reported as warnings (do not change isSendable()).
You can write your own provider (DB/file/memory). A lightweight INI/Text adapter is available in the demo; production apps usually inject their own provider.
REST endpoint (optional)
The package ships an OpenAPI description (public/openapi.json) for a tiny REST API:
GET /validate?email=...POST /validatewith{"email": "..." }GET /health
You can wire these routes in any micro-router or reuse the demo app.
OpenAPI
- File:
public/openapi.json - Version in spec is kept in sync with releases via
docs/OpenApiConfig.phpand the release script.
Generate (in the lib):
composer run openapi:generate
Types & DTOs
Dartcafe\EmailValidator\Value\ValidationResult(mutable, JSON‑serializable)Dartcafe\EmailValidator\Value\ListOutcome- Suggestion types:
Dartcafe\EmailValidator\Value\SuggestedDomain(domain + score)
Everything is annotated for Psalm and IDEs.
Quality
# coding standards composer cs composer cs:fix # static analysis vendor/bin/psalm --no-cache # tests composer test
License
MIT © René Gieling
See LICENSE.