Як перевірити електронну адресу в PHP


218

У мене є ця функція для перевірки адрес електронної пошти:

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

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

Чи правильно це перевірити, чи адреса електронної пошти є дійсною чи ні?


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 щодо електронних адрес і написання регулярних виразів, щоб зловити неправильні електронні адреси, і в той же час не мають помилкових позитивів - те, що жоден смертний не може зробити. Ознайомтеся з цим списком щодо тестів (як невдалих, так і успішних) регулярних виразів, використовуваних функцією PHP filter_var().

Навіть вбудовані функції PHP, клієнти електронної пошти чи сервери не підходять до цього. Все-таки в більшості випадків filter_varце найкращий варіант.

Якщо ви хочете знати, який шаблон регексу PHP (в даний час) використовує для перевірки адрес електронної пошти, перегляньте джерело PHP .

Якщо ви хочете дізнатися більше про адреси електронної пошти, я пропоную вам почати читати технічні характеристики, але я повинен попередити вас, що прочитати їх непросто на будь-якому протязі:

  • rfc5322
  • rfc5321
  • rfc3696
  • rfc6531 (дозволяє символи Unicode, хоча багато клієнтів / серверів не приймають його)

Зауважте, що 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). Схоже, є деякі авторські права на Майкла Раштона . Як зазначалося: "Не соромтеся користуватися цим кодом і перерозподіляти його. Але будь ласка, зберігайте це повідомлення про авторські права".


Хороша відповідь, але за цим посиланням: 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, $ match); тоді ця складна схема буде перевантажувати сервер, якщо вам потрібно розібрати дійсно великий текст.
Владо

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
Aaron Gillion

@AaronGillion Хоча ви правильні щодо кращого способу отримання частин домену, метод все одно поверне помилковий, як checkdnsrr()і поверне помилковий, якби в домені був знак @.
Джабарі

11

Я думаю, вам може бути краще використовувати вбудовані фільтри PHP - у цьому конкретному випадку:

Він може повернути справжнє або помилкове, якщо постачається разом із FILTER_VALIDATE_EMAILпарам.


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);
}

4

Відповів на це у "головному запитанні" щодо підтвердження електронної пошти https://stackoverflow.com/a/41129750/1848217

Для мене правильним способом перевірки електронних листів є:

  1. Перевірте, чи існує символ @, і перед ним і після нього є кілька символів, які не містять @: /^[^@]+@[^@]+$/
  2. Спробуйте надіслати електронний лист на цю адресу з деяким "кодом активації".
  3. Коли користувач "активував" свою електронну адресу, ми побачимо, що все в порядку.

Звичайно, ви можете показати попередження чи підказку в передній частині, коли користувач вводить "дивну" електронну пошту, щоб допомогти йому уникнути поширених помилок, як, наприклад, відсутність крапки в доменній частині або пробіли в імені без цитування тощо. Але ви повинні прийняти адресу "привіт @ світ", якщо користувач дійсно цього хоче.

Крім того, ви повинні пам’ятати, що стандарт електронної адреси був і може змінюватися, тому ви не можете просто вводити один раз і назавжди якісь «стандартні». І ви повинні пам’ятати, що деякі конкретні Інтернет-сервери можуть провалити деякі деталі загального стандарту і насправді працювати з власним «модифікованим стандартом».

Отже, просто перевірте @, натякніть користувачеві на фронтенд та надсилайте електронні листи для підтвердження за вказаною адресою.


1
Ваш регекс перевіряється @, але він насправді не перевіряє, чи він дійсний для будь-якого з RFC, які керують електронною поштою. Це також не працює як написано. Я провів це через regex101.com, і він не зміг відповідати дійсним адресам
Machavity

Ви читаєте лише регулярний вираз або всю відповідь? Повністю не згоден з вами. Скажіть, будь ласка, згідно з тим, що RFC сервер gmail.com передбачає, що joe@gmail.com та jo.e@gmail.com - це одна і та ж адреса? Є безліч серверів, які працюють не за стандартами чи не за стандартами FRESH. Але вони обслуговують електронні листи своїх користувачів. Якщо ви введете кілька повторних файлів один раз і підтвердите лише це, ви не маєте гарантії, що він залишиться правильним у майбутньому, і ваші майбутні користувачі не зможуть зірватися зі своїми електронними листами. Отже, моя позиція однакова: головний момент, якщо ви хочете підтвердити адресу електронної пошти - просто надішліть електронний лист для активації.
FlameStorm

@Machavity але спасибі за звіт про помилку в регулярному виразі, я встановив її від /^[^@]+@[^@+]$/до/^[^@]+@[^@]+$/
FlameStorm

Підтримує вас для виправлення регулярного виразу, але як це покращитися в порівнянні з filter_varметодом? Це також не вирішує проблему з прийняттям неправильно відформатованих адрес. Ваш регулярний joe@domain
гекс з

@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]) не можуть бути перевірені, можуть лише названі хости.

Детальніше дивіться тут .


Я не думаю, що це спрацює, якщо хост-частина електронної адреси буде в IP-адресі у форматі IPv6
GordonM

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;
}

1

Якщо ви просто шукаєте фактичне регулярний вираз , що дозволяє для різних точок, знаків підкреслення та тире, це виглядає наступним чином : [a-zA-z0-9.-]+\@[a-zA-z0-9.-]+.[a-zA-Z]+. Це дозволить tom_anderson.1-neo@my-mail_matrix.comперевірити електронну пошту досить нерозумно .


0
/(?![[:alnum:]]|@|-|_|\.)./

Сьогодні, якщо ви користуєтесь формою HTML5, type=emailтоді ви вже на 80% безпечні, оскільки у двигунів браузера є власний валідатор. Щоб доповнити його, додайте цей регулярний вираз у свій preg_match_all()і заперечуйте його:

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

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


Я ненавиджу занадто без пояснень. Я думаю, що він може сказати: Перевірка електронної пошти веб-переглядача (клієнтська сторона) взагалі не захищена. Будь-хто може надіслати що завгодно на сервер, змінивши код. Тож це очевидний і найбезпечніший спосіб зробити перевірку (знову ж) стороною сервера. Питання тут засноване на PHP, тому його очевидний Кемерон шукав серверне рішення, а не клієнтське рішення.
Джоні

Ця відповідь, можливо, не повністю пов'язана з PHP, але пропозиція HTML охоплює "стандартного" користувача лише за допомогою телефону / ПК. Також користувач отримує інформацію безпосередньо у своєму "браузері" під час користування сайтом. Реальні чеки на стороні сервера не охоплені цим, звичайно. Btw, @Thielicious згадав про зміну PHP, тому його коментар пов'язаний з IMHO.
k00ni

Можливо, він отримав голоси через припущення, що ви "на 80% безпечні, оскільки у двигунів браузера є власний валідатор". Існує багато інших способів надіслати http-запити, ніж через браузер, тому ви не можете припустити, що будь-який запит є безпечним ... навіть якщо ви перевірите агента браузера.
Джабарі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.