INF.03-07-26.01-SGStyczeń 2026 · 49 kroków · ok. 99 min · 38 kryteriów z klucza
Rozwiązanie krok po kroku: Aplikacja internetowa stacji meteorologicznej (Nowoczesny kod (grid, PDO))
To drugi sposób rozwiązania tego samego arkusza: stacja meteorologiczna z bazą pogoda, trzema ikonami wyciętymi z jednej grafiki i stroną index.php z tabelą temperatur lipca oraz średnią temperaturą miesiąca wybranego odnośnikiem. Obie wersje spełniają wszystkie 38 kryteriów klucza - różni je sposób pisania kodu, nie wynik.
Czym ta wersja różni się od podstawowej:
siatka CSS zamiast float + clear i flex: bloki nagłówkowe dostają szerokości z grid-template-columns: 20% 80%, sekcje z repeat(2, 50%), a kolory z arkusza stoją w zmiennych CSS,
tabela z <thead> i <tbody>, odnośniki miesięcy w bloku <nav>, generowane pętlą z tablicy PHP - numer w adresie i nazwa miesiąca nie mogą się rozjechać,
zapytania z aliasami tabel (AS m, AS p) i zapytanie 4 w kolejności kalendarza,
PHP na PDO z wyjątkami: obie operacje na bazie na górze pliku, połączenie zamknięte przed HTML-em, a w HTML tylko pętla foreach i proste warunki,
ikona pogody z funkcji weatherIcon() z wyrażeniem match, z tekstem alternatywnym,
numer miesiąca z adresu sprawdzony filter_var i wstawiony do zapytania przygotowanego; wartości z bazy wypisywane przez escape() (htmlspecialchars).
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; od skryptu 2 pokazuje stan po kliknięciu „Luty”, jak na ilustracji 5. Baza i obraz pogoda.png pochodzą z prawdziwego archiwum pliki9-2 (załącznik do arkusza, hasło w treści zadania).
Jak liczą się punkty. Klucz ma 38 kryteriów (R.1.1 - R.5.9), każde za 1 punkt. Próg zdania części praktycznej to 75%, czyli 29 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 38 kryteriów (pkt)
Krok 1: Baza danych pogoda
ok. 2 min
Polecenie z arkusza
Utwórz bazę danych o nazwie pogoda, z zestawem polskich znaków (np. utf8_polish_ci)
Archiwum pliki9-2.7z ma hasło &M3t3oPogoDa% (cyfry 3 zamiast „e”, znaki & i % na brzegach); w środku są pogoda.sql i pogoda.png. W phpMyAdmin: Nowa, nazwa pogoda, porównywanie utf8_polish_ci, albo w zakładce SQL:
CREATE DATABASE pogoda CHARACTER SET utf8 COLLATE utf8_polish_ci;
Typowe błędy
Skrypt łączy się z bazą po nazwie - literówka w pogoda zabiera także punkt za połączenie (R.5.1).
Krok 2: Import tabel z pliku pogoda.sql
ok. 2 min
Polecenie z arkusza
Z rozpakowanego archiwum zaimportuj tabele z pliku pogoda.sql do utworzonej bazy
Zaznacz bazę pogoda, potem Import → pogoda.sql → Wykonaj. Powstają trzy tabele z ilustracji 1: miejscowosc, miesiace i pomiary. Każdy wiersz pomiary to temperatura jednej miejscowości w jednym miesiącu - klucze obce id_miejscowosc i id_miesiac wskazują, której i w którym.
Krok 3: Zrzut ekranu import.png
Kryteria:
R.1.1 Wykonany import tabel do bazy danych pogoda - 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. Zrzuty zapytań w tym arkuszu są w formacie JPEG - tylko import idzie do PNG.
Typowe błędy
Wykadrowany zrzut albo zrzut bez paska zadań nie jest oceniany (uwaga w kluczu przy R.1).
Krok 4: Zapytanie 1: średnia temperatura lipca
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 liczące średnią temperaturę dla miesiąca o id = 7. Wyliczona wartość jest zaokrąglona do dwóch miejsc po przecinku. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT ROUND(AVG(temperatura), 2) FROM pomiary WHERE id_miesiac = 7; oraz na zrzucie widoczna jest jedynie wartość 29,33
ok. 2 min
Polecenie z arkusza
Zapytanie 1: liczące średnią temperaturę dla miesiąca o identyfikatorze 7. Wyliczona wartość jest zaokrąglona do dwóch miejsc po przecinku
ROUND(AVG(temperatura), 2) - średnia z pomiarów zaokrąglona do dwóch miejsc; ROUND obejmuje całe AVG,
AS srednia - alias: kolumna wyniku ma czytelną nazwę zamiast całego wyrażenia,
WHERE id_miesiac = 7 - tylko lipiec.
Wynik: jedna wartość, 29.33. Zapytanie rozpisane w kilku liniach - SQL nie zwraca uwagi na znaki nowej linii, a każda klauzula stoi osobno. Zapisz je w kwerendy.txt, zrzut jako kw1.jpg.
R.1.4 Wykonane zapytanie 2 wstawiające do tabeli miejscowosc wiersz z danymi „Kijów”, „Ukraina”. Klucz nadawany automatycznie. W pliku z kwerendami lub na zrzucie istnieje zapis np.: INSERT INTO miejscowosc(nazwa, kraj) VALUES ('Kijów','Ukraina'); (klucz główny pominięty lub NULL) oraz na zrzucie widoczny jest komunikat o poprawnym dodaniu rekordu lub tabela ze wstawionym rekordem
ok. 2 min
Polecenie z arkusza
Zapytanie 2: wstawiające do tabeli miejscowosc wiersz z danymi „Kijów”, „Ukraina”. Klucz nadawany automatycznie
Lista kolumn (nazwa, kraj) bez id - klucz nada baza (AUTO_INCREMENT), tu 11. Jawna lista kolumn chroni też przed zmianą struktury tabeli: gdyby ktoś dodał kolumnę, INSERT bez listy przestałby działać. Zrzut z komunikatem albo z tabelą po wstawieniu: kw2.jpg.
Typowe błędy
Wpisanie klucza ręcznie (VALUES (11, ...)) przeczy wymaganiu „klucz nadawany automatycznie” - klucz trzeba pominąć albo podać NULL.
kwerendy.txtTekstzmienione: 4 linie
-- Zapytanie 1SELECT ROUND(AVG(temperatura), 2) AS sredniaFROM pomiaryWHERE id_miesiac = 7;-- Zapytanie 2INSERT INTO miejscowosc (nazwa, kraj)VALUES ('Kijów', 'Ukraina');
Podgląd strony po tym kroku
Krok 6: Zapytanie 3: miasta i temperatury lipca
Kryteria:
R.1.5 Wykonane zapytanie 3 wybierające jedynie nazwy miast i krajów z tabeli miejscowosc oraz odpowiadające im temperatury w miesiącu o id = 7. Należy posłużyć się relacją. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa, kraj, temperatura FROM miejscowosc JOIN pomiary ON miejscowosc.id = id_miejscowosc WHERE id_miesiac = 7; oraz na zrzucie widocznych jest 6 rekordów z danymi: Warszawa Polska 25, Ostrawa Czechy 30, Gdańsk Polska 33, Brno Czechy 21, Praga Czechy 32, Hamburg Niemcy 35
ok. 3 min
Polecenie z arkusza
Zapytanie 3: wybierające jedynie nazwy miast i krajów z tabeli miejscowosc oraz odpowiadające im temperatury w miesiącu o identyfikatorze 7. Należy posłużyć się relacją
FROM miejscowosc AS m JOIN pomiary AS p - aliasy tabel: krótkie m. i p. zamiast pełnych nazw przed każdym polem,
ON p.id_miejscowosc = m.id - relacja: klucz obcy pomiaru wskazuje miejscowość,
WHERE p.id_miesiac = 7 - tylko lipiec,
SELECT m.nazwa, m.kraj, p.temperatura - trzy pola, każde z prefiksem tabeli.
Wynik: sześć wierszy - Warszawa 25, Ostrawa 30, Gdańsk 33, Brno 21, Praga 32, Hamburg 35. To zawartość tabeli z ilustracji 3. Zrzut: kw3.jpg.
kwerendy.txtTekstzmienione: 6 linii
-- Zapytanie 1SELECT ROUND(AVG(temperatura), 2) AS sredniaFROM pomiaryWHERE id_miesiac = 7;-- Zapytanie 2INSERT INTO miejscowosc (nazwa, kraj)VALUES ('Kijów', 'Ukraina');-- Zapytanie 3SELECT m.nazwa, m.kraj, p.temperaturaFROM miejscowosc AS mJOIN pomiary AS p ON p.id_miejscowosc = m.idWHERE p.id_miesiac = 7;
Podgląd strony po tym kroku
Krok 7: Zapytanie 4: średnie temperatury miesięcy
Kryteria:
R.1.6 Wykonane zapytanie 4 wybierające jedynie nazwy miesięcy oraz odpowiadające im średnie temperatury. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa, AVG(temperatura) FROM miesiace JOIN pomiary ON miesiace.id = id_miesiac GROUP BY nazwa; oraz na zrzucie widocznych jest dokładnie 12 miesięcy ze średnimi temperaturami
ok. 3 min
Polecenie z arkusza
Zapytanie 4: wybierające jedynie nazwy miesięcy z tabeli miesiace oraz odpowiadające im średnie temperatury. Należy posłużyć się relacją
FROM miesiace AS ms JOIN pomiary AS p ON p.id_miesiac = ms.id - relacja miesiąc - pomiar (alias ms, bo m mogłoby się mylić z miejscowością),
GROUP BY ms.id, ms.nazwa - grupa to jeden miesiąc; grupowanie po identyfikatorze nie skleiłoby dwóch miesięcy o tej samej nazwie, a nazwa jest dopisana, bo stoi w SELECT,
AVG(p.temperatura) AS srednia - średnia w każdej grupie,
ORDER BY ms.id - kolejność kalendarza zamiast alfabetycznej.
Wynik: dokładnie 12 wierszy, od stycznia do grudnia. Średnie mają cztery miejsca po przecinku; arkusz nie każe ich zaokrąglać. Zrzut: kw4.jpg.
kwerendy.txtTekstzmienione: 7 linii
-- Zapytanie 1SELECT ROUND(AVG(temperatura), 2) AS sredniaFROM pomiaryWHERE id_miesiac = 7;-- Zapytanie 2INSERT INTO miejscowosc (nazwa, kraj)VALUES ('Kijów', 'Ukraina');-- Zapytanie 3SELECT m.nazwa, m.kraj, p.temperaturaFROM miejscowosc AS mJOIN pomiary AS p ON p.id_miejscowosc = m.idWHERE p.id_miesiac = 7;-- Zapytanie 4SELECT ms.nazwa, AVG(p.temperatura) AS sredniaFROM miesiace AS msJOIN pomiary AS p ON p.id_miesiac = ms.idGROUP BY ms.id, ms.nazwaORDER BY ms.id;
Podgląd strony po tym kroku
Etap 2: Grafika i strona HTML
Kroki 8-27 · po tym etapie masz 17 z 38 kryteriów (pkt)
Krok 8: Grafika slonce.png: kadr do kształtu słońca
Kryteria:
R.2.2 Przynajmniej jedna grafika została przycięta dokładnie do kształtu: kształt nie został obcięty oraz nie ma wolnej przestrzeni z żadnej strony (sprawdzić bez powiększania grafiki)
ok. 4 min
Polecenie z arkusza
Z pliku pogoda.png należy wyciąć kształty pełnego słońca, słońca za chmurą i deszczu. Kształty mają wypełniać całą przestrzeń obrazu, tak jak zaznaczony obszar kadrowania na ilustracji 2a. Następnie każdy obraz należy przeskalować do wymiarów dokładnie 100 px na 100 px. Zapisać grafiki jako slonce.png, chmury.png, deszcz.png, zgodnie z ilustracjami 2b, 2c, 2d z zachowaniem przezroczystości.
Grafika jest taka sama w obu wersjach. pogoda.png ma przezroczyste tło i sześć ikon. W GIMP-ie zaznacz prostokątem słońce z lewego górnego rogu, Obraz → Przytnij do zaznaczenia, a potem Obraz → Przytnij do zawartości - GIMP obetnie przezroczyste pasy, aż promienie dotkną wszystkich czterech krawędzi. Kadr: 211 × 212 px. Obraz → Skaluj obraz z rozpiętym łańcuchem, 100 × 100 px, i Plik → Wyeksportuj jakoslonce.png - PNG zachowa przezroczystość.
Typowe błędy
Kadr „na oko” z marginesem zostawia wolną przestrzeń przy brzegach - klucz sprawdza, czy kształt dotyka krawędzi i nie jest obcięty (R.2.2).
Podgląd strony po tym kroku
Krok 9: Grafika chmury.png: słońce za chmurą
ok. 3 min
Polecenie z arkusza
Zapisać grafiki jako slonce.png, chmury.png, deszcz.png, zgodnie z ilustracjami 2b, 2c, 2d z zachowaniem przezroczystości.
Ikona z prawego górnego rogu. Kadr 229 × 212 px nie jest kwadratem, więc skalowanie do 100 × 100 lekko spłaszcza ją w poziomie - arkusz wymaga dokładnie tych wymiarów, a na ilustracji 2c widać to samo.
Podgląd strony po tym kroku
Krok 10: Grafika deszcz.png: chmura z deszczem
Kryteria:
R.2.1 Grafiki slonce.png , chmury.png , deszcz.png przedstawiają kolejno słońce, słońce za chmurą i chmurę z deszczem. Rozmiary przynajmniej jednej grafiki to dokładnie 100 px na 100 px oraz zachowana przezroczystość
ok. 3 min
Polecenie z arkusza
Zapisać grafiki jako slonce.png, chmury.png, deszcz.png, zgodnie z ilustracjami 2b, 2c, 2d z zachowaniem przezroczystości.
Chmura z kroplami z prawej kolumny środkowego rzędu, kadr 185 × 158 px, po skalowaniu 100 × 100. Komplet: słońce, słońce za chmurą i deszcz, każda ikona 100 × 100 z przezroczystym tłem (R.2.1).
Podgląd strony po tym kroku
Krok 11: Plik index.php w HTML5
ok. 2 min
Polecenie z arkusza
Składa się ze strony index.php. Zapisana w języku HTML5
Deklaracja <!DOCTYPE html> i szkielet. Rozszerzenie .php, bo dojdą skrypty - strona otwiera się przez http://localhost/... z folderu htdocs XAMPP-a.
R.2.3 W pliku index.php znajduje się deklaracja HTML5 <!DOCTYPE HTML> i deklaracja języka zawartości witryny: polski np. <html lang="pl">
ok. 1 min
Polecenie z arkusza
Zadeklarowany polski język zawartości witryny
Atrybut lang="pl" - przeglądarka i czytnik ekranu wiedzą, w jakim języku jest treść (dzielenie wyrazów, wymowa). Z deklaracją HTML5 to jedno kryterium.
Krok 16: Bloki: dwa nagłówki, główny z dwiema sekcjami, stopka
Kryteria:
R.2.5 W sekcji body zdefiniowany jest podział na bloki za pomocą semantycznych znaczników HTML5: header+header, 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 wymaga wprost dwóch <header>, <main> z dwoma <section> i <footer> (R.2.5). W tej wersji bloki nie dostają identyfikatorów: szerokości ustawi siatka CSS według kolejności bloków w kodzie (pierwszy nagłówek - pierwsza kolumna), więc nie trzeba ich nazywać tylko po to, żeby nadać width.
R.2.7 W bloku nagłówkowym 1 umieszczono grafikę ze słońcem z tekstem alternatywnym "Słonecznie"
ok. 1 min
Polecenie z arkusza
Zawartość bloku nagłówkowego 1: obraz slonce.png z tekstem alternatywnym „Słonecznie”
<img> z atrybutem alt - tekst zastępuje obraz, gdy go zabraknie, i czyta go czytnik ekranu. HTML5 nie potrzebuje ukośnika na końcu (<img ...>, nie <img ... />).
Zawartość bloku głównego: blok sekcji lewej i blok sekcji prawej. Zawartość bloku sekcji lewej: Nagłówek drugiego stopnia o treści: „Temperatury w lipcu”
R.2.9 Pierwszy wiersz tabeli zawiera komórki nagłówkowe zastosowane znaczniki <tr> i <th>
ok. 2 min
Polecenie z arkusza
Tabela o czterech kolumnach. Pierwszy wiersz zawiera komórki nagłówkowe o treści „Miasto”, „Kraj”, „Temperatura”, „Pogoda”. Tabela jest wypełniona skryptem 1
Pierwszy wiersz <tr> z czterema komórkami <th> stoi w <thead> (część nagłówkowa tabeli), a wiersze z danymi trafią do pustego na razie <tbody> - tam wypisze je skrypt 1. Atrybut scope="col" mówi czytnikowi ekranu, że komórka opisuje kolumnę pod sobą. Klucz sprawdza <tr> i <th> w pierwszym wierszu (R.2.9) - są.
Zawartość bloku sekcji prawej: Nagłówek drugiego stopnia o treści: „Średnie temperatury w roku”
To samo w drugiej sekcji.
index.phpPHPzmienione: 1 linia
<!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Pogoda</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <imgsrc="slonce.png"alt="Słonecznie"> </header> <header> <h1>Pogoda w Europie</h1> </header> <main> <section> <h2>Temperatury w lipcu</h2> <table> <thead> <tr> <thscope="col">Miasto</th> <thscope="col">Kraj</th> <thscope="col">Temperatura</th> <thscope="col">Pogoda</th> </tr> </thead> <tbody> </tbody> </table> </section> <section> <h2>Średnie temperatury w roku</h2> </section> </main> <footer> </footer></body></html>
Podgląd strony po tym kroku (index.php)
Krok 22: Odnośnik „Styczeń” z identyfikatorem 1 - z tablicy
Kryteria:
R.3.4 Przynajmniej jeden odnośnik wysyła identyfikator miesiąca (styczeń: 1, luty: 2 itd.) do tego samego pliku, np. <a href="index.php?id=1">Styczeń</a>
ok. 3 min
Polecenie z arkusza
Dwanaście odnośników dla wszystkich miesięcy roku: „Styczeń” prowadzi do adresu index.php i przesyła metodą GET identyfikator miesiąca równy 1
Dwanaście odnośników różni się tylko liczbą i nazwą, więc zamiast przepisywać dwanaście linii powstaje tablica$months na samej górze pliku (klucz = identyfikator z tabeli miesiace, wartość = nazwa) i jedną pętlę foreach, która dla każdej pary wypisuje <a href="index.php?id=1">Styczeń</a>. Pętla stoi w HTML-u w składni alternatywnej (foreach (...): ... endforeach;), a <?= ... ?> to skrót od <?php echo ... ?>.
Parametr GET to dopisek po znaku zapytania: index.php?id=1 (R.3.4). Odnośniki stoją w bloku <nav> - to grupa nawigacji, klucz dopuszcza bloki pomocnicze. Od tego kroku plik zawiera PHP, więc podgląd to wynik jego uruchomienia.
„Luty” prowadzi do adresu index.php i przesyła metodą GET identyfikator miesiąca równy 2
Jeden nowy element tablicy: 2 => 'Luty'. Pętla sama zrobi z niego odnośnik index.php?id=2 - numer i nazwa stoją obok siebie w jednej parze, więc nie mogą się rozjechać.
R.3.3 Utworzono 12 odnośników z nazwami miesięcy (dopuszczalne literówki w nazwach miesięcy)
ok. 2 min
Polecenie z arkusza
„Grudzień” prowadzi do adresu index.php i przesyła metodą GET identyfikator miesiąca równy 12
Arkusz wypisuje trzy pierwsze miesiące i grudzień, resztę zastępuje wielokropkiem. Tablica ma teraz 12 par, każda w osobnej linii (przecinek po ostatnim elemencie jest w PHP dozwolony i ułatwia dopisywanie). Pętla wypisuje 12 odnośników (R.3.3).
Typowe błędy
Kolejność par nie ma znaczenia dla adresów - liczy się klucz przy nazwie. Zła para (5 => 'Kwiecień') daje jednak zły miesiąc, tak samo jak w wersji z ręcznie pisanymi odnośnikami.
R.2.6 W bloku nagłówkowym 2 znajduje się <h1>: "Pogoda w Europie", w bloku sekcji lewej <h2>: "Temperatury w lipcu", w bloku sekcji prawej <h2>: "Średnie temperatury w roku" i <p> "Średnia temperatura dla wybranego miesiąca wynosi" oraz w stopce <p> z numerem zdającego
ok. 1 min
Polecenie z arkusza
Zawartość stopki: paragraf o treści „Numer zdającego: ”, dalej wstawiony numer zdającego
Akapit w <footer>. Na egzaminie po dwukropku wpisujesz swój numer zdającego (PESEL) - tutaj stoi imię i nazwisko autora rozwiązania. Komplet nagłówków i akapitów z klucza: <h1>, dwa <h2>, akapit w sekcji prawej i w stopce (R.2.6).
Kroki 28-38 · po tym etapie masz 28 z 38 kryteriów (pkt)
Krok 28: Plik styl.css: zmienne kolorów, krój i wyrównanie
Kryteria:
R.4.1 dla selektora body: krój czcionki Georgia, wyrównanie tekstu do środka
ok. 2 min
Polecenie z arkusza
Dla selektora ciała strony: krój czcionki Georgia, wyrównanie tekstu do środka
Arkusz używa dwóch kolorów w kilku miejscach, więc najpierw trafiają do zmiennych CSS w :root (cały dokument): --green i --lime. Kolejne reguły będą pisać var(--green) - zmiana odcienia to jedna linia, a nazwa mówi więcej niż #388E3C.
Reguła body (selektor znacznika - uwaga arkusza): krój Georgia z rodziną zapasową serif na wypadek komputera bez tego kroju i środkowanie. Oba ustawienia dziedziczą elementy strony (R.4.1).
Dodatkowo szerokość pierwszego bloku nagłówkowego 20%, drugiego 80%
<body> staje się siatką (display: grid) o dwóch kolumnach: grid-template-columns: 20% 80%. Dzieci siatki układają się w komórkach po kolei: pierwszy <header> w kolumnie 20%, drugi w kolumnie 80% - stoją obok siebie bez float. Klucz wprost dopuszcza szerokości z grid-template-columns (R.4.5).
Blok główny i stopka też trafiają na razie do kolejnych komórek (każdy w jednej kolumnie) - na całą szerokość rozciągnie je krok o układzie.
R.4.4 wysokość: oba bloki nagłówkowe - 100px, oba bloki sekcji - 600px
ok. 2 min
Polecenie z arkusza
Wspólne dla bloków sekcji lewej i prawej: kolor tła #DCE775, szerokość 50%, wysokość 600 px
Blok główny to druga siatka: repeat(2, 50%) to skrót od 50% 50% - dwie kolumny po połowie, w każdej jedna sekcja. Szerokość sekcji wynika więc z siatki, a reguła section ma tylko tło i wysokość. Komplet wysokości z klucza (R.4.4).
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)
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
grid-column: 1 / -1 rozciąga element od pierwszej do ostatniej linii siatki (-1 to ostatnia linia, niezależnie od liczby kolumn). Blok główny i stopka zajmują więc każdy cały wiersz, a nagłówki dzielą pierwszy wiersz 20% / 80% - układ z ilustracji 4 (R.3.1). Nie ma float ani clear, więc nie ma też ryzyka, że blok główny „wjedzie” pod nagłówki.
Typowe błędy
Bez tej reguły blok główny zostaje w kolumnie 20%, a stopka w kolumnie 80% - sekcje są wąskie, a punkt za układ przepada.
R.4.5 szerokość: pierwszy blok nagłówkowy - 20%, drugi - 80%, oba bloki sekcji - 50% (width lub grid-template-columns lub flex) , selektor table - 80%
R.4.7 styl obramowania dla tabeli - 2px dashed #388E3C
ok. 2 min
Polecenie z arkusza
Dla selektora tabeli: szerokość 80%, marginesy zewnętrzne automatycznie wyliczane przez przeglądarkę, obramowanie linią kreskowaną o szerokości 2 px i kolorze #388E3C
margin: auto przy ustalonej szerokości dzieli wolne miejsce po równo - tabela stoi na środku sekcji. Linia kreskowana to dashed. Skrót border łączy grubość, styl i kolor (tu ze zmiennej). Szerokości z klucza są kompletne (R.4.5).
R.4.3 kolor czcionki: oba bloki nagłówkowe i stopka - biały, selektor a - #388E3C
R.4.9 styl dla odnośnika: czcionka pochylona o rozmiarze 200%
ok. 2 min
Polecenie z arkusza
Dla selektora odnośnika: kolor czcionki #388E3C, marginesy zewnętrzne 20 px, czcionka pochylona o rozmiarze 200%
Selektor znacznika a (uwaga arkusza). Pochylenie to font-style: italic. Odnośnik jest elementem liniowym, więc margin rozsuwa odnośniki w poziomie. Kolory czcionki z klucza są kompletne (R.4.3).
R.4.10 gdy kursor znajdzie się na odnośniku jego kolor tła zmienia się na #388E3C, a kolor czcionki na #DCE775
ok. 1 min
Polecenie z arkusza
Gdy kursor znajdzie się na odnośniku jego kolor tła zmienia się na #388E3C, a kolor czcionki na #DCE775
Pseudoklasa :hover zamienia kolory miejscami (R.4.10). Ten sam wygląd dostaje :focus-visible - odnośnik wybrany klawiszem Tab, bo osoba bez myszy też powinna widzieć, gdzie jest. Klucz sprawdza :hover; dodatkowy selektor po przecinku niczego w nim nie zmienia.
Kroki 39-46 · po tym etapie masz 38 z 38 kryteriów (pkt)
Krok 39: Połączenie PDO na górze pliku
Kryteria: ,
R.5.1 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy pogoda
R.5.2 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 pogoda. Na końcu działania skrypt zamyka połączenie z serwerem
Cała praca z bazą stoi przed<!DOCTYPE html>, obok tablicy miesięcy: najpierw dane, potem HTML, który je tylko wypisuje. Połączenie przez PDO (klucz R.5 przyjmuje MySQLi i PDO):
DSN 'mysql:host=localhost;dbname=pogoda;charset=utf8mb4' - serwer, baza i kodowanie; 'root', '' - użytkownik i puste hasło (R.5.1),
ERRMODE_EXCEPTION - błąd bazy rzuca wyjątek, FETCH_ASSOC - wiersze jako tablice z nazwami kolumn,
$pdo = null zamyka połączenie (PDO nie ma osobnej funkcji „zamknij”) - zapytania trafią między te dwie linie, więc zamknięcie zostanie ostatnią operacją na bazie (R.5.2),
try / catch (PDOException $exception) - gdy serwer bazy nie działa, szczegóły idą do dziennika serwera (error_log), a w bloku głównym pojawia się ogólny komunikat z $dbError (bez nazw tabel i haseł),
funkcja escape() to krótka nazwa htmlspecialchars(..., ENT_QUOTES) - posłuży każdej wartości z bazy.
Zmienne $cities, $average i $dbError mają wartości początkowe, żeby HTML działał także po błędzie bazy.
R.5.3 Skrypt 1 zawiera instrukcję wysyłającą do bazy danych zapytanie 3
ok. 2 min
Polecenie z arkusza
Skrypt 1: Wysyła do bazy danych zapytanie 3
$pdo->query(...) wysyła zapytanie 3 z kwerendy.txt (bez średnika na końcu), a ->fetchAll() od razu odbiera wszystkie sześć wierszy do tablicy $cities (R.5.3). Zapytanie nie ma danych od użytkownika, więc wystarcza query; przygotowane zapytanie przyda się w skrypcie 2.
index.phpPHPzmienione: 6 linii
<?phpfunctionescape(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES);}$months = [1 => 'Styczeń',2 => 'Luty',3 => 'Marzec',4 => 'Kwiecień',5 => 'Maj',6 => 'Czerwiec',7 => 'Lipiec',8 => 'Sierpień',9 => 'Wrzesień',10 => 'Październik',11 => 'Listopad',12 => 'Grudzień',];$cities = [];$average = null;$dbError = '';try {$pdo = new PDO('mysql:host=localhost;dbname=pogoda;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);$cities = $pdo->query('SELECT m.nazwa, m.kraj, p.temperatura FROM miejscowosc AS m JOIN pomiary AS p ON p.id_miejscowosc = m.id WHERE p.id_miesiac = 7' )->fetchAll();$pdo = null;} catch (PDOException $exception) {error_log($exception->getMessage());$dbError = 'Nie udało się pobrać danych pogodowych. Spróbuj ponownie za chwilę.';}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Pogoda</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <imgsrc="slonce.png"alt="Słonecznie"> </header> <header> <h1>Pogoda w Europie</h1> </header> <main><?phpif ($dbError !== ''): ?> <p><?= $dbError?></p><?phpendif; ?> <section> <h2>Temperatury w lipcu</h2> <table> <thead> <tr> <thscope="col">Miasto</th> <thscope="col">Kraj</th> <thscope="col">Temperatura</th> <thscope="col">Pogoda</th> </tr> </thead> <tbody> </tbody> </table> </section> <section> <h2>Średnie temperatury w roku</h2> <navaria-label="Miesiące"><?phpforeach ($monthsas$monthId => $monthName): ?> <ahref="index.php?id=<?= $monthId?>"><?= $monthName?></a><?phpendforeach; ?> </nav> <p>Średnia temperatura dla wybranego miesiąca wynosi</p> </section> </main> <footer> <p>Numer zdającego: Bartosz Bryniarski</p> </footer></body></html>
R.2.8 W bloku sekcji lewej umieszczono tabelę o 4 kolumnach (w skrypcie lub HTML) zastosowano znaczniki <table>, <tr>, <td>
R.5.4 Skrypt 1 w pierwszych trzech kolumnach tabeli wyświetla zwrócone zapytaniem dane dla wszystkich zwróconych zapytaniem rekordów
ok. 3 min
Polecenie z arkusza
W pierwszych trzech kolumnach tabeli zapisuje kolejne nazwy miast, krajów i temperatury dla wszystkich zwróconych zapytaniem rekordów
W <tbody> pętla foreach ($cities as $city): wykona się tyle razy, ile wierszy oddało zapytanie (R.5.4). Pola po nazwach kolumn - $city['kraj'] mówi więcej niż $row[1] i nie zależy od kolejności pól w SELECT. Każda wartość przechodzi przez escape(): nazwa miasta z < albo cudzysłowem nie rozbije tabeli (R.2.8). W podglądzie sześć wierszy, czwarta kolumna jeszcze pusta.
index.phpPHPzmienione: 7 linii
<?phpfunctionescape(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES);}$months = [1 => 'Styczeń',2 => 'Luty',3 => 'Marzec',4 => 'Kwiecień',5 => 'Maj',6 => 'Czerwiec',7 => 'Lipiec',8 => 'Sierpień',9 => 'Wrzesień',10 => 'Październik',11 => 'Listopad',12 => 'Grudzień',];$cities = [];$average = null;$dbError = '';try {$pdo = new PDO('mysql:host=localhost;dbname=pogoda;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);$cities = $pdo->query('SELECT m.nazwa, m.kraj, p.temperatura FROM miejscowosc AS m JOIN pomiary AS p ON p.id_miejscowosc = m.id WHERE p.id_miesiac = 7' )->fetchAll();$pdo = null;} catch (PDOException $exception) {error_log($exception->getMessage());$dbError = 'Nie udało się pobrać danych pogodowych. Spróbuj ponownie za chwilę.';}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Pogoda</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <imgsrc="slonce.png"alt="Słonecznie"> </header> <header> <h1>Pogoda w Europie</h1> </header> <main><?phpif ($dbError !== ''): ?> <p><?= $dbError?></p><?phpendif; ?> <section> <h2>Temperatury w lipcu</h2> <table> <thead> <tr> <thscope="col">Miasto</th> <thscope="col">Kraj</th> <thscope="col">Temperatura</th> <thscope="col">Pogoda</th> </tr> </thead> <tbody><?phpforeach ($citiesas$city): ?> <tr> <td><?= escape($city['nazwa']) ?></td> <td><?= escape($city['kraj']) ?></td> <td><?= escape($city['temperatura']) ?></td> </tr><?phpendforeach; ?> </tbody> </table> </section> <section> <h2>Średnie temperatury w roku</h2> <navaria-label="Miesiące"><?phpforeach ($monthsas$monthId => $monthName): ?> <ahref="index.php?id=<?= $monthId?>"><?= $monthName?></a><?phpendforeach; ?> </nav> <p>Średnia temperatura dla wybranego miesiąca wynosi</p> </section> </main> <footer> <p>Numer zdającego: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (index.php)
Krok 42: Skrypt 1: ikona pogody z funkcji weatherIcon()
Kryteria:
R.5.5 Skrypt 1 wstawia obraz ze słońcem dla temperatury > 30, z deszczem dla temperatury < 26, z chmurą w innych przypadkach
ok. 4 min
Polecenie z arkusza
Czwarta kolumna wypełniona jest obrazem: slonce.png, gdy temperatura dla tego rekordu jest powyżej 30 oC. deszcz.png, gdy temperatura dla tego rekordu jest poniżej 26 oC. chmury.png, w innych przypadkach
Wybór ikony to osobna funkcjaweatherIcon() na górze pliku: dostaje temperaturę, oddaje nazwę pliku i tekst alternatywny. W środku wyrażenie match (true) (PHP 8): sprawdza warunki po kolei i zwraca wartość pierwszego prawdziwego - > 30 słońce, < 26 deszcz, default chmury (R.5.5). W przeciwieństwie do switch nie potrzebuje break i samo jest wartością.
W pętli (int) zamienia temperaturę z bazy (PDO oddaje ją jako tekst "30") na liczbę zgodną z typem parametru. Wynik jak na ilustracji 3: Ostrawa (dokładnie 30) - chmury, Warszawa i Brno - deszcz, reszta - słońce. Ikony dostają tekst alternatywny, którego wersja podstawowa nie ma.
Typowe błędy
>= 30 zamiast > 30 daje Ostrawie słońce - na ilustracji 3 widać, że 30 stopni to jeszcze „inny przypadek”.
R.5.6 Skrypt 2 jest wykonywany tylko, gdy dane zostały wysłane (np. przez isset)
ok. 3 min
Polecenie z arkusza
Skrypt 2, wykonywany tylko jeżeli zostały wysłane dane poprzez kliknięcie odnośnika (ilustracja 5)
isset($_GET['id']) - przy pierwszym wejściu adres to samo index.php i blok się nie wykona (R.5.6). Po kliknięciu miesiąca wartość z adresu sprawdza filter_var z filtrem FILTER_VALIDATE_INT i zakresem 1 - 12: oddaje liczbę całkowitą albo false, gdy w adresie jest coś innego (13, abc, 2 OR 1=1). Adres może wpisać każdy, więc wartość z niego sprawdza się zawsze, zanim trafi dalej.
Krok 44: Skrypt 2: zapytanie 1 dla wybranego miesiąca
Kryteria:
R.5.7 Skrypt 2 zawiera instrukcję wysyłającą do bazy danych zmodyfikowane zapytanie 1, tak że w warunku zapytania jest wstawiona wartość przesłana metodą GET
ok. 3 min
Polecenie z arkusza
Wysyła do bazy danych zapytanie 1, zmodyfikowane tak, żeby wybierane były dane dotyczące wybranego przez użytkownika miesiąca (w miejscu id miesiąca jest wstawiona wartość przesłana metodą GET)
Zapytanie 1, w którym zamiast 7 stoi parametr nazwany :month. prepare wysyła do bazy samą strukturę zapytania, a execute(['month' => $monthId]) - wartość z adresu osobno (R.5.7). Wartość nigdy nie staje się częścią tekstu SQL, więc nie da się nią dopisać warunku - to druga warstwa ochrony obok filter_var.
fetchColumn() oddaje pierwszą kolumnę pierwszego wiersza - tu jedyną liczbę, średnią. Od tego kroku podgląd pokazuje stronę po kliknięciu „Luty” (index.php?id=2); wyniku strona jeszcze nie wypisuje.
Krok 45: Skrypt 2: temperatura w nagłówku trzeciego stopnia
Kryteria: ,
R.5.8 Zwróconą zapytaniem temperaturę wyświetla w sposób „<temp> stopni”
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
Zwróconą zapytaniem temperaturę wyświetla w nagłówku trzeciego stopnia w sposób „<temp> stopni”, gdzie <> oznacza wartość zwróconą zapytaniem
Pod akapitem warunek w składni alternatywnej: gdy $average ma wartość, wypisz <h3>3.50 stopni</h3> - jak na ilustracji 5 (R.5.8). Przy pierwszym wejściu $average zostaje null i nagłówka nie ma.
Nazwy zmiennych i funkcji mówią, co przechowują i robią (R.5.9): $months, $monthId, $monthName, $cities, $city, $icon, $average, $statement, $pdo, $dbError, escape(), weatherIcon().
Zwróconą zapytaniem temperaturę wyświetla w nagłówku trzeciego stopnia w sposób „<temp> stopni”, gdzie <> oznacza wartość zwróconą zapytaniem
Sprawdzone adresy: ?id=13, ?id=abc, ?id=2 OR 1=1 i puste ?id=. filter_var odrzuca każdy z nich, zapytanie się nie wykonuje, a strona pokazuje „brak pomiarów dla tego miesiąca” zamiast samego słowa „stopni” albo średniej ze wszystkich pomiarów podanej jako luty. Podgląd: index.php?id=13. Klucz tego nie sprawdza - to obsługa adresu wpisanego ręcznie.
Kroki 47-48 · po tym etapie masz 38 z 38 kryteriów (pkt)
Krok 47: Co jest inaczej i dlaczego
ok. 3 min
Polecenie z arkusza
Ocenie podlegać będzie 5 rezultatów: operacje na bazie danych, zawartość witryny internetowej, działanie witryny internetowej, styl CSS witryny internetowej, skrypt połączenia z bazą.
Obie wersje dają te same punkty. Różnice:
wersja podstawowa
ta wersja
układ
float + clear dla nagłówków, flex w <main>
siatka: 20% 80% na <body>, repeat(2, 50%) w <main>
Czas przeznaczony na wykonanie zadania wynosi 150 minut.
Wersja podstawowa, gdy liczy się czas albo PDO i siatka nie są opanowane: funkcje mysqli_* i isset są w tabeli 1 arkusza, każdy skrypt stoi tam, gdzie widać jego efekt, a odnośniki da się sprawdzić w kodzie HTML bez uruchamiania PHP.
Ta wersja, gdy PDO, match i siatka CSS są pisane z pamięci. Odnośniki z pętli mają jedną słabość: błąd składni PHP zatrzymuje całą stronę, więc nie widać wtedy także odnośników - sprawdzaj stronę w przeglądarce po każdej zmianie.
Mieszanie też daje pełne punkty: siatka z tej wersji i mysqli_* z podstawowej. W projekcie z formularzami doszłyby jeszcze tokeny CSRF i dane logowania do bazy w osobnym pliku config.php - tu arkusz ma tylko odczyt z bazy i jedną stronę.
Krok 49 · po tym etapie masz 38 z 38 kryteriów (pkt)
Krok 49: 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/…/index.php.
W folderze z numerem zdającego: chmury.png, deszcz.png, import.png, index.php, kw1.jpg - kw4.jpg, kwerendy.txt, przeglądarka.txt, slonce.png, styl.css. 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.