Wysyłanie maili z PHP przy użyciu Zend Mail

Artykuł dodany: 02 czerwca 2016.

Stopień trudności (1 - dla początkujących, 5 - dla ekspertów): 2

Wysyłka maili to, oprócz zapisu do bazy danych, jedna z najczęściej wykorzystywanych funkcji w PHP. Przy okazji sprawia programistom sporo kłopotów z racji skomplikowanej specyfikacji RFC. Trzeba pamiętać o wielu obostrzeniach, poprawnym formatowaniu nagłówków, zabezpieczaniu danych. Ogromna ilość spamu krążącego po sieci powoduje powstawanie coraz to bardziej skomplikowanych filtrów oraz czarnych list które dodatkowo mogą blokować odbieranie maili. Co więcej, użytkownicy systemu Windows nie mają domyślnie zainstalowanego żadnego serwera pocztowego, stąd nie ma co marzyć o doręczeniu wiadomości bezpośrednio z PHP. Aby ułatwić i tak trudny proces, powstają w PHP biblioteki pośredniczące a jedną z nich jest Zend Mail.

Wstępna konfiguracja

Projekt zakłada następującą strukturę plików:

|-phpsettings.php   - plik ustawień
|-mail.php  - logika, wysyłanie wiadomości
|-mail.html - formularz kontaktowy
|-composer.json - plik konfiguracyjny Composera

Sam framework Zenda oraz ogólną ideę wykorzystania poszczególnych komponentów omawiałem przy okazji poprzedniego artykułu. Zend Mail najłatwiej jest dodać przy pomocy systemu Composer, jednak oczywiście możemy pliki pobrać jako spakowane archiwum i wypakować je w katalogu naszego projektu. W tej drugiej sytuacji będziemy musieli na własną rękę zadbać o konfigurację autoloadera.

composer require zendframework/zend-mail

Pamiętajmy również iż nasze środowisko pracy powinno być wcześniej odpowiednio skonfigurowane, aby zminimalizować ryzyko wystąpienia błędów oraz uniezależnić się od konfiguracji danego serwera (patrz poprzedni artykuł).

phpsettings.php

<?php
$env = APPLICATION_ENV ?: 'development';

if ('production' === $env) {
    error_reporting(0);
    ini_set('display_errors', 'off');
    ini_set('display_startup_errors', false);
} else {
    error_reporting(-1);
    ini_set('display_errors', 'on');
    ini_set('display_startup_errors', true);
}

ini_set('date.timezone', 'Europe/Warsaw');
setlocale(LC_ALL, 'pl_PL.UTF8');

ini_set('mbstring.language', 'uni');

ini_set('default_charset', 'UTF-8');
ini_set('input_encoding', 'UTF-8');
ini_set('output_encoding', 'UTF-8');

Przygotowanie formularza po stronie HTML

Oprócz konfiguracji potrzebny nam będzie oczywiście formularz w HTML. Wszystkie pliki o których mowa w artykule najlepiej aby były zapisywane z kodowaniem UTF-8 – jako obecnym standardem obsługi znaków na stronach www.

mail.html

<form action="mail.php" method="POST" class="mail_form">
    <fieldset>
        <legend>Formularz kontaktowy</legend>
        <div>
            <label for="mail_username">Imię i nazwisko</label>
            <input id="mail_username" type="text" name="mail_username" required />
        </div>
        <div>
            <label for="mail_email">Adres e-mail</label>
            <input id="mail_email" type="email" name="mail_email" required />
        </div>
        <div>
            <label for="mail_message">Wiadomość</label>
            <textarea name="mail_message" id="mail_message" cols="30" rows="10"></textarea>
        </div>
    </fieldset>
    <input type="submit" name="mail_send" value="Wyślij wiadomość" />
</form>

W naszym przypadku formularz zawiera 3 podstawowe pola: “Imię i nazwisko”, “Adres e-mail” oraz “Wiadomość”. Zawartość formularza będzie wysyłana do pliku PHP: mail.php gdzie nastąpi wysyłka maila lub wyświetlenie komunikatów błędów / powodzenia.

Obsługa formularza po stronie PHP

Należy sobie uświadomić że maile możemy wysyłać na dwa sposoby. Za pomocą serwera pocztowego wbudowanego (sendmail, dostępnego na maszynie na której uruchamiamy skrypty PHP) oraz z wykorzystaniem protokołu SMTP (czyli przez serwer zewnętrzny typu Gmail, Outlook, WP, Interia itd). Użytkownicy systemu Windows domyślnie nie mają w systemie żadnego serwera pocztowego stąd, bez instalacji dodatkowych programów, będą w stanie wysyłać wiadomości tylko poprzez SMTP.

Możemy teraz dokończyć wstępną konfigurację systemu przez dodanie kodu:

mail.php

<?php

use Zend\Mail;

defined('APPLICATION_ENV') || define('APPLICATION_ENV', 'development');

chdir(dirname(__DIR__));

// Wczytujemy wcześniej zdefiniowany plik ustawień
require 'phpsettings.php';
// Oraz pliki Composera
require 'vendor/autoload.php';

// odczytujemy metodę dostępu do strony
$method = $_SERVER['REQUEST_METHOD'];

// Sprawdzamy czy formularz został wysłany metodą POST przez input name="mail_send"
if ('POST' === $method && isset($_POST['mail_send'])) {
    // kod odpowiedzialny za wysłanie wiadomości
}

W środowisku produkcyjnym powinniśmy ukryć wyświetlanie błędów poprzez zamianę ‘development’ na ‘production’. Moglibyśmy także dodać sekcję `else` a w niej przykładowo komunikat “dostęp zabroniony”. Bardzo ważne jest sprawdzenie poprawności danych wprowadzonych przez użytkownika. Klasa Zend Mail w swoich zależnościach automatycznie importuje komponenty Zend Validator, stąd w kodzie naszego mikro programu moglibyśmy też dokonać walidacji danych:

// Przykładowo dla adresu e-mail:
$validator = new Zend\Validator\EmailAddress();

if ($validator->isValid($_POST['mail_email'])) {
    // email poprawny
} else {
    // email niepoprawny, wyświetl błędy
    foreach ($validator->getMessages() as $message) {
        echo "$message\n";
    }
}

Nie będę tego robił gdyż nie jest to tematem tego artykułu oraz każdy z osobna może potrzebować innych pól w formularzu.

Wysłanie wiadomości przez sendmail

Korzystanie z klasy Zend Mail sprowadza się do ustawienia danych podstawowych dla wiadomości takich jak: adres odbiorcy, temat oraz ciało wiadomości. Następnie należy wybrać metodę transportu. Sendmail to nic innego jak wrapper do wbudowanej w PHP funkcji mail. Komponent Zend Mail zadba o odpowiednie przygotowanie danych, ustawienie nagłówków, zabezpieczenie przed popularnymi typami ataków.

Nasz kod obsługi wiadomości może przyjąć postać:

if ('POST' === $method && isset($_POST['mail_send'])) {
    try {
        $mail = new Mail\Message();
        $mail->setEncoding('UTF-8');
        $mail->setBody($_POST['mail_message']);
        $mail->setFrom($_POST['mail_email'], $_POST['mail_username']);
        $mail->addTo('testowy@naszadresemail', 'Testowy odbiorca');
        $mail->setSubject('Wiadomość wysłana za pomocą formularza kontaktowego');

        $transport = new Mail\Transport\Sendmail();
        $transport->send($mail);
        echo 'Wiadomość została wysłana';
    } catch (\Exception $e) {
        echo $e->getMessage();
    }
}

W przypadku wystąpienia błędu na ekranie zostanie wyświetlony komunikat wyjątku. Może się zdarzyć że wiadomość została poprawnie wysłana nie dotarła jednak do odbiorcy. Oznacza to iż najprawdopodobniej serwer docelowy odrzucił połączenie. Przyczyną może być błędna konfiguracja po stronie usługodawcy, zbanowany adres IP albo nawet tymczasowe problemy z serwerem docelowym.

Wysłanie wiadomości przez SMTP

Wysyłanie wiadomości przez SMTP oferuje znacznie większy zakres możliwości konfiguracyjnych. Jest też większe prawdopodobieństwo doręczenia maila. Serwery dużych firm są raczej poprawnie skonfigurowane, administratorzy dbają o odpowiednie wpisy w DNS czy zabezpieczenia. Minusem mogą być godzinowe limity wiadomości tak skonfigurowane, aby nie wysyłać zbyt dużych porcji maili na raz.

W artykule posłużę się przykładową konfiguracją serwera Wirtualnej Polski.

Konfiguracja po stronie Zend Mail obejmuje: podanie hosta SMTP, portu na którym ma być nawiązane połączenie, klasy połączenia czyli metody uwierzytelniania (plain, login, crammd5), metody połączenia (nieszyfrowane, TLS, SSL) oraz oczywiście loginu i hasła użytkownika.

Połączenie SMTP wymaga do poprawnego działania Zend ServiceManager dlatego musimy jeszcze dodać:

composer require zendframework/zend-servicemanager
if ('POST' === $method && isset($_POST['mail_send'])) {
    try {
        $mail = new Mail\Message();
        $mail->setEncoding('UTF-8');
        $mail->setBody($_POST['mail_message']);
        $mail->setFrom('naszlogin@wp.pl');
        $mail->setReplyTo($_POST['mail_email'], $_POST['mail_username']);
        $mail->addTo('testowy@naszadresemail', 'Testowy odbiorca');
        $mail->setSubject('Wiadomość wysłana za pomocą formularza kontaktowego');

        $transport = new Mail\Transport\Smtp();
        $options   = new Mail\Transport\SmtpOptions([
            'host' => 'smtp.wp.pl',
            'port' => 465,
            'connection_class'  => 'login',
            'connection_config' => [
                'username' => 'login lub email',
                'password' => 'hasło',
                'ssl'      => 'ssl',
            ],
        ]);

        $transport->setOptions($options);
        $transport->send($mail);

        echo 'Wiadomość została wysłana';
    } catch (\Exception $e) {
        echo $e->getMessage();
    }
}

W porównaniu do transportu sendmail należało zmienić nadawcę wiadomości, na adres mailowy z którego wiadomość jest wysyłana. Dla dociekliwych polecam przejrzeć kod poszczególnych klas aby zobaczyć ile pracy należało włożyć w stworzenie poprawnie działającej wysyłki SMTP.

Podsumowanie

Przedstawione w artykule temat to zaledwie początek. Klasa oferuje wiele innych możliwości np. dołączanie własnych nagłówków, wysyłanie załączników. Za jej pomocą można operować zdalnie na skrzynce pocztowej – kasować wiadomości, pobierać informacje o flagach, liczyć maile w skrzynce. Znacząco ułatwia obsługę wiadomości pocztowych ale też zmniejsza ryzyko popełnienia błędu.

Komentarze

Nie ma jeszcze żadnych komentarzy do wyświetlenia. Może chcesz zostać pierwszą osobą która podzieli się swoją opinią?

Dodaj komentarz

*
Nazwa zostanie wyświetlona wraz z komentarzem. Możesz też utworzyć nowe konto w serwisie, dzięki czemu uzyskasz dodatkową funkcjonalność.
*
Akceptowana jest ograniczona składnia Textile. Wszystkie tagi HTML zostaną usunięte.