Envoyer un email avec PHP

E

Lorsque vous vous inscrivez sur un site, par exemple, une confirmation d’inscription vous parvient généralement sous forme d’email contenant les informations que vous avez renseignées. En PHP, l’envoi d’email est effectué grâce à la fonction mail(), que je vous propose de décortiquer avant de voir les possibilités d’utilisation qu’elle permet.

Tout d’abord, la signature de la fonction est la suivante :

mail($to, $subject, $message, [$headers]);

Les trois premiers paramètres sont obligatoires et correspondent respectivement à l’email du destinataire, au sujet du message et au message en lui-même. Le quatrième paramètre est optionnel et permet de préciser un en-tête additionnel.

Précision importante : chaque ligne ne peut excéder 70 caractères et doit impérativement se terminer par un saut de ligne, représenté par \\n. Afin que cette séquence soit bien interpétée comme un saut de ligne, il faut qu’elle soit entourée de guillemets doubles.

mail() en développement local sous Windows

Si vous développez en local sous Windows et que vous souhaitez utiliser la fonction mail() de PHP, il faut paramétrer le fichier php.ini en conséquence, sans cela vos emails ne partiront pas et généreront même une erreur.

Ouvrez le fichier php.ini et rendez vous à la ligne [mail function]. Dans cette section, assignez la valeur du serveur SMTP de votre fournisseur d’accès internet. Par exemple, comme ceci pour Belgacom : SMTP = relay.skynet.be. Redémarrez le serveur ensuite.

Les en-têtes ou headers

Bien que le paramètre soit optionnel, les en-têtes sont incontournables dès lors que vous prenez le parti d’envoyer un email un tant soit peu personnalisé, que ça soit par une adresse de réponse, une copie cachée ou un content-type HTML. Voici la liste des principaux en-têtes :

Reply-To
Adresse email de réponse. Reply-To: adress
From
Adresse email de l’expéditeur. From: “Nom de l’expéditeur”
Cc
Autres destinataires qui recevront le mail en Carbon Copy (chaque destinataire peut voir à qui le message est transmis).Cc: email1, email2, email3 …
Bcc
Autres destinataires qui recevront le mail en Blind Carbon Copy (les destinataires sont cachés).Bcc: email1, email2, email3 …
Content-Type
Type mime du mail et son charset (jeu de caractères).
Content-Transfer-Encoding
Encodage du mail ou de l’une de ses parties (utile dans le cas d’un envoi texte + html par exemple). Il peut par exemple prendre les valeurs 7 et 8 bit (l’encodage 7 bit étant utilisé dans les pays anglophones n’ayant pas besoin de gérer les lettres accentuées).
X-Priority
Niveau de priorité du mail envoyé. Peut prendre une valeur variant de 1 (priorité élevée) à 5 (priorité faible).
X-Mailer
Version de PHP utilisée pour envoyer l’email.
Disposition-Notification-To
Permet de spécifier l’adresse mail de retour pour la confirmation de lecture.

Envoi d’email au format texte

Les emails au format texte ne possèdent pas de mise en forme HTML et ne sont quasiment jamais utilisés seuls. On y recourt généralement en complément d’un email au format HTML, afin de proposer une solution de repli aux clients mails qui ne supportent pas le format HTML.

Voici un exemple d’utilisation d’en-tête dans l’envoi d’un email plain/text :

$headers = '' . \"\\n\";
$headers .= 'From: "Name Firstname"""' . \"\\n\";
$headers .= 'Reply-To: adress@fai.com' . \"\\n\";
$headers .= 'Content-Type: text/plain; charset="utf-8"' . \"\\n\";
$headers .= 'Content-Transfer-Encoding: 8bit' . \"\\n\";
$to      = 'adress@fai.com';
$subject = 'Subject of the mail';
$message = 'Message';
if(mail($to, $subject, $message, $headers)) {
  echo 'Le message a bien été envoyé';
} else { 
  echo 'Le message n\'a pas pu être envoyé';
}

Deux remarques à propos de cet extrait de code.

L’en-tête n’est ni plus ni moins qu’une chaîne de caractères comportant des sauts de lignes. Par souci de lisibilité, et parce qu’idéalement chaque ligne ne devrait pas dépasser 70 caractères, chaque en-tête est écrit sur une ligne concaténée avec la précédente, de façon à ne transmettre qu’une seule chaîne en paramètre.

Le mail est envoyé via une structure if … else classique qui permet de gérer le retour de la fonction (booléen).

Envoi d’email au format HTML

Nous ne devons effectuer que deux petites modifications par rapport à notre email en plain/text. D’abord, notre message sera, cette fois, du HTML. Ensuite, il faut préciser dans l’en-tête content-type qu’il s’agit bien de text/html. C’est tout.

$headers = '' . \"\\n\";
$headers .= 'From: "Name Firstname"""' . \"\\n\";
$headers .= 'Reply-To: adress@fai.com' . \"\\n\";
$headers .= 'Content-Type: text/html; charset="utf-8"' . \"\\n\";
$headers .= 'Content-Transfer-Encoding: 8bit' . \"\\n\";
$to      = 'adress@fai.com';
$subject = 'Subject of the mail';
$message = 'Message';
if(mail($to, $subject, $message, $headers)) {
  echo 'Le message a bien été envoyé';
} else { 
  echo 'Le message n\'a pas pu être envoyé';
}

Voilà, rien de bien compliqué, vous êtes à présent capable d’envoyer un email simple avec PHP.

Envoi d’email mixte

Certains clients mails, de plus en plus rares, ne sont pas capables d’interpréter le code HTML. Pour ceux-là, il faut donc prévoir une solution de repli qui consiste à envoyer un seul mail contenant deux messages, l’un en plain/text et l’autre en text/html. De cette façon, les clients qui peuvent lire le HTML liront le message HTML, ceux qui n’en sont pas capables liront le message texte.

Techniquement, il y a cette fois quelques subtilités auxquelles il faut être attentif.

$boundary = '-----=' . md5(uniqid(mt_rand()));
$to    = 'destination@fai.com';
$from  = 'expedition@fai.com';
$reply = 'replay@fai.com';
$headers = '';
$headers .= 'From: "Nom" <' . $from . '>' . "\\n\";
$headers .= 'Return-Path: <' . $reply . '>' . "\\n\";
$headers .= 'MIME-Version: 1.0' . "\\n\";
$headers .= 'Content-Type: multipart/alternative; boundary ="' . $boundary . '"';
$text = 'Bonjour,' . "\\n\\n\" . 'Voici un message au format texte';
$html = '
  
    Titre
  
  Test de message HTML
  ';
$message  = '';
$message .= 'This is a multi-part message in MIME format.' . "\\n\\n\";
$message .= '--' . $boundary . "\\n\";
$message .= 'Content-Type: text/plain; charset="utf-8"' . "\\n\";
$message .= 'Content-Transfer-Encoding: 8bit' . "\\n\\n\";
$message .= $text . "\\n\\n\";
$message .= '--' . $boundary . "\\n\";
$message .= 'Content-Type: text/html; charset="utf-8"' . "\\n\";
$message .= 'Content-Transfer-Encoding: 8bit' . "\\n\\n\";
$message .= $html . "\\n\\n\";
$message .= '--' . $boundary . "\\n\";
if(mail($to, $subject, $message, $headers)) {
  echo 'Le message a bien été envoyé';
} else { 
  echo 'Le message n\'a pas pu être envoyé';
}

Le premier écueil est de générer une variable $boundary qui permettra au client de bien distinguer les différents contenus. Pour cela, nous devons générer un identifiant unique, une sorte de token dont on est certain qu’il ne se retrouvera nulle part ailleurs dans le code.

Nous utilisons ici trois fonctions natives de PHP pour générer cette frontière : mt_rand() génère un nombre aléatoire entre 0 et la plus grande valeur aléatoire possible sur le système (valeur retournée par mt_getrandmax(). Le retour de cette fonction est ensuite directement passé à la fonction uniqid() qui, elle, retourne un identifiant unique sous forme de string. Enfin, cet identifiant est passé à la fonction de haschage md5(), qui retourne une chaîne sous forme de nombre hexadécimal de 32 caractères. Notre frontière est blindée.

Nous renseignons la frontière dans le header global afin d’en informer le client. Ensuite, nous l’utilisons pour délimiter la partie plain/text et la partie text/html, en n’oubliant pas de clôturer la dernière partie du mail. Pour bien être interprété comme une frontière par le client, la fontière doit, en sus, être entourée par deux tirets accolés.

Enfin, on peut modifier les headers locaux de chaque partie du message, pour plus de clarté. Notez bien que ce n’est pas obligatoire.

L’email n’a plus qu’à être envoyé.

Envoi d’email avec pièces-jointes

Les frontières dans les emails ne servent pas qu’à délimiter les différentes parties de l’email. On peut également, grâce à un délimiteur, envoyer des pièces-jointes à la conditions qu’elles soient encodées en base 64, ce qui est très facile à réaliser avec la fonction base64_encode(), très intéressante si on souhaite encoder des données binaires comme c’est généralement le cas pour une pièce-jointe.

$boundary = '-----=' . md5(uniqid(mt_rand()));
$to    = 'destination@fai.com';
$from  = 'expedition@fai.com';
$reply = 'replay@fai.com';
$headers = '';
$headers .= 'From: "Nom" <' . $from . '>' . "\\n\";
$headers .= 'Return-Path: <' . $reply . '>' . "\\n\";
$headers .= 'MIME-Version: 1.0' . "\\n\";
$headers .= 'Content-Type: multipart/alternative; boundary ="' . $boundary . '"';
$text = 'Bonjour,' . "\\n\\n\" . 'Voici un message au format texte';
$html = '
  
    Titre
  
  Test de message HTML
  ';
$message  = '';
$message .= 'This is a multi-part message in MIME format.' . "\\n\\n\";
$message .= '--' . $boundary . "\\n\";
$message .= 'Content-Type: text/plain; charset="utf-8"' . "\\n\";
$message .= 'Content-Transfer-Encoding: 8bit' . "\\n\\n\";
$message .= $text . "\\n\\n\";
$message .= '--' . $boundary . "\\n\";
$message .= 'Content-Type: text/html; charset="utf-8"' . "\\n\";
$message .= 'Content-Transfer-Encoding: 8bit' . "\\n\\n\";
$message .= $html . "\\n\\n\";
$message .= '--' . $boundary . "\\n\";
$message .= 'Content-Type: image/jpeg; name="nom-du-fichier.jpg"' . "\\n\";
$message .= 'Content-Transfer-Encoding: base64' . "\\n\";
$message .= 'Content-Disposition: attachement; filename="nom_du_fichier.jpg"' . "\\n\\n\";
$message .= chunk_split(base64_encode(file_get_contents('nom_du_fichier.jpg'))) . "\\n\";
if(mail($to, $subject, $message, $headers)) {
  echo 'Le message a bien été envoyé';
} else { 
  echo 'Le message n\'a pas pu être envoyé';
}

Vous aurez remarqué qu’il ne suffit pas d’encoder le retour de file_get_contents() en base 64. Nous devons aussi “spliter” cette chaîne afin de respecter la rfc2045. Sans rentrer dans les détails, retenez que la chaîne encodée doit être découpée en paquets pour faciliter le transport sur le réseau.

Conclusion

Nous avons développé ici quelques bouts de codes assez simples qui représentent l’ABC de l’envoi d’email en PHP. Pour de grosses applications qui font un usage récurrent/massif de l’envoi d’email, il est préférable d’utiliser une classe PHP, qu’elle soit personnelle ou distribuée.

N’hésitez pas à vous lancer dans la création d’une telle classe, qui répondra exactement à vos attentes. Si vous n’avez pas l’âme à développer une classe ou tout simplement que vous ne voulez pas réinventer la roue, il en existe beaucoup, de qualité et distribuées gratuitement sous des licences Open Source. Parmi celles-ci, je vous conseille la classe PHPMailer qui est relativement complète, ou encore le paquet PEAR Mail_Mime.

A propos de l'auteur

Steve Lebleu

Cross-triathlète, amoureux de nature, de grands espaces et ... d'applications web. Curieux et touche-à-tout, je m'intéresse à tous les aspects du développement d'un projet web. Je suis développeur full stack freelance depuis 2018, principalement sur des piles Javascript.

Ajouter un commentaire

  • Bonjour a tous,

    J’ai besoin d’aide pour mes envoie de mail. Quant j’utilise une seule variable $_POST pour mettre du contenu dans ma variable $message je retrouve le message dans ma boite mail mais a partir du moment ou je fais une concaténation plus rien ne sort

    MERCI