INF.03-12-25.01-SGStyczeń 2025 · 34 kroki · ok. 83 min · 40 kryteriów z klucza
Rozwiązanie krok po kroku: Witryna internetowa piekarni (Nowoczesny kod (PDO, zapytania przygotowane))
To drugi sposób rozwiązania tego samego arkusza. Wynik jest ten sam - baza piekarnia z czterema zapytaniami, grafika wypieków na tylnym planie i strona piekarnia.php z listą rodzajów wypieków i tabelą wyrobów wybranego rodzaju - i tak samo spełnia każde kryterium klucza. Inna jest droga:
CSS: kolory z arkusza w zmiennych (:root), margin-inline dla „jedynie lewego i prawego” marginesu; bloki stoją jeden pod drugim, więc nie potrzebują ani float, ani siatki,
HTML: lista rozwijana powiązana z nagłówkiem nad nią (czytnik ekranu powie, czego dotyczy), tabela z <thead> i <tbody>, data w znaczniku <time>,
PHP na PDO: oba zapytania na górze pliku, połączenie zamknięte, zanim zacznie się HTML, lista i tabela wypisywane pętlą foreach w szablonie,
bezpieczeństwo od pierwszej linii: wartość z formularza odczytana przez filter_input, sprawdzona z białą listą rodzajów z bazy i wysłana zapytaniem przygotowanym - bez etapu „dziura i naprawa” z wersji podstawowej; escape() przy każdej wartości z bazy, try/catch przy awarii,
wygoda: lista po wysłaniu pokazuje wybrany rodzaj.
Na końcu krótkie porównanie z wersją podstawową (zakładka obok). Baza i grafiki pochodzą z prawdziwego archiwum pliki1, zapytania zostały wykonane na tej bazie w MariaDB z XAMPP-a, a podgląd strony od etapu PHP to wynik prawdziwego uruchomienia kodu z danego kroku w PHP 8.2 - od Skryptu 2 po wysłaniu formularza z rodzajem „PIECZYWO MIESZANE”, jak na ilustracji 2.
Jak liczą się punkty. Klucz ma 40 kryteriów (R.1.1 - R.5.9), 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 piekarnia
ok. 2 min
Polecenie z arkusza
Utwórz bazę danych o nazwie piekarnia, z zestawem polskich znaków (np. utf8_unicode_ci).
Archiwum pliki1.7z rozpakuj hasłem ULeptnny! do folderu z numerem zdającego: baza.sql, wypieki.jpg, rogal.jpg i teksty.txt. W phpMyAdmin Nowa → piekarnia, porównywanie utf8_unicode_ci, albo zapytaniem:
CREATE DATABASE piekarnia CHARACTER SET utf8 COLLATE utf8_unicode_ci;
Typowe błędy
Nazwa bazy musi być dokładnie piekarnia - skrypty łączą się z bazą po nazwie (R.5.2).
Krok 2: Import tabeli z pliku baza.sql
ok. 2 min
Polecenie z arkusza
Z rozpakowanego archiwum zaimportuj tabelę z pliku baza.sql do utworzonej bazy.
Import → baza.sql. Tabela wyroby z ilustracji 1: 93 wyroby z rodzajem, nazwą, gramaturą (w kilogramach) i ceną. Nazwy kolumn zaczynają się wielką literą (Rodzaj, Nazwa, Cena) - tak trzeba je pisać w zapytaniach i w PHP.
Krok 3: Zrzut ekranu import.png
Kryteria:
R.1.1 Wykonany import tabeli do bazy danych piekarnia co jest udokumentowane plikiem o nazwie import w formacie 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ń, a na nim komunikat phpMyAdmin o udanym imporcie albo tabela wyroby w drzewku bazy. Zapis jako import.png.
Typowe błędy
Wykadrowany zrzut albo zrzut bez paska zadań nie jest oceniany.
Krok 4: Zapytanie 1: wyroby z rodzaju INNE
Kryteria: ,
R.1.2 Zapisany plik kwerendy.txt zawierający co najmniej jedno zapytanie SQL, wynikające z treści zadania
R.1.3 Wykonane zapytanie 1 wybierające jedynie pola z tabeli wyroby : Rodzaj, Nazwa, Gramatura i Cena dla wyrobów z rodzaju „INNE”. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT Rodzaj, Nazwa, Gramatura, Cena FROM wyroby WHERE Rodzaj = "INNE"; oraz wynik działania zapytania udokumentowany zrzutem, na którym widoczne są dokładnie 3 rekordy zawierające jedynie pola: Rodzaj, Nazwa, Gramatura, Cena
ok. 2 min
Polecenie z arkusza
Zapytanie 1: wybierające jedynie pola: Rodzaj, Nazwa, Gramatura i Cena dla wyrobów z rodzaju „INNE”.
Każda klauzula w osobnej linii, napis 'INNE' w apostrofach - tak zapisuje napisy standard SQL (cudzysłów z przykładu w kluczu MariaDB też przyjmie, ale w innych bazach oznacza nazwę kolumny). Wynik: 3 wiersze. Zapytanie zapisz w kwerendy.txt, zrzut jako kw1.png. To zapytanie - z rodzajem z formularza - wyśle Skrypt 2.
Krok 5: Zapytanie 2: rodzaje bez powtórzeń, malejąco
Kryteria:
R.1.4 Wykonane zapytanie 2 wybierające jedynie posortowane malejąco rodzaje wyrobów. Rodzaje wyrobów nie mogą się powtarzać. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT DISTINCT Rodzaj FROM wyroby ORDER BY Rodzaj DESC; (lub GROUP BY wtedy opcjonalnie DISTINCT ) oraz wynik działania zapytania udokumentowany zrzutem, na którym widocznych jest jedynie 11 rekordów z kolumną Rodzaj
ok. 2 min
Polecenie z arkusza
Zapytanie 2: wybierające jedynie posortowane malejąco rodzaje wyrobów. Rodzaje wyrobów nie mogą się powtarzać.
DISTINCT zostawia każdy rodzaj raz: zamiast 93 wierszy - 11. ORDER BY Rodzaj DESC - alfabetycznie od końca: pierwszy „WYROBY CUKIERNICZE I CIASTKARSKIE”, ostatni „INNE”. Zrzut: kw2.png. To zapytanie wypełni listę rozwijaną (Skrypt 1).
kwerendy.txtTekstzmienione: 5 linii
-- Zapytanie 1SELECT Rodzaj, Nazwa, Gramatura, CenaFROM wyrobyWHERE Rodzaj = 'INNE';-- Zapytanie 2SELECT DISTINCT RodzajFROM wyrobyORDER BY Rodzaj DESC;
Podgląd strony po tym kroku
Krok 6: Zapytanie 3: wyroby ze słowem „Chałka”
Kryteria:
R.1.5 Wykonane zapytanie 3 wybierające jedynie pola: ID oraz Nazwa dla wyrobów, których nazwa zawiera słowo „Chałka” . W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT ID, Nazwa FROM wyroby WHERE Nazwa LIKE '%Chałka%'; oraz wynik działania zapytania udokumentowany zrzutem, na którym widoczne są dokładnie 4 rekordy, jedynie pola: ID, Nazwa o numerach id: 57, 58, 61, 62
ok. 2 min
Polecenie z arkusza
Zapytanie 3: wybierające jedynie pola: ID oraz Nazwa dla wyrobów, których nazwa zawiera słowo „Chałka”.
„Zawiera” to LIKE '%Chałka%': % zastępuje dowolny ciąg znaków (także pusty) przed słowem i po nim. Wynik: 4 wiersze o ID 57, 58, 61 i 62, jak w kluczu. Zrzut: kw3.png.
Typowe błędy
LIKE 'Chałka%' pominie nazwy, w których słowo nie stoi na początku.
kwerendy.txtTekstzmienione: 5 linii
-- Zapytanie 1SELECT Rodzaj, Nazwa, Gramatura, CenaFROM wyrobyWHERE Rodzaj = 'INNE';-- Zapytanie 2SELECT DISTINCT RodzajFROM wyrobyORDER BY Rodzaj DESC;-- Zapytanie 3SELECT ID, NazwaFROM wyrobyWHERE Nazwa LIKE '%Chałka%';
Podgląd strony po tym kroku
Krok 7: Zapytanie 4: średnia cena w każdym rodzaju
Kryteria:
R.1.6 Wykonane zapytanie 4 wybierające pole Rodzaj oraz obliczające średnią cenę dla każdej grupy wyrobów z kategorii Rodzaj, zaokrągloną do dwóch miejsc po przecinku. Nazwa (alias) kolumny ze średnią: "Średnia_cena". W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT Rodzaj, ROUND(AVG(Cena), 2) AS "Średnia_cena" FROM wyroby GROUP BY Rodzaj; (AS opcjonalnie ) oraz wynik działania zapytania udokumentowany zrzutem, na którym widocznych jest dokładnie 11 rekordów zawierających jedynie kolumny Rodzaj i alias Średnia_cena oraz pierwszy rekord ma wartości: INNE, 13.88
ok. 3 min
Polecenie z arkusza
Zapytanie 4: wybierające pole Rodzaj oraz obliczające średnią cenę dla każdej grupy wyrobów z kategorii Rodzaj, zaokrągloną do dwóch miejsc po przecinku. Obliczona średnia cena powinna mieć nadaną nazwę kolumny (alias) „Średnia cena”.
GROUP BY Rodzaj - jeden wiersz wyniku na rodzaj, ROUND(AVG(Cena), 2) - średnia zaokrąglona do dwóch miejsc. Alias ze spacją w odwróconych apostrofach`Średnia cena` - w MySQL i MariaDB to znak dla nazw (kolumn, tabel, aliasów), a apostrof jest dla napisów. Cudzysłów z przykładu w kluczu też zadziała.
⚠️ Rozbieżność arkusz-klucz: arkusz podaje alias „Średnia cena” (ze spacją), klucz R.1.6 - Średnia_cena (z podkreśleniem). Tu jest wersja z arkusza; zapis bez spacji (Średnia_cena) nie wymaga cudzysłowu i spełnia brzmienie klucza.
Wynik: 11 wierszy, pierwszy INNE 13.88 - jak w kluczu. Zrzut: kw4.png.
Typowe błędy
Alias ze spacją bez cudzysłowu (AS Średnia cena) to błąd składni.
kwerendy.txtTekstzmienione: 5 linii
-- Zapytanie 1SELECT Rodzaj, Nazwa, Gramatura, CenaFROM wyrobyWHERE Rodzaj = 'INNE';-- Zapytanie 2SELECT DISTINCT RodzajFROM wyrobyORDER BY Rodzaj DESC;-- Zapytanie 3SELECT ID, NazwaFROM wyrobyWHERE Nazwa LIKE '%Chałka%';-- Zapytanie 4SELECT Rodzaj, ROUND(AVG(Cena), 2) AS `Średnia cena`FROM wyrobyGROUP BY Rodzaj;
Podgląd strony po tym kroku
Etap 2: Grafika i strona HTML
Kroki 8-17 · po tym etapie masz 21 z 40 kryteriów (pkt)
R.2.1 Obraz wypieki zawiera różne wypieki w tym rogala po prawej stronie. Rogal zachodzi pod precel oraz dodany cień przesunięty w prawo i w dół dla wszystkich wypieków
R.2.2 Tło obrazu jest przezroczyste oraz szerokość wynosi 1700 px, a wysokość 240 px
ok. 10 min
Polecenie z arkusza
Do grafiki z pliku wypieki.jpg należy dołączyć grafikę z pliku rogal.jpg po prawej stronie. Lewy koniec rogala zachodzi pod precel. Cechy połączonej grafiki: Tło przezroczyste. Cień przesunięty w prawo i w dół. Wymiar: szerokość 1700 px i wysokość 240 px. Nazwa pliku wypieki, format obsługujący przezroczystość.
Grafika jest taka sama jak w wersji podstawowej. W GIMP-ie:
wypieki.jpg (1400 × 240), Obraz → Rozmiar płótna: szerokość 1700, przesunięcie 0 - płótno rośnie w prawo; Warstwa → Warstwa do rozmiaru obrazu,
kanał alfa i Różdżka na białym tle; zamknięte otwory precla dodaj do zaznaczenia z Shift, Delete,
Plik → Otwórz jako warstwyrogal.jpg, białe tło tak samo,
w oknie Warstwy warstwa rogala pod warstwą wypieków - lewy koniec chowa się pod preclem; Przesunięcie ustawia rogal przy prawej krawędzi,
Warstwa → Scal w dół, potem Filtry → Światło i cień → Cień z dodatnimi X i Y - cień dla wszystkich wypieków naraz (R.2.1),
Eksportuj jakowypieki.png - PNG ma przezroczystość, JPG nie (R.2.2).
Typowe błędy
Obraz → Spłaszcz obraz wypełnia przezroczystość kolorem tła. Cień dodany przed wklejeniem rogala obejmie tylko część wypieków.
R.2.3 Witryna internetowa zapisana w pliku piekarnia.php oraz zapisany jawnie standard kodowania polskich znaków, deklaracja HTML5 <!DOCTYPE HTML>, zadeklarowany język witryny: polski np. <html lang="pl"> oraz nadany tytuł strony "PIEKARNIA”. Strona zawiera sekcje head i body
ok. 2 min
Polecenie z arkusza
Cechy witryny: Składa się ze strony o nazwie piekarnia.php. Zapisana w języku HTML5. Zadeklarowany polski język zawartości witryny. Jawnie zastosowany właściwy standard kodowania polskich znaków.
Plik piekarnia.php: <!DOCTYPE html>, <html lang="pl"> i <meta charset="UTF-8"> jako pierwszy element <head>. Rozszerzenie .php, bo strona pobierze dane z bazy - otwierasz ją przez http://localhost/....
R.2.3 Witryna internetowa zapisana w pliku piekarnia.php oraz zapisany jawnie standard kodowania polskich znaków, deklaracja HTML5 <!DOCTYPE HTML>, zadeklarowany język witryny: polski np. <html lang="pl"> oraz nadany tytuł strony "PIEKARNIA”. Strona zawiera sekcje head i body
R.3.2 Strona zawiera działające połączenie z zewnętrznym arkuszem stylów o nazwie styles.css formatowanie CSS pochodzi jedynie z tego arkusza. Uwaga: kryterium to nie jest spełnione również, gdy zdający źle przypisał co najmniej jeden selektor do bloku (np. kropka zamiast #)
ok. 1 min
Polecenie z arkusza
Tytuł strony „PIEKARNIA”, widoczny na karcie przeglądarki. Arkusz stylów w pliku o nazwie styles.css prawidłowo połączony z kodem strony.
<title>, <link> do styles.css - z „s” na końcu (R.3.2) - i viewport, żeby telefon nie pomniejszał strony.
R.2.4 Zdefiniowany układ strony dzielący ją na bloki: nawigacyjny, nagłówkowy, główny i stopki przy pomocy semantycznych znaczników sekcji języka HTML5 zgodnie z ich znaczeniem, w tym do części nagłówkowej - header, do stopki - footer. Dopuszcza się dodatkowe użycie bloków div lub section
R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z ilustracją 2 w arkuszu egzaminacyjnym. Nie należy brać pod uwagę położenia obrazu
ok. 1 min
Polecenie z arkusza
Podział strony na bloki: nawigacyjny, nagłówkowy, główny, stopki. Podział zrealizowany za pomocą semantycznych znaczników sekcji języka HTML5 tak, aby po uruchomieniu w przeglądarce wygląd układu bloków był zgodny z ilustracją 2.
<nav> (na ilustracji nad nagłówkiem), <header>, <main> i <footer> - w tej kolejności w kodzie (R.2.4). Wszystkie bloki stoją jeden pod drugim na całą szerokość, a tak układa je sama przeglądarka - siatka czy flex nic by tu nie dodały (R.3.1).
R.2.7 W bloku nawigacyjnym umieszczone 4 odnośniki o treści KWERENDA1, KWERENDA2, KWERENDA3, KWERENDA4
R.3.3 Po wybraniu przynajmniej jednego odnośnika otwiera/pobiera się plik graficzny
ok. 2 min
Polecenie z arkusza
Zawartość bloku nawigacyjnego: Cztery odnośniki o treści: KWERENDA1, KWERENDA2, KWERENDA3, KWERENDA4. Odnośniki prowadzą do kolejnych zrzutów ekranu z kwerendami.
Cztery <a> do zrzutów kw1.png - kw4.png (R.2.7, R.3.3). Zrzuty leżą w tym samym folderze co strona, więc w href wystarczy nazwa pliku. W podglądzie kliknięcia są zablokowane.
Zawartość bloku nagłówkowego: Nagłówek pierwszego stopnia o treści: „WITAMY”. Nagłówek czwartego stopnia o treści: „NA STRONIE PIEKARNI”. Paragraf (akapit) zawierający tekst. Tekst można skopiować z pliku teksty.txt z wypakowanego archiwum.
<h1>, <h4> i <p> z treścią teksty.txt.
piekarnia.phpPHPzmienione: 3 linie
<!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>PIEKARNIA</title> <linkrel="stylesheet"href="styles.css"></head><body> <imgsrc="wypieki.png"alt="Produkty naszej piekarni"> <nav> <ahref="kw1.png">KWERENDA1</a> <ahref="kw2.png">KWERENDA2</a> <ahref="kw3.png">KWERENDA3</a> <ahref="kw4.png">KWERENDA4</a> </nav> <header> <h1>WITAMY</h1> <h4>NA STRONIE PIEKARNI</h4> <p>Od 31 lat oferujemy najwyższej jakości pieczywo. Naturalnie świeże, naturalnie smaczne. Pieczemy wyłącznie wypieki na naturalnym zakwasie bez polepszaczy i zagęstników. Korzystamy wyłącznie z najlepszych ziaren pochodzących z ekologicznych upraw położonych w rejonach zgierskim i ozorkowskim.</p> </header> <main> </main> <footer> </footer></body></html>
Podgląd strony po tym kroku (piekarnia.php)
Krok 15: Blok główny: nagłówek i formularz
Kryteria: , , ,
R.2.8 W bloku głównym umieszczony formularz z listą rozwijaną oraz przyciskiem „Wybierz”, zastosowane znaczniki <form>, <select>, <option> i przycisku (w skrypcie lub HTML)
R.2.9 Zapisany nagłówek <h1>: „WITAMY”, <h4>: "NA STRONIE PIEKARNI" oraz <h4>: „Wybierz rodzaj wypieków:"
R.3.4 Formularz wysyła dane metodą POST do tego samego pliku (poprawne również bez atrybutu action )
R.3.5 Przycisk realizuje funkcję wysłania danych z formularza (input lub button z typem submit lub button bez typu )
ok. 3 min
Polecenie z arkusza
Zawartość bloku głównego: Nagłówek czwartego stopnia o treści: „Wybierz rodzaj wypieków:”. Formularz wysyłający dane metodą bezpieczną do tego samego pliku. Pola formularza: Lista rozwijana wypełniona przez skrypt 1. Przycisk „Wybierz” wysyłający dane do skryptu 2 z formularza.
<h4> z identyfikatorem choose-type, a lista ma aria-labelledby="choose-type" - czytnik ekranu przeczyta przy niej „Wybierz rodzaj wypieków”. Arkusz każe dać ten tekst w nagłówku, nie w <label>, a aria-labelledby łączy jedno z drugim,
method="post" - „metoda bezpieczna” w rozumieniu arkusza; klucz R.3.4 przyjmuje tylko POST. Formularz, który niczego nie zapisuje, a tylko filtruje, w prawdziwych projektach wysyła się raczej GET-em (adres z wyborem da się zapisać w zakładkach), ale tu decyduje klucz. Bez action formularz wraca do piekarnia.php,
<select name="type"> na razie pusty - opcje dopisze Skrypt 1; przycisk type="submit" z napisem „Wybierz” (R.2.8, R.3.5).
Nagłówki h1, dwa h4 i akapit to komplet R.2.9.
Typowe błędy
method="get" - klucz R.3.4 odbiera punkt, a skrypt czytający dane z POST nic nie dostanie.
piekarnia.phpPHPzmienione: 6 linii
<!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>PIEKARNIA</title> <linkrel="stylesheet"href="styles.css"></head><body> <imgsrc="wypieki.png"alt="Produkty naszej piekarni"> <nav> <ahref="kw1.png">KWERENDA1</a> <ahref="kw2.png">KWERENDA2</a> <ahref="kw3.png">KWERENDA3</a> <ahref="kw4.png">KWERENDA4</a> </nav> <header> <h1>WITAMY</h1> <h4>NA STRONIE PIEKARNI</h4> <p>Od 31 lat oferujemy najwyższej jakości pieczywo. Naturalnie świeże, naturalnie smaczne. Pieczemy wyłącznie wypieki na naturalnym zakwasie bez polepszaczy i zagęstników. Korzystamy wyłącznie z najlepszych ziaren pochodzących z ekologicznych upraw położonych w rejonach zgierskim i ozorkowskim.</p> </header> <main> <h4id="choose-type">Wybierz rodzaj wypieków:</h4> <formmethod="post"> <selectname="type"aria-labelledby="choose-type"> </select> <buttontype="submit">Wybierz</button> </form> </main> <footer> </footer></body></html>
Podgląd strony po tym kroku (piekarnia.php)
Krok 16: Tabela: wiersz nagłówkowy
Kryteria:
R.2.5 W bloku głównym zapisana tabela o 4 kolumnach. Pierwszy wiersz tabeli jest nagłówkowy. Zastosowane znaczniki <table>, <tr>, <th>, <td> (w skrypcie lub HTML)
ok. 2 min
Polecenie z arkusza
Tabela o czterech kolumnach zawierająca: Wiersz z komórkami nagłówkowymi: Rodzaj, Nazwa, Gramatura, Cena. Wiersze z danymi wypełnione skryptem 2.
<thead> z czterema <th scope="col"> w kolejności kolumn zapytania 1 i pusty <tbody> - wiersze z wyrobami dopisze Skrypt 2 (R.2.5). Podział pomaga czytnikom ekranu i wydrukowi długiej tabeli.
piekarnia.phpPHPzmienione: 12 linii
<!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>PIEKARNIA</title> <linkrel="stylesheet"href="styles.css"></head><body> <imgsrc="wypieki.png"alt="Produkty naszej piekarni"> <nav> <ahref="kw1.png">KWERENDA1</a> <ahref="kw2.png">KWERENDA2</a> <ahref="kw3.png">KWERENDA3</a> <ahref="kw4.png">KWERENDA4</a> </nav> <header> <h1>WITAMY</h1> <h4>NA STRONIE PIEKARNI</h4> <p>Od 31 lat oferujemy najwyższej jakości pieczywo. Naturalnie świeże, naturalnie smaczne. Pieczemy wyłącznie wypieki na naturalnym zakwasie bez polepszaczy i zagęstników. Korzystamy wyłącznie z najlepszych ziaren pochodzących z ekologicznych upraw położonych w rejonach zgierskim i ozorkowskim.</p> </header> <main> <h4id="choose-type">Wybierz rodzaj wypieków:</h4> <formmethod="post"> <selectname="type"aria-labelledby="choose-type"> </select> <buttontype="submit">Wybierz</button> </form> <table> <thead> <tr> <thscope="col">Rodzaj</th> <thscope="col">Nazwa</th> <thscope="col">Gramatura</th> <thscope="col">Cena</th> </tr> </thead> <tbody> </tbody> </table> </main> <footer> </footer></body></html>
Podgląd strony po tym kroku (piekarnia.php)
Krok 17: Stopka: autor i data
Kryteria:
R.2.10 W bloku stopki umieszczony paragraf z numerem zdającego oraz paragraf z datą egzaminu
ok. 1 min
Polecenie z arkusza
Zawartość stopki: Paragraf „AUTOR ”, dalej wstawiony numer zdającego. Paragraf „Data: ”, dalej wpisana data egzaminu.
Dwa akapity (R.2.10). Na egzaminie po „AUTOR” wpisujesz swój numer zdającego (PESEL), a po „Data:” dzień egzaminu; tu - autor rozwiązania i data jego przygotowania. Data w znaczniku <time datetime="2026-10-02">: na ekranie zapis polski, a w atrybucie zapis maszynowy RRRR-MM-DD, który rozumieją wyszukiwarki i programy.
piekarnia.phpPHPzmienione: 2 linie
<!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>PIEKARNIA</title> <linkrel="stylesheet"href="styles.css"></head><body> <imgsrc="wypieki.png"alt="Produkty naszej piekarni"> <nav> <ahref="kw1.png">KWERENDA1</a> <ahref="kw2.png">KWERENDA2</a> <ahref="kw3.png">KWERENDA3</a> <ahref="kw4.png">KWERENDA4</a> </nav> <header> <h1>WITAMY</h1> <h4>NA STRONIE PIEKARNI</h4> <p>Od 31 lat oferujemy najwyższej jakości pieczywo. Naturalnie świeże, naturalnie smaczne. Pieczemy wyłącznie wypieki na naturalnym zakwasie bez polepszaczy i zagęstników. Korzystamy wyłącznie z najlepszych ziaren pochodzących z ekologicznych upraw położonych w rejonach zgierskim i ozorkowskim.</p> </header> <main> <h4id="choose-type">Wybierz rodzaj wypieków:</h4> <formmethod="post"> <selectname="type"aria-labelledby="choose-type"> </select> <buttontype="submit">Wybierz</button> </form> <table> <thead> <tr> <thscope="col">Rodzaj</th> <thscope="col">Nazwa</th> <thscope="col">Gramatura</th> <thscope="col">Cena</th> </tr> </thead> <tbody> </tbody> </table> </main> <footer> <p>AUTOR: Bartosz Bryniarski</p> <p>Data: <timedatetime="2026-10-02">02.10.2026</time></p> </footer></body></html>
Podgląd strony po tym kroku (piekarnia.php)
Etap 3: Styl CSS
Kroki 18-24 · po tym etapie masz 31 z 40 kryteriów (pkt)
Krok 18: Plik styles.css: zmienne, krój, ciało strony
Kryteria:
R.4.1 Ustawione domyślne formatowanie wszystkich selektorów (*) krój czcionki: Verdana oraz dla body lub kontenera całej strony tło np. linear-gradient(Teal, AliceBlue), biały kolor czcionki, wyrównanie tekstu do środka, marginesy zewnętrzne (margin) lewy i prawy 20px
ok. 3 min
Polecenie z arkusza
Domyślne formatowanie wszystkich selektorów: krój czcionki Verdana. Dla ciała strony: tło jako gradient liniowy pionowy od koloru Teal do AliceBlue, biały kolor czcionki, wyrównanie tekstu do środka, jedynie marginesy zewnętrzne lewy i prawy 20 px.
zmienne CSS w :root: kolory z arkusza zapisane raz; Sienna występuje w stopce i w tabeli, SandyBrown w bloku głównym i w ramkach,
* z Verdaną i zapasowym krojem bezszeryfowym,
linear-gradient(...) bez kierunku biegnie z góry na dół, czyli pionowo; gradient to obraz, więc stoi w background,
margin-inline: 20px - właściwość logiczna, skrót od margin-left i margin-right naraz. Ustawia jedynie lewy i prawy margines, dokładnie jak w arkuszu; górny i dolny zostają domyślne. Zapis margin: 0 20px z wersji podstawowej przy okazji zeruje górę i dół.
R.4.4 Ustawione cechy obrazu: blokowy sposób wyświetlania, stałe położenie na stronie z odstępem od góry 70px, np.: display: block; position: fixed; (lub absolute; ) top: 70px; dopuszcza się też margin-top: 70px;
R.4.5 Obraz jest pod spodem innych elementów strony np. z-index = -1
ok. 2 min
Polecenie z arkusza
Dla obrazu: szerokość 100%, blokowy sposób wyświetlania, stałe położenie na stronie z odstępem od góry 70 px. Obraz jest zawsze pod spodem treści strony.
position: fixed z top: 70px przypina obraz do okna - nie przewija się ze stroną (R.4.4). z-index: -1 kładzie go pod blokami (R.4.5), a width: 100% liczy się od szerokości okna (R.4.6). Element fixed wypada z przepływu, więc bloki zaczynają się od góry, jakby obrazu nie było. Selektor img wystarcza - to jedyny obraz na stronie.
R.4.9 Ustawione zaokrąglenie górnych rogów bloku nagłówkowego np. border-radius: 15px 15px 0 0; oraz dolnych rogów bloku stopki, np. border-radius: 0 0 15px 15px;
ok. 2 min
Polecenie z arkusza
Dla bloku nagłówkowego: kolor tła RGB 160, 82, 45 z przezroczystością 0.6, marginesy wewnętrzne 10 px, jedynie margines zewnętrzny górny 100 px, jedynie górne rogi zaokrąglone o 15 px.
rgba(160, 82, 45, 0.6) - krycie 0.6, przez tło prześwituje obraz wypieków; tekst zostaje w pełni biały (opacity zrobiłby półprzezroczystym cały blok). Nowa składnia CSS pozwala pisać rgb(160 82 45 / 0.6), ale klucz R.4.2 podaje zapis rgba(...) i ten zostaje,
margin-top: 100px - tylko górny margines,
border-radius: 15px 15px 0 0 - rogi zgodnie z ruchem wskazówek zegara od lewego górnego: górne zaokrąglone, dolne proste (R.4.9).
R.4.2 Ustawiony kolor tła RGBA(160, 82, 45, 0.6) dla bloku nagłówkowego (poprawnie również gdy zdefiniowano opacity: 0.6 ), SandyBrown dla bloku głównego, Sienna dla stopki i dla selektora table
R.4.6 Ustawiona szerokość 100% dla obrazu i 70% dla tabeli
R.4.8 Ustawione marginesy wewnętrzne (padding) 30px dla odnośnika, bloku głównego i stopki, 10px dla bloku nagłówkowego oraz margines zewnętrzny górny 100px dla bloku nagłówkowego i auto dla tabeli
R.4.10 Ustawione dla tabeli i komórek tabeli obramowanie linią ciągłą o grubości 1px i kolorze SandyBrown, dla tabeli obramowanie połączone (border-collapse: collapse)
ok. 2 min
Polecenie z arkusza
Dla selektora tabeli: marginesy zewnętrzne automatycznie wyliczane przez przeglądarkę, szerokość 70%, kolor tła Sienna. Wspólne dla selektora tabeli i komórek tabeli: obramowanie linią ciągłą o grubości 1 px i kolorze SandyBrown, obramowanie połączone.
border-collapse dotyczy całej tabeli, więc stoi w regule table; ramka - wspólnie dla tabeli i komórek (R.4.10). Ramka w kolorze tła bloku głównego wygląda jak jasne odstępy między komórkami. Tła czterech bloków i tabeli to R.4.2, szerokości obrazu i tabeli R.4.6, a odstępy odnośników, bloków i tabeli R.4.8.
Kroki 25-32 · po tym etapie masz 40 z 40 kryteriów (pkt)
Krok 25: Połączenie PDO na górze pliku
Kryteria: , ,
R.5.1 Skrypt jest napisany w języku PHP i zawiera przynajmniej jedną instrukcję zgodną z treścią zadania
R.5.2 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy piekarnia
R.5.3 Ostatnią operacją na bazie jest jej zamknięcie
ok. 4 min
Polecenie z arkusza
Napisane w języku PHP. Należy stosować znaczące nazewnictwo zmiennych i funkcji w języku polskim lub angielskim. Łączą się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie piekarnia. Na końcu jest zamykane połączenie z serwerem.
Praca z bazą stoi przed<!DOCTYPE html>: najpierw dane, potem HTML, który je tylko wypisuje. Połączenie przez PDO (klucz R.5 przyjmuje MySQLi i PDO):
ERRMODE_EXCEPTION - błąd bazy rzuca wyjątek, FETCH_ASSOC - wiersze z nazwami kolumn,
$pdo = null zamyka połączenie; oba skrypty trafią przed tę linię, więc zamknięcie zostanie ostatnią operacją na bazie (R.5.3),
try / catch - przy awarii bazy szczegóły idą do dziennika serwera, a strona dostaje ogólny komunikat,
escape() - htmlspecialchars(..., ENT_QUOTES) pod nazwą, która mówi, co robi. Nazwy wyrobów mają cudzysłowy (Chleb wieloziarnisty "Słońce"), więc ENT_QUOTES jest tu potrzebne.
Od tego kroku podgląd to wynik prawdziwego uruchomienia.
piekarnia.phpPHPzmienione: 21 linii
<?phpfunctionescape(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES);}$types = [];$products = [];$dbError = '';try {$pdo = new PDO('mysql:host=localhost;dbname=piekarnia;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);$pdo = null;} catch (PDOException $exception) {error_log($exception->getMessage());$dbError = 'Nie udało się pobrać oferty. Spróbuj ponownie za chwilę.';}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>PIEKARNIA</title> <linkrel="stylesheet"href="styles.css"></head><body> <imgsrc="wypieki.png"alt="Produkty naszej piekarni"> <nav> <ahref="kw1.png">KWERENDA1</a> <ahref="kw2.png">KWERENDA2</a> <ahref="kw3.png">KWERENDA3</a> <ahref="kw4.png">KWERENDA4</a> </nav> <header> <h1>WITAMY</h1> <h4>NA STRONIE PIEKARNI</h4> <p>Od 31 lat oferujemy najwyższej jakości pieczywo. Naturalnie świeże, naturalnie smaczne. Pieczemy wyłącznie wypieki na naturalnym zakwasie bez polepszaczy i zagęstników. Korzystamy wyłącznie z najlepszych ziaren pochodzących z ekologicznych upraw położonych w rejonach zgierskim i ozorkowskim.</p> </header> <main> <h4id="choose-type">Wybierz rodzaj wypieków:</h4> <formmethod="post"> <selectname="type"aria-labelledby="choose-type"> </select> <buttontype="submit">Wybierz</button> </form> <table> <thead> <tr> <thscope="col">Rodzaj</th> <thscope="col">Nazwa</th> <thscope="col">Gramatura</th> <thscope="col">Cena</th> </tr> </thead> <tbody> </tbody> </table> </main> <footer> <p>AUTOR: Bartosz Bryniarski</p> <p>Data: <timedatetime="2026-10-02">02.10.2026</time></p> </footer></body></html>
Podgląd strony po tym kroku (piekarnia.php)
Krok 26: Skrypt 1: zapytanie 2
Kryteria:
R.5.4 Skrypt 1 zawiera instrukcję wysyłającą do bazy danych zapytanie 2 lub skrypt 2 zawiera instrukcję wysyłającą do bazy danych zmodyfikowane zapytanie 1 w ten sposób, że wartość pola Rodzaj w warunku jest pobrana z formularza
ok. 2 min
Polecenie z arkusza
Skrypt 1: Wysyła do bazy danych zapytanie 2.
$pdo->query(...) wysyła zapytanie 2 (R.5.4). fetchAll(PDO::FETCH_COLUMN) oddaje zwykłą listę napisów - 11 rodzajów - zamiast listy wierszy z jedną kolumną: $types[0] to od razu „WYROBY CUKIERNICZE I CIASTKARSKIE”. Lista przyda się dwa razy: do opcji i do sprawdzenia, czy wysłany rodzaj w ogóle istnieje.
piekarnia.phpPHPzmienione: 6 linii
<?phpfunctionescape(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES);}$types = [];$products = [];$dbError = '';try {$pdo = new PDO('mysql:host=localhost;dbname=piekarnia;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);// Skrypt 1: rodzaje wypieków do listy$types = $pdo->query('SELECT DISTINCT Rodzaj FROM wyroby ORDER BY Rodzaj DESC' )->fetchAll(PDO::FETCH_COLUMN);$pdo = null;} catch (PDOException $exception) {error_log($exception->getMessage());$dbError = 'Nie udało się pobrać oferty. Spróbuj ponownie za chwilę.';}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>PIEKARNIA</title> <linkrel="stylesheet"href="styles.css"></head><body> <imgsrc="wypieki.png"alt="Produkty naszej piekarni"> <nav> <ahref="kw1.png">KWERENDA1</a> <ahref="kw2.png">KWERENDA2</a> <ahref="kw3.png">KWERENDA3</a> <ahref="kw4.png">KWERENDA4</a> </nav> <header> <h1>WITAMY</h1> <h4>NA STRONIE PIEKARNI</h4> <p>Od 31 lat oferujemy najwyższej jakości pieczywo. Naturalnie świeże, naturalnie smaczne. Pieczemy wyłącznie wypieki na naturalnym zakwasie bez polepszaczy i zagęstników. Korzystamy wyłącznie z najlepszych ziaren pochodzących z ekologicznych upraw położonych w rejonach zgierskim i ozorkowskim.</p> </header> <main> <h4id="choose-type">Wybierz rodzaj wypieków:</h4> <formmethod="post"> <selectname="type"aria-labelledby="choose-type"> </select> <buttontype="submit">Wybierz</button> </form> <table> <thead> <tr> <thscope="col">Rodzaj</th> <thscope="col">Nazwa</th> <thscope="col">Gramatura</th> <thscope="col">Cena</th> </tr> </thead> <tbody> </tbody> </table> </main> <footer> <p>AUTOR: Bartosz Bryniarski</p> <p>Data: <timedatetime="2026-10-02">02.10.2026</time></p> </footer></body></html>
R.5.5 Skrypt 1 wyświetla zwrócone zapytaniem wiersze w kolejnych elementach listy rozwijanej
R.5.6 Działanie skryptu 1 oraz skryptu 2 jest uniwersalne dla dowolnej liczby zwróconych rekordów i jest zrealizowane z wykorzystaniem pętli
ok. 3 min
Polecenie z arkusza
Dane zwrócone zapytaniem wyświetla w kolejnych elementach listy rozwijanej.
Pętla foreach ($types as $type): w składni alternatywnej - dla każdego rodzaju jeden <option> (R.5.5); działa dla dowolnej liczby rodzajów (R.5.6). Wartość stoi jawnie w value - to ją wyśle formularz - i przechodzi przez escape(): w atrybucie cudzysłów bez zamiany na encję uciąłby wartość. Podgląd: lista z 11 rodzajami.
piekarnia.phpPHPzmienione: 3 linie
<?phpfunctionescape(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES);}$types = [];$products = [];$dbError = '';try {$pdo = new PDO('mysql:host=localhost;dbname=piekarnia;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);// Skrypt 1: rodzaje wypieków do listy$types = $pdo->query('SELECT DISTINCT Rodzaj FROM wyroby ORDER BY Rodzaj DESC' )->fetchAll(PDO::FETCH_COLUMN);$pdo = null;} catch (PDOException $exception) {error_log($exception->getMessage());$dbError = 'Nie udało się pobrać oferty. Spróbuj ponownie za chwilę.';}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>PIEKARNIA</title> <linkrel="stylesheet"href="styles.css"></head><body> <imgsrc="wypieki.png"alt="Produkty naszej piekarni"> <nav> <ahref="kw1.png">KWERENDA1</a> <ahref="kw2.png">KWERENDA2</a> <ahref="kw3.png">KWERENDA3</a> <ahref="kw4.png">KWERENDA4</a> </nav> <header> <h1>WITAMY</h1> <h4>NA STRONIE PIEKARNI</h4> <p>Od 31 lat oferujemy najwyższej jakości pieczywo. Naturalnie świeże, naturalnie smaczne. Pieczemy wyłącznie wypieki na naturalnym zakwasie bez polepszaczy i zagęstników. Korzystamy wyłącznie z najlepszych ziaren pochodzących z ekologicznych upraw położonych w rejonach zgierskim i ozorkowskim.</p> </header> <main> <h4id="choose-type">Wybierz rodzaj wypieków:</h4> <formmethod="post"> <selectname="type"aria-labelledby="choose-type"><?phpforeach ($typesas$type): ?> <optionvalue="<?= escape($type) ?>"><?= escape($type) ?></option><?phpendforeach; ?> </select> <buttontype="submit">Wybierz</button> </form> <table> <thead> <tr> <thscope="col">Rodzaj</th> <thscope="col">Nazwa</th> <thscope="col">Gramatura</th> <thscope="col">Cena</th> </tr> </thead> <tbody> </tbody> </table> </main> <footer> <p>AUTOR: Bartosz Bryniarski</p> <p>Data: <timedatetime="2026-10-02">02.10.2026</time></p> </footer></body></html>
Podgląd strony po tym kroku (piekarnia.php)
Krok 28: Skrypt 2: wartość z formularza i biała lista
Kryteria:
R.5.8 Skrypt 2 jest zabezpieczony na wypadek braku danych wysyłanych z formularza
ok. 3 min
Polecenie z arkusza
W stanie początkowym, gdy nie wybrano opcji listy rozwijanej, w tabeli nie są wyświetlane wiersze z danymi.
filter_input(INPUT_POST, 'type') czyta pole z wysłanego formularza; gdy formularza nie wysłano, zwraca null, a ?? '' zamienia go na pusty napis. trim obcina spacje z brzegów,
warunek ma dwie części: $selectedType !== '' - formularz wysłano (przy pierwszym wejściu tabela zostaje z samym nagłówkiem, R.5.8), oraz in_array($selectedType, $types, true) - wartość jest jednym z 11 rodzajów z bazy. To biała lista: lista rozwijana podpowiada wybór, ale nie pilnuje go - formularz da się wysłać z dowolnym tekstem (narzędzia programisty, curl). Trzeci argument true porównuje dokładnie, bez zamiany typów.
Blok if jest jeszcze pusty; podgląd: stan po wysłaniu „PIECZYWO MIESZANE”.
piekarnia.phpPHPzmienione: 5 linii
<?phpfunctionescape(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES);}$types = [];$products = [];$dbError = '';$selectedType = trim(filter_input(INPUT_POST, 'type') ?? '');try {$pdo = new PDO('mysql:host=localhost;dbname=piekarnia;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);// Skrypt 1: rodzaje wypieków do listy$types = $pdo->query('SELECT DISTINCT Rodzaj FROM wyroby ORDER BY Rodzaj DESC' )->fetchAll(PDO::FETCH_COLUMN);// Skrypt 2: wyroby wybranego rodzajuif ($selectedType !== '' && in_array($selectedType, $types, true)) { }$pdo = null;} catch (PDOException $exception) {error_log($exception->getMessage());$dbError = 'Nie udało się pobrać oferty. Spróbuj ponownie za chwilę.';}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>PIEKARNIA</title> <linkrel="stylesheet"href="styles.css"></head><body> <imgsrc="wypieki.png"alt="Produkty naszej piekarni"> <nav> <ahref="kw1.png">KWERENDA1</a> <ahref="kw2.png">KWERENDA2</a> <ahref="kw3.png">KWERENDA3</a> <ahref="kw4.png">KWERENDA4</a> </nav> <header> <h1>WITAMY</h1> <h4>NA STRONIE PIEKARNI</h4> <p>Od 31 lat oferujemy najwyższej jakości pieczywo. Naturalnie świeże, naturalnie smaczne. Pieczemy wyłącznie wypieki na naturalnym zakwasie bez polepszaczy i zagęstników. Korzystamy wyłącznie z najlepszych ziaren pochodzących z ekologicznych upraw położonych w rejonach zgierskim i ozorkowskim.</p> </header> <main> <h4id="choose-type">Wybierz rodzaj wypieków:</h4> <formmethod="post"> <selectname="type"aria-labelledby="choose-type"><?phpforeach ($typesas$type): ?> <optionvalue="<?= escape($type) ?>"><?= escape($type) ?></option><?phpendforeach; ?> </select> <buttontype="submit">Wybierz</button> </form> <table> <thead> <tr> <thscope="col">Rodzaj</th> <thscope="col">Nazwa</th> <thscope="col">Gramatura</th> <thscope="col">Cena</th> </tr> </thead> <tbody> </tbody> </table> </main> <footer> <p>AUTOR: Bartosz Bryniarski</p> <p>Data: <timedatetime="2026-10-02">02.10.2026</time></p> </footer></body></html>
Krok 29: Skrypt 2: zapytanie przygotowane z rodzajem z formularza
ok. 4 min
Polecenie z arkusza
Skrypt 2: Wysyła do bazy danych zapytanie 1 zmodyfikowane w ten sposób, że wartość pola Rodzaj w warunku jest pobrana z formularza.
Zapytanie 1 z parametrem nazwanym:type w miejscu „INNE”:
prepare(...) wysyła do bazy samo zapytanie, z dziurą na wartość,
execute(['type' => $selectedType]) wysyła wartość osobno - baza nigdy nie potraktuje jej jak kodu SQL, więc nawet x' OR '1'='1 jest tylko napisem, którego nie ma w kolumnie Rodzaj,
fetchAll() - wyroby wybranego rodzaju trafiają do $products, a $statement = null zwalnia zapytanie przed zamknięciem połączenia.
Biała lista z poprzedniego kroku i zapytanie przygotowane chronią na dwa sposoby: pierwsza odrzuca wartości spoza listy, drugie gwarantuje, że żadna wartość nie zmieni zapytania. Podgląd: zapytanie poszło, wyniku jeszcze nie wypisano.
piekarnia.phpPHPzmienione: 6 linii
<?phpfunctionescape(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES);}$types = [];$products = [];$dbError = '';$selectedType = trim(filter_input(INPUT_POST, 'type') ?? '');try {$pdo = new PDO('mysql:host=localhost;dbname=piekarnia;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);// Skrypt 1: rodzaje wypieków do listy$types = $pdo->query('SELECT DISTINCT Rodzaj FROM wyroby ORDER BY Rodzaj DESC' )->fetchAll(PDO::FETCH_COLUMN);// Skrypt 2: wyroby wybranego rodzajuif ($selectedType !== '' && in_array($selectedType, $types, true)) {$statement = $pdo->prepare('SELECT Rodzaj, Nazwa, Gramatura, Cena FROM wyroby WHERE Rodzaj = :type' );$statement->execute(['type' => $selectedType]);$products = $statement->fetchAll();$statement = null; }$pdo = null;} catch (PDOException $exception) {error_log($exception->getMessage());$dbError = 'Nie udało się pobrać oferty. Spróbuj ponownie za chwilę.';}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>PIEKARNIA</title> <linkrel="stylesheet"href="styles.css"></head><body> <imgsrc="wypieki.png"alt="Produkty naszej piekarni"> <nav> <ahref="kw1.png">KWERENDA1</a> <ahref="kw2.png">KWERENDA2</a> <ahref="kw3.png">KWERENDA3</a> <ahref="kw4.png">KWERENDA4</a> </nav> <header> <h1>WITAMY</h1> <h4>NA STRONIE PIEKARNI</h4> <p>Od 31 lat oferujemy najwyższej jakości pieczywo. Naturalnie świeże, naturalnie smaczne. Pieczemy wyłącznie wypieki na naturalnym zakwasie bez polepszaczy i zagęstników. Korzystamy wyłącznie z najlepszych ziaren pochodzących z ekologicznych upraw położonych w rejonach zgierskim i ozorkowskim.</p> </header> <main> <h4id="choose-type">Wybierz rodzaj wypieków:</h4> <formmethod="post"> <selectname="type"aria-labelledby="choose-type"><?phpforeach ($typesas$type): ?> <optionvalue="<?= escape($type) ?>"><?= escape($type) ?></option><?phpendforeach; ?> </select> <buttontype="submit">Wybierz</button> </form> <table> <thead> <tr> <thscope="col">Rodzaj</th> <thscope="col">Nazwa</th> <thscope="col">Gramatura</th> <thscope="col">Cena</th> </tr> </thead> <tbody> </tbody> </table> </main> <footer> <p>AUTOR: Bartosz Bryniarski</p> <p>Data: <timedatetime="2026-10-02">02.10.2026</time></p> </footer></body></html>
R.5.7 Skrypt 2 wyświetla zwrócone zapytaniem rekordy w kolejnych wierszach tabeli, w przeznaczonych do tego komórkach
R.5.9 W skryptach zastosowana przynajmniej jedna zmienna lub funkcja oraz nazwy wszystkich funkcji i zmiennych są w języku polskim lub angielskim i odzwierciedlają cel zastosowania
ok. 3 min
Polecenie z arkusza
Zwrócone zapytaniem wiersze wyświetla w kolejnych wierszach tabeli, w przeznaczonych do tego komórkach.
W <tbody> pętla foreach ($products as $product): - wiersz <tr> z czterema <td> po nazwach kolumn ($product['Nazwa']), każda przez escape() (R.5.7). Przy pierwszym wejściu $products jest pustą tablicą i pętla nie wypisze nic. Nazwy zmiennych i funkcji po angielsku mówią, co przechowują (R.5.9). Podgląd: 7 wyrobów z rodzaju PIECZYWO MIESZANE - jak na ilustracji 2.
piekarnia.phpPHPzmienione: 8 linii
<?phpfunctionescape(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES);}$types = [];$products = [];$dbError = '';$selectedType = trim(filter_input(INPUT_POST, 'type') ?? '');try {$pdo = new PDO('mysql:host=localhost;dbname=piekarnia;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);// Skrypt 1: rodzaje wypieków do listy$types = $pdo->query('SELECT DISTINCT Rodzaj FROM wyroby ORDER BY Rodzaj DESC' )->fetchAll(PDO::FETCH_COLUMN);// Skrypt 2: wyroby wybranego rodzajuif ($selectedType !== '' && in_array($selectedType, $types, true)) {$statement = $pdo->prepare('SELECT Rodzaj, Nazwa, Gramatura, Cena FROM wyroby WHERE Rodzaj = :type' );$statement->execute(['type' => $selectedType]);$products = $statement->fetchAll();$statement = null; }$pdo = null;} catch (PDOException $exception) {error_log($exception->getMessage());$dbError = 'Nie udało się pobrać oferty. Spróbuj ponownie za chwilę.';}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>PIEKARNIA</title> <linkrel="stylesheet"href="styles.css"></head><body> <imgsrc="wypieki.png"alt="Produkty naszej piekarni"> <nav> <ahref="kw1.png">KWERENDA1</a> <ahref="kw2.png">KWERENDA2</a> <ahref="kw3.png">KWERENDA3</a> <ahref="kw4.png">KWERENDA4</a> </nav> <header> <h1>WITAMY</h1> <h4>NA STRONIE PIEKARNI</h4> <p>Od 31 lat oferujemy najwyższej jakości pieczywo. Naturalnie świeże, naturalnie smaczne. Pieczemy wyłącznie wypieki na naturalnym zakwasie bez polepszaczy i zagęstników. Korzystamy wyłącznie z najlepszych ziaren pochodzących z ekologicznych upraw położonych w rejonach zgierskim i ozorkowskim.</p> </header> <main> <h4id="choose-type">Wybierz rodzaj wypieków:</h4> <formmethod="post"> <selectname="type"aria-labelledby="choose-type"><?phpforeach ($typesas$type): ?> <optionvalue="<?= escape($type) ?>"><?= escape($type) ?></option><?phpendforeach; ?> </select> <buttontype="submit">Wybierz</button> </form> <table> <thead> <tr> <thscope="col">Rodzaj</th> <thscope="col">Nazwa</th> <thscope="col">Gramatura</th> <thscope="col">Cena</th> </tr> </thead> <tbody><?phpforeach ($productsas$product): ?> <tr> <td><?= escape($product['Rodzaj']) ?></td> <td><?= escape($product['Nazwa']) ?></td> <td><?= escape($product['Gramatura']) ?></td> <td><?= escape($product['Cena']) ?></td> </tr><?phpendforeach; ?> </tbody> </table> </main> <footer> <p>AUTOR: Bartosz Bryniarski</p> <p>Data: <timedatetime="2026-10-02">02.10.2026</time></p> </footer></body></html>
Podgląd strony po tym kroku (piekarnia.php)
Krok 31: Zapamiętany wybór na liście
ok. 2 min
Dodatek spoza arkusza. Po wysłaniu formularza strona ładuje się od nowa i lista wracała do pierwszej opcji - nie było widać, którego rodzaju dotyczy tabela. Opcja równa $selectedType dostaje atrybut selected. Podgląd: po wysłaniu „PIECZYWO MIESZANE” lista pokazuje ten rodzaj.
piekarnia.phpPHPzmienione: 1 linia
<?phpfunctionescape(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES);}$types = [];$products = [];$dbError = '';$selectedType = trim(filter_input(INPUT_POST, 'type') ?? '');try {$pdo = new PDO('mysql:host=localhost;dbname=piekarnia;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);// Skrypt 1: rodzaje wypieków do listy$types = $pdo->query('SELECT DISTINCT Rodzaj FROM wyroby ORDER BY Rodzaj DESC' )->fetchAll(PDO::FETCH_COLUMN);// Skrypt 2: wyroby wybranego rodzajuif ($selectedType !== '' && in_array($selectedType, $types, true)) {$statement = $pdo->prepare('SELECT Rodzaj, Nazwa, Gramatura, Cena FROM wyroby WHERE Rodzaj = :type' );$statement->execute(['type' => $selectedType]);$products = $statement->fetchAll();$statement = null; }$pdo = null;} catch (PDOException $exception) {error_log($exception->getMessage());$dbError = 'Nie udało się pobrać oferty. Spróbuj ponownie za chwilę.';}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>PIEKARNIA</title> <linkrel="stylesheet"href="styles.css"></head><body> <imgsrc="wypieki.png"alt="Produkty naszej piekarni"> <nav> <ahref="kw1.png">KWERENDA1</a> <ahref="kw2.png">KWERENDA2</a> <ahref="kw3.png">KWERENDA3</a> <ahref="kw4.png">KWERENDA4</a> </nav> <header> <h1>WITAMY</h1> <h4>NA STRONIE PIEKARNI</h4> <p>Od 31 lat oferujemy najwyższej jakości pieczywo. Naturalnie świeże, naturalnie smaczne. Pieczemy wyłącznie wypieki na naturalnym zakwasie bez polepszaczy i zagęstników. Korzystamy wyłącznie z najlepszych ziaren pochodzących z ekologicznych upraw położonych w rejonach zgierskim i ozorkowskim.</p> </header> <main> <h4id="choose-type">Wybierz rodzaj wypieków:</h4> <formmethod="post"> <selectname="type"aria-labelledby="choose-type"><?phpforeach ($typesas$type): ?> <optionvalue="<?= escape($type) ?>"<?= $type === $selectedType ? ' selected' : ''?>><?= escape($type) ?></option><?phpendforeach; ?> </select> <buttontype="submit">Wybierz</button> </form> <table> <thead> <tr> <thscope="col">Rodzaj</th> <thscope="col">Nazwa</th> <thscope="col">Gramatura</th> <thscope="col">Cena</th> </tr> </thead> <tbody><?phpforeach ($productsas$product): ?> <tr> <td><?= escape($product['Rodzaj']) ?></td> <td><?= escape($product['Nazwa']) ?></td> <td><?= escape($product['Gramatura']) ?></td> <td><?= escape($product['Cena']) ?></td> </tr><?phpendforeach; ?> </tbody> </table> </main> <footer> <p>AUTOR: Bartosz Bryniarski</p> <p>Data: <timedatetime="2026-10-02">02.10.2026</time></p> </footer></body></html>
Podgląd strony po tym kroku (piekarnia.php)
Krok 32: Komunikaty: rodzaj spoza listy i awaria bazy
ok. 3 min
Polecenie z arkusza
Na końcu jest zamykane połączenie z serwerem.
Nad tabelą dwa komunikaty, każdy tylko wtedy, gdy jest co powiedzieć:
$formError - formularz przyszedł z wartością spoza białej listy. Sprawdzone uruchomieniem: wysłanie x' OR '1'='1 (w wersji podstawowej ta sama próba bez zabezpieczeń pokazała wszystkie 93 wyroby) daje komunikat „Nie ma takiego rodzaju wypieków.” i pustą tabelę, a <b>X</b> nie zamienia się w pogrubienie,
$dbError - awaria bazy; uruchomienie z błędną nazwą bazy pokazuje komunikat, pustą listę i tabelę z samym nagłówkiem, bez nazwy bazy i kodu błędu SQL na stronie.
W normalnym działaniu połączenie jest zamykane ($pdo = null) zaraz po obu skryptach, przed pierwszą linią HTML. Podgląd: stan po wysłaniu „PIECZYWO MIESZANE”.
piekarnia.phpPHPzmienione: 9 linii
<?phpfunctionescape(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES);}$types = [];$products = [];$dbError = '';$formError = '';$selectedType = trim(filter_input(INPUT_POST, 'type') ?? '');try {$pdo = new PDO('mysql:host=localhost;dbname=piekarnia;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);// Skrypt 1: rodzaje wypieków do listy$types = $pdo->query('SELECT DISTINCT Rodzaj FROM wyroby ORDER BY Rodzaj DESC' )->fetchAll(PDO::FETCH_COLUMN);// Skrypt 2: wyroby wybranego rodzajuif ($selectedType !== '' && in_array($selectedType, $types, true)) {$statement = $pdo->prepare('SELECT Rodzaj, Nazwa, Gramatura, Cena FROM wyroby WHERE Rodzaj = :type' );$statement->execute(['type' => $selectedType]);$products = $statement->fetchAll();$statement = null; } elseif ($selectedType !== '') {$formError = 'Nie ma takiego rodzaju wypieków.'; }$pdo = null;} catch (PDOException $exception) {error_log($exception->getMessage());$dbError = 'Nie udało się pobrać oferty. Spróbuj ponownie za chwilę.';}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>PIEKARNIA</title> <linkrel="stylesheet"href="styles.css"></head><body> <imgsrc="wypieki.png"alt="Produkty naszej piekarni"> <nav> <ahref="kw1.png">KWERENDA1</a> <ahref="kw2.png">KWERENDA2</a> <ahref="kw3.png">KWERENDA3</a> <ahref="kw4.png">KWERENDA4</a> </nav> <header> <h1>WITAMY</h1> <h4>NA STRONIE PIEKARNI</h4> <p>Od 31 lat oferujemy najwyższej jakości pieczywo. Naturalnie świeże, naturalnie smaczne. Pieczemy wyłącznie wypieki na naturalnym zakwasie bez polepszaczy i zagęstników. Korzystamy wyłącznie z najlepszych ziaren pochodzących z ekologicznych upraw położonych w rejonach zgierskim i ozorkowskim.</p> </header> <main> <h4id="choose-type">Wybierz rodzaj wypieków:</h4> <formmethod="post"> <selectname="type"aria-labelledby="choose-type"><?phpforeach ($typesas$type): ?> <optionvalue="<?= escape($type) ?>"<?= $type === $selectedType ? ' selected' : ''?>><?= escape($type) ?></option><?phpendforeach; ?> </select> <buttontype="submit">Wybierz</button> </form><?phpif ($dbError !== ''): ?> <p><?= escape($dbError) ?></p><?phpendif; ?><?phpif ($formError !== ''): ?> <p><?= escape($formError) ?></p><?phpendif; ?> <table> <thead> <tr> <thscope="col">Rodzaj</th> <thscope="col">Nazwa</th> <thscope="col">Gramatura</th> <thscope="col">Cena</th> </tr> </thead> <tbody><?phpforeach ($productsas$product): ?> <tr> <td><?= escape($product['Rodzaj']) ?></td> <td><?= escape($product['Nazwa']) ?></td> <td><?= escape($product['Gramatura']) ?></td> <td><?= escape($product['Cena']) ?></td> </tr><?phpendforeach; ?> </tbody> </table> </main> <footer> <p>AUTOR: Bartosz Bryniarski</p> <p>Data: <timedatetime="2026-10-02">02.10.2026</time></p> </footer></body></html>
Podgląd strony po tym kroku (piekarnia.php)
Etap 5: Porównanie z wersją podstawową
Krok 33 · po tym etapie masz 40 z 40 kryteriów (pkt)
Krok 33: 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
kolory
wpisane w każdej regule
zmienne CSS w :root
marginesy ciała strony
margin: 0 20px (zeruje też górę i dół)
margin-inline: 20px (jedynie lewy i prawy)
lista rozwijana
bez powiązania z nagłówkiem
aria-labelledby na nagłówek, value w opcjach
baza
mysqli_connect, mysqli_fetch_row, $row[0]
PDO z wyjątkami, FETCH_COLUMN, $product['Nazwa']
wartość z formularza
$_POST["type"] wklejone w zapytanie (luka pokazana i naprawiona w dobrych praktykach)
filter_input + biała lista + prepare z :type od początku
wybór po wysłaniu
lista wraca do pierwszej opcji (w dobrych praktykach selected)
selected od razu
zła wartość / awaria bazy
pusta tabela / biała strona z błędem
komunikat nad tabelą
Na egzaminie wersja podstawowa trzyma się funkcji z tabeli 1 arkusza i jest szybsza do napisania, a klucz R.5.4 ocenia tylko to, że wartość z formularza trafia do zapytania - nie ocenia bezpieczeństwa. Nowoczesna nie ma luki, którą trzeba potem łatać. W prawdziwym projekcie doszłyby jeszcze dane logowania do bazy w osobnym pliku config.php (tu klucz szuka połączenia w kodzie strony).
Krok 34 · po tym etapie masz 40 z 40 kryteriów (pkt)
Krok 34: 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.
W przeglądarka.txt nazwa przeglądarki, w której strona była sprawdzana przez http://localhost/.... W folderze: piekarnia.php, styles.css, kwerendy.txt, import.png, kw1.png - kw4.png, wypieki.png i przeglądarka.txt. Nagraj płytę, sprawdź jej odczyt i opisz ją numerem zdającego.
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.