You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为PHP邮箱校验代码添加邮箱前缀拼写错误检测

How to Add Email Prefix (Local Part) Validation to Your PHP SMTP Check

First off, let's clarify why your current code misses typos like somepne@domain.com: Most mail servers will accept any valid-looking local part via the RCPT TO command to prevent email address enumeration (a common spammer tactic). That said, many servers will return an explicit error if the mailbox truly doesn't exist—we just need to improve how we handle the SMTP response and ensure we're getting the full server output.

Here's how to modify your code to better detect invalid local parts, plus key caveats to keep in mind:

Key Changes to Implement

  • Improve the get_data function: Your original version only reads 2 lines of server response, which might miss critical multi-line error messages.
  • Enhance SMTP response parsing: Explicitly check for error codes like 550 (meaning "mailbox unavailable") that confirm the local part is invalid.
  • Add inconclusive status: Handle cases where servers hide mailbox status to block spam, so you don't false-positive valid addresses.

Modified Full Code

<?php
/* 
This script validates an e-mail address using getmxrr and fsockopen
1. Validates the syntax of the address.
2. Gets MX records by hostname
3. Connects to mail server and verifies mailbox (using SMTP commands HELO/MAIL FROM/RCPT TO)
When the function "validate_email([email])" fails connecting the highest priority MX server, 
it tries the next one, and so on.
Returns 0 if invalid, 1 if valid, 2 if validation is inconclusive (server hid mailbox status)
*/

$email = "someone@domain.com";
echo "email $email, 0=invalid, 1=valid, 2=inconclusive : ".validate_email($email)."<br>";
$email = "someone@domain.co";
echo "email $email, 0=invalid, 1=valid, 2=inconclusive : ".validate_email($email)."<br>";
$email = "somepne@domain.com";
echo "email $email, 0=invalid, 1=valid, 2=inconclusive : ".validate_email($email)."<br>";

function validate_email($email){
    $mailparts = explode("@", $email);
    $hostname = $mailparts[1];

    // Validate email address syntax (fixed regex delimiters + case insensitivity)
    $exp = "/^[a-z'0-9]+([._-][a-z'0-9]+)*@([a-z0-9]+([._-][a-z0-9]+))+$/i";
    $b_valid_syntax = preg_match($exp, $email);

    // Get MX addresses via getmxrr
    $b_mx_avail = getmxrr($hostname, $mx_records, $mx_weight);
    $b_server_found = 0;
    $inconclusive = false;

    if($b_valid_syntax && $b_mx_avail){
        // Sort MX records by priority
        $mxs = array();
        for($i=0; $i<count($mx_records); $i++){
            $mxs[$mx_weight[$i]] = $mx_records[$i];
        }
        ksort($mxs, SORT_NUMERIC);
        reset($mxs);

        while (list ($mx_weight, $mx_host) = each ($mxs) ) {
            if($b_server_found == 0 && !$inconclusive){
                // Try connection with 3-second timeout
                $fp = @fsockopen($mx_host, 25, $errno, $errstr, 3);
                if($fp){
                    stream_set_timeout($fp, 3);
                    $ms_resp = "";

                    // Use your own domain for HELO/MAIL FROM to avoid being blocked
                    $helo_resp = send_command($fp, "HELO yourdomain.com");
                    $ms_resp .= $helo_resp;

                    $mail_from_resp = send_command($fp, "MAIL FROM:<no-reply@yourdomain.com>");
                    $ms_resp .= $mail_from_resp;

                    // Check recipient address
                    $rcpt_text = send_command($fp, "RCPT TO:<".$email.">");
                    $ms_resp .= $rcpt_text;

                    // Parse RCPT TO response codes
                    if(substr($rcpt_text, 0, 3) == "250"){
                        // Mailbox confirmed valid
                        $b_server_found = 1;
                    } elseif(in_array(substr($rcpt_text, 0, 3), ["550", "551", "553"])){
                        // Mailbox definitely doesn't exist
                        $b_server_found = 0;
                        break; // No need to try other servers
                    } else {
                        // Server returned ambiguous response (anti-spam measure)
                        $inconclusive = true;
                    }

                    // Clean up connection
                    send_command($fp, "QUIT");
                    fclose($fp);
                }
            }
        }
    }

    // Return 2 if validation is inconclusive, else return validity status
    return $inconclusive ? 2 : $b_server_found;
}

function send_command($fp, $out){
    fwrite($fp, $out . "\r\n");
    return get_data($fp);
}

// Updated to read all available server response data until timeout
function get_data($fp){
    $s = "";
    $info = stream_get_meta_data($fp);
    while(!feof($fp) && !$info['timed_out']){
        $s .= fgets($fp, 1024);
        $info = stream_get_meta_data($fp);
    }
    return $s;
}

// Support Windows platforms
if (!function_exists ('getmxrr') ) {
    function getmxrr($hostname, &$mxhosts, &$mxweight) {
        if (!is_array ($mxhosts) ) {
            $mxhosts = array ();
        }
        if (!empty ($hostname) ) {
            $output = "";
            @exec ("nslookup.exe -type=MX $hostname.", $output);
            $imx=-1;
            foreach ($output as $line) {
                $imx++;
                $parts = array();
                if (preg_match ("/^$hostname\tMX preference = ([0-9]+), mail exchanger = (.*)$/", $line, $parts) ) {
                    $mxweight[$imx] = $parts[1];
                    $mxhosts[$imx] = $parts[2];
                }
            }
            return ($imx != -1);
        }
        return false;
    }
}
?>

What Changed & Why

  1. Regex Fix: Updated the syntax regex to use proper delimiters and case insensitivity for better compatibility with mixed-case addresses.
  2. Improved get_data: Now reads all server response lines until timeout, ensuring we don't miss multi-line error messages.
  3. Enhanced Response Parsing:
    • Explicitly checks for 550, 551, 553 codes (all confirm the mailbox doesn't exist) and immediately marks the email as invalid.
    • Adds an "inconclusive" status (return value 2) for servers that hide mailbox status to block spam.
  4. Timeout Adjustments: Increased timeouts slightly to handle slower mail servers.
  5. Domain Best Practices: Changed HELO and MAIL FROM to use your own domain (using microsoft.com may get you blocked by anti-spam filters).

Important Caveats

  • Anti-Enumeration Measures: Large providers like Gmail and Outlook intentionally hide mailbox status to prevent spammers from harvesting addresses. For these, this method will return 2 (inconclusive) even for typos.
  • IP Blocking: Frequent SMTP validation attempts can get your server's IP flagged as spam. Use this sparingly, and never run it against random addresses.
  • Final Validation: The only 100% reliable way to confirm an email is valid is to send a confirmation link and have the user click it. This SMTP check is just a pre-filter to catch obvious typos.

内容的提问来源于stack exchange,提问作者Sparky

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 07:22:08