Lekcja 16: Filtrowanie i Walidacja Danych (Filter Functions)

Witaj w szesnastej lekcji kursu PHP! W poprzednich lekcjach, szczególnie przy obsłudze formularzy i przesyłaniu plików, wielokrotnie podkreślaliśmy fundamentalne znaczenie walidacji i oczyszczania danych pochodzących od użytkownika. Jest to absolutnie kluczowy element budowania bezpiecznych i niezawodnych aplikacji internetowych. PHP, od wersji 5.2, dostarcza potężne i elastyczne rozszerzenie "Filter", które znacznie ułatwia ten proces. W tej lekcji dogłębnie zbadamy możliwości funkcji filtrowania, które pozwalają na efektywną walidację (sprawdzanie poprawności) i sanityzację (oczyszczanie) danych wejściowych.

Dlaczego Filtrowanie i Walidacja są Tak Ważne?

Zanim przejdziemy do technicznych szczegółów, przypomnijmy, dlaczego nie możemy ufać żadnym danym przychodzącym z zewnątrz aplikacji (np. od użytkownika przez formularz, z parametrów URL, z plików cookie, czy nawet z zewnętrznych API):

Zasada jest prosta: Filtruj dane wejściowe, oczyszczaj dane wyjściowe (Filter Input, Escape Output). Rozszerzenie Filter w PHP skupia się na pierwszej części tej zasady.

Rozszerzenie Filter w PHP

Rozszerzenie Filter jest domyślnie włączone w PHP od wersji 5.2.0. Dostarcza ono zestaw funkcji i stałych zaprojektowanych specjalnie do walidacji i sanityzacji danych. Główne funkcje, z którymi będziemy pracować, to:

Typy Filtrów

Filtry w PHP można podzielić na dwie główne kategorie:

  1. Filtry Walidujące (Validating Filters): Służą do sprawdzania, czy dane spełniają określone kryteria (np. czy są poprawnym adresem e-mail, liczbą całkowitą w danym zakresie itp.). Jeśli dane są poprawne, filtr zazwyczaj zwraca te dane (ewentualnie skonwertowane do odpowiedniego typu, np. string na int). Jeśli dane nie przejdą walidacji, filtr zwraca false. Można to zmienić za pomocą flagi FILTER_NULL_ON_FAILURE, aby w przypadku błędu zwracał null.
  2. Filtry Sanityzujące (Sanitizing Filters): Służą do usuwania lub modyfikowania nielegalnych lub niepożądanych znaków z danych, aby uczynić je bezpieczniejszymi lub zgodnymi z oczekiwanym formatem. Te filtry zawsze zwracają przetworzony string, nawet jeśli oryginalny string nie zawierał żadnych znaków do usunięcia/zmiany.

Istnieją również "Inne Filtry" (np. FILTER_UNSAFE_RAW) oraz możliwość zdefiniowania własnego filtra za pomocą FILTER_CALLBACK.

Najczęściej Używane Filtry Walidujące

Najczęściej Używane Filtry Sanityzujące

Inne Filtry

Użycie filter_var()

filter_var() jest podstawową funkcją do filtrowania pojedynczej zmiennej.

<?php
// Walidacja adresu e-mail
$email1 = "test@example.com";
$email2 = "niepoprawny email";

$valid_email1 = filter_var($email1, FILTER_VALIDATE_EMAIL);
if ($valid_email1 !== false) {
    echo htmlspecialchars($email1) . " jest poprawnym adresem e-mail.<br>"; // Wyświetli
} else {
    echo htmlspecialchars($email1) . " NIE jest poprawnym adresem e-mail.<br>";
}

$valid_email2 = filter_var($email2, FILTER_VALIDATE_EMAIL);
if ($valid_email2 !== false) {
    echo htmlspecialchars($email2) . " jest poprawnym adresem e-mail.<br>";
} else {
    echo htmlspecialchars($email2) . " NIE jest poprawnym adresem e-mail.<br>"; // Wyświetli
}

// Walidacja liczby całkowitej z zakresem
$wiek = "25";
$options_int = [
    "options" => [
        "min_range" => 18,
        "max_range" => 65,
        "default" => 0 // Wartość domyślna, jeśli walidacja zawiedzie i nie użyto FILTER_NULL_ON_FAILURE
    ],
    "flags" => FILTER_NULL_ON_FAILURE // Zwróci null zamiast false w przypadku błędu
];

$valid_wiek = filter_var($wiek, FILTER_VALIDATE_INT, $options_int);
if ($valid_wiek !== null) {
    echo "Wiek $valid_wiek jest poprawny i mieści się w zakresie.<br>"; // Wyświetli
} else {
    echo "Wiek ".htmlspecialchars($wiek)." jest niepoprawny lub poza zakresem.<br>";
}

$wiek_niepoprawny = "sto";
$valid_wiek_niepoprawny = filter_var($wiek_niepoprawny, FILTER_VALIDATE_INT, $options_int);
if ($valid_wiek_niepoprawny === null) {
    echo "Wartość '".htmlspecialchars($wiek_niepoprawny)."' nie jest poprawną liczbą całkowitą lub jest poza zakresem.<br>"; // Wyświetli
}

// Sanityzacja stringa (przykład z htmlspecialchars, bo FILTER_SANITIZE_STRING jest przestarzały)
$niebezpieczny_string = "<script>alert(\'XSS\');</script> Witaj świecie!";
// Zamiast FILTER_SANITIZE_STRING:
$oczyszczony_string_htmlspecialchars = htmlspecialchars($niebezpieczny_string, ENT_QUOTES, 'UTF-8');
echo "Oczyszczony (htmlspecialchars): " . $oczyszczony_string_htmlspecialchars . "<br>";

// Sanityzacja URL
$url = "http://przykład.com/ścieżka z spacjami";
$sanitized_url = filter_var($url, FILTER_SANITIZE_URL);
echo "Oryginalny URL: " . htmlspecialchars($url) . "<br>";
echo "Sanityzowany URL: " . htmlspecialchars($sanitized_url) . "<br>"; // http://przyklad.com/ciezkazspacjami

// Użycie FILTER_CALLBACK
function walidujKodPocztowy($kod) {
    // Prosta walidacja formatu XX-XXX
    if (preg_match("/^\d{2}-\d{3}$/", $kod)) {
        return $kod;
    } else {
        return false;
    }
}
$kod1 = "12-345";
$kod2 = "12345";

$valid_kod1 = filter_var($kod1, FILTER_CALLBACK, ["options" => "walidujKodPocztowy"]);
$valid_kod2 = filter_var($kod2, FILTER_CALLBACK, ["options" => "walidujKodPocztowy"]);

echo "Kod $kod1: " . ($valid_kod1 ? "Poprawny ($valid_kod1)" : "Niepoprawny") . "<br>"; // Poprawny
echo "Kod $kod2: " . ($valid_kod2 ? "Poprawny ($valid_kod2)" : "Niepoprawny") . "<br>"; // Niepoprawny
?>

Użycie filter_input()

filter_input() pozwala na pobranie i przefiltrowanie zmiennej bezpośrednio ze źródeł wejściowych, takich jak $_GET, $_POST, $_COOKIE, $_SERVER, $_ENV. Jest to często preferowany sposób, ponieważ jasno określa, skąd pochodzą dane.

Pierwszy argument to stała określająca typ wejścia:

Drugi argument to nazwa zmiennej (klucz w odpowiedniej superglobalnej tablicy).

<?php
// Załóżmy, że URL to: skrypt.php?id=123&email=test@example.com

// Pobranie i walidacja 'id' z GET jako integer
$id_uzytkownika = filter_input(INPUT_GET, "id", FILTER_VALIDATE_INT);
if ($id_uzytkownika === false) {
    echo "ID użytkownika jest niepoprawne.<br>";
} elseif ($id_uzytkownika === null) {
    echo "Parametr ID nie został przesłany.<br>"; // Jeśli 'id' nie ma w URL
} else {
    echo "ID użytkownika: $id_uzytkownika<br>";
}

// Pobranie i sanityzacja 'email' z GET
$email_uzytkownika = filter_input(INPUT_GET, "email", FILTER_SANITIZE_EMAIL);
if ($email_uzytkownika !== null) {
    echo "Sanityzowany e-mail: " . htmlspecialchars($email_uzytkownika) . "<br>";
    // Dodatkowa walidacja po sanityzacji
    if (filter_var($email_uzytkownika, FILTER_VALIDATE_EMAIL)) {
        echo "E-mail jest poprawny po sanityzacji.<br>";
    } else {
        echo "E-mail NIE jest poprawny po sanityzacji.<br>";
    }
}

// Przykład dla POST (załóżmy, że formularz wysłał pole 'wiek')
// $wiek_z_post = filter_input(INPUT_POST, "wiek", FILTER_VALIDATE_INT, [
//     "options" => ["min_range" => 1, "max_range" => 120],
//     "flags" => FILTER_NULL_ON_FAILURE
// ]);
// if ($wiek_z_post === null) {
//     echo "Wiek nie został podany, jest niepoprawny lub poza zakresem.<br>";
// } else {
//     echo "Wiek z POST: $wiek_z_post<br>";
// }
?>

Jeśli zmienna nie istnieje w danym źródle wejściowym, filter_input() zwróci null. Jeśli zmienna istnieje, ale nie przejdzie walidacji, zwróci false (lub null, jeśli użyto FILTER_NULL_ON_FAILURE).

Użycie filter_var_array() i filter_input_array()

Te funkcje są bardzo przydatne, gdy potrzebujemy przefiltrować wiele wartości naraz. Przyjmują one tablicę definicji filtrów.

Tablica definicji filtrów to tablica asocjacyjna, gdzie klucze odpowiadają kluczom w tablicy danych (dla filter_var_array) lub nazwom zmiennych wejściowych (dla filter_input_array). Wartościami mogą być:

<?php
// Przykład dla filter_var_array()
$dane_wejsciowe = [
    "nazwa_produktu" => "  <strong>Super Produkt!</strong>  ",
    "cena" => "99,99", // Używamy przecinka jako separatora
    "ilosc" => "10szt",
    "email_kontaktowy" => "kontakt@example com",
    "strona_www" => "htp://niepoprawny-url"
];

$definicje_filtrow_var = [
    "nazwa_produktu" => [
        "filter" => FILTER_CALLBACK, // Użyjemy htmlspecialchars jako callback
        "options" => function($value) { return htmlspecialchars(trim($value), ENT_QUOTES, 'UTF-8'); }
    ],
    "cena" => [
        "filter" => FILTER_VALIDATE_FLOAT,
        "options" => ["decimal" => ","], // Określamy, że przecinek to separator dziesiętny
        "flags" => FILTER_NULL_ON_FAILURE
    ],
    "ilosc" => [
        "filter" => FILTER_VALIDATE_INT,
        "options" => ["min_range" => 1],
        "flags" => FILTER_NULL_ON_FAILURE
    ],
    "email_kontaktowy" => FILTER_VALIDATE_EMAIL,
    "strona_www" => [
        "filter" => FILTER_VALIDATE_URL,
        "flags" => FILTER_NULL_ON_FAILURE
    ],
    "nieistniejace_pole" => FILTER_VALIDATE_INT // Co jeśli pole nie istnieje w $dane_wejsciowe?
];

$przefiltrowane_dane = filter_var_array($dane_wejsciowe, $definicje_filtrow_var);

echo "<h3>Dane po filter_var_array():</h3><pre>";
print_r($przefiltrowane_dane);
echo "</pre>";
/*
Wynik będzie zawierał:
- nazwa_produktu: oczyszczona
- cena: float 99.99 lub null
- ilosc: int 10 lub null (bo "10szt" nie jest czystym intem, chyba że użyjemy FILTER_SANITIZE_NUMBER_INT najpierw)
  W tym przypadku, bez uprzedniej sanityzacji, ilosc będzie null.
  Aby to naprawić, można by zrobić: 'ilosc' => ['filter' => FILTER_SANITIZE_NUMBER_INT, 'flags' => FILTER_REQUIRE_SCALAR], a potem walidować.
  Lub lepiej, najpierw sanityzacja, potem walidacja w osobnym kroku dla 'ilosc'.
  Dla uproszczenia, zmieńmy 'ilosc' w $dane_wejsciowe na "10".
- email_kontaktowy: false (bo niepoprawny)
- strona_www: null (bo niepoprawny i FILTER_NULL_ON_FAILURE)
- nieistniejace_pole: null (bo nie ma go w $dane_wejsciowe i $dodaj_puste_jako_null jest domyślnie true)
*/

// Poprawiony przykład dla 'ilosc' w $dane_wejsciowe:
$dane_wejsciowe_poprawione = $dane_wejsciowe;
$dane_wejsciowe_poprawione["ilosc"] = "10"; // Zmieniamy na poprawny int
$przefiltrowane_dane_poprawione = filter_var_array($dane_wejsciowe_poprawione, $definicje_filtrow_var);
echo "<h3>Dane po filter_var_array() (poprawiona ilość):</h3><pre>";
print_r($przefiltrowane_dane_poprawione);
echo "</pre>";


// Przykład dla filter_input_array() (załóżmy, że dane są w $_POST)
// $_POST = [
//     "username" => " Jan Kowalski ",
//     "user_age" => "30lat",
//     "user_email" => "jan.kowalski@example.com"
// ];

$definicje_filtrow_input = [
    "username" => [
        "filter" => FILTER_SANITIZE_SPECIAL_CHARS // Użyjmy tego zamiast przestarzałego FILTER_SANITIZE_STRING
    ],
    "user_age" => FILTER_SANITIZE_NUMBER_INT,
    "user_email" => FILTER_VALIDATE_EMAIL,
    "user_terms" => [
        "filter" => FILTER_VALIDATE_BOOLEAN,
        "flags" => FILTER_NULL_ON_FAILURE // Jeśli nie ma 'user_terms' w POST, będzie null
    ]
];

// $przefiltrowane_post = filter_input_array(INPUT_POST, $definicje_filtrow_input);
// echo "<h3>Dane z POST po filter_input_array():</h3><pre>";
// print_r($przefiltrowane_post);
// echo "</pre>";

/*
Jeśli $_POST zawierałoby powyższe dane, $przefiltrowane_post wyglądałoby mniej więcej tak:
[
    "username" => " Jan Kowalski " (znaki specjalne byłyby zakodowane, spacje pozostają),
    "user_age" => "30" (string, bo sanitize zwraca string),
    "user_email" => "jan.kowalski@example.com" (string, bo poprawny email),
    "user_terms" => null (bo nie było w $_POST i użyliśmy FILTER_NULL_ON_FAILURE)
]
*/
?>

Domyślnie, jeśli klucz z definicji filtrów nie istnieje w tablicy wejściowej (dla filter_var_array) lub w źródle wejściowym (dla filter_input_array), to w wynikowej tablicy pojawi się ten klucz z wartością null. Można to kontrolować trzecim argumentem tych funkcji: $dodaj_puste_jako_null (domyślnie true). Jeśli ustawione na false, brakujące klucze nie pojawią się w wyniku.

Flagi i Opcje Filtrów

Wiele filtrów można dostosować za pomocą flag i opcji.

Flagi (Flags): Są to stałe (np. FILTER_FLAG_IPV4, FILTER_FLAG_STRIP_LOW), które modyfikują zachowanie filtra. Można łączyć wiele flag za pomocą bitowego operatora OR (|).

Opcje (Options): Są to dodatkowe parametry przekazywane do filtra w postaci tablicy asocjacyjnej. Klucze w tej tablicy to nazwy opcji (np. min_range, regexp, default), a wartości to ich ustawienia.

Przykłady użycia opcji i flag były już pokazane wcześniej. Ważne opcje to m.in.:

Flagi specyficzne dla filtrów:

Flagi ogólne (mogą być używane z wieloma filtrami lub w definicjach dla filter_var_array/filter_input_array):

Najlepsze Praktyki

  1. Zawsze waliduj dane po stronie serwera. Walidacja po stronie klienta (JavaScript) jest tylko dodatkiem dla UX.
  2. Filtruj dane wejściowe, oczyszczaj dane wyjściowe. Używaj filtrów do walidacji i sanityzacji danych przychodzących. Używaj np. htmlspecialchars() przy wyświetlaniu danych na stronie HTML.
  3. Stosuj zasadę białej listy (whitelisting) zamiast czarnej listy (blacklisting). Zamiast próbować blokować wszystkie znane złe wzorce (co jest trudne i podatne na błędy), określaj, jakie dane są dozwolone i akceptuj tylko te.
  4. Bądź jak najbardziej specyficzny. Wybieraj filtr, który najlepiej pasuje do oczekiwanego typu i formatu danych. Jeśli walidujesz liczbę całkowitą, użyj FILTER_VALIDATE_INT z odpowiednimi opcjami zakresu, a nie tylko np. FILTER_SANITIZE_NUMBER_INT.
  5. Sanityzacja nie zawsze oznacza walidację. Filtr sanityzujący może zwrócić wartość, która nadal nie jest poprawna z punktu widzenia logiki aplikacji (np. FILTER_SANITIZE_NUMBER_INT z "abc123def" zwróci "123", co może być poprawną liczbą, ale niekoniecznie tym, czego oczekiwano). Często potrzebne jest połączenie sanityzacji i walidacji.
  6. Rozważ kolejność operacji. Czasami może być konieczne najpierw zsanityzowanie danych (np. usunięcie niepotrzebnych spacji za pomocą trim()), a dopiero potem ich walidacja.
  7. Używaj filter_input() i filter_input_array(), aby jasno określić źródło danych (GET, POST itp.).
  8. Dokładnie testuj swoją logikę walidacji dla różnych przypadków, w tym poprawnych danych, niepoprawnych danych i danych granicznych.

Podsumowanie Lekcji

W tej lekcji dogłębnie poznaliśmy rozszerzenie Filter w PHP, które jest niezwykle użytecznym narzędziem do zapewniania bezpieczeństwa i integralności danych w aplikacjach webowych. Nauczyliśmy się rozróżniać filtry walidujące i sanityzujące, poznaliśmy najważniejsze z nich oraz sposoby ich konfiguracji za pomocą flag i opcji. Przećwiczyliśmy użycie funkcji filter_var(), filter_input(), filter_var_array() i filter_input_array(). Podkreśliliśmy również kluczowe najlepsze praktyki dotyczące filtrowania i walidacji danych.

Pamiętaj, że solidna walidacja i sanityzacja danych wejściowych to fundament bezpiecznego programowania w PHP. W następnej lekcji zajmiemy się zarządzaniem stanem użytkownika za pomocą sesji i ciasteczek.


Zadanie praktyczne

Stwórz formularz rejestracyjny (rejestracja_form.html) z następującymi polami:

Napisz skrypt PHP (rejestracja_handler.php), który odbiera dane z tego formularza (metodą POST) i wykonuje następujące operacje walidacji i sanityzacji używając funkcji filter:
  1. Nazwa użytkownika: Sanityzuj używając FILTER_SANITIZE_SPECIAL_CHARS. Sprawdź, czy nie jest pusta i czy ma np. od 3 do 20 znaków.
  2. Adres e-mail: Zwaliduj używając FILTER_VALIDATE_EMAIL. Sanityzuj używając FILTER_SANITIZE_EMAIL.
  3. Hasło: Sprawdź, czy nie jest puste i czy ma co najmniej 8 znaków. Sprawdź, czy hasło i powtórzone hasło są identyczne. (Nie filtruj hasła samą funkcją filter pod kątem zawartości, tylko sprawdzaj długość).
  4. Wiek: Zwaliduj jako liczbę całkowitą w zakresie np. 13-100 lat używając FILTER_VALIDATE_INT.
  5. Link do profilu: Jeśli podany, zwaliduj jako URL używając FILTER_VALIDATE_URL. Sanityzuj używając FILTER_SANITIZE_URL.
  6. Zgoda na regulamin: Zwaliduj jako boolean (czy checkbox został zaznaczony).
Wyświetl wszystkie przesłane (i ewentualnie zsanityzowane) dane oraz listę błędów walidacji, jeśli wystąpiły. Jeśli wszystko jest poprawne, wyświetl komunikat o sukcesie.

Pokaż przykładowe rozwiązanie

rejestracja_form.html

<!DOCTYPE html>
<html lang="pl">
<head>
    <meta charset="UTF-8">
    <title>Formularz Rejestracji</title>
</head>
<body>
    <h2>Rejestracja Użytkownika</h2>
    <form action="rejestracja_handler.php" method="POST">
        <p><label for="username">Nazwa użytkownika:</label><br>
           <input type="text" id="username" name="username" required></p>
        <p><label for="email">Adres e-mail:</label><br>
           <input type="email" id="email" name="email" required></p>
        <p><label for="password">Hasło (min. 8 znaków):</label><br>
           <input type="password" id="password" name="password" required></p>
        <p><label for="password_confirm">Powtórz hasło:</label><br>
           <input type="password" id="password_confirm" name="password_confirm" required></p>
        <p><label for="age">Wiek (13-100):</label><br>
           <input type="text" id="age" name="age" required></p>
        <p><label for="profile_url">Link do profilu (opcjonalne):</label><br>
           <input type="url" id="profile_url" name="profile_url"></p>
        <p><input type="checkbox" id="terms" name="terms" value="accepted" required> 
           <label for="terms">Akceptuję regulamin</label></p>
        <p><input type="submit" value="Zarejestruj"></p>
    </form>
</body>
</html>
            

rejestracja_handler.php

<?php
$bledy = [];
$danePoprawne = [];

if ($_SERVER["REQUEST_METHOD"] == "POST") {
    // Definicje filtrów dla filter_input_array
    $definicjeFiltrow = [
        "username" => FILTER_SANITIZE_SPECIAL_CHARS,
        "email" => FILTER_SANITIZE_EMAIL,
        "password" => FILTER_DEFAULT, // Hasła nie filtrujemy, tylko walidujemy ręcznie
        "password_confirm" => FILTER_DEFAULT,
        "age" => FILTER_VALIDATE_INT,
        "profile_url" => FILTER_SANITIZE_URL,
        "terms" => FILTER_VALIDATE_BOOLEAN
    ];

    $daneWejsciowe = filter_input_array(INPUT_POST, $definicjeFiltrow);

    // 1. Nazwa użytkownika
    $danePoprawne["username"] = $daneWejsciowe["username"];
    if (empty(trim($danePoprawne["username"]))) {
        $bledy["username"] = "Nazwa użytkownika jest wymagana.";
    } elseif (strlen($danePoprawne["username"]) < 3 || strlen($danePoprawne["username"]) > 20) {
        $bledy["username"] = "Nazwa użytkownika musi mieć od 3 do 20 znaków.";
    }

    // 2. Adres e-mail
    $danePoprawne["email"] = $daneWejsciowe["email"];
    if (!filter_var($danePoprawne["email"], FILTER_VALIDATE_EMAIL)) {
        $bledy["email"] = "Podaj poprawny adres e-mail.";
    }

    // 3. Hasło
    $danePoprawne["password"] = $daneWejsciowe["password"]; // Oryginalne hasło do sprawdzenia
    $password_confirm = $daneWejsciowe["password_confirm"];
    if (empty($danePoprawne["password"])) {
        $bledy["password"] = "Hasło jest wymagane.";
    } elseif (strlen($danePoprawne["password"]) < 8) {
        $bledy["password"] = "Hasło musi mieć co najmniej 8 znaków.";
    } elseif ($danePoprawne["password"] !== $password_confirm) {
        $bledy["password_confirm"] = "Hasła nie są zgodne.";
    }

    // 4. Wiek
    $opcjeWieku = ["options" => ["min_range" => 13, "max_range" => 100]];
    $wiek = filter_var($daneWejsciowe["age"], FILTER_VALIDATE_INT, $opcjeWieku);
    if ($wiek === false) {
        $bledy["age"] = "Wiek musi być liczbą całkowitą między 13 a 100.";
    } else {
        $danePoprawne["age"] = $wiek;
    }

    // 5. Link do profilu (opcjonalne)
    $danePoprawne["profile_url"] = $daneWejsciowe["profile_url"];
    if (!empty($danePoprawne["profile_url"]) && !filter_var($danePoprawne["profile_url"], FILTER_VALIDATE_URL)) {
        $bledy["profile_url"] = "Podany link do profilu jest niepoprawny.";
    }

    // 6. Zgoda na regulamin
    $danePoprawne["terms"] = filter_var($daneWejsciowe["terms"], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
    if ($danePoprawne["terms"] !== true) {
        $bledy["terms"] = "Musisz zaakceptować regulamin.";
    }

    // Wyświetlanie wyników
    echo "<h1>Wynik Rejestracji</h1>";
    if (empty($bledy)) {
        echo "<p style=\"color: green;\">Rejestracja przebiegła pomyślnie!</p>";
        echo "<h3>Przesłane dane (po sanityzacji/walidacji):</h3>";
        echo "<ul>";
        echo "<li>Nazwa użytkownika: " . htmlspecialchars($danePoprawne["username"]) . "</li>";
        echo "<li>E-mail: " . htmlspecialchars($danePoprawne["email"]) . "</li>";
        // NIE WYŚWIETLAJ HASŁA! Tutaj byłoby hashowanie i zapis do bazy.
        echo "<li>Wiek: " . htmlspecialchars((string)$danePoprawne["age"]) . "</li>";
        echo "<li>Link do profilu: " . (!empty($danePoprawne["profile_url"]) ? htmlspecialchars($danePoprawne["profile_url"]) : "Nie podano") . "</li>";
        echo "<li>Zgoda na regulamin: Tak</li>";
        echo "</ul>";
    } else {
        echo "<p style=\"color: red;\">Wystąpiły błędy w formularzu:</p>";
        echo "<ul style=\"color: red;\">";
        foreach ($bledy as $klucz => $blad) {
            echo "<li><strong>" . htmlspecialchars(ucfirst(str_replace("_"," ", $klucz))) . ":</strong> " . htmlspecialchars($blad) . "</li>";
        }
        echo "</ul>";
    }
} else {
    echo "<p>Formularz nie został wysłany.</p>";
}

echo "<p><a href=\"rejestracja_form.html\">Powrót do formularza</a></p>";
?>
            

Zadanie do samodzielnego wykonania

Napisz skrypt, który przyjmuje przez GET parametr id_produktu. Użyj funkcji filter do:

  1. Sanityzacji id_produktu tak, aby zawierał tylko cyfry (FILTER_SANITIZE_NUMBER_INT).
  2. Walidacji zsanityzowanego id_produktu jako liczby całkowitej dodatniej (FILTER_VALIDATE_INT z opcją min_range ustawioną na 1).
Wyświetl oryginalną wartość, zsanityzowaną wartość oraz wynik walidacji (lub komunikat o błędzie). Przetestuj dla różnych wartości, np. ?id_produktu=123, ?id_produktu=abc456def, ?id_produktu=-5, ?id_produktu=0, ?id_produktu= (pusty).


FAQ - Filtrowanie i Walidacja Danych

Jaka jest różnica między sanityzacją a walidacją?

Walidacja sprawdza, czy dane są zgodne z oczekiwanym formatem/regułami (np. czy to poprawny e-mail, czy liczba mieści się w zakresie). Zwraca informację tak/nie (lub dane/false). Sanityzacja modyfikuje dane, usuwając lub zmieniając potencjalnie niebezpieczne lub niechciane znaki. Zawsze zwraca zmodyfikowane dane.

Czy filtry PHP są wystarczające do pełnego zabezpieczenia aplikacji?

Filtry są bardzo potężnym narzędziem, ale stanowią tylko część strategii bezpieczeństwa. Należy je łączyć z innymi technikami, takimi jak oczyszczanie danych wyjściowych (np. htmlspecialchars), używanie zapytań przygotowanych (prepared statements) do baz danych, ochrona przed CSRF, odpowiednia konfiguracja serwera i PHP, oraz regularne aktualizacje oprogramowania.

Kiedy używać FILTER_NULL_ON_FAILURE?

Flaga FILTER_NULL_ON_FAILURE jest przydatna, gdy chcemy wyraźnie odróżnić sytuację, w której dane nie przeszły walidacji (filtr zwróci null) od sytuacji, w której poprawne dane mają wartość, która mogłaby być interpretowana jako false (np. liczba 0, pusty string dla niektórych filtrów walidujących). Ułatwia to pisanie bardziej jednoznacznej logiki warunkowej.

Czy FILTER_SANITIZE_STRING jest bezpieczny do użycia?

FILTER_SANITIZE_STRING został oznaczony jako przestarzały (deprecated) w PHP 8.0 i usunięty w PHP 8.1. Miał on pewne problemy i nie był w pełni niezawodny w zapobieganiu XSS we wszystkich kontekstach. Zamiast niego zaleca się używanie bardziej specyficznych filtrów sanityzujących (np. FILTER_SANITIZE_SPECIAL_CHARS) lub funkcji takich jak htmlspecialchars() do oczyszczania danych przed wyświetleniem.

Jak walidować bardziej złożone struktury danych, np. tablice zagnieżdżone?

Dla tablic zagnieżdżonych można używać filter_var_array() z zagnieżdżonymi definicjami filtrów. Kluczem w definicji filtra może być ścieżka do elementu w tablicy. Alternatywnie, można iterować po tablicy i stosować filter_var() do poszczególnych elementów lub napisać własną funkcję walidującą (ewentualnie używając FILTER_CALLBACK).

Czy filtry obsługują kodowanie znaków (np. UTF-8)?

Wiele filtrów, zwłaszcza te sanityzujące, działa na poziomie bajtów lub zakłada pewne kodowanie (często ASCII lub ISO-8859-1). Przy pracy z danymi UTF-8, szczególnie przy sanityzacji stringów, należy być ostrożnym. Funkcje takie jak htmlspecialchars() pozwalają na określenie kodowania (trzeci argument). Dla walidacji (np. e-mail z FILTER_FLAG_EMAIL_UNICODE) obsługa Unicode jest stopniowo dodawana.

Co jeśli potrzebuję bardzo specyficznej, niestandardowej walidacji?

Możesz użyć FILTER_CALLBACK i przekazać własną funkcję walidującą. Alternatywnie, możesz połączyć standardowe filtry z własną logiką napisaną w PHP, np. używając wyrażeń regularnych (preg_match) lub sprawdzając warunki specyficzne dla Twojej aplikacji po wstępnym przefiltrowaniu danych.