🛠️ Sklep internetowy – PHP + MySQL (bezpieczna wersja)

Co przećwiczysz w tym projekcie?

SQL · baza + produktyZadanie 1

Stwórz bazę danych i tabelę produktów

Utwórz bazę sklep oraz tabelę produkty z przykładowymi danymi.

CREATE DATABASE IF NOT EXISTS sklep;
USE sklep;

CREATE TABLE IF NOT EXISTS produkty (
  id    INT AUTO_INCREMENT PRIMARY KEY,
  nazwa VARCHAR(100) NOT NULL,
  cena  DECIMAL(10,2) NOT NULL
);

INSERT INTO produkty (nazwa, cena) VALUES
  ('Mysz gamingowa RGB', 79.99),
  ('Klawiatura mechaniczna', 199.99),
  ('Monitor 24"', 599.00),
  ('Słuchawki bezprzewodowe', 129.90),
  ('Laptop 15"', 2999.99);

Możesz dodać więcej kolumn, np. opis, kategoria, stan_magazynu.

PHP · połączenieZadanie 2

Plik db.php – połączenie z bazą

Stwórz plik db.php z połączeniem do bazy danych (mysqli) i ustawieniem kodowania.

<?php
$host = 'localhost';
$user = 'root';
$pass = '';
$db   = 'sklep';

$conn = new mysqli($host, $user, $pass, $db);
if ($conn->connect_error) {
    die("Błąd połączenia: " . $conn->connect_error);
}
$conn->set_charset("utf8mb4");

Na serwerze produkcyjnym używaj oddzielnego użytkownika MySQL z ograniczonymi uprawnieniami.

PHP · lista produktówZadanie 3

index.php – wyświetlanie listy produktów

Wyświetl wszystkie produkty z bazy na stronie głównej sklepu, dbając o poprawne formatowanie i zabezpieczenie HTML.

<?php
require_once 'db.php';
?>
<!DOCTYPE html>
<html lang="pl">
<head>
  <meta charset="UTF-8">
  <title>Sklep internetowy</title>
</head>
<body>
  <h1>Lista produktów</h1>
  <?php
  $sql = "SELECT id, nazwa, cena FROM produkty";
  $result = $conn->query($sql);

  if ($result && $result->num_rows > 0) {
      while ($row = $result->fetch_assoc()) {
          $id    = (int)$row['id'];
          $nazwa = htmlspecialchars($row['nazwa'], ENT_QUOTES, 'UTF-8');
          $cena  = number_format((float)$row['cena'], 2, ',', ' ');
          echo "<div>";
          echo "<strong>{$nazwa}</strong> - {$cena} zł";
          echo " (ID: {$id})";
          echo "</div>";
      }
  } else {
      echo "Brak produktów.";
  }
  $conn->close();
  ?>
</body>
</html>
PHP · wyszukiwanieZadanie 4

search.php – wyszukiwanie produktów (prepared)

Dodaj stronę, która wyszukuje produkty po fragmencie nazwy. Użyj prepared statements, aby uniknąć SQL injection.

<?php
require_once 'db.php';
?>
<!DOCTYPE html>
<html lang="pl">
<head>
  <meta charset="UTF-8">
  <title>Wyszukiwanie produktów</title>
</head>
<body>
  <h1>Wyszukiwanie produktów</h1>

  <form method="get">
    Wyszukaj produkt:
    <input type="text" name="query">
    <button type="submit">Szukaj</button>
  </form>

  <?php
  if (isset($_GET['query'])) {
      $query = trim($_GET['query']);

      if ($query !== '') {
          $like = "%" . $query . "%";

          $stmt = $conn->prepare("SELECT id, nazwa, cena FROM produkty WHERE nazwa LIKE ?");
          $stmt->bind_param("s", $like);
          $stmt->execute();
          $result = $stmt->get_result();

          if ($result->num_rows > 0) {
              while ($row = $result->fetch_assoc()) {
                  $nazwa = htmlspecialchars($row['nazwa'], ENT_QUOTES, 'UTF-8');
                  $cena  = number_format((float)$row['cena'], 2, ',', ' ');
                  echo "<div>Produkt: {$nazwa} - Cena: {$cena} zł</div>";
              }
          } else {
              echo "Brak produktów spełniających kryteria.";
          }

          $stmt->close();
      } else {
          echo "Podaj frazę do wyszukania.";
      }
  }

  $conn->close();
  ?>
</body>
</html>
SQL · klienciZadanie 5

Tabela klienci – przechowywanie użytkowników

Stwórz tabelę użytkowników, w której hasło będzie przechowywane w postaci hash.

CREATE TABLE klienci (
  id    INT AUTO_INCREMENT PRIMARY KEY,
  login VARCHAR(50)  NOT NULL UNIQUE,
  haslo VARCHAR(255) NOT NULL
);
PHP · rejestracjaZadanie 6

register.php – rejestracja z password_hash + prepared

Przy rejestracji hasło powinno być haszowane funkcją password_hash(), a dane wstawiane prepared statementem.

<?php
session_start();
require_once 'db.php';

$info = "";

if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_POST["zarejestruj"])) {
    $login = trim($_POST["login"] ?? "");
    $haslo = $_POST["haslo"] ?? "";

    if ($login === "" || $haslo === "") {
        $info = "Uzupełnij login i hasło.";
    } else {
        $hash = password_hash($haslo, PASSWORD_DEFAULT);

        $stmt = $conn->prepare("INSERT INTO klienci (login, haslo) VALUES (?, ?)");
        $stmt->bind_param("ss", $login, $hash);

        if ($stmt->execute()) {
            $info = "Zarejestrowano użytkownika: " . htmlspecialchars($login, ENT_QUOTES, 'UTF-8');
        } else {
            $info = "Błąd rejestracji (login może być zajęty).";
        }

        $stmt->close();
    }
}
?>
<!DOCTYPE html>
<html lang="pl">
<head>
  <meta charset="UTF-8">
  <title>Rejestracja</title>
</head>
<body>
  <h1>Rejestracja</h1>

  <form method="post">
    Login: <input type="text" name="login"><br>
    Hasło: <input type="password" name="haslo"><br>
    <input type="submit" name="zarejestruj" value="Zarejestruj się">
  </form>

  <?php
  if ($info !== "") {
      echo "<p>" . htmlspecialchars($info, ENT_QUOTES, 'UTF-8') . "</p>";
  }
  ?>
</body>
</html>
PHP · logowanieZadanie 7

login.php – logowanie z password_verify + prepared

Przy logowaniu pobierz hash hasła z bazy przez prepared statement i sprawdź go funkcją password_verify().

<?php
session_start();
require_once 'db.php';

$komunikat = "";

if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_POST["zaloguj"])) {
    $login = trim($_POST["login"] ?? "");
    $haslo = $_POST["haslo"] ?? "";

    if ($login === "" || $haslo === "") {
        $komunikat = "Podaj login i hasło.";
    } else {
        $stmt = $conn->prepare("SELECT id, haslo FROM klienci WHERE login = ?");
        $stmt->bind_param("s", $login);
        $stmt->execute();
        $result = $stmt->get_result();

        if ($result && $result->num_rows === 1) {
            $u = $result->fetch_assoc();
            if (password_verify($haslo, $u["haslo"])) {
                $_SESSION["klient_id"] = (int)$u["id"];
                $_SESSION["login"]     = $login;
                $komunikat = "Zalogowano jako " . htmlspecialchars($login, ENT_QUOTES, 'UTF-8');
            } else {
                $komunikat = "Błędne hasło.";
            }
        } else {
            $komunikat = "Nie znaleziono użytkownika.";
        }

        $stmt->close();
    }
}
?>
<!DOCTYPE html>
<html lang="pl">
<head>
  <meta charset="UTF-8">
  <title>Logowanie</title>
</head>
<body>
  <h1>Logowanie</h1>

  <form method="post">
    Login: <input type="text" name="login"><br>
    Hasło: <input type="password" name="haslo"><br>
    <input type="submit" name="zaloguj" value="Zaloguj się">
  </form>

  <?php
  if ($komunikat !== "") {
      echo "<p>" . htmlspecialchars($komunikat, ENT_QUOTES, 'UTF-8') . "</p>";
  }
  ?>
</body>
</html>
PHP · sesjaZadanie 8

Prosty koszyk w sesji

Dodaj do strony z produktami formularz „Dodaj do koszyka” i przechowuj koszyk w tablicy sesyjnej.

<?php
session_start();
require_once 'db.php';

if (!isset($_SESSION["koszyk"])) {
    $_SESSION["koszyk"] = [];
}

if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_POST["produkt_id"])) {
    $id = (int)$_POST["produkt_id"];
    $_SESSION["koszyk"][$id] = ($_SESSION["koszyk"][$id] ?? 0) + 1;
    echo "Dodano produkt do koszyka.<br>";
}
?>

W pętli z produktami dodaj do każdego produktu przycisk z ukrytym produkt_id.

<form method="post">
  <input type="hidden" name="produkt_id" value="<?= $id ?>">
  <button type="submit">Dodaj do koszyka</button>
</form>
SQL · zamówieniaZadanie 9

Tabele zamowienia i zamowienia_produkty

Utwórz tabele do przechowywania zamówień oraz produktów w zamówieniu.

CREATE TABLE zamowienia (
  id              INT AUTO_INCREMENT PRIMARY KEY,
  klient_id       INT,
  data_zamowienia DATETIME DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE zamowienia_produkty (
  zamowienie_id INT,
  produkt_id    INT,
  ilosc         INT
);
PHP · zapis zamówieniaZadanie 1️⃣0

Zapisanie koszyka jako zamówienie

Po kliknięciu „Złóż zamówienie” zapisz dane z koszyka w tabelach zamowienia i zamowienia_produkty.

<?php
session_start();
require_once 'db.php';

if (!isset($_SESSION["klient_id"])) {
    die("Tylko zalogowany użytkownik może złożyć zamówienie.");
}

if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_POST["zloz"]) && !empty($_SESSION["koszyk"])) {
    $klient_id = (int)$_SESSION["klient_id"];

    $stmtZ = $conn->prepare("INSERT INTO zamowienia (klient_id) VALUES (?)");
    $stmtZ->bind_param("i", $klient_id);
    $stmtZ->execute();
    $zamowienie_id = $stmtZ->insert_id;
    $stmtZ->close();

    $stmtP = $conn->prepare(
        "INSERT INTO zamowienia_produkty (zamowienie_id, produkt_id, ilosc) VALUES (?, ?, ?)"
    );

    foreach ($_SESSION["koszyk"] as $pid => $ilosc) {
        $pid   = (int)$pid;
        $ilosc = (int)$ilosc;
        $stmtP->bind_param("iii", $zamowienie_id, $pid, $ilosc);
        $stmtP->execute();
    }

    $stmtP->close();
    $_SESSION["koszyk"] = [];
    echo "Zamówienie złożone!";
}
?>

<form method="post">
  <input type="submit" name="zloz" value="Złóż zamówienie">
</form>
PHP · raportZadanie 1️⃣1

Raport: lista zamówień z produktami

Wyświetl wszystkie zamówienia z nazwą klienta i listą produktów (JOIN kilku tabel).

<?php
require_once 'db.php';

$sql = "SELECT z.id, k.login, z.data_zamowienia,
               p.nazwa, zp.ilosc
        FROM zamowienia z
        JOIN klienci k ON z.klient_id = k.id
        JOIN zamowienia_produkty zp ON zp.zamowienie_id = z.id
        JOIN produkty p ON p.id = zp.produkt_id
        ORDER BY z.id DESC";

$res = $conn->query($sql);
$last_id = null;

while ($row = $res->fetch_assoc()) {
    if ($row["id"] != $last_id) {
        echo "<h3>Zamówienie #" . $row["id"] . " przez "
           . htmlspecialchars($row["login"], ENT_QUOTES, 'UTF-8')
           . " (" . $row["data_zamowienia"] . ")</h3>";
        $last_id = $row["id"];
    }
    $nazwa = htmlspecialchars($row["nazwa"], ENT_QUOTES, 'UTF-8');
    echo "- {$nazwa} x " . (int)$row["ilosc"] . "<br>";
}
$conn->close();
?>
PHP · CSRFZadanie 1️⃣2

Zabezpieczenie formularza przed CSRF

Dodaj token CSRF do formularzy modyfikujących dane (np. rejestracja, dodawanie produktu).

<?php
session_start();
if (empty($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
?>
<form method="post">
  <input type="hidden" name="csrf_token"
         value="<?= htmlspecialchars($_SESSION['csrf_token'], ENT_QUOTES, 'UTF-8') ?>">
  ... reszta formularza ...
</form>
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if (!hash_equals($_SESSION['csrf_token'] ?? '', $_POST['csrf_token'] ?? '')) {
        die("Nieautoryzowany dostęp.");
    }
    // Przetwarzanie formularza...
}
?>
SQL · indeksyZadanie 1️⃣3

Optymalizacja: indeks na nazwie produktu

Dodaj indeks na kolumnie nazwa w tabeli produkty, aby przyspieszyć wyszukiwanie.

CREATE INDEX idx_nazwa ON produkty (nazwa);

Portal edukacyjny © 2026 – Sklep PHP + SQL (wersja edukacyjna, bezpieczne wzorce kodu)