PHP Blog von PHPGangsta

Archive for the ‘Zend_Mail’ tag

Inline Grafiken in HTML E-Mails mit Zend_Mail automatisch versenden

with one comment

Ich (Ralf Eggert) stand neulich vor dem Problem, dass ich alle Grafiken in einer HTML E-Mail automatisch als Inline-Images identifizieren und dann an die E-Mail anhängen wollte. Die Grundidee zur Lösung des Problems ist es, mit einem regulären Ausdruck alle Grafiken zu finden und dann entsprechend anzuhängen. Beim Versand gab es ein paar Probleme, die ich zusammen mit Michaels Hilfe lösen konnte. Und genau deshalb findet ihr hier nun diesen kurzen Gastbeitrag.

Der Code unten zeigt ein komplettes lauffähiges Beispiel. Nach der Konfiguration wird auch gleich ein Zend_Mail Objekt erstellt und der HTML Bereich mit Inhalten bestückt. Über preg_match_all und array_filter werden alle Fundstellen in IMG Tags, im BACKGROUND Attribut sowie im Inline-CSS gefunden. Das Array mit den Ergebnissen wird dann durchlaufen. Abhängig davon, ob es sich um Grafiken auf dem Server oder externe Grafiken handelt, wird dann das entsprechende Attachment erstellt.
Weiterlesen »

Written by Ralf Eggert

Mai 6th, 2010 at 9:33 am

IMAP-Emails lesen mit dem Zend Framework

with 4 comments

Emails werden nicht nur von Menschen für Menschen geschrieben, sie werden auch häufig zur asynchronen Kommunikation zwischen Rechnern und Programmen eingesetzt.

In der Vergangenheit habe ich bereits einige Scripte geschrieben die Emails periodisch abholen und bearbeiten. Darunter waren zum Beispiel Backup-Benachrichtigungen von einem Programm das früher nicht in der Lage war, die Ergebnisse in eine Datenbank zu schreiben. Um eine Übersicht über einen Zeitraum zu erhalten muß man die Ergebnisse also per Email in ein Postfach senden lassen und dieses periodisch abrufen und die Emails parsen.

Ein anderes Script hat zum Beispiel ein Postfach nach Anhängen durchsucht. Dieses Postfach wurde dazu benutzt, Dokumente automatisiert verarbeiten zu lassen. Dazu wurden die Emails geparst, die Anhänge gelöst und dann je nach Absender an einen bestimmten Sachbearbeiter gesendet oder an ein automatisiertes System weitergeleitet bzw. auf ein Netzwerklaufwerk abgelegt.

Es gibt noch viele weitere Möglichkeiten, wofür Emails nützlich sein können und warum man sie automatisiert verarbeiten sollte.

Früher habe ich diese Arbeit mit den imap_* Funktionen erledigt. Heutzutage nutze ich dafür natürlich die Zend-Framework Klassen Zend_Mail_Storage_Imap und Zend_Mail_Message. Der Zugriff via IMAP ist der POP3-Möglichkeit vorzuziehen, da wir dann z.B. Flags oder Ordner nutzen können um Mails zu markieren/verschieben die gerade in Progress sind bzw. fertig bearbeitet wurden.

Um eine Verbindung zum Mailserver aufzubauen nutzen wir einfach den Konstruktor:

$storage = new Zend_Mail_Storage_Imap(array(
    'host'     => 'imap.firma.de',
    'user'     => 'postfach@firma.de',
    'password' => 'passworthier'));

Eine verschlüsselte SSL oder TLS Verbindung ist natürlich auch möglich (hier SSL):

$storage = new Zend_Mail_Storage_Imap(array(
    'host'     => 'imap.firma.de',
    'user'     => 'postfach@firma.de',
    'password' => 'passworthier',
    'ssl'      => 'SSL'));

Nachdem die Verbindungsparameter spezifiziert wurden kann man nun auf den Server zugreifen. Als erstes möchten wir uns die Gesamtanzahl der Nachrichten im aktuellen Ordner anzeigen lassen:

echo $storage->countMessages() . ' Nachrichten im Ordner';

Mit Hilfe einer foreach-Schleife können wir nun alle Emails durchlaufen. Das ist möglich da das Storage-Object das Iterator-Pattern implementiert:

foreach ($storage as $messageNum => $message) {
    echo 'Email von '.$message->from.': '.$message->subject;
}

Wie man oben sehen kann werden die Header der Email einfach wie ein public Attribut abgefragt. Das wird intern über die magische __get() Funktion ermöglicht. Man kann aber auch die getHeader() Funktion nutzen, die gerade im Fall von “besonderen” Headern wie “Reply-To” einfacher einzusetzen ist ($message->reply-to wäre ungültig).

Um auf den Inhalt der Email zugreifen zu können nutzt man die getContent()-Funktion:

echo $message->getContent();

Da Emails aus mehreren Teilen bestehen können (Textteil, HTML-Teil, Anhänge, Inline-Bilder usw) kann man diese Teile auch untersuchen:

$foundPart = null;
foreach (new RecursiveIteratorIterator($message) as $part) {
    try {
        if (strtok($part->contentType, ';') == 'text/plain') {
            $foundPart = $part;
            break;
        }
    } catch (Zend_Mail_Exception $e) {
        // ignorieren
    }
}
if (!$foundPart) {
    echo 'kein reiner Text-Teil gefunden';
} else {
    echo "Reiner Text-Teil: \n" . $foundPart;
}

Auf IMAP-Servern können wir wie bereits erwähnt Emails mit Flags versehen und abfragen.

if ($message->hasFlag(Zend_Mail_Storage::FLAG_FLAGGED)) {
        continue;
}

Damit würden wie alle Emails überspringen die geflaggt sind. Flags können wie gesagt dafür genutzt werden um bereits bearbeitete Emails zu markieren.

$storage->setFlags($messageNum, array(Zend_Mail_Storage::FLAG_FLAGGED));

Man kann natürlich auch auf einzelne Emails zugreifen:

$message = $storage->getMessage($messageNum);

Dabei sollte man jedoch daran denken dass $messageNum die Sequenznummer ist. Wenn eine Email heute die Sequenznummer 3 hat, kann sie morgen schon die Sequenznummer 2 haben wenn Email 1 oder 2 in der Zwischenzeit gelöscht wurde.

Emails löschen ist auch sehr einfach:

$storage->removeMessage($messageNum);

Wie bereits geschrieben kann es dabei zu Verschiebungen der Sequenznummern kommen, es ist also Vorsicht geboten.

Die Zend-Framework-Klassen sind für die meisten einfachen Aufgaben sehr gut brauchbar. Wenn man allerdings erweiterte Funktionalitäten benötigt muß man die Klasse erweitern. Um einigen Problemen aus dem Weg zu gehen habe ich beispielsweise alle Funktionen erweitert, sodass ich mit UniqueIDs arbeiten kann statt Sequenznummern. Desweiteren sind folgende Funktionen dazugekommen die man des öfteren mal benötigt (für die meisten davon gibt es bereits Issue-Tracker-Einträge, um sie in das Framework aufzunehmen):

sort() Methode: http://framework.zend.com/issues/browse/ZF-9138
search() Methode: http://framework.zend.com/issues/browse/ZF-8858
Kopieren mehrere Emails mit einem IMAP-Befehl (nicht copy() in einer Schleife aufrufen): http://framework.zend.com/issues/browse/ZF-8513
Flags auf mehrere Emails gleichzeitig setzen (anstatt setFlags() in einer Schleife aufrufen zu müssen): http://framework.zend.com/issues/browse/ZF-8488
Mehr Kontrolle über den EXPUNGE Befehl: http://framework.zend.com/issues/browse/ZF-5655

Written by Michael Kliewe

März 12th, 2010 at 9:34 am

Posted in PHP

Tagged with , ,