Zend_Mail: wysyłanie poczty w Zend Framework

by Mateusz Tymek — on Zend Framework, PHP

Head's up! This post was written back in 2010 and is very likely to contain outdated information.

Zend_Mail: wysyłanie poczty z załącznikami w Zend Framework

Wysyłanie poczty w czystym PHP nigdy nie należało do przyjemności. Funkcja mail() może i sprawdza się gdy chcemy wysłać krótką wiadomość tekstową do jednego odbiorcy, jednak zawodzi w bardziej skomplikowanych zastosowaniach. Wyobraźmy sobie wysyłanie listu z dołączonym plikiem PDF i kilkoma obrazkami wplecionymi w treść. Bez jakiejś dodatkowej biblioteki musielibyśmy utworzyć całą wiadomość ręcznie (przykład). Na szczęście programiści Zenda przygotowali dosyć dobrą klasę którą możemy łatwo wykorzystać w naszej aplikacji: Zend_Mail. W tym tekście pokażę przykłady zastosowania tej klasy. Oprócz tego, zaprezentuję prosty sposób na testowanie wysyłania wiadomości przy pomocy własnego transportu.

Testowanie poczty

Wysyłanie e-maili nie zadziała jeśli w naszym systemie nie mamy poprawnie skonfigurowanego serwera poczty. Domyślnie pakiety typu WAMP Server, XAMPP czy Krasnal nie posiadają takiego serwera. Funkcja mail() po prostu nie wyśle poczty a Zend_Mail wygeneruje wyjątek. Zatem jak sprawdzić działanie aplikacji bez umieszczania jej na serwerze produkcyjnym? Wyjścia są co najmniej dwa. Można zainstalować i skonfigurować oprogramowanie wysyłające pocztę (sendmail, qmail...). Można też przygotować własny transport i wpiąć go do Zend_Mail. Drugi sposób jest łatwiejszy i bardziej praktyczny - mamy pełną kontrolę nad tym co stanie się z wysyłaną pocztą. W środowisku deweloperskim najlepiej po prostu zapisać wysyłane wiadomości do jakiegoś katalogu, tak aby można było sprawdzić je przy pomocy programu pocztowego. Poczta nie zostanie nigdzie wysłana, ale zobaczymy dokładnie to samo co użytkownik.
Oto przykładowy, używany przeze mnie transport:

class My_Mail_Transport_File extends Zend_Mail_Transport_Sendmail
{
    protected $_savePath;

    public function getSavePath()
    {
        return $this->_savePath;
    }

    public function setSavePath($path)
    {
        $this->_savePath = $path;
    }

    function _sendMail()
    {
        $fileName = time() . '.eml';
        $data = 'Subject: ' . $this->_mail->getSubject() . "\n"
              . 'To: ' . $this->recipients . "\n"
              . $this->header . "\n\n"
              . $this->body;
        file_put_contents($this->getSavePath() . '/' . $fileName, $data);
    }
}

Trzeba jeszcze powiedzieć klasie Zend_Mail aby domyślnie używała tego transportu w środowisku deweloperskim. Najlepiej zrobić to w bootstrapie. Określimy w nim katalog w którym zapisywane będą pliki .eml. Dodatkowo ustawimy domyślnego nadawcę wiadomości.

public function _initMail()
{
    if ('development' === APPLICATION_ENV) {
        $transport = new My_Mail_Transport_File();
        $transport->setSavePath(APPLICATION_PATH . '/../data/mail');
    } else {
        $transport = new Zend_Mail_Transport_Sendmail();
    }

    Zend_Mail::setDefaultTransport($transport);
    Zend_Mail::setDefaultFrom('[email protected]');
}

Dzięki takiej konfiguracji każda próba wysłania listu zaowocuje utworzeniem pliku z rozszerzeniem .eml, który może być odczytany przez dowolnego klienta poczty. W ten sposób możemy sprawdzić co tak naprawdę otrzymają użytkownicy projektowanego serwisu.
Polecam też otworzyć taki plik w zwykłym edytorze tekstu aby zobaczyć jak wygląda wewnętrzna struktura e-maila (i docenić ile pracy Zend_Mail wykonuje za nas).

Wysyłanie poczty

Zend_Mail udostępnia bardzo intuicyjny zestaw metod. Na wysłanie maila składa się kilka kroków: utworzenie obiektu tej klasy, ustawienie parametrów takich jak temat i lista odbiorców, a na końcu - wywołanie metody send():

$mail = new Zend_Mail('UTF-8');
$mail->setSubject('Test wiadomości e-mail')
      ->addTo('[email protected]')
      ->setBodyHtml('To jest list w formacie <strong>html</strong>');
      ->send();

Należy pamiętać aby zawsze tworzyć obiekty Zend_Mail z parametrem UTF-8. Domyślne kodowanie to ISO-8859-1, bez którego odbiorca nie zobaczy poprawnie liter z polskimi ogonkami.

Załączniki

Dodawanie załączników w formie plików dołączonych do listu zostało dobrze opisane w dokumentacji Zenda. Prosty przykład z tamtej strony:

$mail = new Zend_Mail('UTF-8');

$at = $mail->createAttachment($myImage);
$at->type        = 'image/gif';
$at->encoding    = Zend_Mime::ENCODING_8BIT;
$at->filename    = 'test.gif';

$mail->send();

Wstawienie obrazka gdzieś w treści wiadomości wymaga trochę więcej pracy. Takie załączniki osadzane są według schematu Content-ID, opisanego w RFC2111. Schemat ten wymaga przyporządkowania wybranym załącznikom nagłówka Content-ID (cid), o unikalnej wartości. Dzięki niej będzie można odnieść się do załącznika w innej części wiadomości. Cała wiadomość musi mieć typ (nagłówek Content-Type) ustawiony na multipart/related - aby wskazać, że zawiera powiązane ze sobą elementy.
Wartość cid może być w zasadzie dowolna. Może to być na przykład suma MD5 obliczona z całego załącznika.
Aby odwołać się do takiego zasobu, należy posłużyć się URL-em w postaci cid:IDENTYFIKATOR.

Załóżmy że chcemy wstawić logo strony gdzieś w środku treści emaila:

$logo = file_get_contents(APPLICATION_PATH . '/../public/images/logo.png');

$at = new Zend_Mime_Part($logo);

$at->type = 'image/png';
$at->encoding = Zend_Mime::ENCODING_BASE64;
$at->disposition = Zend_Mime::DISPOSITION_INLINE;
$at->filename = 'logo.png';
$at->id = md5($logo);

$bodyHtml = <<<BODY
Obrazek:<br />
<img src="cid:{$at->id}" alt="Logo" /><br />
To jest treść w formacie <strong>HTML</strong>
BODY;

$mail = new Zend_Mail('UTF-8');
$mail->setType(Zend_Mime::MULTIPART_RELATED)
     ->addAttachment($at)
     ->setSubject('Test wiadomo?ci e-mail')
     ->addTo('[email protected]')
     ->setBodyHtml($bodyHtml);

$mail->send();

Po otwarciu utworzonego pliku eml powinniśmy zobaczyć załącznik między dwoma linijkami tekstu.

Podsumowanie

Przedstawione tutaj przykłady można uznać za wstęp do bardzo szerokiego tematu jakim jest tworzenie listów i wysyłanie poczty elektronicznej. Można by pokusić się o rozbudowanie klasy Zend_Mail tak aby sama generowała odpowiednie identyfikatory CID dla dołączanych zasobów (przykład takiej klasy znajduje się w jednej ze stron poniżej).

Inne zagadnienia o których warto poczytać to między innymi:

  • Jak zbudować wiadomość w formacie HTML, aby wyglądała podobnie niezależnie od programu pocztowego?
  • Jak rozesłać newsletter do większej liczby odbiorców (np 10 000)?
  • Jak sprawić aby taki newsletter nie został uznany za spam?

Więcej informacji

Polecam również książkę "Skalowalne witryny internetowe" (wyd. Helion), w której jeden z rozdziałów poświęcony jest poczcie elektronicznej.


comments powered by Disqus