Dodajemy formularz kontaktowy 3#

Walidacja PHP + Swiftmailer


W tym poście opisuję walidację danych z formularza (przesłanych przez AJAX) po stronie serwera (PHP) oraz tworzenie wiadomości, ustawienie połączenia z serwerem pocztowym oraz wreszcie wysłanie wiadomości.

Jeśli trafiłeś tu bezpośrednio, zajrzyj do pierwszego postu, gdzie opisuję założenia i tworzę podstawową strukturę formularza.

Wpisy w tej serii:
  1. HTML + otwieranie / zamykanie JQuery
  2. Walidacja HTML5/JS + AJAX
  3. Walidacja PHP + Swiftmailer
  4. Dostępność formularza
  5. Zrefaktoryzowana wersja + Github

W kolejnym poście zajmiemy się poprawą dostępności formularza.

Swiftmailer

Co to takiego?

Swiftmailer to biblioteka do wysyłania maili napisana w PHP5.

Dlaczego nie wbudowany w PHP mail()?

Funkcja mail() jest już przestarzała i stosowanie jej nie należy do dobrych praktyk. Ponadto maile wysyłane za jego pomocą często trafiają do spamu. Dlatego powinniśmy używać nowszych rozwiązań, jak np. Swiftmailer, które oferują dużo większe możliwości i są bardziej bezpieczne.

Instalacja Swiftmailer-a

Pobieramy paczkę z gita i wrzucamy do folderu z naszą stroną. Tworzymy plik PHP, który będzie obsługiwał wysyłanie maili, w moim przypadku emailform.php, i załączamy pobraną paczkę.

<?php
  require_once 'swiftmailer/lib/swift_required.php';
  //dalszy kod

?>

I gotowe. Możemy korzystać ze Swiftmailera. Przygotujmy sobie jeszcze oddzielny plik konfiguracyjny z wrażliwymi danymi, których nie powinniśmy udostępniać. Gdy korzystamy na przykład z systemu kontroli wersji GIT, nie chcemy, aby te dane były umieszczone publicznie w naszym repozytorium.

Niech będzie to plik emailconfig.php. Utworzymy w nim tablicę z danymi, która będzie zwracana do zmiennej:

<?php
  return [
      'secretKey' => 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-XX',
      'mailServer' => 'mail.domena.pl',
      'port'  => '587',
      'username' => 'nazwa-konta',
      'password' => 'haslo',
      'myEmail' => '[email protected]'
  ];
?>
Gdzie:
  • 'secretKey' - nasz sekretny klucz do reCAPTCHA
  • 'mailServer' - nasz serwer pocztowy
  • 'port' - port dla danej uslugi, w przypdaku SMTP jest to 587.
  • 'username', 'password' - nazwa i hasło naszego konta pocztowego.
  • 'myEmail' - adres email na który mają trafiać wiadomości.

Jest to nieco lepszy sposób, gdyż nie zaśmiecamy sobie pliku zwykłymi zmiennymi globalnymi, w przypadku gdybyśmy to zadeklarowali tak:

  $secretKey = "XXX";
 //itd.

Teraz wystarczy dodać nazwę naszego pliku z danymi do .gitignore i będzie on ignorowany przez GIT-a.

Również powiniśmy zablokować dostęp do tego pliku na naszym serwerze. Aby to zrobić wystarczy dodać kilka linijek do pliku .htaccess na naszym serwerze:

<Files emailconfig.php>
order allow,deny
deny from all
</Files>

Pobieramy i przypisujemy sobie naszą tablicę do zmiennej $config:

  $config = require_once 'emailconfig.php';

I teraz będziemy mogli uzyskać dostęp do naszych danych w następujący sposób:

  $secretKey = $config['secretKey'];

Odbieramy dane wysłane za pomocą AJAX

Przechowywane są one w tablicy super globalnej $_POST, tak jak to zdefiniowaliśmy w żądaniu AJAX w parametrze “type”.

  $userEmail = $_POST['userEmail'];
  $subject = filter_var ($_POST['subject'], FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES);
  $messageText = filter_var ($_POST['message'], FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES);
Gdzie:
  • filter_var -jest to filtr, przez który przepuszczamy zmienne, aby pozbyć się niechcianych znaków. Z filtrem FILTER_SANITIZE_STRING usuwane są tagi i znaki specjalne. Flaga FILTER_FLAG_NO_ENCODE_QUOTES pomija znaki cudzysłowu.

Zanim przejdziemy do walidacji, odbierzmy jeszcze odpowiedź reCAPTCHA i przypiszmy do zmiennej $answear.

  $checkIfBot = file_get_contents('https://www.google.com/recaptcha/api/siteverify?secret='.$secretKey.'&response='.$_POST['g-recaptcha-response']);
  $recaptcha = json_decode($checkIfBot);
Gdzie:
  • file_get_contents() - ta funkcja zwraca zawartość plików jako string. W naszym przypadku będzie to odpowiedź wygenerowana na podstawie danych w URL, które są przesyłane tak jak metodą GET, o której wspominałem wcześniej.
  • json_decode() - dekoduje dane JSON. Dzięki temu będziemy mogli później uzyskać dostęp do odpowiedzi w ten sposób:
$recaptcha->success

Walidacja PHP

Sprawdzamy poszczególne dane od użytkownika oraz odpowiedź reCAPTCHA i tworzymy zmienną $alert, która będzie przetrzymywać komunikaty:

  if(!filter_var($userEmail, FILTER_VALIDATE_EMAIL)){
    $alert = 'Podaj poprawny email!';
  }
  else if (empty($subject)){
    $alert = 'Wpisz jakiś temat!';
  }
  else if (empty($messageText)){
    $alert = 'Pusta wiadomość? Napisz coś!';
  }
  else if($recaptcha->success===false){
    $alert = 'Potwierdź, że nie jesteś robotem!';
  }
Gdzie:
  • FILTER_VALIDATE_EMAIL - ten filtr sprawdza czy zmienna jest poprawnym adresem email. Jeśli tak zwraca “true”. Zauważ, że przed funkcją jest negacja(!). Jeśli email nie jest podany zwraca “false”, więc nie ma potrzeby sprawdzać tego osobno.
  • empty() - Sprawdza czy wartość jest pusta. Jeśli tak zwraca “true”. Jako puste brane są pod uwagę “”, 0, “0”, null.

I możemy wreszcie skonfigurować połączenie za pomocą Swiftmailera, wysłać wiadomość i wyświetlić komunikat w zależności czy się to powiodło czy nie:

  else {
    $transport = Swift_SmtpTransport::newInstance($config['mailServer'], $config['port'])
      ->setUsername($config['username'])
      ->setPassword($config['password'])
      ;

    $mailer = Swift_Mailer::newInstance($transport);

    $message = Swift_Message::newInstance($subject)
    ->setFrom($userEmail)
    ->setReplyTo($userEmail)
    ->setTo($config['myEmail'])
    ->setBody($messageText)
    ;

    $emailSent = $mailer->send($message);

    if ($emailSent){
      $alert = 'Wysłano. Dzięki za wiadomość!';
    }
    else {
      $alert = 'Coś poszło nie tak.';
    }
  }

Myślę, że ten kod jest w miarę jasny. Jeśli zastanawiasz się co za co odpowiada odsyłam do dokumentacji.

Jak odebrać błedy / komunikaty PHP przez AJAX?

Bardzo prosto. Musimy je wcześniej jednak przekonwertować na format JSON, gdyż taki zadeklarowaliśmy w naszym żądaniu AJAX w polu “dataType”. php?start_inline=1 $response = json_encode(array( 'text' => $alert )); Pozostało skorzystać z funkcji exit(), która kończy skrypt i przekazuje naszą odpowiedź: php?start_inline=1 exit($response);

Oto nasz cały kod:

<?php
  require_once 'swiftmailer/lib/swift_required.php';
  $config = require_once 'emailconfig.php';
  $secretKey = $config['secretKey'];

  $userEmail= $_POST['userEmail'];
  $subject= filter_var($_POST['subject'], FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES);
  $messageText= filter_var($_POST['message'], FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES);

  $checkIfBot = file_get_contents('https://www.google.com/recaptcha/api/siteverify?secret='.$secretKey.'&response='.$_POST['g-recaptcha-response']);
  $recaptcha = json_decode($checkIfBot);

  if(!filter_var($userEmail, FILTER_VALIDATE_EMAIL)){
    $alert = 'Podaj poprawny email!';
  }
  else if (empty($subject)){
    $alert = 'Wpisz jakiś temat!';
  }
  else if (empty($messageText)){
    $alert = 'Pusta wiadomość? Napisz coś!';
  }
  else if($recaptcha->success===false){
    $alert = 'Potwierdź, że nie jesteś robotem!';
  }
  else {
    $transport = Swift_SmtpTransport::newInstance($config['mailServer'], $config['port'])
      ->setUsername($config['username'])
      ->setPassword($config['password'])
      ;

    $mailer = Swift_Mailer::newInstance($transport);

    $message = Swift_Message::newInstance($subject)
    ->setFrom($userEmail)
    ->setReplyTo($userEmail)
    ->setTo($config['myEmail'])
    ->setBody($messageText)
    ;

    $emailSent = $mailer->send($message);

    if ($emailSent){
      $alert = 'Wysłano. Dzięki za wiadomość!';
    }
    else {
      $alert = 'Coś poszło nie tak.';
    }
  }

  $response = json_encode(array(
        'text' => $alert
  ));

  exit($response);
?>

Jeśli uważasz, że to co robię jest przydatne, polub stronę bloga na Facebooku. Wrzucam tam m.in. informacje o nowych wpisach, o promocjach dla programistów i inne.