Как проверить адрес электронной почты в PHP

218

У меня есть эта функция для проверки адресов электронной почты:

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

    return (bool)preg_match($v, $EMAIL);
}

Это нормально для проверки правильности адреса электронной почты?

Cameron
источник
1
Если это работает, это работает. Вы не можете сделать его лучше, он слишком маленький. Единственное, что не хорошо, это стиль. validateEmailбыло бы как правдиво, так и мимоходом $email, нет $EMAIL.
Стэн
Просто хотел убедиться, что у меня не было серьезных проблем в коде, вот и все :)
Кэмерон
См. Также stackoverflow.com/questions/201323/… для получения дополнительной информации о том, как и как не использовать регулярные выражения для проверки адресов электронной почты.
Legoscia
5
Это не сможет подтвердить многие действительные адреса электронной почты. Например, *@example.com или'@example.com или я @ [127.0.0.1] или вы @ [ipv6: 08B0: 1123: AAAA :: 1234]
jcoder
7
@jcoder, не то чтобы я рекомендовал это регулярное выражение, но, по крайней мере, мы можем надеяться, что кто-то, использующий такие адреса для подпевания и т. д., не будет жаловаться, если он потерпит неудачу :)
Halil Özgür

Ответы:

569

Самый простой и безопасный способ проверить правильность адреса электронной почты - использовать filter_var()функцию:

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

Дополнительно вы можете проверить, определяет ли домен MXзапись:

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

Но это все еще не гарантирует, что почта существует. Единственный способ узнать это - отправить письмо с подтверждением.


Теперь, когда у вас есть простой ответ, не стесняйтесь читать о проверке адреса электронной почты, если вы хотите узнать или иначе просто использовать быстрый ответ и двигаться дальше. Никаких обид.

Попытка подтвердить адрес электронной почты с помощью регулярных выражений является «невозможной» задачей. Я бы сказал, что это регулярное выражение бесполезно. Есть три RFC относительно адресов электронной почты и написания регулярных выражений, чтобы перехватывать неправильные адреса электронной почты, и в то же время не иметь ложных срабатываний - это то, что смертный не может сделать. Проверьте этот список для тестов (как неудачных, так и успешных) регулярных выражений, используемых filter_var()функцией PHP .

Даже встроенные функции PHP, почтовые клиенты или серверы не понимают это правильно. Все еще в большинстве случаевfilter_var это лучший вариант.

Если вы хотите узнать, какой шаблон регулярных выражений использует PHP (в настоящее время) для проверки адресов электронной почты, обратитесь к исходному тексту PHP. .

Если вы хотите узнать больше об адресах электронной почты, я предлагаю вам начать чтение спецификаций, но я должен предупредить вас, что это нелегко читать с любой натяжкой:

  • rfc5322
  • RFC5321
  • rfc3696
  • rfc6531 (разрешает символы юникода, хотя многие клиенты / серверы не принимают его)

Обратите внимание, что filter_var(), как уже говорилось, доступно только с PHP 5.2. Если вы хотите, чтобы он работал с более ранними версиями PHP, вы можете использовать регулярное выражение, используемое в 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 = 'test@gmail.com';

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

PS Примечание по шаблону регулярных выражений, использованному выше (из источника PHP). Похоже, на это есть авторское право Майкла Раштона . Как указано: «Не стесняйтесь использовать и распространять этот код. Но, пожалуйста, сохраните это уведомление об авторских правах».

PeeHaa
источник
Хороший ответ, но по этой ссылке: haacked.com/archive/2007/08/21/… имя пользователя o локально может быть строкой в ​​кавычках, но FILTER_VALIDATE_EMAIL его не принимает.
Даниэль Де Леон
3
Это не работает для всех адресов электронной почты, как указано. Также посмотрите список неудачных тестов в моем ответе, чтобы увидеть, что некоторые строки в кавычках работают, а другие нет.
PeeHaa
4
Нет, слишком много неудачных тестов по этому шаблону emailtester.pieterhordijk.com/test-pattern/MTAz :-)
PeeHaa
1
Этот шаблон чрезвычайно сложен в случае, если вам нужно использовать его с функцией типа «preg_match_all» над большой текстовой строкой с электронными письмами внутри. Если у кого-то из вас есть проще, пожалуйста, поделитесь. Я имею в виду, если вы хотите: preg_match_all ($ pattern, $ text_string, $ соответствия); тогда этот сложный шаблон будет перегружать сервер, если вам нужно проанализировать действительно большой текст.
Владо
4
@PeeHaa: Postfix 3.0 поддерживает его почти два года: postfix.org/SMTPUTF8_README.html , он включен в Ubuntu 16.04 и будет включен, например, в следующий выпуск Debian. Exim имеет экспериментальную поддержку. Поставщики веб-почты, такие как Gmail, также добавили поддержку для отправки / получения таких писем, хотя вы еще не можете создавать учетные записи Unicode. Широкое использование и поддержка находятся в пределах досягаемости и filter_varотстают довольно долго, даже если они меняют это прямо сейчас (я опубликовал отчет об ошибках).
Икито
43

Вы можете использовать filter_var для этого.

<?php
   function validateEmail($email) {
      return filter_var($email, FILTER_VALIDATE_EMAIL);
   }
?>
Кэмерон Мартин
источник
1
прекратите добавлять эту функцию, так как это не проверяет домены. если вы добавляете какой-то @ адрес, это действительно. и это не так!
Герр Ненту '11
Что со всеми однострочными функциями, содержащими однострочные функции? Я вижу их повсюду. Когда это стало «вещью»? (риторический). Это нужно остановить.
Голубая вода
15

По моему опыту, regexрешения имеют слишком много ложных срабатываний, а filter_var()решения имеют ложные отрицания (особенно со всеми новыми TLD ).

Вместо этого лучше убедиться, что адрес содержит все необходимые части адреса электронной почты (пользователь, символ "@" и домен), а затем убедиться, что сам домен существует.

Невозможно определить (на стороне сервера), существует ли пользователь электронной почты для внешнего домена.

Это метод, который я создал в классе Utility:

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;
}
Джабари
источник
Neverbounce утверждает, что их API может проверить до 97% доставки. Конечно, если вы не против передать свою базу данных контактов.
Том Рассел
stristrне сможет получить домен, если есть несколько знаков @. Лучше explode('@',$email)и проверить этоsizeof($array)==2
Аарон Джиллион
@AaronGillion Несмотря на то, что вы правы в том, что лучший способ получить доменные части, метод все равно вернет false, как checkdnsrr()и false, если в домене был знак @.
Джабари
11

Я думаю, что вам может быть лучше использовать встроенные фильтры PHP - в данном конкретном случае:

Он может возвращать истину или ложь, если поставляется с FILTER_VALIDATE_EMAILпараметром.

Fluffeh
источник
9

Это не только проверит вашу электронную почту, но и очистит ее от неожиданных символов:

$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);
}
Excalibur
источник
4

Ответили на это в «топ-вопросе» о проверке электронной почты https://stackoverflow.com/a/41129750/1848217

Для меня правильный способ проверки электронной почты:

  1. Убедитесь, что символ @ существует, и до и после него есть не-@ символы: /^[^@]+@[^@]+$/
  2. Попробуйте отправить электронное письмо на этот адрес с некоторым «кодом активации».
  3. Когда пользователь «активирует» свой адрес электронной почты, мы увидим, что все правильно.

Конечно, вы можете показывать некоторые предупреждения или всплывающие подсказки во внешнем интерфейсе, когда пользователь вводит «странное» электронное письмо, чтобы помочь ему избежать распространенных ошибок, таких как отсутствие точки в доменной части или пробелов в имени без кавычек и так далее. Но вы должны принять адрес "hello @ world", если пользователь действительно этого хочет.

Кроме того, вы должны помнить, что стандартный адрес электронной почты был и может развиваться, поэтому вы не можете просто ввести какое-то «стандартно-действительное» регулярное выражение раз и навсегда. И вы должны помнить, что некоторые конкретные интернет-серверы могут не работать с некоторыми деталями общего стандарта и фактически работать с собственным «измененным стандартом».

Итак, просто отметьте @, назовите пользователя на веб-интерфейс и отправьте письма с подтверждением на указанный адрес.

FlameStorm
источник
1
Ваше регулярное выражение проверяет @, но не проверяет, действительно ли оно для любого из RFC, которые управляют электронной почтой. Это также не работает как написано. Я запустил его через regex101.com, и он не смог
найти
Вы читаете только регулярное выражение или весь ответ? Полностью с тобой не согласен. Просто скажите мне, пожалуйста, по какому RFC серверу gmail.com предполагается, что joe@gmail.com и jo.e@gmail.com - это один и тот же адрес? Есть много серверов, которые работают не по стандартам или по стандартам FRESH. Но они обслуживают электронные письма своих пользователей. Если вы наберете какое-то регулярное выражение один раз и подтвердите только этим, у вас нет гарантии, что оно останется правильным в будущем, и ваши будущие пользователи не ошибутся с их электронными письмами «нового пути». Итак, моя позиция такая же: главное, если вы хотите подтвердить адрес электронной почты - просто отправьте письмо активации.
FlameStorm
@Mavavity, но спасибо за сообщение об ошибке в регулярном выражении, я исправил это /^[^@]+@[^@+]$/в/^[^@]+@[^@]+$/
FlameStorm
Подсказки для исправления регулярного выражения, но как это улучшить по сравнению с filter_varметодом? Это не решает проблему принятия плохо отформатированных адресов. Ваше регулярное выражение с радостью примет joe@domainв качестве действительного адреса электронной почты, когда это не так
Machavity
@Machavity, ну, например, на вашем сервере есть конкретная версия PHP, и вы не можете обновить ее до последней версии. Например, у вас есть php 5.5.15. В 2018 году был расширен стандарт действительных писем. Это будет реализовано в php 7.3.10 в ближайшее время. И там будет хорошо работающая функция filter_var($email, FILTER_VALIDATE_EMAIL, $newOptions). Но у вас есть старая функция на сервере, вы не можете обновить в некоторых случаях. И вы потеряете клиентов с некоторыми новыми действительными электронными письмами. Кроме того, еще раз отмечаю, что не все серверы, обслуживающие электронную почту, работают строго в соответствии с общепринятым и современным стандартом адресов электронной почты.
FlameStorm
3

Если вы хотите проверить, является ли указанный домен с адреса электронной почты действительным, используйте что-то вроде:

/*
* Check for valid MX record for given email domain
*/
if(!function_exists('check_email_domain')){
    function check_email_domain($email) {
        //Get host name from email and check if it is valid
        $email_host = explode("@", $email);     
        //Add a dot to the end of the host name to make a fully qualified domain name and get last array element because an escaped @ is allowed in the local part (RFC 5322)
        $host = end($email_host) . "."; 
        //Convert to ascii (http://us.php.net/manual/en/function.idn-to-ascii.php)
        return checkdnsrr(idn_to_ascii($host), "MX"); //(bool)       
    }
}

Это удобный способ отфильтровать множество недействительных адресов электронной почты вместе со стандартной проверкой электронной почты, поскольку действительный формат электронной почты не означает действительную электронную почту .

Обратите внимание, что idn_to_ascii()(или его родственная функция idn_to_utf8()) функция может быть недоступна в вашей установке PHP, для нее требуются расширения PECL intl> = 1.0.2 и PECL idn> = 0.1.

Также имейте в виду, что IPv4 или IPv6 как часть домена в электронной почте (например user@[IPv6:2001:db8::1]) не могут быть проверены, только именованные хосты.

Подробнее здесь .

Бад Дамьянов
источник
Я не думаю, что это будет работать, если
основная
2

Прочитав ответы здесь, я получил следующее:

public static function isValidEmail(string $email) : bool
{
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        return false;
    }

    //Get host name from email and check if it is valid
    $email_host = array_slice(explode("@", $email), -1)[0];

    // Check if valid IP (v4 or v6). If it is we can't do a DNS lookup
    if (!filter_var($email_host,FILTER_VALIDATE_IP, [
        'flags' => FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE,
    ])) {
        //Add a dot to the end of the host name to make a fully qualified domain name
        // and get last array element because an escaped @ is allowed in the local part (RFC 5322)
        // Then convert to ascii (http://us.php.net/manual/en/function.idn-to-ascii.php)
        $email_host = idn_to_ascii($email_host.'.');

        //Check for MX pointers in DNS (if there are no MX pointers the domain cannot receive emails)
        if (!checkdnsrr($email_host, "MX")) {
            return false;
        }
    }

    return true;
}
Pelmered
источник
1

Если вы просто ищете фактическое регулярное выражение , что позволяет для различных точек, знаков подчеркивания и тире, это выглядит следующим образом : [a-zA-z0-9.-]+\@[a-zA-z0-9.-]+.[a-zA-Z]+. Это позволит tom_anderson.1-neo@my-mail_matrix.comпроверять довольно глупое письмо .

smulholland2
источник
0
/(?![[:alnum:]]|@|-|_|\.)./

В настоящее время, если вы используете форму HTML5 с, type=emailто вы уже на 80% в безопасности, так как браузерные движки имеют свой собственный валидатор. Чтобы дополнить его, добавьте это регулярное выражение в ваш preg_match_all()и отрицайте его:

if (!preg_match_all("/(?![[:alnum:]]|@|-|_|\.)./",$email)) { .. }

Найдите регулярное выражение, используемое формами HTML5 для проверки
https://regex101.com/r/mPEKmy/1

Thielicious
источник
Я тоже ненавижу понижения без объяснения причин. Ну, я думаю, он мог бы сказать: проверка электронной почты браузера (на стороне клиента) вообще не безопасна. Любой может отправить что угодно на сервер, изменив код. Так что это очевидный и самый безопасный способ проверки (опять же) на стороне сервера. Вопрос здесь основан на PHP, поэтому очевидно, что Кэмерон искал решение для сервера, а не для решения клиента.
Джонни
Этот ответ может не полностью относиться к PHP, но предложение HTML охватывает «стандартного» пользователя, использующего только телефон / ПК. Также пользователь получает информацию прямо в «своем» браузере при использовании сайта. Настоящие проверки на стороне сервера этим не покрываются, конечно. Кстати, @Thielicious упомянул изменение PHP, поэтому его комментарий связан с ИМХО.
19
Вероятно, он получил отрицательные голоса из-за предположения, что вы «на 80% в безопасности, так как браузерные движки имеют свой собственный валидатор». Существует много других способов отправки http-запросов, чем через браузер, поэтому вы не можете предполагать, что любой запрос безопасен ... даже если вы проверите агент браузера.
Джабари