如何为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_datafunction: 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
- Regex Fix: Updated the syntax regex to use proper delimiters and case insensitivity for better compatibility with mixed-case addresses.
- Improved
get_data: Now reads all server response lines until timeout, ensuring we don't miss multi-line error messages. - Enhanced Response Parsing:
- Explicitly checks for
550,551,553codes (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.
- Explicitly checks for
- Timeout Adjustments: Increased timeouts slightly to handle slower mail servers.
- Domain Best Practices: Changed
HELOandMAIL FROMto 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
相关产品推荐
相关产品推荐

