How to validate an email address in PHP

358,462

Solution 1

The easiest and safest way to check whether an email address is well-formed is to use the filter_var() function:

if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
    // invalid emailaddress
}

Additionally you can check whether the domain defines an MX record:

if (!checkdnsrr($domain, 'MX')) {
    // domain is not valid
}

But this still doesn't guarantee that the mail exists. The only way to find that out is by sending a confirmation mail.


Now that you have your easy answer feel free to read on about email address validation if you care to learn or otherwise just use the fast answer and move on. No hard feelings.

Trying to validate an email address using a regex is an "impossible" task. I would go as far as to say that that regex you have made is useless. There are three rfc's regarding emailaddresses and writing a regex to catch wrong emailadresses and at the same time don't have false positives is something no mortal can do. Check out this list for tests (both failed and succeeded) of the regex used by PHP's filter_var() function.

Even the built-in PHP functions, email clients or servers don't get it right. Still in most cases filter_var is the best option.

If you want to know which regex pattern PHP (currently) uses to validate email addresses see the PHP source.

If you want to learn more about email addresses I suggest you to start reading the specs, but I have to warn you it is not an easy read by any stretch:

Note that filter_var() is as already stated only available as of PHP 5.2. In case you want it to work with earlier versions of PHP you could use the regex used in PHP:

<?php

$pattern = '/^(?!(?:(?:\\x22?\\x5C[\\x00-\\x7E]\\x22?)|(?:\\x22?[^\\x5C\\x22]\\x22?)){255,})(?!(?:(?:\\x22?\\x5C[\\x00-\\x7E]\\x22?)|(?:\\x22?[^\\x5C\\x22]\\x22?)){65,}@)(?:(?:[\\x21\\x23-\\x27\\x2A\\x2B\\x2D\\x2F-\\x39\\x3D\\x3F\\x5E-\\x7E]+)|(?:\\x22(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21\\x23-\\x5B\\x5D-\\x7F]|(?:\\x5C[\\x00-\\x7F]))*\\x22))(?:\\.(?:(?:[\\x21\\x23-\\x27\\x2A\\x2B\\x2D\\x2F-\\x39\\x3D\\x3F\\x5E-\\x7E]+)|(?:\\x22(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21\\x23-\\x5B\\x5D-\\x7F]|(?:\\x5C[\\x00-\\x7F]))*\\x22)))*@(?:(?:(?!.*[^.]{64,})(?:(?:(?:xn--)?[a-z0-9]+(?:-+[a-z0-9]+)*\\.){1,126}){1,}(?:(?:[a-z][a-z0-9]*)|(?:(?:xn--)[a-z0-9]+))(?:-+[a-z0-9]+)*)|(?:\\[(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){7})|(?:(?!(?:.*[a-f0-9][:\\]]){7,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?)))|(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){5}:)|(?:(?!(?:.*[a-f0-9]:){5,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3}:)?)))?(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))(?:\\.(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))){3}))\\]))$/iD';

$emailaddress = '[email protected]';

if (preg_match($pattern, $emailaddress) === 1) {
    // emailaddress is valid
}

P.S. A note on the regex pattern used above (from the PHP source). It looks like there is some copyright on it of Michael Rushton. As stated: "Feel free to use and redistribute this code. But please keep this copyright notice."

Solution 2

You can use filter_var for this.

<?php
   function validateEmail($email) {
      return filter_var($email, FILTER_VALIDATE_EMAIL);
   }
?>

Solution 3

In my experience, regex solutions have too many false positives and filter_var() solutions have false negatives (especially with all of the newer TLDs).

Instead, it's better to make sure the address has all of the required parts of an email address (user, "@" symbol, and domain), then verify that the domain itself exists.

There is no way to determine (server side) if an email user exists for an external domain.

This is a method I created in a Utility class:

public static function validateEmail($email)
{
    // SET INITIAL RETURN VARIABLES

        $emailIsValid = FALSE;

    // MAKE SURE AN EMPTY STRING WASN'T PASSED

        if (!empty($email))
        {
            // GET EMAIL PARTS

                $domain = ltrim(stristr($email, '@'), '@') . '.';
                $user   = stristr($email, '@', TRUE);

            // VALIDATE EMAIL ADDRESS

                if
                (
                    !empty($user) &&
                    !empty($domain) &&
                    checkdnsrr($domain)
                )
                {$emailIsValid = TRUE;}
        }

    // RETURN RESULT

        return $emailIsValid;
}

Solution 4

I think you might be better off using PHP's inbuilt filters - in this particular case:

It can return a true or false when supplied with the FILTER_VALIDATE_EMAIL param.

Solution 5

This will not only validate your email, but also sanitize it for unexpected characters:

$email  = $_POST['email'];
$emailB = filter_var($email, FILTER_SANITIZE_EMAIL);

if (filter_var($emailB, FILTER_VALIDATE_EMAIL) === false ||
    $emailB != $email
) {
    echo "This email adress isn't valid!";
    exit(0);
}
Share:
358,462

Related videos on Youtube

Cameron
Author by

Cameron

Updated on April 26, 2022

Comments

  • Cameron
    Cameron about 2 years

    I have this function to validate an email addresses:

    function validateEMAIL($EMAIL) {
        $v = "/[a-zA-Z0-9_-.+]+@[a-zA-Z0-9-]+.[a-zA-Z]+/";
    
        return (bool)preg_match($v, $EMAIL);
    }
    

    Is this okay for checking if the email address is valid or not?

    • Stan
      Stan almost 12 years
      If it works it works. You can't really make it better, it's too small. Only thing that's not good is style. validateEmail would be corret, as well as passing $email, not $EMAIL.
    • Cameron
      Cameron almost 12 years
      Just wanted to make sure I didn't have any major problems in the code that's all :)
    • legoscia
      legoscia almost 12 years
      See also stackoverflow.com/questions/201323/… for more about how and how not to use regular expressions to validate email addresses.
    • jcoder
      jcoder over 11 years
      That would fail to validate many valid email addresses. For example *@example.com or '@example.com or me@[127.0.0.1] or you@[ipv6:08B0:1123:AAAA::1234]
    • emmanuel honore
      emmanuel honore over 11 years
    • Halil Özgür
      Halil Özgür over 11 years
      @jcoder, not that I'm recommending that regex, but at least we can hope anyone using such addresses for sing up etc wouldn't complain when it fails :)
    • LINKeRxUA
      LINKeRxUA over 9 years
    • Mike Q
      Mike Q almost 7 years
      See complete answer here: for PHP 5.x stackoverflow.com/questions/19522092/…
    • Michael Geier
      Michael Geier over 5 years
      If you really want to use that regex you should move the "-" to the beginning of the character set to avoid errors: [-a-zA-Z0-9_.+] (if it's not at the beginning the "-" is interpreted as range).
    • Iman
      Iman over 5 years
      Have you tried debounce.io ?
    • Eugene Zakharenko
      Eugene Zakharenko over 2 years
      It failes even on [email protected], strongly not recommend this to use!
  • Daniel De León
    Daniel De León over 11 years
    Good answer, but according this link: haacked.com/archive/2007/08/21/… the user name o locally part can be quoted-string, but the FILTER_VALIDATE_EMAIL do not accept it.
  • PeeHaa
    PeeHaa over 11 years
    It does not work for all emailaddresses as stated. Also see the list of failed tests in my answer to see that some quoted strings do work and others not.
  • Ilia
    Ilia over 10 years
    @PeeHaa, probably, it would be better to use regex that would check for non-standard, UTF-8 accepted email addresses: stackoverflow.com/a/20992349/1455661
  • PeeHaa
    PeeHaa over 10 years
    Nope, too many failed tests on that pattern emailtester.pieterhordijk.com/test-pattern/MTAz :-)
  • Vlado
    Vlado over 8 years
    This pattern is extremely complex in case you need to use it with function like "preg_match_all" over big text string with emails inside. If any of you has simpler please share. I mean if you want to: preg_match_all($pattern, $text_string, $matches); then this complex pattern will overload the server if you need to parse really big text.
  • numediaweb
    numediaweb over 8 years
    filter_var will not validate emails like; уникум@из.рф even if they are correct!
  • sergio
    sergio over 8 years
    This will validate: $email = '"><script>alert(1);</script>"@test.com'; echo filter_var($email, FILTER_VALIDATE_EMAIL)
  • PeeHaa
    PeeHaa over 8 years
    What is your point @sergio? Again as stated @sergio there are several "failures".
  • sergio
    sergio over 8 years
    Need take into account that it can cause an XSS
  • PeeHaa
    PeeHaa over 8 years
    XSS prevention has nothing to do with this answer.
  • iquito
    iquito about 8 years
    filter_var is not really an option in "modern" applications - it does not support UTF8, which has been allowed for some time in the local part of email addresses.
  • PeeHaa
    PeeHaa about 8 years
    @iquito do you have a list of said "modern" mail servers that do support utf8 email addresses by any chance?
  • iquito
    iquito about 8 years
    @PeeHaa: Postfix 3.0 supports it for almost two years now: postfix.org/SMTPUTF8_README.html , and it is included in Ubuntu 16.04 and will be included in the next Debian release, for example. Exim has experimental support. Webmail providers like Gmail have also added support for sending/receiving such emails, although you cannot yet create unicode accounts. Widespread use and support is within reach, and filter_var will lag behind by quite some time, even if they change it right now (I have posted a bug report).
  • Machavity
    Machavity over 7 years
    Your regex does check for @, but it doesn't really check that it's valid per any of the RFCs that govern email. It also doesn't work as written. I ran it through regex101.com and it failed to match valid addresses
  • FlameStorm
    FlameStorm over 7 years
    Do you read only regex or the whole answer? Fully disagree with you. Just say me please, according what RFC the gmail.com server assumes that [email protected] and [email protected] is the same address? There are lot of servers which works not by standards or not by FRESH standards. But thay serve emails of their users. If you type some regexp once, and validate only by that, you have no guarantee that it will stay right in future and your future users will not fail with their "new-way" emails. So, my position is the same: main point if you want to verify email address - just send activation email.
  • FlameStorm
    FlameStorm over 7 years
    @Machavity but thanks for bugreport in regexp, i fixed it from /^[^@]+@[^@+]$/ to /^[^@]+@[^@]+$/
  • Machavity
    Machavity over 7 years
    Props to you for fixing the regex, but how does that improve over the filter_var method? It doesn't fix the problem of it accepting badly formatted addresses either. Your regex will happily accept joe@domain as a valid email address, when it's not
  • FlameStorm
    FlameStorm over 7 years
    @Machavity, well, for example, there's an concrete version of PHP on your server and you can't update it to newest. For example, you have php 5.5.15 . In 2018 standard of valid emails was extended. It will realized in php 7.3.10 soon. And there'll good-working function filter_var($email, FILTER_VALIDATE_EMAIL, $newOptions). But you have old function on server, you cant update in some cases. And you will loose clients with some new valid emails. Also, once more I notice, that not all email-serving severs works strictly accordingly to common and modern standard of email adresses.
  • FlameStorm
    FlameStorm over 7 years
    And the other - admin@localhost is valid email address. What about "little internets" - intranets? There can be addresses like joe@matesnet.
  • Tom Russell
    Tom Russell almost 7 years
    Neverbounce claims their API is able to validate to 97% delivery. As long as you don't mind handing over your contacts database, of course.
  • Jonny
    Jonny over 6 years
    I hate downvotes too w/o explanation. Well I guess he might say: Browser email check (client side) is not secure at all. Anyone can send anything to a server by changing the code. So it's obvious and the most secure way to do the check (again) server side. The question here is based on PHP, so its obvious Cameron was looking for a server solution and not for a client solution.
  • GordonM
    GordonM about 6 years
    I don't think it will work if the host portion of the email address is in IP address in IPv6 format
  • k00ni
    k00ni over 5 years
    This answer may not fully PHP related, but is HTML suggestion covers the "standard" user using just a phone/PC. Also the user gets an info directly in "his" browser while using the site. Real checks on server side are not covered with this, sure. Btw, @Thielicious mentioned a PHP change, so his comment is related IMHO.
  • Herr Nentu'
    Herr Nentu' over 4 years
    stop adding this function as this does not validate domains. if you are adding some@address this is valid. and it's not!
  • Aaron Gillion
    Aaron Gillion over 4 years
    stristr will fail to get the domain if there are multiple @ signs. Better to explode('@',$email) and check that sizeof($array)==2
  • Jabari
    Jabari over 4 years
    It probably received down votes due the the assumption that you're "80% safe since browser engines have their own validator". There are many other ways to send http requests than through a browser, so you can't assume that any request is safe...even if you check the browser agent.
  • Jabari
    Jabari over 4 years
    @AaronGillion While you are correct as far as a better way to get domain parts, the method would still return false as checkdnsrr() would return false if there were an @ sign in the domain.
  • Admin
    Admin about 4 years
    What's with all the one line functions containing one line functions? I am seeing them everywhere. When did this become a "thing"? (rhetorical). This needs to stop.
  • User1337
    User1337 about 3 years
    Is there any reason for the array_slice? Why don't you just use explode("@", $email)[1]? Can @ characters appear in the user part of the email address?
  • Pelmered
    Pelmered about 3 years
    @User1337 I think it was for backwards compatibility. Accessing the return type directly like that is not supported before PHP 5.4 (I think). However, that is a pretty old and unsupported version by now so I would probably do as you suggest.
  • Pelmered
    Pelmered about 3 years
    In most cases, you probably don't want to strip illegal characters like that when validating. If you check an email address with illegal characters, that should not validate.
  • User1337
    User1337 about 3 years
    I just tested it, and you are actually right. From the perspective of someone who started coding a couple of years ago, it's unbelievable what programmers had to deal with to achieve the simplest things.
  • PeeHaa
    PeeHaa about 3 years
    @JaydeepChauhan that is just nonsense 3v4l.org/p1MP5
  • Vladimir Hidalgo
    Vladimir Hidalgo almost 3 years
    @user2607743 I think it makes sense, if you, one year later with 100 usages of it in your project and you want to improve the way you validate the emails.... then it's going to be faster to edit 1 function than a hundred places.
  • Christopher K.
    Christopher K. over 2 years
    @HerrNentu' whats wrong with some@address? It is a perfectly valid email address. Like root@localhost is one. You are just doing the wrong thing. You are syntactically validating the form of the email address, and some@address is valid according to the RFC. But what you want to do is validate that an address is reachable. some@address is only reachable if the host address is known in your network. To validate reachability, you can check the DNS (check the host exists) or use SMTP (check the mailbox exists).
  • Christopher K.
    Christopher K. over 2 years
    An MX entry is not necessary to receive emails. If none is present, the A entry will be used. See serverfault.com/questions/470649/…
  • Christopher K.
    Christopher K. over 2 years
    An MX entry is not necessary to receive emails. If none is present, the A entry will be used. See serverfault.com/questions/470649/…
  • Christopher K.
    Christopher K. over 2 years
    An MX entry is not necessary to receive emails. If none is present, the A entry will be used. See serverfault.com/questions/470649/…
  • Herr Nentu'
    Herr Nentu' over 2 years
    @ChristopherK. the problem is that it validates the email address without a domain.
  • Alex Tank
    Alex Tank over 2 years
    @HerrNentu' actually it's debatable, here is a validation examples 3v4l.org/Vo8K1 so intranet + localhost domains were not accepted, which is a bit unexpected, as both of them a valid domains for intranet systems
  • Pelmered
    Pelmered over 2 years
    @ChristopherK. Oh, that was interesting. I have used a check like this in various projects and have probably validated over a million email addresses, and this has never been a problem. I think it's a pretty good check to make to make sure the domain is actually pointed somewhere. Maybe a fallback check for an A pointer could be used, but that might do more harm than good even it seems like a more correct check.