INF.03-06-26.01-SGStyczeń 2026 · 51 kroków · ok. 97 min · 39 kryteriów z klucza
Rozwiązanie krok po kroku: Portal diagnostyki chorób (Nowoczesny kod (grid, PDO))
To drugi sposób rozwiązania tego samego arkusza: portal do diagnostyki online, baza choroby z relacją n..m, grafika z napisem przyciętym do liter i jedna strona zdrowie.php z listą chorób zakaźnych, listą rozwijaną i objawami wybranej choroby. Obie wersje spełniają wszystkie 39 kryteriów klucza - różni je sposób pisania kodu, nie wynik.
Czym ta wersja różni się od podstawowej:
siatka CSS w bloku głównym (grid-template-columns: 50% 50%), kolory z arkusza w zmiennych CSS,
zapytania z aliasami tabel (AS c, AS co), grupowanie po identyfikatorze, nie po nazwie,
PHP na PDO z wyjątkami, cała logika na górze pliku, a w HTML tylko pętle foreach; tryby pobierania FETCH_COLUMN (sama lista nazw) i FETCH_KEY_PAIR (identyfikator → nazwa),
identyfikator z formularza sprawdzony filter_var i wstawiony do zapytania przygotowanego, lista pamięta wybraną chorobę, a choroba bez objawów dostaje komunikat zamiast pustej ramki.
Kroki nadal idą zdanie po zdaniu za treścią arkusza. Podgląd w każdym kroku z PHP to wynik prawdziwego uruchomienia kodu z tego kroku w PHP 8.2 na bazie z arkusza; po wysłaniu formularza pokazuje objawy COVID-19, jak na ilustracji 6. Baza i grafika pochodzą z prawdziwego archiwum pliki9-1.
Jak liczą się punkty. Klucz ma 39 kryteriów (R.1.1 - R.5.9), każde za 1 punkt. Próg zdania części praktycznej to 75%, czyli 30 punktów. Przy każdym kroku jest napisane, które kryteria spełnia; egzaminator ocenia spełnienie kryteriów z klucza, nie zgodność z tym kodem.
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 39 kryteriów (pkt)
Krok 1: Baza danych choroby
ok. 2 min
Polecenie z arkusza
Utwórz bazę danych o nazwie choroby, z zestawem polskich znaków (np. utf8_polish_ci)
Archiwum pliki9-1.7z ma hasło *ZdroWia!!! (gwiazdka na początku, trzy wykrzykniki). W phpMyAdmin: Nowa, nazwa choroby, porównywanie utf8_polish_ci, albo w zakładce SQL:
CREATE DATABASE choroby CHARACTER SET utf8 COLLATE utf8_polish_ci;
Typowe błędy
Skrypty łączą się z bazą po nazwie - literówka w choroby zabiera także punkt za połączenie (R.5.1).
Krok 2: Import tabel z pliku choroby.sql
ok. 2 min
Polecenie z arkusza
Z rozpakowanego archiwum zaimportuj tabele z pliku choroby.sql do utworzonej bazy
Zaznacz bazę choroby, potem Import → choroby.sql → Wykonaj. Powstają trzy tabele z ilustracji 1: choroby (pole zakazna z literą T albo N), objawy i tabela pośredniczącachoroby_objawy - każdy jej wiersz to para „ta choroba ma ten objaw”. Tak zapisuje się relację n..m: choroba ma wiele objawów, a ten sam objaw (np. gorączka) występuje w wielu chorobach.
Krok 3: Zrzut ekranu import.png
Kryteria:
R.1.1 Wykonany import tabel do bazy danych choroby - 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
Print Screen (cały ekran z paskiem zadań), wklej do edytora grafiki i zapisz jako import.png: komunikat o udanym imporcie i trzy tabele w drzewku bazy.
Typowe błędy
Wykadrowany zrzut albo zrzut bez paska zadań nie jest oceniany (uwaga w kluczu przy R.1).
Krok 4: Zapytanie 1: choroby zakaźne alfabetycznie
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 nazwy chorób zakaźnych, posortowane rosnąco według nazwy. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa FROM choroby WHERE zakazna = 'T' ORDER BY nazwa; oraz na zrzucie widocznych jest dokładnie 7 rekordów z danymi: angina, COVID-19, grypa, odra, ospa, różyczka, świnka
ok. 2 min
Polecenie z arkusza
Zapytanie 1: wybierające jedynie nazwy chorób zakaźnych, posortowane rosnąco według nazwy
WHERE zakazna = 'T' - choroby zakaźne; litera w apostrofach, bo pole jest tekstowe,
ORDER BY nazwa - rosnąco (ASC jest domyślne).
Wynik: angina, COVID-19, grypa, odra, ospa, różyczka, świnka (R.1.3). „COVID-19” stoi między „angina” a „grypa”, bo porównywanie utf8_polish_ci z tabeli nie rozróżnia wielkości liter, a „ś” jest w polskim alfabecie za „s”. Zapytania zapisuj w kwerendy.txt z komentarzem -- nad każdym, zrzut jako kw1.png.
kwerendy.txtTekstnowy plik
-- Zapytanie 1SELECT nazwaFROM chorobyWHERE zakazna = 'T'ORDER BY nazwa;
Podgląd strony po tym kroku
Krok 5: Zapytanie 2: identyfikatory i nazwy chorób
Kryteria:
R.1.4 Wykonane zapytanie 2 wybierające jedynie identyfikatory i nazwy dla wszystkich chorób. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT id, nazwa FROM choroby; oraz na zrzucie widocznych jest dokładnie 16 rekordów, id od 1 do 16
ok. 1 min
Polecenie z arkusza
Zapytanie 2: wybierające jedynie identyfikatory i nazwy dla wszystkich chorób
Dwie kolumny bez warunku: 16 wierszy, identyfikatory 1 - 16 (R.1.4). Z tego zapytania Skrypt 2 zrobi opcje listy rozwijanej. W bazie choroba Kawasaki jest zapisana jako „Kavasaki” - dane z arkusza zostają bez zmian. Zrzut: kw2.png.
R.1.5 Wykonane zapytanie 3 wybierające jedynie nazwy objawów dla choroby o id 5. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa FROM objawy JOIN choroby_objawy ON id = id_objawy WHERE id_choroby = 5; oraz na zrzucie widocznych jest dokładnie 6 rekordów: ból głowy, gorączka, mdłości, rozwolnienie, wymioty, drgawki
ok. 3 min
Polecenie z arkusza
Zapytanie 3: wybierające jedynie nazwy objawów dla choroby o id 5. Należy posłużyć się relacją
FROM objawy AS o JOIN choroby_objawy AS co - aliasyo i co skracają nazwy tabel w całym zapytaniu,
ON co.id_objawy = o.id - relacja: objaw łączy się z wierszami tabeli pośredniczącej, które go wskazują,
WHERE co.id_choroby = 5 - tylko pary choroby 5 (jelitówki).
Tabeli choroby nie trzeba: identyfikator choroby jest już w tabeli pośredniczącej. Obie tabele mają kolumnę nazwa, więc o.nazwa mówi, o którą chodzi. Wynik: ból głowy, gorączka, mdłości, rozwolnienie, wymioty, drgawki (R.1.5). Zrzut: kw3.png.
kwerendy.txtTekstzmienione: 6 linii
-- Zapytanie 1SELECT nazwaFROM chorobyWHERE zakazna = 'T'ORDER BY nazwa;-- Zapytanie 2SELECT id, nazwaFROM choroby;-- Zapytanie 3SELECT o.nazwaFROM objawy AS oJOIN choroby_objawy AS co ON co.id_objawy = o.idWHERE co.id_choroby = 5;
Podgląd strony po tym kroku
Krok 7: Zapytanie 4: liczba objawów chorób zakaźnych
Kryteria:
R.1.6 Wykonane zapytanie 4 wybierające jedynie nazwy chorób oraz wyliczoną dla nich liczbę objawów zgrupowanych dla każdej choroby zakaźnej. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT choroby.nazwa, COUNT(objawy.id) FROM choroby JOIN choroby_objawy ON choroby.id = id_choroby JOIN objawy ON objawy.id = id_objawy WHERE zakazna = 'T' GROUP by choroby.nazwa; (lub porównanie kluczy po WHERE) oraz na zrzucie widocznych jest 7 rekordów z danymi: angina 9, COVID-19 11, grypa 8, odra 4, ospa 6, różyczka 7, świnka 6
ok. 4 min
Polecenie z arkusza
Zapytanie 4: wybierające jedynie nazwy chorób oraz wyliczoną dla nich liczbę objawów zgrupowane dla każdej choroby zakaźnej. Należy posłużyć się relacją n..m.
Relacja n..m to dwa złączenia przez tabelę pośredniczącą: choroby → choroby_objawy → objawy.
COUNT(o.id) AS liczba_objawow - liczba objawów w grupie, z aliasem zamiast nazwy COUNT(o.id),
WHERE c.zakazna = 'T' - tylko choroby zakaźne,
GROUP BY c.id, c.nazwa - grupa to jedna choroba, rozpoznawana po kluczu głównym. Grupowanie po samej nazwie skleiłoby dwie różne choroby o tej samej nazwie w jedną; nazwa jest w GROUP BY obok id, bo trafia do SELECT.
Bez warunku WHERE wynik ma 14 wierszy (wszystkie choroby, które mają objawy) zamiast 7.
kwerendy.txtTekstzmienione: 8 linii
-- Zapytanie 1SELECT nazwaFROM chorobyWHERE zakazna = 'T'ORDER BY nazwa;-- Zapytanie 2SELECT id, nazwaFROM choroby;-- Zapytanie 3SELECT o.nazwaFROM objawy AS oJOIN choroby_objawy AS co ON co.id_objawy = o.idWHERE co.id_choroby = 5;-- Zapytanie 4SELECT c.nazwa, COUNT(o.id) AS liczba_objawowFROM choroby AS cJOIN choroby_objawy AS co ON co.id_choroby = c.idJOIN objawy AS o ON o.id = co.id_objawyWHERE c.zakazna = 'T'GROUP BY c.id, c.nazwa;
Podgląd strony po tym kroku
Etap 2: Grafika i strona HTML
Kroki 8-27 · po tym etapie masz 18 z 39 kryteriów (pkt)
Krok 8: Grafika zdrowia.png: napis na przezroczystym tle
ok. 4 min
Polecenie z arkusza
Wykonaj grafikę zdrowia.png o cechach: Wysokość 100 px. Tło przeźroczyste. Zawartością jest tekst ZDROWIA zapisany wielkimi literami, dowolną czcionką bezszeryfową
W GIMP-ie Plik → Nowy, w opcjach zaawansowanych Wypełnienie: Przezroczystość. Narzędziem Tekst napis ZDROWIA krojem bezszeryfowym (DejaVu Sans, Arial, Verdana) w dużym rozmiarze, np. 200 px - przy pomniejszaniu krawędzie liter zostaną gładkie, a przy powiększaniu małego napisu by się rozmyły. Eksport do PNG, bo JPEG nie ma przezroczystości.
Podgląd strony po tym kroku
Krok 9: Grafika zdrowia.png: przycięcie do napisu i wysokość 100 px
Kryteria:
R.2.1 Grafika zdrowia.png ma wysokość dokładnie 100 px, tło przezroczyste oraz napis wielkimi literami ZDROWIA. Napis wypełnia w pionie całą grafikę, dopuszcza się przycięcie tuż przy napisie lub lekkie podcięcie liter tak, że zniekształci to np. literę o (kilka pikseli)
ok. 3 min
Polecenie z arkusza
Tekst ma wysokość 100 px i wypełnia w pionie całą grafikę. W poziomie należy przyciąć grafikę do szerokości napisu. Szerokość ta będzie uzależniona od rodzaju dobranej czcionki.
Kolejność ma znaczenie: najpierw Obraz → Przytnij do zawartości (obcina przezroczyste pasy, napis dotyka wszystkich krawędzi), potemObraz → Skaluj obraz z zamkniętym łańcuchem do wysokości 100 px. Tu wyszło 641 × 100 px; okrągła litera O wystaje odrobinę ponad Z i D, więc to ona wyznacza górę i dół (R.2.1).
Typowe błędy
Skalowanie przed przycięciem daje grafikę wysoką na 100 px, ale z pustymi pasami nad i pod napisem - tekst nie wypełnia wtedy grafiki w pionie.
Składa się ze strony o nazwie zdrowie.php. Zapisana w języku HTML5
<!DOCTYPE html> i szkielet. Nazwa pliku jest nietypowa - zdrowie.php, a nie index.php - więc w przeglądarce trzeba ją podać w adresie: http://localhost/<folder>/zdrowie.php.
R.2.3 W sekcji head znajduje się jawnie podany standard kodowania polskich znaków i tytuł strony "Wykaz chorób”
ok. 1 min
Polecenie z arkusza
Tytuł strony „Wykaz chorób”
<title> - tekst karty przeglądarki (z kodowaniem - R.2.3). <meta name="viewport"> pokazuje stronę na telefonie w jego prawdziwej szerokości; arkusz tego nie wymaga.
R.2.4 W sekcji body zdefiniowany jest podział na bloki za pomocą semantycznych znaczników HTML5: header, nav, main->section+section, footer (dopuszcza się zastosowanie bloków pomocniczych)
ok. 2 min
Polecenie z arkusza
Podział strony na bloki zrealizowany za pomocą semantycznych znaczników bloków języka HTML5 tak, aby po uruchomieniu w przeglądarce układ bloków na stronie był zgodny z ilustracją 4
Klucz R.2.4 wymienia znaczniki: header, nav, main z dwiema section, footer. Sekcje są jedynymi dziećmi <main> - siatka na <main> postawi je obok siebie.
Identyfikatory opisują zawartość: infectious (choroby zakaźne) i checker (sprawdzanie objawów). left/right przestałyby pasować po pierwszej zmianie układu.
R.3.3 Kliknięcie każdego odnośnika otwiera stronę w nowym oknie: Szpitale - https://szpitale.pl/, Przychodnie - https://www.przychodnie.pl/, NFZ - https://www.nfz.gov.pl/ (adresy stron z www lub bez)
ok. 2 min
Polecenie z arkusza
Wszystkie odnośniki otwierają się w nowym oknie przeglądarki
target="_blank" w każdym z trzech odnośników - klucz sprawdza kliknięciem wszystkie trzy (R.3.3). Obok rel="noopener": strona otwarta w nowej karcie nie dostaje odwołania do karty, z której ją otwarto, więc nie może jej po cichu podmienić na fałszywą. Współczesne przeglądarki robią to domyślnie dla _blank, ale jawny zapis działa wszędzie i dokumentuje intencję.
Typowe błędy
Jeden odnośnik bez target="_blank" wystarczy, żeby kryterium R.3.3 przepadło w całości. Linki w podglądzie są zablokowane - sprawdź je w przeglądarce.
Krok 21: Sekcja chorób zakaźnych: nagłówek i lista
ok. 1 min
Polecenie z arkusza
Zawartość bloku głównego: blok sekcji lewej i blok sekcji prawej. Zawartość bloku sekcji lewej: Nagłówek drugiego stopnia o treści „Choroby zakaźne”. Lista numerowana (uporządkowana) wypełniona skryptem 1
<h2> i pusta lista numerowana <ol> - numery 1 - 7 doda sama przeglądarka, elementy <li> wypisze Skrypt 1.
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
Zawartość bloku sekcji prawej: Nagłówek drugiego stopnia o treści „Objawy chorób”. Formularz wysyłający dane metodą bezpieczną do tego samego pliku
<h2> i <form method="post"> bez action - brak action to „ten sam plik” (R.3.4). „Metoda bezpieczna” w arkuszu to POST: dane w treści żądania, a nie w adresie. Ten formularz tylko pyta o dane i niczego nie zapisuje, więc w prawdziwym serwisie pasowałby do niego GET (adres z wybraną chorobą dałoby się zapisać w zakładkach), ale klucz ocenia POST.
<select name="disease"> - pod tą nazwą wybrany identyfikator trafi do $_POST. Na ilustracji lista nie ma podpisu, więc zamiast widocznej etykiety jest aria-label="Choroba" dla czytnika ekranu. Opcje wypisze Skrypt 2.
R.2.9 W bloku sekcji prawej znajduje się blok (np. div, section)
ok. 1 min
Polecenie z arkusza
Blok w którym umieszczony jest efekt działania skryptu 3
<div id="symptoms"> pod formularzem (R.2.9). Blok stoi w HTML zawsze - przed wysłaniem formularza jest pusty, ale ma kropkowaną ramkę z CSS, jak na ilustracji 3.
R.2.5 W bloku nagłówkowym znajduje się <h1>: „Informacja o chorobach w Polsce”, w bloku sekcji lewej <h2>: „Choroby zakaźne", w bloku sekcji prawej <h2>: „Objawy chorób” oraz <p> z numerem zdającego w stopce
ok. 1 min
Polecenie z arkusza
Zawartość stopki: paragraf o treści „Stronę opracował: ”, dalej wstawiony numer zdającego
Akapit w <footer>. Tu stoi imię i nazwisko autora rozwiązania - na egzaminie wpisujesz swój numer zdającego (PESEL). Są już nagłówki i akapit z R.2.5.
Kroki 28-38 · po tym etapie masz 29 z 39 kryteriów (pkt)
Krok 28: Zmienne CSS z kolorami arkusza
ok. 2 min
Polecenie z arkusza
Styl CSS zdefiniowany jest w całości w zewnętrznym pliku o nazwie styl.css. W tabeli 2 i 3 zawarto pomoc do niektórych właściwości CSS.
Trzy kolory w kodzie szesnastkowym trafiają raz do zmiennych CSS. #FFECB3 jest zarazem tłem prawej sekcji i kolorem odnośników - jedna zmienna --color-cream pilnuje, żeby to był naprawdę ten sam kolor. W regułach kolor pojawia się jako var(--color-cream).
Krok 29: Krój Trebuchet MS dla wszystkich selektorów
Kryteria:
R.4.1 domyślnie dla wszystkich selektorów (*): krój czcionki "Trebuchet MS", sans-serif (dozwolona tylko taka kolejność)
ok. 1 min
Polecenie z arkusza
Domyślnie dla wszystkich selektorów: krój czcionki Trebuchet MS, w przypadku braku sans-serif
Selektor * (R.4.1). Nazwę kroju ze spacją bierze się w cudzysłów: "Trebuchet MS" - bez niego przeglądarka mogłaby odczytać dwie osobne nazwy. Na końcu rodzina zapasowa sans-serif.
R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z ilustracją 4 w arkuszu egzaminacyjnym (prawidłowo zastosowano 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)
R.4.4 wysokość: blok sekcji lewej i prawej - 400px, szerokość: blok sekcji lewej i prawej - 50% (width lub grid-template-columns lub flex)
ok. 2 min
Polecenie z arkusza
Podział strony na bloki zrealizowany za pomocą semantycznych znaczników bloków języka HTML5 tak, aby po uruchomieniu w przeglądarce układ bloków na stronie był zgodny z ilustracją 4
<main> staje się siatką o dwóch kolumnach po 50%: choroby zakaźne po lewej, objawy po prawej. Nagłówek, menu i stopka są poza siatką, więc stoją nad i pod nią - bez float i clear (R.3.1). Szerokości w grid-template-columns i wysokość 400 px dają komplet R.4.4.
Typowe błędy
Odstęp gap przy kolumnach 50% + 50% wypchnąłby siatkę poza ekran - przy procentach z arkusza odstępów nie ma.
R.4.2 kolor tła: blok nagłówkowy, nawigacyjny, stopka - #FF8F00, blok sekcji lewej - #FFF8E1, blok sekcji prawej - #FFECB3, kolor czcionki: selektora a - #FFECB3
R.4.3 dla odnośnika rozmiar czcionki 180%, bez podkreślenia (text-decoration:none)
ok. 2 min
Polecenie z arkusza
Dla selektora odnośnika: kolor czcionki #FFECB3, jedynie margines zewnętrzny lewy 70 px, czcionka o rozmiarze 180%, bez podkreślenia
Selektor znacznika a (uwaga arkusza). „Jedynie margines zewnętrzny lewy” to margin-left - margin: 70px odsunąłby odnośnik ze wszystkich stron. Ta reguła zamyka kolory (R.4.2) i styl odnośnika (R.4.3).
R.4.10 gdy kursor znajdzie się na odnośniku jego kolor czcionki zmienia się na biały i dodawane jest podkreślenie
ok. 1 min
Polecenie z arkusza
Gdy kursor znajdzie się na odnośniku jego kolor czcionki zmienia się na biały i dodawane jest podkreślenie
Pseudoklasa :hover - kolor i podkreślenie naraz (R.4.10); underline przywraca to, co zdjęła poprzednia reguła. Ta sama zmiana dla :focus-visible: kto przechodzi po menu klawiszem Tab, widzi, na którym odnośniku jest.
R.4.8 dla selektora img styl position: absolute; left: 100px; top: 500px; oraz obraz jest zawsze na wierzchu (np. z-index)
ok. 2 min
Polecenie z arkusza
Dla selektora obrazu: zawsze na wierzchu, pozycjonowanie na stronie - przesunięcie względem rodzica: 100 px od lewej krawędzi i 500 px od góry, element ten jest usuwany z normalnego przepływu dokumentu
Tabela 3 arkusza: przesunięcie względem rodzica z usunięciem z przepływu to position: absolute, a miejsce wyznaczają left i top (R.4.8). Obraz nie zajmuje już miejsca pod stopką. „Względem rodzica” znaczy dokładnie: względem najbliższego przodka z ustawionym position - tu żaden blok go nie ma, więc punktem odniesienia jest cała strona. z-index: 1 kładzie obraz nad zwykłymi blokami.
Typowe błędy
position: fixed też usuwa z przepływu, ale liczy przesunięcie od okna i obraz nie przewija się ze stroną - to nie jest „względem rodzica”.
R.4.6 marginesy wewnętrzne (padding) : blok nagłówkowy, nawigacyjny i stopka - 10px, blok ze skryptem 3 - 30px, marginesy zewnętrzne (margin) : blok ze skryptem 3 - 50px 180px 0 50px, dla odnośnika margin-left: 70px
R.4.7 dla bloku ze skryptem 3: obramowanie 1px dotted black
ok. 2 min
Polecenie z arkusza
Dla bloku, w którym umieszczony jest skrypt 3: obramowanie linią kropkowaną o szerokości 1 px i kolorze czarnym, marginesy wewnętrzne 30 px, marginesy zewnętrzne: góra 50 px, prawy 180 px, dół 0, lewy 50 px
Skrót border: 1px dotted black (R.4.7). Cztery wartości margin idą zgodnie z ruchem wskazówek zegara: góra, prawy, dół, lewy - 50px 180px 0 50px. Pusty blok ma 60 px wysokości z samego padding, dlatego na ilustracji 3 widać ramkę przed wysłaniem formularza. Komplet marginesów R.4.6.
Rozmiar czcionki dla selektorów span: drugiego 120%, czwartego 240%, piątego 150%, siódmego 160%, dziewiątego 130%, dziesiątego 190%. Należy wykonać za pomocą przeznaczonej do tego pseudoklasy
:nth-child(n) wybiera element, który jest n-tym dzieckiem swojego rodzica (R.4.9). Rodzicem spanów będzie blok #symptoms, w którym Skrypt 3 nie wypisze nic poza spanami - drugi span jest więc drugim dzieckiem.
Nowsze przeglądarki znają też :nth-child(2 of span) i :nth-of-type(2), które liczą tylko spany, ale klucz wymienia wprost span:nth-child(n) - przy nim zostaje.
Typowe błędy
nth-child liczy wszystkie dzieci, nie tylko spany - nagłówek albo akapit przed spanami w tym samym bloku przesunąłby numerację o jeden.
Kroki 39-48 · po tym etapie masz 39 z 39 kryteriów (pkt)
Krok 39: Połączenie PDO, funkcja escape() i zamknięcie
Kryteria: ,
R.5.1 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy choroby
R.5.2 Ostatnią operacją na bazie jest jej zamknięcie
ok. 4 min
Polecenie z arkusza
Łączą się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie choroby. Na końcu działania skrypt zamyka połączenie z serwerem
Cały PHP stoi na górze pliku, przed <!DOCTYPE html>: najpierw dane, potem HTML, który je tylko wypisuje. new PDO(...) z charset=utf8mb4 w napisie połączenia (R.5.1) i dwiema opcjami: ERRMODE_EXCEPTION - błąd SQL rzuca wyjątek, FETCH_ASSOC - wiersze z nazwami kolumn. try/catch zamienia nieudane połączenie na ogólny komunikat z kodem 500, bez szczegółów z treści wyjątku.
Obok funkcja escape() - htmlspecialchars z ENT_QUOTES dla wszystkiego, co trafi z bazy na stronę (ochrona przed XSS). Nazwa jest pełnym słowem, bo arkusz wymaga nazw mówiących, do czego służą.
Na dole bloku zamknięcie: $statement = null; (obiekt zapytania trzyma połączenie) i $pdo = null;. Zapytania będą dopisywane nad tymi liniami - zamknięcie zostaje ostatnią operacją na bazie (R.5.2). Od tego kroku podgląd to wynik prawdziwego uruchomienia w PHP 8.2.
Typowe błędy
PDO nie ma w tabeli 1 arkusza - nazwy stałych trzeba znać na pamięć. Kto nie jest pewny składni, pisze funkcjami mysqli_* z tabeli 1 (wersja podstawowa).
zdrowie.phpPHPzmienione: 21 linii
<?php// Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumntry {$pdo = new PDO('mysql:host=localhost;dbname=choroby;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);} catch (PDOException $exception) {http_response_code(500);exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');}functionescape(string|int|float|null$value): string{returnhtmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');}// Wszystkie dane pobrane - zamknięcie połączenia$statement = null;$pdo = null;?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Wykaz chorób</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Informacja o chorobach w Polsce</h1> </header> <nav> <ahref="https://szpitale.pl/"target="_blank"rel="noopener">Szpitale</a> <ahref="https://www.przychodnie.pl/"target="_blank"rel="noopener">Przychodnie</a> <ahref="https://www.nfz.gov.pl/"target="_blank"rel="noopener">NFZ</a> </nav> <main> <sectionid="infectious"> <h2>Choroby zakaźne</h2> <ol> </ol> </section> <sectionid="checker"> <h2>Objawy chorób</h2> <formmethod="post"> <selectname="disease"aria-label="Choroba"> </select> <buttontype="submit">Sprawdź</button> </form> <divid="symptoms"> </div> </section> </main> <footer> <p>Stronę opracował: Bartosz Bryniarski</p> </footer> <imgsrc="zdrowia.png"alt="Życzymy zdrowia!"></body></html>
Podgląd strony po tym kroku (zdrowie.php)
Krok 40: Skrypt 1: zapytanie 1
Kryteria:
R.5.3 Przynajmniej jeden skrypt zawiera instrukcję wysyłającą do bazy danych zapytanie
ok. 2 min
Polecenie z arkusza
Skrypt 1: Wysyła do bazy danych zapytanie 1
Zapytanie 1 przez $pdo->query(...) - nie ma w nim wartości od użytkownika, więc nie potrzeba prepare (R.5.3). fetchAll(PDO::FETCH_COLUMN) zwraca samą kolumnę nazwa jako zwykłą listę napisów: ['angina', 'COVID-19', …] - bez tablic w tablicy, bo z każdego wiersza potrzebne jest jedno pole. Zapytanie w cudzysłowach, bo w środku są apostrofy wokół T.
zdrowie.phpPHPzmienione: 3 linie
<?php// Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumntry {$pdo = new PDO('mysql:host=localhost;dbname=choroby;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);} catch (PDOException $exception) {http_response_code(500);exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');}functionescape(string|int|float|null$value): string{returnhtmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');}// Skrypt 1: choroby zakaźne (zapytanie 1)$infectious = $pdo->query("SELECT nazwa FROM choroby WHERE zakazna = 'T' ORDER BY nazwa")->fetchAll(PDO::FETCH_COLUMN);// Wszystkie dane pobrane - zamknięcie połączenia$statement = null;$pdo = null;?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Wykaz chorób</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Informacja o chorobach w Polsce</h1> </header> <nav> <ahref="https://szpitale.pl/"target="_blank"rel="noopener">Szpitale</a> <ahref="https://www.przychodnie.pl/"target="_blank"rel="noopener">Przychodnie</a> <ahref="https://www.nfz.gov.pl/"target="_blank"rel="noopener">NFZ</a> </nav> <main> <sectionid="infectious"> <h2>Choroby zakaźne</h2> <ol> </ol> </section> <sectionid="checker"> <h2>Objawy chorób</h2> <formmethod="post"> <selectname="disease"aria-label="Choroba"> </select> <buttontype="submit">Sprawdź</button> </form> <divid="symptoms"> </div> </section> </main> <footer> <p>Stronę opracował: Bartosz Bryniarski</p> </footer> <imgsrc="zdrowia.png"alt="Życzymy zdrowia!"></body></html>
R.5.4 Skrypt 1 wyświetla z każdego zwróconego zapytaniem 1 rekordu element <li>
R.2.7 W bloku sekcji lewej umieszczona jedna lista numerowana z przynajmniej jednym elementem, zastosowane znaczniki <ol>, <li> (w skrypcie lub HTML)
ok. 2 min
Polecenie z arkusza
W każdym elemencie listy umieszcza zwrócone zapytaniem pole rekordu
Pętla foreach ... endforeach - składnia alternatywna PHP do przeplatania z HTML: jeden <li> na chorobę (R.5.4, R.2.7), numery 1 - 7 dopisuje przeglądarka, bo lista to <ol>. <?= ... ?> to skrót od <?php echo ... ?>. Podgląd: siedem chorób jak na ilustracji 3.
zdrowie.phpPHPzmienione: 3 linie
<?php// Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumntry {$pdo = new PDO('mysql:host=localhost;dbname=choroby;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);} catch (PDOException $exception) {http_response_code(500);exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');}functionescape(string|int|float|null$value): string{returnhtmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');}// Skrypt 1: choroby zakaźne (zapytanie 1)$infectious = $pdo->query("SELECT nazwa FROM choroby WHERE zakazna = 'T' ORDER BY nazwa")->fetchAll(PDO::FETCH_COLUMN);// Wszystkie dane pobrane - zamknięcie połączenia$statement = null;$pdo = null;?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Wykaz chorób</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Informacja o chorobach w Polsce</h1> </header> <nav> <ahref="https://szpitale.pl/"target="_blank"rel="noopener">Szpitale</a> <ahref="https://www.przychodnie.pl/"target="_blank"rel="noopener">Przychodnie</a> <ahref="https://www.nfz.gov.pl/"target="_blank"rel="noopener">NFZ</a> </nav> <main> <sectionid="infectious"> <h2>Choroby zakaźne</h2> <ol><?phpforeach ($infectiousas$diseaseName): ?> <li><?= escape($diseaseName) ?></li><?phpendforeach; ?> </ol> </section> <sectionid="checker"> <h2>Objawy chorób</h2> <formmethod="post"> <selectname="disease"aria-label="Choroba"> </select> <buttontype="submit">Sprawdź</button> </form> <divid="symptoms"> </div> </section> </main> <footer> <p>Stronę opracował: Bartosz Bryniarski</p> </footer> <imgsrc="zdrowia.png"alt="Życzymy zdrowia!"></body></html>
Podgląd strony po tym kroku (zdrowie.php)
Krok 42: Skrypt 2: zapytanie 2
ok. 2 min
Polecenie z arkusza
Skrypt 2: Wysyła do bazy danych zapytanie 2
PDO::FETCH_KEY_PAIR zamienia wynik z dwiema kolumnami w tablicę „pierwsza kolumna → druga”: [1 => 'angina', 2 => 'ospa', …]. Dokładnie tego potrzebuje lista rozwijana - identyfikator do value, nazwa do treści opcji.
zdrowie.phpPHPzmienione: 3 linie
<?php// Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumntry {$pdo = new PDO('mysql:host=localhost;dbname=choroby;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);} catch (PDOException $exception) {http_response_code(500);exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');}functionescape(string|int|float|null$value): string{returnhtmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');}// Skrypt 1: choroby zakaźne (zapytanie 1)$infectious = $pdo->query("SELECT nazwa FROM choroby WHERE zakazna = 'T' ORDER BY nazwa")->fetchAll(PDO::FETCH_COLUMN);// Skrypt 2: wszystkie choroby do listy rozwijanej (zapytanie 2) - identyfikator => nazwa$diseases = $pdo->query('SELECT id, nazwa FROM choroby')->fetchAll(PDO::FETCH_KEY_PAIR);// Wszystkie dane pobrane - zamknięcie połączenia$statement = null;$pdo = null;?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Wykaz chorób</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Informacja o chorobach w Polsce</h1> </header> <nav> <ahref="https://szpitale.pl/"target="_blank"rel="noopener">Szpitale</a> <ahref="https://www.przychodnie.pl/"target="_blank"rel="noopener">Przychodnie</a> <ahref="https://www.nfz.gov.pl/"target="_blank"rel="noopener">NFZ</a> </nav> <main> <sectionid="infectious"> <h2>Choroby zakaźne</h2> <ol><?phpforeach ($infectiousas$diseaseName): ?> <li><?= escape($diseaseName) ?></li><?phpendforeach; ?> </ol> </section> <sectionid="checker"> <h2>Objawy chorób</h2> <formmethod="post"> <selectname="disease"aria-label="Choroba"> </select> <buttontype="submit">Sprawdź</button> </form> <divid="symptoms"> </div> </section> </main> <footer> <p>Stronę opracował: Bartosz Bryniarski</p> </footer> <imgsrc="zdrowia.png"alt="Życzymy zdrowia!"></body></html>
Krok 43: Skrypt 2: opcje z identyfikatorem w value
Kryteria:
R.5.5 Skrypt 2 wyświetla z każdego zwróconego zapytaniem 2 rekordu opcję listy rozwijanej, tak że identyfikator jest przypisany atrybutowi value, a nazwa - treści opcji
ok. 2 min
Polecenie z arkusza
Każdym zwróconym rekordem wypełnia opcje listy w ten sposób, że identyfikator jest przypisany do atrybutu wartości opcji, a nazwa jest treścią opcji
foreach ($diseases as $id => $name) - pętla po tablicy z kluczami daje od razu klucz i wartość. value = identyfikator, treść = nazwa (R.5.5). Rozwiń listę w podglądzie: 16 chorób w kolejności identyfikatorów, od anginy do „Kavasaki”, jak na ilustracji 5.
zdrowie.phpPHPzmienione: 3 linie
<?php// Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumntry {$pdo = new PDO('mysql:host=localhost;dbname=choroby;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);} catch (PDOException $exception) {http_response_code(500);exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');}functionescape(string|int|float|null$value): string{returnhtmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');}// Skrypt 1: choroby zakaźne (zapytanie 1)$infectious = $pdo->query("SELECT nazwa FROM choroby WHERE zakazna = 'T' ORDER BY nazwa")->fetchAll(PDO::FETCH_COLUMN);// Skrypt 2: wszystkie choroby do listy rozwijanej (zapytanie 2) - identyfikator => nazwa$diseases = $pdo->query('SELECT id, nazwa FROM choroby')->fetchAll(PDO::FETCH_KEY_PAIR);// Wszystkie dane pobrane - zamknięcie połączenia$statement = null;$pdo = null;?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Wykaz chorób</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Informacja o chorobach w Polsce</h1> </header> <nav> <ahref="https://szpitale.pl/"target="_blank"rel="noopener">Szpitale</a> <ahref="https://www.przychodnie.pl/"target="_blank"rel="noopener">Przychodnie</a> <ahref="https://www.nfz.gov.pl/"target="_blank"rel="noopener">NFZ</a> </nav> <main> <sectionid="infectious"> <h2>Choroby zakaźne</h2> <ol><?phpforeach ($infectiousas$diseaseName): ?> <li><?= escape($diseaseName) ?></li><?phpendforeach; ?> </ol> </section> <sectionid="checker"> <h2>Objawy chorób</h2> <formmethod="post"> <selectname="disease"aria-label="Choroba"><?phpforeach ($diseasesas$id => $name): ?> <optionvalue="<?= escape($id) ?>"><?= escape($name) ?></option><?phpendforeach; ?> </select> <buttontype="submit">Sprawdź</button> </form> <divid="symptoms"> </div> </section> </main> <footer> <p>Stronę opracował: Bartosz Bryniarski</p> </footer> <imgsrc="zdrowia.png"alt="Życzymy zdrowia!"></body></html>
Podgląd strony po tym kroku (zdrowie.php)
Krok 44: Skrypt 3: tylko po wysłaniu formularza
Kryteria:
R.5.6 Skrypt 3 zawiera instrukcję sprawdzającą czy dane z formularza zostały wysłane oraz instrukcję pobierającą dane z tablicy globalnej odpowiadającej zastosowanej metodzie formularza ($_POST, $_GET, $_REQUEST)
ok. 3 min
Polecenie z arkusza
Skrypt 3, wykonywany tylko wtedy, gdy formularz wysłał dane (ilustracja 6)
isset($_POST['disease']) sprawdza, czy formularz wysłał dane - pole disease istnieje w $_POST dopiero po kliknięciu „Sprawdź” (R.5.6: instrukcja sprawdzająca i odczyt z tablicy $_POST).
Wartość z listy to tekst, a lista podsuwa tylko 1 - 16, ale żądanie POST da się wysłać z dowolną treścią. filter_var(..., FILTER_VALIDATE_INT, ...) zwraca liczbę całkowitą od 1 albo false - dla „5 OR 1=1” też false. W wersji podstawowej z egzaminu ta właśnie wartość wypisywała 74 objawy, czyli wszystkie powiązania z tabeli pośredniczącej.
Przed warunkiem $selectedId = null i $symptoms = [] - szablon może z nich korzystać także przy zwykłym wejściu. Od tego kroku podgląd pokazuje stronę po wybraniu COVID-19 (identyfikator 15).
zdrowie.phpPHPzmienione: 7 linii
<?php// Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumntry {$pdo = new PDO('mysql:host=localhost;dbname=choroby;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);} catch (PDOException $exception) {http_response_code(500);exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');}functionescape(string|int|float|null$value): string{returnhtmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');}// Skrypt 1: choroby zakaźne (zapytanie 1)$infectious = $pdo->query("SELECT nazwa FROM choroby WHERE zakazna = 'T' ORDER BY nazwa")->fetchAll(PDO::FETCH_COLUMN);// Skrypt 2: wszystkie choroby do listy rozwijanej (zapytanie 2) - identyfikator => nazwa$diseases = $pdo->query('SELECT id, nazwa FROM choroby')->fetchAll(PDO::FETCH_KEY_PAIR);// Skrypt 3: objawy choroby wybranej w formularzu (zapytanie 3)$selectedId = null;$symptoms = [];if (isset($_POST['disease'])) {$selectedId = filter_var($_POST['disease'], FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);}// Wszystkie dane pobrane - zamknięcie połączenia$statement = null;$pdo = null;?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Wykaz chorób</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Informacja o chorobach w Polsce</h1> </header> <nav> <ahref="https://szpitale.pl/"target="_blank"rel="noopener">Szpitale</a> <ahref="https://www.przychodnie.pl/"target="_blank"rel="noopener">Przychodnie</a> <ahref="https://www.nfz.gov.pl/"target="_blank"rel="noopener">NFZ</a> </nav> <main> <sectionid="infectious"> <h2>Choroby zakaźne</h2> <ol><?phpforeach ($infectiousas$diseaseName): ?> <li><?= escape($diseaseName) ?></li><?phpendforeach; ?> </ol> </section> <sectionid="checker"> <h2>Objawy chorób</h2> <formmethod="post"> <selectname="disease"aria-label="Choroba"><?phpforeach ($diseasesas$id => $name): ?> <optionvalue="<?= escape($id) ?>"><?= escape($name) ?></option><?phpendforeach; ?> </select> <buttontype="submit">Sprawdź</button> </form> <divid="symptoms"> </div> </section> </main> <footer> <p>Stronę opracował: Bartosz Bryniarski</p> </footer> <imgsrc="zdrowia.png"alt="Życzymy zdrowia!"></body></html>
R.5.7 Skrypt 3 wysyła zmodyfikowane zapytanie 3 tak, że w warunku podany jest identyfikator wysłany przez formularz
ok. 3 min
Polecenie z arkusza
Wysyła do bazy danych zmodyfikowane zapytanie 3 w ten sposób, że w warunku podany jest identyfikator wysłany przez formularz
Zapytanie 3 z kwerendy.txt, w którym zamiast 5 stoi parametr nazwany :disease; wartość idzie osobno w execute(['disease' => $selectedId]) (R.5.7). Baza zna strukturę zapytania, zanim zobaczy dane, więc wartość z formularza nigdy nie stanie się częścią SQL - to druga warstwa ochrony obok filter_var.
FETCH_COLUMN - lista samych nazw objawów. Zapytanie w kodzie PHP rozpisane w kilku liniach jak w kwerendy.txt; znaki nowej linii w napisie bazie nie przeszkadzają.
zdrowie.phpPHPzmienione: 9 linii
<?php// Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumntry {$pdo = new PDO('mysql:host=localhost;dbname=choroby;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);} catch (PDOException $exception) {http_response_code(500);exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');}functionescape(string|int|float|null$value): string{returnhtmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');}// Skrypt 1: choroby zakaźne (zapytanie 1)$infectious = $pdo->query("SELECT nazwa FROM choroby WHERE zakazna = 'T' ORDER BY nazwa")->fetchAll(PDO::FETCH_COLUMN);// Skrypt 2: wszystkie choroby do listy rozwijanej (zapytanie 2) - identyfikator => nazwa$diseases = $pdo->query('SELECT id, nazwa FROM choroby')->fetchAll(PDO::FETCH_KEY_PAIR);// Skrypt 3: objawy choroby wybranej w formularzu (zapytanie 3)$selectedId = null;$symptoms = [];if (isset($_POST['disease'])) {$selectedId = filter_var($_POST['disease'], FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);if ($selectedId !== false) {$statement = $pdo->prepare('SELECT o.nazwa FROM objawy AS o JOIN choroby_objawy AS co ON co.id_objawy = o.id WHERE co.id_choroby = :disease' );$statement->execute(['disease' => $selectedId]);$symptoms = $statement->fetchAll(PDO::FETCH_COLUMN); }}// Wszystkie dane pobrane - zamknięcie połączenia$statement = null;$pdo = null;?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Wykaz chorób</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Informacja o chorobach w Polsce</h1> </header> <nav> <ahref="https://szpitale.pl/"target="_blank"rel="noopener">Szpitale</a> <ahref="https://www.przychodnie.pl/"target="_blank"rel="noopener">Przychodnie</a> <ahref="https://www.nfz.gov.pl/"target="_blank"rel="noopener">NFZ</a> </nav> <main> <sectionid="infectious"> <h2>Choroby zakaźne</h2> <ol><?phpforeach ($infectiousas$diseaseName): ?> <li><?= escape($diseaseName) ?></li><?phpendforeach; ?> </ol> </section> <sectionid="checker"> <h2>Objawy chorób</h2> <formmethod="post"> <selectname="disease"aria-label="Choroba"><?phpforeach ($diseasesas$id => $name): ?> <optionvalue="<?= escape($id) ?>"><?= escape($name) ?></option><?phpendforeach; ?> </select> <buttontype="submit">Sprawdź</button> </form> <divid="symptoms"> </div> </section> </main> <footer> <p>Stronę opracował: Bartosz Bryniarski</p> </footer> <imgsrc="zdrowia.png"alt="Życzymy zdrowia!"></body></html>
R.5.8 Skrypt 3 wyświetla w znaczniku <span> każdy zwrócony zapytaniem objaw choroby np. <span> $wiersz[0] </span>
ok. 2 min
Polecenie z arkusza
Każdy zwrócony zapytaniem objaw choroby zapisuje w znaczniku span w ten sposób, że przed i po objawie jest spacja
Jeden <span> na objaw, a spacje stoją wewnątrz znacznika, przed i po nazwie: <span> <?= escape($symptom) ?> </span> (R.5.8). Spany są elementami liniowymi - bez tych spacji objawy zlepiłyby się w jeden wyraz.
Teraz działają reguły :nth-child: 11 objawów COVID-19, „bóle stawów” (czwarty span) największe, jak na ilustracji 6.
zdrowie.phpPHPzmienione: 3 linie
<?php// Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumntry {$pdo = new PDO('mysql:host=localhost;dbname=choroby;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);} catch (PDOException $exception) {http_response_code(500);exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');}functionescape(string|int|float|null$value): string{returnhtmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');}// Skrypt 1: choroby zakaźne (zapytanie 1)$infectious = $pdo->query("SELECT nazwa FROM choroby WHERE zakazna = 'T' ORDER BY nazwa")->fetchAll(PDO::FETCH_COLUMN);// Skrypt 2: wszystkie choroby do listy rozwijanej (zapytanie 2) - identyfikator => nazwa$diseases = $pdo->query('SELECT id, nazwa FROM choroby')->fetchAll(PDO::FETCH_KEY_PAIR);// Skrypt 3: objawy choroby wybranej w formularzu (zapytanie 3)$selectedId = null;$symptoms = [];if (isset($_POST['disease'])) {$selectedId = filter_var($_POST['disease'], FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);if ($selectedId !== false) {$statement = $pdo->prepare('SELECT o.nazwa FROM objawy AS o JOIN choroby_objawy AS co ON co.id_objawy = o.id WHERE co.id_choroby = :disease' );$statement->execute(['disease' => $selectedId]);$symptoms = $statement->fetchAll(PDO::FETCH_COLUMN); }}// Wszystkie dane pobrane - zamknięcie połączenia$statement = null;$pdo = null;?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Wykaz chorób</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Informacja o chorobach w Polsce</h1> </header> <nav> <ahref="https://szpitale.pl/"target="_blank"rel="noopener">Szpitale</a> <ahref="https://www.przychodnie.pl/"target="_blank"rel="noopener">Przychodnie</a> <ahref="https://www.nfz.gov.pl/"target="_blank"rel="noopener">NFZ</a> </nav> <main> <sectionid="infectious"> <h2>Choroby zakaźne</h2> <ol><?phpforeach ($infectiousas$diseaseName): ?> <li><?= escape($diseaseName) ?></li><?phpendforeach; ?> </ol> </section> <sectionid="checker"> <h2>Objawy chorób</h2> <formmethod="post"> <selectname="disease"aria-label="Choroba"><?phpforeach ($diseasesas$id => $name): ?> <optionvalue="<?= escape($id) ?>"><?= escape($name) ?></option><?phpendforeach; ?> </select> <buttontype="submit">Sprawdź</button> </form> <divid="symptoms"><?phpforeach ($symptomsas$symptom): ?> <span> <?= escape($symptom) ?> </span><?phpendforeach; ?> </div> </section> </main> <footer> <p>Stronę opracował: Bartosz Bryniarski</p> </footer> <imgsrc="zdrowia.png"alt="Życzymy zdrowia!"></body></html>
Podgląd strony po tym kroku (zdrowie.php)
Krok 47: Lista pamięta wybraną chorobę
ok. 2 min
Polecenie z arkusza
Każdym zwróconym rekordem wypełnia opcje listy w ten sposób, że identyfikator jest przypisany do atrybutu wartości opcji, a nazwa jest treścią opcji
Na ilustracji 6 po kliknięciu „Sprawdź” lista pokazuje „COVID-19”. Strona ładuje się od nowa, więc bez pomocy lista wróciłaby do anginy - nad objawami innej choroby. Opcja, której identyfikator jest równy wybranemu, dostaje atrybut selected. Porównanie ścisłe === działa, bo klucze tablicy z FETCH_KEY_PAIR są liczbami całkowitymi, a $selectedId po filter_var też.
zdrowie.phpPHPzmienione: 1 linia
<?php// Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumntry {$pdo = new PDO('mysql:host=localhost;dbname=choroby;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);} catch (PDOException $exception) {http_response_code(500);exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');}functionescape(string|int|float|null$value): string{returnhtmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');}// Skrypt 1: choroby zakaźne (zapytanie 1)$infectious = $pdo->query("SELECT nazwa FROM choroby WHERE zakazna = 'T' ORDER BY nazwa")->fetchAll(PDO::FETCH_COLUMN);// Skrypt 2: wszystkie choroby do listy rozwijanej (zapytanie 2) - identyfikator => nazwa$diseases = $pdo->query('SELECT id, nazwa FROM choroby')->fetchAll(PDO::FETCH_KEY_PAIR);// Skrypt 3: objawy choroby wybranej w formularzu (zapytanie 3)$selectedId = null;$symptoms = [];if (isset($_POST['disease'])) {$selectedId = filter_var($_POST['disease'], FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);if ($selectedId !== false) {$statement = $pdo->prepare('SELECT o.nazwa FROM objawy AS o JOIN choroby_objawy AS co ON co.id_objawy = o.id WHERE co.id_choroby = :disease' );$statement->execute(['disease' => $selectedId]);$symptoms = $statement->fetchAll(PDO::FETCH_COLUMN); }}// Wszystkie dane pobrane - zamknięcie połączenia$statement = null;$pdo = null;?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Wykaz chorób</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Informacja o chorobach w Polsce</h1> </header> <nav> <ahref="https://szpitale.pl/"target="_blank"rel="noopener">Szpitale</a> <ahref="https://www.przychodnie.pl/"target="_blank"rel="noopener">Przychodnie</a> <ahref="https://www.nfz.gov.pl/"target="_blank"rel="noopener">NFZ</a> </nav> <main> <sectionid="infectious"> <h2>Choroby zakaźne</h2> <ol><?phpforeach ($infectiousas$diseaseName): ?> <li><?= escape($diseaseName) ?></li><?phpendforeach; ?> </ol> </section> <sectionid="checker"> <h2>Objawy chorób</h2> <formmethod="post"> <selectname="disease"aria-label="Choroba"><?phpforeach ($diseasesas$id => $name): ?> <optionvalue="<?= escape($id) ?>"<?= $id === $selectedId ? ' selected' : ''?>><?= escape($name) ?></option><?phpendforeach; ?> </select> <buttontype="submit">Sprawdź</button> </form> <divid="symptoms"><?phpforeach ($symptomsas$symptom): ?> <span> <?= escape($symptom) ?> </span><?phpendforeach; ?> </div> </section> </main> <footer> <p>Stronę opracował: Bartosz Bryniarski</p> </footer> <imgsrc="zdrowia.png"alt="Życzymy zdrowia!"></body></html>
Podgląd strony po tym kroku (zdrowie.php)
Krok 48: Komunikat dla choroby bez objawów
Kryteria:
R.5.9 W skryptach zastosowana przynajmniej jedna zmienna oraz nazwy wszystkich zmiennych są w języku polskim lub angielskim i odzwierciedlają cel zastosowania
ok. 2 min
Polecenie z arkusza
Każdy zwrócony zapytaniem objaw choroby zapisuje w znaczniku span w ten sposób, że przed i po objawie jest spacja
Baza z arkusza ma dwie choroby bez ani jednego objawu: nosówkę (13) i „Kavasaki” (16) - wersja z egzaminu zostawia wtedy pustą ramkę i nie wiadomo, czy strona zadziałała. Pusta lista $symptoms daje komunikat „W bazie nie ma objawów tej choroby.”, a identyfikator odrzucony przez filter_var - „Wybierz chorobę z listy.”. Akapit stoi tylko wtedy, gdy spanów nie ma, więc numeracja :nth-child się nie przesuwa. Podgląd pokazuje stan po wybraniu nosówki.
Nazwy zmiennych mówią, co przechowują (R.5.9): $pdo, $infectious, $diseaseName, $diseases, $id, $name, $selectedId, $symptoms, $symptom, $statement.
zdrowie.phpPHPzmienione: 5 linii
<?php// Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumntry {$pdo = new PDO('mysql:host=localhost;dbname=choroby;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);} catch (PDOException $exception) {http_response_code(500);exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');}functionescape(string|int|float|null$value): string{returnhtmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');}// Skrypt 1: choroby zakaźne (zapytanie 1)$infectious = $pdo->query("SELECT nazwa FROM choroby WHERE zakazna = 'T' ORDER BY nazwa")->fetchAll(PDO::FETCH_COLUMN);// Skrypt 2: wszystkie choroby do listy rozwijanej (zapytanie 2) - identyfikator => nazwa$diseases = $pdo->query('SELECT id, nazwa FROM choroby')->fetchAll(PDO::FETCH_KEY_PAIR);// Skrypt 3: objawy choroby wybranej w formularzu (zapytanie 3)$selectedId = null;$symptoms = [];if (isset($_POST['disease'])) {$selectedId = filter_var($_POST['disease'], FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);if ($selectedId !== false) {$statement = $pdo->prepare('SELECT o.nazwa FROM objawy AS o JOIN choroby_objawy AS co ON co.id_objawy = o.id WHERE co.id_choroby = :disease' );$statement->execute(['disease' => $selectedId]);$symptoms = $statement->fetchAll(PDO::FETCH_COLUMN); }}// Wszystkie dane pobrane - zamknięcie połączenia$statement = null;$pdo = null;?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Wykaz chorób</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Informacja o chorobach w Polsce</h1> </header> <nav> <ahref="https://szpitale.pl/"target="_blank"rel="noopener">Szpitale</a> <ahref="https://www.przychodnie.pl/"target="_blank"rel="noopener">Przychodnie</a> <ahref="https://www.nfz.gov.pl/"target="_blank"rel="noopener">NFZ</a> </nav> <main> <sectionid="infectious"> <h2>Choroby zakaźne</h2> <ol><?phpforeach ($infectiousas$diseaseName): ?> <li><?= escape($diseaseName) ?></li><?phpendforeach; ?> </ol> </section> <sectionid="checker"> <h2>Objawy chorób</h2> <formmethod="post"> <selectname="disease"aria-label="Choroba"><?phpforeach ($diseasesas$id => $name): ?> <optionvalue="<?= escape($id) ?>"<?= $id === $selectedId ? ' selected' : ''?>><?= escape($name) ?></option><?phpendforeach; ?> </select> <buttontype="submit">Sprawdź</button> </form> <divid="symptoms"><?phpforeach ($symptomsas$symptom): ?> <span> <?= escape($symptom) ?> </span><?phpendforeach; ?><?phpif ($selectedId === false): ?> <p>Wybierz chorobę z listy.</p><?phpelseif ($selectedId !== null && $symptoms === []): ?> <p>W bazie nie ma objawów tej choroby.</p><?phpendif; ?> </div> </section> </main> <footer> <p>Stronę opracował: Bartosz Bryniarski</p> </footer> <imgsrc="zdrowia.png"alt="Życzymy zdrowia!"></body></html>
Podgląd strony po tym kroku (zdrowie.php)
Etap 5: Porównanie z wersją podstawową
Kroki 49-50 · po tym etapie masz 39 z 39 kryteriów (pkt)
Krok 49: Co jest inaczej i dlaczego
ok. 3 min
Polecenie z arkusza
Ocenie będzie podlegać 5 rezultatów: operacje na bazie danych, zawartość witryny internetowej, działanie witryny internetowej, styl CSS witryny internetowej oraz skrypt połączenia z bazą.
Czas przeznaczony na wykonanie zadania wynosi 150 minut.
Wersja podstawowa, gdy liczy się czas albo PDO nie jest opanowane: funkcje mysqli_* i isset są w tabeli 1 arkusza, a każdy skrypt stoi tam, gdzie widać jego efekt.
Ta wersja, gdy PDO i siatka CSS są pisane z pamięci. Tryby FETCH_COLUMN i FETCH_KEY_PAIR skracają pętle, ale trzeba je znać - pomyłka w nazwie stałej to błąd krytyczny.
Mieszanie też daje pełne punkty: siatka z tej wersji i mysqli_* z podstawowej.
Krok 51 · po tym etapie masz 39 z 39 kryteriów (pkt)
Krok 51: Plik przeglądarka.txt i nagranie płyty
ok. 5 min
Polecenie z arkusza
UWAGA: 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 przeglądarka.txt nazwa przeglądarki, w której strona była sprawdzana przez http://localhost/…/zdrowie.php.
W folderze z numerem zdającego: import.png, kw1.png - kw4.png, kwerendy.txt, przeglądarka.txt, styl.css, zdrowia.png, zdrowie.php. Po nagraniu sprawdź odczyt płyty.
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.