SQL od podstaw · część 28

PHP i baza danych: mysqli oraz parametry zapytań

Połącz lokalną aplikację z bazą, pobierz produkt po identyfikatorze i bezpiecznie wyświetl wynik. Pełny przykład PHP z walidacją, parametrami i obsługą błędów.

Część bazodanowa INF.03

Przeglądarka → PHP → baza → HTML

PHP wykonuje się na serwerze. Przeglądarka wysyła żądanie HTTP, skrypt łączy się z bazą, wykonuje zapytanie i tworzy odpowiedź. Użytkownik otrzymuje HTML, a nie kod PHP ani hasło do bazy. Nie otwieraj pliku .php przez file:// — uruchom go przez lokalny serwer WWW obsługujący PHP.

1. Wejście

Identyfikator produktu z parametru adresu.

2. Zapytanie

Walidacja, przygotowana instrukcja i związanie wartości.

3. Odpowiedź

Kontrola wyniku, kodowanie HTML i komunikat dla użytkownika.

mysqli jest rozszerzeniem PHP dla MySQL i zgodnych połączeń z MariaDB. Nie jest uniwersalnym sterownikiem SQL Server, Oracle ani Access. Edytor SQL w tym kursie nie wykonuje PHP.

Przygotowanie oddzielnego środowiska

  1. Uruchom lokalny Apache/PHP i serwer bazy, np. XAMPP. Sprawdź rzeczywisty silnik, port i dostępność rozszerzenia mysqli.
  2. Zaimportuj bazę kursu z części 26. Użyj osobnego konta z SELECT na produkty, jak w części 25. Nie wykorzystuj konta administratora działającej strony.
  3. Utwórz prywatny plik konfiguracji db.php poza katalogiem udostępnianym przez serwer WWW. W XAMPP oznacza to poza htdocs.
  4. W katalogu publicznym projektu zapisz tylko produkty.php. Wskazaną w nim ścieżkę konfiguracji zastąp własną. Hasło uzupełnij lokalnie; nie wklejaj go do wpisu, repozytorium ani zrzutu.

Przykładowa konfiguracja — wartości są znacznikami do zastąpienia, nie gotowymi danymi dostępowymi:

<?php
// Zapisz jako db.php POZA katalogiem publicznym serwera.
// Uzupełnij tylko danymi oddzielnej bazy ćwiczeniowej.
return [
    'host' => '127.0.0.1',
    'port' => 3306,
    'database' => 'cn_sql_cwiczenia_2026',
    'user' => 'cn_czytelnik',
    'password' => '<TWOJE_HASLO>',
];

Host i port muszą odpowiadać konfiguracji serwera i konta. W połączeniach zdalnych dodatkowo skonfiguruj TLS i weryfikację certyfikatu zgodnie ze sterownikiem. Ten przykład dotyczy lokalnego ćwiczenia.

Pełny odczyt produktu po identyfikatorze

Kod dla PHP 8.x używa bind_result, więc nie wymaga metody get_result zależnej od dostępności mysqlnd. W środowisku udostępnionym innym osobom wyłącz publiczne wyświetlanie szczegółów błędów PHP i chroń logi.

<?php
declare(strict_types=1);
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
header('Content-Type: text/html; charset=UTF-8');

function h(string $value): string {
    return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}

$id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT,
    ['options' => ['min_range' => 1, 'max_range' => 2147483647]]);
if ($id === null || $id === false) {
    http_response_code(400);
    exit('Podaj poprawne dodatnie id produktu, np. ?id=1.');
}

try {
    // Zmień na rzeczywistą ścieżkę do konfiguracji POZA htdocs.
    $cfg = require '/sciezka/poza/htdocs/db.php';
    $db = new mysqli($cfg['host'], $cfg['user'], $cfg['password'],
        $cfg['database'], $cfg['port']);
    $db->set_charset('utf8mb4');
    $stmt = $db->prepare('SELECT nazwa, cena FROM produkty WHERE id = ?');
    $stmt->bind_param('i', $id);
    $stmt->execute();
    $stmt->bind_result($nazwa, $cena);
    $found = $stmt->fetch();
    $stmt->close();
    $db->close();
} catch (Throwable $e) {
    // Chronione logi serwera, bez danych połączenia i wejścia użytkownika.
    error_log('CN: blad odczytu produktu; typ=' . get_class($e));
    http_response_code(500);
    exit('Nie udało się pobrać danych. Spróbuj później.');
}
if (!$found) {
    http_response_code(404);
    exit('Nie znaleziono produktu.');
}
?>
<!doctype html>
<html lang="pl">
<meta charset="UTF-8">
<title>Produkt — ćwiczenie SQL</title>
<h1><?= h((string)$nazwa) ?></h1>
<p>Cena: <?= h((string)$cena) ?> zł</p>
</html>

Po dostosowaniu konfiguracji otwórz lokalny adres projektu zakończony /produkty.php?id=1. Na początkowych danych powinien wyświetlić Klawiatura i cenę 120.00. Dla poprawnego id bez rekordu otrzymasz komunikat 404, a dla błędnego parametru — 400. To oczekiwane zachowanie do samodzielnej kontroli, nie zapis wykonanego tu testu.

Co robią najważniejsze linie?

  • mysqli_report włącza zgłaszanie problemów jako wyjątków, aby obsłużyć je w jednym miejscu.
  • set_charset('utf8mb4') ustala kodowanie połączenia. Nagłówek HTTP i zapis pliku UTF-8 dotyczą innej warstwy; potrzebna jest zgodność całego przepływu.
  • prepare przygotowuje strukturę SQL. Znak ? jest miejscem na wartość, bez dopisywania apostrofów do szablonu.
  • bind_param('i', $id) wiąże zmienną całkowitą. Typy to i — integer, d — double, s — string, b — dane binarne. Wartości DECIMAL często warto przekazywać jako prawidłowy tekst dziesiętny, aby nie wprowadzać błędów float.
  • execute wykonuje zapytanie; bind_result i fetch odbierają kolumny i wiersz.
  • htmlspecialchars zabezpiecza osadzenie danych w tekście HTML i cytowanych atrybutach. Nie jest funkcją zabezpieczającą SQL ani dowolny kod JavaScript.

Wiele wierszy można odczytać pętlą while ($stmt->fetch()). Dla zapytań zmieniających dane kontroluj m.in. affected_rows i transakcję. Zero zmienionych wierszy w MySQL może oznaczać brak dopasowania lub brak rzeczywistej zmiany wartości — zależy też od opcji połączenia.

Parametr jest wartością, nie nazwą kolumny

Nie sklejaj wejścia użytkownika w SQL, np. "WHERE id = " . $_GET['id']. Parametryzacja zachowuje strukturę polecenia nawet wtedy, gdy dane zawierają apostrof. Walidacja sprawdza natomiast, czy wartość ma sens dla aplikacji.

ORDER BY ? nie wybiera dynamicznie nazwy kolumny. Nazwy tabel, kolumn i kierunek sortowania wybieraj z zamkniętej listy dozwolonych fragmentów kodu. Parametrami nadal przekazuj wartości. Uprawnienia konta bazy i kontrola dostępu użytkownika aplikacji są potrzebne również przy przygotowanych zapytaniach.

Ten sam odczyt w innych bazach

ŚrodowiskoPrzykład / sterownik
MySQL / MariaDBmysqli: SELECT nazwa, cena FROM produkty WHERE id = ?
SQL ServerMicrosoft Drivers for PHP: sqlsrv albo PDO_SQLSRV; nazwa obiektu np. dbo.produkty, parametry według API sterownika.
OracleOCI8: SELECT nazwa, cena FROM produkty WHERE id = :id, następnie oci_bind_by_name.
AccessODBC z odpowiednim sterownikiem ACE i zgodną architekturą procesu; albo formularze/kwerendy w Access. Samo mysqli nie otworzy pliku .accdb.

Wspólny jest cel: szablon zapytania i oddzielne dane. Zmieniają się biblioteka, konfiguracja, obsługa wyników i typów oraz część składni SQL.

Ćwiczenia z rozwiązaniami

1. Dlaczego potrzebujemy walidacji i parametrów?

Walidacja odrzuca np. ujemny identyfikator lub tablicę zamiast liczby. Parametry zapobiegają zmianie struktury SQL przez przekazaną wartość. To różne zadania.

2. Zmień filtr na nazwę produktu

Sprawdź, czy wejście jest pojedynczym tekstem o dopuszczalnej długości. Użyj WHERE nazwa = ? i bind_param(’s’, $nazwa). Dla wielu pasujących produktów zastosuj pętlę fetch. Wyszukiwanie tekstowe zależy od kolacji bazy.

3. Plan samodzielnej kontroli strony

Sprawdź id=1, nieistniejące dodatnie id, brak id i tekst zamiast liczby. Zwróć uwagę na kody HTTP, komunikaty oraz brak sekretów w HTML. W oddzielnych danych ćwiczeniowych sprawdź poprawne wyświetlanie znaków specjalnych jako tekstu. Nie modyfikuj produkcyjnej bazy.

Źródła

PHP: przygotowane zapytania mysqli · PHP: set_charset · PHP: htmlspecialchars

To pomost między bazami danych a programowaniem aplikacji w INF.03. Nie zastępuje pozostałych wymagań kwalifikacji dotyczących WWW.

Przewijanie do góry