Page 1 of 3
How can you verify if an email address is OK in real-time? One easy solution is to use the SMTP protocol to see if there is a server that will at least begin to accept an email to the address in question. Implementing such a system is also more than half way to implementing a raw SMTP email service in PHP.
Many websites, including this one, collect email addresses for one reason or another and a common problem is keeping the address list clean and free of dead email addresses. What is needed is some easy way of checking that an email address is active.
It is probably worth saying at this point that there is no way of doing this with absolute certainty. All you can hope for is to pick up obvious non-functional email addresses. To try and validate email addresses in real-time is something you can spend hours on and still fail.
You can also spend ages figuring out a regular expression to make sure that the user has entered a correctly formed email address. This can be useful but in most cases it helps very little. All a user has to do is enter a single wrong character and the email address is still correctly formed but it probably won't work.
Another standard strategy is to send the user an email with a coded link that they can click on. This works but it takes time and relies on the user to respond in a reasonable time. It also doesn't help with the problem of users who make mistakes entering email addresses.
It is possible to make a live check on the validity of an email by basically going through the procedure that is used to actually deliver the email. It also doesn't take much to extend the short function presented in this article to actually send an email using nothing but PHP, i.e. no sendmail or PHP mail. You don't even need to have an SMTP server set up.
But first some simple theory.
If you want to send an email to someone the first thing that the SMTP server does is to take the email address and extract the domain name - the bit after the @. It then uses this to look up using DNS to find the domains MX records. These contain the URLs of the SMTP servers that mail should be delivered to. For reasons of reliability and efficiency domains are allowed to assign multiple mail servers and assign priorities that indicate the order that they should be used.
Some domains, not many, don't have an MX record and in this case the SMTP server simply sends the email to the domain.
In all cases port 25 is used for the connection.
PHP has a function that can be used to look up MX records and this works under Windows since version 5.3.0. So to start our email validation function off we need to split the email address and look up the domain MX records:
list($name,$Domain) = split('@',$email);
The getmxrr function returns false if it can't find any mail servers listed as MX records. In this case we need to use the original domain name:
Now we have at least one SMTP server URL to check, i.e. the domain.
Sockets and SMTP
What we need to do next is check for each of the SMTP servers listed in $POFFS that at least one will accept email for the email address in question. To do this we need to open a socket on port 25 to the server and send some SMTP commands. All socket connections are by their nature unreliable and so you need to set a timeout after which it will be assumed that the server isn't responding:
Five seconds should be long enough to give a server to respond as we are only checking email validity. If you can afford to make the user wait longer than five seconds increase the timeout for a more reliable test.
One problem with PHP is that its error handling is something of a mess due to the introduction of object oriented exception handling fairly late in its development. In this case the specific problem is that if you try to open a socket on a URL that doesn't correspond to a server the open will timeout and generate an error which will be displayed as a warning. In most other languages you would deal with this by using a Try-Catch statement but in PHP this isn't quite as simple. The simplest and most direct solution is to change the level of error reporting for this section of the program and set it back to what it was when the function completes:
This stores the old error reporting level in $oldErrorLevel and sets the new level to ignore warnings.
Finally before we start checking we set a flag to indicate that we haven't as yet found a server that accepts email for the specified address:
Now we can start testing each possible SMTP server in turn:
foreach($POFFS as $PO )
The first thing to do is open a socket to the server using the fsockopen function:
$sock = fsockopen($PO, 25,
$errno, $errstr, $timeout);
This attempts to open a socket to the SMTP server using port 25. If it fails it returns false and the error details are in $errono and $errstr. In this case we aren't interested in the reason for failing. The final parameter specifies the timeout to be used before giving up and returning false.
If the fsockopen function returns anything other than false we have an SMTP server live on the other end of the socket. If it returned false then we move on to the next SMTP server if there is one.