Serwis używa plików cookies do analizy ruchu i poprawy działania. Szczegóły
E.14-01-22.01-SGStyczeń 2022 · 39 kroków · ok. 73 min · 34 kryteria z klucza
Rozwiązanie krok po kroku: Grzybobranie - baza grzybów (Wersja podstawowa)
Grzybobranie: baza grzybobranie z tabelami grzyby, rodzina i potrawy, cztery zapytania (grzyby jadalne, grzyby do zup z nazwą rodziny, pliki zdjęć i nowa kolumna opis), miniatura zdjęcia podgrzybka, strona grzyby.php z pięcioma blokami, style w styl.css oraz trzy skrypty PHP: lista grzybów jadalnych w akapitach, grzyby do zup na liście punktowanej i galeria zdjęć z nazwą grzyba w dymku.
To arkusz E.14 z sesji styczeń 2022 - kwalifikacji formuły 2012, którą zastąpiła INF.03. Kroki idą zdanie po zdaniu za treścią arkusza - przy każdym jest polecenie z arkusza, a kod pokazuje dokładnie te linie, które je spełniają. Skrypty są najpierw napisane najprościej, funkcjami mysqli z tabeli 1 arkusza, a na końcu sprawdzone na prawdziwym ataku i zabezpieczone.
To jest wersja podstawowa. W zakładce „Nowoczesny kod (flex, PDO)” jest drugi sposób rozwiązania tego samego arkusza: bloki ustawione flexboxem, zmienne CSS i PDO z jednym połączeniem na górze pliku - obie wersje są zgodne z kluczem.
Skąd materiały. Arkusz wymienia archiwum pliki1.zip z plikiem dane.sql; to archiwum nie zostało opublikowane. Tym samym hasłem @grzYbY otwiera się oryginalne archiwum CKE z sesji styczeń 2019 (zadanie E.14-05-19.01, załącznik na stronie arkusza): są w nim wszystkie zdjęcia wymienione w arkuszu i plik dane2.sql z tabelami grzyby, rodzina i potrawy zgodnymi z obrazem 1 i z wynikami w kluczu. Na egzaminie importuje się dane.sql z archiwum na pulpicie - w tym rozwiązaniu jego rolę gra dane2.sql. Zapytania i zrzuty są policzone na MariaDB 10.4, a podgląd od kroków z PHP to wynik prawdziwego uruchomienia kodu w PHP 8.2.
Jak liczą się punkty. Klucz ma 34 kryteria (R.1.1 - R.5.8), każde za 1 punkt. Próg zdania części praktycznej to 75%, czyli 26 punktów - można stracić 8 kryteriów i nadal zdać. 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 34 kryteriów (pkt)
Krok 1: Folder zdającego i archiwum z materiałami
ok. 2 min
Polecenie z arkusza
Aby wykonać zadanie, zaloguj się na konto Egzamin bez hasła. Na pulpicie znajdziesz archiwum ZIP o nazwie pliki1.zip zabezpieczone hasłem: @grzYbY Archiwum należy rozpakować. Na pulpicie konta Egzamin utwórz folder. Jako nazwy folderu użyj numeru, którym został podpisany arkusz. Rozpakowane pliki umieść w tym folderze. Po skończonej pracy wyniki zapisz w tym folderze.
Na pulpicie tworzysz folder nazwany numerem PESEL - w tym rozwiązaniu w jego miejscu stoi imię i nazwisko autora, Bartosz Bryniarski. Archiwum rozpakowujesz 7-Zipem do tego folderu, podając hasło @grzYbY (wielkość liter ma znaczenie). W środku są zdjęcia ośmiu grzybów i plik SQL z tabelami.
Archiwum z sesji 2022 nie zostało opublikowane; na stronie arkusza jest archiwum pliki5.zip z sesji styczeń 2019, które otwiera się tym samym hasłem i ma te same zdjęcia oraz tabele (plik nazywa się tam dane2.sql).
Typowe błędy
Pliki zapisane poza folderem zdającego (np. tylko w htdocs) nie trafią na płytę - a ocenia się wyłącznie to, co jest na płycie.
Krok 2: XAMPP i pusta baza grzybobranie
ok. 2 min
Polecenie z arkusza
Za pomocą narzędzia phpMyAdmin wykonaj podane operacje na bazie danych: Utwórz nową bazę danych o nazwie grzybobranie
W XAMPP Control Panel klikasz Start przy Apache i MySQL, potem http://localhost/phpmyadmin. Nowa w lewym panelu, nazwa grzybobranie, porównywanie np. utf8mb4_polish_ci, Utwórz.
Typowe błędy
Nazwa bazy musi brzmieć dokładnie grzybobranie - import do innej bazy nie daje R.1.1, a skrypt połączy się z bazą o tej nazwie (R.5.2).
Krok 3: Import tabel i zrzut import.png
Kryteria:
R.1.1 Wykonano import tabel do bazy danych grzybobranie , czynność udokumentowano plikiem o nazwie import w formacie PNG
ok. 3 min
Polecenie z arkusza
Do bazy grzybobranie zaimportuj tabele z pliku dane.sql z rozpakowanego archiwum Wykonaj zrzut ekranu po imporcie. Zrzut zapisz w formacie PNG i nazwij import. Nie kadruj zrzutu. Powinien on obejmować cały ekran, z widocznym paskiem zadań. Na zrzucie powinny być widoczne elementy wskazujące na poprawnie wykonany import tabel
Zaznacz bazę grzybobranie, karta Import, Wybierz plik → plik SQL z archiwum (na egzaminie dane.sql, w archiwum z 2019 dane2.sql), Wykonaj. Plik z 2019 tworzy 12 tabel - poza trzema z obrazu 1 są tam tabele z innych zadań; zostawiasz je, zadanie ich nie dotyczy.
Struktura z obrazu 1: tabela grzyby ma pola id, rodzina_id, potrawy_id, nazwa, potoczna, jadalny, miesiac_zbierania i nazwa_pliku. Pola rodzina_id i potrawy_id to klucze obce do tabel rodzina (1 pieczarkowate … 5 boczniakowate) i potrawy (1 sos … 4 zupa, 5 niejadalne). Pole jadalny ma wartość 1 dla grzyba jadalnego i 0 dla niejadalnego.
Po imporcie widać komunikat o sukcesie i listę tabel w lewym panelu. Zrzut całego ekranu z paskiem zadań zapisujesz jako import.png.
Typowe błędy
Wykadrowany zrzut albo zrzut bez paska zadań nie jest oceniany (uwaga w kluczu przy R.1).
Podgląd strony po tym kroku
Krok 4: Zapytanie 1: grzyby jadalne
Kryteria: ,
R.1.2 Zapisano plik kwerendy.txt z zapytaniami SQL zawierający co najmniej jedno zapytanie wynikające z treści zadania
R.1.3 Utworzono zapytanie 1 wybierające jedynie pola id, nazwa oraz potoczna z tabeli grzyby jedynie dla grzybów jadalnych. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT id, nazwa, potoczna FROM grzyby WHERE jadalny = 1; oraz wynik działania udokumentowano zrzutem, na którym widocznych jest dokładnie 7 rekordów o id: 1, 2, 3, 4, 6, 7, 8; jedynie pola id, nazwa, potoczna
ok. 3 min
Polecenie z arkusza
Zapisz i wykonaj zapytania SQL działające na bazie danych. Zapytania zapisz w pliku kwerendy.txt. Wykonaj zrzuty ekranu przedstawiające wyniki działania zapytań. Zrzuty zapisz w formacie PNG i nadaj im nazwy kw1, kw2, kw3, kw4. Zrzuty powinny obejmować cały ekran monitora z widocznym paskiem zadań Zapytanie 1: wybierające jedynie pola id, nazwa oraz potoczna z tabeli grzyby jedynie dla grzybów jadalnych
WHERE jadalny = 1 - grzyb jadalny ma w tym polu 1 (opis pod obrazem 1).
Wynik: 7 wierszy o id 1, 2, 3, 4, 6, 7, 8 - brakuje tylko szatana (id 5). Zapytanie kopiujesz do pliku kwerendy.txt, a cały ekran z wynikiem zapisujesz jako kw1.png - w tym arkuszu zrzuty zapytań też są w PNG. To zapytanie wyśle skrypt 1.
kwerendy.txtTekstnowy plik
-- Zapytanie 1SELECT id, nazwa, potoczna FROM grzyby WHERE jadalny = 1;
Podgląd strony po tym kroku
Krok 5: Zapytanie 2: grzyby do zup z nazwą rodziny
Kryteria:
R.1.4 Utworzono zapytanie 2 wybierające jedynie pole potoczna z tabeli grzyby oraz odpowiadające mu pole nazwa z tabeli rodzina jedynie dla grzybów, do których przypisana jest potrawa zupa. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT potoczna, rodzina.nazwa FROM grzyby JOIN rodzina ON rodzina_id = rodzina.id WHERE potrawy_id = 4; (lub INNER JOIN lub porównanie kluczy po WHERE) oraz wynik działania udokumentowano zrzutem, na którym widoczne są dokładnie 2 rekordy z danymi: prawdziwek, borowikowate, podgrzybek, borowikowate
ok. 3 min
Polecenie z arkusza
Zapytanie 2: wybierające jedynie pole potoczna z tabeli grzyby oraz odpowiadające mu pole nazwa z tabeli rodzina jedynie dla grzybów, do których przypisana jest potrawa zupa
SELECT potoczna, rodzina.nazwa - pole nazwa jest w obu tabelach, więc trzeba wskazać, z której: rodzina.nazwa,
FROM grzyby JOIN rodzina ON grzyby.rodzina_id = rodzina.id - relacja: klucz obcy grzyba równa się kluczowi głównemu rodziny,
WHERE potrawy_id = 4 - zupa ma w tabeli potrawy identyfikator 4 (obraz 1).
Wynik: 2 wiersze - prawdziwek, borowikowate i podgrzybek, borowikowate. Zrzut: kw2.png. To zapytanie wyśle skrypt 2.
Typowe błędy
WHERE potrawy = 'zupa' nie zadziała - tabela grzyby ma tylko numer potrawy. Bez rodzina. przed nazwa baza zgłosi błąd #1052 (kolumna niejednoznaczna).
kwerendy.txtTekstzmienione: 3 linie
-- Zapytanie 1SELECT id, nazwa, potoczna FROM grzyby WHERE jadalny = 1;-- Zapytanie 2SELECT potoczna, rodzina.nazwa FROM grzyby JOIN rodzina ON grzyby.rodzina_id = rodzina.id WHERE potrawy_id = 4;
Podgląd strony po tym kroku
Krok 6: Zapytanie 3: pliki zdjęć i nazwy grzybów
Kryteria:
R.1.5 Utworzono zapytanie 3 wybierające jedynie pola nazwa_pliku oraz nazwa z tabeli grzyby . W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa_pliku, nazwa FROM grzyby; oraz wynik działania udokumentowano zrzutem, na którym widoczne jest 8 rekordów, jedynie pola nazwa_pliku i nazwa
ok. 2 min
Polecenie z arkusza
Zapytanie 3: wybierające jedynie pola nazwa_pliku oraz nazwa z tabeli grzyby
Dwie kolumny, bez warunku - 8 wierszy, od pieczarka.jpg, pieczarka dwuzarodnikowa do trufla.jpg, trufla letnia. Zrzut: kw3.png. Skrypt 3 zrobi z każdego wiersza zdjęcie: nazwa pliku będzie źródłem, nazwa - dymkiem.
kwerendy.txtTekstzmienione: 3 linie
-- Zapytanie 1SELECT id, nazwa, potoczna FROM grzyby WHERE jadalny = 1;-- Zapytanie 2SELECT potoczna, rodzina.nazwa FROM grzyby JOIN rodzina ON grzyby.rodzina_id = rodzina.id WHERE potrawy_id = 4;-- Zapytanie 3SELECT nazwa_pliku, nazwa FROM grzyby;
Podgląd strony po tym kroku
Krok 7: Zapytanie 4: kolumna opis w tabeli rodzina
Kryteria:
R.1.6 Utworzono zapytanie 4 dodające do tabeli rodzina pole opis typu napisowego. W pliku z kwerendami lub na zrzucie istnieje zapis np.: ALTER TABLE rodzina ADD opis TEXT; (lub ADD COLUMN, dowolne typy napisowe) oraz wynik działania udokumentowano zrzutem, na którym widoczny jest komunikat o poprawnie dodanej kolumnie lub struktura tabeli z dodaną kolumną
ok. 2 min
Polecenie z arkusza
Zapytanie 4: dodające do tabeli rodzina pole opis typu napisowego
ALTER TABLE zmienia strukturę istniejącej tabeli, a ADD opis TEXT dodaje kolumnę opis typu tekstowego (klucz przyjmuje też ADD COLUMN i inne typy napisowe, np. VARCHAR(200)). Istniejące wiersze dostają w niej NULL.
phpMyAdmin odpowiada „MySQL zwrócił pusty wynik” - zmiana struktury niczego nie wybiera. Na zrzucie kw4.png warto pokazać kartę Struktura tabeli rodzina z nową kolumną - klucz przyjmuje komunikat albo strukturę.
Typowe błędy
Drugie wykonanie tego samego ALTER TABLE kończy się błędem #1060 „Duplicate column name” - kolumna już istnieje.
kwerendy.txtTekstzmienione: 3 linie
-- Zapytanie 1SELECT id, nazwa, potoczna FROM grzyby WHERE jadalny = 1;-- Zapytanie 2SELECT potoczna, rodzina.nazwa FROM grzyby JOIN rodzina ON grzyby.rodzina_id = rodzina.id WHERE potrawy_id = 4;-- Zapytanie 3SELECT nazwa_pliku, nazwa FROM grzyby;-- Zapytanie 4ALTER TABLE rodzina ADD opis TEXT;
Podgląd strony po tym kroku
Etap 2: Grafika i strona HTML
Kroki 8-17 · po tym etapie masz 15 z 34 kryteriów (pkt)
Krok 8: Kopia zdjęcia: podgrzybek-miniatura.jpg
ok. 1 min
Polecenie z arkusza
Obraz podgrzybek.jpg należy skopiować na nowy i nazwać podgrzybek-miniatura.jpg (powinny być dwa pliki: podgrzybek.jpg i podgrzybek-miniatura.jpg).
W folderze zdającego kopiujesz podgrzybek.jpg (Ctrl+C, Ctrl+V) i zmieniasz nazwę kopii na podgrzybek-miniatura.jpg. Oryginał zostaje bez zmian - do niego poprowadzi odnośnik z miniatury.
Krok 9: Miniatura: wysokość 80 px z zachowaniem proporcji
Kryteria:
R.2.7 Obraz podgrzybek-miniatura.jpg przeskalowano z zachowaniem proporcji do wysokości 80 px, odpowiadająca temu szerokość wynosi 107 px (± 5 px)
ok. 2 min
Polecenie z arkusza
Obraz podgrzybek-miniatura.jpg należy przeskalować z zachowaniem proporcji do wysokości 80 px
Skalowanie oryginału zamiast kopii - zostanie jeden mały plik, a odnośnik z miniatury otworzy tę samą miniaturę.
Podgląd strony po tym kroku
Krok 10: Plik grzyby.php i kodowanie polskich znaków
Kryteria:
R.2.1 Witrynę internetową zapisano w pliku grzyby.php oraz zastosowano właściwy standard kodowania polskich znaków
ok. 2 min
Polecenie z arkusza
Składa się ze strony o nazwie grzyby.php Zastosowany właściwy standard kodowania polskich znaków
Plik grzyby.php w folderze zdającego. Rozszerzenie .php, bo dojdą skrypty. <!DOCTYPE html> - HTML5, lang="pl" - treść po polsku, a <meta charset="UTF-8"> jako pierwszy element <head> mówi przeglądarce, jak odczytać polskie litery; plik trzeba też zapisać w UTF-8 (R.2.1). Stronę sprawdzasz przez http://localhost/…/grzyby.php.
R.3.3 Strona zawiera działające połączenie z zewnętrznym arkuszem stylów o nazwie styl.css , formatowanie pochodzi jedynie z tego arkusza Uwaga: kryterium to nie jest spełnione również, gdy źle przypisano co najmniej jeden selektor do bloku (np. kropka zamiast #)
ok. 1 min
Polecenie z arkusza
Arkusz stylów w pliku o nazwie styl.css prawidłowo połączony z kodem strony
<link rel="stylesheet" href="styl.css"> w <head>. Całe formatowanie ma pochodzić z tego pliku - bez atrybutów style w znacznikach (R.3.3).
Typowe błędy
Klucz odbiera R.3.3 także wtedy, gdy choć jeden selektor nie trafia w blok (np. kropka zamiast # przy identyfikatorze).
Krok 13: Bloki: tytułowy, miniatury, lewy, prawy i stopka
Kryteria:
R.2.3 Wygląd strony zdefiniowano dzieląc ją na bloki: tytułowy, miniatury, lewy, prawy, stopka. Zastosowano znaczniki sekcji
ok. 2 min
Polecenie z arkusza
Podział strony na bloki: na górze: tytułowy i miniatury, poniżej: lewy i prawy, poniżej stopka. Podział zrealizowany za pomocą znaczników sekcji tak, aby układ bloków strony po uruchomieniu w przeglądarce, był zgodny z Obrazem 2
Pięć bloków w kolejności z obrazu 2: cztery <section> z identyfikatorami title, thumbnail, left i right (nazwy po angielsku - arkusz ich nie narzuca) oraz <footer> (R.2.3). Na razie stoją jeden pod drugim; w pary ustawi je CSS.
Typowe błędy
Bloki z samych <div> nie spełniają R.2.3 - klucz wymaga znaczników sekcji.
Krok 15: Blok miniatury: miniatura będąca odnośnikiem
Kryteria: ,
R.2.6 W bloku miniatury umieszczono obraz podgrzybek-miniatura.jpg , z tekstem alternatywnym "Grzybobranie"
R.3.2 Miniatura jest odnośnikiem, którego kliknięcie pobiera / otwiera plik podgrzybek.jpg
ok. 2 min
Polecenie z arkusza
Zawartość bloku miniatury: obraz podgrzybek-miniatura.jpg z tekstem alternatywnym „Grzybobranie”, jest on odnośnikiem, który prowadzi do obrazu podgrzybek.jpg
Obraz z src i alt (R.2.6) stoi wewnątrz znacznika <a>: całe zdjęcie staje się odnośnikiem. href="podgrzybek.jpg" prowadzi do pełnego zdjęcia - przeglądarka otworzy je w tej samej karcie (R.3.2). To typowy wzorzec galerii: mała miniatura na stronie, duży plik po kliknięciu.
Krok 16: Blok lewy: dwa nagłówki i lista punktowana
Kryteria:
R.2.5 W bloku lewym umieszczono listę punktowaną, zastosowano znaczniki <ul>, <li> oraz ich zamknięcia
ok. 2 min
Polecenie z arkusza
Zawartość bloku lewego: Nagłówek trzeciego stopnia o treści „Grzyby jadalne” efekt działania skryptu 1 Nagłówek trzeciego stopnia o treści „Polecamy do zup” lista punktowana (nieuporządkowana), a w niej efekt działania skryptu 2
Trzeciego stopnia to <h3>. Lista punktowana (nieuporządkowana) to <ul> (R.2.5) - na razie pusta: jej elementy <li> wypisze skrypt 2. Między nagłówkami zostaje miejsce na skrypt 1.
Typowe błędy
<ol> to lista numerowana - arkusz wymaga punktowanej.
R.2.4 W bloku tytułowym zapisano h1: "Czas na grzyby!" oraz w bloku lewym zapisano dwa h3: "Grzyby jadalne" i "Polecamy do zup" oraz w stopce zapisano akapit z numerem zdającego
ok. 1 min
Polecenie z arkusza
Zawartość stopki: akapit (paragraf) o treści „Autor: ”, dalej wstawiony numer zdającego
Akapit <p> w stopce. Na egzaminie po dwukropku wpisujesz swój numer PESEL; w tym rozwiązaniu stoi tam imię i nazwisko autora, Bartosz Bryniarski. Nagłówki <h1>, dwa <h3> i akapit w stopce dają R.2.4.
Kroki 18-26 · po tym etapie masz 26 z 34 kryteriów (pkt)
Krok 18: Plik styl.css: krój Helvetica dla całej strony
Kryteria:
R.4.1 Ustawiono domyślny krój czcionki dla całej strony na Helvetica (dla * lub html, lub body, lub kontenera całej strony)
ok. 1 min
Polecenie z arkusza
Domyślne dla całej strony: krój czcionki Helvetica
Nowy plik styl.css. Selektor uniwersalny * obejmuje każdy element strony (R.4.1). Windows nie ma kroju Helvetica - przeglądarka podstawi zastępczy krój bezszeryfowy, zwykle Arial, i tak też wygląda obraz 2.
R.4.10 W momencie, gdy kursor znajdzie się na obrazie, jego kolor obramowania zmienia się na Maroon
ok. 1 min
Polecenie z arkusza
W momencie, gdy kursor znajdzie się na obrazie, jego kolor obramowania zmienia się na Maroon
Pseudoklasa :hover działa, gdy kursor jest nad elementem. Zmieniasz tylko kolor ramki - border-color - grubość i styl zostają z reguły img (R.4.10). Na obrazie 2 tak wygląda drugie zdjęcie.
R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z Obrazem 2 w arkuszu egzaminacyjnym
ok. 2 min
Polecenie z arkusza
Podział strony na bloki: na górze: tytułowy i miniatury, poniżej: lewy i prawy, poniżej stopka. Podział zrealizowany za pomocą znaczników sekcji tak, aby układ bloków strony po uruchomieniu w przeglądarce, był zgodny z Obrazem 2
Wszystkie cztery sekcje dostają float: left. Opływanie układa je kolejno od lewej: tytułowy (80%) i miniatury (20%) wypełniają pierwszy rząd, więc lewy (25%) przechodzi do drugiego, a prawy (75%) staje obok niego. Stopka z clear: both zaczyna się pod blokami (R.3.1).
Typowe błędy
Bez clear: both stopka wsunie się pod pływające bloki i jej zielony pasek zniknie u góry strony.
Kroki 27-35 · po tym etapie masz 34 z 34 kryteriów (pkt)
Krok 27: Miejsce skryptu 1 i połączenie z bazą
Kryteria: ,
R.5.1 Skrypt napisano w języku PHP i zawiera przynajmniej jedną zgodną ze składnią instrukcję
R.5.2 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy grzybobranie
ok. 3 min
Polecenie z arkusza
Napisane w języku PHP Skrypt łączy się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie grzybobranie
Skrypt 1 ma się wypisać pod nagłówkiem „Grzyby jadalne”, więc tam zaczynasz blok PHP, między <?php a ?> (R.5.1). Pierwsza instrukcja: mysqli_connect z tabeli 1 - kolejno serwer localhost, użytkownik root, puste hasło '' i baza grzybobranie (R.5.2). Połączenie trafia do zmiennej $connection; skrypty 2 i 3 skorzystają z tej samej zmiennej, bo PHP wykonuje bloki jednego pliku po kolei.
Od tego kroku podgląd to wynik prawdziwego uruchomienia strony w PHP 8.2 - na razie wygląda jak wcześniej. Stronę sprawdzasz przez http://localhost/…, a nie otwierając plik z dysku.
R.5.5 Skrypt 1 wyświetla wszystkie pobrane z bazy danych wiersze w osobnych akapitach w formacie: <id>. <nazwa> (<potoczna>), gdzie pola zapisane w <> są pobrane z bazy danych
ok. 3 min
Polecenie z arkusza
Kolejne wiersze są wyświetlane w osobnych akapitach w formacie: <id>. <nazwa> (<potoczna>), gdzie pola zapisane w <> są pobrane z bazy danych
mysqli_fetch_row($result) zwraca kolejny wiersz jako tablicę numerowaną od zera: $row[0] to id, $row[1] - nazwa, $row[2] - potoczna (kolejność z SELECT). Gdy wierszy zabraknie, zwraca false i pętla while się kończy,
echo "<p>$row[0]. $row[1] ($row[2])</p>"; - każdy wiersz w osobnym akapicie: numer, kropka, nazwa, a nazwa potoczna w nawiasie (R.5.5). W cudzysłowie "…" PHP podstawia wartości zmiennych.
Podgląd: siedem akapitów, od 1. pieczarka dwuzarodnikowa (pieczarka) do 8. trufla letnia (trufla).
Blok PHP skryptu 2 stoi wewnątrz<ul> - to, co wypisze, znajdzie się na liście. Zapytanie 2 z kwerendy.txt bez zmian, przez to samo połączenie $connection.
grzyby.phpPHPzmienione: 3 linie
<!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Grzybobranie</title> <linkrel="stylesheet"href="styl.css"></head><body> <sectionid="title"> <h1>Czas na grzyby!</h1> </section> <sectionid="thumbnail"> <ahref="podgrzybek.jpg"><imgsrc="podgrzybek-miniatura.jpg"alt="Grzybobranie"></a> </section> <sectionid="left"> <h3>Grzyby jadalne</h3><?php$connection = mysqli_connect('localhost', 'root', '', 'grzybobranie');$result = mysqli_query($connection, 'SELECT id, nazwa, potoczna FROM grzyby WHERE jadalny = 1;');while ($row = mysqli_fetch_row($result)) {echo"<p>$row[0]. $row[1] ($row[2])</p>"; }?> <h3>Polecamy do zup</h3> <ul><?php$result = mysqli_query($connection, 'SELECT potoczna, rodzina.nazwa FROM grzyby JOIN rodzina ON grzyby.rodzina_id = rodzina.id WHERE potrawy_id = 4;');?> </ul> </section> <sectionid="right"> </section> <footer> <p>Autor: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (grzyby.php)
Krok 31: Skrypt 2: każdy wiersz jako element listy
Kryteria:
R.5.6 Skrypt 2 wyświetla każdy wiersz jako element listy, w formacie: <potoczna>, rodzina: <nazwa rodziny>, gdzie pola zapisane w <> są pobrane z bazy danych
ok. 2 min
Polecenie z arkusza
wyświetla każdy wiersz jako elementy listy, w formacie: <potoczna>, rodzina: <nazwa rodziny>, gdzie pola zapisane w <> są pobrane z bazy danych
Ta sama pętla co w skrypcie 1, ale każdy wiersz w znaczniku <li> - element listy punktowanej. Między polami stały tekst „, rodzina: ” (R.5.6). Podgląd: prawdziwek, rodzina: borowikowate i podgrzybek, rodzina: borowikowate.
grzyby.phpPHPzmienione: 3 linie
<!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Grzybobranie</title> <linkrel="stylesheet"href="styl.css"></head><body> <sectionid="title"> <h1>Czas na grzyby!</h1> </section> <sectionid="thumbnail"> <ahref="podgrzybek.jpg"><imgsrc="podgrzybek-miniatura.jpg"alt="Grzybobranie"></a> </section> <sectionid="left"> <h3>Grzyby jadalne</h3><?php$connection = mysqli_connect('localhost', 'root', '', 'grzybobranie');$result = mysqli_query($connection, 'SELECT id, nazwa, potoczna FROM grzyby WHERE jadalny = 1;');while ($row = mysqli_fetch_row($result)) {echo"<p>$row[0]. $row[1] ($row[2])</p>"; }?> <h3>Polecamy do zup</h3> <ul><?php$result = mysqli_query($connection, 'SELECT potoczna, rodzina.nazwa FROM grzyby JOIN rodzina ON grzyby.rodzina_id = rodzina.id WHERE potrawy_id = 4;');while ($row = mysqli_fetch_row($result)) {echo"<li>$row[0], rodzina: $row[1]</li>"; }?> </ul> </section> <sectionid="right"> </section> <footer> <p>Autor: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (grzyby.php)
Krok 32: Skrypt 3: zapytanie 3 w bloku prawym
ok. 1 min
Polecenie z arkusza
Działanie skryptu 3: Wysyła do bazy zapytanie 3
Blok prawy zawiera tylko efekt skryptu 3. Zapytanie 3 bez zmian - dwie kolumny wszystkich grzybów.
grzyby.phpPHPzmienione: 3 linie
<!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Grzybobranie</title> <linkrel="stylesheet"href="styl.css"></head><body> <sectionid="title"> <h1>Czas na grzyby!</h1> </section> <sectionid="thumbnail"> <ahref="podgrzybek.jpg"><imgsrc="podgrzybek-miniatura.jpg"alt="Grzybobranie"></a> </section> <sectionid="left"> <h3>Grzyby jadalne</h3><?php$connection = mysqli_connect('localhost', 'root', '', 'grzybobranie');$result = mysqli_query($connection, 'SELECT id, nazwa, potoczna FROM grzyby WHERE jadalny = 1;');while ($row = mysqli_fetch_row($result)) {echo"<p>$row[0]. $row[1] ($row[2])</p>"; }?> <h3>Polecamy do zup</h3> <ul><?php$result = mysqli_query($connection, 'SELECT potoczna, rodzina.nazwa FROM grzyby JOIN rodzina ON grzyby.rodzina_id = rodzina.id WHERE potrawy_id = 4;');while ($row = mysqli_fetch_row($result)) {echo"<li>$row[0], rodzina: $row[1]</li>"; }?> </ul> </section> <sectionid="right"><?php$result = mysqli_query($connection, 'SELECT nazwa_pliku, nazwa FROM grzyby;');?> </section> <footer> <p>Autor: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (grzyby.php)
Krok 33: Skrypt 3: zdjęcie dla każdego wiersza
Kryteria:
R.5.7 Skrypt 3 wyświetla obrazy, dla których nazwa pliku jest pobrana z bazy. Liczba obrazów jest równa liczbie zwróconych zapytaniem wierszy
ok. 3 min
Polecenie z arkusza
Dla każdego wiersza wartości zwrócone zapytaniem: nazwa pliku i nazwa wykorzystane są do wyświetlenia obrazu, w ten sposób, że nazwa pliku jest źródłem obrazu
Dla każdego wiersza echo wypisuje znacznik <img>: nazwa pliku $row[0] trafia do atrybutu src - pliki zdjęć leżą w folderze strony, więc sama nazwa wystarcza. Cudzysłowy wokół wartości atrybutów muszą być poprzedzone ukośnikiem \", bo cały napis echo też jest w cudzysłowie. Tekst alternatywny alt z nazwą grzyba przyda się czytnikom ekranu (arkusz go nie wymaga).
Podgląd: osiem zdjęć - tyle, ile wierszy zwróciło zapytanie (R.5.7). Mają już ramki i zaokrąglone rogi z reguły img.
Typowe błędy
echo "<img src="$row[0]">"; bez ukośników przed cudzysłowami kończy napis w połowie - PHP zgłosi błąd składni i strona się nie wyświetli.
grzyby.phpPHPzmienione: 3 linie
<!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Grzybobranie</title> <linkrel="stylesheet"href="styl.css"></head><body> <sectionid="title"> <h1>Czas na grzyby!</h1> </section> <sectionid="thumbnail"> <ahref="podgrzybek.jpg"><imgsrc="podgrzybek-miniatura.jpg"alt="Grzybobranie"></a> </section> <sectionid="left"> <h3>Grzyby jadalne</h3><?php$connection = mysqli_connect('localhost', 'root', '', 'grzybobranie');$result = mysqli_query($connection, 'SELECT id, nazwa, potoczna FROM grzyby WHERE jadalny = 1;');while ($row = mysqli_fetch_row($result)) {echo"<p>$row[0]. $row[1] ($row[2])</p>"; }?> <h3>Polecamy do zup</h3> <ul><?php$result = mysqli_query($connection, 'SELECT potoczna, rodzina.nazwa FROM grzyby JOIN rodzina ON grzyby.rodzina_id = rodzina.id WHERE potrawy_id = 4;');while ($row = mysqli_fetch_row($result)) {echo"<li>$row[0], rodzina: $row[1]</li>"; }?> </ul> </section> <sectionid="right"><?php$result = mysqli_query($connection, 'SELECT nazwa_pliku, nazwa FROM grzyby;');while ($row = mysqli_fetch_row($result)) {echo"<img src=\"$row[0]\" alt=\"$row[1]\">"; }?> </section> <footer> <p>Autor: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (grzyby.php)
Krok 34: Skrypt 3: nazwa grzyba w dymku
Kryteria:
R.5.8 W skrypcie 3 zwrócona zapytaniem nazwa jest zapisana jako atrybut title obrazu
ok. 1 min
Polecenie z arkusza
a nazwa jest tytułem obrazu wyświetlanym jako dymek, gdy kursor myszy znajdzie się na elemencie (patrz obraz 2).
Atrybut title - przeglądarka pokazuje jego treść w dymku po zatrzymaniu kursora nad elementem (R.5.8). Na obrazie 2 kursor stoi nad drugim zdjęciem i widać dymek „czubajka kania”.
grzyby.phpPHPzmienione: 1 linia
<!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Grzybobranie</title> <linkrel="stylesheet"href="styl.css"></head><body> <sectionid="title"> <h1>Czas na grzyby!</h1> </section> <sectionid="thumbnail"> <ahref="podgrzybek.jpg"><imgsrc="podgrzybek-miniatura.jpg"alt="Grzybobranie"></a> </section> <sectionid="left"> <h3>Grzyby jadalne</h3><?php$connection = mysqli_connect('localhost', 'root', '', 'grzybobranie');$result = mysqli_query($connection, 'SELECT id, nazwa, potoczna FROM grzyby WHERE jadalny = 1;');while ($row = mysqli_fetch_row($result)) {echo"<p>$row[0]. $row[1] ($row[2])</p>"; }?> <h3>Polecamy do zup</h3> <ul><?php$result = mysqli_query($connection, 'SELECT potoczna, rodzina.nazwa FROM grzyby JOIN rodzina ON grzyby.rodzina_id = rodzina.id WHERE potrawy_id = 4;');while ($row = mysqli_fetch_row($result)) {echo"<li>$row[0], rodzina: $row[1]</li>"; }?> </ul> </section> <sectionid="right"><?php$result = mysqli_query($connection, 'SELECT nazwa_pliku, nazwa FROM grzyby;');while ($row = mysqli_fetch_row($result)) {echo"<img src=\"$row[0]\" alt=\"$row[1]\" title=\"$row[1]\">"; }?> </section> <footer> <p>Autor: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (grzyby.php)
Krok 35: Zamknięcie połączenia z bazą
Kryteria:
R.5.3 Po wykonaniu operacji na bazie zastosowano funkcję zamknięcia bazy
ok. 1 min
Polecenie z arkusza
Na końcu działania skrypt zamyka połączenie z serwerem
mysqli_close($connection) na końcu skryptu 3 - ostatniego miejsca, w którym strona korzysta z bazy (R.5.3). Strona spełnia wszystkie kryteria arkusza; podgląd pokazuje stan z obrazu 2. Dalej trzy kroki o zabezpieczeniach - klucz ich nie wymaga.
Typowe błędy
mysqli_close na końcu skryptu 1 zamknie połączenie, zanim skrypty 2 i 3 wyślą swoje zapytania - PHP 8 zgłosi błąd „mysqli object is already closed”.
grzyby.phpPHPzmienione: 1 linia
<!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Grzybobranie</title> <linkrel="stylesheet"href="styl.css"></head><body> <sectionid="title"> <h1>Czas na grzyby!</h1> </section> <sectionid="thumbnail"> <ahref="podgrzybek.jpg"><imgsrc="podgrzybek-miniatura.jpg"alt="Grzybobranie"></a> </section> <sectionid="left"> <h3>Grzyby jadalne</h3><?php$connection = mysqli_connect('localhost', 'root', '', 'grzybobranie');$result = mysqli_query($connection, 'SELECT id, nazwa, potoczna FROM grzyby WHERE jadalny = 1;');while ($row = mysqli_fetch_row($result)) {echo"<p>$row[0]. $row[1] ($row[2])</p>"; }?> <h3>Polecamy do zup</h3> <ul><?php$result = mysqli_query($connection, 'SELECT potoczna, rodzina.nazwa FROM grzyby JOIN rodzina ON grzyby.rodzina_id = rodzina.id WHERE potrawy_id = 4;');while ($row = mysqli_fetch_row($result)) {echo"<li>$row[0], rodzina: $row[1]</li>"; }?> </ul> </section> <sectionid="right"><?php$result = mysqli_query($connection, 'SELECT nazwa_pliku, nazwa FROM grzyby;');while ($row = mysqli_fetch_row($result)) {echo"<img src=\"$row[0]\" alt=\"$row[1]\" title=\"$row[1]\">"; } mysqli_close($connection);?> </section> <footer> <p>Autor: Bartosz Bryniarski</p> </footer></body></html>
Kroki 36-38 · po tym etapie masz 34 z 34 kryteriów (pkt)
Krok 36: Zabezpieczenie: htmlspecialchars w atrybutach obrazu
ok. 4 min
Polecenie z arkusza
Dla każdego wiersza wartości zwrócone zapytaniem: nazwa pliku i nazwa wykorzystane są do wyświetlenia obrazu, w ten sposób, że nazwa pliku jest źródłem obrazu, a nazwa jest tytułem obrazu wyświetlanym jako dymek, gdy kursor myszy znajdzie się na elemencie (patrz obraz 2).
Skrypty wklejają tekst z bazy prosto do HTML-a. Sprawdzone na wersji z poprzednich kroków: wystarczy, że ktoś (np. przez panel administracyjny albo inny formularz) zapisze w tabeli nazwę grzyba z cudzysłowem, np. kania" onmouseover="…". Cudzysłów kończy wartość atrybutu title, a reszta napisu staje się nowym atrybutem - kodem JavaScript, który przeglądarka wykona u każdego, kto najedzie kursorem na zdjęcie (atak XSS). W teście ten kod podmieniał całą stronę na napis „Atak XSS”.
Lekarstwo: htmlspecialchars($tekst, ENT_QUOTES) zamienia <, >, & oraz oba rodzaje cudzysłowów na encje (" …). Przeglądarka pokaże je jako zwykłe znaki w dymku i atrybut się nie zamknie. Podgląd: ta sama zmieniona nazwa w bazie - w dymku drugiego zdjęcia widać cały napis, a kod się nie wykonuje. Stara linia zostaje w komentarzu. W bloku lewym widać jeszcze skutek tej samej nazwy w akapicie skryptu 1: fragment <h1>Atak XSS</h1> stał się prawdziwym nagłówkiem - to zabezpiecza następny krok.
Typowe błędy
Bez ENT_QUOTES starsze wersje PHP (przed 8.1) nie zamieniały apostrofu - atrybut w apostrofach (title='…') dało się zamknąć tak samo.
Krok 37: Zabezpieczenie: htmlspecialchars w skryptach 1 i 2
ok. 2 min
Polecenie z arkusza
Kolejne wiersze są wyświetlane w osobnych akapitach w formacie: <id>. <nazwa> (<potoczna>), gdzie pola zapisane w <> są pobrane z bazy danych
Ten sam problem dotyczy akapitów skryptu 1 i elementów listy skryptu 2: nazwa ze znacznikiem <script> zostałaby wykonana. Zasada jest jedna - każdy tekst z bazy zabezpieczasz w chwili wypisywania, niezależnie od tego, gdzie trafia. id jest liczbą z bazy i tego nie potrzebuje.
Napisy są teraz sklejane kropką (.) zamiast podstawiane w cudzysłowie - wywołania funkcji nie da się wstawić do napisu "…". Podgląd: zmieniona nazwa w akapicie 2 wyświetla się dosłownie, z cudzysłowami.
Krok 38: Zabezpieczenie: bez komunikatów błędów dla odwiedzających
ok. 3 min
Polecenie z arkusza
Skrypt łączy się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie grzybobranie
Sprawdzone na wersji podstawowej ze złym hasłem do bazy: PHP 8 rzuca wyjątek mysqli_sql_exception i strona kończy się komunikatem „Fatal error: Uncaught mysqli_sql_exception: Access denied for user 'root'@'localhost'” z pełną ścieżką pliku na serwerze i numerem linii. Taki komunikat podpowiada atakującemu nazwę użytkownika bazy, układ katalogów i miejsce w kodzie.
Połączenie stoi teraz w bloku try, a catch (mysqli_sql_exception $exception) przechwytuje wyjątek i kończy stronę (exit) krótkim zdaniem „Baza grzybów jest chwilowo niedostępna.”. Szczegóły błędu zostają w zmiennej $exception - w prawdziwym serwisie zapisuje się je do dziennika na serwerze, a nie na stronę. Podgląd: zwykły stan strony (baza działa).
Krok 39 · po tym etapie masz 34 z 34 kryteriów (pkt)
Krok 39: Zawartość folderu i nagranie płyty
ok. 5 min
Polecenie z arkusza
UWAGA: katalog z rezultatami pracy oraz płytę należy opisać numerem, którym został podpisany arkusz, czyli numerem PESEL lub w przypadku jego braku numerem paszportu.
W folderze z numerem zdającego muszą być: import.png, kw1.png - kw4.png, kwerendy.txt, grzyby.php, styl.css, wszystkie zdjęcia z archiwum oraz podgrzybek.jpg i podgrzybek-miniatura.jpg (plik SQL z archiwum może zostać). Strona korzysta z ośmiu zdjęć i miniatury - bez nich egzaminator zobaczy puste ramki. Nagraj płytę, sprawdź jej odczyt i opisz ją numerem zdającego.
Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.
To jedno z możliwych rozwiązań. Egzaminator ocenia spełnienie kryteriów z klucza odpowiedzi, a nie zgodność z tym kodem - inna, poprawna droga też daje punkty.
Autor rozwiązania: Bartosz Bryniarski. Licencja CC BY-NC 4.0 - kopiowanie i przeróbki do celów niekomercyjnych, z podaniem autora i źródła (link do tej strony) (zasady wykorzystania). Treść arkusza i klucza oceniania pochodzi z CKE.