Wysyłanie e-maile ze skryptu PHP

Adam Boduch

Obecnie funkcja wysyłania e-maili jest używana praktycznie w większości większych serwisów opartych na systemie dynamicznego generowania stron. W tym artykule opiszemy w jaki sposób posługiwać się funkcją mail() standardowo dostępną w PHP jak i bardziej zaawansowanym systemem - mianowicie gniazdkami.

Funkcja mail()

Wysyłanie e-maili z poziomu PHP jest niesamowicie proste. Wystarczy bowiem użyć wbudowanej funkcji mail(), która upraszcza całą sprawę. Oto sposób jej użycia:

bool mail ( string to, string subject, string message [, string additional_headers [, string additional_parameters]])

Oznaczenie parametrów:

  • $to - adres e-mail odbiorcy e-maila
  • $subject - temat wiadomości
  • $message - treść wiadomości
  • $additional_headers - dodatkowe nagłówki (parametr opcjonalny)
  • $additional_parameters - dodatkowe parametry przekazywane do SendMaila (parametr opcjonalny)

Przykładowe użycie funkcji mail() wygląda tak:

mail('mail@serwer.com', 'Temat', 'Treść');

Jest to najprostsze z możliwych wywołań tej funkcji. Spowoduje wysłanie e-maila o treści "Treść" z tematem "Temat" pod adres "mail@serwer.com".

Co do nagłówków: aby dokładnie zrozumieć zasadę dodawnia nagłówków, należy zapoznać się ze specyfikacją wysyłania e-maili np. tutaj: http://www.faqs.org/rfcs/rfc1896.

Przykładowo: możesz dodać nagłówek "From", który oznacza nadawce listu.

mail('mail@serwer.com', 'Temat', 'Treść',  
          'From: Adam Boduch <adam@boduch.net>\r\n
           Reply-To: Adam Boduch <adam@boduch.net>\r\n');

W powyższej instrukcji do e-maila zostaną dołączone nagłówki 'From' oraz 'Reply-To' (adres zwrotny). Nie zaszkodzi także ustawić nagłówka 'Return-Path' czyli adresu na który zostanie odesłany list w razie, gdy np. skrzynka odbiorcy nie istnieje.

Uwaga! Poszczególne nagłówki e-maili muszą być oddzielone od siebie znakami \r\n (Czyli CRLF, Carrige-Return oraz Line-Feed). Aczkolwiek na systemach typu Unix może działać także, gdy nagłówki będą oddzielone znakiem \n.

Używanie gniazdek

Na wielu serwerach funkcja mail() jest blokowana przez administratorów. Z różnych względów - np. ze względu na obciązania serwera, w przypadku wysyłania dużej ilości listów lub możliwość rozsyłania spamu. Dobrym sposobem jest wykorzystanie gniazdek w celu przesłania listu. Chociaż jest to metoda bardziej czasochłonna, ale pozwala na połączenie się z dowolnym serwerem i wysłanie z tego serwera listu. W artykule przedstawimy prostą klasę, która umożliwi połączenie się z serwerem i wysłania e-maila.

Aby wysłać e-maila z konkretnego serwera należy się z nim połączyć (najczęściej na porcie 25) oraz przekazać mu zrozumiałe dla niego komendy - np.

HELO mail.swerer.pl\r\n
MAIL FROM: <adres@serwer.pl>\r\n
RCPT TO: <odbiorca@serwer.pl>\r\n
DATA\r\n
Subject: Temat\r\n
From: Adam Boduchrn\r\n
Wiadomość\r\n
.\r\n
QUIT\r\n

Dla przykładu powyższe instrukcje umożliwią wysłanie listu do "odbiorca@sewer.pl".

Ale przejdźmy do rzeczy: połączenie z serwerem może odbyć się za pomocą funkcji fsockopen().

/* otwarcie gniazdka (laczenie sie z serwerem poczty) */
$this->socket = fsockopen($this->host, 25, $errno, $error, 30);
if (!$this->socket)
{
    Error("$error ($errno)");
}

Pierwszym parametrem tej funkcji musi być nazwa hosta; drugim port. Trzeci i czwarty parametr to opis błędu oraz numer błędu. Ostatni parametr to $timeout (ilość sekund przez jakie skrypt bedzie próbował połączyćsię z serwerem).

Wysyłanie komendy odybwa się za pośrednictwem funkcji fwrite():

fwrite($this->socket, $var_command)

Pierwszym parametrem musi być uchwyt do gniazdka (uzyskany jako rezultat działania funkcji fsockopen()). Drugi parametr to treść wysyłanej komendy.

Równie prosto można odczytać tekst, który został zwrócony przez serwer (funkcja fgets()).

fgets($this->socket, 100);

Pierwszym parametrem musi być oczywiście uchwyt, a drugim ilość znaków jakie zostaną odczytane. Funkcja fgets() zwraca łańcuch (string), który zawiera treść zwróconą przez serwer. To chyba wszystko co trzeba wiedzieć na temat łączenia się z serwerem i wysyłaniem do niego komend. Nie należy oczywiście zapominać o tym, aby na końcu zamknąć połączenie (funkcja fclose()).

Klasa Mail

Na podstawie tego, co napisaliśmy można napisać klasę, która uprości cały proces przesyłania wiadomości. Kod źródłowy całego skryptu przedstawia się następująco:

<?php
  
   function Error($msg)
   {
   /* funkcja wyswietla komunikat bledu pocztym konczy dzialanie programu. Wlasciwie stanowi niejako alias do funkcji die() */
        echo $msg;
        exit;
   }

   error_reporting  (E_ERROR | E_WARNING | E_PARSE); // wyswietlaj bledy
   set_time_limit(30); // time-out na 30 sek.

   class Mail
   {

       var $host; // host, z ktorym bedziemy sie laczyc
       var $socket; // uchwyt

       var $mail_subject; // temat listu
       var $mail_sender; // nadawca
       var $mail_recipient; // odbiorca
       var $mail_content; // treść

       var $log; // log

       function Mail($server_host)
       {
           if (empty($server_host))
           {
                Error('Nie wpisałeś hosta, z którym mam się połączyć');
           }

           $this->host = $server_host;
       }

       function connect()
       {
          /* otwarcie gniazdka (laczenie sie z serwerem poczty) */
           $this->socket = fsockopen($this->host, 25, $errno, $error, 30);
           if (!$this->socket)
           {
               Error("$error ($errno)");
           }

           /* wyslanie tekstu przywitalnego */
           $this->send_cmd("HELO " . $this->host . "\r\n");
       }
       
       function disconnect()
       {
       /* rozlaczenie z serwerem; przed tym nalezy jednak wyslac polecenie QUIT */
           $this->send_cmd("QUIT\r\n");
           fclose($this->socket);
       }
       
       function send_cmd($var_command)
       {
       /* funkcja wysyla do gniazdka polecenie z parametru $var_command oraz odczytuje odpowiedz
          z serwera i przypisuja ja do zmiennej $log */
           if ( fwrite($this->socket, $var_command) )
           {
               $this->log .= fgets($this->socket, 100) . '<br>';
           }
       }

       
       function set_mail_sender($var_sender)
       {
           $this->mail_sender = $var_sender;
       }
       
       function set_mail_recipient($var_recipient)
       {
           $this->mail_recipient = $var_recipient;
       }
       
       function set_mail_subject($var_subject)
       {
           $this->mail_subject = $var_subject;
       }
       
       function set_mail_content($var_content)
       {
           $this->mail_content = $var_content;
       }
       
       function send_mail()
       {
           if (empty($this->mail_recipient) ||
               empty($this->mail_sender)) Error('Nie wpisałeś nadawcy lub odbiorcy e-maila!');
               
           /* nadawca wiadomosci */
           $this->send_cmd("MAIL FROM: <" . $this->mail_sender . ">\r\n");
           
           /* odbiorca wiadomosci */
           $this->send_cmd("RCPT TO: <" . $this->mail_recipient . ">\r\n"); 
           
           /*  informuje, ze zaraz rozpoczniemy wysylanie danych */
           $this->send_cmd("DATA\r\n");
           
           /* temat wiadomosci */
           $this->send_cmd("Subject: " . $this->mail_subject . "\r\n");
           
           /* kodowanie */
           $this->send_cmd("Content-type: text/html; charset=iso-8859-2\r\n");
           
           /* naglowki */
           $this->send_cmd("From: " . $this->mail_sender . "\r\n");
           $this->send_cmd("Reply-To: " . $this->mail_sender . "\r\n");
           $this->send_cmd("Return-Path: " . $this->mail_sender . "\r\n\r\n");
           
           /* tresc wiadomosci */
           $this->send_cmd($this->mail_content . "\r\n");
           $this->send_cmd(".\r\n"); // koniec wiadomosci

       }

   }

   $mail = new Mail('mail.pf.pl');
   $mail->set_mail_sender('bald@pf.pl');
   $mail->set_mail_recipient('root@localhost.localdomain');
   $mail->set_mail_subject('Testowy temat');
   $mail->set_mail_content('Jakas tresc');
   $mail->connect();
   $mail->send_mail();
   $mail->disconnect();

   echo $mail->log;
?>

Na samym końcu następuje skorzystanie z tej klasy oraz z serwera mail.pf.pl do wysłania listu. Na końcu powinien zostać wyświetlony log z działania klasy (log sporządzony na podstawie komunikatów zwracanych przez serwer).

5 komentarzy

Ta "dopracowana" klasa PHPmailer, to z punktu widzenia obiektowego nędza. Pola w większości publiczne, weryfikacja wprowadzonych danych dopiero w metodzie send (czyli najpóźniej jak się da, albo żadna), praktycznie żadnych możliwości odchudzenia klasy z niepotrzebnego w wielu zastosowaniach kodu... 65 KB.
Fakt, że technicznie wyczerpuje specyfikację, ale to jedyne co można o niej powiedzieć. Może poza tym, że tak jak sam PHP jest ona sklejką niezbyt powiązanych ze sobą metod różnych autorów.

Czy musze miec u siebie na kompie serwer SMTP zeby korzystac z funkcji mail w podstawowej wersji? np Microsoft Exchange?

Lepiej skorzystać z napisanej już przez kogoś dopracowanej klasy PHPmailer :P Załącznki dodaje się poprzez metodę Attachment.

Musisz zaznajomić się ze znaczniem nagłówków e-mailach . Póżniej należy jedynie dodać odpowiedni nagłówek.

Informacje: [url]http://www.faqs.org/rfcs/rfc1896[/url]
[url]http://www.zend.com/zend/spotlight/sendmimeemailpart1.php[/url]

Jak wysłać maila z załącznikiem?