Jak sprawdzić poprawność adresu e-mail w PHP

218

Mam tę funkcję do sprawdzania poprawności adresów e-mail:

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

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

Czy można sprawdzić, czy adres e-mail jest prawidłowy, czy nie?

Cameron
źródło
1
Jeśli to działa, to działa. Naprawdę nie można tego poprawić, jest za mały. Jedyne, co nie jest dobre, to styl. validateEmailbyłoby słuszne, a także przemijające $email, a nie $EMAIL.
Stan
Chciałem się tylko upewnić, że nie mam większych problemów z kodem, to wszystko :)
Cameron,
Zobacz także stackoverflow.com/questions/201323/..., aby dowiedzieć się więcej o tym, jak i jak nie używać wyrażeń regularnych do sprawdzania poprawności adresów e-mail.
legoscia,
5
To nie sprawdziłoby poprawności wielu prawidłowych adresów e-mail. Na przykład *@example.com lub'@example.com lub ja @ [127.0.0.1] lub ty @ [ipv6: 08B0: 1123: AAAA :: 1234]
jcoder
7
@jcoder, nie to, że polecam regex, ale przynajmniej możemy mieć nadzieję, że każdy, kto używa takich adresów do śpiewania itp. nie narzeka, gdy się nie powiedzie :)
Halil Özgür

Odpowiedzi:

569

Najłatwiejszym i najbezpieczniejszym sposobem sprawdzenia, czy adres e-mail jest poprawnie sformułowany, jest skorzystanie z filter_var()funkcji:

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

Dodatkowo możesz sprawdzić, czy domena definiuje MXrekord:

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

Ale to wciąż nie gwarantuje, że poczta istnieje. Jedynym sposobem, aby się tego dowiedzieć, jest wysłanie wiadomości potwierdzającej.


Teraz, gdy masz łatwą odpowiedź, możesz przeczytać dalej o sprawdzaniu poprawności adresu e-mail, jeśli chcesz się nauczyć lub w inny sposób po prostu skorzystać z szybkiej odpowiedzi i przejść dalej. Bez urazy.

Próba sprawdzenia poprawności adresu e-mail przy użyciu wyrażenia regularnego jest „niemożliwym” zadaniem. Chciałbym posunąć się nawet do stwierdzenia, że ​​ten regex, który stworzyłeś, jest bezużyteczny. Istnieją trzy rfc dotyczące adresów e-mail i napisania wyrażenia regularnego w celu złapania niewłaściwych adresów e-mail, a jednocześnie brak fałszywych alarmów jest czymś, czego żaden śmiertelny nie może zrobić. Sprawdź tę listę pod kątem testów (zarówno nieudanych, jak i udanych) wyrażenia regularnego używanego przez filter_var()funkcję PHP .

Nawet wbudowane funkcje PHP, klienci poczty e-mail lub serwery nie działają poprawnie. Nadal w większości przypadków filter_varjest najlepszą opcją.

Jeśli chcesz wiedzieć, jakiego wzorca wyrażenia regularnego używa PHP (obecnie) do sprawdzania adresów e-mail, zobacz źródło PHP .

Jeśli chcesz dowiedzieć się więcej o adresach e-mail, sugeruję, abyś zaczął czytać specyfikacje, ale muszę cię ostrzec, że nie jest to łatwe do odczytania pod żadnym względem:

Zauważ, że filter_var()jak już wspomniano, dostępne tylko od PHP 5.2. Jeśli chcesz, aby działał ze starszymi wersjami PHP, możesz użyć wyrażenia regularnego używanego w 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
}

PS Uwaga na temat wzorca wyrażeń regularnych używanych powyżej (ze źródła PHP). Wygląda na to, że jest trochę praw autorskich do Michaela Rushtona . Jak stwierdzono: „Zachowaj swobodę w używaniu i rozpowszechnianiu tego kodu. Ale zachowaj tę informację o prawach autorskich”.

PeeHaa
źródło
Dobra odpowiedź, ale zgodnie z tym linkiem: haacked.com/archive/2007/08/21/... nazwa użytkownika lub część lokalna może być cytowana-ciąg, ale FILTER_VALIDATE_EMAIL nie akceptuje.
Daniel De León
3
Jak już wspomniano, nie działa dla wszystkich adresów e-mail. Zobacz także listę nieudanych testów w mojej odpowiedzi, aby zobaczyć, że niektóre cytowane ciągi działają, a inne nie.
PeeHaa
4
Nie, zbyt wiele nieudanych testów na tym wzorze emailtester.pieterhordijk.com/test-pattern/MTAz :-)
PeeHaa
1
Ten wzorzec jest niezwykle złożony, na wypadek gdybyś musiał go używać z funkcją taką jak „preg_match_all” nad dużym ciągiem tekstowym z zawartymi w nim wiadomościami e-mail. Jeśli któryś z was ma prostsze, proszę podzielić się. Mam na myśli, jeśli chcesz: preg_match_all ($ pattern, $ text_string, $ mecze); wtedy ten złożony wzór spowoduje przeciążenie serwera, jeśli trzeba przeanalizować naprawdę duży tekst.
Vlado
4
@PeeHaa: Postfix 3.0 obsługuje go już od prawie dwóch lat: postfix.org/SMTPUTF8_README.html i jest zawarty w Ubuntu 16.04 i na przykład w kolejnej wersji Debiana. Exim ma wsparcie eksperymentalne. Dostawcy poczty internetowej, tacy jak Gmail, również dodali obsługę wysyłania / odbierania takich wiadomości e-mail, chociaż nie można jeszcze utworzyć kont Unicode. Szerokie zastosowanie i wsparcie jest w zasięgu ręki i filter_varpozostanie w tyle przez długi czas, nawet jeśli teraz to zmienią (opublikowałem raport o błędzie).
iquito,
43

Możesz do tego użyć filter_var .

<?php
   function validateEmail($email) {
      return filter_var($email, FILTER_VALIDATE_EMAIL);
   }
?>
Cameron Martin
źródło
1
przestań dodawać tę funkcję, ponieważ nie sprawdza ona poprawności domen. jeśli dodajesz jakiś adres @, jest to poprawne. i to nie jest!
Herr Nentu '11
Co się dzieje ze wszystkimi funkcjami jednej linii zawierającymi funkcje jednej linii? Widzę ich wszędzie. Kiedy stało się to „rzeczą”? (retoryczny). To musi się skończyć.
Blue Water
15

Z mojego doświadczenia wynika, że regexrozwiązania mają zbyt wiele fałszywych wyników pozytywnych, a filter_var()rozwiązania mają fałszywe negatywy (szczególnie w przypadku wszystkich nowszych TLD ).

Zamiast tego lepiej jest upewnić się, że adres zawiera wszystkie wymagane części adresu e-mail (użytkownik, symbol „@” i domena), a następnie sprawdzić, czy sama domena istnieje.

Nie ma możliwości ustalenia (po stronie serwera), czy istnieje użytkownik poczty e-mail dla domeny zewnętrznej.

Oto metoda, którą stworzyłem w klasie 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;
}
Jabari
źródło
Neverbounce twierdzi, że ich API jest w stanie zweryfikować dostawę do 97%. Oczywiście dopóki nie masz nic przeciwko przekazaniu bazy danych kontaktów.
Tom Russell
stristrnie uda się uzyskać domeny, jeśli istnieje wiele znaków @. Lepiej explode('@',$email)i sprawdź tosizeof($array)==2
Aaron Gillion
@AaronGillion Chociaż masz rację co do lepszego sposobu uzyskania części domeny, metoda nadal zwróciłaby wartość false, tak jak checkdnsrr()zwróciłaby wartość false, gdyby w domenie był znak @.
Jabari
11

Myślę, że lepiej byłoby użyć wbudowanych filtrów PHP - w tym konkretnym przypadku:

Może zwrócić wartość prawda lub fałsz, jeśli zostanie dostarczony z FILTER_VALIDATE_EMAILparametrem.

Fluffeh
źródło
9

Spowoduje to nie tylko sprawdzenie poprawności twojego e-maila, ale także odkażenie go w przypadku nieoczekiwanych znaków:

$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
źródło
4

Odpowiedzieli na to pytanie w „głównym pytaniu” na temat weryfikacji wiadomości e-mail https://stackoverflow.com/a/41129750/1848217

Dla mnie właściwy sposób sprawdzania wiadomości e-mail to:

  1. Sprawdź, czy symbol @ istnieje, a przed i po nim jest kilka symboli innych niż @: /^[^@]+@[^@]+$/
  2. Spróbuj wysłać wiadomość e-mail na ten adres z jakimś „kodem aktywacyjnym”.
  3. Gdy użytkownik „aktywuje” swój adres e-mail, zobaczymy, że wszystko jest w porządku.

Oczywiście możesz wyświetlać pewne ostrzeżenia lub podpowiedzi na froncie, gdy użytkownik wpisuje „dziwny” e-mail, aby pomóc mu uniknąć typowych błędów, takich jak brak kropki w części domeny lub spacji w nazwie bez cytowania i tak dalej. Ale musisz zaakceptować adres „hello @ world”, jeśli użytkownik naprawdę tego chce.

Pamiętaj też, że standard adresu e-mail był i może ewoluować, więc nie możesz po prostu wpisać raz „raz na zawsze” wyrażenia regularnego. I musisz pamiętać, że niektóre konkretne serwery internetowe mogą zawieść niektóre szczegóły wspólnego standardu i faktycznie działać z własnym „zmodyfikowanym standardem”.

Więc po prostu sprawdź @, podpowiedź użytkownikowi na interfejsie i wyślij e-maile weryfikacyjne na podany adres.

FlameStorm
źródło
1
Wyrażenie regularne sprawdza @, ale tak naprawdę nie sprawdza, czy jest poprawne według któregokolwiek z RFC, które rządzą pocztą e-mail. To również nie działa jak napisano. Uruchomiłem go przez regex101.com i nie udało się dopasować prawidłowych adresów
Machavity,
Czy czytasz tylko regex czy całą odpowiedź? Całkowicie się z tobą nie zgadzam. Powiedz mi tylko proszę, zgodnie z tym, co RFC serwer gmail.com zakłada, że ​​[email protected] i [email protected] to ten sam adres? Istnieje wiele serwerów, które nie działają według standardów lub nie według standardów FRESH. Ale mogą one obsługiwać e-maile użytkowników. Jeśli wpiszesz jakieś wyrażenie regularne tylko raz i potwierdzisz je tylko przez to, nie masz gwarancji, że pozostanie ono w przyszłości, a Twoi przyszli użytkownicy nie zawiodą z powodu wiadomości e-mail w „nowy sposób”. Więc moja pozycja jest taka sama: główny punkt, jeśli chcesz zweryfikować adres e-mail - wystarczy wysłać e-mail aktywacyjny.
FlameStorm,
@Machavity, ale dzięki za zgłoszenie błędu w regexp, naprawiłem to od /^[^@]+@[^@+]$/do/^[^@]+@[^@]+$/
FlameStorm
Proponuje ci naprawienie wyrażenia regularnego, ale jak to się poprawia w stosunku do filter_varmetody? Nie rozwiązuje też problemu przyjmowania źle sformatowanych adresów. Twój regex z przyjemnością zaakceptuje joe@domainjako prawidłowy adres e-mail, jeśli nie jest
Machavity,
@Machavity, na przykład na twoim serwerze jest konkretna wersja PHP i nie możesz jej zaktualizować do najnowszej wersji. Na przykład masz php 5.5.15. W 2018 r. Standard ważnych wiadomości e-mail został rozszerzony. Zostanie to wkrótce zrealizowane w php 7.3.10. I będzie dobrze działająca funkcja filter_var($email, FILTER_VALIDATE_EMAIL, $newOptions). Ale masz starą funkcję na serwerze, w niektórych przypadkach nie można zaktualizować. I stracisz klientów z kilkoma nowymi ważnymi e-mailami. Po raz kolejny zauważam, że nie wszystkie serwery obsługujące pocztę elektroniczną działają ściśle zgodnie ze wspólnym i nowoczesnym standardem adresów e-mail.
FlameStorm,
3

Jeśli chcesz sprawdzić, czy podana domena z adresu e-mail jest poprawna, użyj czegoś takiego:

/*
* 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)       
    }
}

Jest to przydatny sposób na filtrowanie wielu nieprawidłowych adresów e-mail wraz ze standardową weryfikacją adresu e-mail, ponieważ prawidłowy format wiadomości e-mail nie oznacza prawidłowego adresu e-mail .

Zauważ, że idn_to_ascii()funkcja (lub jego siostrzana funkcja idn_to_utf8()) może nie być dostępna w twojej instalacji PHP, wymaga ona rozszerzenia PECL intl> = 1.0.2 i PECL idn> = 0.1.

Należy również pamiętać, że IPv4 lub IPv6 jako część domeny w wiadomości e-mail (na przykład user@[IPv6:2001:db8::1]) nie mogą zostać zweryfikowane, mogą to być tylko hosty o nazwie .

Zobacz więcej tutaj .

Bud Damyanov
źródło
Nie sądzę, aby zadziałało, jeśli część hosta adresu e-mail ma adres IP w formacie IPv6
GordonM
2

Po przeczytaniu tutaj odpowiedzi otrzymałem:

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;
}
Rozdrobnione
źródło
1

Jeśli jesteś po prostu patrząc na rzeczywiste regex, która pozwala na różnych kropek i kresek, podkreślenia, to w następujący sposób: [a-zA-z0-9.-]+\@[a-zA-z0-9.-]+.[a-zA-Z]+. Pozwoli to na dość głupio wyglądającą wiadomość e-mail, taką jak tom_anderson.1-neo@my-mail_matrix.comwalidacja.

smulholland2
źródło
0
/(?![[:alnum:]]|@|-|_|\.)./

W dzisiejszych czasach, jeśli używasz formularza HTML5 type=email, jesteś już w 80% bezpieczny, ponieważ silniki przeglądarek mają swój własny walidator. Aby go uzupełnić, dodaj ten wyrażenie regularne do swojego preg_match_all()i zaneguj go:

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

Znajdź regex używany przez formularze HTML5 do sprawdzania poprawności
https://regex101.com/r/mPEKmy/1

Thielicious
źródło
Nienawidzę też głosów negatywnych bez wyjaśnienia. Wydaje mi się, że mógłby powiedzieć: Sprawdzanie adresu e-mail w przeglądarce (po stronie klienta) wcale nie jest bezpieczne. Każdy może wysłać cokolwiek na serwer, zmieniając kod. Jest to więc oczywisty i najbezpieczniejszy sposób na sprawdzenie (ponownie) po stronie serwera. Pytanie tutaj opiera się na PHP, więc oczywiste, że Cameron szukał rozwiązania serwerowego, a nie rozwiązania klienckiego.
Jonny
Ta odpowiedź może nie być w pełni związana z PHP, ale sugeruje, że HTML obejmuje „standardowego” użytkownika korzystającego tylko z telefonu / komputera. Również użytkownik otrzymuje informacje bezpośrednio w „swojej” przeglądarce podczas korzystania ze strony. Rzeczywiste kontrole po stronie serwera nie są objęte tym, oczywiście. Btw, @Thielicious wspomniał o zmianie PHP, więc jego komentarz dotyczy IMHO.
k00ni
Prawdopodobnie otrzymała głosy odrzucone z powodu założenia, że ​​jesteś „w 80% bezpieczny, ponieważ silniki przeglądarek mają swój własny walidator”. Istnieje wiele innych sposobów wysyłania żądań HTTP niż przez przeglądarkę, więc nie można zakładać, że każde żądanie jest bezpieczne ... nawet jeśli sprawdzisz agenta przeglądarki.
Jabari