Comment valider une adresse e-mail en PHP

218

J'ai cette fonction pour valider une adresse email:

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

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

Est-ce correct de vérifier si l'adresse e-mail est valide ou non?

Cameron
la source
1
Si ça marche ça marche. On ne peut pas vraiment l'améliorer, c'est trop petit. La seule chose qui n'est pas bonne, c'est le style. validateEmailserait corret, ainsi que passant $email, non $EMAIL.
Stan
Je voulais juste m'assurer que je n'ai eu aucun problème majeur dans le code, c'est tout :)
Cameron
Voir également stackoverflow.com/questions/201323/… pour en savoir plus et comment ne pas utiliser les expressions régulières pour valider les adresses e-mail.
legoscia
5
Cela ne permettrait pas de valider de nombreuses adresses e-mail valides. Par exemple *@example.com ou'@example.com ou moi @ [127.0.0.1] ou vous @ [ipv6: 08B0: 1123: AAAA :: 1234]
jcoder
7
@jcoder, pas que je recommande cette expression régulière, mais au moins nous pouvons espérer que quiconque utilise de telles adresses pour chanter, etc. ne se plaindra pas en cas d'échec :)
Halil Özgür

Réponses:

568

La façon la plus simple et la plus sûre de vérifier si une adresse e-mail est bien formée est d'utiliser la filter_var()fonction:

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

De plus, vous pouvez vérifier si le domaine définit un MXenregistrement:

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

Mais cela ne garantit toujours pas que le courrier existe. La seule façon de le savoir est d'envoyer un e-mail de confirmation.


Maintenant que vous avez votre réponse facile, n'hésitez pas à lire sur la validation des adresses e-mail si vous souhaitez apprendre ou utiliser simplement la réponse rapide et continuer. Pas d'émotions fortes.

Tenter de valider une adresse e-mail à l'aide d'une expression régulière est une tâche «impossible». J'irais jusqu'à dire que ce regex que vous avez fait est inutile. Il y a trois RFC concernant les adresses e-mail et l'écriture d'une expression rationnelle pour attraper les adresses e-mail erronées et en même temps, ne pas avoir de faux positifs, c'est quelque chose qu'aucun mortel ne peut faire. Consultez cette liste pour les tests (échoués et réussis) de l'expression rationnelle utilisée par la filter_var()fonction PHP .

Même les fonctions PHP intégrées, les clients de messagerie ou les serveurs ne fonctionnent pas correctement. Dans la plupart des cas, filter_varc'est toujours la meilleure option.

Si vous voulez savoir quel modèle d'expression régulière PHP (actuellement) utilise pour valider les adresses e-mail, consultez la source PHP .

Si vous voulez en savoir plus sur les adresses e-mail, je vous suggère de commencer à lire les spécifications, mais je dois vous avertir que ce n'est pas une lecture facile par tous les bouts:

Notez que cela filter_var()n'est déjà indiqué que disponible à partir de PHP 5.2. Si vous voulez qu'il fonctionne avec des versions antérieures de PHP, vous pouvez utiliser l'expression régulière utilisée 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
}

PS Une note sur le modèle d'expression régulière utilisé ci-dessus (à partir de la source PHP). Il semble qu'il y ait des droits d'auteur sur Michael Rushton . Comme indiqué: "N'hésitez pas à utiliser et à redistribuer ce code. Mais veuillez conserver cette notice de copyright."

PeeHaa
la source
Bonne réponse, mais selon ce lien: haacked.com/archive/2007/08/21/… le nom d'utilisateur o localement peut être entre guillemets, mais le FILTER_VALIDATE_EMAIL ne l'accepte pas.
Daniel De León
3
Cela ne fonctionne pas pour toutes les adresses e-mail comme indiqué. Consultez également la liste des tests ayant échoué dans ma réponse pour voir que certaines chaînes citées fonctionnent et d'autres non.
PeeHaa
4
Non, trop de tests ont échoué sur ce modèle emailtester.pieterhordijk.com/test-pattern/MTAz :-)
PeeHaa
1
Ce modèle est extrêmement complexe au cas où vous auriez besoin de l'utiliser avec une fonction comme "preg_match_all" sur une grande chaîne de texte avec des e-mails à l'intérieur. Si l'un de vous a plus simple, veuillez partager. Je veux dire si vous voulez: preg_match_all ($ pattern, $ text_string, $ matches); alors ce modèle complexe surchargera le serveur si vous avez besoin d'analyser un texte très volumineux.
Vlado
4
@PeeHaa: Postfix 3.0 le prend en charge depuis presque deux ans maintenant: postfix.org/SMTPUTF8_README.html , et il est inclus dans Ubuntu 16.04 et sera inclus dans la prochaine version de Debian, par exemple. Exim a un support expérimental. Les fournisseurs de messagerie Web comme Gmail ont également ajouté la prise en charge pour l'envoi / la réception de tels e-mails, bien que vous ne puissiez pas encore créer de comptes Unicode. Une utilisation et un support généralisés sont à portée de main et filter_varseront à la traîne d'un certain temps, même s'ils les modifient actuellement (j'ai publié un rapport de bogue).
iquito
43

Vous pouvez utiliser filter_var pour cela.

<?php
   function validateEmail($email) {
      return filter_var($email, FILTER_VALIDATE_EMAIL);
   }
?>
Cameron Martin
la source
1
arrêtez d'ajouter cette fonction car cela ne valide pas les domaines. si vous ajoutez une adresse @, cela est valide. et ce n'est pas le cas!
Herr Nentu '11
Qu'y a-t-il avec toutes les fonctions d'une seule ligne contenant des fonctions d'une seule ligne? Je les vois partout. Quand est-ce devenu une "chose"? (rhétorique). Cela doit cesser.
Blue Water
15

D'après mon expérience, les regexsolutions ont trop de faux positifs et les filter_var()solutions ont de faux négatifs (en particulier avec tous les nouveaux TLD ).

Au lieu de cela, il est préférable de s'assurer que l'adresse contient toutes les parties requises d'une adresse e-mail (utilisateur, symbole "@" et domaine), puis vérifiez que le domaine lui-même existe.

Il n'y a aucun moyen de déterminer (côté serveur) si un utilisateur de messagerie existe pour un domaine externe.

Il s'agit d'une méthode que j'ai créée dans une 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
la source
Neverbounce affirme que son API est en mesure de valider à 97% de livraison. Tant que cela ne vous dérange pas de remettre votre base de données de contacts, bien sûr.
Tom Russell
stristréchouera à obtenir le domaine s'il y a plusieurs signes @. Mieux vaut explode('@',$email)et vérifier celasizeof($array)==2
Aaron Gillion
@AaronGillion Bien que vous ayez raison de trouver un meilleur moyen d'obtenir des parties de domaine, la méthode retournerait toujours false comme checkdnsrr()s'il y avait un signe @ dans le domaine.
Jabari
11

Je pense que vous feriez mieux d'utiliser les filtres intégrés de PHP - dans ce cas particulier:

Il peut retourner un vrai ou un faux lorsqu'il est fourni avec le FILTER_VALIDATE_EMAILparamètre.

Fluffeh
la source
9

Cela permettra non seulement de valider votre e-mail, mais aussi de le nettoyer pour les caractères inattendus:

$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
la source
4

A répondu à cette question dans la «question principale» sur la vérification des e-mails https://stackoverflow.com/a/41129750/1848217

Pour moi, la bonne façon de vérifier les e-mails est:

  1. Vérifiez que le symbole @ existe, et avant et après il y a des symboles non @: /^[^@]+@[^@]+$/
  2. Essayez d'envoyer un e-mail à cette adresse avec un "code d'activation".
  3. Lorsque l'utilisateur "activera" son adresse e-mail, nous verrons que tout va bien.

Bien sûr, vous pouvez afficher un avertissement ou une info-bulle en front-end lorsque l'utilisateur a tapé un e-mail "étrange" pour l'aider à éviter les erreurs courantes, comme aucun point dans la partie du domaine ou des espaces dans le nom sans citer, etc. Mais vous devez accepter l'adresse "hello @ world" si l'utilisateur le veut vraiment.

En outre, vous devez vous rappeler que la norme d'adresse e-mail a été et peut évoluer, vous ne pouvez donc pas simplement taper une expression rationnelle "standard valide" une fois pour toutes. Et vous devez vous rappeler que certains serveurs Internet concrets peuvent faire échouer certains détails de la norme commune et, en fait, fonctionner avec leur propre "norme modifiée".

Donc, il suffit de cocher @, d'indiquer l'utilisateur sur le frontend et d'envoyer des e-mails de vérification à une adresse donnée.

FlameStorm
la source
1
Votre expression régulière vérifie @, mais elle ne vérifie pas vraiment qu'elle est valide par aucun des RFC qui régissent le courrier électronique. Cela ne fonctionne pas non plus comme écrit. Je l'ai exécuté via regex101.com et il n'a pas réussi à faire correspondre les adresses valides
Machavity
Lisez-vous uniquement l'expression régulière ou la réponse entière? Entièrement en désaccord avec vous. Dites-moi simplement s'il vous plaît, selon quel RFC le serveur gmail.com suppose que [email protected] et [email protected] est la même adresse? Il y a beaucoup de serveurs qui ne fonctionnent pas selon les normes ou non selon les normes FRESH. Mais ils servent les e-mails de leurs utilisateurs. Si vous tapez une expression rationnelle une fois et ne validez que par cela, vous n'avez aucune garantie qu'elle restera correcte à l'avenir et vos futurs utilisateurs n'échoueront pas avec leurs e-mails "nouvelle façon". Donc, ma position est la même: point principal si vous voulez vérifier l'adresse e-mail - envoyez simplement un e-mail d'activation.
FlameStorm
@Machavity mais merci pour bugreport dans regexp, je l'ai corrigé de /^[^@]+@[^@+]$/à/^[^@]+@[^@]+$/
FlameStorm
Les accessoires pour vous de fixer le regex, mais comment cela améliore-t-il par rapport à la filter_varméthode? Cela ne résout pas non plus le problème d'accepter des adresses mal formatées. Votre expression régulière acceptera volontiers joe@domaincomme adresse e-mail valide, quand ce n'est pas le cas
Machavity
@Machavity, eh bien, par exemple, il y a une version concrète de PHP sur votre serveur et vous ne pouvez pas la mettre à jour vers la plus récente. Par exemple, vous avez php 5.5.15. En 2018, la norme des e-mails valides a été étendue. Il sera bientôt réalisé en php 7.3.10. Et il y aura une bonne fonction filter_var($email, FILTER_VALIDATE_EMAIL, $newOptions). Mais vous avez une ancienne fonction sur le serveur, vous ne pouvez pas mettre à jour dans certains cas. Et vous perdrez des clients avec de nouveaux e-mails valides. De plus, une fois de plus, je remarque que tous les serveurs de messagerie ne fonctionnent pas strictement en fonction du standard commun et moderne des adresses e-mail.
FlameStorm
3

Si vous souhaitez vérifier si le domaine fourni à partir de l'adresse e-mail est valide, utilisez quelque chose comme:

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

C'est un moyen pratique de filtrer un grand nombre d'adresses e-mail non valides, ainsi qu'une validation standard des e-mails, car un format d'e-mail valide ne signifie pas un e-mail valide .

Notez que la fonction idn_to_ascii()(ou sa fonction sœur idn_to_utf8()) peut ne pas être disponible dans votre installation PHP, elle nécessite les extensions PECL intl> = 1.0.2 et PECL idn> = 0.1.

Gardez également à l'esprit que IPv4 ou IPv6 en tant que partie de domaine dans le courrier électronique (par exemple user@[IPv6:2001:db8::1]) ne peut pas être validé, seuls les hôtes nommés le peuvent.

Voir plus ici .

Bud Damyanov
la source
Je ne pense pas que cela fonctionnera si la partie hôte de l'adresse e-mail est en adresse IP au format IPv6
GordonM
2

Après avoir lu les réponses ici, voici ce que je me suis retrouvé avec:

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
la source
1

Si vous êtes à la recherche d'un regex qui permet de divers points, tirets et underscores, il suit que: [a-zA-z0-9.-]+\@[a-zA-z0-9.-]+.[a-zA-Z]+. Cela permettra de tom_anderson.1-neo@my-mail_matrix.comvalider un e-mail d'aspect assez stupide .

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

De nos jours, si vous utilisez un formulaire HTML5 avec, type=emailvous êtes déjà à 80% en sécurité car les moteurs de navigateur ont leur propre validateur. Pour le compléter, ajoutez cette expression régulière à votre preg_match_all()et annulez-la:

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

Trouvez l'expression régulière utilisée par les formulaires HTML5 pour la validation
https://regex101.com/r/mPEKmy/1

Thielicious
la source
Je déteste aussi les downvotes sans explication. Eh bien, je suppose qu'il pourrait dire: la vérification des e-mails du navigateur (côté client) n'est pas sécurisée du tout. Tout le monde peut envoyer n'importe quoi à un serveur en changeant le code. Il est donc évident et le moyen le plus sûr de vérifier (encore) côté serveur. La question ici est basée sur PHP, donc son évident Cameron cherchait une solution serveur et non une solution client.
Jonny
Cette réponse n'est peut-être pas entièrement liée à PHP, mais la suggestion HTML couvre l'utilisateur "standard" utilisant uniquement un téléphone / PC. L'utilisateur obtient également une information directement dans "son" navigateur lors de l'utilisation du site. Les vrais contrôles côté serveur ne sont pas couverts par cela, bien sûr. Btw, @Thielicious a mentionné un changement PHP, donc son commentaire est lié à mon humble avis.
k00ni
Il a probablement reçu des votes négatifs en raison de l'hypothèse que vous êtes "à 80% sûr car les moteurs de navigateur ont leur propre validateur". Il existe de nombreuses autres façons d'envoyer des requêtes http que via un navigateur, vous ne pouvez donc pas supposer que toute requête est sûre ... même si vous vérifiez l'agent de navigateur.
Jabari