Image de l'article Envoyer un mail en HTML, une newsletter avec PHP (partie 2)

Envoyer un mail en HTML, une newsletter avec PHP (partie 2)

Dans cette seconde partie de l'article, je vais m'attarder sur l'intégration des images dans l'email.

Intégrez les images dans le mail en pièce jointe

Lorsque l'on envoie une newsletter en HTML, on insère les images avec une url absolue. Selon les paramètres du lecteur de mail, ces images ne sont pas chargées à l'affichage, mais après une confirmation de l'utilisateur. Rien de dramatique il doit simplement cliquer sur un bouton "Charger les images", mais pour certains clients c'est déjà trop compliqué.

Pour répondre à cela il faut intégrer les images. Nous allons donc les encoder en base64, les intégrer en pièces jointes dans le corps du message et remplacer les url par des identifiants uniques (cid).

La structure du corps du message change légèrement, il y a :

Ces deux parties principales sont délimitées par un type MIME Content-type: multipart/alternative;. Les éléments de la partie HTML + image seront eux délimités par un type MIME Content-Type: multipart/related;.

Il faut donc générer deux séparateurs différents. Pour bien comprendre voici à quoi ressemble visuellement la structure de l'email.

Content-type: multipart/alternative; Content-Type: multipart/related;


// on crée le séparateur entre la version texte et HTML
$sepAlternative = '-----='.md5(uniqid(mt_rand()));
// on crée le séparateur entre le code HTML et les images
$sepRelated = '-----='.md5(uniqid(mt_rand()));
// cherche les images
preg_match_all('~~si', $html, $matches);
$i = 0;
// on utilise la variable $paths pour enregistré le chemin de chaque image et son identifiant attribué
$paths = array();
// boucle à travers les images trouvées
foreach($matches[1] as $img){
	$img_old = $img;
	// vérifie si l'image est un lien relatif
	if(strpos($img, "http://") == false){
		$uri = parse_url($img);
		$paths[$i]['path'] = $_SERVER['DOCUMENT_ROOT'].$uri['path'];
		// génère un identifiant pour l'image
		$content_id = md5($img);
		// remplace la source de l'image par l'identifiant
		// <img src="image.png"> deviendra <img src="cid:9c08925d9cd90b3eca357d3c4c928f89">
		$html = str_replace($img_old, 'cid:'.$content_id, $html, $count);
		$paths[$i++]['cid'] = $content_id;
	}
}

// corps du message
// 1- on commence par un séparateur pour signifier le début des versions alternatives
$msg = "--$sepAlternative\n";
// 2- on affiche des entêtes pour la version texte
$msg .= "Content-Type: text/plain; charset=\"$charset\"\n";
$msg .= "Content-Transfer-Encoding: 8bits\n\n";
// 3- on place la version texte
$msg .= $txt."\n\n";
// 4- on ajoute un séparateur pour signifier la fin d'une version alterntive et le début d'une autre
$msg .= "--$sepAlternative\n";
// 5- on débute la version HTML on signifiant un multipart
$msg .= "Content-Type: multipart/related; boundary=\"$sepRelated\"\n\n";
$msg .= "--$sepRelated\n";
// 6- on affiche des entêtes pour la version HTML
$msg .= "Content-Type: text/html; charset=\"$charset\"\n";
$msg .= "Content-Transfer-Encoding: 8bits\n\n";
// 7- on affiche la version HTML
$msg .= "$html\n\n";
// 8- on affiche un séparateur
$msg .= "--$sepRelated\n";

foreach($paths as $path){
	if(file_exists($path['path'])){
		$imgType = substr(strrchr($path['path'], '.' ),1);
		$file = file_get_contents($path['path']);
		$msg_part = "";
		// affiche le type MIME de l'image
		switch($imgType){
		case 'png':
		case 'PNG':
			$msg_part .= "Content-Type: image/png";
			break;
		case 'jpg':
		case 'jpeg':
		case 'JPG':
		case 'JPEG':
			$msg_part .= "Content-Type: image/jpeg";
			break;
		case 'gif':
		case 'GIF':
			$msg_part .= "Content-Type: image/gif";
			break;
		}
		// nom du fichier d'origine
		$msg_part .= "; file_name=\"".basename($path['path'])."\"\n";
		// Content-ID
		$msg_part .= 'Content-ID: <'.$path['cid'].">\n";
		// encodage des données. Ici base64
		$msg_part .= "Content-Transfer-Encoding: base64\n";
		// affich
		$msg_part .= "Content-Disposition: inline; filename=\"".basename($path['path'])."\"\n\n";
		// encode et affiche l'image. chunk_split permet de générer des lignes de 76 caractères
		$msg_part .= chunk_split(base64_encode($file))."\n\n";
		// ajoute une séparateur
		$msg_part .= "--$sepRelated\n";
		$msg .= $msg_part;
	}
}
// ajoute les séparateurs finaux
$msg .= "--$sepRelated--\n\n--$sepAlternative--\n";

Conseil pour la version HTML

Les lecteurs de mail ont des moteurs de rendu HTML très différents et généralement obsolètes. Entre les versions d'Outlook, les webmails (gmail, yahoo, zimbra, etc), mail d'Apple, Thunderbird, etc. Chacun gère le code HTML selon ses propres désirs et ses propres bugs. Néanmoins, quelques bonnes pratiques vous permettrons de vous en sortir et d'envoyer un email qui passe à peu près convenablement partout :

 

Image Viewer