INF.03 · Aplikacje internetowe PHP
Projekt końcowy: kompletny serwis filmowy
Połącz elementy w jeden działający serwis.
- PHP8.3
- mysqli · MariaDB
- Praktyka i diagnoza
Po tej lekcji: Uruchomisz i przetestujesz cały projekt, wyjaśniając drogę danych i własne decyzje.
Zrozum mechanizm
Projekt łączy formularze, funkcje, bazę, sesje i autoryzację. Katalog jest publiczny, panel prywatny, a rekordy filmów mają właścicieli. Użytkownik może zarejestrować się, zalogować, utworzyć własny film, zmienić go, dodać publiczne filmy do koszyka i zapisać symulowane zamówienie. Każda z tych operacji ma osobny warunek dostępu i sposób sprawdzenia.
Oddziel src od public. Konfiguracja i funkcje nie są plikami do pobierania przez HTTP. Nie oddawaj config.local.php, haseł ani sesji. Plik schema.sql zapewnia odtwarzalne środowisko z fikcyjnymi danymi. Testy powinny obejmować sukces i odmowę: brak logowania, zły format, cudzy rekord, brak tokenu, powtórzenie i awarię zapisu.
Wersja szkolna pokazuje mechanizmy, nie jest gotową usługą publiczną. Przed realną publikacją potrzebne są m.in. aktualizacje, HTTPS, potwierdzenie i odzyskanie konta, dopracowane ograniczanie nadużyć, monitoring oraz procedura kopii i odtwarzania. To osobne decyzje, których nie zastąpi atrakcyjny widok formularza.
Jak uruchamiać krótkie przykłady?
Każdy krótki przykład oznaczony własnym plikiem uruchamiaj osobno, nie doklejaj wszystkich definicji do jednego pliku. W edytorze zapisz go jako .php, a w terminalu tego folderu wpisz np. php for.php. Wtedy pokazane końce wierszy PHP_EOL są widoczne. W odpowiedzi HTML znak końca wiersza sam nie robi nowej linii na ekranie: użyj <br> albo znacznika <pre>. Wyświetl źródło pozwala zobaczyć tekst odpowiedzi.
Przykłady opisane jako fragment projektu wymagają paczki KINO / LAB i wcześniejszej konfiguracji. Nie uruchamiaj ich jako samodzielnych endpointów. Pełny plik niżej pokazuje kontekst. Funkcje h(), pole() i wymagajPost() są nasze; ich definicje wczytuje bootstrap.php.
Jak odczytać przepływ całej aplikacji?
Nie zaczynaj od przepisywania całego pliku. Wybierz jedną czynność: dodanie filmu. Prześledź, gdzie powstaje każda wartość. Ten wycinek pokazuje kolejność wywołań wewnątrz poprawnego POST w dodaj.php.
Podstawowa forma: formularz → dane → walidacja → zapytanie → wynik → odpowiedź
<?php
$idUzytkownika = wymagajLogowania();
wymagajPost();
[$tytul, $rok, $gatunek, $cena] = daneFilmu($_POST);
// Dalej: prepare, bind_param, execute (pełny dodaj.php).
przekieruj('/panel.php');
- wymagajLogowania zwraca ID z sesji lub przerywa dostęp. wymagajPost sprawdza metodę i CSRF. To nasze funkcje, zdefiniowane w src/funkcje.php.
- daneFilmu przyjmuje tablicę formularza i zwraca cztery sprawdzone wartości. Nawiasy po lewej rozpakowują wynik, nie tworzą nowego formularza.
- Zapytanie zapisuje dane z ID właściciela. przekieruj wysyła 303 i exit; dlatego instrukcje po nim nie są dalszym etapem odpowiedzi.
- Zacznij od działających podstaw bez bazy. Dopiero po konfiguracji MariaDB przejdź katalog → rejestracja → logowanie → własny CRUD → koszyk → zamówienie → wylogowanie.
Jak czytać pozostałe zapisy w pełnym przykładzie
| Zapis | Znaczenie | Przykład użycia |
|---|---|---|
pole($dane, $nazwa) | Nasza funkcja: pobiera wskazany klucz tylko wtedy, gdy ma wartość string; inaczej oddaje pusty napis. isset sprawdza obecność i brak null, is_string typ. | pole($_POST, "email") |
h($tekst) | Nasza funkcja kodująca tekst/atrybut HTML przez htmlspecialchars; nie wykonuje walidacji zakresu ani zabezpieczenia SQL. | h($film["tytul"]) |
tokenFormularza() | Nasza funkcja zwraca HTML ukrytego pola csrf z wartością sesji zakodowaną przez h. | <?= tokenFormularza() ?> |
naglowek($tytul), stopka() | Nasze funkcje wypisujące wspólny HTML strony. void oznacza brak zwracanego wyniku do użycia w wyrażeniu. | naglowek("Katalog"); |
pieniadze($grosze) | Nasza funkcja formatuje grosze jako złote. number_format przyjmuje liczbę, liczbę miejsc po przecinku, separator dziesiętny i separator grup. Wynik to string. | number_format(12.99, 2, ",", " ") |
dirname(__DIR__) | dirname daje katalog nadrzędny; __DIR__ to katalog bieżącego pliku. W public prowadzi to do katalogu projektu, a dopisana ścieżka wskazuje src. | dirname(__DIR__)."/src/bootstrap.php" |
-> oraz :: | -> wybiera właściwość lub metodę obiektu; :: odnosi się do elementu statycznego klasy. To nie jest zapis dostępu do tablicy. | $zapytanie->execute(); |
Sprawdź w działaniu
Połącz elementy w jeden działający serwis — sprawdź w modelu
Model w przeglądarce wyjaśnia mechanizm na fikcyjnych danych. Nie wykonuje PHP ani zapytań SQL i nie tworzy kont. Prawdziwe skrypty uruchomisz lokalnie z paczki.
Przewidź rezultat, wykonaj próbę i wyjaśnij, która decyzja po stronie serwera decyduje o wyniku.
Uruchom krok po kroku
Zacznij od START.txt i importu schematu. Uruchom katalog. Przejdź całą ścieżkę jako gość i dwa fikcyjne konta. Dodaj jedną własną funkcję, np. filtr gatunku, pozostawiając parametry SQL i kontrolę dostępu. Na końcu odtwórz projekt w nowym folderze i czystej bazie ćwiczeniowej z własnego kompletu plików.
Plik wzorcowy: schema.sql
CREATE DATABASE IF NOT EXISTS serwis_filmowy CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE serwis_filmowy;
CREATE TABLE IF NOT EXISTS Uzytkownicy (
id INT AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(254) NOT NULL UNIQUE,
haslo_hash VARCHAR(255) NOT NULL
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS Filmy (
id INT AUTO_INCREMENT PRIMARY KEY,
tytul VARCHAR(100) NOT NULL,
rok SMALLINT NOT NULL,
gatunek VARCHAR(40) NOT NULL,
cena_grosze INT NOT NULL CHECK (cena_grosze >= 0),
wlasciciel_id INT NULL,
FOREIGN KEY (wlasciciel_id) REFERENCES Uzytkownicy(id)
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS Zamowienia (
id INT AUTO_INCREMENT PRIMARY KEY,
uzytkownik_id INT NOT NULL,
klucz CHAR(64) NOT NULL UNIQUE,
suma_grosze INT NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'symulacja',
utworzono TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (uzytkownik_id) REFERENCES Uzytkownicy(id)
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS Pozycje (
zamowienie_id INT NOT NULL,
film_id INT NOT NULL,
ilosc SMALLINT NOT NULL CHECK (ilosc BETWEEN 1 AND 10),
cena_grosze INT NOT NULL,
PRIMARY KEY (zamowienie_id, film_id),
FOREIGN KEY (zamowienie_id) REFERENCES Zamowienia(id),
FOREIGN KEY (film_id) REFERENCES Filmy(id)
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS ProbyLogowania (
klucz CHAR(64) PRIMARY KEY,
liczba INT NOT NULL DEFAULT 0,
od_kiedy BIGINT NOT NULL
) ENGINE=InnoDB;
INSERT INTO Filmy (id,tytul,rok,gatunek,cena_grosze) VALUES
(1,'Podróż',2024,'przygodowy',1299), (2,'Kosmos',2023,'science fiction',1599),
(3,'Dom',2025,'familijny',999)
ON DUPLICATE KEY UPDATE id=id;
Punkt wejścia kompletnego projektu
<?php
require_once dirname(__DIR__).'/src/db.php';
$szukaj = trim(pole($_GET, 'szukaj'));
if (mb_strlen($szukaj) > 100) {
http_response_code(422);
exit('Wpisz do 100 znaków.');
}
$wzorzec = '%'.$szukaj.'%';
$zapytanie = $polaczenie->prepare('SELECT id, tytul, rok, gatunek, cena_grosze FROM Filmy WHERE tytul LIKE ? ORDER BY rok DESC, id');
$zapytanie->bind_param('s', $wzorzec);
$zapytanie->execute();
$filmy = $zapytanie->get_result()->fetch_all(MYSQLI_ASSOC);
naglowek('Wybierz historię na dziś.');
?>
<form method="get" action="/index.php">
<label for="szukaj">Szukaj tytułu</label>
<input id="szukaj" name="szukaj" maxlength="100" value="<?= h($szukaj) ?>">
<button>Szukaj</button>
</form>
<p><a href="/rejestracja.php">Utwórz konto</a> · <a href="/logowanie.php">Zaloguj się</a></p>
<div class="karty">
<?php foreach ($filmy as $film) { ?>
<article><span><?= (int) $film['rok'] ?> / <?= h($film['gatunek']) ?></span>
<h2><?= h($film['tytul']) ?></h2><p><?= pieniadze((int) $film['cena_grosze']) ?></p>
<form action="/koszyk.php" method="post">
<?= tokenFormularza() ?>
<input type="hidden" name="id" value="<?= (int) $film['id'] ?>">
<button name="akcja" value="dodaj">Dodaj do koszyka</button>
</form>
</article>
<?php } ?>
</div>
<?php
if (! $filmy) {
echo '<p>Brak filmów spełniających warunek.</p>';
}
stopka();
Pobierz i odtwórz projekt
W „Źródłach i materiałach” na końcu pobierz KINO / LAB.zip. Rozpakuj całość; instrukcja START.txt opisuje konfigurację. Pierwsze 6 przykładów w public/podstawy działa bez bazy. Dalsze wymagają lokalnej MariaDB 11.4, importu schema.sql i lokalnego konta bazy z minimalnymi prawami. Gotowa paczka pokazuje całość, ale w tej lekcji wykonaj opisane kroki i własne zadanie.
Ćwiczenie z pomocą · samodzielna praca
Twoja próba — podpowiedzi otwieraj po kolei
Rozbuduj serwis o filtr gatunku i stronę szczegółów filmu. Oddaj ZIP bez konfiguracji lokalnej oraz kartę co najmniej 10 prób.
Projekt odtwarza się z plików i schema.sql; kontrole dostępu nadal działają; karta testów podaje wyniki.
? Potrzebujesz pomocy? Rozwiązuj krok po kroku.
Krok 1 — pokaż podpowiedź
Odszukaj wskazany plik i zapisz przewidywany rezultat przed zmianą.
Krok 2 — pokaż podpowiedź
Zmień jedną rzecz. Sprawdź metodę żądania, dane i odpowiedź lub wynik programu.
Krok 3 — pokaż podpowiedź
Wykonaj próbę graniczną lub błędną. Porównaj także stan bazy lub sesji, nie tylko komunikat.
Otwórz tylko tyle podpowiedzi, ile potrzebujesz. Po każdym kroku wróć do zadania i sprawdź wynik.
Podsumowanie
Co warto zapamiętać
- Uruchomisz i przetestujesz cały projekt, wyjaśniając drogę danych i własne decyzje.
- Potrafisz wskazać w kodzie przyczynę wyniku i zaplanować próbę błędnych danych.
- Nie oddajesz haseł, pliku config.local.php ani wartości cookie.
Dalej na swoim koncie
Rozgrzewki, quiz i zadania są w biurku ucznia.
Teoria jest otwarta dla wszystkich. Żeby rozwiązywać zadania, zapisywać szkice i widzieć swój postęp, zaloguj się.
Zaloguj się →