Validating email addresses in PHP

Jul 08

Introduction

Data validation and sanitation ranks in the top 3 things you need to understand as a developer. Validating user data can be of the utmost importance if you plan on putting that information into a database, or if you have to use it for other site functions later on. One of the questions I get asked quite a bit is how to validate an email address. Around the web I’ve seen a number of 2 mile long regex’s and convoluted functions that ‘help’ with email validation, but they aren’t really needed. PHP has built in functions for validating most date that a user can provide for you. This obviously includes email addresses, but it has a downfall or two that you have to account for…

filter_var/filter_input

The filter_var/filter_input functions are life savers when it comes to data validation, and once you know how to use them and what constants to use its pretty simple as well. Lets look at a simple example of validating an email address.

<?php
$emailAddress = 'some@email.com';
if(filter_var($emailAddress, FILTER_VALIDATE_EMAIL) === false) {
    // Email address is invalid!
}

This is a simple example that will allow you to throw an error if the email address provided to you is invalid. Of course you would probably getting your email address from POST/GET data but you get the point. Now as I said before there are a few downfalls that you might run into depending on what exactly your app needs as far as validation. Lets look at another example…

<?php
// Assuming here that $_POST['email'] = 'some@email'
if(filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL) === false) {
    $error = 'Email address is invalid!';
}

If you run the code above you will notice the the email address ‘some@email’ is considered valid. This is because it is. Technically, I can have a system on my network with the name ‘email’ that has a mail server on it. So I could in theory send an email to ‘some@email’ and have it delivered. Most of the time in your own development this will not be the case. So we need a way around this. Before we do this, though, lets look at one more example…

<?php
$emailAddress = 'some@iKnowThisDomainDoesntExist.notarealtld';
if(filter_var($emailAddress, FILTER_VALIDATE_EMAIL) === false) {
    $error = 'Email address is invalid!';
}

Again, this email is regarded as valid, because it is in a valid format as per the RFC. So, the question becomes, how do we validate for both of these situations? Well, again filter_var comes to the rescue? We can hand filter_var a callback to validate a given parameter. So lets get to it. This is what I use to validate email addresses…

<?php
$emailValidationCallback = function($emailAddress) {
    // Basic validation
    if(filter_var($emailAddress, FILTER_VALIDATE_EMAIL) === false) {
        return false;
    }

    // Break email address into parts
    list($name, $server) = explode('@', $emailAddress);

    // Check that server is a FQDN
    if(strpos($server, '.') === false) {
        return false;
    }

    // Check that there is a MX record for the server provided
    if(getmxrr($server, $mxhosts) === false) {
        return false;
    }

    // Validators should return the value when it is valid
    return $emailAddress;
};

// Assuming here that $_GET['email'] = 'some@email'
if(filter_input(INPUT_GET, 'email', FILTER_CALLBACK, array('options' => $emailValidationCallback)) === false) {
    die('email is invalid!');
}

Conclusion

Remember to always use the level of validation that is correct for your use case. If you are validating the users email address through a confirmation link emailed to them, you don’t need to go through the trouble of custom callback. You’ll know their email is valid because if it isn’t they wont be able to use your application. I hope this helps you with your future validation. If you have any requests for other validation methods please let me know, and as always I’m interesting in your comments on my validation method and what you personally use.

2 comments

  1. Phil /

    You need to expand your dns check to look for an A record if there isn’t an MX record present.

Leave a Reply