Lerne Coding
PHPMailer ist die sichere Art E-Mails zu versenden mit PHP
21.07.2020

E-Mails einfach und sicher verschicken mit dem PHPMailer

Inhaltsverzeichnis
[[TABLE OF CONTENTS]]
access_timeGeschätzte Lesezeit ca. 8 Minuten

In diesem Artikel möchte ich dir erklären wie man heutzutage Emails in PHP verschickt. Dazu verwendet man das Composer Package PHPMailer, ein von der Community entwickeltes Framework, welches immer die neusten Standards beachtet. Diese ist ein guter Mailer, um E-Mails via PHP zu versenden.

Github Logophpmailer/PHPMailer
20.572 41 9.682

The classic email sending library for PHP

Vorab die Theorie

Wenn du eine Email an mich verschicken möchtest, dann sagst du dem PHPMailer: "Hey, schick doch bitte mal eine Email an Tim". Sobald du das getan hast, versucht PHP sich über das Internet oder Lokal (je nach dem, ob du einen internen oder externen Mailserver verwendest) mit deinem SMTP-Emailserver zu verbinden und sich mit deinen Login-Daten zu authentifizieren.

Ist diese Anfrage erfolgreich beim SMTP-Server angekommen, sucht dieser sich eine Route zu meinem Emailserver. Je nach dem, wie stark meine Anforderungen an eine E-Mail zur Vermeidung von Spam sind, kann deine Anfrage nun angenommen oder abgelehnt werden. Provider wie GMX oder 1&1 haben da höhere Ansprüche, selbstgehostete Webspaces mit Plesk sind dagegen eher toleranter. Sollten deine Emails öfter abgelehnt werden, informiere einen Systemadministrator und lass dir dabei helfen, deine Domain richtig zu konfigurieren. Ob deine Email vertrauenswürdig ist oder nicht, kannst du auf Mailtester überprüfen

Wenn ich nun mit dem Thunderbird meinen Emailserver abfrage, verbinde ich mich über IMAP oder POP3 und kann je nach Protokoll (eher historisch bedingt; heute weniger relevant) entweder die Emails auf meinem Server lassen oder sie auf meinem Rechner speichern und auf meinem Mailserver löschen. Heutzutage wird im Normalfall aber nichts mehr gelöscht; beide Protokolle lassen die Emails stattdessen auf dem Server bestehen. Für Webmailnutzer ist das aber komplett irrelevant.

Installation mittels Composer

Sobald du den Composer installiert hast (wie das funktioniert erklärt dir Felix in diesem Artikel), kannst du folgenden Befehl ausführen und die neuste Version des PHPMailers wird automatisch installiert:

 composer require phpmailer/phpmailer

Wie eine Email verschickt wird?

Composer richtig importieren

Als erstes muss natürlich irgendwo in deinem Code der Composer inkludiert werden. Wichtig: Solltest du ihn versehentlich doppelt importieren, kann das zu Fehlern führen, deswegen nutze ich gerne require_once, da die Datei, falls sie bereits eingebunden wurde, nicht nochmal importiert wird und keine Fehler entstehen können.

require_once "vendor/autoload.php";

Fehler abfangen

Wenn im Produktivbetrieb dein Mailversand Probleme bereiten sollte, kannst du dem Nutzer keine Fehlermeldung ausgeben, da sich im Log auch teilweise sensible Daten von deinem Mailserver befinden könnten, deswegen empfehle ich dir deinen kompletten Mailversand in einen Try-Catch-Block zu packen oder zumindest die send() Funktion und die Ausgabe in eine Datei zu schreiben. Um diese Anleitung aber so simpel wie möglich zu halten, gebe ich diese nur aus.

require_once "vendor/autoload.php";

try {

    // neue instanz der klasse erstellen
    $mail = new PHPMailer\PHPMailer\PHPMailer(true);

} catch (PHPMailer\PHPMailer\Exception $e) {
        echo "Mailer Error: ".$mail->ErrorInfo; 
}

PHP Namespaces

Auch wenn im Beispiel vom PHPMailer die Namespaces mit use einfacher zugänglich gemacht wurden, sehe ich keinen großen Bedarf das auch bei mir zu machen, da wir die Namespaces sowieso nur je einmal brauchen. Wer das trotzdem machen möchte, kann mit use einmal alle Namespaces an den Anfang der Datei schreiben und braucht dafür dann nur den Namen nach dem letzten Slash zu schreiben. Zum Beispiel: PHPMailer\PHPMailer\PHPMailerClassPHPMailerClass

<?php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\Exception;

require_once "vendor/autoload.php";

// (...)

try {

    // neue instanz der klasse erstellen
    $mail = new PHPMailer(true); // namespace wurde bereits mit use definiert

// (...)

PHPMailer debuggen

Nun kannst du dem PHPMailer mitteilen, ob und welche Informationen er dir während des Versands ausgeben soll. Das macht im Debug-Mode Sinn oder falls du eine Version updatest und etwas nicht mehr richtig funktioniert. Im Konstruktor sagst du ihm, ob er eine Ausgabe machen darf oder nicht und definierst dann in der Klassenvariable SMTPDebug welche Informationen du haben möchtest.

Welche Fehler dir der PHPMailer noch anzeigen kann, kannst du hier nachlesen.

// neue instanz der klasse erstellen
$mail = new PHPMailer\PHPMailer\PHPMailer(true);

// gibt einen ausführlichen log aus
$mail->SMTPDebug = PHPMailer\PHPMailer\SMTP::DEBUG_SERVER;

/*
    Profi-Lösung
*/

$debug = true; // or
$debug = false;

// neue instanz der klasse erstellen
$mail = new PHPMailer\PHPMailer\PHPMailer($debug);

if ($debug) {
        // gibt einen ausführlichen log aus
        $mail->SMTPDebug = PHPMailer\PHPMailer\SMTP::DEBUG_SERVER; 
}

Authentifizieren mittels SMTP

Nun kommen wir zur Authentifizierung. Diese erfolgt im Normalfall über SMTP. Den folgenden Code kann man also in den meisten Fällen bedenkenlos kopieren:

// authentifiziere dich über den smtp-login
$mail->isSMTP();
$mail->SMTPAuth = true;

Nun musst du dem PHPMailer deine Daten anvertrauen, damit dein Mailserver dem PHPMailer vertraut. Du benötigst einmal die Adresse deines Mailservers, das Protokoll mit dem jeweiligen Port (SMTPS oder TLS/SSL) sowie deinen Nutzernamen und dein Passwort. Ich möchte dir an dieser Stelle keine Vorschriften machen welches Protokoll du zu verwenden hast. Schau einfach schnell selbst im Internet nach dem aktuellsten Protokoll. Die meisten Mailserverbetreiber haben dafür eine eigene Dokumentation.

Thema der Tabelle "Provider Dokumentationen"
Name Link zur Dokumentation
T-Online https://www.telekom.de/hilfe/festnetz-internet-tv/e-mail/e-mail-server-e-mail-protokolle-und-e-mail-einrichtung/imap/einrichtung-imap?samChecked=true
Gmail https://support.google.com/mail/answer/7126229?hl=de
GMX https://hilfe.gmx.net/pop-imap/pop3/serverdaten.html
Strato https://www.strato.de/faq/mail/externes-e-mail-programm-mit-strato-e-mail-adresse-nutzen/
1&1 https://hilfe-center.1und1.de/e-mail-c82645/1und1-e-mail-adresse-c84749/bedienung-c84680/e-mail-kontoeinstellungen-fuer-eine-1und1-e-mail-adresse-a783411.html
Web.de https://hilfe.web.de/pop-imap/pop3/serverdaten.html
// (...)
$mail->Host       = "smtp.deineDomain.de";
$mail->Port       = "587";
$mail->Username   = "deinNameHier@deineDomain.de";
$mail->Password   = "habsPasswortVergessen9934";
$mail->SMTPSecure = PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_STARTTLS;
$mail->SMTPSecure = PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_SMTPS;
// (...)

Zwei-Faktor-Authentifizierung

Falls du Gmail mit einer Zwei-Faktor-Authentifizierung nutzt, ist das sehr lobenswert (zumindest aus IT-Sicherheitsaspekten), aber dein Login wird scheitern. Wie du da vorgehen musst wird dir in diesem Artikel in der PHPMailer-Dokumentation nochmal genauer erklärt.

Selbst ausgestellte Zertifikate (Self-signed Certificate)

Solltest du z.B. Plesk verwenden (oder ähnliches), kann es der Fall sein, dass du dich trotz korrekter Login-Daten nicht einloggen kannst, weil das SSL-Zertifikat nicht auf deinen Emailserver ausgestellt ist. Das ist zwar ungünstig konfiguriert, du kannst es aber umgehen, indem du ihm sagst, du erlaubst selbstzertifizierte Verbindungen. Kontaktiere trotzdem deinen Webmaster und fordere ihn auf das Zertifikat auszustellen.

$mail->SMTPOptions = array(
    'ssl' => array(
          'verify_peer' => false,
          'verify_peer_name' => false,
          'allow_self_signed' => true
    ),
    'tls' => array(
        'verify_peer' => false,
        'verify_peer_name' => false,
        'allow_self_signed' => true
    )
);

Empfänger angeben

Als nächstes definieren wir einen Absendernamen und die Empfänger. Das solltest du bereits aus deinem Mailprogramm kennen. Als erstes kommt die Email-Adresse, danach kann optional noch ein Name angegeben werden, der dann statt der Adresse im Mailprogramm angezeigt wird. Das ist eine Schönheitsoptimierung und kann auch weggelassen werden.

$mail->setFrom('info@example.com', 'Information');
$mail->addAddress('info@example.com', 'Information');
$mail->addAddress('info@example.com');
$mail->addReplyTo('info@example.com', 'Information');
$mail->addCC('info@example.com');
$mail->addBCC('info@example.com', 'Information');

Dateien anhängen an eine Email

Möchtest du in deiner Email Bilder, Dokumente, Musik, Videos oder anderweitige Dateien versenden, hilft dir die addAttachment() Funktion. Diese liest den Inhalt der Datei aus, inkludiert diesen und benennt über den optionalen Parameter die Datei auch um (sehr empfehlenswert).

$mail->addAttachment("/home/webuser/Schreibtisch/Spiderman.png");
$mail->addAttachment("/home/webuser/Schreibtisch/Spiderman.png", "Spiderman.png");

Manchmal ist es sinnvoll auch ZIP-Archive anzuhängen, Felix hat dazu ein Artikel erstellt wie man ZIP-Archive in PHP erstellt: "Arbeiten in PHP mit ZIP-Archiven"

HTML-Inhalt und Fallback

Bevor wir nun unsere Email versenden, müssen wir uns aber noch um das wichtigste kümmern: Wir brauchen Inhalt! Dieser besteht normalerweise immer aus einer HTML-Version und einer, falls der Nutzer HTML-Mail deaktiviert hat oder Uraltsoftware verwendet, Textversion, die nochmal grob den Inhalt zusammenfasst.

WARNUNG!! Wenn du einen Webmailer oder ein anderes cooles neues Mailprogramm wie den Thunderbird benutzt, betrifft dich das jetzt weniger, aber die Kollegen der Microsoftfraktion haben mit Outlook noch ältere Internet Explorer Technologie und ihnen wird es die Seite zerschießen, wenn du responsives HTML5 (aktueller Standard) verwendest. Bist du auf MS-Kunden angewiesen (hauptsächlich Geschäftskunden), dann würde ich dein neues Emailtemplate mit MJML designen, eine spezielle, ähnlich wie HTML, auf XML basierende Containersprache, mit der dann HTML generiert wird. HTML sollte überall gut funktionieren.

$mail->isHTML(true);
$mail->Subject = '';
$mail->Body    = '';
$mail->AltBody = '';

Und Tschüss!

Wenn du nun alles andere so konfiguriert hast, dass es funktioniert, kannst du deine Email endlich wegschicken. Du hast es geschafft! Ich erinnere dich nochmals an den Try-Catch-Block!

// (...)
try {
    // (...)
    $mail->send();
} catch (PHPMailer\PHPMailer\Exception $e) {
    echo "Message could not be sent. Mailer Error: ".$mail->ErrorInfo; 
}

Beispielcode

Zum Abschluss möchte ich dir nochmal einen fertigen Code zeigen. Du darfst ihn gerne kopieren und bei dir einfügen.

$debug = true; // or
$debug = false;

require_once "vendor/autoload.php";

try {
        // neue instanz der klasse erstellen
        $mail = new PHPMailer\PHPMailer\PHPMailer($debug);

        if ($debug) {
                // gibt einen ausführlichen log aus
                $mail->SMTPDebug = PHPMailer\PHPMailer\SMTP::DEBUG_SERVER;
        }

        // authentifiziere dich über den smtp-login
        $mail->isSMTP();
        $mail->SMTPAuth = true;

        // login
        $mail->Host       = "smtp.deineDomain.de";
        $mail->Port       = "587";
        $mail->Username   = "deinNameHier@deineDomain.de";
        $mail->Password   = "habsPasswortVergessen9934";
        $mail->SMTPSecure = PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_STARTTLS;
        $mail->SMTPSecure = PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_SMTPS;
        $mail->SMTPOptions = array(
            'ssl' => array(
                  'verify_peer' => false,
                  'verify_peer_name' => false,
                  'allow_self_signed' => true
            ),
            'tls' => array(
                'verify_peer' => false,
                'verify_peer_name' => false,
                'allow_self_signed' => true
            )
        );

        $mail->addAttachment("/home/webuser/Schreibtisch/Spiderman.png", "Spiderman.png");

        $mail->isHTML(true);
        $mail->Subject = utf8_encode($Subject);
        $mail->Body    = utf8_encode($Html);
        $mail->AltBody = utf8_encode($Text);

        $mail->send();

} catch (PHPMailer\PHPMailer\Exception $e) {
    echo "Message could not be sent. Mailer Error: ".$mail->ErrorInfo; 
}

oder als simple Funktion:

<?php

$debug = true; // or
$debug = false; 

require_once "vendor/autoload.php";

if(sendEMail('mail@example.de', 'Your Name', 'Here is your Spiderman!', '<h1>Spiderman!</h1>', 'Spiderman!', array("Spiderman.png" => "/path/to/img.png"))) 
{ echo "\nPasst! Schau mal in dein Postfach Spiderman ist da!\n"; }
else
{ echo "\nERROR! Ein interner Fehler ist aufgetreten! Die E-Mail konnte nicht korrekt zugestellt werden\n"; }

function sendEMail($receiver,$receiverName,$subject,$html,$text,$AttmFiles=array()){
    global $debug;

    $mail = new PHPMailer\PHPMailer\PHPMailer($debug);

    if ($debug)
    { $mail->SMTPDebug = PHPMailer\PHPMailer\SMTP::DEBUG_SERVER; }

    $mail->isSMTP();
    $mail->SMTPAuth   = true;
    $mail->Host       = "example.de";
    $mail->Port       = "465";
    $mail->Username   = "mail@example.de";
    $mail->Password   = "deinpasswort";
    // $mail->SMTPSecure = PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_STARTTLS; // port 587
    $mail->SMTPSecure = PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_SMTPS; // port 465
    $mail->CharSet    = 'utf-8';
    $mail->Debugoutput = 'html';
    $mail->SMTPOptions = array(
      'ssl' => array(
          'verify_peer' => false
          ,'verify_peer_name' => false
          ,'allow_self_signed' => true
      ),
      'tls' => array(
        'verify_peer' => false
        ,'verify_peer_name' => false
        ,'allow_self_signed' => true
      )
    );

    $mail->setFrom("mail@example.de", 'Dein Name');
    $mail->addAddress($receiver, $receiverName);

    foreach($AttmFiles as $key => $value)
    { $mail->addAttachment($value, $key); }

    $mail->isHTML(true);
    $mail->Subject = $subject;
    $mail->Body    = $html;
    $mail->AltBody = $text;

    try {
        $mail->send();
        return true;
    } catch (PHPMailer\PHPMailer\Exception $e) {
        if($debug)
        { echo "Message could not be sent. Mailer Error: ".$mail->ErrorInfo; }
        return false;
    }
}
Bildquelle - Vielen Dank an die Ersteller:innen für dieses Bild
Kommentare zum Artikel
Alex schreibt ... Kommentar vom 21.08.2021
Frage(n)

Wirklich schönes Tutorial!

Kann man phpMailer auch für 2 verschiedene Emailadressen und Betreffs nutzen?

1x normales Kontaktformular und 1x als Angebotsanforderung wo nur die Emailadresse eingetagen wird und eine Email mit vorgefertigtem Text verschickt wird

Grüße Alex

Antworten
Alex
Antwort von Tim Riedl Kommentar vom 22.08.2021
Re: Frage(n)

Hi Alex,

danke für deinen Kommentar, natürlich kannst du auch verschiedene E-Mail-Adressen verwenden. Wobei du jetzt nicht unterscheidest, ob du die verschiedenen E-Mail-Adressen zum versenden oder empfangen verwenden möchtest. Als Empfänger kannst du die verschiedenen Adressen einfach untereinander angeben:

$phpmailer->addAddress('user1@hellocoding.de', '1. User');
$phpmailer->addAddress('user2@hellocoding.de', '2. User');

sollten es zwei verschiedene E-Mails werden, gibt es auch Funktionen, gesetzte Werte auf 0 zurückzusetzen. Nur weil du die E-Mail dann schonmal versendet hast, kannst die restlichen Daten auch nochmals zum senden verwenden. Alternativ kannst du aber auch allen eine blinde Kopie schicken und die Nutzer alle auf BCC setzen. Alle verfügbaren Funktionen vom PHPMailer, findest du in der Dokumentation https://phpmailer.github.io/PHPMailer/classes/PHPMailer-PHPMailer-PHPMailer.html#method_clearAddresses

$phpmailer->addAddress('user1@hellocoding.de', '1. User');

$phpmailer->send();

$phpmailer->clearAllRecipients();
$phpmailer->addAddress('user2@hellocoding.de', '2. User');
$phpmailer->send();

Den Absender kannst du ganz einfach überschreiben, indem du ihn einfach prozedural überschreibst

$phpmailer->send();

$phpmailer->Username   = "neu.mail@example.de";
$phpmailer->Password   = "deinanderespasswort";
$phpmailer->setFrom("neu.mail@example.de", 'Dein neuer Name');
$phpmailer->send();

Möchtest du E-Mails mit einem Template versenden, dann verwendet man hier bei größeren Projekten wie Laravel eine Template Engine dafür, der Text kann aber auch selbst generiert werden, sofern das Projekt keine super hohe Lesebarkeit aufweisen soll. Bedenke aber: Anders als bei Browsern verwenden gerade im Enterprise Umfeld noch viele veraltete E-Mail-Programme wie bspw Outlook 2013 oder 2007, die kein HTML 5 Standard verwenden. Da kannst du dir von MJML helfen lassen, und in einer HTML ähnlichen Sprache ein abwertskompatibles Template schreiben: https://mjml.io/

Das war jetzt sehr viel Input, ich hoffe aber, viele deiner möglicherweise aufkommenden Fragen beantwortet zu haben 😉

Grüße Tim

Tim Riedl
Christian schreibt ... Kommentar vom 07.02.2022
phpmailer formularfelder

Hallo toller Beitrag! Muss ich auch bei PHP Mailer meine Formularfelder escapen, also z.B. mit htmlspecialchars() damit kein schadcode eingeschleust werden kann? Was sollte man security mäßig noch beachten? Vielen Dank! Christian

Antworten
Christian
Antwort von Tim Riedl Kommentar vom 08.02.2022
RE: phpmailer formularfelder

Hallo Christian,

du musst halt drauf achten, dass der Nutzer dir keine dubiosen Links unterjubelt und auch kein JavaScript in der Mail enthalten ist, sonst könnte man ja deine IP loggen, dich auf Seiten weiterleiten mit zero-day vulnerabilities und dann auch ggf. deinen Browser knacken (je nachdem wie unbeliebt du dich bei dem Hacker machst und er Aufwand investiert 🙂 das ist jetzt mal ein Extremfall) Prinzipiell kann ich dir in einem größeren seriösen Umfeld empfehlen die Mail dann auch nochmal über eine Template Engine wie Blade oder Twig zu generieren, da wird sowas schon sehr gut von Haus aus gefiltert 😉

~ Tim

Tim Riedl
Kommentar schreiben

Vom Autor Empfohlen
close