{"record":{"id":"379ec4bb6eed84b2","repo":"egulias/EmailValidator","slug":"the-s-class-requires-the-intl-extension-379ec4","errorCode":null,"errorMessage":"The %s class requires the Intl extension.","messagePattern":"The (.+?) class requires the Intl extension\\.","errorType":"exception","errorClass":"\\LogicException","httpStatus":null,"severity":"error","filePath":"src/Validation/Extra/SpoofCheckValidation.php","lineNumber":21,"sourceCode":"namespace Egulias\\EmailValidator\\Validation\\Extra;\n\nuse \\Spoofchecker;\nuse Egulias\\EmailValidator\\EmailLexer;\nuse Egulias\\EmailValidator\\Result\\SpoofEmail;\nuse Egulias\\EmailValidator\\Result\\InvalidEmail;\nuse Egulias\\EmailValidator\\Validation\\EmailValidation;\n\nclass SpoofCheckValidation implements EmailValidation\n{\n    /**\n     * @var InvalidEmail|null\n     */\n    private $error;\n\n    public function __construct()\n    {\n        if (!extension_loaded('intl')) {\n            throw new \\LogicException(sprintf('The %s class requires the Intl extension.', __CLASS__));\n        }\n    }\n\n    public function isValid(string $email, EmailLexer $emailLexer) : bool\n    {\n        $checker = new Spoofchecker();\n        $checker->setChecks(Spoofchecker::SINGLE_SCRIPT);\n\n        if ($checker->isSuspicious($email)) {\n            $this->error = new SpoofEmail();\n        }\n\n        return $this->error === null;\n    }\n\n    public function getError() : ?InvalidEmail\n    {\n        return $this->error;","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/egulias/EmailValidator/blob/d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa/src/Validation/Extra/SpoofCheckValidation.php#L3-L39","documentation":"SpoofCheckValidation uses the Spoofchecker class from the Intl (ICU) extension to detect visually confusable / mixed-script email addresses (a phishing technique). Its constructor throws a LogicException when extension_loaded('intl') is false, because the Spoofchecker API it calls in isValid() does not exist without that extension. Note it checks the extension itself while DNSCheckValidation checks the idn_to_ascii function — same root cause, different probe.","triggerScenarios":"Instantiating `new SpoofCheckValidation()` on a PHP build without ext-intl, e.g. `new EmailValidator()->isValid($email, new SpoofCheckValidation())`. Very commonly indirect: adding spoof checking to a combined validator `new MultipleValidationWithAnd([new RFCValidation(), new SpoofCheckValidation()])` — the composite constructor itself is fine, but PHP instantiates the argument list eagerly, so the LogicException fires on the same line. Deploying with `composer install --ignore-platform-reqs` when composer.json suggests ext-intl also surfaces this only at runtime.","commonSituations":"Hardening an existing email validator with spoof/phishing checks on a server that never needed intl before; alpine/slim Docker base images; CI green (unit tests mock or skip it) while production throws; hosts where ext-intl exists in CLI but is disabled in the FPM/php.ini used by the web server.","solutions":["Install and enable the Intl extension: Debian/Ubuntu `apt-get install php-intl`, Alpine `apk add php83-intl`, Docker `docker-php-ext-install intl`; verify with `php -m | grep intl` in the exact SAPI (cli vs fpm) that runs your code.","If spoof checking is a nice-to-have, gate it on extension availability: build the validation list with `extension_loaded('intl') ? new SpoofCheckValidation() : new RFCValidation()` so missing intl degrades gracefully.","Remove SpoofCheckValidation from the validation set if the environment cannot have intl and spoof detection is not required — RFCValidation/NoRFCWarningsValidation cover syntax and RFC compliance without it."],"exampleFix":"// before\n$validations = [\n    new RFCValidation(),\n    new SpoofCheckValidation(), // throws LogicException without ext-intl\n];\n\n// after\n$validations = [new RFCValidation()];\nif (extension_loaded('intl')) {\n    $validations[] = new SpoofCheckValidation();\n}","handlingStrategy":"validation","validationCode":"$validations = [new RFCValidation()];\nif (extension_loaded('intl')) {\n    $validations[] = new SpoofCheckValidation();\n}\nreturn new MultipleValidationWithAnd($validations);","typeGuard":"function canUseSpoofCheckValidation(): bool\n{\n    return extension_loaded('intl') && class_exists(\\Spoofchecker::class);\n}","tryCatchPattern":"try {\n    $validation = new SpoofCheckValidation();\n} catch (\\LogicException $e) {\n    // Intl extension missing: skip spoof checking rather than crashing validation\n    $validation = new RFCValidation();\n}","preventionTips":["Declare `\"ext-intl\": \"*\"` in composer.json require when your app uses SpoofCheckValidation, so `composer install --ignore-platform-reqs` is the only way to end up without it.","Gate spoof-check construction behind extension_loaded('intl') when spoof detection is optional, especially in libraries shipped to unknown hosts.","Check `php -m | grep intl` for the web SAPI (FPM/Apache), not just CLI — a green CLI check does not prove the web server has intl loaded."],"tags":["php","intl-extension","spoofchecker","missing-extension","phishing-detection"],"backgroundTag":"missing-php-extension","analyzedSha":"d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa","analyzedAt":"2026-08-21T01:55:18.494Z","schemaVersion":2},"datasetVersion":"2026-08-21T03:17:12.404Z"}