Email Address class

This is the third of my easy to use everyday input-you-do-get-quite-often-validating classes: the email address. It ties in nicely with the last two articles, since an email address must end in either a domain name or an ip address.

class.EmailAddress.php

<?php

/*
 * Represents an Email address
 */
class EmailAddress
{
    
/*
     * Constructs an email address object from the given string
     */
    
public function __construct($email)
    {
        
$parts self::parse($email);
        if (
$parts === false) throw new Exception('Email address not valid');
        
$this->parts $parts;
    }

    
/*
     * Parses the given string to a 2-item array: one@two. Returns false if the
     *  address is invalid.
     *
     * Use as validator:
     *  if (EmailAddress::parse($string) !== false) // string is valid address
     */
    
public static function parse($email)
    {
        
$email trim($email);

        
// First, we find the last @ sign (@ signs may occurr between quotes in the
        //  first part)
        
$atPos strrpos($email'@');
        if (
$atPos === false) return false;        // no @ sign found

        // Split around the found @ sign
        
$local  substr($email0$atPos);
        
$domain substr($email$atPos+1);

        
// Check the lengths of both parts;
        
if (strlen($local) < || strlen($local) > 64) return false;
        if (
strlen($domain) < || strlen($domain) > 255) return false;

        
// Check part before @ sign
        
if ($local[0] == '"')
        {
            
// Option one: quoted string
            
if (!preg_match('/^"[^"]+"$/'$local)) return false;
        }
        else
        {
            
// Option two: dot-atom, ie a set of strings seperated by dots
            //  Escaped characters: \$ \/ \{ \}
            
if (!preg_match("/^[A-Za-z0-9!#\$%&'*+\/=?^_`\{|\}~-]+(\.[A-Za-z0-9!#\$%&'*+\/=?^_`\{|\}~-]+)*\$/"$local)) return false;
        }

        
// Check the part after the @ sign
        
if (preg_match('/^\[?[0-9\.]+\]?$/'$domain))
        {
            
// check ip
            
if (IPv4::parse($domain) === false) return false;
        }
        else
        {
            
// check domain name
            
if (DomainName::parse($domain) === false) return false;
        }

        
// return the seperated parts of the address;
        
return (array($local$domain));
    }

    public function 
__toString()
    {
        return 
implode('@'$this->parts);
    }

    private 
$parts;
}

?>

The validating looks complicated but is really quite straightforward. First the lengths of the parts before and after the @ sign are checked (they must be at least 1 and at most x characters long). Then the address is split around the @ and the parts are checked individually.

The second part is easy, it must be either a domain name or an ip address. The first part is a bit more complicated, it must be either a quoted string ("like this") or a 'dot-atom'; a set of (non-empty) string, seperated by dots.

To see the script in action on some crazy email addresses, check here.

Hope this makes life a little easier for you.

Jul 9th, 2008

Comments

Michael wrote:

Today I noticed (or rather, I was notified) that the test script wasn't working. Turns out I forgot to fix the "ereg" use in the domain class --> patched!

Apr 12th, 2010

Michael wrote:

I've updated it a little to use preg_match again, now that ereg has been deprecated

Nov 12th, 2009

Post your comments here

If you wish to add code to your comment you can use code tags, like this: <code class="php">yourCodeHere</code>.
Quite a large number of languages are supported, although I can't guarantee it'll be pretty. Inside the code tags you can use any characters except for the string "</code>".