Como validar um endereço de email em PHP

218

Eu tenho esta função para validar um endereço de email:

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

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

Não há problema em verificar se o endereço de email é válido ou não?

Cameron
fonte
1
Se funcionar, funciona. Você não pode realmente melhorar, é muito pequeno. A única coisa que não é boa é o estilo. validateEmailseria correto, além de passageiro $email, não $EMAIL.
Stan
Só queria ter certeza que eu não tinha grandes problemas no código que é tudo :)
Cameron
Consulte também stackoverflow.com/questions/201323/… para obter mais informações sobre como e como não usar expressões regulares para validar endereços de email.
legoscia 20/08/2012
5
Isso não validaria muitos endereços de email válidos. Por exemplo *@exemplo.com ou'@exemplo.com ou eu @ [127.0.0.1] ou você @ [ipv6: 08B0: 1123: AAAA :: 1234]
jcoder
7
@jcoder, não que eu estou recomendando que regex, mas pelo menos podemos esperar alguém usando esses endereços para cantar até etc não iria reclamar quando ele falhar :)
Halil Özgür

Respostas:

568

A maneira mais fácil e segura de verificar se um endereço de e-mail está bem formado é usar a filter_var()função:

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

Além disso, você pode verificar se o domínio define um MXregistro:

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

Mas isso ainda não garante que o correio existe. A única maneira de descobrir isso é enviando um email de confirmação.


Agora que você tem sua resposta fácil, sinta-se à vontade para ler sobre a validação de endereço de e-mail, se quiser aprender ou simplesmente usar a resposta rápida e seguir em frente. Sem ressentimentos.

Tentar validar um endereço de email usando uma regex é uma tarefa "impossível". Eu diria que esse regex que você fez é inútil. Existem três rfc em relação a endereços de email e escrever um regex para capturar endereço de email errado e, ao mesmo tempo, não ter falsos positivos, é algo que nenhum mortal pode fazer. Confira esta lista para testes (com falha e com êxito) do regex usado pela filter_var()função do PHP .

Mesmo as funções embutidas do PHP, clientes ou servidores de email não acertam. Ainda na maioria dos casos, filter_varé a melhor opção.

Se você deseja saber qual padrão de expressão regular o PHP (atualmente) usa para validar endereços de email, consulte a fonte do PHP .

Se você quiser saber mais sobre endereços de e-mail, sugiro que você comece a ler as especificações, mas tenho que avisar que não é uma leitura fácil, de forma alguma:

Note que isso filter_var()já está disponível apenas a partir do PHP 5.2. Caso você queira que ele funcione com versões anteriores do PHP, você pode usar o regex usado no 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 Uma observação sobre o padrão regex usado acima (da fonte PHP). Parece que existem alguns direitos autorais de Michael Rushton . Como afirmado: "Sinta-se livre para usar e redistribuir este código. Mas mantenha este aviso de direitos autorais".

PeeHaa
fonte
Boa resposta, mas de acordo com este link: haacked.com/archive/2007/08/21/… o nome do usuário ou parte local pode ser citado, mas o FILTER_VALIDATE_EMAIL não o aceita.
Daniel De León
3
Não funciona para todos os endereços de email, conforme indicado. Veja também a lista de testes com falha na minha resposta para ver que algumas seqüências de caracteres citadas funcionam e outras não.
PeeHaa
4
Não, muitos testes falharam nesse padrão. Emailtester.pieterhordijk.com/test-pattern/MTAz :-)
PeeHaa
1
Esse padrão é extremamente complexo, caso você precise usá-lo com a função "preg_match_all" sobre grandes cadeias de texto com emails dentro. Se algum de vocês for mais simples, compartilhe. Quero dizer, se você quiser: preg_match_all ($ pattern, $ text_string, $ correspondências); esse padrão complexo sobrecarregará o servidor se você precisar analisar um texto realmente grande.
Vlado
4
@PeeHaa: O Postfix 3.0 o suporta há quase dois anos: postfix.org/SMTPUTF8_README.html , e está incluído no Ubuntu 16.04 e será incluído no próximo lançamento Debian, por exemplo. Exim tem suporte experimental. Provedores de webmail como o Gmail também adicionaram suporte para enviar / receber esses emails, embora você ainda não possa criar contas unicode. O uso e o suporte generalizados estão ao nosso alcance e filter_varficarão para trás por algum tempo, mesmo que eles sejam alterados agora (eu publiquei um relatório de bug).
Iquito 20/05
43

Você pode usar filter_var para isso.

<?php
   function validateEmail($email) {
      return filter_var($email, FILTER_VALIDATE_EMAIL);
   }
?>
Cameron Martin
fonte
1
pare de adicionar essa função, pois isso não valida domínios. se você estiver adicionando algum endereço @, isso é válido. e não é!
Herr Nentu '11 de
O que há com todas as funções de uma linha que contêm funções de uma linha? Eu os vejo em todos os lugares. Quando isso se tornou uma "coisa"? (retórico). Isso precisa parar.
Blue Water
15

Na minha experiência, as regexsoluções têm muitos falsos positivos e as filter_var()soluções têm falsos negativos (especialmente com todos os TLDs mais recentes ).

Em vez disso, é melhor garantir que o endereço tenha todas as partes necessárias de um endereço de email (usuário, símbolo "@" e domínio) e verifique se o domínio existe.

Não há como determinar (lado do servidor) se existe um usuário de email para um domínio externo.

Este é um método que eu criei em uma 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;
}
Jabari
fonte
A Neverbounce alega que sua API é capaz de validar a entrega de 97%. Contanto que você não se importe de entregar seu banco de dados de contatos, é claro.
Tom Russell
stristrfalhará ao obter o domínio se houver vários sinais @. Melhor explode('@',$email)e verificar issosizeof($array)==2
Aaron Gillion 15/02
@AaronGillion Embora você esteja correto quanto à melhor maneira de obter partes do domínio, o método ainda retornará false, assim como checkdnsrr()retornaria false se houvesse um sinal @ no domínio.
Jabari 16/02
11

Eu acho que você pode estar melhor usando os filtros embutidos do PHP - neste caso em particular:

Pode retornar um valor verdadeiro ou falso quando fornecido com o FILTER_VALIDATE_EMAILparâmetro

Fluffeh
fonte
9

Isso não apenas validará seu email, mas também o limpará 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
fonte
4

Respondeu a isso na 'principal pergunta' sobre verificação de e-mails https://stackoverflow.com/a/41129750/1848217

Para mim, o caminho certo para verificar e-mails é:

  1. Verifique se o símbolo @ existe e antes e depois dele existem alguns símbolos não @: /^[^@]+@[^@]+$/
  2. Tente enviar um email para este endereço com algum "código de ativação".
  3. Quando o usuário "ativou" seu endereço de e-mail, veremos que está tudo certo.

Obviamente, você pode mostrar algum aviso ou dica de ferramenta no front-end quando o usuário digitar o email "estranho" para ajudá-lo a evitar erros comuns, como nenhum ponto na parte do domínio ou espaços no nome sem citar e assim por diante. Mas você deve aceitar o endereço "hello @ world" se o usuário realmente o desejar.

Além disso, lembre-se de que o padrão de endereço de e-mail foi e pode evoluir; portanto, você não pode apenas digitar alguma regexp "com padrão válido" de uma vez por todas. E você deve se lembrar que alguns servidores de Internet concretos podem falhar em alguns detalhes do padrão comum e de fato trabalhar com o próprio "padrão modificado".

Portanto, marque @, insira o usuário no frontend e envie e-mails de verificação no endereço fornecido.

FlameStorm
fonte
1
Seu regex verifica @, mas não verifica se é válido para qualquer um dos RFCs que governam o email. Também não funciona como está escrito. Eu corri através regex101.com e não conseguiu igualar endereços válidos
Machavity
Você lê apenas regex ou a resposta completa? Discordo totalmente de você. Diga-me, por favor, de acordo com o RFC que o servidor gmail.com assume que [email protected] e [email protected] são o mesmo endereço? Existem muitos servidores que não funcionam de acordo com os padrões ou com os padrões FRESH. Mas eles servem e-mails de seus usuários. Se você digitar alguma regexp uma vez e validar somente com isso, não há garantia de que ela permanecerá correta no futuro e que seus futuros usuários não falharão com os emails "novos". Portanto, minha posição é a mesma: ponto principal, se você deseja verificar o endereço de e-mail - basta enviar um e-mail de ativação.
FlameStorm
@Machavity mas obrigado pelo relatório de erros no regexp, eu o /^[^@]+@[^@+]$//^[^@]+@[^@]+$/
corrigi
Adereços para você corrigir o regex, mas como isso melhora o filter_varmétodo? Também não resolve o problema de aceitar endereços mal formatados. Seu regex vai aceitar alegremente joe@domaincomo um endereço de email válido, quando não é
Machavity
@ Machavity, bem, por exemplo, existe uma versão concreta do PHP no seu servidor e você não pode atualizá-la para a mais nova. Por exemplo, você tem o php 5.5.15. Em 2018, o padrão de e-mails válidos foi estendido. Ele será realizado no php 7.3.10 em breve. E haverá uma função de bom trabalho filter_var($email, FILTER_VALIDATE_EMAIL, $newOptions). Mas você tem uma função antiga no servidor, você não pode atualizar em alguns casos. E você perderá clientes com alguns novos e-mails válidos. Além disso, mais uma vez, percebo que nem todos os servidores de envio de email funcionam estritamente de acordo com o padrão comum e moderno de endereços de email.
FlameStorm
3

Se você deseja verificar se o domínio fornecido a partir do endereço de email é 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)       
    }
}

Essa é uma maneira útil de filtrar muitos endereços de email inválidos, juntamente com a validação de email padrão, porque o formato de email válido não significa email válido .

Note que a função idn_to_ascii()(ou a função irmã dele idn_to_utf8()) pode não estar disponível na instalação do PHP, requer extensões PECL intl> = 1.0.2 e PECL idn> = 0.1.

Lembre-se também de que o IPv4 ou IPv6 como parte do domínio no email (por exemplo user@[IPv6:2001:db8::1]) não pode ser validado, apenas hosts nomeados podem.

Veja mais aqui .

Bud Damyanov
fonte
Eu não acho que ele vai trabalhar, se a parte do host do endereço de e-mail é o endereço IP no formato IPv6
GordonM
2

Depois de ler as respostas aqui, foi com isso que acabei:

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

Se você está apenas procurando um regex real que permite a vários pontos, sublinhados e traços, é a seguinte: [a-zA-z0-9.-]+\@[a-zA-z0-9.-]+.[a-zA-Z]+. Isso permitirá que um e-mail com aparência bastante estúpida tom_anderson.1-neo@my-mail_matrix.comseja validado.

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

Atualmente, se você usa um formulário HTML5 type=email, já está 80% seguro, pois os mecanismos do navegador têm seu próprio validador. Para complementá-lo, adicione este regex ao seu preg_match_all()e negue-o:

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

Encontre a regex usada pelos formulários HTML5 para validação
https://regex101.com/r/mPEKmy/1

Thielicious
fonte
Eu odeio votos negativos também sem explicação. Bem, acho que ele pode dizer: A verificação de e-mail do navegador (lado do cliente) não é segura. Qualquer pessoa pode enviar qualquer coisa para um servidor alterando o código. Portanto, é óbvio e a maneira mais segura de verificar (novamente) o lado do servidor. A questão aqui é baseada em PHP, portanto, é óbvio que Cameron estava procurando por uma solução de servidor e não por uma solução de cliente.
Jonny
Esta resposta pode não estar totalmente relacionada ao PHP, mas a sugestão de HTML cobre o usuário "padrão" usando apenas um telefone / PC. Além disso, o usuário recebe uma informação diretamente no navegador "his" enquanto usa o site. Verificações reais no lado do servidor não são cobertas com isso, com certeza. Btw, @Thielicious mencionou uma mudança no PHP, então seu comentário está relacionado ao IMHO.
K00ni
Provavelmente recebeu votos negativos devido à suposição de que você está "80% seguro, pois os mecanismos de navegador têm seu próprio validador". Existem muitas outras maneiras de enviar solicitações HTTP além do navegador, portanto, você não pode assumir que qualquer solicitação seja segura ... mesmo que verifique o agente do navegador.
Jabari 16/02