Serwis używa plików cookies do analizy ruchu i poprawy działania. Szczegóły
EE.09-04-21.06-SGCzerwiec 2021 · 42 kroki · ok. 83 min · 35 kryteriów z klucza
Rozwiązanie krok po kroku: Panel administratora portalu społecznościowego (Wersja podstawowa)
Panel administratora portalu społecznościowego: baza dane4 z tabelami osoby i hobby z pliku CKE, cztery zapytania (LIMIT, złączenie JOIN dla jednej osoby, funkcje AVG, MIN i MAX oraz UPDATE), zdjęcie wykadrowane do kwadratu 100 × 100 px oraz strona users.php z dwoma skryptami PHP: w bloku lewym lista użytkowników z wiekiem obliczonym z roku urodzenia, w bloku prawym karta osoby o identyfikatorze wpisanym w formularzu.
To arkusz EE.09 z czerwca 2021 - kwalifikacji formuły 2017, którą zastąpiła INF.03. W tej samej sesji był pokrewny arkusz EE.09-01-21.06 (lista przyjaciół): ma te same materiały - bazę osób z hobby i osiem zdjęć - ale inne zapytania, inną stronę i inny skrypt; jego rozwiązanie jest przy tamtym arkuszu. 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 wstrzyknięciu SQL i zabezpieczone.
To jest wersja podstawowa. W zakładce „Nowoczesny kod (flex, PDO)” jest drugi sposób rozwiązania tego samego arkusza: flexbox zamiast float, etykieta formularza, PDO z zapytaniem przygotowanym na górze pliku i szablon z pętlą foreach - obie wersje są zgodne z kluczem.
Baza i zdjęcia pochodzą z oryginalnego archiwum CKE zad4.zip (załącznik do pobrania na stronie arkusza, hasło w treści zadania). Zapytania i zrzuty są policzone na MariaDB 10.4 - tej samej, która jest w XAMPP. Podgląd od kroków z PHP to wynik prawdziwego uruchomienia kodu w PHP 8.2 (klucz każe sprawdzać w XAMPP 7.4.8 - użyte funkcje działają w obu wersjach tak samo). Wiek w podglądzie jest liczony od roku 2026, dlatego jest wyższy niż na obrazie 2 w arkuszu, przygotowanym w 2019 roku.
Jak liczą się punkty. Klucz ma 35 kryteriów (R.1.1 - R.5.8), każde za 1 punkt. Próg zdania części praktycznej to 75%, czyli 27 punktów - można stracić 8 kryteriów i nadal zdać. Przy każdym kroku jest napisane, które kryteria spełnia; egzaminator ocenia spełnienie kryteriów z klucza, nie zgodność z tym kodem.
Suwak i strzałki obok niego - albo ←→ na klawiaturze - przechodzą krok po kroku; wyjaśnienie, pliki i podgląd zmieniają się razem z krokiem.
Lista „Etap” przenosi od razu do wybranego etapu.
„Rozwiązuję samodzielnie” chowa rozwiązanie kroku - najpierw własna próba, potem odsłonięcie.
„Tryb prezentacji” pokazuje same kroki, bez menu strony - na rzutnik.
Etap 1: Baza danych
Kroki 1-7 · po tym etapie masz 6 z 35 kryteriów (pkt)
Krok 1: Folder zdającego i archiwum zad4
ok. 2 min
Polecenie z arkusza
Na pulpicie znajduje się archiwum ZIP o nazwie zad4.zip zabezpieczone hasłem: *Uzytkownicy^Portalu$ Należy utworzyć folder o nazwie odpowiadającej numerowi PESEL zdającego lub w przypadku jego braku innemu numerowi, którym został podpisany arkusz. Archiwum należy rozpakować do tego folderu.
Na pulpicie tworzysz folder nazwany numerem PESEL - w tym rozwiązaniu w jego miejscu stoi imię i nazwisko autora, Bartosz Bryniarski. Archiwum zad4.zip otwierasz programem 7-Zip (albo innym programem do archiwów ZIP) i rozpakowujesz do tego folderu, podając hasło *Uzytkownicy^Portalu$ - z gwiazdką na początku i znakiem dolara na końcu. W środku jest plik dane.sql i osiem zdjęć: osoba1.jpg - osoba8.jpg.
Pliki strony muszą leżeć w tym samym folderze co zdjęcia - skrypt wstawi nazwę zdjęcia z bazy bez żadnej ścieżki.
Typowe błędy
Pliki zapisane poza folderem zdającego (np. w htdocs bez kopii w folderze) nie trafią na płytę - a ocenia się tylko to, co jest na płycie.
Krok 2: XAMPP i pusta baza dane4
ok. 2 min
Polecenie z arkusza
Uruchom usługi MySQL i Apache za pomocą XAMPP Control Panel. Za pomocą narzędzia phpMyAdmin wykonaj podane operacje na bazie danych: Utwórz bazę danych o nazwie dane4
W XAMPP Control Panel klikasz Start przy Apache i MySQL, potem w przeglądarce otwierasz http://localhost/phpmyadmin. Nowa w lewym panelu, nazwa dane4 (z cyfrą na końcu), Utwórz. Porównywanie napisów możesz zostawić domyślne - tabele z pliku i tak przyniosą własne kodowanie (latin1).
Typowe błędy
Baza dane (bez cyfry) to strata R.1.1 i R.5.1 - skrypt łączy się z bazą po nazwie, a klucz sprawdza nazwę dane4.
Krok 3: Import pliku dane.sql i zrzut egzamin.jpg
Kryteria:
R.1.1 Wykonano import tabel do bazy danych dane4 , czynność udokumentowano plikiem o nazwie egzamin w formacie JPEG
ok. 3 min
Polecenie z arkusza
Do bazy dane4 zaimportuj plik dane.sql z rozpakowanego archiwum. Wykonaj zrzut ekranu po imporcie. Zrzut zapisz w folderze z numerem PESEL lub w przypadku jego braku w folderze z innym numerem, którym został podpisany arkusz, w formacie JPEG i nazwij egzamin. 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
Zaznacz bazę dane4, karta Import, Wybierz plik → dane.sql, Wykonaj. Plik tworzy pięć tabel: hobby (16 rekordów), osoby (40 rekordów) oraz puste producenci, produkty i rodzaje - do zadania potrzebne są tylko dwie pierwsze.
„Elementy wskazujące na poprawnie wykonany import” to zielony komunikat o sukcesie i lista tabel w lewym panelu albo na karcie Struktura bazy. Zrzut całego ekranu z paskiem zadań zapisujesz jako egzamin.jpg - w tym arkuszu w formacie JPEG, a zrzuty zapytań w PNG.
Typowe błędy
Ten zrzut ma nazwę egzamin i format JPEG. 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: pierwsze 30 osób
Kryteria: ,
R.1.2 Zapisano plik tekstowy o nazwie kwerendy zawierający co najmniej jedno zapytanie SQL, wynikające z treści zadania
R.1.3 Utworzono zapytanie 1 wybierające jedynie pola id, imie, nazwisko, rok_urodzenia, zdjecie z tabeli osoby . Kwerenda wybiera początkowe 30 wierszy, jawnie wskazana liczba wierszy. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT id, imie, nazwisko, rok_urodzenia, zdjecie FROM osoby LIMIT 30; oraz wynik działania zapytania udokumentowano zrzutem, na którym widocznych jest dokładnie 30 rekordów jedynie pola id, imie, nazwisko, rok_urodzenia, zdjecie
ok. 3 min
Polecenie z arkusza
Zapisz i wykonaj zapytania SQL działające na bazie dane4. Zapytania zapisz w pliku kwerendy.txt, w folderze z numerem PESEL lub w przypadku jego braku w folderze z innym numerem, którym został podpisany arkusz. Wykonaj zrzuty ekranu przedstawiające wyniki działania kwerend. 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, imie, nazwisko, rok_urodzenia, zdjecie z tabeli osoby. Kwerenda wybiera pierwsze 30 wierszy, należy jawnie wskazać liczbę wierszy
Zaznacz bazę dane4, karta SQL:
SELECT id, imie, nazwisko, rok_urodzenia, zdjecie - pięć kolumn z arkusza („jedynie” wyklucza SELECT *, które dołożyłoby Hobby_id i opis),
FROM osoby,
LIMIT 30 - jawnie wskazana liczba wierszy. Tabela ma 40 osób, a phpMyAdmin sam pokazuje 25 wierszy na stronę - dlatego limit musi stać w zapytaniu.
Wynik: 30 rekordów, od id 2 (Monika Kowalska) do id 31 (Grzegorz Roh). Zapytanie kopiujesz do pliku kwerendy.txt, a cały ekran zapisujesz jako kw1.png - przy 30 wierszach przewiń wynik tak, żeby było widać komunikat „Pokazano wiersze 0 - 29 (30 łącznie)”. To zapytanie wyśle skrypt w bloku lewym.
kwerendy.txtTekstnowy plik
-- Zapytanie 1SELECT id, imie, nazwisko, rok_urodzenia, zdjecie FROM osoby LIMIT 30;
Podgląd strony po tym kroku
Krok 5: Zapytanie 2: dane osoby o id 10 z nazwą hobby
Kryteria:
R.1.4 Utworzono zapytanie 2 wybierające jedynie pola imie, nazwisko, rok_urodzenia, opis, zdjecie z tabeli osoby oraz odpowiadające im pole nazwa z tabeli hobby dla osoby o id równym 10. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT imie, nazwisko, rok_urodzenia, opis, zdjecie, nazwa FROM osoby JOIN hobby ON osoby.Hobby_id = hobby.id WHERE osoby.id = 10; możliwe INNER JOIN lub bez JOIN (porównanie kluczy w where) oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczny jest dokładnie jeden rekord: Roksana Rybnik 1995 pozdrawiam osoba3.jpg biegi
ok. 4 min
Polecenie z arkusza
Zapytanie 2: wybierające jedynie pola imie, nazwisko, rok_urodzenia, opis, zdjecie z tabeli osoby oraz odpowiadające im pole nazwa z tabeli hobby dla osoby o id równym 10
SELECT imie, nazwisko, rok_urodzenia, opis, zdjecie, nazwa - pięć kolumn z osoby i nazwa hobby z hobby,
FROM osoby JOIN hobby ON osoby.Hobby_id = hobby.id - złączenie: do osoby dołączany jest wiersz hobby o numerze z jej kolumny Hobby_id,
WHERE osoby.id = 10 - kolumna id jest w obu tabelach, więc trzeba wskazać tabelę.
Wynik: jeden rekord - Roksana Rybnik, 1995, pozdrawiam, osoba3.jpg, biegi, dokładnie jak w kluczu. Zrzut: kw2.png. To zapytanie wyśle skrypt w bloku prawym, z identyfikatorem z formularza w miejscu liczby 10.
Typowe błędy
Samo WHERE id = 10 przy złączeniu kończy się błędem #1052 („Column 'id' in where clause is ambiguous”).
kwerendy.txtTekstzmienione: 3 linie
-- Zapytanie 1SELECT id, imie, nazwisko, rok_urodzenia, zdjecie FROM osoby LIMIT 30;-- Zapytanie 2SELECT imie, nazwisko, rok_urodzenia, opis, zdjecie, nazwa FROM osoby JOIN hobby ON osoby.Hobby_id = hobby.id WHERE osoby.id = 10;
Podgląd strony po tym kroku
Krok 6: Zapytanie 3: średni, najniższy i najwyższy rok urodzenia
Kryteria:
R.1.5 Utworzono zapytanie 3 liczące średni rok urodzenia oraz najniższy i najwyższy rok urodzenia z tabeli osoby . W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT AVG(rok_urodzenia), MIN(rok_urodzenia), MAX(rok_urodzenia) FROM osoby; oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczne są jedynie dane: 1992.7 1967 2009 (pierwsza wartość z dowolnym zaokrągleniem)
ok. 2 min
Polecenie z arkusza
Zapytanie 3: liczące średni rok urodzenia oraz najniższy i najwyższy rok urodzenia z tabeli osoby
Trzy funkcje agregujące w jednym SELECT: AVG(rok_urodzenia) - średnia, MIN(…) - najmniejsza wartość, MAX(…) - największa. Funkcja agregująca zamienia całą tabelę w jeden wiersz wyniku.
Wynik: 1992.7000, 1967, 2009 - zgodnie z kluczem (1992,7 w dowolnym zaokrągleniu, najstarsza osoba urodziła się w 1967, najmłodsza w 2009). Zrzut: kw3.png.
kwerendy.txtTekstzmienione: 3 linie
-- Zapytanie 1SELECT id, imie, nazwisko, rok_urodzenia, zdjecie FROM osoby LIMIT 30;-- Zapytanie 2SELECT imie, nazwisko, rok_urodzenia, opis, zdjecie, nazwa FROM osoby JOIN hobby ON osoby.Hobby_id = hobby.id WHERE osoby.id = 10;-- Zapytanie 3SELECT AVG(rok_urodzenia), MIN(rok_urodzenia), MAX(rok_urodzenia) FROM osoby;
Podgląd strony po tym kroku
Krok 7: Zapytanie 4: nowy opis dla osób z hobby 4
Kryteria:
R.1.6 Utworzono zapytanie 4 aktualizujące pole opis z tabeli osoby dla osób, których pole Hobby_id jest równe 4. Wartość pola opis dowolna. W pliku z kwerendami lub na zrzucie istnieje zapis np.: UPDATE osoby SET opis="..." WHERE Hobby_id = 4; (dowolny wpis w miejscu kropek) oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczny jest komunikat o poprawnie wykonanym zapytaniu lub tabela ze zmienionymi danymi
ok. 2 min
Polecenie z arkusza
Zapytanie 4: aktualizujące pole opis z tabeli osoby dla osób, których pole Hobby_id jest równe 4. Wartość nowego wpisu w polu opis jest dowolna będąca możliwym hobby
UPDATE osoby - zmiana istniejących rekordów,
SET opis = 'akrobatyka' - nowa wartość pola; arkusz pozwala wpisać dowolne hobby,
WHERE Hobby_id = 4 - tylko osoby z hobby numer 4 (gimnastyka).
W bazie jest jedna taka osoba - Anna Kowalska z id 25 - więc phpMyAdmin melduje 1 wiersz zmieniony. Zrzut: kw4.png (komunikat albo tabela ze zmienionym opisem). Plik kwerendy.txt ma komplet czterech zapytań.
Typowe błędy
UPDATE bez WHERE zmienia opis wszystkim 40 osobom - phpMyAdmin nie pyta o potwierdzenie, a cofnąć to można tylko ponownym importem.
kwerendy.txtTekstzmienione: 3 linie
-- Zapytanie 1SELECT id, imie, nazwisko, rok_urodzenia, zdjecie FROM osoby LIMIT 30;-- Zapytanie 2SELECT imie, nazwisko, rok_urodzenia, opis, zdjecie, nazwa FROM osoby JOIN hobby ON osoby.Hobby_id = hobby.id WHERE osoby.id = 10;-- Zapytanie 3SELECT AVG(rok_urodzenia), MIN(rok_urodzenia), MAX(rok_urodzenia) FROM osoby;-- Zapytanie 4UPDATE osoby SET opis = 'akrobatyka' WHERE Hobby_id = 4;
Podgląd strony po tym kroku
Etap 2: Grafika i strona HTML
Kroki 8-24 · po tym etapie masz 18 z 35 kryteriów (pkt)
Krok 8: Grafika: kadr 1:1 z twarzą
ok. 2 min
Polecenie z arkusza
Plik osoba1.jpg, wypakowany z archiwum, skadrowany do proporcji 1:1, tak aby była widoczna twarz osoby.
osoba1.jpg z archiwum ma 300 × 200 px - jest poziomy, a pozostałe zdjęcia są kwadratowe. W GIMP-ie narzędzie Zaznaczenie prostokątne z opcją Stała: proporcje 1:1, zaznaczenie 200 × 200 px (cała wysokość zdjęcia) przesunięte tak, żeby twarz była w środku - tutaj od x = 35 px - i Obraz → Przytnij do zaznaczenia. Podgląd pokazuje kadr przed skalowaniem.
Typowe błędy
Kadr z lewej krawędzi zdjęcia (x = 0) ucina twarz z prawej strony - klucz wymaga, żeby twarz była widoczna (R.2.1).
Podgląd strony po tym kroku
Krok 9: Grafika: rozmiar dokładnie 100 × 100 px
Kryteria:
R.2.1 Plik osoba1.jpg zawiera widoczną twarz osoby, rozmiar zdjęcia dokładnie 100 px na 100 px
ok. 2 min
Polecenie z arkusza
Przeskalowany do rozmiaru dokładnie 100 px na 100 px. Pozostałe pliki graficzne są przygotowane i nie należy ich zmieniać
Obraz → Skaluj obraz: szerokość 100, wysokość 100 - kadr jest kwadratowy, więc proporcje się nie zmieniają. Zapis pod tą samą nazwą: Plik → Nadpisz osoba1.jpg (zwykłe Zapisz w GIMP-ie zapisuje format .xcf). Zdjęcia osoba2.jpg - osoba8.jpg mają już 100 × 100 px i zostają bez zmian.
Typowe błędy
Skalowanie całego zdjęcia 300 × 200 do 100 × 100 bez kadrowania spłaszczy twarz.
Podgląd strony po tym kroku
Krok 10: Plik settings.html
ok. 1 min
Polecenie z arkusza
Odsyłacz o treści „Inne ustawienia” prowadzący do strony settings.html (należy utworzyć plik settings.html i zapisać w nim treść: „strona w trakcie budowy”)
Najpierw cel odsyłacza: w edytorze tworzysz plik settings.html z jednym zdaniem: strona w trakcie budowy. Arkusz każe zapisać w nim tylko tę treść, więc bez szkieletu HTML - przeglądarka i tak wyświetli tekst. Sam odsyłacz powstanie w bloku lewym.
settings.htmlHTMLnowy plik
strona w trakcie budowy
Podgląd strony po tym kroku (settings.html)
Krok 11: Plik users.php
Kryteria:
R.2.2 Witrynę internetową zapisano w pliku PHP o nazwie users.php
ok. 2 min
Polecenie z arkusza
Składa się ze strony o nazwie users.php
Plik users.php w folderze zdającego, obok zdjęć. Rozszerzenie .php, bo dojdą skrypty - na razie to czysty HTML. <!DOCTYPE html> oznacza HTML5, lang="pl" mówi przeglądarce i czytnikom ekranu, że treść jest po polsku. Nazwę pliku sprawdza klucz (R.2.2).
R.2.3 Zastosowano właściwy standard kodowania polskich znaków dla witryny
ok. 1 min
Polecenie z arkusza
Zastosowany właściwy standard kodowania polskich znaków
<meta charset="UTF-8"> jako pierwsza rzecz w <head>. Bez tej linii „Społecznościowy” i „Użytkownicy” mogą wyświetlić się jako krzaki. Plik trzeba też zapisać w UTF-8.
R.3.5 Strona zawiera działające połączenie z zewnętrznym arkuszem stylów o nazwie styl4.css , formatowanie pochodzi jedynie z tego arkusza Uwaga: kryterium to nie jest spełnione również, gdy źle przypisano co najmniej jednen selektor do bloku (np. kropka zamiast #)
ok. 1 min
Polecenie z arkusza
Arkusz stylów w pliku o nazwie styl4.css prawidłowo połączony z kodem strony
<link rel="stylesheet" href="styl4.css"> w <head>. Nazwa ma w sobie cyfrę: styl4.css, nie styl.css. Plik stylów powstanie po HTML-u.
Typowe błędy
Klucz uznaje R.3.5 tylko przy działającym połączeniu, w którym wszystkie selektory trafiają w bloki - kropka zamiast # przy identyfikatorze odbiera punkt.
R.2.5 Wygląd strony zdefiniowano dzieląc ją na bloki: baner, lewy i prawy, stopka. Zastosowano znaczniki sekcji
ok. 2 min
Polecenie z arkusza
Podział strony na bloki: baner, lewy i prawy, stopka. Podział zrealizowany za pomocą znaczników sekcji tak, aby wygląd układu bloków strony po uruchomieniu w przeglądarce, był zgodny z Obrazem 2
Cztery bloki w kolejności z obrazu 2: baner <header>, blok lewy i prawy jako <section>, stopka <footer>. Obie sekcje mają te same style, ale różną treść, więc dostają identyfikatory: left i right (nazwy po angielsku - arkusz ich nie narzuca). Na razie bloki są puste i stoją jeden pod drugim; obok siebie ustawi je CSS.
R.3.2 Na stronie umieszczono odnośnik "Inne ustawienia", po jego wybraniu otwiera się strona settings.html
ok. 1 min
Polecenie z arkusza
Odsyłacz o treści „Inne ustawienia” prowadzący do strony settings.html
<a href="settings.html"> - kliknięcie otwiera plik z poprzedniego kroku, z tego samego folderu (R.3.2). Odsyłacz jest ostatni w bloku lewym: skrypt 1 wpisze listę użytkowników między nagłówkiem a odsyłaczem, jak na obrazie 2.
Krok 19: Blok prawy: nagłówek „Podaj id użytkownika”
Kryteria:
R.2.6 W bloku banera zapisano nagłówek h3: "Portal Społecznościowy - panel administratora" oraz w bloku lewym nagłówek h4: "Użytkownicy" i w bloku prawym nagłówek h4: "Podaj id użytkownika"
ok. 1 min
Polecenie z arkusza
Zawartość bloku prawego: Nagłówek czwartego stopnia o treści „Podaj id użytkownika”
Drugi <h4>. Strona ma już komplet nagłówków: <h3> w banerze i dwa <h4> w blokach (R.2.6).
R.3.3 Formularz wysyła dane do tego samego pliku metodą POST
ok. 1 min
Polecenie z arkusza
Formularz wysyłający dane metodą post do tego samego pliku i zawierający:
<form method="post"> - bez atrybutu action formularz wysyła dane do tego samego adresu, z którego został wczytany, czyli do users.php (R.3.3). Metoda POST przesyła dane w treści żądania; w PHP odczytasz je z tablicy $_POST.
Typowe błędy
method="get" dopisze identyfikator do adresu (users.php?id=10), a skrypt czytający $_POST niczego nie dostanie.
<input type="number"> - przeglądarka pokaże strzałki do zmiany wartości i nie pozwoli wpisać liter. Atrybut name="id" to klucz, pod którym wartość trafi do PHP: $_POST['id'].
Typowe błędy
type="text" wygląda podobnie, ale klucz R.3.4 wymaga pola typu number.
R.2.7 W bloku prawym umieszczono formularz z polem edycyjnym i przyciskiem, zastosowano zancznik <form> oraz jego zamknięcie
R.3.4 Pole edycyjne formularza jest typu number, przycisk ZOBACZ wysyła dane z formularza (np. typ submit dla input lub button albo button bez typu)
ok. 1 min
Polecenie z arkusza
przycisk wysyłający dane o treści "ZOBACZ”
<button type="submit">ZOBACZ</button> - przycisk wysyłający formularz, z napisem wielkimi literami jak w arkuszu. Formularz ma komplet: pole i przycisk w znaczniku <form> z zamknięciem (R.2.7), pole typu number i przycisk, który wysyła dane (R.3.4). Znacznik <button> da się w CSS złapać prostym selektorem button, a styl przycisku jest w tym arkuszu osobnym wymaganiem.
Typowe błędy
<button type="button"> niczego nie wysyła - brak punktu R.3.4 i skrypt 2 nigdy się nie uruchomi.
R.2.8 W stopce umieszczono napis „Stronę wykonał: ”, dalej wstawiony numer PESEL zdającego
ok. 1 min
Polecenie z arkusza
Zawartość stopki: napis o treści „Stronę wykonał: ”, dalej wstawiony numer PESEL zdającego lub w przypadku jego braku inny numer, którym został podpisany arkusz
Na egzaminie po dwukropku wpisujesz swój numer PESEL. W tym rozwiązaniu w jego miejscu stoi imię i nazwisko autora: Bartosz Bryniarski.
Kroki 25-30 · po tym etapie masz 27 z 35 kryteriów (pkt)
Krok 25: Plik styl4.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 selektora * lub body lub html lub kontenera całej strony)
ok. 1 min
Polecenie z arkusza
Cechy formatowania CSS: Domyślne dla całej strony: krój czcionki Helvetica
Nowy plik styl4.css w folderze zdającego. Pierwsza reguła: selektor uniwersalny * z font-family: Helvetica (R.4.1). Selektor * obejmuje też pole formularza i przycisk, które kroju po body nie dziedziczą. Windows nie ma kroju Helvetica - przeglądarka podstawi wtedy Arial, który wygląda prawie tak samo.
styl4.cssCSSnowy plik
* {font-family: Helvetica;}
Podgląd strony po tym kroku (users.php)
Krok 26: Baner i stopka: tło, kolor, wyśrodkowanie, wysokość
Kryteria: ,
R.4.3 Ustawiono biały kolor czcionki dla banera i stopki
R.4.6 Ustawiono wyrównanie tekstu do środka dla banera i stopki
ok. 2 min
Polecenie z arkusza
Dla banera i stopki: kolor tła #AF4448, biały kolor czcionki, wyrównanie tekstu do środka, wysokość 20 px
Dwa selektory rozdzielone przecinkiem dostają jedną wspólną regułę. Biały tekst (R.4.3) i wyśrodkowanie (R.4.6) banera i stopki są ustawione.
Krok 27: Blok lewy i prawy: tło, wysokość, szerokość
Kryteria: , ,
R.4.2 Ustawiono kolor tła #AF4448 dla banera i stopki oraz #FFA4A2 dla bloku lewego i prawego
R.4.4 Ustawiono wysokość bloków: baner i stopka 20px, lewy i prawy 650px
R.4.5 Ustawiono szerokość bloków prawego i lewego 50%
ok. 2 min
Polecenie z arkusza
Dla bloku lewego i prawego: kolor tła #FFA4A2, wysokość 650 px, szerokość 50%
Selektory identyfikatorów #left i #right - z krzyżykiem, bo w HTML-u są atrybuty id. Kolory tła wszystkich bloków (R.4.2), wysokości (R.4.4) i szerokości (R.4.5) są ustawione.
Typowe błędy
Bloki o sumie szerokości 100% nie mogą mieć marginesów, padding ani obramowań - każdy piksel więcej zepchnie blok prawy pod lewy.
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ł zrealizowany za pomocą znaczników sekcji tak, aby wygląd układu bloków strony po uruchomieniu w przeglądarce, był zgodny z Obrazem 2
Obie sekcje dostają float: left: blok lewy staje przy lewej krawędzi, prawy tuż obok niego - razem 50% + 50%. Stopka z clear: both zaczyna się dopiero pod pływającymi blokami (R.3.1). Arkusz nie podaje tych właściwości, ale wymaga układu z obrazu 2.
Typowe błędy
Bez clear: both stopka wsunie się pod pływające bloki i jej pasek schowa się za nimi.
R.4.7 Ustawiono marginesy zewnętrzne (margin) dla selektora h4 na 0px (0 poprawnie też bez jednostki)
ok. 1 min
Polecenie z arkusza
Dla selektora nagłówka czwartego stopnia: marginesy zewnętrzne 0 px
Przeglądarka daje nagłówkom domyślne marginesy zewnętrzne. margin: 0 (przy zerze jednostka nie jest potrzebna) dosuwa „Użytkownicy” i „Podaj id użytkownika” do górnej krawędzi bloków, jak na obrazie 2 (R.4.7).
Krok 30: Przycisk: tło, kolor, marginesy wewnętrzne i zewnętrzne
Kryteria:
R.4.8 Ustawiono styl przycisku wysyłającego dane z formularza: kolor tła #AF4448, biały kolor czcionki, marginesy wewnętrzne (padding) 5px oraz zewnętrzne (margin) 10px
ok. 2 min
Polecenie z arkusza
Dla przycisku wysyłającego dane z formularza: kolor tła #AF4448, biały kolor czcionki, marginesy wewnętrzne 5 px, marginesy zewnętrzne 10 px
Selektor znacznika button - na stronie jest jeden przycisk. padding (margines wewnętrzny) powiększa przycisk wokół napisu, margin (zewnętrzny) odsuwa go od pola i krawędzi bloku (R.4.8). Arkusz stylów jest kompletny.
Typowe błędy
Gdyby przycisk był zapisany jako <input type="submit">, selektor musiałby brzmieć input[type="submit"] - sam input objąłby też pole liczby.
Kroki 31-39 · po tym etapie masz 35 z 35 kryteriów (pkt)
Krok 31: Skrypt 1: miejsce i połączenie z bazą dane4
Kryteria:
R.5.1 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy dane4
ok. 3 min
Polecenie z arkusza
Skrypt łączy się z serwerem bazy danych na localhost, użytkownik root bez hasła, baza danych o nazwie dane4
„Efekt działania skryptu 1” ma być w bloku lewym, między nagłówkiem a odsyłaczem (obraz 2) - tam piszesz pierwszy blok PHP, między <?php a ?>. Serwer wykona go, zanim strona trafi do przeglądarki.
Pierwsza instrukcja: mysqli_connect z tabeli 1 arkusza - kolejno serwer localhost, użytkownik root, puste hasło '' i nazwa bazy dane4. Połączenie trafia do zmiennej $connection i wystarczy dla obu skryptów: zmienne z jednego bloku PHP są widoczne w kolejnych blokach tego samego pliku (R.5.1).
Od tego kroku podgląd to wynik działania strony po stronie serwera (prawdziwe uruchomienie w PHP 8.2) - na razie wygląda jak wcześniej. Stronę sprawdzasz przez http://localhost/…, a nie otwierając plik z dysku: przeglądarka sama nie wykona PHP.
R.5.3 Skrypt 1 wysyła do bazy danych zapytanie 1 lub skrypt 2 wysyła zapytanie 2 zmodyfikowane w ten sposób, że w warunku jest przypisane id podane w formularzu
ok. 2 min
Polecenie z arkusza
Działanie skryptu 1 w panelu lewym: Wysyła do bazy danych zapytanie 1
mysqli_query($connection, '…') wysyła zapytanie 1 skopiowane z kwerendy.txt (R.5.3). Wynik - 30 wierszy - trafia do zmiennej $result; strona jeszcze niczego nie wypisuje.
Krok 33: Skrypt 1: każdy użytkownik w osobnej linii
Kryteria:
R.5.5 Skrypt 1 Wyświetla każdy zwrócony zapytaniem wiersz w osobnej linii o treści: "<id>. <imie> <nazwisko>" gdzie pola <id>, <imie>, <nazwisko> zostały pobrane z bazy (wiek nie jest sprawdzany w tym kryterium)
ok. 3 min
Polecenie z arkusza
Dla każdego wiersza zwróconego zapytaniem: Wyświetla w osobnej linii treść: <id>. <imie><nazwisko>, < wiek> lat, gdzie pola <id>, <imie>, <nazwisko> zostały pobrane z bazy, a <wiek> oznacza obliczony wiek osoby
while ($row = mysqli_fetch_row($result)) - funkcja z tabeli 1 zwraca kolejny wiersz jako tablicę numerowaną od zera, w kolejności kolumn z zapytania: $row[0] id, $row[1] imię, $row[2] nazwisko, $row[3] rok urodzenia, $row[4] zdjęcie. Gdy wierszy zabraknie, zwraca false i pętla się kończy,
echo skleja (operatorem .) identyfikator, kropkę ze spacją, imię, spację i nazwisko, a <br> na końcu przenosi kolejną osobę do nowej linii (R.5.5).
Podgląd: 30 linii, od „2. Monika Kowalska” do „31. Grzegorz Roh”. Wiek dojdzie w następnym kroku - klucz ocenia go osobno.
Typowe błędy
Bez <br> wszystkie osoby sklejają się w jeden długi wiersz tekstu - przeglądarka ignoruje znaki nowej linii w HTML-u.
Krok 34: Skrypt 1: wiek obliczony z roku urodzenia
Kryteria:
R.5.4 Skrypt 1 oblicza wiek osoby na podstawie jej roku urodzenia, np. z wykorzystaniem funkcji date('Y') lub wykorzystując rok 2019 lub 2021
ok. 2 min
Polecenie z arkusza
Oblicza wiek osoby na podstawie jej roku urodzenia
date('Y') zwraca bieżący rok czterema cyframi; odjęcie roku urodzenia daje wiek (z dokładnością do roku - arkusz nie podaje dnia urodzenia). Wynik trafia do zmiennej $age i do linii: „, ” + wiek + „ lat” (R.5.4).
Podgląd liczy od roku 2026: Monika Kowalska z rocznika 1976 ma 50 lat. Na obrazie 2 w arkuszu stoi „43 lat”, bo zrzut zrobiono w 2019 roku - klucz przyjmuje date('Y'), a także rok 2019 albo 2021 wpisany na stałe.
Typowe błędy
Rok wpisany na stałe (2021 - $row[3]) za rok pokaże zły wiek. date('y') z małą literą zwraca dwie cyfry (np. 21) i daje ujemny wiek.
Krok 35: Skrypt 2: identyfikator z formularza w zapytaniu 2
ok. 4 min
Polecenie z arkusza
Działanie skryptu 2 w panelu prawym wykonywanego po wypełnieniu formularza: Wysyła do bazy danych zapytanie 2, zmodyfikowane w ten sposób, że w warunku zamiast przypisanego numeru id równego 10, jest numer id przesłany z formularza
Drugi blok PHP stoi w bloku prawym, pod linią poziomą:
isset($_POST['id']) - prawdziwe dopiero po kliknięciu „ZOBACZ”. Przy pierwszym wejściu formularz nie był wysłany i skrypt 2 nic nie robi,
$id = $_POST['id']; - wartość pola o nazwie id,
zapytanie 2 z kwerendy.txt z jedną zmianą: w miejscu 10 stoi $id. Całe zapytanie jest w cudzysłowie "…", więc PHP podstawia wartość zmiennej (w apostrofach by tego nie zrobił),
mysqli_fetch_row odczytuje jedyny wiersz wyniku do $row: $row[0] imię, $row[1] nazwisko, $row[2] rok urodzenia, $row[3] opis, $row[4] zdjęcie, $row[5] nazwa hobby.
Klucz przyjmuje R.5.3 za skrypt 1 albo skrypt 2 - punkt jest już przyznany. Podgląd: formularz wysłany z wartością 10, jak na obrazie 2; karta osoby pojawi się w kolejnych krokach.
Krok 36: Skrypt 2: nagłówek z identyfikatorem, imieniem i nazwiskiem
Kryteria:
R.5.6 Skrypt 2 wyświetla nagłówek h2 o treści <id>. <imie> <nazwisko>, gdzie pole <id> jest identyfikatorem pobranym z formularza, a pola <imie>, <nazwisko> są polami pobranymi z bazy
ok. 2 min
Polecenie z arkusza
Wyświetla nagłówek drugiego stopnia o treści <id>. <imie><nazwisko>, gdzie pole <id> jest identyfikatorem pobranym z formularza, a pola <imie>, <nazwisko> są polami pobranymi z bazy
<h2> z identyfikatorem z formularza ($id), kropką i imieniem oraz nazwiskiem z bazy. Podgląd: „10. Roksana Rybnik” (R.5.6).
Krok 37: Skrypt 2: zdjęcie z bazy, tekst alternatywny z formularza
Kryteria:
R.5.7 Skrypt 2 wyświetla zdjęcie tak, że źródłem jest plik o nazwie pobranej z bazy a tekstem alternatywnym jest identyfikator pobrany z formularza
ok. 2 min
Polecenie z arkusza
Wyświetla zdjęcie tak, że źródłem jest plik o nazwie pobranej z bazy a tekstem alternatywnym jest identyfikator pobrany z formularza
<img> z nazwą pliku z bazy w src ($row[4], dla id 10 to osoba3.jpg) i identyfikatorem z formularza w alt - tak każe arkusz, choć zwykle tekst alternatywny opisuje zdjęcie (R.5.7).
Krok 38: Skrypt 2: trzy paragrafy - rok urodzenia, opis, hobby
Kryteria:
R.5.8 Skrypt 2 wyświetla trzy paragrafy (akapity) z rokiem urodzenia, opisem i hobby, wartości zostały pobrane z bazy
ok. 2 min
Polecenie z arkusza
Wyświetla trzy paragrafy (akapity) o treści, kolejno: Rok urodzenia: <rok_urodzenia>, gdzie pole <rok_urodzenia> jest pobrane zapytaniem; Opis: <opis>, gdzie pole <opis> jest pobrane zapytaniem; Hobby: <nazwa>, gdzie pole <nazwa> jest pobrane zapytaniem
Trzy echo, każdy z osobnym <p>, w kolejności z arkusza. Nazwa hobby pochodzi z tabeli hobby - dzięki złączeniu w zapytaniu 2 jest w tym samym wierszu co dane osoby. Podgląd: Rok urodzenia: 1995, Opis: pozdrawiam, Hobby: biegi - jak na obrazie 2 (R.5.8).
R.5.2 Ostatnią operacją na bazie jest jej zamknięcie (zgodne ze składnią)
ok. 1 min
Polecenie z arkusza
Po wykonaniu operacji na bazie danych skrypt zamyka połączenie z serwerem.
mysqli_close($connection) na końcu drugiego bloku PHP, poza warunkiem if - wykona się zawsze, także wtedy, gdy formularz nie był wysłany. To ostatnia operacja na bazie (R.5.2): skrypt 1 skończył pracę wcześniej, więc połączenie można zamknąć dopiero tutaj. Strona spełnia wszystkie kryteria arkusza. Dalej dwa kroki o zabezpieczeniach - klucz ich nie wymaga.
Typowe błędy
mysqli_close na końcu skryptu 1 zamyka połączenie, zanim skrypt 2 wyśle zapytanie - po kliknięciu „ZOBACZ” PHP 8 kończy się błędem.
Kroki 40-41 · po tym etapie masz 35 z 35 kryteriów (pkt)
Krok 40: Zabezpieczenie: identyfikator jako liczba i zapytanie przygotowane
ok. 4 min
Polecenie z arkusza
Wysyła do bazy danych zapytanie 2, zmodyfikowane w ten sposób, że w warunku zamiast przypisanego numeru id równego 10, jest numer id przesłany z formularza
Pole type="number" pilnuje cyfr tylko w przeglądarce - żądanie POST można wysłać z pominięciem formularza, z dowolną treścią. Skrypt wkleja ją do zapytania wprost, a to SQL injection. Sprawdzone na wersji z poprzednich kroków:
id o treści 0 OR osoby.id = 2 zamienia warunek w WHERE osoby.id = 0 OR osoby.id = 2 i strona pokazuje inną osobę niż ta, o którą „pytano” - tak samo da się wyciągnąć dane z dowolnej tabeli bazy,
puste pole (kliknięcie „ZOBACZ” bez wpisania liczby) daje zapytanie WHERE osoby.id = ; - PHP 8.1 i nowsze zgłaszają wyjątek mysqli_sql_exception i zamiast strony widać „Fatal error”.
Lekarstwo w dwóch warstwach. filter_var(…, FILTER_VALIDATE_INT) zwraca liczbę całkowitą albo false - wszystko inne dostaje komunikat zamiast zapytania. Do bazy idzie zapytanie przygotowane: mysqli_prepare ze znakiem ? w miejscu identyfikatora, mysqli_stmt_bind_param z typem 'i' (liczba całkowita), mysqli_stmt_execute i mysqli_stmt_get_result, który oddaje wynik do tej samej funkcji mysqli_fetch_row. Podgląd: ten sam „identyfikator-atak” kończy się komunikatem. Stare linie zostają w komentarzu.
Typowe błędy
Liczba liter w 'i' musi równać się liczbie znaków ? i liczbie zmiennych - inaczej mysqli_stmt_bind_param zgłosi błąd.
Krok 41: Zabezpieczenie: brak osoby i htmlspecialchars na wyjściu
ok. 4 min
Polecenie z arkusza
Wyświetla trzy paragrafy (akapity) o treści, kolejno: Rok urodzenia: <rok_urodzenia>, gdzie pole <rok_urodzenia> jest pobrane zapytaniem; Opis: <opis>, gdzie pole <opis> jest pobrane zapytaniem; Hobby: <nazwa>, gdzie pole <nazwa> jest pobrane zapytaniem
Dwie poprawki tego, co strona wypisuje:
identyfikator, którego nie ma w bazie (np. 99), daje pusty wynik - mysqli_fetch_row zwraca null, a odczyt $row[0] z null w PHP 8 kończy się ostrzeżeniami „Trying to access array offset on value of type null” na stronie. Teraz jest na to komunikat,
htmlspecialchars dla każdej wartości z bazy (array_map przepuszcza przez funkcję cały wiersz naraz). Opis jest wpisem użytkownika portalu - sprawdzone: opis Roksany zmieniony na <b>Hej!</b> <script>…</script> bez tej funkcji wyświetla się pogrubiony, a skrypt z wpisu się wykonuje (atak XSS). Podgląd pokazuje ten sam wpis dosłownie, ze znacznikami.
Identyfikator nie potrzebuje htmlspecialchars: po filter_var jest liczbą całkowitą. Na egzaminie wystarczy wersja sprzed etapu „Dobre praktyki” - klucz tych zabezpieczeń nie sprawdza.
Typowe błędy
htmlspecialchars przy zapisie do bazy zamiast przy wypisywaniu psuje dane (w bazie lądują encje). Dane w bazie zostają surowe, kodowanie robi się na wyjściu.
Krok 42 · po tym etapie masz 35 z 35 kryteriów (pkt)
Krok 42: Plik przeglądarka.txt i nagranie płyty
ok. 5 min
Polecenie z arkusza
UWAGA: po zakończeniu pracy utwórz w folderze z numerem PESEL lub w przypadku jego braku w folderze z innym numerem, którym został podpisany arkusz plik tekstowy o nazwie przeglądarka.txt. Zapisz w nim nazwę przeglądarki internetowej, w której weryfikowałeś poprawność działania witryny. Nagraj płytę z rezultatami pracy.
W przeglądarka.txt wpisujesz nazwę przeglądarki, w której sprawdzasz stronę przez http://localhost/….
W folderze z numerem zdającego muszą być: egzamin.jpg, kw1.png, kw2.png, kw3.png, kw4.png, kwerendy.txt, osoba1.jpg - osoba8.jpg, przeglądarka.txt, settings.html, styl4.css i users.php (plik dane.sql z archiwum może zostać). Nagraj płytę, sprawdź jej odczyt, opisz ją numerem zdającego i zostaw na stanowisku w pudełku razem z arkuszem. Pusta lub nieczytelna płyta to zero punktów za wszystkie pięć rezultatów.
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.