Cómo validar una dirección de correo electrónico en PHP

218

Tengo esta función para validar una dirección de correo electrónico:

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

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

¿Está bien verificar si la dirección de correo electrónico es válida o no?

Cameron
fuente
1
Si funciona, funciona. Realmente no puedes mejorarlo, es demasiado pequeño. Lo único que no es bueno es el estilo. validateEmailsería correcto, así como pasar $email, no $EMAIL.
Stan
Solo quería asegurarme de que no tenía ningún problema importante en el código, eso es todo :)
Cameron
Consulte también stackoverflow.com/questions/201323/… para obtener más información sobre cómo y cómo no usar expresiones regulares para validar las direcciones de correo electrónico.
legoscia
55
Eso no podría validar muchas direcciones de correo electrónico válidas. Por ejemplo *@example.com o'@example.com o yo @ [127.0.0.1] o usted @ [ipv6: 08B0: 1123: AAAA :: 1234]
jcoder
77
@jcoder, no es que recomiende esa expresión regular, pero al menos podemos esperar que cualquiera que use tales direcciones para cantar, etc., no se queje cuando falla :)
Halil Özgür

Respuestas:

568

La forma más fácil y segura de verificar si una dirección de correo electrónico está bien formada es usar la filter_var()función:

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

Además, puede verificar si el dominio define un MXregistro:

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

Pero esto todavía no garantiza que exista el correo. La única forma de averiguarlo es enviando un correo de confirmación.


Ahora que tiene una respuesta fácil, no dude en seguir leyendo sobre la validación de la dirección de correo electrónico si le interesa aprender o simplemente usar la respuesta rápida y seguir adelante. Sin resentimientos.

Intentar validar una dirección de correo electrónico utilizando una expresión regular es una tarea "imposible". Me atrevería a decir que esa expresión regular que has hecho es inútil. Hay tres rfc con respecto a las direcciones de correo electrónico y escribir una expresión regular para atrapar direcciones de correo electrónico incorrectas y al mismo tiempo no tener falsos positivos es algo que ningún mortal puede hacer. Consulte esta lista para ver las pruebas (fallidas y exitosas) de la expresión regular utilizada por PHPfilter_var() función .

Incluso las funciones PHP integradas, los clientes o servidores de correo electrónico no funcionan correctamente. Aún en la mayoría de los casosfilter_var es la mejor opción.

Si desea saber qué patrón de expresiones regulares utiliza PHP (actualmente) para validar las direcciones de correo electrónico, consulte la fuente de PHP .

Si desea obtener más información sobre las direcciones de correo electrónico, le sugiero que comience a leer las especificaciones, pero debo advertirle que no es una lectura fácil de ninguna manera:

Tenga en cuenta que filter_var() como ya se indicó, solo está disponible a partir de PHP 5.2. En caso de que desee que funcione con versiones anteriores de PHP, puede usar la expresión regular utilizada en 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
}

PD Una nota sobre el patrón regex utilizado anteriormente (de la fuente PHP). Parece que hay algunos derechos de autor sobre él de Michael Rushton . Como se indicó: "Siéntase libre de usar y redistribuir este código. Pero conserve este aviso de copyright".

PeeHaa
fuente
Buena respuesta, pero de acuerdo con este enlace: haacked.com/archive/2007/08/21/… el nombre de usuario o parte local puede ser entre comillas, pero FILTER_VALIDATE_EMAIL no lo acepta.
Daniel De León
3
No funciona para todas las direcciones de correo electrónico como se indicó. También vea la lista de pruebas fallidas en mi respuesta para ver que algunas cadenas citadas funcionan y otras no.
PeeHaa
44
No, demasiadas pruebas fallidas en ese patrón emailtester.pieterhordijk.com/test-pattern/MTAz :-)
PeeHaa
1
Este patrón es extremadamente complejo en caso de que necesite usarlo con una función como "preg_match_all" sobre una cadena de texto grande con correos electrónicos dentro. Si alguno de ustedes tiene más simple, por favor comparta. Quiero decir si quieres: preg_match_all ($ pattern, $ text_string, $ coincide); entonces este patrón complejo sobrecargará el servidor si necesita analizar texto realmente grande.
Vlado
44
@PeeHaa: Postfix 3.0 lo soporta desde hace casi dos años: postfix.org/SMTPUTF8_README.html , y está incluido en Ubuntu 16.04 y se incluirá en la próxima versión de Debian, por ejemplo. Exim tiene soporte experimental. Los proveedores de correo web como Gmail también han agregado soporte para enviar / recibir dichos correos electrónicos, aunque todavía no puede crear cuentas Unicode. El uso generalizado y el soporte están al alcance, y filter_varse retrasarán bastante tiempo, incluso si lo cambian en este momento (he publicado un informe de error).
iquito
43

Puede usar filter_var para esto.

<?php
   function validateEmail($email) {
      return filter_var($email, FILTER_VALIDATE_EMAIL);
   }
?>
Cameron Martin
fuente
1
deje de agregar esta función ya que esto no valida los dominios. Si agrega alguna dirección @, esto es válido. y no lo es!
Herr Nentu '11 de
¿Qué pasa con todas las funciones de una línea que contienen funciones de una línea? Los estoy viendo en todas partes. ¿Cuándo se convirtió esto en una "cosa"? (retórico). Esto tiene que parar.
Blue Water el
15

En mi experiencia, las regexsoluciones tienen demasiados falsos positivos y las filter_var()soluciones tienen falsos negativos (especialmente con todos los TLD más nuevos ).

En cambio, es mejor asegurarse de que la dirección tenga todas las partes requeridas de una dirección de correo electrónico (usuario, símbolo "@" y dominio), luego verifique que el dominio en sí existe.

No hay forma de determinar (del lado del servidor) si existe un usuario de correo electrónico para un dominio externo.

Este es un método que creé en una clase de utilidad:

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
fuente
Neverbounce afirma que su API puede validarse con una entrega del 97%. Siempre y cuando no le importe entregar su base de datos de contactos, por supuesto.
Tom Russell el
stristrno podrá obtener el dominio si hay múltiples signos @. Mejor explode('@',$email)y verifique esosizeof($array)==2
Aaron Gillion
@AaronGillion Si bien está en lo correcto en cuanto a una mejor manera de obtener partes del dominio, el método aún devolvería falso como checkdnsrr()devolvería falso si hubiera un signo @ en el dominio.
Jabari
11

Creo que podría ser mejor usar los filtros incorporados de PHP, en este caso particular:

Puede devolver un verdadero o falso cuando se le proporciona el FILTER_VALIDATE_EMAILparámetro.

Fluffeh
fuente
9

Esto no solo validará su correo electrónico, sino que también lo desinfectará para caracteres inesperados:

$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
fuente
4

Respondí esto en la 'pregunta principal' sobre la verificación de correos electrónicos https://stackoverflow.com/a/41129750/1848217

Para mí, la forma correcta de revisar los correos electrónicos es:

  1. Verifique que el símbolo @ exista, y antes y después hay algunos símbolos que no son @: /^[^@]+@[^@]+$/
  2. Intente enviar un correo electrónico a esta dirección con algún "código de activación".
  3. Cuando el usuario "active" su dirección de correo electrónico, veremos que todo está bien.

Por supuesto, puede mostrar alguna advertencia o información sobre herramientas en el front-end cuando el usuario escribió un correo electrónico "extraño" para ayudarlo a evitar errores comunes, como ningún punto en la parte del dominio o espacios en el nombre sin citar, etc. Pero debe aceptar la dirección "hello @ world" si el usuario realmente la quiere.

Además, debe recordar que el estándar de dirección de correo electrónico fue y puede evolucionar, por lo que no puede simplemente escribir alguna expresión regular "estándar válida" de una vez y para siempre. Y debe recordar que algunos servidores de Internet concretos pueden fallar algunos detalles del estándar común y de hecho funcionan con un "estándar modificado" propio.

Por lo tanto, simplemente marque @, insinúe al usuario en la interfaz y envíe correos electrónicos de verificación en la dirección dada.

Tormenta de llamas
fuente
1
Su expresión regular verifica @, pero en realidad no verifica que sea válida según ninguno de los RFC que rigen el correo electrónico. Tampoco funciona como está escrito. Lo ejecuté a través de regex101.com y no pudo coincidir con direcciones válidas
Machavity
¿Lees solo expresiones regulares o la respuesta completa? Totalmente en desacuerdo contigo. Solo dígame por favor, de acuerdo con la RFC que el servidor de gmail.com supone que [email protected] y [email protected] es la misma dirección. Hay muchos servidores que no funcionan según los estándares o no según los estándares FRESH. Pero sirven correos electrónicos de sus usuarios. Si escribe alguna expresión regular una vez y valida solo con eso, no tiene garantía de que se mantendrá en el futuro y sus futuros usuarios no fallarán con sus correos electrónicos de "nueva forma". Por lo tanto, mi posición es la misma: punto principal si desea verificar la dirección de correo electrónico, solo envíe un correo electrónico de activación.
FlameStorm
@Machavity pero gracias por el informe de errores en regexp, lo arreglé de /^[^@]+@[^@+]$/a/^[^@]+@[^@]+$/
FlameStorm
Ayuda para arreglar la expresión regular, pero ¿cómo mejora eso sobre el filter_varmétodo? Tampoco soluciona el problema de aceptar direcciones mal formateadas. Su expresión regular lo aceptará felizmente joe@domaincomo una dirección de correo electrónico válida, cuando no lo es
Machavity
@Machavity, bueno, por ejemplo, hay una versión concreta de PHP en su servidor y no puede actualizarla a la más nueva. Por ejemplo, tienes php 5.5.15. En 2018 se amplió el estándar de correos electrónicos válidos. Se realizará en php 7.3.10 pronto. Y habrá una buena función de trabajo filter_var($email, FILTER_VALIDATE_EMAIL, $newOptions). Pero tiene una función antigua en el servidor, no puede actualizar en algunos casos. Y perderá clientes con algunos nuevos correos electrónicos válidos. Además, una vez más me doy cuenta de que no todos los servidores de correo electrónico funcionan estrictamente de acuerdo con el estándar común y moderno de direcciones de correo electrónico.
FlameStorm
3

Si desea verificar si el dominio proporcionado desde la dirección de correo electrónico es válido, use algo como:

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

Esta es una forma práctica de filtrar muchas direcciones de correo electrónico no válidas, junto con la validación de correo electrónico estándar, porque el formato de correo electrónico válido no significa correo electrónico válido .

Tenga en cuenta que la función idn_to_ascii()(o su función hermana idn_to_utf8()) puede no estar disponible en su instalación de PHP, requiere extensiones PECL intl> = 1.0.2 y PECL idn> = 0.1.

También tenga en cuenta que IPv4 o IPv6 como parte del dominio en el correo electrónico (por ejemplo user@[IPv6:2001:db8::1]) no se pueden validar, solo los hosts con nombre pueden hacerlo .

Ver más aquí .

Bud Damyanov
fuente
No creo que funcione si la parte del host de la dirección de correo electrónico está en dirección IP en formato IPv6
GordonM
2

Después de leer las respuestas aquí, esto es con lo que terminé:

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
fuente
1

Si sólo está buscando una expresión regular real que permite varios puntos, guiones y guiones, como sigue: [a-zA-z0-9.-]+\@[a-zA-z0-9.-]+.[a-zA-Z]+. Eso permitirá tom_anderson.1-neo@my-mail_matrix.comvalidar un correo electrónico de aspecto bastante estúpido .

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

Hoy en día, si usa un formulario HTML5, type=emailentonces ya está un 80% seguro ya que los motores de navegador tienen su propio validador. Para complementarlo, agregue esta expresión regular a su preg_match_all()y niegue:

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

Encuentre la expresión regular utilizada por los formularios HTML5 para la validación
https://regex101.com/r/mPEKmy/1

Thielicious
fuente
Odio los votos negativos también sin explicación. Bueno, supongo que podría decir: la verificación del correo electrónico del navegador (del lado del cliente) no es segura en absoluto. Cualquiera puede enviar cualquier cosa a un servidor cambiando el código. Por lo tanto, es obvio y la forma más segura de verificar (nuevamente) el lado del servidor. La pregunta aquí se basa en PHP, por lo que es obvio que Cameron estaba buscando una solución de servidor y no una solución de cliente.
Jonny
Es posible que esta respuesta no esté completamente relacionada con PHP, pero su sugerencia de HTML cubre al usuario "estándar" que usa solo un teléfono / PC. Además, el usuario obtiene una información directamente en "su" navegador mientras usa el sitio. Verificaciones reales en el lado del servidor no están cubiertas con esto, claro. Por cierto, @Thielicious mencionó un cambio en PHP, por lo que su comentario está relacionado con mi humilde opinión.
k00ni
Probablemente recibió votos negativos debido a la suposición de que está "80% seguro ya que los motores de navegador tienen su propio validador". Hay muchas otras formas de enviar solicitudes http que a través de un navegador, por lo que no puede asumir que cualquier solicitud es segura ... incluso si verifica el agente del navegador.
Jabari