To trzeci sposób rozwiązania tego samego arkusza - tak, jak zorganizowano by tę witrynę w prawdziwym projekcie PHP bez frameworka. Wynik jest ten sam co w pozostałych zakładkach i spełnia każde kryterium klucza, ale kod jest rozłożony na pliki według zadań:
wspólne części stron w osobnych plikach: nagłówek (naglowek.php), blok boczny z listami (boczny.php) i stopka (stopka.php) istnieją raz i obie strony dołączają je przez require - poprawka bloku bocznego to zmiana w jednym pliku,
dostęp do bazy w jednym pliku baza.php: połączenie oraz funkcje objectNames(), gallery() i place(), które wysyłają zapytania i zwracają gotowe tablice; strony nie zawierają ani jednej linii SQL,
mysqli w stylu obiektowym z wyjątkami (mysqli_report) i zapytaniami przygotowanymi (prepare, bind_param) - ta sama biblioteka co funkcje z tabeli 1 arkusza, ale zapisana jak w nowoczesnym kodzie; awarię bazy łapie jedna funkcja set_exception_handler,
układ i styl - ten sam CSS na siatce co w zakładce „Nowoczesny kod”.
⚠️ Uwaga egzaminacyjna: klucz każe sprawdzać połączenie i zapytania „w kodzie źródłowym strony”. Tu są w baza.php, więc egzaminator musi zajrzeć do dołączonego pliku. Na egzaminie bezpieczniej trzymać PHP w pliku strony (wersja podstawowa albo nowoczesna) - ta wersja pokazuje, dokąd prowadzi dobra organizacja kodu.
Baza i zdjęcia pochodzą z prawdziwego archiwum pliki1, a podgląd każdego kroku z PHP to wynik prawdziwego uruchomienia stron na serwerze z tą bazą.
Jak liczą się punkty. Klucz ma 37 kryteriów (R.1.1 - R.5.7), każde za 1 punkt. Próg zdania części praktycznej to 75%, czyli 28 punktów. Przy każdym kroku jest napisane, które kryteria spełnia.
Suwak i strzałki obok niego - albo ←→ na klawiaturze - przechodzą krok po kroku; wyjaśnienie, pliki i podgląd zmieniają się razem z krokiem.
Lista „Etap” przenosi od razu do wybranego etapu.
„Rozwiązuję samodzielnie” chowa rozwiązanie kroku - najpierw własna próba, potem odsłonięcie.
„Tryb prezentacji” pokazuje same kroki, bez menu strony - na rzutnik.
Etap 1: Baza danych
Kroki 1-7 · po tym etapie masz 6 z 37 kryteriów (pkt)
Krok 1: Baza danych islandia
Kryteria:
R.1.1 Wykonany import tabel do bazy danych islandia - plik import w formacie JPEG
ok. 3 min
Polecenie z arkusza
Utwórz bazę danych o nazwie islandia, z zestawem znaków utf8_unicode_ci.
Najpierw rozpakuj archiwum pliki1.7z z pulpitu (hasło isLandi@ z treści zadania) - są w nim baza1.sql i dziewięć zdjęć. W phpMyAdmin kliknij Nowa, wpisz islandia i wybierz utf8_unicode_ci. To samo w SQL:
CREATE DATABASE islandia CHARACTER SET utf8 COLLATE utf8_unicode_ci;
utf8_unicode_ci to ogólne porównywanie Unicode: sortuje polskie („gorące źródło”) i islandzkie litery („Fuglaskoðunarhús”) według zasad Unicode, a przy porównaniach nie rozróżnia wielkości liter (ci - case insensitive). Dziedziczą je jednak tylko tabele zakładane później bez własnego ustawienia - tabele z baza1.sql mają w definicji utf8_polish_ci i to ono działa w zapytaniach.
Typowe błędy
Nazwa bazy musi być dokładnie islandia - skrypty łączą się z bazą po nazwie (R.5.1).
Krok 2: Import tabel z pliku baza1.sql
Kryteria:
R.1.1 Wykonany import tabel do bazy danych islandia - plik import w formacie JPEG
ok. 2 min
Polecenie z arkusza
Do utworzonej bazy zaimportuj tabele z pliku baza1.sql.
Zaznacz bazę islandia, karta Import, plik baza1.sql, Wykonaj. Plik tworzy pięć tabel, ale zadanie korzysta tylko z trzech z ilustracji 1: kontynenty, rodzaje (rodzaje obiektów: wodospad, szczyt, siedlisko zwierząt…) i obiekty (25 obiektów z kluczami obcymi idRodzaj i idKontynent). Nazwy kolumn mają wielkie litery w środku (idObiekt, nazwaCechy, wartoscCechy) - w zapytaniach pisz je dokładnie tak.
Krok 3: Zrzut ekranu import.jpg
Kryteria:
R.1.1 Wykonany import tabel do bazy danych islandia - 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 (Print Screen) z komunikatem o udanym imporcie albo listą tabel, zapis jako import.jpg - w tym arkuszu import jest w JPEG, a zrzuty zapytań w PNG.
Typowe błędy
import.png zamiast import.jpg albo wykadrowany zrzut to strata punktu R.1.1.
Krok 4: Zapytanie 1: obiekty z Islandii
Kryteria: ,
R.1.2 W pliku kwerendy.txt zapisane co najmniej jedno zapytanie SQL, wynikające z treści zadania
R.1.3 Wykonane zapytanie 1 wybierające jedynie pola idObiekt, plik i nazwa z tabeli obiekty dla Islandii. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT idObiekt, plik, nazwa FROM obiekty WHERE panstwo = "Islandia"; oraz na zrzucie widocznych jest dokładnie 9 rekordów o identyfikatorach 46 ÷ 54
ok. 2 min
Polecenie z arkusza
Zapytanie 1: wybierające jedynie pola idObiekt, plik i nazwa z tabeli obiekty dla Islandii.
Każda klauzula w osobnej linii: co (trzy kolumny w kolejności z arkusza), skąd (obiekty), które wiersze (WHERE panstwo = 'Islandia'). Tekst w SQL w apostrofach - to zapis zgodny ze standardem, działający w każdej bazie (MySQL przyjmuje też cudzysłów, jak w kluczu).
Wynik: 9 wierszy o identyfikatorach 46-54 (R.1.3). Zapytanie do kwerendy.txt, zrzut kw1.png.
R.1.4 Wykonane zapytanie 2 wybierające jedynie pola plik, nazwa, nazwaCechy, wartoscCechy, opis z tabeli obiekty oraz odpowiadające im pole rodzaj z tabeli rodzaje dla obiektu o identyfikatorze 46. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT plik, nazwa, nazwaCechy, wartoscCechy, opis, rodzaj FROM obiekty JOIN rodzaje USING(idRodzaj) WHERE idObiekt = 46; (lub INNER JOIN lub porównanie kluczy po ON lub WHERE ) oraz na zrzucie widoczny jest dokładnie 1 rekord dla nazwy Strokkur
ok. 3 min
Polecenie z arkusza
Zapytanie 2: wybierające jedynie pola plik, nazwa, nazwaCechy, wartoscCechy, opis z tabeli obiekty oraz odpowiadające im pole rodzaj z tabeli rodzaje dla obiektu o identyfikatorze 46. Należy posłużyć się relacją.
Złączenie z aliasami tabel (obiekty AS o, rodzaje AS r): każda kolumna mówi, z której tabeli pochodzi, a warunek złączenia r.idRodzaj = o.idRodzaj nie jest dwuznaczny, choć kolumna idRodzaj jest w obu tabelach.
Wynik: jeden wiersz - gejzer Strokkur (R.1.4). Zrzut kw2.png. Strona obiekty.php wyśle to samo zapytanie z identyfikatorem z adresu w miejscu 46.
Typowe błędy
ON idRodzaj = idRodzaj bez nazw tabel to błąd „Column idRodzaj in on clause is ambiguous”.
kwerendy.txtTekstzmienione: 6 linii
-- Zapytanie 1SELECT idObiekt, plik, nazwaFROM obiektyWHERE panstwo = 'Islandia';-- Zapytanie 2SELECT o.plik, o.nazwa, o.nazwaCechy, o.wartoscCechy, o.opis, r.rodzajFROM obiekty AS oJOIN rodzaje AS r ON r.idRodzaj = o.idRodzajWHERE o.idObiekt = 46;
Podgląd strony po tym kroku
Krok 6: Zapytanie 3: wodospady Islandii
Kryteria:
R.1.5 Wykonane zapytanie 3 wybierające jedynie nazwy obiektów z Islandii, których identyfikator rodzaju to 10. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa FROM obiekty WHERE idRodzaj = 10 AND Panstwo = "Islandia"; oraz na zrzucie widoczne są dokładnie 4 rekordy: Skogafoss, Seljalandsfoss, Godafoss, Dettifoss
ok. 2 min
Polecenie z arkusza
Zapytanie 3: wybierające jedynie nazwy obiektów z Islandii, których identyfikator rodzaju to 10.
Dwa warunki naraz - AND. Identyfikator rodzaju jest w samej tabeli obiekty, więc złączenie nie jest potrzebne. Wynik: Skogafoss, Seljalandsfoss, Godafoss, Dettifoss (R.1.5), zrzut kw3.png.
Typowe błędy
OR zamiast AND zwróci wszystkie obiekty z Islandii i wszystkie wodospady z bazy.
kwerendy.txtTekstzmienione: 6 linii
-- Zapytanie 1SELECT idObiekt, plik, nazwaFROM obiektyWHERE panstwo = 'Islandia';-- Zapytanie 2SELECT o.plik, o.nazwa, o.nazwaCechy, o.wartoscCechy, o.opis, r.rodzajFROM obiekty AS oJOIN rodzaje AS r ON r.idRodzaj = o.idRodzajWHERE o.idObiekt = 46;-- Zapytanie 3SELECT nazwaFROM obiektyWHERE panstwo = 'Islandia' AND idRodzaj = 10;
Podgląd strony po tym kroku
Krok 7: Zapytanie 4: liczba obiektów każdego rodzaju
Kryteria:
R.1.6 Wykonane zapytanie 4 wybierające jedynie rodzaj z tabeli rodzaje oraz zliczające liczbę obiektów danego rodzaju. Kolumna z liczbą obiektów powinna mieć nadaną nazwę (alias) „Liczba obiektów”. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT rodzaj, COUNT(obiekty.idObiekt) AS "Liczba obiektów" FROM Rodzaje JOIN obiekty USING(idRodzaj) GROUP BY rodzaj; (Po COUNT dowolna nazwa pola tabeli obiekty, AS opcjonalnie, lub porównanie kluczy po ON lub WHERE ) oraz na zrzucie widocznych jest dokładnie 8 rekordów: gorące źródło 1, jaskinia 2, jezioro 1, park narodowy 1, siedlisko zwierząt 2, szczyt 4, wodospad 12, wulkan 2
ok. 3 min
Polecenie z arkusza
Zapytanie 4: wybierające jedynie rodzaj z tabeli rodzaje oraz zliczające liczbę obiektów (wodospadów, szczytów itd.) danego rodzaju. Kolumna z liczbą obiektów powinna mieć nadaną nazwę (alias) „Liczba obiektów”. Należy posłużyć się grupowaniem i relacją.
JOIN obiekty AS o ON o.idRodzaj = r.idRodzaj - relacja: do każdego rodzaju jego obiekty,
GROUP BY r.idRodzaj, r.rodzaj - grupa to jeden rodzaj wskazany kluczem; dwa różne rodzaje o tej samej nazwie nie zleją się w jeden wiersz,
COUNT(o.idObiekt) AS "Liczba obiektów" - alias ze spacją i polską literą musi być w cudzysłowie (tu cudzysłów oznacza nazwę, nie tekst),
ORDER BY r.rodzaj - kolejność alfabetyczna podana wprost; samo grupowanie jej nie gwarantuje.
Wynik: 8 wierszy - gorące źródło 1, jaskinia 2, jezioro 1, park narodowy 1, siedlisko zwierząt 2, szczyt 4, wodospad 12, wulkan 2 (R.1.6). Zrzut kw4.png.
Typowe błędy
Bez GROUP BYCOUNT policzy wszystkie 25 obiektów w jednym wierszu.
kwerendy.txtTekstzmienione: 7 linii
-- Zapytanie 1SELECT idObiekt, plik, nazwaFROM obiektyWHERE panstwo = 'Islandia';-- Zapytanie 2SELECT o.plik, o.nazwa, o.nazwaCechy, o.wartoscCechy, o.opis, r.rodzajFROM obiekty AS oJOIN rodzaje AS r ON r.idRodzaj = o.idRodzajWHERE o.idObiekt = 46;-- Zapytanie 3SELECT nazwaFROM obiektyWHERE panstwo = 'Islandia' AND idRodzaj = 10;-- Zapytanie 4SELECT r.rodzaj, COUNT(o.idObiekt) AS "Liczba obiektów"FROM rodzaje AS rJOIN obiekty AS o ON o.idRodzaj = r.idRodzajGROUP BY r.idRodzaj, r.rodzajORDER BY r.rodzaj;
Podgląd strony po tym kroku
Etap 2: Strona HTML
Kroki 8-22 · po tym etapie masz 16 z 37 kryteriów (pkt)
Krok 8: Zdjęcia w folderze zdającego
ok. 1 min
Polecenie z arkusza
Witryna składa się z podstron: islandia.php i obiekty.php.
Dziewięć zdjęć z rozpakowanego archiwum kopiujesz do folderu zdającego, obok stron. Skrypty będą je wskazywać samą nazwą pliku z kolumny plik, więc ścieżka względna wystarcza. Arkusz nie wymaga ich obróbki.
Podgląd strony po tym kroku
Krok 9: Plik islandia.php w HTML5
Kryteria:
R.2.1 W pliku islandia.php lub obiekty.php znajduje się deklaracja HTML5 <!DOCTYPE HTML> i deklaracja języka zawartości: polski np. <html lang="pl">
ok. 1 min
Polecenie z arkusza
Cechy wspólne obu stron: Zapisane w języku HTML5.
Plik islandia.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.2 W sekcji head znajduje się jawnie podany standard kodowania polskich znaków i tytuł strony "Islandia”
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.2 W sekcji head znajduje się jawnie podany standard kodowania polskich znaków i tytuł strony "Islandia”
ok. 1 min
Polecenie z arkusza
Tytuł strony „Islandia”.
<title>Islandia</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.3 W sekcji body zdefiniowany jest podział na bloki za pomocą semantycznych znaczników HTML5: header, aside, main -> section, footer (dopuszcza się stosowanie bloków pomocniczych)
ok. 2 min
Polecenie z arkusza
Podział strony na bloki: nagłówkowy, boczny, główny, stopki. Blok główny zawiera 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ą 4.
Znaczniki z tabeli 4 arkusza i klucza R.2.3: header, main z section w środku, aside, footer.
Kolejność w kodzie to kolejność czytania: nagłówek, treść główna, dodatki z boku, stopka - tak stronę przeczyta czytnik ekranu i tak zobaczy ją telefon bez stylów. O tym, że blok boczny stanie po prawej od samej góry, zdecyduje siatka CSS, a nie miejsce w kodzie. (Przy układzie na float blok boczny musiałby stać w kodzie przed blokiem głównym - tak jest w wersji podstawowej.)
Krok 15: Nagłówek pierwszego stopnia będący odnośnikiem
Kryteria: ,
R.2.4 W bloku nagłówkowym znajduje się <h1>: "Zwiedzaj Islandię", w bloku bocznym <h3>: "Do zwiedzania" w bloku głównym <h2>: "Galeria" lub "Opis miejsca" oraz <p> z numerem zdającego w stopce
R.3.3 Przynajmniej na jednej stronie kliknięcie odnośnika z bloku nagłówkowego prowadzi do strony islandia.php
ok. 1 min
Polecenie z arkusza
Zawartość bloku nagłówkowego: nagłówek pierwszego stopnia o treści „Zwiedzaj Islandię”. Treść jest odnośnikiem do strony islandia.php.
Odnośnik wewnątrz nagłówka (<h1><a>…</a></h1>) - nagłówek zostaje nagłówkiem, a jego treść da się kliknąć. Ze strony obiekty.php zaprowadzi z powrotem do galerii (R.2.4, R.3.3).
Krok 16: Blok główny: nagłówek „Galeria” i blok sekcji
Kryteria:
R.2.4 W bloku nagłówkowym znajduje się <h1>: "Zwiedzaj Islandię", w bloku bocznym <h3>: "Do zwiedzania" w bloku głównym <h2>: "Galeria" lub "Opis miejsca" oraz <p> z numerem zdającego w stopce
ok. 1 min
Polecenie z arkusza
Zawartość bloku głównego dla strony islandia.php: nagłówek drugiego stopnia o treści: „Galeria”. Blok sekcji z efektem wykonania skryptu 3.
<h2> przed <section> - nagłówek stoi nad ramką sekcji, jak na ilustracji 2. Sekcję wypełni galeria ze skryptu 3.
R.2.4 W bloku nagłówkowym znajduje się <h1>: "Zwiedzaj Islandię", w bloku bocznym <h3>: "Do zwiedzania" w bloku głównym <h2>: "Galeria" lub "Opis miejsca" oraz <p> z numerem zdającego w stopce
ok. 1 min
Polecenie z arkusza
Zawartość bloku bocznego: nagłówek trzeciego stopnia o treści „Do zwiedzania”.
R.2.6 Przynajmniej jeden element listy zawiera zagnieżdżoną jedną listę numerowaną (w skrypcie lub HTML, znaczniki <ol> i <li>, znacznik <ol> zawarto wewnątrz <li> listy z R.2.5)
ok. 2 min
Polecenie z arkusza
Element „Wodospady:” zawiera listę numerowaną (uporządkowaną) wypełnioną skryptem 1. Element „Siedliska zwierząt:” zawiera listę numerowaną (uporządkowaną) wypełnioną skryptem 2.
Lista zagnieżdżona to <ol>wewnątrz<li>, między tekstem a </li> (R.2.6). Elementy dopiszą skrypty 1 i 2.
Typowe błędy
<ol> między </li> a następnym <li> nie jest zagnieżdżona w elemencie - to niepoprawny HTML i brak R.2.6.
R.2.4 W bloku nagłówkowym znajduje się <h1>: "Zwiedzaj Islandię", w bloku bocznym <h3>: "Do zwiedzania" w bloku głównym <h2>: "Galeria" lub "Opis miejsca" oraz <p> z numerem zdającego w stopce
R.2.9 Stopka zawiera linię poziomą (<hr> lub za pomocą obramowania w CSS )
ok. 1 min
Polecenie z arkusza
Zawartość bloku stopki: linia pozioma. Paragraf o treści „Autor: ”, dalej wstawiony numer zdającego.
<hr> (element pusty, bez ukośnika w HTML5) i paragraf (R.2.4, R.2.9). Tu stoi nazwisko autora rozwiązania - na egzaminie wpisujesz swój numer zdającego (PESEL).
Witryna składa się z podstron: islandia.php i obiekty.php. Cechy wspólne obu stron: podział strony na bloki: nagłówkowy, boczny, główny, stopki.
Nagłówek, blok boczny i stopka będą identyczne na obu stronach. Zamiast kopiować je do obiekty.php, każdy trafia do własnego pliku: naglowek.php, boczny.php, stopka.php. W islandia.php w ich miejscu stoją trzy linie <?php require 'plik.php'; ?> - serwer wkleja zawartość pliku przy każdym wyświetleniu strony, więc przeglądarka dostaje dokładnie ten sam HTML co wcześniej.
require, a nie include: gdy pliku brakuje, require zatrzymuje stronę błędem, a include tylko ostrzega i wyświetla stronę bez nagłówka czy stopki. Brakująca część strony to błąd, który ma być widać od razu.
Pliki dołączane mają rozszerzenie .php i nie mają <!DOCTYPE> ani <head> - to fragmenty, nie strony. Deklaracja HTML5, <head> i bloki główne zostają w plikach stron, bo tam szuka ich klucz (R.2.1, R.2.2). Od tego kroku podgląd to wynik strony z serwera.
Typowe błędy
Ścieżka w require liczy się od pliku strony, która go wywołuje - pliki dołączane leżą w tym samym folderze co strony.
R.2.8 Na stronie obiekty.php w bloku głównym lub bloku sekcji znajdują się elementy <h2>, <h3> i przynajmniej jeden <p> (w skrypcie lub HTML )
ok. 2 min
Polecenie z arkusza
Zawartość bloku głównego dla strony obiekty.php: nagłówek drugiego stopnia o treści: „Opis miejsca”. Blok sekcji z efektem działania skryptu 4.
Druga strona to kopia islandia.php z innym nagłówkiem bloku głównego: „Opis miejsca”. Jest krótka - nagłówek, blok boczny i stopkę dołącza z tych samych plików, więc obie strony na pewno wyglądają jak jedna witryna (R.2.8).
Kroki 23-32 · po tym etapie masz 27 z 37 kryteriów (pkt)
Krok 23: 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: kolor PaleTurquoise i zaokrąglenie 20px dotyczą dwóch bloków, więc wpisane raz występują w regułach jako var(--accent) i var(--radius). W inspektorze widać obliczone wartości - kryteria sprawdza się tak samo.
R.4.1 domyślne formatowanie selektorów (*): krój czcionki "Comic Sans MS" - jako pierwszy, sans-serif - jako ostatni
ok. 1 min
Polecenie z arkusza
Domyślnie, dla wszystkich selektorów: krój czcionki Comic Sans MS, w przypadku braku sans-serif.
Selektor *, nazwa ze spacjami w cudzysłowie, na końcu rodzina zastępcza sans-serif (R.4.1).
styl.cssCSSzmienione: 4 linie
:root {--accent: PaleTurquoise;--frame: DimGray;--radius: 20px;}* {font-family: "Comic Sans MS", sans-serif;}
Podgląd strony po tym kroku (islandia.php)
Krok 25: Układ bloków zgodny z ilustracją 4: siatka
Kryteria: ,
R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z ilustracją 4 w arkuszu egzaminacyjnym (prawidłowo zastosowane właściwości CSS decydujące o układzie bloków na stronie, np. float i clear albo display flex albo grid albo position; żaden blok nie zachodzi na inne bloki - sprawdzić w inspektorze)
R.4.3 szerokość: blok nagłówkowy, główny - 70%, boczny - 25% (width lub grid-template- columns lub flex ) wysokość: blok nagłówkowy - 100px, boczny - 700px, sekcja - 400px
ok. 4 min
Polecenie z arkusza
Podział zrealizowany za pomocą semantycznych znaczników języka HTML5 tak, aby po uruchomieniu w przeglądarce układ bloków był zgodny z ilustracją 4.
Ciało strony staje się siatką, a grid-template-areas rysuje ilustrację 4 tekstem: w pierwszym wierszu nagłówek i blok boczny, w drugim blok główny i dalej blok boczny (to samo słowo w dwóch wierszach = blok na dwa wiersze), w trzecim stopka na całą szerokość. Cztery krótkie reguły przypisują bloki do obszarów (R.3.1).
grid-template-columns: 70% 25% - szerokości z arkusza: nagłówek i blok główny 70%, blok boczny 25% (klucz R.4.3 przyjmuje je w siatce), column-gap: 5% - reszta szerokości to odstęp,
grid-template-rows: auto 1fr - pierwszy wiersz ma wysokość nagłówka, a drugi bierze resztę wysokości bloku bocznego; bez tego siatka rozdzieliłaby nadmiar po równo i pod nagłówkiem powstałaby dziura,
align-items: start - bloki nie rozciągają się do wysokości wiersza, więc nagłówek ma swoje 100 px.
Bloki nie nachodzą na siebie, a stopka zawsze jest pod najwyższym z nich - bez clear.
Typowe błędy
width: 70% na nagłówku w siatce liczy się od szerokości kolumny, nie strony - szerokości trzymaj w grid-template-columns.
R.4.2 kolor tła: blok nagłówkowy i boczny - PaleTurquoise
R.4.5 wyrównanie tekstu: blok nagłówkowy, główny - do środka
R.4.7 zaokrąglenie rogów (border-radius) : blok nagłówkowy, boczny - 20px
ok. 1 min
Polecenie z arkusza
Dla bloku nagłówkowego i głównego: szerokość 70%, wyrównanie tekstu do środka. Dla bloku nagłówkowego i bocznego: kolor tła PaleTurquoise, zaokrąglenie rogów 20 px.
Szerokość 70% jest już w siatce. Dochodzą wspólne reguły par bloków: wyśrodkowany tekst (R.4.5), tło i zaokrąglenie ze zmiennych (R.4.2, R.4.7).
R.4.3 szerokość: blok nagłówkowy, główny - 70%, boczny - 25% (width lub grid-template- columns lub flex ) wysokość: blok nagłówkowy - 100px, boczny - 700px, sekcja - 400px
R.4.8 wysokość linii (line-hight) : blok boczny - 2
ok. 1 min
Polecenie z arkusza
Dodatkowo dla bloku bocznego: szerokość 25%, wysokość 700 px, wysokość linii (interlinia) 2.
Szerokość 25% to druga kolumna siatki. Wysokość 700 px i line-height: 2 - liczba bez jednostki to dwukrotność rozmiaru czcionki, więc interlinia rośnie razem z tekstem (R.4.3, R.4.8).
R.4.3 szerokość: blok nagłówkowy, główny - 70%, boczny - 25% (width lub grid-template- columns lub flex ) wysokość: blok nagłówkowy - 100px, boczny - 700px, sekcja - 400px
R.4.4 marginesy wewnętrzne (padding ): blok nagłówkowy - 2%, stopka jedynie padding-top: 10px; marginesy zewnętrzne (margin ): sekcja - górny i dolny - 10px, pozostałe - 50px
R.4.6 cechy sekcji: obramowanie 1px dashed DimGray, paski przewijania (overflow) auto
ok. 2 min
Polecenie z arkusza
Dla bloku sekcji: obramowanie linią kreskową o szerokości 1 px i kolorze DimGray, marginesy zewnętrzne górny i dolny 10 px, pozostałe 50 px, wysokość 400 px, paski przewijania pojawiające się tylko w przypadku przepełnienia bloku.
margin: 10px 50px - pierwsza wartość góra i dół, druga lewa i prawa; overflow: auto - paski tylko przy przepełnieniu; dashed - linia kreskowa (R.4.3, R.4.4, R.4.6).
Krok 31: Klasa miniatury: wysokość, przezroczystość, tranzycja
Kryteria:
R.4.9 styl klasy .miniatury : wysokość - 200px, padding: 10px, przezroczystość (opacity) - 0.8, tranzycja trwająca 1s (transition lub transition-duration)
ok. 2 min
Polecenie z arkusza
Dla klasy miniatury przypisanej do obrazów ze strony islandia.php, generowanych skryptem 3: wysokość 200 px, marginesy wewnętrzne 10 px, przezroczystość 0.8, dodana tranzycja trwająca 1 sekundę.
Nazwa klasy z arkusza. transition: opacity 1s podaje, którą właściwość animować i jak długo (tabela 3 arkusza) - przeglądarka nie musi śledzić zmian wszystkich właściwości, a przypadkowa zmiana innej (np. rozmiaru) nie zacznie się nagle animować (R.4.9).
Kroki 33-51 · po tym etapie masz 37 z 37 kryteriów (pkt)
Krok 33: Dane połączenia w pliku config.php
Kryteria:
R.5.1 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy islandia oraz ostatnią operacją na bazie jest jej zamknięcie (wystarczy na jednej stronie )
ok. 2 min
Polecenie z arkusza
Wymagania dotyczące skryptów: napisane w języku PHP. Łączą się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie islandia.
Dane logowania w osobnym pliku, który tylko zwraca tablicę z czterema wartościami dla mysqli: serwer, użytkownik, hasło, nazwa bazy. Na serwerze produkcyjnym podmienia się ten jeden plik, a do repozytorium z kodem go nie dodaje się.
Typowe błędy
config.php i baza.php muszą trafić do folderu z rezultatami.
config.phpPHPnowy plik
<?php// Dane połączenia z bazą - w jednym miejscu, poza plikami stronreturn ['host' => 'localhost','user' => 'root','password' => '','database' => 'islandia',];
R.5.1 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy islandia oraz ostatnią operacją na bazie jest jej zamknięcie (wystarczy na jednej stronie )
R.5.2 Zastosowana przynajmniej jedna zmienna oraz nazewnictwo jest w języku polskim lub angielskim i odzwierciedla cel zastosowania
ok. 4 min
Polecenie z arkusza
Łączą się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie islandia.
Plik baza.php łączy się z bazą, a obie strony zaczynają się od require 'baza.php'; (R.5.1, R.5.2).
new mysqli(...) - ta sama biblioteka co mysqli_connect z tabeli 1, ale w stylu obiektowym: połączenie to obiekt $db, a operacje to jego metody ($db->query(), $db->prepare(), $db->close()),
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT) - każdy błąd bazy to wyjątek (w PHP 8.1 i nowszych to zachowanie domyślne, wpisane jawnie nie zależy od wersji),
set_charset('utf8mb4') - kodowanie połączenia; bez niego polskie i islandzkie litery mogłyby przyjść z bazy zniekształcone,
set_exception_handler - jedna funkcja obsługuje każdy nieobsłużony wyjątek w całej witrynie: szczegóły zapisuje do dziennika błędów serwera (error_log), a użytkownik widzi tylko ogólny komunikat i kod 500. Nie trzeba otaczać try każdego zapytania na każdej stronie.
Typowe błędy
mysqli_report musi stać przednew mysqli - inaczej błąd samego połączenia nie będzie wyjątkiem.
baza.phpPHPnowy plik
<?php// Połączenie z bazą i funkcje pobierające dane - wspólne dla obu stron$config = require'config.php';mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);set_exception_handler(function (Throwable $error): void {error_log((string) $error);http_response_code(500);echo'Baza danych jest chwilowo niedostępna. Spróbuj ponownie później.';});$db = new mysqli($config['host'], $config['user'], $config['password'], $config['database']);$db->set_charset('utf8mb4');
Krok 35: Funkcja e(): bezpieczne wypisywanie tekstu
ok. 2 min
Polecenie z arkusza
Należy stosować znaczące nazewnictwo zmiennych w języku polskim lub angielskim.
Funkcja e() w baza.php jest dostępna na obu stronach i we wszystkich plikach dołączanych. htmlspecialchars z ENT_QUOTES zamienia <, >, & i cudzysłowy na encje, więc tekst z bazy nie rozbije atrybutu ani nie wykona się jako skrypt (XSS). Typ ?string przyjmuje też null - kolumny nazwaCechy, wartoscCechy i opis mogą być puste.
baza.phpPHPzmienione: 5 linii
<?php// Połączenie z bazą i funkcje pobierające dane - wspólne dla obu stron$config = require'config.php';mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);set_exception_handler(function (Throwable $error): void {error_log((string) $error);http_response_code(500);echo'Baza danych jest chwilowo niedostępna. Spróbuj ponownie później.';});$db = new mysqli($config['host'], $config['user'], $config['password'], $config['database']);$db->set_charset('utf8mb4');functione(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');}
Krok 36: Skrypt 1: zapytanie 3 w funkcji objectNames()
Kryteria:
R.5.3 Skrypty zawierają przynajmniej jedną instrukcję wysyłającą zapytanie do bazy danych: skrypt 2 zmodyfikowane zapytanie 3 - idRodzaj = 14 skrypt 1 zapytanie 3 skrypt 3 - zapytanie 1 skrypt 4 - zmodyfikowane zapytanie 2 - id pobrane z tablicy $_GET
ok. 4 min
Polecenie z arkusza
Skrypt 1 na obu stronach: wysyła do bazy danych zapytanie 3.
Zapytanie 3 mieszka w funkcji objectNames($db, $typeId) w baza.php (R.5.3), a strona tylko ją wywołuje: $waterfalls = objectNames($db, 10);.
prepare z ? w miejscu numeru rodzaju, bind_param('i', $typeId) - wartość jako liczba całkowita (i), osobno od treści SQL,
execute(), a get_result()->fetch_all(MYSQLI_ASSOC) odbiera wszystkie wiersze naraz,
array_column($rows, 'nazwa') wyciąga z wierszy samą kolumnę nazwa - funkcja zwraca zwykłą listę nazw.
Typy przy parametrach (mysqli $db, int $typeId) i wyniku (array) mówią, czego funkcja oczekuje i co oddaje - pomyłka (np. numer jako napis z adresu) skończy się czytelnym błędem.
baza.phpPHPzmienione: 9 linii
<?php// Połączenie z bazą i funkcje pobierające dane - wspólne dla obu stron$config = require'config.php';mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);set_exception_handler(function (Throwable $error): void {error_log((string) $error);http_response_code(500);echo'Baza danych jest chwilowo niedostępna. Spróbuj ponownie później.';});$db = new mysqli($config['host'], $config['user'], $config['password'], $config['database']);$db->set_charset('utf8mb4');functione(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');}functionobjectNames(mysqli $db, int $typeId): array{$query = $db->prepare("SELECT nazwa FROM obiekty WHERE panstwo = 'Islandia' AND idRodzaj = ?");$query->bind_param('i', $typeId);$query->execute();$rows = $query->get_result()->fetch_all(MYSQLI_ASSOC);returnarray_column($rows, 'nazwa');}
R.5.4 Skrypt 1 lub skrypt 2 wyświetla wszystkie zwrócone zapytaniem nazwy jako elementy listy
ok. 2 min
Polecenie z arkusza
Każda zwrócona zapytaniem nazwa jest wyświetlana jako element listy.
Pętla stoi w boczny.php - jedynym miejscu z kodem bloku bocznego: foreach (...): … endforeach; i jeden <li> na nazwę przez e() (R.5.4). Plik dołączany widzi zmienne strony, która go dołącza, więc korzysta z $waterfalls. Dlatego obie strony muszą tę zmienną przygotować - obiekty.php dostaje tę samą linię z objectNames().
Typowe błędy
Strona, która dołącza boczny.php bez przygotowania $waterfalls, kończy się ostrzeżeniem „Undefined variable” i błędem pętli.
Krok 38: Skrypt 2: siedliska zwierząt na obu stronach
Kryteria: ,
R.5.3 Skrypty zawierają przynajmniej jedną instrukcję wysyłającą zapytanie do bazy danych: skrypt 2 zmodyfikowane zapytanie 3 - idRodzaj = 14 skrypt 1 zapytanie 3 skrypt 3 - zapytanie 1 skrypt 4 - zmodyfikowane zapytanie 2 - id pobrane z tablicy $_GET
R.5.4 Skrypt 1 lub skrypt 2 wyświetla wszystkie zwrócone zapytaniem nazwy jako elementy listy
ok. 2 min
Polecenie z arkusza
Skrypt 2 na obu stronach: wysyła do bazy danych zapytanie 3, zmodyfikowane tak, że w warunku sprawdzane jest pole idRodzaj równe 14. Każda zwrócona zapytaniem nazwa jest wyświetlana jako element listy.
„Zmodyfikowane zapytanie” to ta sama funkcja z innym argumentem: objectNames($db, 14). Jedna linia w każdej stronie i druga pętla w boczny.php (R.5.3, R.5.4) - blok boczny jest gotowy na obu stronach naraz, bo istnieje w jednym pliku.
Krok 39: Skrypt 3: zapytanie 1 w funkcji gallery()
Kryteria:
R.5.3 Skrypty zawierają przynajmniej jedną instrukcję wysyłającą zapytanie do bazy danych: skrypt 2 zmodyfikowane zapytanie 3 - idRodzaj = 14 skrypt 1 zapytanie 3 skrypt 3 - zapytanie 1 skrypt 4 - zmodyfikowane zapytanie 2 - id pobrane z tablicy $_GET
ok. 1 min
Polecenie z arkusza
Skrypt 3 na stronie islandia.php: wysyła do bazy danych zapytanie 1.
Zapytanie 1 nie ma danych z zewnątrz, więc wystarcza $db->query(...), a fetch_all(MYSQLI_ASSOC) zwraca wszystkie wiersze z nazwami kolumn (R.5.3). Strona woła gallery($db).
baza.phpPHPzmienione: 6 linii
<?php// Połączenie z bazą i funkcje pobierające dane - wspólne dla obu stron$config = require'config.php';mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);set_exception_handler(function (Throwable $error): void {error_log((string) $error);http_response_code(500);echo'Baza danych jest chwilowo niedostępna. Spróbuj ponownie później.';});$db = new mysqli($config['host'], $config['user'], $config['password'], $config['database']);$db->set_charset('utf8mb4');functione(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');}functionobjectNames(mysqli $db, int $typeId): array{$query = $db->prepare("SELECT nazwa FROM obiekty WHERE panstwo = 'Islandia' AND idRodzaj = ?");$query->bind_param('i', $typeId);$query->execute();$rows = $query->get_result()->fetch_all(MYSQLI_ASSOC);returnarray_column($rows, 'nazwa');}functiongallery(mysqli $db): array{return$db->query("SELECT idObiekt, plik, nazwa FROM obiekty WHERE panstwo = 'Islandia'") ->fetch_all(MYSQLI_ASSOC);}
Krok 41: Skrypt 3: dymek z nazwą i klasa miniatury
Kryteria:
R.3.5 Przynajmniej jeden obraz ze strony islandia.php ma przypisaną klasę miniatury oraz atrybut title
ok. 1 min
Polecenie z arkusza
Dodatkowo pole nazwa jest tekstem pojawiającym się w postaci dymka, gdy kursor znajdzie się na obrazie (ilustracja 2, czwarty obraz). Obraz ma przypisaną klasę miniatury.
Dymek to atrybut title, a klasa miniatury podpina regułę z CSS (R.3.5).
R.3.4 Na stronie islandia.php umieszczono przynajmniej jeden odnośnik, którego kliknięcie powoduje otwarcie strony obiekty.php oraz zostaje wysłane żądanie metodą GET z jednym parametrem
R.5.5 Skrypt 3 dla każdego zwróconego zapytaniem wiersza wstawia idObiekt jako wartość parametru przesłanego metodą GET
ok. 2 min
Polecenie z arkusza
Dla każdego zwróconego zapytaniem wiersza wypisuje odnośnik prowadzący do strony obiekty.php oraz przekazujący do tej strony metodą GET parametr o wartości równej polu idObiekt.
Odnośnik obiekty.php?id=… wokół obrazu, wartość parametru to idObiekt (R.3.4, R.5.5). Odnośnik i obraz w jednej linii kodu - spacje wewnątrz <a> przeglądarka podkreśliłaby jako kreskę.
Krok 43: Zamknięcie połączenia na stronie islandia.php
Kryteria:
R.5.1 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy islandia oraz ostatnią operacją na bazie jest jej zamknięcie (wystarczy na jednej stronie )
ok. 1 min
Polecenie z arkusza
Na końcu należy zamknąć połączenie z bazą.
$db->close() - obiektowy odpowiednik mysqli_close($db) (R.5.1). Stoi zaraz po pobraniu wszystkich danych: szablon i pliki dołączane korzystają już tylko z tablic, więc połączenie nie musi czekać na koniec strony.
Krok 44: Skrypt 4: identyfikator z adresu, sprawdzony
ok. 2 min
Polecenie z arkusza
Skrypt 4 na stronie obiekty.php: wysyła do bazy danych zapytanie 2, zmodyfikowane w ten sposób, że w warunku jest sprawdzane id obiektu przesłane jako parametr metody GET ze strony islandia.php.
Wartość z $_GET['id'] przechodzi przez filter_var z FILTER_VALIDATE_INT i min_range 1: ?id=53 daje liczbę 53, a ?id=abc, ?id=-5 albo brak parametru (?? null) - false, bez ostrzeżeń PHP. Funkcja place() z następnego kroku przyjmuje tylko int, więc do bazy nie da się przekazać niczego innego.
R.5.3 Skrypty zawierają przynajmniej jedną instrukcję wysyłającą zapytanie do bazy danych: skrypt 2 zmodyfikowane zapytanie 3 - idRodzaj = 14 skrypt 1 zapytanie 3 skrypt 3 - zapytanie 1 skrypt 4 - zmodyfikowane zapytanie 2 - id pobrane z tablicy $_GET
ok. 3 min
Polecenie z arkusza
Wysyła do bazy danych zapytanie 2, zmodyfikowane w ten sposób, że w warunku jest sprawdzane id obiektu przesłane jako parametr metody GET ze strony islandia.php.
Zapytanie 2 z ? w miejscu identyfikatora i bind_param('i', …) (R.5.3) - wartość z adresu nigdy nie staje się częścią polecenia SQL. fetch_assoc() zwraca wiersz albo null, gdy takiego miejsca nie ma; typ wyniku ?array mówi to wprost.
Na stronie operator warunkowy: dla złego identyfikatora funkcja w ogóle nie jest wołana, a $place jest null.
baza.phpPHPzmienione: 13 linii
<?php// Połączenie z bazą i funkcje pobierające dane - wspólne dla obu stron$config = require'config.php';mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);set_exception_handler(function (Throwable $error): void {error_log((string) $error);http_response_code(500);echo'Baza danych jest chwilowo niedostępna. Spróbuj ponownie później.';});$db = new mysqli($config['host'], $config['user'], $config['password'], $config['database']);$db->set_charset('utf8mb4');functione(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');}functionobjectNames(mysqli $db, int $typeId): array{$query = $db->prepare("SELECT nazwa FROM obiekty WHERE panstwo = 'Islandia' AND idRodzaj = ?");$query->bind_param('i', $typeId);$query->execute();$rows = $query->get_result()->fetch_all(MYSQLI_ASSOC);returnarray_column($rows, 'nazwa');}functiongallery(mysqli $db): array{return$db->query("SELECT idObiekt, plik, nazwa FROM obiekty WHERE panstwo = 'Islandia'") ->fetch_all(MYSQLI_ASSOC);}functionplace(mysqli $db, int $objectId): ?array{$query = $db->prepare('SELECT o.plik, o.nazwa, o.nazwaCechy, o.wartoscCechy, o.opis, r.rodzaj FROM obiekty AS o JOIN rodzaje AS r ON r.idRodzaj = o.idRodzaj WHERE o.idObiekt = ?' );$query->bind_param('i', $objectId);$query->execute();return$query->get_result()->fetch_assoc();}
Krok 50: Skrypt 4: opis i strona dla nieznanego miejsca
Kryteria:
R.5.7 Skrypt 4 wyświetla obraz oraz dane których id zostało przesłane metodą GET
ok. 2 min
Polecenie z arkusza
W paragrafie wyświetla pole opis.
Ostatni paragraf to opis (R.5.7). Gałąź else mówi wprost, że miejsca nie znaleziono, a http_response_code(404) - ustawiony przed HTML-em - zgłasza przeglądarce i wyszukiwarkom brak strony.
Krok 51: Zamknięcie połączenia na stronie obiekty.php
Kryteria:
R.5.1 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy islandia oraz ostatnią operacją na bazie jest jej zamknięcie (wystarczy na jednej stronie )
ok. 1 min
Polecenie z arkusza
Na końcu należy zamknąć połączenie z bazą.
$db->close() po ostatnim zapytaniu strony (R.5.1). Zapytania przygotowane w funkcjach są zmiennymi lokalnymi - znikają po wyjściu z funkcji, więc nic nie trzyma już połączenia.
Krok 52 · po tym etapie masz 37 z 37 kryteriów (pkt)
Krok 52: Co jest inaczej i którą wersję wybrać na egzaminie
ok. 3 min
Polecenie z arkusza
Czas przeznaczony na wykonanie zadania wynosi 150 minut.
Wszystkie trzy wersje dają komplet 37 punktów, jeśli egzaminator znajdzie kod. Różnice:
wersja podstawowa
nowoczesny kod
ta wersja
pliki PHP
2 strony
2 strony + config.php
2 krótkie strony + baza.php, 3 części stron, config.php
biblioteka
funkcje mysqli_*
PDO
mysqli obiektowe
SQL
w stronach
na górze stron
tylko w baza.php, w funkcjach
blok boczny
dwie kopie
dwie kopie
jeden plik boczny.php
awaria bazy
błąd PHP
try / catch na każdej stronie
jedno set_exception_handler
mysqli obiektowe i PDO są równie dobre; mysqli działa tylko z MySQL/MariaDB, PDO także z innymi bazami. Kto zna funkcje z tabeli 1, przesiada się na mysqli obiektowe najłatwiej: mysqli_query($db, $sql) to $db->query($sql).
Na egzaminie wybierz wersję podstawową albo nowoczesną: klucz każe sprawdzać R.5.1-R.5.3 i R.5.5 „w kodzie źródłowym strony”, a tu połączenie i zapytania są w innym pliku. Tę organizację stosuj w projektach i na zajęciach - dodanie trzeciej podstrony to nowy plik z dwiema linijkami PHP i dwoma require.
Krok 53 · po tym etapie masz 37 z 37 kryteriów (pkt)
Krok 53: Plik przeglądarka.txt i nagranie płyty
ok. 5 min
Polecenie z arkusza
Po zakończeniu pracy utwórz plik tekstowy o nazwie przeglądarka.txt. Zapisz w nim nazwę przeglądarki internetowej, w której weryfikowana była poprawność działania witryny. Nagraj płytę z rezultatami pracy.
W folderze z numerem zdającego oprócz plików z arkusza (dziewięć zdjęć, import.jpg, islandia.php, obiekty.php, kw1.png … kw4.png, kwerendy.txt, przeglądarka.txt, styl.css) muszą być wszystkie pliki dołączane: baza.php, config.php, naglowek.php, boczny.php, stopka.php. Bez nich strony się nie wyświetlą. Płytę sprawdź po nagraniu.
Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.
To jedno z możliwych rozwiązań. Egzaminator ocenia spełnienie kryteriów z klucza odpowiedzi, a nie zgodność z tym kodem - inna, poprawna droga też daje punkty.
Autor rozwiązania: Bartosz Bryniarski. Licencja CC BY-NC 4.0 - kopiowanie i przeróbki do celów niekomercyjnych, z podaniem autora i źródła (link do tej strony) (zasady wykorzystania). Treść arkusza i klucza oceniania pochodzi z CKE.