INF.03-02-26.06-SGCzerwiec 2026 · 49 kroków · ok. 91 min · 41 kryteriów z klucza
Rozwiązanie krok po kroku: Witryna Łowcy wodospadów (Nowoczesny kod (grid, PDO))
To drugi sposób rozwiązania tego samego arkusza. Wynik jest ten sam - baza wodospady, pięć zapytań, zdjęcie przeskalowane do 133 px, strona index.php z odnośnikami kontynentów, tabelą wodospadów, formularzem i galerią, style w styl.css - i tak samo spełnia każde kryterium klucza. Inna jest droga:
blok główny na siatce CSS (grid-template-columns: 30% 70%) zamiast display: flex, kolory i zaokrąglenia z arkusza w zmiennych CSS, tabela z częściami thead i tbody,
PHP na PDO z logiką na górze pliku: wszystkie zapytania wykonują się przed HTML-em, a szablon tylko wypisuje dane przez funkcję e(),
dane z zewnątrz są sprawdzane: identyfikator kontynentu z adresu przechodzi przez filter_var z wartością domyślną 6, a pola formularza przez filter_input - adres ?id=6 OR 1=1 niczego nie zmienia, a puste pole nie wywraca strony,
zapis przez zapytanie przygotowane z obsługą błędu klucza obcego (wodospad spoza bazy daje komunikat, nie błąd PHP) i PRG: po zapisie strona przekierowuje sama na siebie, więc odświeżenie nie dopisze osiągnięcia drugi raz.
Wersja podstawowa (zakładka obok) pisze kod najprostszymi funkcjami z tabeli 1 arkusza i dopiero na końcu go poprawia; tu kod jest od razu taki, jak w prawdziwym projekcie. Na końcu jest krótkie porównanie obu wersji i podpowiedź, którą wybrać na egzaminie.
Baza i zdjęcia pochodzą z prawdziwego archiwum pliki2 (załącznik na stronie arkusza, hasło w treści zadania), a podgląd każdego kroku z PHP to wynik prawdziwego uruchomienia strony na serwerze z tą bazą; tabela pokazuje Europę, jak na ilustracji 2.
Jak liczą się punkty. Klucz ma 41 kryteriów (R.1.1 - R.5.9), każde za 1 punkt. Próg zdania części praktycznej to 75%, czyli 31 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-8 · po tym etapie masz 7 z 41 kryteriów (pkt)
Krok 1: Baza danych wodospady
ok. 2 min
Polecenie z arkusza
Utwórz bazę danych o nazwie wodospady, z zestawem znaków utf8_unicode_ci.
Rozpakuj pliki2.7z z pulpitu (hasło z treści zadania) i załóż folder z numerem zdającego. W phpMyAdmin: Nowa, nazwa wodospady, porównywanie utf8_unicode_ci. W SQL:
CREATE DATABASE wodospady CHARACTER SET utf8 COLLATE utf8_unicode_ci;
Arkusz nazywa to „zestawem znaków”, ale utf8_unicode_ci to metoda porównywania - zestawem jest utf8.
Typowe błędy
Literówka w nazwie bazy zabiera też punkty za skrypty - połączenie podaje nazwę bazy (R.5.1).
Krok 2: Import tabel z pliku baza2.sql
ok. 2 min
Polecenie z arkusza
Do utworzonej bazy zaimportuj tabele z pliku baza2.sql.
Zaznacz bazę wodospady, Import → baza2.sql → Wykonaj. Powstaje pięć tabel z ilustracji 1, w tym pusta osiagniecia - tabela pośrednicząca relacji m:n między turystami a obiektami.
Krok 3: Zrzut ekranu import.jpg
Kryteria:
R.1.1 Wykonany import tabel do bazy danych wodospady - plik import w formacie JPEG
ok. 2 min
Polecenie z arkusza
Wykonaj zrzut ekranu po imporcie. Zapisz zrzut w formacie JPEG 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, zapis jako import.jpg - import w JPEG, zrzuty zapytań w PNG.
Typowe błędy
Wykadrowany zrzut albo zrzut bez paska zadań nie jest oceniany.
Krok 4: Zapytanie 1: wszystkie kontynenty
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 identyfikatory i nazwy wszystkich kontynentów. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT idKontynent, nazwa FROM kontynenty; (lub * po SELECT ) oraz na zrzucie widoczne są dane dla identyfikatorów 1 ÷ 7
ok. 1 min
Polecenie z arkusza
Zapytanie 1: wybierające identyfikatory i nazwy wszystkich kontynentów.
Dwie kolumny bez warunku: siedem kontynentów, identyfikatory 1-7 (R.1.3). Zapytanie do kwerendy.txt, zrzut kw1.png.
R.1.4 Wykonane zapytanie 2 wybierające jedynie pola nazwa, wartoscCechy i plik z tabeli obiekty dla wodospadów z Polski. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa, wartoscCechy, plik FROM obiekty WHERE idRodzaj = 10 AND panstwo = "Polska";(wymagane sprawdzenie, że jest to wodospad) oraz na zrzucie widoczne są dokładnie 4 rekordy dla: Kamieńczyk, Mała Siklawa, Wielka Siklawa, Wilczki
ok. 2 min
Polecenie z arkusza
Zapytanie 2: wybierające jedynie pola nazwa, wartoscCechy i plik z tabeli obiekty dla wodospadów z Polski (w warunku można zastosować pole idRodzaj = 10).
Dwa warunki naraz (AND): rodzaj 10 to wodospad, państwo zawęża do Polski. Każdy warunek w osobnej linii - łatwo dopisać trzeci albo zakomentować jeden. Wynik: Kamieńczyk, Mała Siklawa, Wielka Siklawa, Wilczki (R.1.4), zrzut kw2.png.
Typowe błędy
Sam warunek idRodzaj = 10 zwraca 12 wodospadów z całego świata - klucz wymaga obu warunków.
R.1.5 Wykonane zapytanie 3 wybierające jedynie pola idObiekt, panstwo, nazwa i wartoscCechy z tabeli obiekty dla pól idRodzaj = 10 i idKontynent = 6. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT idObiekt, panstwo, nazwa, wartoscCechy FROM obiekty WHERE idRodzaj = 10 AND idKontynent = 6; oraz na zrzucie widocznych jest dokładnie 8 rekordów o id: 42, 43, 44, 45, 47, 48, 53, 54
ok. 2 min
Polecenie z arkusza
Zapytanie 3: wybierające jedynie pola idObiekt, panstwo, nazwa i wartoscCechy z tabeli obiekty dla pól idRodzaj = 10 i idKontynent = 6.
Te same dwa warunki, zamiast państwa kontynent 6 (Europa). Wynik: osiem wierszy o identyfikatorach 42, 43, 44, 45, 47, 48, 53, 54 (R.1.5), zrzut kw3.png. Na stronie w miejscu 6 stanie identyfikator kontynentu z adresu.
R.1.6 Wykonane zapytanie 4 wstawiające do tabeli osiagniecia rekord o wartościach: idObiekt = 34, idTurysta = 2. Klucz podstawowy nadawany automatycznie. W pliku z kwerendami lub na zrzucie istnieje zapis np.: INSERT INTO osiagniecia(`idObiekt`, `idTurysta`) VALUES (34, 2); (klucz główny pominięty lub NULL) oraz na zrzucie widoczny jest komunikat o poprawnie dodanym rekordzie lub tabela z dodanym rekordem
ok. 2 min
Polecenie z arkusza
Zapytanie 4: wstawiające do tabeli osiagniecia rekord o wartościach: idObiekt = 34, idTurysta = 2. Klucz podstawowy nadawany automatycznie.
Lista kolumn podana jawnie, bez klucza idOsiagniecie - baza nada go sama (AUTO_INCREMENT), tu 1. Jawna lista kolumn sprawia też, że zapytanie przeżyje dopisanie do tabeli nowej kolumny. Zrzut z komunikatem albo tabelą: kw4.png (R.1.6).
R.1.7 Wykonane zapytanie 5 wybierające identyfikatory i wszystkie pola nick turystów. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT idTurysta, nick FROM turysci; oraz na zrzucie widocznych jest dokładnie 13 rekordów
ok. 1 min
Polecenie z arkusza
Zapytanie 5: wybierające pola idTurysta i nick z tabeli turysci.
Dwie kolumny, 13 turystów (R.1.7), zrzut kw5.png. Identyfikator trafi do value opcji listy, nick będzie jej treścią.
Kroki 9-25 · po tym etapie masz 19 z 41 kryteriów (pkt)
Krok 9: Grafika siklawica.jpg: szerokość 133 px
Kryteria:
R.2.1 Grafika siklawica.jpg ma szerokość 133 px i wysokość 200 px (± 5 px)
ok. 3 min
Polecenie z arkusza
Grafikę należy przeskalować z zachowaniem proporcji do szerokości 133 px.
Oryginał ma 1000 × 1500 px. Obraz → Skaluj obraz, szerokość 133 px przy zamkniętym łańcuchu - wysokość wychodzi 200 px (R.2.1: 133 × 200 ±5). Eksport pod tą samą nazwą.
Typowe błędy
Otwarty łańcuch proporcji zostawia wysokość 1500 px - zdjęcie jest zniekształcone.
Podgląd strony po tym kroku
Krok 10: Plik index.php w HTML5
Kryteria:
R.2.2 W pliku index.php znajduje się deklaracja HTML5 <!DOCTYPE HTML> i deklaracja języka zawartości: polski np. <html lang="pl">
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 zdającego. <!DOCTYPE html> włącza tryb standardów HTML5, dalej szkielet: <html>, <head> i <body>. Rozszerzenie .php, bo na początku pliku stanie kod PHP.
R.2.3 W sekcji head znajduje się jawnie podany standard kodowania polskich znaków i tytuł strony "Wodospady"
ok. 1 min
Polecenie z arkusza
Jawnie zastosowany właściwy standard kodowania polskich znaków.
<meta charset="UTF-8"> jako pierwszy element <head>. Ten sam standard ustawi połączenie z bazą (charset=utf8mb4 w PDO), więc polskie litery przejdą bez zmian całą drogę: baza → PHP → przeglądarka.
R.2.3 W sekcji head znajduje się jawnie podany standard kodowania polskich znaków i tytuł strony "Wodospady"
ok. 1 min
Polecenie z arkusza
Tytuł strony „Wodospady”.
<title>Wodospady</title> - napis na karcie przeglądarki. Obok <meta name="viewport">: bez niego telefon pokazałby stronę pomniejszoną jak na monitorze. Arkusz tego nie wymaga, a w każdym współczesnym szablonie ta linia jest.
R.2.4 W sekcji body zdefiniowany jest podział na bloki za pomocą semantycznych znaczników HTML5: header, main -> aside + section, article, footer (dopuszcza się stosowanie bloków pomocniczych)
ok. 2 min
Polecenie z arkusza
Podział strony na bloki: nagłówkowy, główny, artykułu, stopki. Blok główny zawiera blok boczny i blok sekcji. 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ą 3.
header, main z aside i section, potem article i footer (R.2.4). Artykuł i stopka stoją poza main - na ilustracji 3 zajmują całą szerokość pod blokiem głównym.
R.2.6 W bloku sekcji znajduje się tabela o czterech kolumnach (znaczniki <table>, <tr>, <td>; w skrypcie lub HTML )
R.2.7 Pierwszy wiersz tabeli zawiera cztery komórki nagłówkowe (znaczniki <tr>, <th> )
ok. 2 min
Polecenie z arkusza
Zawartość bloku sekcji: tabela o czterech kolumnach. Pierwszy wiersz jest wierszem nagłówkowym z komórkami: „Identyfikator”, „Państwo”, „Nazwa wodospadu”, „Wysokość”. Tabela jest wypełniona skryptem 2.
Wiersz z czterema komórkami <th> (R.2.6, R.2.7) w części <thead>, a miejsce na dane w <tbody>. Podział mówi przeglądarce i czytnikom ekranu, który wiersz opisuje kolumny; przy wydruku długiej tabeli thead powtarza się na każdej stronie. Wiersze z danymi wstawi szablon PHP do tbody.
R.3.4 Formularz wysyła dane metodą POST do tego samego pliku (poprawne również bez atrybutu action )
ok. 2 min
Polecenie z arkusza
Formularz wysyłający dane metodą bezpieczną do tego samego pliku. Formularz zawiera pola rozmieszczone zgodnie z ilustracją 2.
method="post" - dane w treści żądania. Bez atrybutu action formularz wysyła dane pod bieżący adres razem z ?id=…, więc wybrany kontynent zostaje (R.3.4).
Etykieta o treści: „identyfikator wodospadu”. Pole edycyjne typu numerycznego połączone z etykietą.
<label for="waterfall"> łączy się z polem po id, type="number" przyjmuje tylko liczby (R.3.6). Dodatkowo min="1" i required: przeglądarka nie wyśle pustego pola ani zera i od razu pokaże podpowiedź. To wygoda dla użytkownika, nie zabezpieczenie - żądanie można wysłać z pominięciem przeglądarki, więc PHP sprawdzi wartość jeszcze raz.
R.2.8 W bloku sekcji znajduje się formularz z 2 etykietami, polem edycyjnym, listą rozwijaną (może być pusta) , i przyciskiem "Wpisz". Co najmniej jedno pole formularza ma powiązaną etykietę (label z for i id lub zagnieżdżenie kontrolki w label)
R.3.5 Przycisk realizuje funkcję wysłania danych z formularza (input lub button z typem submit lub button bez typu )
ok. 1 min
Polecenie z arkusza
Przycisk o treści „Wpisz” wysyłający dane do skryptu 4.
<button type="submit"> wysyła formularz (R.2.8, R.3.5). Elementy formularza są liniowe, więc stoją w jednym wierszu jak na ilustracji 2.
R.2.5 W bloku nagłówkowym znajduje się <h2>: "Łowcy wodospadów", w bloku sekcji <h4>: "Wpisz osiągnięcie do bazy", w bloku artykułu <h3>: "Wodospady w Polsce" oraz <p> z numerem zdającego w stopce
ok. 1 min
Polecenie z arkusza
Zawartość bloku stopki: paragraf o treści „Autor: ”, dalej wstawiony numer zdającego.
Paragraf w <footer> (R.2.5 - razem z nagłówkami h2, h4, h3). Tu stoi nazwisko autora rozwiązania - na egzaminie wpisujesz swój numer zdającego (PESEL).
Kroki 26-35 · po tym etapie masz 30 z 41 kryteriów (pkt)
Krok 26: Zmienne CSS z wartościami arkusza
ok. 2 min
Polecenie z arkusza
Styl CSS zdefiniowany jest w całości w zewnętrznym pliku o nazwie styl.css.
Plik styl.css zaczyna się od zmiennych w :root: kolory z arkusza i zaokrąglenie 20px, które dotyczy trzech bloków. W regułach występują jako var(--radius) - zmiana zaokrąglenia całej strony to jedna linia.
Krok 27: Krój i wyrównanie dla wszystkich selektorów
Kryteria:
R.4.1 domyślne formatowanie selektorów (*): krój czcionki Garamond - jako pierwszy, serif - jako ostatni, wyrównanie tekstu do środka
ok. 1 min
Polecenie z arkusza
Domyślnie, dla wszystkich selektorów: krój czcionki Garamond, w przypadku braku serif, wyrównanie tekstu do środka.
Selektor *: najpierw Garamond, na końcu rodzina zastępcza serif (R.4.1). text-align z selektora * dostaje każdy element osobno, nie tylko dziedziczy od rodzica.
Dla bloku nagłówkowego i stopki: kolor tła SkyBlue, marginesy wewnętrzne 8 px, zewnętrzne 10 px, zaokrąglenie rogów 20 px.
Te same marginesy i zaokrąglenie ma też blok artykułu (następne zdanie arkusza), więc wspólna część trzech bloków jest w jednej regule header, footer, article, a różni je tylko kolor tła. Zmiana odstępu w całej stronie to wtedy jedna linia, a nie trzy.
R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z ilustracją 3 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)
R.4.4 wysokość: blok sekcji - 300px, blok artykułu - 280px szerokość: blok boczny - 30%, blok sekcji - 70% (width lub grid-template-columns lub flex ), selektor table - 90%, odnośnik - 65%
ok. 2 min
Polecenie z arkusza
Dla bloku bocznego: szerokość 30%.
Blok główny staje się siatką o dwóch kolumnach: 30% na blok boczny i 70% na sekcję - obie szerokości z arkusza w jednej linii grid-template-columns (klucz R.4.4 przyjmuje je w siatce). Dzieci siatki same trafiają do kolejnych kolumn, a artykuł i stopka poza main zostają pod spodem - bez float i clear (R.3.1).
W wersji podstawowej to samo robi display: flex z szerokościami w regułach bloków. Siatka trzyma cały podział w jednym miejscu.
Typowe błędy
width: 30% na bloku bocznym wewnątrz siatki liczyłby się od szerokości kolumny, nie bloku głównego.
R.4.3 dla selektora img marginesy zewnętrzne (margin) - 4px
ok. 2 min
Polecenie z arkusza
Dla selektora obrazu: marginesy zewnętrzne 4 px, umieszczone obok siebie od lewej strony.
Tu zostaje float: left. Obrazy już stoją obok siebie, ale text-align: center z selektora * ustawia je na środku artykułu. Arkusz każe formatować obraz wyłącznie selektorem img (uwaga pod listą stylów), więc nie można przestawić wyrównania w artykule ani zrobić z niego siatki - a jedyną właściwością samego obrazu, która dosuwa go do lewej krawędzi, jest float. Artykuł ma stałą wysokość, więc pływające obrazy nie wystają poza niego (R.4.3).
Dla selektora tabeli: szerokość 90%, obramowanie kropkowane o szerokości 1 px i kolorze czarnym.
border: 1px dotted black (R.4.8). Dodatkowo margin: 0 auto - wolne 10% szerokości dzieli się po równo na boki i tabela stoi na środku sekcji, jak na ilustracji 2 (text-align nie przesuwa tabeli, tylko tekst).
R.4.9 styl odnośnika: blokowy sposób wyświetlania (display: block) , rozmiar czcionki - 130%, bez podkreślenia (text-decoration: none)
ok. 2 min
Polecenie z arkusza
Dla selektora odnośnika: blokowy sposób wyświetlania, kolor tła DodgerBlue, biały kolor czcionki, szerokość 65%, rozmiar czcionki 130%, marginesy zewnętrzne 10 px, bez podkreślenia.
display: block (tabela 2 arkusza) - dopiero blok przyjmuje szerokość 65% i stoi w osobnym wierszu. Właściwości w kolejności: układ, wymiary i odstępy, tekst, kolory (R.4.2, R.4.4, R.4.6, R.4.9). Odnośniki wypisze szablon PHP.
Kroki 36-47 · po tym etapie masz 41 z 41 kryteriów (pkt)
Krok 36: Połączenie z bazą przez PDO i funkcja e()
Kryteria: ,
R.5.1 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy wodospady oraz ostatnią operacją na bazie jest jej zamknięcie
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 wodospady.
Kod PHP stoi na samym początku pliku: najpierw wszystkie operacje na bazie, potem szablon, który tylko wypisuje gotowe dane. Klucz R.5 przyjmuje PDO tak samo jak MySQLi (R.5.1, R.5.2).
e() - każdy tekst z bazy przejdzie przez htmlspecialchars z ENT_QUOTES, więc nick z < czy cudzysłowem nie rozbije listy ani nie wykona się jako skrypt (XSS); typ ?string przyjmuje też null z pustych kolumn,
new PDO(...) - połączenie i wybór bazy wodospady: pierwszy argument to DSN, czyli adres bazy dla PDO w jednym napisie (mysql:host=localhost;dbname=wodospady;charset=utf8mb4, z kodowaniem połączenia utf8mb4), dalej użytkownik root i puste hasło,
ERRMODE_EXCEPTION (błąd SQL = wyjątek) i FETCH_ASSOC (wiersze z nazwami kolumn),
try … catch - przy awarii bazy ogólny komunikat i kod 500 zamiast treści błędu z nazwą serwera.
Od tego kroku podgląd to prawdziwy wynik strony z serwera.
Typowe błędy
Klucz sprawdza połączenie i wybór bazy wodospady w kodzie źródłowym strony (R.5.1), więc DSN z nazwą bazy stoi w index.php, a nie w dołączanym pliku.
Krok 38: Skrypt 1: odnośniki kontynentów z parametrem id
Kryteria: ,
R.3.3 Przynajmniej jeden odnośnik z nazwą kontynentu prowadzi do strony index.php (w skrypcie lub HTML )
R.5.4 Skrypt 1 dla wszystkich zwróconych rekordów tworzy odnośnik do pliku index.php , w którym identyfikator jest przesyłany metodą GET, a nazwa jest treścią odnośnika. Po kliknięciu w odnośnik zmienia się zawartość tabeli na odpowiadającą wybranemu kontynentowi
ok. 3 min
Polecenie z arkusza
Dla każdego zwróconego zapytaniem rekordu wyświetla odnośnik, którego treścią jest nazwa kontynentu. Odnośnik prowadzi do tej samej strony i przesyła identyfikator kontynentu jako parametr metody GET. Na ilustracji 4 jest przedstawiony adres URL po wybraniu odnośnika Europa.
W bloku bocznym pętla w składni szablonu (foreach (...): … endforeach;): odnośnik zostaje zwykłym HTML-em, a wartości wstawia <?= ... ?>. Parametr GET doklejony po znaku zapytania - index.php?id=6 jak na ilustracji 4 (tabela 4 arkusza). Atrybuty są w HTML-u, więc cudzysłowy nie wymagają ukośników jak w napisie dla echo (R.3.3, R.5.4).
Krok 39: Skrypt 2: identyfikator kontynentu z adresu
ok. 3 min
Polecenie z arkusza
Skrypt 2 pobiera wartość przesłaną metodą GET, a w przypadku braku - wartością 6. Wartość ta jest identyfikatorem kontynentu.
Jedno wyrażenie robi trzy rzeczy:
$_GET['id'] ?? 6 - wartość z adresu, a gdy parametru nie ma - 6 (Europa), bez ostrzeżenia „Undefined array key”,
filter_var(…, FILTER_VALIDATE_INT) przepuszcza tylko liczbę całkowitą, min_range 1 odrzuca zero i liczby ujemne,
opcja default podaje 6 także wtedy, gdy wartość jest zła (?id=abc, ?id=6 OR 1=1).
W $continentId jest więc zawsze poprawna liczba. Tablica $_GET stoi w kodzie wprost - klucz R.5.5 sprawdza, że identyfikator jest pobierany metodą GET.
Krok 40: Skrypt 2: zapytanie 3 dla wybranego kontynentu
Kryteria:
R.5.5 Skrypt 2 zawiera instrukcję wysyłającą do bazy zapytanie 3, zmodyfikowane tak, że identyfikator kontynentu jest pobrany metodą GET, gdy $_GET istnieje
ok. 2 min
Polecenie z arkusza
Wysyła do bazy danych zapytanie 3, zmodyfikowane tak, że sprawdzany jest identyfikator kontynentu, patrz poprzedni punkt.
Zapytanie 3 jako zapytanie przygotowane: w miejscu 6 parametr :continentId, wartość osobno w execute (R.5.5). Nawet gdyby walidacja coś przepuściła, wartość nie stanie się częścią polecenia SQL.
R.5.6 Skrypt 2: wszystkie rekordy zwrócone zapytaniem 3 wyświetla jako kolejne wiersze tabeli, pola są wstawione w odpowiednie komórki
ok. 3 min
Polecenie z arkusza
Dla każdego zwróconego zapytaniem rekordu wstawia wiersz tabeli, wypełniając kolejne komórki zwróconymi polami (np. jeśli wybrano odnośnik Europa - tabela zawiera dane wodospadów z Europy).
W tbody jeden <tr> na wodospad, cztery komórki po nazwach kolumn (R.5.6). Podgląd: osiem wodospadów Europy, jak na ilustracji 2. Po kliknięciu „Ameryka Północna” (?id=3) zostaje jeden wiersz (Yosemite), „Antarktyda” (?id=5) zostawia sam nagłówek - tam w bazie nie ma wodospadów.
R.5.3 Skrypt 1 zawiera instrukcję wysyłającą do bazy danych zapytanie 1 lub Skrypt 3 zawiera instrukcję wysyłającą do bazy danych zapytanie 5
ok. 1 min
Polecenie z arkusza
Skrypt 3 wysyła do bazy danych zapytanie 5.
PDO::FETCH_KEY_PAIR zamienia wynik dwóch kolumn w tablicę „klucz → wartość”: $tourists[1] to „Ewa”. Dla listy rozwijanej to dokładnie para value → treść opcji (R.5.3).
Krok 43: Skrypt 3: opcje z identyfikatorem w value
Kryteria: ,
R.3.6 Pole edycyjne jest typu number. Lista rozwijana zawiera co najmniej jedno imię (<option> w skrypcie lub HTML )
R.5.7 Skrypt 3: wszystkie zwrócone nicki są wyświetlane w liście rozwijanej jako opcje, a identyfikatory przypisane są do atrybutu value
ok. 2 min
Polecenie z arkusza
Kolejne rekordy są opcjami listy rozwijanej tak, że atrybut wartości to pole idTurysta, a treść to nick.
foreach ($tourists as $touristId => $nick) - klucz tablicy trafia do value, wartość jest treścią opcji (R.3.6, R.5.7). Formularz wyśle identyfikator, bo tabela osiagniecia przechowuje idTurysta, nie nick. 13 turystów, pierwsza Ewa.
R.5.8 Skrypt 4 działa tylko wtedy, gdy dane zostały wysłane z formularza (wystarczy sprawdzenie jednej kontrolki )
ok. 3 min
Polecenie z arkusza
Skrypt 4 działa tylko w przypadku, gdy z formularza zostały przesłane dane.
Pola formularza czyta filter_input(INPUT_POST, …) z filtrem liczby całkowitej: wynik to liczba, false dla złej wartości (puste pole, „abc”) albo null, gdy formularza w ogóle nie wysłano. Warunek if ($waterfallId && $touristId) jest prawdziwy tylko wtedy, gdy przyszły dwie poprawne liczby - przy zwykłym wejściu na stronę i przy pustym polu zapis się nie wykona (R.5.8).
filter_input czyta dane prosto z żądania, nie z tablicy $_POST, której wartości można w kodzie przypadkiem nadpisać. Przy identyfikatorze kontynentu zostało $_GET, bo tam klucz szuka go w kodzie.
R.5.9 Skrypt 4 wysyła do bazy danych zapytanie 4, dane pobrane z formularza są wstawiane do bazy jako identyfikatory: obiektu i turysty (sprawdzić w phpMyAdmin dla dwóch wstawień, należy podawać tylko id z tabeli )
ok. 3 min
Polecenie z arkusza
Wysyła do bazy danych zapytanie 4, zmodyfikowane w ten sposób, że do bazy wstawione są wartości pobrane z formularza (zakłada się że użytkownik wpisuje tylko identyfikatory wyświetlone w tabeli i nie trzeba sprawdzać poprawności identyfikatora).
Zapytanie 4 przygotowane, z parametrami nazwanymi w miejscu 34 i 2 (R.5.9). Podgląd: stan po wysłaniu wodospadu 42 z turystką Ewą - strona wygląda tak samo, nowy rekord widać w phpMyAdmin w tabeli osiagniecia.
Krok 46: Skrypt 4: komunikat, błąd klucza obcego i przekierowanie
ok. 5 min
Polecenie z arkusza
Skrypt 4 wysyła do bazy danych zapytanie 4, zmodyfikowane w ten sposób, że do bazy wstawione są wartości pobrane z formularza.
Arkusz zwalnia ze sprawdzania identyfikatora, ale tabela osiagniecia ma klucz obcy: wodospad 999, którego nie ma w bazie, baza odrzuca. Bez obsługi strona kończyłaby się błędem „Integrity constraint violation”. Dlatego:
try/catch wokół zapisu łapie wyjątek; kod 23000 to naruszenie więzów (klucz obcy) - wtedy komunikat dla użytkownika, a każdy inny błąd idzie dalej (throw) do ogólnego catch,
po udanym zapisie wzorzec PRG (Post/Redirect/Get): komunikat trafia do sesji, header('Location: …', true, 303) każe przeglądarce otworzyć stronę od nowa GET-em - z tym samym ?id=, więc wybrany kontynent zostaje. Bez przekierowania F5 wysłałoby formularz drugi raz i dopisało drugie, identyczne osiągnięcie,
przy następnym wejściu $message odbiera komunikat z sesji i od razu go usuwa - pokaże się raz, w paragrafie pod formularzem, przez e().
Przekierowanie stoi po bloku try, przed HTML-em - nagłówek wysyła się przed treścią. Podgląd: próba zapisu wodospadu 999 z komunikatem.
Typowe błędy
header() po wypisaniu czegokolwiek (nawet pustej linii przed <?php) kończy się ostrzeżeniem „headers already sent” i przekierowanie nie działa.
R.5.1 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy wodospady 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ą.
W PDO połączenie zamyka się, gdy znika ostatnie odwołanie do obiektu. Zapytania przygotowane ($waterfallsQuery, $insert) też je trzymają, więc najpierw one, potem $db = null (R.5.1). Przypisanie null do zmiennej, która nie powstała (gdy formularza nie wysłano, $insert nie istnieje), jest bezpieczne.
Podgląd: strona po udanym zapisie i przekierowaniu - komunikat „Osiągnięcie zapisane.” pod formularzem.
Typowe błędy
$db->close() nie istnieje w PDO - to błąd „Call to undefined method”.
Krok 48 · po tym etapie masz 41 z 41 kryteriów (pkt)
Krok 48: Co jest inaczej i którą wersję wybrać na egzaminie
ok. 2 min
Polecenie z arkusza
Czas przeznaczony na wykonanie zadania wynosi 150 minut.
Obie wersje dają komplet 41 punktów. Różnice:
wersja podstawowa
wersja nowoczesna
blok główny
display: flex
grid-template-columns: 30% 70%
tabela
wiersze w <table>
thead i tbody
baza
funkcje mysqli_* z tabeli 1
PDO, wyjątki, połączenie w pliku strony
?id= z adresu
wklejony do SQL (?id=6 OR 1=1 pokazywał całą bazę)
filter_var z wartością domyślną + parametr
formularz
isset i sklejony INSERT
filter_input, zapytanie przygotowane, try/catch
po zapisie
ta sama strona (F5 dopisuje drugi raz)
PRG z komunikatem w sesji
float został przy obrazach - arkusz pozwala formatować je tylko selektorem img.
Na egzaminie: wersja podstawowa używa tylko funkcji z tabeli 1 arkusza i jest krótsza. Wersję nowoczesną pisz, jeśli znasz PDO i filtry na pamięć - arkusz nie podaje ich składni. 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. Gotowa aplikacja dostałaby jeszcze token CSRF w formularzu (ukryte pole z losową wartością z sesji, sprawdzane przy zapisie - blokuje wysłanie formularza z cudzej strony); klucz tego nie wymaga.
Krok 49 · po tym etapie masz 41 z 41 kryteriów (pkt)
Krok 49: 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. Nagraj płytę z rezultatami pracy.
W folderze z numerem zdającego: import.jpg, index.php, kamienczyk.jpg, kw1.png … kw5.png, kwerendy.txt, przeglądarka.txt, siklawa.jpg, siklawica.jpg, styl.css, wilczki.jpg. Płytę sprawdź po nagraniu i opisz 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.