使用 checkdnsrr 验证电子邮件,是好还是坏的解决方案?

Validating emails using checkdnsrr, a good or bad solution?

我正在使用以下代码来验证电子邮件

if (checkdnsrr($domain , "MX")) {
    echo 'mx - pass <br>';
} else {
    echo 'mx - fail <br>';
}

我的愿望是检查域是否有效并且是否有 MX 记录。

我已经在使用正则表达式来检查电子邮件格式,但是人们输入了诸如 someone@gmail.con 之类的内容,这显然是错误的,但通过了基本格式验证。

我想进一步验证,但我不想走得太远而得到假阴性。

有没有人发现我的解决方案有任何问题或有更好的方法?

您的解决方案非常好!但是,在您进行 DNS 调用之前,我建议您先使用 FILTER_VALIDATE_EMAIL 验证电子邮件地址,然后将其传递给 MX DNS 检查。

虽然可能不需要检查 MX 记录是否存在,但您希望避免退回电子邮件,请继续!

是的,使用 checkdnsrr() 是个好主意。什么时候使用它是你应该关注的。我使用四遍系统。这是骨架,而不是我为电子邮件所做的确切组织。

四通系统

传递 1) 我的自定义过滤器使用 filter_input_array() 和此规则(其中过滤器出现在 class/object 的方法中。根据需要添加元素(用于其他领域)。

$customFilterRules = [
    'email' => ['filter'  => FILTER_CALLBACK,
                'flags'   => FILTER_REQUIRE_SCALAR,
                'options' => [$this, 'scrubber']
];

通过 2) 将 filter_input_array() 与此过滤规则一起使用。根据需要添加更多元素(对于其他字段)。

$phpFilterRules = [
    'email' => ['filter' => FILTER_SANITIZE_EMAIL,
                'flags'  => FILTER_REQUIRE_SCALAR]
];

通过 3) 使用此验证规则对 filter_input_array() 的输出使用 filter_var_array()。根据需要添加更多规则。

$phpValidationRules = [
    'email' => ['filter' => FILTER_VALIDATE_EMAIL,
                'flags'  => FILTER_REQUIRE_SCALAR]
];

第 4 次) 用 EmailValidator class 检查以下内容。此外,如果您不喜欢使用 filter_input_array(),这里有一些来自此 class 的方法通常可能会有帮助。根据需要进行更改。

哦,我还检查了基本的、特定于应用程序的、可接受的电子邮件地址长度:

$length = mb_strlen($email, 'UTF-8') //Make a decision based on this.

此外,我还有一个很棒的、特定于应用程序的电子邮件正则表达式,用于 preg_match()。我只接受 128 个字符的电子邮件地址。

'/(?>\A[A-Za-z0-9_-][A-Za-z0-9_.-]{0,62}?[A-Za-z0-9_-]{0,1}@{1}?(?:(?:[A-Za-z0-9]{1}?){1}?(?:[A-Za-z0-9.-]{0,61}?[A-Za-z0-9]{1}?){0,1}?){1,127}?\.{1}?[a-z]{2,20}?\z){1}?/u'

这里有一些 EmailValidator 方法。

/**
 * Compares 2 email addresses. If 1, just keep going.
 */
private function sameEmailAddress()
{
    if (count($this->filteredInputArray) === 2) {  //If there are two.
        if ($this->identical($this->filteredInputArray['email1'], $this->filteredInputArray['email2'])) {
            return true;
        }

        $this->testResultsArray['email1'] = false;
        $this->testResultsArray['email2'] = false;
        $this->errorMessagesArray['email1'] = 'Does not match e-mail below.';
        $this->errorMessagesArray['email2'] = 'Does not match e-mail above.';
        return false;
    }

    if (count($this->filteredInputArray) === 1) {  //If there is only 1.
        return true;
    }

    return false;
}

/**
 * Finds problems with e-mail as a whole.
 * There is a regular expression you can do this with.
 */
private function consecutivePeriodTest ($emailAddress, &$errorMessage)
{
    if (!preg_match('/\A(?!..)+?\z/', $emailAddress)) {
        return true;
    }

    $errorMessage = 'Consecutive periods are illegal!';
    return false;
}

最后,我用checkdnsrr().

/**
 * Given an array of unique domains, check DNS MX records.
 */
private function mxDNSPing(array $uniqueDomains)
{   
    $badDomains = [];

    foreach ($uniqueDomains as $key => $domain) {
        if (!checkdnsrr($domain, 'MX')) {
            $this->testResultsArray[$key] = false;
            $this->errorMessagesArray[$key] = 'No DNS MX records found.';
            $badDomains[$key] = $domain;
        }
    }

    return $badDomains;
}

正在确定电子邮件地址有什么问题。

/**
 * Finds problems with local or domain parts.
 * Should break up into two methods, though.
 */
private function emailPartProblemFinder($string, &$errorMessage)
{
    $emailParts = $this->string->getEmailAddressParts($string); //explode() on `@`

    if (count($emailParts) !== 2) {
        $errorMessage = 'Invalid e-mail address!';
    } else {
        list($localPart, $domain) = $emailParts;

        $localLength  = mb_strlen($localPart);
        $domainLength = mb_strlen($domain);

        if ($localLength === 0) {
            $errorMessage = 'Missing local part of address.';
        } elseif ($localLength > 64) {
            $errorMessage = 'Only 64 characters are alloed before the @ symbol ('.$localLength.' given)';
        } elseif (mb_strrpos($string, '.') === ($localLength - 1)) {
            $errorMessage = 'The local part of an email address cannot end with a period (.).';
        } elseif (mb_strpos($string, '..') >= 0) {
            $errorMessage = 'The local part of an email address cannot contain consecutive periods (..).';
        } elseif ($domainLength < 4) { //x.yy, is my minimum domain format.
            $errorMessage = 'Domain part < 4 characters. ('.$domainLength.' given)';
        } elseif ($domainLength > 253) {
            $errorMessage = 'Domain part exceeds 253 characters. ('.$domainLength.' given)';
        }
    }

    return;
}

/**
 * Finds problems with e-mail as a whole.
 */
private function emailAddressProblemFinder($string, $max, &$errorMessage)
{
    $length = mb_strlen($string, 'UTF-8');
    $atSymbolCount = mb_substr_count($string, '@', 'UTF-8');

    if ($length === 0) {
        return false;    //The reason was already assigned to the error message inside of $this->validateInput()
    } elseif ($length > $max) {
        $errorMessage = 'Exceeds max length (' . $max . ' characters)';
    } elseif ((mb_strpos($string, '@') === 0)) {
        $errorMessage = 'Cannot start with a @';
    } elseif ((mb_strrpos($string, '@') === ($length - 1))) {
        $errorMessage = 'Cannot end with a @';
    } elseif ($atSymbolCount > 1) {
        $errorMessage = '@ appears '.$atSymbolCount.' times.';
    } elseif ((mb_strpos($string, '@') === false)) {
        $errorMessage = 'The @ symbol is missing.';
    } elseif (mb_strpos($string, '.') === 0) {
        $errorMessage = 'The local part of an email address cannot start with a period (.).';
    } else {
        $this->emailPartProblemFinder($string, $errorMessage);
    }

    return;
}

方法 emailAddressProblemFinder() 仅被调用以发现问题所在。如有必要,它会调用 emailPartProblemFinder()

我的观点是,在使用 checkdnsrr() 之前,您可以进行大量测试。这其中的智慧值得你和其他人争论。不管怎样,我总是喜欢看别人做的!

多年来我一直在使用 chkdnsrr(),没有任何问题,直到本周(通过如上所述的预检查),当它无缘无故地踢出一个有效域 (dfp.com.my) 时。我找不到造成这种情况的原因,所以我现在确保安装了 nslookup(在 CentOS 上,那是 sudo yum install bind-utils),并使用了以下内容:

function isDomainValid($domain) {
    $output = [];
    $return = false;
    exec("nslookup $domain", $output, $return);
    return $return === 0;
}