Come convalidare un indirizzo e-mail in PHP


218

Ho questa funzione per convalidare un indirizzo email:

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

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

Va bene per verificare se l'indirizzo e-mail è valido o no?


1
Se funziona, funziona. Non puoi davvero migliorarlo, è troppo piccolo. L'unica cosa che non va bene è lo stile. validateEmailsarebbe corret, oltre che passare $email, no $EMAIL.
Stan,

Volevo solo per assicurarsi che non ho avuto grossi problemi nel codice che è tutto :)
Cameron

Vedi anche stackoverflow.com/questions/201323/… per ulteriori informazioni su come e come non utilizzare le espressioni regolari per convalidare gli indirizzi e-mail.
Legoscia,

5
Ciò non riuscirebbe a convalidare molti indirizzi e-mail validi. Ad esempio *@example.com o'@example.com o me @ [127.0.0.1] o tu @ [ipv6: 08B0: 1123: AAAA :: 1234]
jcoder

7
@jcoder, non che sto raccomandando quel regex, ma almeno possiamo sperare che chiunque usi tali indirizzi per cantare ecc. non si lamenti quando fallisce :)
Halil Özgür

Risposte:


568

Il modo più semplice e sicuro per verificare se un indirizzo e-mail è ben formato è utilizzare la filter_var()funzione:

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

Inoltre puoi verificare se il dominio definisce un MXrecord:

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

Ma ciò non garantisce ancora l'esistenza della posta. L'unico modo per scoprirlo è inviare una mail di conferma.


Ora che hai la tua risposta facile sentiti libero di leggere sulla convalida dell'indirizzo e-mail se ti interessa imparare o utilizzare semplicemente la risposta veloce e andare avanti. Senza rancore.

Cercare di convalidare un indirizzo e-mail usando regex è un'attività "impossibile". Direi che quella regex che hai creato è inutile. Ci sono tre RFC per quanto riguarda gli indirizzi e-mail e la scrittura di una regex per catturare indirizzi e-mail sbagliati e allo stesso tempo non avere falsi positivi è qualcosa che nessun mortale può fare. Dai un'occhiata a questo elenco per i test (sia falliti che riusciti) del regex usato dalla filter_var()funzione di PHP .

Anche le funzioni PHP integrate, i client e i server di posta elettronica non funzionano correttamente. Ancora nella maggior parte dei casi filter_varè l'opzione migliore.

Se vuoi sapere quale modello regex PHP (attualmente) utilizza per convalidare gli indirizzi e-mail, vedi la fonte PHP .

Se vuoi saperne di più sugli indirizzi e-mail ti consiglio di iniziare a leggere le specifiche, ma devo avvisarti che non è una lettura facile da nessuna parte:

Nota che filter_var()è già stato dichiarato disponibile solo a partire da PHP 5.2. Se vuoi che funzioni con le versioni precedenti di PHP puoi usare il regex usato 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 = 'test@gmail.com';

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

PS Una nota sul modello regex usato sopra (dalla fonte PHP). Sembra che ci sia un po 'di copyright su di esso di Michael Rushton . Come indicato: "Sentiti libero di usare e ridistribuire questo codice. Ti preghiamo di tenere presente questa nota sul copyright."


Buona risposta, ma secondo questo link: haacked.com/archive/2007/08/21/… il nome utente o la parte locale può essere citato come stringa, ma FILTER_VALIDATE_EMAIL non lo accetta.
Daniel De León,

3
Non funziona per tutti gli indirizzi e- mail come indicato. Vedi anche l'elenco dei test falliti nella mia risposta per vedere che alcune stringhe tra virgolette funzionano e altre no.
PeeHaa,

4
No, troppi test falliti su quel modello emailtester.pieterhordijk.com/test-pattern/MTAz :-)
PeeHaa,

1
Questo modello è estremamente complesso nel caso in cui sia necessario utilizzarlo con una funzione come "preg_match_all" su una stringa di testo grande con e-mail all'interno. Se qualcuno di voi ha più semplice si prega di condividere. Voglio dire se vuoi: preg_match_all ($ pattern, $ text_string, $ match); quindi questo schema complesso sovraccaricherà il server se è necessario analizzare un testo veramente grande.
Vlado,

4
@PeeHaa: Postfix 3.0 lo supporta da quasi due anni: postfix.org/SMTPUTF8_README.html , ed è incluso in Ubuntu 16.04 e sarà incluso nella prossima versione di Debian, per esempio. Exim ha supporto sperimentale. I provider di webmail come Gmail hanno anche aggiunto il supporto per l'invio / la ricezione di tali e-mail, sebbene non sia ancora possibile creare account unicode. L'uso e il supporto diffusi sono a portata di mano e filter_varrimarranno indietro per un bel po 'di tempo, anche se lo cambiano in questo momento (ho pubblicato una segnalazione di bug).
iquito,

43

Per questo puoi usare filter_var .

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

1
interrompere l'aggiunta di questa funzione in quanto ciò non convalida i domini. se stai aggiungendo un indirizzo @ questo è valido. e non lo è!
Herr Nentu '

Cosa c'è con tutte le funzioni a una riga che contengono funzioni a una riga? Li vedo ovunque. Quando è diventata una "cosa"? (retorico). Questo deve finire.
Acqua blu

15

Nella mia esperienza, le regexsoluzioni hanno troppi falsi positivi e le filter_var()soluzioni hanno falsi negativi (specialmente con tutti i TLD più recenti ).

Invece, è meglio assicurarsi che l'indirizzo abbia tutte le parti richieste di un indirizzo e-mail (utente, simbolo "@" e dominio), quindi verificare che il dominio stesso esista.

Non è possibile determinare (lato server) se esiste un utente di posta elettronica per un dominio esterno.

Questo è un metodo che ho creato in una classe 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 afferma che la sua API è in grado di convalidare fino al 97% della consegna. Finché non ti dispiace consegnare il database dei contatti, ovviamente.
Tom Russell,

stristrnon riuscirà a ottenere il dominio se ci sono più segni @. Meglio explode('@',$email)e controllare chesizeof($array)==2
Aaron Gillion il

@AaronGillion Sebbene tu abbia ragione per quanto riguarda un modo migliore per ottenere parti di dominio, il metodo restituirà comunque false come checkdnsrr()restituirebbe false se ci fosse un segno @ nel dominio.
Jabari,

11

Penso che potresti stare meglio usando i filtri integrati di PHP - in questo caso particolare:

Può restituire un valore vero o falso se fornito con il FILTER_VALIDATE_EMAILparametro


9

Questo non solo convaliderà la tua e-mail, ma anche la sanificherà per caratteri inaspettati:

$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

Ha risposto a questa "domanda principale" sulla verifica delle e-mail https://stackoverflow.com/a/41129750/1848217

Per me il modo giusto per controllare le e-mail è:

  1. Verifica che il simbolo @ esista e prima e dopo di esso ci sono alcuni simboli non @: /^[^@]+@[^@]+$/
  2. Prova a inviare un'email a questo indirizzo con un "codice di attivazione".
  3. Quando l'utente "attiva" il suo indirizzo e-mail, vedremo che è tutto a posto.

Naturalmente, puoi mostrare alcuni avvisi o suggerimenti in front-end quando l'utente digita email "strane" per aiutarlo a evitare errori comuni, come nessun punto nella parte del dominio o spazi nel nome senza virgolette e così via. Ma devi accettare l'indirizzo "ciao @ mondo" se l'utente lo desidera davvero.

Inoltre, devi ricordare che lo standard dell'indirizzo email era e può evolversi, quindi non puoi semplicemente digitare regexp "standard-valid" una volta per tutte. E devi ricordare che alcuni server Internet concreti possono fallire alcuni dettagli dello standard comune e in effetti lavorare con il proprio "standard modificato".

Quindi, basta controllare @, suggerire all'utente in frontend e inviare e-mail di verifica su un determinato indirizzo.


1
Il tuo regex verifica @, ma in realtà non verifica che sia valido per nessuno degli RFC che regolano la posta elettronica. Inoltre non funziona come scritto. L'ho passato su regex101.com e non è riuscito a trovare indirizzi validi
Machavity,

Leggi solo regex o l'intera risposta? Completamente in disaccordo con te. Dimmi solo per favore, secondo quale RFC il server gmail.com presume che joe@gmail.com e jo.e@gmail.com abbiano lo stesso indirizzo? Esistono molti server che non funzionano secondo gli standard o secondo gli standard FRESH. Ma servono e-mail dei loro utenti. Se si digita un po 'di regexp una volta e si convalida solo con quello, non si ha alcuna garanzia che rimarrà nel futuro e che i futuri utenti non falliranno con le loro e-mail "nuove". Quindi, la mia posizione è la stessa: punto principale se si desidera verificare l'indirizzo e-mail - è sufficiente inviare e-mail di attivazione.
FlameStorm,

@Machavity ma grazie per il bugreport in regexp, l'ho corretto da /^[^@]+@[^@+]$/a/^[^@]+@[^@]+$/
FlameStorm il

Props per il fissaggio del regex, ma come si migliora il filter_varmetodo? Non risolve il problema accettando indirizzi mal formattati. Il tuo regex accetterà felicemente joe@domaincome un indirizzo email valido, quando non lo è
Machavity,

@Machavity, beh, per esempio, c'è una versione concreta di PHP sul tuo server e non puoi aggiornarla alla più recente. Ad esempio, hai php 5.5.15. Nel 2018 è stato esteso lo standard delle e-mail valide. Presto sarà realizzato in php 7.3.10. E ci sarà una buona funzione filter_var($email, FILTER_VALIDATE_EMAIL, $newOptions). Ma hai una vecchia funzione sul server, non puoi aggiornarla in alcuni casi. E perderai i clienti con alcune nuove e-mail valide. Inoltre, ancora una volta noto che non tutti i server di posta elettronica funzionano in modo strettamente conforme agli standard comuni e moderni degli indirizzi di posta elettronica.
FlameStorm,

3

Se vuoi verificare se il dominio fornito dall'indirizzo email è valido, usa qualcosa come:

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

Questo è un modo pratico per filtrare molti indirizzi e-mail non validi, insieme alla convalida e-mail standard, perché un formato e-mail valido non significa e-mail valida .

Nota che la funzione idn_to_ascii()(o la sua funzione sorella idn_to_utf8()) potrebbe non essere disponibile nella tua installazione PHP, richiede estensioni PECL intl> = 1.0.2 e PECL idn> = 0.1.

Inoltre, tieni presente che IPv4 o IPv6 come parte del dominio nell'email (ad esempio user@[IPv6:2001:db8::1]) non possono essere convalidati, solo gli host con nome possono farlo .

Vedi di più qui .


Non credo che funzionerà se la parte host dell'indirizzo e-mail è in indirizzo IP in formato IPv6
GordonM,

2

Dopo aver letto le risposte qui, questo è quello che ho finito con:

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

Se siete solo in cerca di un regex reale che permette di vari punti, trattini e trattini, come segue: [a-zA-z0-9.-]+\@[a-zA-z0-9.-]+.[a-zA-Z]+. Ciò consentirà tom_anderson.1-neo@my-mail_matrix.comdi convalidare un'e-mail dall'aspetto abbastanza stupido .


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

Al giorno d'oggi, se usi un modulo HTML5 con type=emailallora sei già sicuro dell'80% poiché i motori di browser hanno il loro validatore. Per completarlo, aggiungi questa regex alla tua preg_match_all()e negala:

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

Trova il regex utilizzato dai moduli HTML5 per la convalida
https://regex101.com/r/mPEKmy/1


Odio anche i downvotes senza spiegazione. Beh, suppongo che potrebbe dire: il controllo e-mail del browser (lato client) non è affatto sicuro. Chiunque può inviare qualsiasi cosa a un server modificando il codice. Quindi è ovvio e il modo più sicuro per eseguire il controllo (di nuovo) sul lato server. La domanda qui si basa su PHP, quindi evidentemente Cameron stava cercando una soluzione server e non una soluzione client.
Jonny

Questa risposta potrebbe non essere completamente correlata a PHP, ma il suggerimento HTML riguarda l'utente "standard" che utilizza solo un telefono / PC. Inoltre l'utente ottiene informazioni direttamente nel "suo" browser durante l'utilizzo del sito. I controlli reali sul lato server non sono coperti da questo, certo. A proposito, @Thielicious ha menzionato una modifica di PHP, quindi il suo commento è correlato a IMHO.
k00ni,

Probabilmente ha ricevuto voti negativi a causa dell'assunto che sei "sicuro all'80% poiché i motori di browser hanno il loro validatore". Esistono molti altri modi per inviare richieste HTTP che attraverso un browser, quindi non puoi presumere che qualsiasi richiesta sia sicura ... anche se controlli l'agente del browser.
Jabari,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.