INF.03-06-25.06-SGCzerwiec 2025 · 47 kroków · ok. 95 min · 42 kryteria z klucza
Rozwiązanie krok po kroku: Portal biblioteki internetowej (Nowoczesny kod (grid, PDO))
To drugi sposób rozwiązania tego samego arkusza. Wynik jest ten sam - baza biblioteka z pięcioma operacjami, przezroczysta grafika, strona biblioteka.php z czterema sekcjami i czterema skryptami - i tak samo spełnia każde kryterium klucza. Inna jest droga:
siatka CSS w podejściu „mobile first”: domyślnie sekcje stoją jedna pod drugą (wąski ekran), a jedno zapytanie @media (min-width: 801px) ustawia je w czterech kolumnach - bez float i clear,
PHP na PDO: jedno zapytanie przygotowane wykonane kolejno dla trzech gatunków i pętla po tablicy sekcji zamiast trzech skopiowanych skryptów, filter_input i biała lista dla id książki, przekierowanie po rezerwacji (PRG), funkcja e() przy każdym wypisaniu i dane logowania w config.php.
Arkusz każe skrypty 2 i 3 „powielić i dostosować”; klucz ocenia, czy działają we wszystkich trzech sekcjach (R.5.8), a nie liczbę kopii - pętla daje ten sam wynik bez powtórzeń. Wersja podstawowa (zakładka obok) robi to najpierw kopiami, a funkcje wprowadza na końcu. Na końcu jest krótkie porównanie obu wersji.
Grafika i baza pochodzą z prawdziwego archiwum pliki4 (załącznik na stronie arkusza), a podgląd każdego kroku z PHP to wynik prawdziwego uruchomienia strony na serwerze z tą bazą.
Jak liczą się punkty. Klucz ma 42 kryteria (R.1.1 - R.5.10), każde za 1 punkt. Próg zdania to 75%, czyli 32 punkty. Przy każdym kroku jest napisane, które kryteria spełnia.
Suwak i strzałki obok niego - albo ←→ na klawiaturze - przechodzą krok po kroku; wyjaśnienie, pliki i podgląd zmieniają się razem z krokiem.
Lista „Etap” przenosi od razu do wybranego etapu.
„Rozwiązuję samodzielnie” chowa rozwiązanie kroku - najpierw własna próba, potem odsłonięcie.
„Tryb prezentacji” pokazuje same kroki, bez menu strony - na rzutnik.
Etap 1: Baza danych
Kroki 1-8 · po tym etapie masz 7 z 42 kryteriów (pkt)
Krok 1: Baza danych biblioteka
Kryteria:
R.1.1 Wykonany import tabeli do bazy danych biblioteka , czynność udokumentowana w pliku import.png
ok. 2 min
Polecenie z arkusza
Utwórz bazę danych o nazwie biblioteka, z zestawem polskich znaków (np. utf8_unicode_ci).
phpMyAdmin → Nowa → nazwa biblioteka, porównywanie utf8_unicode_ci. W SQL:
CREATE DATABASE biblioteka CHARACTER SET utf8 COLLATE utf8_unicode_ci;
Typowe błędy
Literówka w nazwie bazy odcina też punkty za skrypty - połączenie podaje nazwę bazy (R.5.1).
Krok 2: Import tabel z pliku biblio.sql
Kryteria:
R.1.1 Wykonany import tabeli do bazy danych biblioteka , czynność udokumentowana w pliku import.png
ok. 2 min
Polecenie z arkusza
Z rozpakowanego archiwum zaimportuj tabele z pliku biblio.sql do utworzonej bazy.
Zaznacz bazę biblioteka, karta Import, plik biblio.sql, Wykonaj. Dwie tabele z ilustracji 1: ksiazka (20 książek, gatunek = liryka, epika albo dramat) i wypozyczenia (58 wypożyczeń, id_ks to klucz obcy do ksiazka.id).
Krok 3: Zrzut ekranu import.png
Kryteria:
R.1.1 Wykonany import tabeli do bazy danych biblioteka , czynność udokumentowana w pliku 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ń.
Cały ekran z paskiem zadań, zapis jako import.png. Zrzuty zapytań w tym arkuszu są w JPEG.
Typowe błędy
Wykadrowany zrzut nie jest oceniany.
Krok 4: Zapytanie 1: książki z gatunku liryka
Kryteria: ,
R.1.2 Zapisany plik kwerendy.txt zawierający co najmniej jedno zapytanie SQL, wynikające z treści zadania
R.1.3 Wykonane zapytanie 1 wybierające jedynie id i tytuły książek z gatunku liryka. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT id, tytul FROM ksiazka WHERE gatunek = "liryka"; oraz wynik działania zapytania udokumentowany zrzutem, na którym widocznych jest dokładnie 5 rekordów z id: 1, 5, 6, 19, 20
ok. 2 min
Polecenie z arkusza
Zapytanie 1: wybierające jedynie id i tytuły książek z gatunku liryka.
Każda klauzula w osobnej linii, napis w apostrofach - to zapis ze standardu SQL, działający w każdej bazie (cudzysłów jako napis rozumie tylko MySQL). Wynik: 5 książek, id 1, 5, 6, 19, 20 (R.1.3). Zapytanie do kwerendy.txt, zrzut kw1.jpg. To samo zapytanie, z gatunkiem jako parametrem, wypełni trzy listy rozwijane.
kwerendy.txtTekstnowy plik
-- Zapytanie 1SELECT id, tytulFROM ksiazkaWHERE gatunek = 'liryka';
R.1.4 Wykonane zapytanie 2 wybierające pierwsze 15 rekordów, jedynie tytuł książki oraz odpowiadające mu id czytelnika i data oddania posortowane rosnąco według daty oddania. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT tytul, id_cz, data_odd FROM ksiazka JOIN wypozyczenia ON id = id_ks ORDER BY data_odd LIMIT 15; (możliwe również INNER JOIN lub porównanie kluczy po WHERE) oraz wynik działania zapytania udokumentowany zrzutem, na którym widoczne jest dokładnie 15 rekordów, pierwsze 5 rekordów o id_cz: 1, 2, 1, 4, 4
ok. 4 min
Polecenie z arkusza
Zapytanie 2: wybierające pierwsze 15 rekordów, jedynie tytuł książki oraz odpowiadające mu id czytelnika i data oddania posortowane rosnąco według daty oddania. Należy posłużyć się relacją.
Złączenie z aliasami: ksiazka AS k, wypozyczenia AS w, warunek ON w.id_ks = k.id (klucz obcy = klucz główny). Kolejność klauzul jest stała: FROM → JOIN → ORDER BY → LIMIT - najpierw sortowanie, potem odcięcie piętnastu pierwszych.
Wynik: 15 wierszy, pierwsze pięć z czytelnikami 1, 2, 1, 4, 4 (R.1.4), zrzut kw2.jpg.
Typowe błędy
LIMIT 15 bez ORDER BY zwraca piętnaście dowolnych wierszy.
kwerendy.txtTekstzmienione: 7 linii
-- Zapytanie 1SELECT id, tytulFROM ksiazkaWHERE gatunek = 'liryka';-- Zapytanie 2SELECT k.tytul, w.id_cz, w.data_oddFROM ksiazka AS kJOIN wypozyczenia AS w ON w.id_ks = k.idORDER BY w.data_odd ASCLIMIT 15;
Podgląd strony po tym kroku
Krok 6: Zapytanie 3: nowa kolumna rezerwacja
Kryteria:
R.1.5 Wykonane zapytanie 3 modyfikujące strukturę tabeli ksiazka . Dodana jest nowa kolumna o nazwie rezerwacja typu jednocyfrowego TINYINT, przyjmująca wartość domyślną 0. W pliku z kwerendami lub na zrzucie istnieje zapis np.: ALTER TABLE ksiazka ADD COLUMN rezerwacja TINYINT(1) DEFAULT 0; (COLUMN opcjonalne) oraz wynik działania zapytania udokumentowany zrzutem, na którym widoczny jest komunikat o poprawnym wykonaniu zapytania lub zmieniona struktura tabeli
ok. 3 min
Polecenie z arkusza
Zapytanie 3: modyfikujące strukturę tabeli ksiazka, poprzez dodanie nowej kolumny o nazwie rezerwacja typu TINYINT, o długości 1 Bajta, która przyjmuje wartość domyślną 0 (oznacza, że książka nie jest zarezerwowana).
ALTER TABLE … ADD COLUMN zmienia strukturę. Typ TINYINT zajmuje zawsze 1 bajt (zakres -128…127) - to on spełnia „długość 1 bajta”. Liczba w nawiasie, TINYINT(1), to tylko szerokość wyświetlania (podpowiedź dla programów, ile cyfr pokazać), nie rozmiar; zostaje, bo tak zapisuje to klucz.
NOT NULL DEFAULT 0: każda książka, także dodana później bez podania tej kolumny, ma 0, a nie pusty NULL - a rezerwacja ma tylko dwa stany. Zrzut kw3.jpg (R.1.5). Bez tej kolumny skrypt 3 nie zadziała.
Typowe błędy
UPDATE zamiast ALTER TABLE zmienia dane, nie strukturę - kolumny nie przybędzie.
kwerendy.txtTekstzmienione: 3 linie
-- Zapytanie 1SELECT id, tytulFROM ksiazkaWHERE gatunek = 'liryka';-- Zapytanie 2SELECT k.tytul, w.id_cz, w.data_oddFROM ksiazka AS kJOIN wypozyczenia AS w ON w.id_ks = k.idORDER BY w.data_odd ASCLIMIT 15;-- Zapytanie 3ALTER TABLE ksiazka ADD COLUMN rezerwacja TINYINT(1) NOT NULL DEFAULT 0;
Podgląd strony po tym kroku
Krok 7: Zapytanie 4: rezerwacja książki o id 1
Kryteria:
R.1.6 Wykonane zapytanie 4 aktualizujące w tabeli ksiazka pole rezerwacja na wartość 1 dla id = 1. W pliku z kwerendami lub na zrzucie istnieje zapis np.: UPDATE ksiazka SET rezerwacja = 1 WHERE id = 1;
ok. 2 min
Polecenie z arkusza
Zapytanie 4: aktualizujące pole rezerwacja na wartość 1 dla książki, której id=1.
UPDATE zmienia dane: SET mówi co, WHERE - w którym wierszu (R.1.6). Zrzut kw4.jpg z komunikatem o jednym zmienionym wierszu.
Typowe błędy
UPDATE bez WHERE rezerwuje wszystkie 20 książek.
kwerendy.txtTekstzmienione: 5 linii
-- Zapytanie 1SELECT id, tytulFROM ksiazkaWHERE gatunek = 'liryka';-- Zapytanie 2SELECT k.tytul, w.id_cz, w.data_oddFROM ksiazka AS kJOIN wypozyczenia AS w ON w.id_ks = k.idORDER BY w.data_odd ASCLIMIT 15;-- Zapytanie 3ALTER TABLE ksiazka ADD COLUMN rezerwacja TINYINT(1) NOT NULL DEFAULT 0;-- Zapytanie 4UPDATE ksiazkaSET rezerwacja = 1WHERE id = 1;
Podgląd strony po tym kroku
Krok 8: Zapytanie 5: tytuł książki o id 4
Kryteria:
R.1.7 Wykonane zapytanie 5 wybierające jedynie tytuł książki dla id = 4. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT tytul FROM ksiazka WHERE id = 4; oraz wynik działania zapytania udokumentowany zrzutem, na którym widoczny jest tytuł Antygona
ok. 1 min
Polecenie z arkusza
Zapytanie 5: wybierające jedynie tytuł książki, której id=4.
Jedna kolumna, jeden wiersz po kluczu głównym - Antygona (R.1.7), zrzut kw5.jpg.
kwerendy.txtTekstzmienione: 5 linii
-- Zapytanie 1SELECT id, tytulFROM ksiazkaWHERE gatunek = 'liryka';-- Zapytanie 2SELECT k.tytul, w.id_cz, w.data_oddFROM ksiazka AS kJOIN wypozyczenia AS w ON w.id_ks = k.idORDER BY w.data_odd ASCLIMIT 15;-- Zapytanie 3ALTER TABLE ksiazka ADD COLUMN rezerwacja TINYINT(1) NOT NULL DEFAULT 0;-- Zapytanie 4UPDATE ksiazkaSET rezerwacja = 1WHERE id = 1;-- Zapytanie 5SELECT tytulFROM ksiazkaWHERE id = 4;
Podgląd strony po tym kroku
Etap 2: Grafika i strona HTML
Kroki 9-22 · po tym etapie masz 20 z 42 kryteriów (pkt)
Krok 9: Grafika obraz.png: wysokość 200 px, przezroczyste tło
Kryteria:
R.2.1 Grafika obraz.png ma tło przezroczyste oraz została przeskalowana z zachowaniem proporcji do wysokości 200 px, odpowiadająca temu szerokość wynosi 188 px (± 5 px)
ok. 5 min
Polecenie z arkusza
Cechy grafiki obraz.jpg: przeskalowana z zachowaniem proporcji do wysokości 200 px. Przezroczystość zamiast białego tła. Zapisana pod tą samą nazwą, w formacie PNG.
W GIMP-ie: Warstwa → Przezroczystość → Dodaj kanał alfa, różdżka na białym tle i Delete, Skaluj obraz do wysokości 200 px przy zamkniętym łańcuchu (szerokość 188 px), Wyeksportuj jakoobraz.png (R.2.1). PNG, bo JPEG nie przechowuje przezroczystości.
Typowe błędy
Eksport do .jpg przywraca białe tło.
Podgląd strony po tym kroku
Krok 10: Plik biblioteka.php w HTML5
Kryteria:
R.2.2 W pliku biblioteka.php zapisana deklaracja HTML5 <!DOCTYPE HTML>, deklaracja języka zawartości: polski np. <html lang="pl">, w sekcji head znajduje się jawnie podany standard kodowania polskich znaków
ok. 1 min
Polecenie z arkusza
Składa się ze strony o nazwie biblioteka.php. Zapisana w języku HTML5.
Plik biblioteka.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 pliku biblioteka.php zapisana deklaracja HTML5 <!DOCTYPE HTML>, deklaracja języka zawartości: polski np. <html lang="pl">, w sekcji head znajduje się jawnie podany standard kodowania polskich znaków
ok. 1 min
Polecenie z arkusza
Zadeklarowany polski język zawartości witryny.
lang="pl" w <html> - czytnik ekranu czyta polską wymową, przeglądarka dobiera słownik i dzielenie wyrazów.
R.2.2 W pliku biblioteka.php zapisana deklaracja HTML5 <!DOCTYPE HTML>, deklaracja języka zawartości: polski np. <html lang="pl">, w sekcji head znajduje się jawnie podany standard kodowania polskich znaków
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.
<title>Biblioteka miejska</title> - napis na karcie przeglądarki. Obok <meta name="viewport">: bez niego telefon pokazałby stronę pomniejszoną jak na monitorze. Arkusz tego nie wymaga, a w każdym współczesnym szablonie ta linia jest.
R.2.4 W sekcji body zdefiniowany jest podział na bloki za pomocą semantycznych znaczników HTML 5: header, section+section+section+section, footer
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ą 3.
Ilustracja 3 nie ma bloku głównego: nagłówek, cztery bloki sekcji, stopka - dokładnie znaczniki z klucza R.2.4. Wszystkie bloki są bezpośrednio w <body>, więc to <body> stanie się siatką w CSS. Sekcje nie potrzebują identyfikatorów - wszystkie wyglądają tak samo.
Typowe błędy
Cztery <div> wyglądają tak samo, ale R.2.4 wymaga znaczników semantycznych.
Krok 18: Pierwsza sekcja: formularz z listą rozwijaną
Kryteria: , ,
R.2.7 W przynajmniej jednym bloku umieszczony formularz
R.2.8 W przynajmniej jednym bloku umieszczona lista rozwijana i przycisk "Rezerwuj", do listy zastosowane znaczniki <select> i <option> (w HTML lub skrypcie)
R.3.3 Przynajmniej jeden formularz wysyła dane metodą POST do tego samego pliku (poprawne również bez atrybutu action)
ok. 2 min
Polecenie z arkusza
Formularz wysyłający dane metodą bezpieczną do tego samego pliku, z elementami: lista rozwijana wypełniona skryptem 2.
method="post" bez action (R.2.7, R.3.3). Lista ma nazwę poetry - każda sekcja dostanie inną (poetry, epic, drama), więc PHP rozpozna, z której sekcji przyszło zamówienie. Arkusz nie daje liście etykiety, dlatego aria-label="Książka": czytnik ekranu przeczyta, czego dotyczy lista.
Typowe błędy
Ta sama nazwa listy w trzech formularzach uniemożliwia wskazanie sekcji, w której pokazać komunikat (R.5.8).
R.2.8 W przynajmniej jednym bloku umieszczona lista rozwijana i przycisk "Rezerwuj", do listy zastosowane znaczniki <select> i <option> (w HTML lub skrypcie)
R.3.4 Przynajmniej jeden przycisk realizuje funkcję wysłania danych z formularza (input lub button z typem submit lub button bez typu)
ok. 1 min
Polecenie z arkusza
Przycisk „Rezerwuj” wysyłający dane z formularza do skryptu 3.
<button type="submit"> w formularzu (R.2.8, R.3.4). Komunikat skryptu 3 stanie pod formularzem.
R.2.5 Umieszczone w blokach sekcji nagłówki <h2>: „Liryka”, „Epika”, „Dramat”, „Zaległe książki” oraz paragraf z numerem zdającego w stopce
ok. 2 min
Polecenie z arkusza
Zawartość drugiego bloku sekcji: nagłówek drugiego stopnia o treści „Epika”, formularz z listą rozwijaną i przyciskiem „Rezerwuj”. Zawartość trzeciego bloku sekcji: nagłówek drugiego stopnia o treści „Dramat”, formularz z listą rozwijaną i przyciskiem „Rezerwuj”.
Ta sama budowa z innym nagłówkiem i inną nazwą listy: epic, drama (R.2.5).
R.2.5 Umieszczone w blokach sekcji nagłówki <h2>: „Liryka”, „Epika”, „Dramat”, „Zaległe książki” oraz paragraf z numerem zdającego w stopce
R.2.10 Zastosowany znacznik <strong> do zapisu treści stopki
ok. 1 min
Polecenie z arkusza
Zawartość stopki: paragraf o treści „Autor: ”, dalej wstawiony numer zdającego. Tekst w paragrafie jest zapisany za pomocą znacznika semantycznego oznaczającego tekst ważny, formatowany domyślnie jako pogrubiony.
Cała treść paragrafu w <strong> (R.2.10). Tu nazwisko autora rozwiązania - na egzaminie wpisujesz swój numer zdającego (PESEL), bo tego sprawdza klucz R.2.5.
Typowe błędy
<b> pogrubia, ale nie jest znacznikiem semantycznym.
R.4.2 Ustawiony dla wszystkich bloków sekcji kolor tła Plum
R.4.4 Ustawiona wysokość bloku nagłówkowego i stopki 200px oraz wszystkich bloków sekcji 400px
R.4.6 Ustawione marginesy zewnętrzne (margin) dla wszystkich bloków sekcji 5px
R.4.7 Ustawiona dla bloków nagłówkowego, stopki, wszystkich sekcji cecha overflow: hidden;
R.4.8 Ustawiona dla wszystkich bloków sekcji cecha box-shadow: 4px 4px 10px DimGray;
ok. 2 min
Polecenie z arkusza
Dla bloków sekcji: kolor tła Plum, wysokość 400 px, marginesy zewnętrzne 5 px, cień bloku o przesunięciu 4 px w obu osiach, rozmyciu 10 px i kolorze DimGray, w przypadku przepełnienia bloku zawartość poza blokiem jest ukrywana.
Jedna reguła na cztery sekcje; cień bloku z tabeli 2: box-shadow: 4px 4px 10px DimGray (R.4.2, R.4.4, R.4.6, R.4.7, R.4.8).
R.3.5 Sekcje są umiejscowione obok siebie dla ekranu o szerokości powyżej 800 px; dla ekranu o szerokości mniejszej niż 800 px, sekcje są umiejscowione jedna pod drugą
ok. 2 min
Polecenie z arkusza
W przypadku ekranów o szerokości do 800 px szerokość bloków sekcji 100%, powyżej 800 px szerokość bloków sekcji 22%.
Podejście „mobile first”: najpierw układ dla wąskiego ekranu, bez żadnego @media. <body> staje się siatką z jedną kolumną 100% - każdy blok w osobnym wierszu, sekcje jedna pod drugą (prawa część ilustracji 2). Nagłówek i stopka dostają grid-column: 1 / -1: „od pierwszej do ostatniej linii siatki”, czyli zawsze całą szerokość, ile kolumn by nie było.
Dlaczego od wąskiego: telefon nie pobiera i nie przelicza reguł dla dużego ekranu, a szeroki układ to dodatek, który włącza się dopiero, gdy jest miejsce.
R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z ilustracją 3 w arkuszu egzaminacyjnym dla ekranów o szerokości powyżej 800 px (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)
R.3.5 Sekcje są umiejscowione obok siebie dla ekranu o szerokości powyżej 800 px; dla ekranu o szerokości mniejszej niż 800 px, sekcje są umiejscowione jedna pod drugą
R.4.9 W przypadku ekranów o szerokości do 800 px szerokość bloków sekcji 100%, powyżej 800 px szerokość bloków sekcji 22%, np. @media only screen and (max-width: 800px) { section { width:100%; } } (wystarczy jedna reguła @media, wtedy szerokość w sekcji wymagana, obie szerokości 100% i 22% muszą być zdefiniowane)
ok. 3 min
Polecenie z arkusza
W przypadku ekranów o szerokości do 800 px szerokość bloków sekcji 100%, powyżej 800 px szerokość bloków sekcji 22%.
„Powyżej 800 px” to @media (min-width: 801px). W środku zmienia się tylko jedna właściwość: siatka dostaje cztery kolumny po 22% (repeat(4, 22%)) i piątą 1fr na resztę szerokości. Sekcje wypełniają kolejno cztery kolumny, a nagłówek i stopka z 1 / -1 rozciągają się na wszystkie pięć - układ z ilustracji 3, bez float i bez clear (R.3.1, R.3.5).
Obie szerokości z arkusza - 100% i 22% - są zapisane w siatce (R.4.9). Margines 5 px sekcji mieści się w kolumnie, więc ramka sekcji jest o 10 px węższa niż kolumna.
Typowe błędy
Klucz R.4.9 podaje przykład z width w sekcji. Jeśli wolisz trzymać się dokładnie tego zapisu, zostaw section { width: 22%; } z @media jak w wersji podstawowej - w siatce width w procentach liczyłby się od kolumny, a nie od strony, więc obu sposobów naraz się nie łączy.
Kroki 31-45 · po tym etapie masz 42 z 42 kryteriów (pkt)
Krok 31: Dane połączenia w pliku config.php
Kryteria:
R.5.1 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy biblioteka
ok. 2 min
Polecenie z arkusza
Łączą się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie biblioteka.
Dane logowania do bazy w osobnym pliku config.php, który tylko zwraca tablicę (return [...]). W prawdziwym projekcie hasło do bazy jest inne na każdym serwerze i nie trafia do repozytorium razem z kodem strony - wystarczy podmienić jeden plik. Arkusz dopuszcza dodatkowe pliki („ewentualnie inne przygotowane pliki”).
DSN (mysql:host=localhost;dbname=biblioteka;charset=utf8mb4) to adres bazy dla PDO w jednym napisie: rodzaj serwera, host, nazwa bazy i kodowanie połączenia. Plik bez HTML-u nie ma końcowego ?> - nic po nim nie wypisze się przypadkiem.
Typowe błędy
Plik config.php musi trafić do folderu z rezultatami - bez niego strona się nie połączy, a egzaminator nie znajdzie nazwy bazy.
config.phpPHPnowy plik
<?php// Dane połączenia z bazą - w jednym miejscu, poza plikiem stronyreturn ['dsn' => 'mysql:host=localhost;dbname=biblioteka;charset=utf8mb4','user' => 'root','password' => '',];
R.5.1 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy biblioteka
R.5.10 W skryptach zastosowana przynajmniej jedna zmienna oraz nazwy wszystkich zmiennych są w języku polskim lub angielskim i odzwierciedlają cel zastosowania
ok. 3 min
Polecenie z arkusza
Napisane w języku PHP. Łączą się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie biblioteka.
Kod PHP stoi na samym początku pliku, przed <!DOCTYPE html>: najpierw wszystkie operacje na bazie, potem szablon strony, który tylko wypisuje gotowe dane. Klucz R.5 przyjmuje PDO tak samo jak MySQLi.
require 'config.php' - dane połączenia trafiają do $config,
new PDO(...) - połączenie i wybór bazy; opcja ERRMODE_EXCEPTION sprawia, że każdy błąd SQL zatrzymuje skrypt wyjątkiem, zamiast po cichu zwracać false, a FETCH_ASSOC - że wiersze przychodzą jako tablice z nazwami kolumn ($row['nazwa'] zamiast $row[1]),
try … catch (PDOException $error) - gdy baza nie działa, użytkownik widzi zwykły komunikat, a nie treść błędu z nazwą serwera i ścieżkami; http_response_code(500) mówi przeglądarce, że to błąd serwera.
Od tego kroku podgląd to prawdziwy wynik strony z serwera - na razie bez zmian.
Krok 33: Funkcja e(): bezpieczne wypisywanie tekstu
ok. 2 min
Polecenie z arkusza
Należy stosować znaczące nazewnictwo zmiennych i funkcji w języku polskim lub angielskim.
Każdy napis z bazy albo z formularza, który trafi do HTML-u, przejdzie przez e() (od escape). htmlspecialchars zamienia <, >, & i - dzięki ENT_QUOTES - oba cudzysłowy na encje, więc napis <script> zapisany kiedyś w bazie wyświetli się jako tekst, a nie wykona (ochrona przed XSS). Escapowanie robi się na wyjściu, tuż przed wypisaniem - w bazie zostają dane surowe.
Krótka nazwa to świadomy wyjątek od „znaczącego nazewnictwa”: e() występuje w szablonie wiele razy i jest powszechnie znaną konwencją (tak samo nazywa się w Laravelu). Typy string przy parametrze i wyniku mówią, czego funkcja oczekuje; liczby PHP zamieni na tekst sam.
R.2.6 W bloku nagłówkowym umieszczona przynajmniej jedna grafika obraz.png (w HTML lub skrypcie)
R.5.3 Skrypt 1 wyświetla 20 razy grafikę za pomocą pętli (w zależności od szerokości ekranu grafiki mogą być ukryte)
ok. 2 min
Polecenie z arkusza
Skrypt 1: wyświetla 20 razy grafikę obraz.png. Należy zastosować instrukcję iteracyjną (pętlę).
Pętla for w składni szablonu (for (...): … endfor;) - między nimi zwykły HTML obrazu, powtórzony 20 razy (licznik od 0 do 19, R.5.3). Wynik jest ten sam co echo w pętli, ale znacznik <img> zostaje HTML-em, a nie napisem w PHP.
Pusty alt="": dwadzieścia identycznych obrazów to dekoracja, nie treść. Pusty tekst alternatywny mówi czytnikowi ekranu „pomiń”; alt="książki" kazałby mu przeczytać słowo „książki” dwadzieścia razy. Arkusz nie podaje tekstu alternatywnego dla tej grafiki.
Typowe błędy
str_repeat('<img …>', 20) daje ten sam HTML, ale arkusz wymaga instrukcji iteracyjnej - funkcja nie jest pętlą w kodzie zdającego (R.5.3).
R.5.4 Przynajmniej jeden skrypt zawiera instrukcję wysyłającą do bazy danych zapytanie
ok. 3 min
Polecenie z arkusza
Skrypt 2 w sekcji „Liryka”: wysyła do bazy danych zapytanie 1.
Zapytanie 1 jako zapytanie przygotowane z parametrem :genre - za chwilę to samo zapytanie posłuży trzem gatunkom. execute(['genre' => 'liryka']) wysyła je z wartością (R.5.4).
PDO::FETCH_KEY_PAIR zamienia wynik z dwiema kolumnami w tablicę „klucz → wartość”: [1 => 'Pieśń nad pieśniami', 5 => 'Bogurodzica', …]. Id i tytuł są dokładnie tym, czego potrzebuje opcja listy (id w value, tytuł jako treść).
R.5.5 Skrypt 2 w przynajmniej jednej sekcji wyświetla tytuły książek zwrócone zapytaniem. Rodzaj utworów zgadza się z nazwą sekcji (liryka, epika, dramat) . Atrybut value każdej opcji to id zwrócone zapytaniem
ok. 2 min
Polecenie z arkusza
Ze zwróconych zapytaniem wierszy generuje kolejne opcje listy rozwijanej w ten sposób, że tytuł jest treścią opcji, a id jest przypisane do atrybutu value.
foreach ($poetryBooks as $id => $title) od razu rozdziela klucz i wartość: id trafia do value, tytuł (przez e()) jest treścią opcji (R.5.5). Formularz wyśle id, a użytkownik widzi tytuł. Id to liczba z bazy - escapowanie nie jest potrzebne. Lista Liryka ma pięć tytułów, jak na ilustracji 4.
Typowe błędy
Opcja bez value wyśle tytuł zamiast id - skrypt 3 nie znajdzie książki po id.
Krok 37: Skrypt 2 dla trzech gatunków: pętla zamiast kopii
Kryteria: ,
R.5.5 Skrypt 2 w przynajmniej jednej sekcji wyświetla tytuły książek zwrócone zapytaniem. Rodzaj utworów zgadza się z nazwą sekcji (liryka, epika, dramat) . Atrybut value każdej opcji to id zwrócone zapytaniem
R.5.8 Skrypty 2 i 3 działają we wszystkich trzech sekcjach zgadza się gatunek "Liryka", "Epika", "Dramat" z wyświetlanymi w danej liście tytułami oraz tekst pojawia się tylko pod listą w której był dokonany wybór
ok. 3 min
Polecenie z arkusza
Skrypt 2 do sekcji „Epika” należy powielić i dostosować: działa on na zmodyfikowanym zapytaniu 1, tak że wypełnia listę rozwijaną książkami z gatunku „epika”. Skrypt 2 do sekcji „Dramat” należy powielić i dostosować: wypełnia listę rozwijaną książkami z gatunku „dramat”.
Zamiast trzech kopii skryptu - tablica sekcji na początku pliku: $sections = ['poetry' => 'liryka', 'epic' => 'epika', 'drama' => 'dramat'], czyli nazwa listy w formularzu → gatunek w bazie. Pętla po niej wykonuje to samo przygotowane zapytanie trzy razy, za każdym razem z innym gatunkiem: zapytanie przygotowuje się raz, a wykonuje wiele razy. Wyniki lądują w $books['liryka'], $books['epika'], $books['dramat'].
W HTML-u każda lista bierze swoją część tablicy. Zmiana zapytania to teraz jedno miejsce zamiast trzech; klucz R.5.8 sprawdza, czy w każdej sekcji są książki jej gatunku (8 epiki, 7 dramatów) - i są.
Typowe błędy
Gdyby arkusz sprawdzał trzy kopie kodu, a nie działanie, pętla byłaby ryzykiem - tu klucz ocenia wynik w trzech sekcjach (R.5.5, R.5.8).
Krok 38: Skrypt 3: tylko gdy wybrano książkę z listy liryki
ok. 3 min
Polecenie z arkusza
Skrypt 3 (ilustracja 5), jeżeli dokonano wyboru w liście rozwijanej liryki wykonuje czynności: wysyła zapytanie 5, wyświetla komunikat w paragrafie i wysyła zapytanie 4.
Dwa filtry na wejściu:
filter_input(INPUT_POST, 'poetry', FILTER_VALIDATE_INT) - liczba całkowita z listy poetry, false dla czegoś innego, null, gdy wysłano inny formularz albo żaden,
biała lista: isset($books['liryka'][$bookId]) - id musi być jednym z id liryki wczytanych przed chwilą z bazy. Formularz wysłany ręcznie z id dramatu albo z id 999 nie przejdzie.
Dopiero po obu warunkach skrypt cokolwiek robi. Podgląd od tego kroku: stan po wybraniu „Na dom w Czarnolesie” i kliknięciu „Rezerwuj” (ilustracja 5).
R.5.4 Przynajmniej jeden skrypt zawiera instrukcję wysyłającą do bazy danych zapytanie
ok. 2 min
Polecenie z arkusza
Wysyła do bazy danych zapytanie 5, zmodyfikowane w ten sposób, że w klauzuli WHERE podana jest wartość id wysłana przez formularz.
Zapytanie 5 przygotowane z parametrem :id (R.5.4). fetchColumn() zwraca od razu wartość pierwszej kolumny pierwszego wiersza - tytuł - bez tablicy i bez pętli.
Krok 40: Skrypt 3: komunikat o rezerwacji po przekierowaniu
Kryteria:
R.5.6 Skrypt 3 wyświetla zwrócony zapytaniem tytuł. Tytuł jest równy wybranemu z listy rozwijanej. Jest wyświetlany w postaci: "Książka <tytuł> została zarezerwowana"
ok. 4 min
Polecenie z arkusza
Wyświetla w paragrafie tekst Książka <tytuł> została zarezerwowana, gdzie pole <> zawiera wartość wybraną z bazy.
Rezerwacja zapisuje dane, więc po niej strona stosuje wzorzec PRG (Post/Redirect/Get):
sekcja i tytuł z zapytania 5 trafiają do sesji ($_SESSION['reserved'], sesję otwiera session_start()),
po wszystkich operacjach na bazie header('Location: biblioteka.php', true, 303) każe przeglądarce otworzyć stronę od nowa GET-em,
przy tym wejściu $reserved odbiera dane z sesji i od razu je usuwa - komunikat pokaże się raz. Odbiór stoi przed obsługą formularza, bo w żądaniu POST sesja jest jeszcze pusta.
Bez przekierowania odświeżenie strony (F5) wysłałoby rezerwację ponownie. Paragraf składa funkcja reservationMessage($reserved, 'poetry'): zwraca komunikat tylko wtedy, gdy rezerwacja dotyczyła tej sekcji - stąd ilustracja 5 ma paragraf wyłącznie w Liryce (R.5.6). Tytuł przechodzi przez e().
Typowe błędy
header() po wypisaniu czegokolwiek (nawet pustej linii przed <?php) nie zadziała - „headers already sent”.
R.5.4 Przynajmniej jeden skrypt zawiera instrukcję wysyłającą do bazy danych zapytanie
R.5.7 Skrypt 3 dla wybranej z listy książki uaktualnia jej rekord w bazie danych, wpisując do pola rezerwacja wartość 1. Należy sprawdzić w phpMyAdmin
ok. 2 min
Polecenie z arkusza
Wysyła do bazy danych zmodyfikowane zapytanie 4, w ten sposób że w klauzuli WHERE podana jest wartość id wysłana przez formularz.
Zapytanie 4 przygotowane z tym samym :id - w bazie rezerwacja wybranej książki zmienia się na 1 (R.5.7, sprawdzane w phpMyAdmin). Kolumnę dodało zapytanie 3.
Krok 42: Skrypt 3 dla trzech sekcji: pętla po tablicy sekcji
Kryteria: , ,
R.5.6 Skrypt 3 wyświetla zwrócony zapytaniem tytuł. Tytuł jest równy wybranemu z listy rozwijanej. Jest wyświetlany w postaci: "Książka <tytuł> została zarezerwowana"
R.5.7 Skrypt 3 dla wybranej z listy książki uaktualnia jej rekord w bazie danych, wpisując do pola rezerwacja wartość 1. Należy sprawdzić w phpMyAdmin
R.5.8 Skrypty 2 i 3 działają we wszystkich trzech sekcjach zgadza się gatunek "Liryka", "Epika", "Dramat" z wyświetlanymi w danej liście tytułami oraz tekst pojawia się tylko pod listą w której był dokonany wybór
ok. 3 min
Polecenie z arkusza
Skrypt 3 do sekcji „Epika” należy powielić i dostosować: pobiera on dane wysłane formularzem z sekcji „Epika”. Skrypt 3 do sekcji „Dramat” należy powielić i dostosować: pobiera on dane wysłane formularzem z sekcji „Dramat”. Należy zwrócić uwagę na to, że wyświetlany w wyniku wykonania formularza paragraf powinien pojawić się tylko w odpowiedniej sekcji.
Ta sama tablica $sections co w skrypcie 2: pętla foreach ($sections as $field => $genre) sprawdza po kolei listy poetry, epic i drama. Wysłany jest zawsze tylko jeden formularz, więc dane znajdzie co najwyżej jeden obieg - i to jego sekcja ($field) trafia do sesji. Biała lista sprawdza id w książkach tego gatunku.
W HTML-u pod formularzem Epiki i Dramatu stoi to samo wywołanie z inną nazwą sekcji. Komunikat pojawia się tylko tam, gdzie kliknięto „Rezerwuj” (R.5.8).
R.5.4 Przynajmniej jeden skrypt zawiera instrukcję wysyłającą do bazy danych zapytanie
ok. 2 min
Polecenie z arkusza
Skrypt 4: wysyła do bazy danych zapytanie 2.
Zapytanie 2 bez danych od użytkownika - query i fetchAll() do $overdue (R.5.4). Długie zapytanie można złamać na dwie linie w napisie; SQL nie zwraca uwagi na znaki nowej linii.
Krok 44: Skrypt 4: element listy z trzema wartościami
Kryteria: ,
R.2.9 W czwartym bloku sekcji umieszczona jedna lista punktowana z elementami (w HTML lub skrypcie) , zastosowane znaczniki <ul> i <li>
R.5.9 Skrypt 4 wyświetla w sekcji czwartej zwrócone zapytaniem kolejne rekordy w elementach listy: dane tytuł, id czytelnika i datę (nie wszystkie muszą być widoczne ze względu na właściwość overflow)
ok. 2 min
Polecenie z arkusza
Każdy zwrócony zapytaniem wiersz wyświetla w elemencie listy kolejno: tytuł, id czytelnika i datę oddania oddzielone od siebie spacją.
Jeden <li> na wypożyczenie, wartości po nazwach kolumn rozdzielone spacjami (R.2.9, R.5.9). Dolne pozycje mogą być obcięte przez overflow: hidden - klucz to dopuszcza.
R.5.2 Ostatnią operacją na bazie jest jej zamknięcie
ok. 1 min
Polecenie z arkusza
Na końcu zamykane jest połączenie z serwerem.
$db = null; na końcu bloku try zamyka połączenie PDO (R.5.2) - po wszystkich zapytaniach i przed przekierowaniem. Szablon korzysta już tylko z gotowych tablic.
Krok 46 · po tym etapie masz 42 z 42 kryteriów (pkt)
Krok 46: Co jest inaczej i którą wersję wybrać na egzaminie
ok. 2 min
Obie wersje dają komplet 42 punktów. Różnice:
wersja podstawowa
wersja nowoczesna
układ
float + clear w @media (min-width: 801px)
siatka na <body>, „mobile first”
skrypty 2 i 3
trzy kopie, na końcu funkcje
tablica $sections i pętla od początku
baza
mysqli_*, id sklejone z SQL
PDO, zapytania przygotowane, config.php
dane z formularza
isset($_POST[...])
filter_input i biała lista id z bazy
po rezerwacji
komunikat od razu
PRG: przekierowanie, komunikat z sesji
grafiki w nagłówku
echo w pętli, alt z tekstem
szablon for … endfor, alt="" dla dekoracji
Na egzaminie: arkusz wprost każe skopiować skrypty - kopiowanie jest szybkie i trudno się przy nim pomylić, a klucz ocenia działanie, więc obie drogi są bezpieczne. Pętla po tablicy opłaca się, gdy sekcji jest więcej albo zapytanie trzeba poprawić. Formularz z prawdziwą rezerwacją dostałby jeszcze token CSRF (ochrona przed wysłaniem formularza z cudzej strony) - klucz tego nie wymaga.
Krok 47 · po tym etapie masz 42 z 42 kryteriów (pkt)
Krok 47: 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: biblioteka.php, config.php, import.png, kw1.jpg … kw5.jpg, kwerendy.txt, obraz.png, przeglądarka.txt, styl.css. 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.