INF.03-05-26.06-SGCzerwiec 2026 · 53 kroki · ok. 99 min · 40 kryteriów z klucza
Rozwiązanie krok po kroku: Witryna sklepu z antykami (Nowoczesny kod (grid, PDO))
To drugi sposób rozwiązania tego samego arkusza. Wynik jest ten sam - baza antyki z czterema zapytaniami, kadr czerwonego fotela, strona index.php z trzema kategoriami mebli, przyciskiem „KUP” i koszykiem - i tak samo spełnia każde kryterium klucza. Inna jest droga:
siatka CSS: blok sekcji i blok boczny w grid-template-columns: 70% 30%, kolor LightCoral w zmiennej CSS,
PHP na PDO, logika przed HTML-em: na początku pliku obsługa zakupu i wszystkie odczyty z bazy przez małe funkcje z zapytaniami przygotowanymi, a niżej szablon strony, który tylko wypisuje gotowe tablice,
jeden szablon mebla w funkcji showFurniture() wołanej pod każdym nagłówkiem zamiast trzech kopii Skryptu 1,
bezpieczeństwo: numer mebla sprawdzony przez filter_input i w bazie, przekierowanie po zakupie (odświeżenie strony nie kupuje drugi raz), htmlspecialchars przy każdym wypisaniu.
Na końcu krótkie porównanie z wersją podstawową (zakładka obok). Baza i zdjęcia pochodzą z prawdziwego archiwum pliki5, a podgląd kroków z PHP to wynik prawdziwego uruchomienia strony w PHP 8.2 na bazie z arkusza - z pustym koszykiem jak na ilustracji 2, a od kroków z koszykiem w stanie z ilustracji 6.
Jak liczą się punkty. Klucz ma 40 kryteriów (R.1.1 - R.5.10), każde za 1 punkt. Próg zdania to 75%, czyli 30 punktów. Przy każdym kroku jest napisane, które kryteria spełnia.
Suwak i strzałki obok niego - albo ←→ na klawiaturze - przechodzą krok po kroku; wyjaśnienie, pliki i podgląd zmieniają się razem z krokiem.
Lista „Etap” przenosi od razu do wybranego etapu.
„Rozwiązuję samodzielnie” chowa rozwiązanie kroku - najpierw własna próba, potem odsłonięcie.
„Tryb prezentacji” pokazuje same kroki, bez menu strony - na rzutnik.
Etap 1: Baza danych
Kroki 1-7 · po tym etapie masz 6 z 40 kryteriów (pkt)
Krok 1: Baza danych antyki
ok. 2 min
Polecenie z arkusza
Utwórz bazę danych o nazwie antyki, z zestawem polskich znaków (np. utf8_polish_ci)
Archiwum pliki5.7z rozpakuj hasłem Twoje@ntyki&; foldery sofy, fotele i komody przenieś w całości - arkusz zaznacza, że obrazy mają zostać w folderach. W phpMyAdmin Nowa → antyki, porównywanie utf8_polish_ci:
CREATE DATABASE antyki CHARACTER SET utf8 COLLATE utf8_polish_ci;
Krok 2: Import tabel z pliku antyki.sql
ok. 2 min
Polecenie z arkusza
Do utworzonej bazy zaimportuj tabele z pliku antyki.sql
Baza antyki → Import → antyki.sql. Trzy tabele z ilustracji 1: klienci, meble (11 mebli w trzech kategoriach) i pusta zakupy - tabela pośrednicząca relacji m:n.
Krok 3: Zrzut ekranu import.png
Kryteria:
R.1.1 Wykonany import tabel do bazy danych antyki - plik import.png
ok. 2 min
Polecenie z arkusza
Wykonaj zrzut ekranu po imporcie. Zapisz zrzut w formacie PNG pod nazwą import. Nie kadruj zrzutu. Powinien on obejmować cały ekran monitora, z widocznym paskiem zadań. Na zrzucie powinny być widoczne elementy wskazujące na poprawnie wykonany import tabel
Cały ekran z paskiem zadań, zapis jako import.png.
Typowe błędy
Wykadrowany zrzut nie jest oceniany.
Krok 4: Zapytanie 1: sofy
Kryteria: ,
R.1.2 W pliku kwerendy.txt zapisane co najmniej jedno zapytanie SQL, wynikające z treści zadania
R.1.3 Wykonane zapytanie 1 wybierające jedynie pola idMeble, nazwa, plik, styl, cena i opis z tabeli meble dla kategorii 1. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT idMeble, nazwa, plik, styl, cena, opis FROM meble WHERE kategoria = '1'; oraz na zrzucie widoczne są dokładnie 3 rekordy z id 1, 2, 3
ok. 2 min
Polecenie z arkusza
Zapytanie 1: wybierające jedynie pola idMeble, nazwa, plik, styl, cena i opis z tabeli meble dla kategorii 1
Sześć pól i warunek kategoria = 1, każda klauzula w osobnej linii. Wynik: sofy o identyfikatorach 1, 2, 3. W skrypcie to samo zapytanie dostanie zamiast 1 parametr - i obsłuży wszystkie trzy kategorie. Zapytanie do kwerendy.txt, zrzut kw1.png.
R.1.4 Wykonane zapytanie 2 wstawiające rekord do tabeli zakupy o wartościach identyfikator klienta: 1, identyfikator mebla: 6, jedna sztuka. Klucz główny nadawany automatycznie. W pliku z kwerendami lub na zrzucie istnieje zapis np.: INSERT INTO `zakupy`(`idKlienci`, `idMeble`, `sztuk`) VALUES ('1','6', '1'); (klucz główny pominięty lub NULL) oraz na zrzucie widoczny jest komunikat o pomyślnym dodaniu rekordu lub rekord
ok. 2 min
Polecenie z arkusza
Zapytanie 2: wstawiające rekord do tabeli zakupy o wartościach identyfikator klienta: 1, identyfikator mebla: 6, jedna sztuka. Klucz główny nadawany automatycznie
Lista kolumn pomija klucz id - nada go AUTO_INCREMENT. Liczby bez apostrofów, bo kolumny są liczbowe. Zrzut z komunikatem albo z tabelą: kw2.png.
Typowe błędy
Klucz wpisany ręcznie przeczy wymaganiu „klucz główny nadawany automatycznie”.
R.1.5 Wykonane zapytanie 3 wybierające jedynie nazwę i cenę mebli, które zostały zakupione przez klienta o identyfikatorze równym 1. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa, cena FROM zakupy JOIN meble USING(idMeble) WHERE idKlienci=1; (lub porównanie kluczy po ON lub WHERE ) oraz na zrzucie widoczne są tylko rekordy dla idKlienci = 1, liczba rekordów zależna od zawartości tabeli zakupy
ok. 3 min
Polecenie z arkusza
Zapytanie 3: wybierające jedynie nazwę i cenę mebli, które zostały zakupione przez klienta o identyfikatorze równym 1. Należy posłużyć się relacją
Aliasy tabel (z, m) i warunek złączenia w ON: klucz obcy z.idMeble równa się kluczowi głównemu m.idMeble. Przedrostki są tu konieczne, bo kolumna idMeble jest w obu tabelach - USING(idMeble) z klucza robi to samo krócej, ON działa także wtedy, gdy kolumny nazywają się różnie. Wynik po zapytaniu 2: Barokowa komoda, 4000. Zrzut kw3.png.
kwerendy.txtTekstzmienione: 6 linii
-- Zapytanie 1SELECT idMeble, nazwa, plik, styl, cena, opisFROM mebleWHERE kategoria = 1;-- Zapytanie 2INSERT INTO zakupy (idKlienci, idMeble, sztuk)VALUES (1, 6, 1);-- Zapytanie 3SELECT m.nazwa, m.cenaFROM zakupy AS zJOIN meble AS m ON m.idMeble = z.idMebleWHERE z.idKlienci = 1;
Podgląd strony po tym kroku
Krok 7: Zapytanie 4: mebel i kupujący (relacja m:n)
Kryteria:
R.1.6 Wykonane zapytanie 4 wybierające jedynie nazwę mebla oraz odpowiadające mu imię i nazwisko kupującego klienta. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa, imie, nazwisko FROM zakupy JOIN meble USING(idMeble) JOIN klienci USING(idKlienci); (lub porównanie kluczy po ON lub WHERE ) oraz na zrzucie widoczne są rekordy w zależności od zawartości tabeli zakupy
ok. 3 min
Polecenie z arkusza
Zapytanie 4: wybierające jedynie nazwę mebla oraz odpowiadające mu imię i nazwisko kupującego klienta. Należy posłużyć się relacją m:n
Tabela pośrednicząca zakupy w środku i dwa złączenia: z meblami i z klientami. Wynik: Barokowa komoda, Anna Kowalska. Zrzut kw4.png.
kwerendy.txtTekstzmienione: 6 linii
-- Zapytanie 1SELECT idMeble, nazwa, plik, styl, cena, opisFROM mebleWHERE kategoria = 1;-- Zapytanie 2INSERT INTO zakupy (idKlienci, idMeble, sztuk)VALUES (1, 6, 1);-- Zapytanie 3SELECT m.nazwa, m.cenaFROM zakupy AS zJOIN meble AS m ON m.idMeble = z.idMebleWHERE z.idKlienci = 1;-- Zapytanie 4SELECT m.nazwa, k.imie, k.nazwiskoFROM zakupy AS zJOIN meble AS m ON m.idMeble = z.idMebleJOIN klienci AS k ON k.idKlienci = z.idKlienci;
Podgląd strony po tym kroku
Etap 2: Grafika i strona HTML
Kroki 8-19 · po tym etapie masz 12 z 40 kryteriów (pkt)
Krok 8: Grafika f3.png: kadr czerwonego fotela
ok. 3 min
Polecenie z arkusza
Cechy grafiki f3.png: Grafikę należy skadrować tak, aby zawierała tylko czerwony fotel – trzeci od lewej strony
W GIMP-ie zaznaczenie prostokątne wokół trzeciego, czerwonego fotela, Obraz → Przytnij do zaznaczenia, potem Obraz → Przytnij do zawartości - odcina przezroczysty margines.
Podgląd strony po tym kroku
Krok 9: Grafika f3.png: wysokość 640 px i płótno 524 px
Kryteria:
R.2.1 Grafika f3.png ma szerokość 524 px i wysokość 640 px i zawiera jedynie czerwony fotel
ok. 4 min
Polecenie z arkusza
Po skadrowaniu należy zmienić wymiary grafiki na 524 px szerokości i 640 px wysokości. Proporcje muszą być zachowane, fotel musi znajdować się na środku grafiki (np. poprzez przeskalowanie grafiki z zachowaniem proporcji do wysokości 640 px, a następnie zmianę wymiarów płótna do szerokości 524 px z wyśrodkowaniem)
Skaluj obraz do wysokości 640 px przy zamkniętym łańcuchu, potem Rozmiar płótna - szerokość 524 px i Wyśrodkuj. Warstwa do rozmiaru obrazu i eksport z nadpisaniem fotele/f3.png.
Typowe błędy
Skalowanie od razu do 524 × 640 przy rozpiętym łańcuchu rozciąga fotel.
Podgląd strony po tym kroku
Krok 10: Plik index.php w HTML5
ok. 1 min
Polecenie z arkusza
Składa się ze strony o nazwie index.php. Zapisana w języku HTML5
Plik index.php w folderze z rezultatami, obok folderów z obrazami. Na razie sam szkielet HTML5; kod PHP stanie później przed<!DOCTYPE html>.
Jawnie zastosowany właściwy standard kodowania polskich znaków
<meta charset="UTF-8">. To samo kodowanie ustawi połączenie z bazą (charset=utf8mb4), więc polskie litery przejdą bez zmian: baza → PHP → przeglądarka.
R.2.4 W sekcji body zdefiniowany jest podział na bloki za pomocą semantycznych znaczników HTML5: header, main -> section + aside, footer (dopuszcza się stosowanie bloków pomocniczych)
ok. 2 min
Polecenie z arkusza
Podział strony na bloki: nagłówkowy, główny i stopkę. Blok główny zawiera blok sekcji i blok boczny. Podział zrealizowany za pomocą semantycznych znaczników języka HTML5 tak, aby po uruchomieniu w przeglądarce układ bloków był zgodny z ilustracją 4
header, main z section i aside, footer - lista z klucza R.2.4. Obok siebie ustawi je siatka CSS.
Zawartość bloku sekcji: Nagłówek drugiego stopnia o treści: „- Sofy -”. Nagłówek drugiego stopnia o treści: „- Fotele -”. Nagłówek drugiego stopnia o treści: „- Komody -”
Nagłówki zostają w HTML-u, a nie w tablicy PHP: są stałym tekstem strony i egzaminator znajdzie je w kodzie nawet wtedy, gdy PHP nie zadziała.
R.2.5 W bloku nagłówkowym znajduje się <h1>: "Najlepsze antyki w mieście", w bloku sekcji <h2>: "- Sofy -", "- Fotele -" i "- Komody -", w bloku bocznym <h2>: "Koszyk" i <p> "Zalogowano: Anna Kowalska" oraz <p> z numerem zdającego w stopce
ok. 1 min
Polecenie z arkusza
Zawartość bloki stopki: paragraf o treści „Stronę wykonał: ”, dalej wstawiony numer zdającego
Na egzaminie po dwukropku wpisujesz swój numer zdającego (PESEL); tutaj stoi imię i nazwisko autora rozwiązania.
Domyślnie, dla wszystkich selektorów: krój czcionki Georgia
LightCoral występuje w arkuszu dwa razy (blok mebla, przycisk), więc trafia do zmiennej CSS--coral w :root; odwołanie var(--coral). Potem selektor * z krojem.
R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z ilustracją 4 w arkuszu egzaminacyjnym (prawidłowo zastosowane właściwości CSS decydujące o układzie bloków na stronie, np. float i clear albo display flex albo grid albo position; żaden blok nie zachodzi na inne bloki - sprawdzić w inspektorze) nie należy brać pod uwagę bloków wewnątrz sekcji
ok. 2 min
Polecenie z arkusza
Podział zrealizowany za pomocą semantycznych znaczników języka HTML5 tak, aby po uruchomieniu w przeglądarce układ bloków był zgodny z ilustracją 4
Blok główny jako siatka z dwiema kolumnami 70% i 30% - to szerokości bloku sekcji i bloku bocznego z arkusza zapisane w jednym miejscu. Klucz R.4.4 dopuszcza je w grid-template-columns, a R.3.1 układ na grid. Sekcja i blok boczny same trafiają do kolejnych kolumn, a stopka zostaje pod blokiem głównym bez clear.
Krok 24: Blok sekcji: wysokość i suwaki zawsze widoczne
Kryteria:
R.4.5 paski przewijania: sekcja - scroll
ok. 2 min
Polecenie z arkusza
Dla bloku sekcji: szerokość 70%, wysokość 700 px, suwaki do przewijania zawartości bloku zawsze widoczne
Szerokość 70% daje już kolumna siatki. „Zawsze widoczne” to overflow: scroll - suwaki są nawet przy treści, która się mieści (auto pokazałby je tylko w razie potrzeby).
R.4.4 wysokość: blok sekcji 700px szerokość: blok sekcji 70%, blok boczny 30% (width lub grid-template-columns lub flex ), selektor img 90%
ok. 1 min
Polecenie z arkusza
Dla selektora obrazu: szerokość 90%, wyrównanie tekstu do środka
Selektor znacznika img. Szerokość liczy się od bloku obrazu. text-align na obrazie niczego nie przesuwa (obraz nie ma treści), ale arkusz go wymienia, więc zostaje.
Dla klasy obraz (skrypt 1): szerokość 30%, opływanie z prawej strony
Tekst ma płynąć po prawej stronie zdjęcia, czyli blok pływa w lewo: float: left. Tu siatka nie zastąpi float: klucz R.4.9 podaje tę właściwość wprost, a „opływanie” to dosłownie zadanie dla float - informacje obok zdjęcia, opis pod spodem.
Typowe błędy
float: right stawia zdjęcie po prawej - odwrotnie niż na ilustracji 5.
R.4.9 styl klasy .obraz: szerokość 30%, opływanie po prawej stronie - float: left styl klasy .opis: tekst znajduje się pod blokami obrazu i informacji, na całej szerokości bloku nadrzędnego (np. clear: both; lub width: 100%)
ok. 1 min
Polecenie z arkusza
Dla klasy opis (skrypt 1): znajduje się pod blokami obrazu i informacji, na całej szerokości bloku nadrzędnego
clear: both - opis zaczyna się pod pływającym obrazem, na całej szerokości bloku mebla, a blok mebla obejmuje zdjęcie w całości.
Kroki 34-51 · po tym etapie masz 40 z 40 kryteriów (pkt)
Krok 34: Połączenie PDO na początku pliku
Kryteria:
R.5.2 Zastosowana przynajmniej jedna zmienna oraz nazewnictwo jest w języku polskim lub angielskim i odzwierciedla cel zastosowania
ok. 3 min
Polecenie z arkusza
Łączą się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie antyki
Kod PHP stoi na samym początkuindex.php, przed <!DOCTYPE html>: najpierw cała praca z bazą, potem szablon, który tylko wypisuje gotowe dane. Klucz R.5 przyjmuje PDO tak samo jak MySQLi.
new PDO(...) - połączenie i wybór bazy antyki: pierwszy argument to DSN, czyli adres bazy dla PDO w jednym napisie (mysql:host=localhost;dbname=antyki;charset=utf8mb4 - serwer, nazwa bazy i kodowanie połączenia), dalej użytkownik root i puste hasło,
ERRMODE_EXCEPTION zamienia każdy błąd SQL w wyjątek, FETCH_ASSOC daje wiersze jako tablice z nazwami kolumn ($item['nazwa']),
try … catch (PDOException $error) - gdy baza nie działa, kupujący widzi zwykły komunikat zamiast treści błędu z nazwą serwera i ścieżkami.
Od tego kroku podgląd to prawdziwy wynik strony z serwera.
Typowe błędy
Klucz sprawdza połączenie i wybór bazy antyki w kodzie źródłowym strony (R.5.1, razem z zamknięciem na końcu), więc DSN z nazwą bazy stoi w index.php, a nie w dołączanym pliku.
Krok 35: Skrypt 1a: zapytanie 1 z parametrem i blok mebla dla każdego wiersza
Kryteria:
R.5.3 Skrypt 1 zawiera instrukcję wysyłającą do bazy danych zapytanie 1 lub Skrypt 2 - zapytanie 2, lub Skrypt 3 - zapytanie 3
ok. 5 min
Polecenie z arkusza
Pierwszy wariant skryptu – 1a – wysyła do bazy danych zapytanie 1. Każdy zwrócony zapytaniem wiersz jest wypisywany w osobnym bloku formatowanym klasą mebel, według schematu (ilustracja 5)
Trzy warianty Skryptu 1 różnią się tylko numerem kategorii, więc zamiast trzech kopii są dwie funkcje:
fetchFurniture($db, $category) wysyła zapytanie 1 jako zapytanie przygotowane: w miejscu 1 stoi parametr :category, a wartość idzie osobno w execute. Typy przy parametrach (PDO, int) i wyniku (array) mówią, czego funkcja oczekuje. Zmienna $query żyje tylko w funkcji - to ważne przy zamykaniu połączenia,
stała FOLDERS łączy numer kategorii z folderem obrazów, a pętla foreach pobiera meble wszystkich trzech kategorii do tablicy $furniture,
showFurniture($items, $folder) to szablon: między ?> i <?php stoi zwykły HTML bloku mebla, powtórzony dla każdego wiersza (foreach … endforeach).
Pod nagłówkiem „- Sofy -” jedno wywołanie. W podglądzie trzy puste bloki mebli - styl .mebel czekał już w CSS.
Krok 36: Skrypt 1a: blok obrazu z folderu kategorii
ok. 3 min
Polecenie z arkusza
Blok obrazu: Formatowany klasą obraz. Wyświetla obraz z pliku o nazwie pobranej z bazy danych i ścieżce dostępu odpowiednio do kategorii: sofy/, fotele/, komody/ oraz tekście alternatywnym „mebel”
W bazie jest sama nazwa pliku, a ścieżkę składa folder kategorii i nazwa: sofy/s1.png. Zapis <?= ... ?> to skrót od <?php echo ... ?>.
Każdy napis z bazy przechodzi przez e() (od escape): htmlspecialchars z ENT_QUOTES zamienia <, >, & i oba cudzysłowy na encje. Napis z bazy, w którym ktoś zapisał "><script>, wyświetli się jako tekst, a nie wykona (ochrona przed XSS). Escapuje się na wyjściu, tuż przed wypisaniem - w bazie zostają dane surowe.
R.2.10 Na stronie znajdują się znaczniki <h3> i <h4> (w skrypcie lub HTML )
ok. 1 min
Polecenie z arkusza
W nagłówku trzeciego stopnia cenę w postaci „CENA: <cena> zł”. Patrz ilustracje 2, 5
PDO zwraca wartości z MariaDB jako napisy, więc e() przyjmuje też cenę („1000”). Kolumna jest typu decimal(10,0) - bez miejsc po przecinku, jak na ilustracji 2.
R.2.7 W bloku sekcji znajduje się przynajmniej jeden formularz z przyciskiem "KUP". Domknięcie </form> zaraz za przyciskiem (w HTML lub skrypcie )
R.3.3 Przynajmniej jeden formularz wysyła dane metodą POST do tego samego pliku (poprawne również bez atrybutu action )
R.3.4 Przynajmniej jeden przycisk realizuje funkcję wysłania danych z formularza (input lub button z typem submit lub button bez typu ) oraz ma przypisany atrybut value
ok. 2 min
Polecenie z arkusza
Formularz wysyłający dane do tego samego pliku metodą bezpieczną z jednym przyciskiem „KUP”. Przycisk wysyła dane, ma przypisany atrybut wartości równy polu idMeble z zapytania. Patrz tabela 2.
Formularz method="post" bez action - dane wracają do tego samego pliku. Przycisk bez typu wysyła formularz; name="buy" to klucz w $_POST, a value - identyfikator mebla. </form> zaraz za przyciskiem, jak wymaga klucz.
R.2.6 W bloku sekcji znajduje się przynajmniej jeden blok z przypisaną klasą mebel , zawierający trzy bloki, z czego pierwszy z klasą obraz, ostatni z klasą opis (w HTML lub skrypcie)
R.5.5 Na stronie wyświetlany jest przynajmniej jeden rekord zwrócony zapytaniem: nazwa, styl - "styl <styl>", cena - "CENA: <cena> zł" i opis
ok. 1 min
Polecenie z arkusza
Blok opisu: Jest formatowany klasą opis. Wyświetla zwrócony z bazy danych opis
Trzeci blok z klasą opis - clear: both stawia go pod zdjęciem. Podgląd: trzy sofy jak na ilustracji 2.
Drugi wariant skryptu – 1b – wysyła do bazy danych zapytanie 1 zmodyfikowane tak, że sprawdzana jest kategoria równa 2
Zapytanie dla kategorii 2 wykonała już pętla na początku pliku (to samo zapytanie przygotowane z :category = 2), więc pod „- Fotele -” wystarczy jedno wywołanie szablonu z folderem fotele.
R.5.4 Skrypt 1 wyświetla przynajmniej po jednym obrazie z każdego z podfolderów: sofy/, fotele/, komody/ z tekstem alternatywnym "mebel"
R.5.6 Na stronie wyświetlane są informacje ze wszystkich rekordów tabeli meble , podzielone na trzy kategorie (wymagana liczba zwróconych rekordów > 0)
ok. 1 min
Polecenie z arkusza
Trzeci wariant skryptu – 1c – wysyła do bazy danych zapytanie 1 zmodyfikowane tak, że sprawdzana jest kategoria równa 3
Trzecie wywołanie. Na stronie jest teraz wszystkie 11 mebli z bazy, każdy z obrazem z właściwego folderu. Poprawka w szablonie bloku mebla zmienia od razu wszystkie trzy kategorie.
Krok 44: Skrypt 2: tylko po kliknięciu „KUP”, numer sprawdzony
ok. 3 min
Polecenie z arkusza
Skrypt 2 obsługujący dane z formularza: Jeżeli zostały wysłane dane z formularza
Obsługa zakupu stoi przed odczytem mebli i koszyka: koszyk pokaże już nowy zakup. Warunek $_SERVER['REQUEST_METHOD'] === 'POST' sprawdza, czy strona przyszła z formularza.
Wartość przycisku jest w kodzie strony, ale żądanie da się wysłać z dowolną wartością. filter_input(INPUT_POST, 'buy', FILTER_VALIDATE_INT, ...) zwraca liczbę całkowitą tylko wtedy, gdy wysłano liczbę od 1 w górę - dla abc, 0, -5 albo 6 OR 1=1 daje false, a gdy pola brak - null. Sprawdzanie wejścia to pierwsza warstwa ochrony.
R.5.7 Skrypt 2 wstawia rekord do bazy danych dla idMeble odpowiadającego meblowi, dla którego wybrano przycisk (sprawdzić w phpMyAdmin przynajmniej dla dwóch mebli z różnych kategorii)
ok. 5 min
Polecenie z arkusza
Wysyła do bazy danych zapytanie 2, zmodyfikowane w ten sposób, że w miejscu identyfikatora mebla zostanie umieszczona wartość atrybutu value z przycisku „KUP” (wciśnięty przycisk przy niebieskim fotelu – identyfikator 9), zostanie przesłana wartość 9. Zakupy są uproszczone – kupuje zawsze klient o identyfikatorze 1, kupowana jest zawsze 1 sztuka
Dwie nowe funkcje z zapytaniami przygotowanymi:
furnitureExists() - druga warstwa ochrony: liczba może być poprawna, a mebla o takim numerze może nie być (99). Zapytanie COUNT(*) z parametrem, fetchColumn() zwraca pierwszą kolumnę pierwszego wiersza,
addPurchase() - zapytanie 2 z parametrami :client i :furniture w miejscu 1 i 6. Klient to stała CLIENT_ID = 1, liczba sztuk zostaje 1.
Po zapisie header('Location: index.php', true, 303) i exit - wzorzec PRG (Post/Redirect/Get): przeglądarka dostaje polecenie wczytania strony od nowa zwykłym GET. Bez tego odświeżenie strony (F5) wysłałoby formularz drugi raz i kupiło mebel podwójnie. Przekierowanie działa, bo przed nim nic nie zostało wypisane - cały kod PHP stoi przed HTML-em.
Podgląd: stan po kliknięciu „KUP” przy niebieskim fotelu - w tabeli zakupy przybył rekord z idMeble = 9 (sprawdzone zapytaniem po uruchomieniu).
Typowe błędy
header() po jakimkolwiek wypisanym znaku (nawet spacji przed <?php) kończy się ostrzeżeniem „headers already sent” i przekierowanie nie działa.
Gdy numer nie przeszedł sprawdzenia, zakup się nie odbywa, a pod komodami pojawia się akapit z komunikatem - w miejscu, które arkusz przeznacza na „efekt działania skryptu 2”. Po udanym zakupie nie ma czego wyświetlać: zakup widać w koszyku.
Sprawdzone na serwerze: buy równe abc, 0, -5, 99, 6 OR 1=1, puste pole i <script> - za każdym razem komunikat, zero wierszy w tabeli zakupy i żadnego ostrzeżenia PHP. W wersji bez sprawdzania abc i 99 przerywały stronę błędem bazy.
R.2.8 W bloku bocznym znajduje się jedna lista numerowana (znacznik <ol>; w skrypcie lub HTML )
ok. 1 min
Polecenie z arkusza
Skrypt 3: Tworzy listę numerowaną (uporządkowaną)
<ol> stoi w szablonie jako zwykły HTML - PHP wypisze tylko elementy. Od tego kroku podgląd pokazuje stan z ilustracji 6: wcześniej kupiono niebieski fotel i sofę 2-osobową, teraz kliknięto „KUP” przy barokowej komodzie.
R.5.8 Elementy listy w koszyku są pobrane z bazy danych: nazwy i ceny mebli. Jeżeli lista jest pusta sprawdzić w kodzie PHP
ok. 2 min
Polecenie z arkusza
Każdy zwrócony zapytaniem wiersz wypisuje jako element listy o treści „<nazwa>, cena: <cena>”, gdzie <> oznacza zwrócone dane.
Pętla w szablonie, jeden <li> na zakup: „Fotel niebieski, cena: 800”, „Sofa 2-osobowa, cena: 1200”, „Barokowa komoda, cena: 4000” - jak na ilustracji 6. Pusty koszyk to pusta tablica: pętla nie wypisze nic, jak na ilustracji 2.
R.2.9 W bloku bocznym pod listą znajduje się napis "Koszt całkowity: " (w skrypcie lub HTML )
R.5.9 Skrypt 3 zawiera instrukcje obliczające całkowity koszt poprzez zsumowanie wszystkich cen zwróconych zapytaniem
R.5.10 Obliczony koszt całkowity jest wyświetlony na stronie
ok. 2 min
Polecenie z arkusza
Skrypt na podstawie zwróconych cen oblicza koszt całkowity koszyka. Wartość jest wyświetlona pod listą, w postaci „Koszt całkowity: <koszt>” (ilustracja 6). Gdy koszyk jest pusty to koszt całkowity wynosi 0 (ilustracja 2).
array_column($cart, 'cena') wyciąga z koszyka same ceny, a array_sum je dodaje: 800 + 1200 + 4000 = 6000. Dla pustego koszyka suma pustej tablicy to 0 - bez osobnej zmiennej startowej i bez ostrzeżenia. Wynik wypisuje akapit pod listą. Liczba wyliczona w PHP nie pochodzi od użytkownika, więc nie potrzebuje e().
R.5.1 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy antyki oraz ostatnią operacją na bazie jest jej zamknięcie
ok. 2 min
Polecenie z arkusza
Na końcu należy zamknąć połączenie z bazą
$db = null; zaraz po ostatnim zapytaniu zamyka połączenie PDO - to ostatnia operacja na bazie (R.5.1), a szablon używa już tylko gotowych tablic. Połączenie zamyka się naprawdę dopiero, gdy nie zostaje żaden obiekt zapytania, który z niego korzysta - dlatego zapytania żyją w funkcjach ($query znika po return), a nie w zmiennych globalnych.
Typowe błędy
$db->close() nie istnieje w PDO - skończy się błędem „Call to undefined method”.
Krok 52 · po tym etapie masz 40 z 40 kryteriów (pkt)
Krok 52: Co jest inaczej i którą wersję wybrać na egzaminie
ok. 2 min
Obie wersje dają komplet 40 punktów. Różnice:
wersja podstawowa
wersja nowoczesna
układ
display: flex na <main>, szerokości w width
grid-template-columns: 70% 30%
połączenie
mysqli_connect w treści strony
PDO w treści strony, przed HTML-em
Skrypt 1
trzy kopie pętli while z echo
jedno zapytanie przygotowane w funkcji, szablon w funkcji
wypisywanie
echo "<div class=\"mebel\">"
HTML w szablonie, <?= e(...) ?>
Skrypt 2
wartość z $_POST sklejona z SQL
filter_input, sprawdzenie w bazie, parametr, PRG
suma
pętla z $total = $total + ...
array_sum(array_column(...))
Klucz wymuszafloat: left dla bloku obrazu (R.4.9) - obie wersje go mają. Na egzaminie wersja podstawowa wymaga mniej pisania i każdy wariant Skryptu 1 stoi dokładnie tam, gdzie mówi arkusz. Nowoczesna ma więcej kodu na starcie, ale poprawka bloku mebla to jedna zmiana zamiast trzech, a złośliwe dane z formularza nie wywracają strony. Poza egzaminem dane logowania do bazy trzyma się zwykle w osobnym pliku (np. config.php), żeby hasło nie trafiało do repozytorium razem z kodem - tu stoją w index.php, bo klucz sprawdza połączenie w kodzie strony. Kolejne warstwy, których klucz nie wymaga: token CSRF w formularzu (zakup tylko ze strony sklepu, nie z obcej strony podsuwającej formularz) i zalogowany klient zamiast stałej CLIENT_ID.
Krok 53 · po tym etapie masz 40 z 40 kryteriów (pkt)
Krok 53: Plik przeglądarka.txt i nagranie płyty
ok. 5 min
Polecenie z arkusza
Po zakończeniu pracy utwórz plik tekstowy o nazwie przeglądarka.txt. Zapisz w nim nazwę przeglądarki internetowej, w której weryfikowana była poprawność działania witryny. Umieść go w folderze z numerem zdającego. Nagraj płytę z rezultatami pracy.
W folderze z numerem zdającego: foldery sofy, fotele (z przerobionym f3.png) i komody, a także import.png, index.php, kw1.png … kw4.png, kwerendy.txt, przeglądarka.txt, styl.css. Płytę sprawdź po nagraniu.
Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.
To jedno z możliwych rozwiązań. Egzaminator ocenia spełnienie kryteriów z klucza odpowiedzi, a nie zgodność z tym kodem - inna, poprawna droga też daje punkty.
Autor rozwiązania: Bartosz Bryniarski. Licencja CC BY-NC 4.0 - kopiowanie i przeróbki do celów niekomercyjnych, z podaniem autora i źródła (link do tej strony) (zasady wykorzystania). Treść arkusza i klucza oceniania pochodzi z CKE.