INF.03-04-25.06-SGCzerwiec 2025 · 52 kroki · ok. 106 min · 41 kryteriów z klucza
Rozwiązanie krok po kroku: Portal firmy przewozowej (Wersja podstawowa)
Rozwiązanie buduje portal firmy przewozowej: baza przewozy z czterema kwerendami (SELECT, INSERT, DELETE, JOIN), grafika auto.png z przezroczystym tłem i napisem w perspektywie, strona przewozy.php z tabelą zadań, formularzem i listą usług, style w styles.css oraz dwa skrypty PHP: lista zadań z odnośnikami „Usuń” (kasowanie rekordu) i dodawanie zadania z formularza.
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ą. Kod PHP jest najpierw pisany funkcjami z tabeli 1 arkusza (proceduralne mysqli), a na końcu zostaje przerobiony na wersję z prawdziwych projektów: zapytania przygotowane z bindowaniem, escapowanie danych i przekierowanie po zapisie. W tym arkuszu nie ma JavaScriptu.
Obok jest druga wersja tego rozwiązania - zakładka Nowoczesny kod (grid, PDO): te same punkty, ale sekcje na siatce CSS, PHP na PDO, sprawdzanie id i daty przy wejściu oraz przekierowanie po zapisie od pierwszej linijki.
Grafiki i baza pochodzą z prawdziwego archiwum pliki11 (załącznik do pobrania na stronie arkusza, hasło w treści zadania). Grafika auta jest złożona z plików archiwum tak, jak każe arkusz; zrzuty wyników zapytań są odtworzone z tej bazy - na egzaminie robisz je sam, z całego ekranu.
Jak liczą się punkty. Klucz ma 41 kryteriów (R.1.1 - R.5.9), każde za 1 punkt. Próg zdania części praktycznej to 75%, czyli 31 punktów - można stracić 10 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 41 kryteriów (pkt)
Krok 1: Baza danych przewozy
Kryteria:
R.1.1 Wykonany import tabeli do bazy danych przewozy , czynność udokumentowana w pliku import.png
ok. 2 min
Polecenie z arkusza
Utwórz bazę danych o nazwie przewozy, z zestawem polskich znaków (np. utf8_unicode_ci).
W przeglądarce otwierasz http://localhost/phpmyadmin, klikasz Nowa w lewym panelu, wpisujesz nazwę przewozy i z listy metod porównywania wybierasz utf8_unicode_ci (arkusz podaje ją jako przykład; utf8_polish_ci też jest poprawne). Tabele z pliku SQL mają jednak zestaw utf8 i porównywanie utf8_polish_ci zapisane we własnej definicji, więc opisy zadań („Przewóz mebli z ulicy Nawrot…”) zachowają ogonki przy każdym wyborze - ustawienie bazy dziedziczą tylko tabele zakładane później bez własnego ustawienia.
Typowe błędy
Nazwa bazy musi być dokładnie przewozy - skrypty PHP łączą się z bazą po nazwie, więc literówka odetnie punkty także za skrypt (R.5.2).
Krok 2: Import tabel z pliku baza.sql
Kryteria:
R.1.1 Wykonany import tabeli do bazy danych przewozy , czynność udokumentowana w pliku import.png
ok. 2 min
Polecenie z arkusza
Z rozpakowanego archiwum zaimportuj tabelę z pliku baza.sql do utworzonej bazy.
Wejdź do bazy przewozy (musi być zaznaczona w lewym panelu), karta Import, Wybierz plik → baza.sql z rozpakowanego archiwum, Wykonaj.
Po imporcie są dwie tabele z ilustracji 1: osoby (id_osoba, imie, nazwisko, telefon - 6 wierszy) i zadania (id_zadania, zadanie, data, osoba_id - 7 wierszy). Kolumna osoba_id w zadaniach to klucz obcy do id_osoba - jedna osoba ma wiele zadań. Uwaga: klucze nazywają się tu id_osoba i id_zadania, nie po prostu id - w zapytaniach trzeba pisać dokładnie tak.
Typowe błędy
Import bez wcześniejszego wejścia do bazy przewozy tworzy tabele w innej bazie (np. test) - skrypty nie znajdą wtedy tabeli zadania.
Krok 3: Zrzut ekranu import.png
Kryteria:
R.1.1 Wykonany import tabeli do bazy danych przewozy , 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ń.
Klawisz Print Screen (cały ekran, nie Alt+PrtScr, który łapie tylko aktywne okno), wklejasz do edytora grafiki i zapisujesz jako import.png w folderze z numerem zdającego. Na zrzucie ma być widać komunikat phpMyAdmin o pomyślnym imporcie albo obie tabele w bazie. W tym arkuszu wszystkie zrzuty są w PNG.
Typowe błędy
Wykadrowany zrzut (samo okno przeglądarki, bez paska zadań) nie jest oceniany - klucz mówi to wprost.
Krok 4: Zapytanie 1: lista zadań
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_zadania, zadanie i datę z tabeli zadania . W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT id_zadania, zadanie, data FROM zadania; oraz wynik działania udokumentowany zrzutem, na którym widocznych jest dokładnie 7 lub 8 rekordów i wyświetlone jedynie kolumny id_zadania, zadanie i data
ok. 2 min
Polecenie z arkusza
Zapytanie 1: wybierające jedynie id_zadania, zadanie i datę z tabeli zadania.
Zakładka SQL w phpMyAdmin (przy zaznaczonej bazie przewozy). Klauzula po klauzuli:
SELECT id_zadania, zadanie, data - dokładnie trzy kolumny z arkusza; SELECT * dołożyłby osoba_id. Kolumna nazywa się data (tak jak w tabeli), choć to słowo kluczowe w wielu językach - w MySQL działa bez problemu.
FROM zadania - bez WHERE, bo chcesz wszystkie zadania.
Wynik: 7 wierszy (wykonujesz zapytania w kolejności z arkusza, więc jeszcze przed wstawieniem i usunięciem - klucz dopuszcza 7 lub 8). Zapytanie kopiujesz do pliku kwerendy.txt i robisz zrzut: kw1.png. To samo zapytanie wyśle skrypt 1.
kwerendy.txtTekstnowy plik
-- Zapytanie 1SELECT id_zadania, zadanie, data FROM zadania;
Podgląd strony po tym kroku
Krok 5: Zapytanie 2: nowe zadanie dla osoby o id 1
Kryteria:
R.1.4 Wykonane zapytanie 2 wstawiające do tabeli zadania rekord z danymi: „spotkanie firmowe”, dnia „2024-05-10” dla osoby o id = 1 . W pliku z kwerendami lub na zrzucie istnieje zapis np.: INSERT INTO zadania VALUES (NULL, 'Spotkanie firmowe', '2024-05- 10', 1); oraz wynik działania udokumentowany zrzutem, na którym widoczny jest komunikat o poprawnie wstawionym rekordzie lub tabela z dodanym rekordem
ok. 3 min
Polecenie z arkusza
Zapytanie 2: wstawiające do tabeli zadania rekord z danymi: „spotkanie firmowe”, dnia „2024-05-10” dla osoby o id równym 1.
Wstawianie wiersza to INSERT INTO zadania (...) VALUES (...). W nawiasie po nazwie tabeli wymieniasz kolumny bez id_zadania - klucz główny ma autonumerację, więc baza sama nada mu wartość 8. Wartości w VALUES w tej samej kolejności: opis zadania i data w apostrofach (napisy; data w formacie RRRR-MM-DD), osoba_id jako liczba 1.
Klucz pokazuje też krótszy wariant INSERT INTO zadania VALUES (NULL, 'spotkanie firmowe', '2024-05-10', 1) - bez listy kolumn trzeba podać wszystkie cztery wartości, a NULL w miejscu klucza oznacza „nadaj sam”. Zrzut kw2.png ma pokazać komunikat o wstawieniu wiersza. Ten sam INSERT (z danymi z formularza) wyśle skrypt 2.
Typowe błędy
Data '10-05-2024' zamiast '2024-05-10' - MySQL zapisze 0000-00-00 albo odrzuci wiersz. Daty zawsze RRRR-MM-DD.
kwerendy.txtTekstzmienione: 3 linie
-- Zapytanie 1SELECT id_zadania, zadanie, data FROM zadania;-- Zapytanie 2INSERT INTO zadania (zadanie, data, osoba_id) VALUES ('spotkanie firmowe', '2024-05-10', 1);
Podgląd strony po tym kroku
Krok 6: Zapytanie 3: usunięcie zadania o id 2
Kryteria:
R.1.5 Wykonane zapytanie 3 usuwające rekord o id_zadania = 2 z tabeli zadania. W pliku z kwerendami lub na zrzucie istnieje zapis np.: DELETE FROM zadania WHERE id_zadania = 2; oraz wynik działania udokumentowany zrzutem, na którym widoczny jest komunikat o poprawnie usuniętym rekordzie lub widoczna tabela bez rekordu o id_zadania = 2
ok. 2 min
Polecenie z arkusza
Zapytanie 3: usuwające rekord o id_zadania równym 2 z tabeli zadania.
DELETE FROM zadania WHERE id_zadania = 2 - usuwanie wierszy to DELETE FROM z nazwą tabeli, a WHERE mówi który. Klucz główny jest liczbą, więc bez apostrofów. phpMyAdmin zapyta o potwierdzenie. Znika „Przewóz telewizora z ulicy Marszałkowskiej…”. Zrzut kw3.png ma pokazać komunikat o usunięciu rekordu.
Ten sam DELETE (z id z odnośnika) wyśle skrypt 1 po kliknięciu „Usuń”.
Typowe błędy
DELETE FROM zadania bez WHERE kasuje wszystkie zadania - i nie ma cofania. Przy DELETE zawsze najpierw uruchom SELECT z tym samym WHERE.
kwerendy.txtTekstzmienione: 3 linie
-- Zapytanie 1SELECT id_zadania, zadanie, data FROM zadania;-- Zapytanie 2INSERT INTO zadania (zadanie, data, osoba_id) VALUES ('spotkanie firmowe', '2024-05-10', 1);-- Zapytanie 3DELETE FROM zadania WHERE id_zadania = 2;
Podgląd strony po tym kroku
Krok 7: Zapytanie 4: kto ma jakie zadanie
Kryteria:
R.1.6 Wykonane zapytanie 4 wybierające jedynie imię, nazwisko i numer telefonu osoby oraz odpowiadające im zadanie i datę z tabeli zadania . W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT imie, nazwisko, telefon, zadanie, data FROM zadania INNER JOIN osoby ON id_osoba = osoba_id; (lub samo JOIN, LEFT JOIN, możliwe też porównanie kluczy po WHERE) oraz wynik działania udokumentowany zrzutem, na którym widoczne są rekordy zawierające jedynie pola imie, nazwisko, telefon, zadanie, data
ok. 3 min
Polecenie z arkusza
Zapytanie 4: wybierające jedynie imię, nazwisko i numer telefonu osoby oraz odpowiadające im zadanie i datę z tabeli zadania. Należy posłużyć się relacją.
Imię, nazwisko i telefon są w osoby, zadanie i data w zadania - „posłużyć się relacją” znaczy złączyć tabele po kluczu.
SELECT imie, nazwisko, telefon, zadanie, data - trzy kolumny z jednej tabeli, dwie z drugiej.
FROM zadania JOIN osoby ON osoby.id_osoba = zadania.osoba_id - do każdego zadania dokładasz wiersz osoby, której id_osoba równa się osoba_id zadania. ON mówi, po których kolumnach pasują wiersze; nazwy poprzedzasz tabelą dla czytelności.
Wynik: 7 wierszy (po wstawieniu i usunięciu z poprzednich kroków), w tym nowe „spotkanie firmowe” przypisane Annie. Zrzut: kw4.png. Plik kwerendy.txt ma komplet czterech zapytań.
Typowe błędy
JOIN bez ON daje iloczyn kartezjański: 7 · 6 = 42 wiersze z przypadkowymi osobami. Klucz przyjmuje INNER JOIN, LEFT JOIN i starszy zapis FROM zadania, osoby WHERE id_osoba = osoba_id.
kwerendy.txtTekstzmienione: 3 linie
-- Zapytanie 1SELECT id_zadania, zadanie, data FROM zadania;-- Zapytanie 2INSERT INTO zadania (zadanie, data, osoba_id) VALUES ('spotkanie firmowe', '2024-05-10', 1);-- Zapytanie 3DELETE FROM zadania WHERE id_zadania = 2;-- Zapytanie 4SELECT imie, nazwisko, telefon, zadanie, data FROM zadania JOIN osoby ON osoby.id_osoba = zadania.osoba_id;
Podgląd strony po tym kroku
Etap 2: Grafika i strona HTML
Kroki 8-27 · po tym etapie masz 21 z 41 kryteriów (pkt)
Krok 8: Grafika auto.png: przezroczyste tło i szerokość 400 px
Kryteria:
R.2.1 Grafika auto.png zawiera przezroczyste tło oraz jej szerokość to 400 px, odpowiadająca temu wysokość to 200 px (± 5 px)
ok. 5 min
Polecenie z arkusza
Grafika o nazwie auto w formacie PNG, przygotowana na podstawie grafiki auto1.jpg. Tło przezroczyste. Przeskalowana z zachowaniem proporcji do szerokości 400 px.
Plik auto1.jpg z archiwum ma 1200 × 600 px i białe tło. W GIMP-ie:
Warstwa → Przezroczystość → Dodaj kanał alfa - JPEG nie ma przezroczystości, trzeba ją najpierw włączyć,
narzędzie Zaznaczenie rozmyte (różdżka) z małym progiem, klik w białe tło, Delete - tło znika (szachownica). Próg musi być mały, bo auto też jest prawie białe,
Obraz → Skaluj obraz, szerokość 400 px z zamkniętym łańcuchem proporcji - wysokość wychodzi 200 px (klucz: 200 ±5),
Plik → Wyeksportuj jakoauto.png - tylko PNG zachowa przezroczystość.
Typowe błędy
Eksport do .jpg zabija przezroczystość (tło wraca białe) - klucz R.2.1 sprawdza przezroczyste tło. Zbyt duży próg różdżki „zjada” białą karoserię razem z tłem.
Podgląd strony po tym kroku
Krok 9: Grafika auto.png: napisy na boku auta w perspektywie
Kryteria:
R.2.2 Tekst z grafiki napisy.jpg jest nałożony na bok samochodu utworzonej grafiki. Rozmiar tekstu nie przekracza rozmiaru boku samochodu oraz transformowano jego kształt jak na ilustracji 2 w arkuszu egzaminacyjnym
ok. 6 min
Polecenie z arkusza
Na boku samochodu wklejone napisy z pliku napisy.jpg dopasowane do wielkości i położenia samochodu, zgodnie z ilustracją 2 (należy zastosować perspektywę).
Plik z napisami (w archiwum napisy.png, zielone tło) wczytujesz jako nową warstwę (Plik → Otwórz jako warstwy). Zielone tło usuwasz różdżką (tu próg może być większy - kolor jest jednolity), warstwę skalujesz tak, żeby napis zmieścił się na boku furgonu (ok. 180 px szerokości), i przesuwasz na bok auta.
Perspektywa: narzędzie Perspektywa (Shift+P) - przeciągasz narożniki warstwy tak, żeby napis „położył się” na boku auta: lewa krawędź nieco wyżej, prawa niżej i mniejsza - zgodnie z kierunkiem, w którym stoi auto na ilustracji 2. Klucz R.2.2 sprawdza, że tekst nie wychodzi poza bok samochodu i że jego kształt został przekształcony. Eksport ponownie do auto.png.
⚠️ Rozbieżność arkusz-załącznik: arkusz mówi o pliku napisy.jpg, a w archiwum pliki11 jest napisy.png - to ten sam obraz, użyj pliku z archiwum.
Typowe błędy
Napis wklejony „płasko”, bez perspektywy, nie spełnia R.2.2 nawet jeśli leży na boku auta - arkusz mówi wprost „należy zastosować perspektywę”. Napis większy niż bok auta też odpada.
R.2.3 W pliku przewozy.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. 2 min
Polecenie z arkusza
Składa się ze strony o nazwie przewozy.php. Zapisana w języku HTML5.
W edytorze zaznaczającym składnię tworzysz plik przewozy.php w folderze zdającego, obok auto.png i zrzutów. Rozszerzenie .php, bo dojdą skrypty PHP - na razie to czysty HTML.
Pierwsza linia <!DOCTYPE html> mówi przeglądarce, że dokument jest w HTML5. Dalej szkielet każdej strony: <html>, a w nim <head> (informacje o stronie) i <body> (to, co widać w oknie).
R.2.3 W pliku przewozy.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.
Atrybut lang="pl" w znaczniku <html>. Czytnik ekranu czyta wtedy tekst polską wymową, a przeglądarka dobiera słownik. Na stronie nic się nie zmienia - to informacja dla programów.
R.2.3 W pliku przewozy.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 pierwsza rzecz w <head>. „Jawnie” znaczy: wpisane w kodzie, nie zdane na domysł przeglądarki. Bez tej linii „Półdarmo” i opisy zadań z bazy mogłyby wyświetlić się jako krzaki. Plik trzeba też zapisać w UTF-8.
Tytuł strony „Firma Przewozowa” widoczny na karcie przeglądarki.
<title> w <head> - napis na karcie przeglądarki, nie nagłówek na stronie (ten będzie inny: „Firma przewozowa Półdarmo”). Klucz R.2.4 sprawdza tekst „Firma Przewozowa”.
R.3.2 Strona zawiera działające połączenie z zewnętrznym arkuszem stylów o nazwie styles.css formatowanie CSS pochodzi jedynie z tego arkusza
ok. 1 min
Polecenie z arkusza
Arkusz stylów w pliku o nazwie styles.css prawidłowo połączony z kodem strony.
<link rel="stylesheet" href="styles.css"> w <head>. Uwaga na nazwę: styles.css z „s” na końcu - klucz R.3.2 sprawdza dokładnie tę nazwę. Plik napiszesz po HTML.
Typowe błędy
href="styl.css" z przyzwyczajenia po innych arkuszach - przeglądarka nie znajdzie pliku i cały rezultat R.4 przepada.
R.2.5 W sekcji body zdefiniowany jest podział na bloki za pomocą semantycznych znaczników HTML 5: header, nav, main -> 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. Zawartość bloku głównego: sekcja lewa i prawa.
Z ilustracji 3: blok nagłówkowy, pod nim nawigacyjny, potem blok główny z dwiema sekcjami obok siebie (lewa szeroka, prawa wąska), na dole stopka. Klucz R.2.5 wymienia znaczniki wprost: <header>, <nav>, <main> z dwoma <section> w środku, <footer>.
Sekcje dostają identyfikatory left i right, bo każda będzie stylowana inaczej. Nazwy identyfikatorów, klas i zmiennych piszesz w tym rozwiązaniu po angielsku (arkusz dopuszcza polski lub angielski, wymaga konsekwencji); wyjątkiem są nazwy narzucone przez arkusz (przewozy.php, styles.css, kolumny bazy). To, że sekcje staną obok siebie, zrobi CSS.
Typowe błędy
Sekcje poza <main> nie spełniają R.2.5 - klucz mówi „main → section + section”, czyli sekcje wewnątrz bloku głównego.
R.2.6 Zapisane w bloku nagłówkowym <h1>: "Firma przewozowa Półdarmo", w sekcji lewej <h2>: "Zadania do wykonania", w sekcji prawej <h3>: "Nasza specjalność" oraz w stopce paragraf z numerem zdającego
ok. 1 min
Polecenie z arkusza
Zawartość bloku nagłówkowego: nagłówek pierwszego stopnia o treści: „Firma przewozowa Półdarmo”.
Pierwszego stopnia to <h1>. Egzaminator sprawdza dokładnie ten znacznik - w sekcjach będą <h2> i <h3>, więc nie mieszaj stopni (klucz R.2.6 wymienia wszystkie trzy).
Krok 17: Blok nawigacyjny: odnośniki do zrzutów kwerend
Kryteria:
R.3.6 Wybranie przynajmniej jednego odnośnika powoduje wyświetlenie lub pobranie zrzutu ekranu z kwerendą
ok. 2 min
Polecenie z arkusza
Zawartość bloku nawigacyjnego: odnośniki o treści kwerenda1, kwerenda2, kwerenda3, kwerenda4 do plików zawierających zrzuty ekranu kwerend.
Cztery odnośniki <a> - tekst z arkusza, a w href nazwy plików ze zrzutami z etapu bazy danych (kw1.png … kw4.png), które leżą w tym samym folderze. Kliknięcie otwiera zrzut w przeglądarce (klucz R.3.6 sprawdza przynajmniej jeden). W podglądzie kliknięcia są zablokowane - podgląd pokazuje jedną stronę.
Typowe błędy
href="C:\Users\...\kw1.png" działa tylko na komputerze autora. Ścieżka ma być względna, a plik obok przewozy.php.
Krok 18: Sekcja lewa: nagłówek „Zadania do wykonania”
Kryteria:
R.2.6 Zapisane w bloku nagłówkowym <h1>: "Firma przewozowa Półdarmo", w sekcji lewej <h2>: "Zadania do wykonania", w sekcji prawej <h3>: "Nasza specjalność" oraz w stopce paragraf z numerem zdającego
ok. 1 min
Polecenie z arkusza
Zawartość sekcji lewej: nagłówek drugiego stopnia o treści: „Zadania do wykonania”.
Krok 19: Sekcja lewa: tabela z wierszem nagłówkowym
Kryteria:
R.2.8 W sekcji lewej umieszczona tabela o 3 kolumnach i wierszu z komórkami nagłówkowymi, (w skrypcie lub HTML) zastosowane znaczniki <table>, <tr>, <th>, <td>
ok. 2 min
Polecenie z arkusza
Tabela o trzech kolumnach i wierszu z komórkami nagłówkowymi o treści: „Zadanie do wykonania”, „Data realizacji”, „Akcja”. Tabela jest wypełniona i modyfikowana przez skrypt 1. Jest dodatkowo modyfikowana przez skrypt 2.
<table> z jednym wierszem <tr> i komórkami nagłówkowymi<th> - nie <td>. Kolejne wiersze (zadania z bazy, z odnośnikiem „Usuń” w trzeciej kolumnie) dopisze skrypt 1; skrypt 2 „modyfikuje” tabelę pośrednio - dodaje rekord do bazy, który skrypt 1 od razu wyświetli.
R.3.3 Formularz wysyła dane metodą POST do tego samego pliku (poprawne również bez atrybutu action)
ok. 1 min
Polecenie z arkusza
Formularz wysyłający dane do tego samego pliku metodą bezpieczną, obsługiwany przez skrypt 2.
„Metoda bezpieczna” w rozumieniu arkusza to POST - dane nie pojawiają się w adresie strony, jak przy GET. W specyfikacji HTTP „bezpieczne” są co prawda metody, które niczego nie zmieniają na serwerze (GET i HEAD), ale arkusz używa tego słowa w innym sensie, a klucz wymaga POST. „Do tego samego pliku” to brak atrybutu action (przeglądarka wysyła formularz na adres bieżącej strony) albo action="przewozy.php" - klucz R.3.3 przyjmuje oba warianty.
Typowe błędy
method="get" (albo brak method, bo GET jest domyślny) wyśle dane w adresie - arkusz i klucz wymagają tu POST, a skrypt czytający $_POST nic nie dostanie.
R.2.7 W sekcji lewej umieszczony formularz zawierający dwa pola edycyjne i przycisk "Dodaj" oraz zastosowana przynajmniej jedna etykieta <label>
ok. 2 min
Polecenie z arkusza
Pole edycyjne, poprzedzone etykietą „Zadanie do wykonania: ”.
Etykieta <label> z atrybutem for wskazującym id pola - kliknięcie w napis ustawia kursor w polu (klucz R.2.7 wymaga przynajmniej jednej etykiety). Pole <input type="text"> z name="task" - pod tą nazwą wartość trafi do PHP. „Poprzedzone etykietą” to etykieta przed polem, w tej samej linii, jak na ilustracji 2; <br> po polu przenosi następną parę do nowej linii.
R.2.7 W sekcji lewej umieszczony formularz zawierający dwa pola edycyjne i przycisk "Dodaj" oraz zastosowana przynajmniej jedna etykieta <label>
R.3.5 Pole edycyjne do wpisania daty jest typu date
ok. 1 min
Polecenie z arkusza
Pole edycyjne przeznaczone do wpisania daty, poprzedzone etykietą „Data realizacji: ”.
„Pole przeznaczone do wpisania daty” to <input type="date"> - przeglądarka pokazuje kalendarz, a wartość wysyła zawsze w formacie RRRR-MM-DD, dokładnie takim, jakiego potrzebuje baza. Klucz R.3.5 sprawdza typ date wprost. Na ilustracji 2 widać pole z maską dd.mm.rrrr - to właśnie type="date".
Typowe błędy
type="text" dla daty to strata R.3.5, a użytkownik mógłby wpisać 10.05.2024 - MySQL nie przyjmie takiego formatu.
R.2.7 W sekcji lewej umieszczony formularz zawierający dwa pola edycyjne i przycisk "Dodaj" oraz zastosowana przynajmniej jedna etykieta <label>
R.3.4 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 „Dodaj” wysyłający dane z formularza.
<button type="submit">Dodaj</button> wewnątrz formularza wysyła go po kliknięciu. Klucz R.3.4 przyjmuje też <input type="submit" value="Dodaj">. Sekcja lewa jest kompletna.
R.2.9 W sekcji prawej umieszczony obraz auta z tekstem alternatywnym "auto firmowe"
ok. 1 min
Polecenie z arkusza
Zawartość sekcji prawej: obraz auto.png z tekstem alternatywnym o treści: „auto firmowe”.
<img src="auto.png" alt="auto firmowe"> - plik PNG złożony w etapie grafiki, leżący obok przewozy.php. Tekst alternatywny dokładnie z arkusza (klucz R.2.9). Dzięki przezroczystemu tłu auto „stoi” na zielonym tle sekcji - to zobaczysz po CSS.
R.2.6 Zapisane w bloku nagłówkowym <h1>: "Firma przewozowa Półdarmo", w sekcji lewej <h2>: "Zadania do wykonania", w sekcji prawej <h3>: "Nasza specjalność" oraz w stopce paragraf z numerem zdającego
ok. 1 min
Polecenie z arkusza
Nagłówek trzeciego stopnia o treści: „Nasza specjalność”.
Lista nieuporządkowana <ul> z pięcioma <li> - teksty dokładnie z arkusza, w tej kolejności. Klucz R.2.10 liczy elementy: ma być pięć. Punktory zmienisz w CSS na okręgi.
Typowe błędy
<ol> to lista uporządkowana (numerowana). Arkusz mówi „punktowana (nieuporządkowana)”, czyli <ul>.
R.2.6 Zapisane w bloku nagłówkowym <h1>: "Firma przewozowa Półdarmo", w sekcji lewej <h2>: "Zadania do wykonania", w sekcji prawej <h3>: "Nasza specjalność" oraz w stopce paragraf z numerem zdającego
ok. 1 min
Polecenie z arkusza
Zawartość stopki: paragraf o treści „Stronę wykonał: ”, dalej wstawiony numer zdającego.
Paragraf <p> w <footer>. W tym rozwiązaniu po dwukropku stoi nazwisko autora - na egzaminie wpisujesz swój numer zdającego (PESEL), bo tego sprawdza klucz R.2.6. Struktura HTML strony jest kompletna - dalej style.
Kroki 28-38 · po tym etapie masz 32 z 41 kryteriów (pkt)
Krok 28: Plik styles.css: ciało strony
Kryteria:
R.4.1 Ustawione dla selektora body: kolor tła #FFCC99, krój czcionki Garamond, wyrównanie tekstu do środka
ok. 2 min
Polecenie z arkusza
Dla selektora ciała strony: kolor tła #FFCC99, krój czcionki Garamond, wyrównanie tekstu do środka.
Utwórz plik styles.css obok przewozy.php. Selektor znacznika body - arkusz zastrzega, że ciało strony stylujesz wyłącznie selektorem znacznika. Kolor szesnastkowy #FFCC99 (brzoskwiniowy) przepisujesz z arkusza, krój Garamond, text-align: center wyśrodkowuje tekst całej strony (dziedziczą go wszystkie bloki). W podglądzie zmienia się tło i czcionka - znak, że <link> zadziałał.
styles.cssCSSnowy plik
body {background-color: #FFCC99;font-family: Garamond;text-align: center;}
Podgląd strony po tym kroku (przewozy.php)
Krok 29: Nagłówek i stopka: wspólne style
Kryteria: , , ,
R.4.2 Ustawione kolory tła: DarkGreen dla bloku nagłówkowego i stopki oraz MediumSeaGreen dla bloku nawigacyjnego i sekcji prawej
R.4.3 Ustawiony biały kolor czcionki dla bloków nagłówkowego i stopki oraz dla selektora a
R.4.4 Ustawione wyrównanie tekstu do lewej dla sekcji prawej, odstępy między wyrazami 90px dla bloku nawigacyjnego oraz rozmiar czcionki 140% dla bloku nagłówkowego i stopki
R.4.7 Ustawione marginesy wewnętrzne (padding) dla bloku nagłówkowego i stopki 30px, dla bloku nawigacyjnego 20px, dla komórek tabeli 5px oraz marginesy zewnętrzne (margin) dla selektora table górny i dolny:30px, pozostałe: auto
ok. 2 min
Polecenie z arkusza
Dla bloku nagłówkowego i stopki: kolor tła DarkGreen, biały kolor czcionki, marginesy wewnętrzne: 30 px, rozmiar czcionki 140%.
Wspólne cechy dwóch bloków piszesz raz - selektory po przecinku header, footer. Margines wewnętrzny to padding (zewnętrzny byłby margin). font-size: 140% powiększa tekst względem domyślnego; dla <h1> w nagłówku to 140% podstawy, od której nagłówek liczy swoją wielkość - stąd duży napis jak na ilustracji 2.
Typowe błędy
margin: 30px zamiast padding: 30px - odstęp będzie na zewnątrz zielonego pola. Kryterium R.4.7 mówi o marginesach wewnętrznych.
Krok 30: Blok nawigacyjny: tło, odstępy między wyrazami, margines wewnętrzny
Kryteria: , ,
R.4.2 Ustawione kolory tła: DarkGreen dla bloku nagłówkowego i stopki oraz MediumSeaGreen dla bloku nawigacyjnego i sekcji prawej
R.4.4 Ustawione wyrównanie tekstu do lewej dla sekcji prawej, odstępy między wyrazami 90px dla bloku nawigacyjnego oraz rozmiar czcionki 140% dla bloku nagłówkowego i stopki
R.4.7 Ustawione marginesy wewnętrzne (padding) dla bloku nagłówkowego i stopki 30px, dla bloku nawigacyjnego 20px, dla komórek tabeli 5px oraz marginesy zewnętrzne (margin) dla selektora table górny i dolny:30px, pozostałe: auto
ok. 2 min
Polecenie z arkusza
Dla bloku nawigacyjnego: kolor tła MediumSeaGreen, odstępy między wyrazami: 90 px, marginesy wewnętrzne 20 px.
Selektor nav. „Odstępy między wyrazami” to właściwość word-spacing - rozsuwa odnośniki, bo każdy z nich to jedno słowo („kwerenda1”, „kwerenda2”…) oddzielone spacją. Dzięki temu cztery odnośniki rozkładają się na szerokości paska jak na ilustracji 2, bez żadnych dodatkowych znaczników.
Typowe błędy
letter-spacing rozsuwa litery, nie wyrazy - napisy rozciągną się, a odnośniki zostaną obok siebie. Arkusz mówi „między wyrazami”.
R.4.6 Ustawiona szerokość sekcji lewej na 70% oraz sekcji prawej na 30% (width lub grid- template-columns lub flex)
ok. 1 min
Polecenie z arkusza
Dla sekcji lewej: szerokość 70%, wysokość 500 px.
Selektor identyfikatora #left. Szerokość w procentach liczy się względem rodzica (<main>), wysokość jest stała. Sekcje stoją jeszcze jedna pod drugą - obok siebie ustawisz je za dwa kroki.
R.4.2 Ustawione kolory tła: DarkGreen dla bloku nagłówkowego i stopki oraz MediumSeaGreen dla bloku nawigacyjnego i sekcji prawej
R.4.4 Ustawione wyrównanie tekstu do lewej dla sekcji prawej, odstępy między wyrazami 90px dla bloku nawigacyjnego oraz rozmiar czcionki 140% dla bloku nagłówkowego i stopki
R.4.5 Ustawiona wysokość obu sekcji na 500px
R.4.6 Ustawiona szerokość sekcji lewej na 70% oraz sekcji prawej na 30% (width lub grid- template-columns lub flex)
ok. 1 min
Polecenie z arkusza
Dla sekcji prawej: kolor tła MediumSeaGreen, szerokość 30%, wysokość 500 px, wyrównanie tekstu do lewej.
Selektor #right. text-align: leftnadpisuje wyśrodkowanie odziedziczone z body - nagłówek i lista w prawej sekcji są wyrównane do lewej, jak na ilustracji 2. 70% + 30% = 100% - sekcje zmieszczą się obok siebie.
R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z ilustracją 3 w arkuszu egzaminacyjnym (prawidłowo zastosowane właściwości CSS decydujące o układzie bloków na stronie, np. float i clear albo display flex albo grid albo position)
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.
Dopisz float: left do obu sekcji - pływają w lewo i ustawiają się obok siebie (70% + 30% w jednym wierszu). Stopka dostaje osobną regułę clear: both, żeby wróciła pod pływające sekcje (wspólna reguła header, footer zostaje). Układ zgadza się z ilustracją 3. Klucz R.3.1 przyjmuje też display: flex albo grid.
Typowe błędy
Bez clear: both stopka wjeżdża obok sekcji albo pod nie, a <main> ma zerową wysokość - układ przestaje zgadzać się z ilustracją 3.
R.4.7 Ustawione marginesy wewnętrzne (padding) dla bloku nagłówkowego i stopki 30px, dla bloku nawigacyjnego 20px, dla komórek tabeli 5px oraz marginesy zewnętrzne (margin) dla selektora table górny i dolny:30px, pozostałe: auto
R.4.8 Ustawione dla tabeli obramowanie 1px dashed DarkGreen
ok. 2 min
Polecenie z arkusza
Dla selektora tabeli: obramowanie linią kreskowaną o grubości 1 px i kolorze DarkGreen, tabela wyśrodkowana, marginesy zewnętrzne górny i dolny 30 px.
Selektor znacznika table (arkusz tego wymaga). Skrót border: 1px dashed DarkGreen łączy grubość, styl (kreskowana) i kolor. margin: 30px auto to dwie wartości: 30 px góra i dół, auto lewa i prawa - a auto właśnie wyśrodkowuje tabelę (klucz R.4.7: „górny i dolny 30px, pozostałe: auto”).
Typowe błędy
text-align: center z body wyśrodkowuje tekst w komórkach, nie samą tabelę - tabela bez margin: auto stoi przy lewej krawędzi sekcji.
R.4.7 Ustawione marginesy wewnętrzne (padding) dla bloku nagłówkowego i stopki 30px, dla bloku nawigacyjnego 20px, dla komórek tabeli 5px oraz marginesy zewnętrzne (margin) dla selektora table górny i dolny:30px, pozostałe: auto
ok. 1 min
Polecenie z arkusza
Dla komórek tabeli i komórek nagłówkowych: marginesy wewnętrzne 5 px.
Selektory td, th po przecinku - zwykłe i nagłówkowe komórki dostają padding: 5px, dzięki czemu tekst nie klei się do krawędzi. Efekt zobaczysz w pełni, gdy skrypt wypełni tabelę.
R.4.10 W momencie, gdy kursor myszy znajdzie się na wierszu tabeli jego kolor tła zmienia się na DarkGreen, a kolor czcionki na biały
ok. 1 min
Polecenie z arkusza
W momencie, gdy kursor myszy znajdzie się na wierszu tabeli jego kolor tła zmienia się na DarkGreen, a kolor czcionki na biały.
Pseudoklasa :hover na wierszu tr. Efekt z ilustracji 2 (podświetlony trzeci wiersz) zobaczysz, gdy skrypt wypełni tabelę; wiersz nagłówkowy też reaguje. Arkusz stylów jest kompletny.
Kroki 39-48 · po tym etapie masz 41 z 41 kryteriów (pkt)
Krok 39: Połączenie z bazą danych
Kryteria: , ,
R.5.1 Skrypt napisany w języku PHP i zawiera przynajmniej jedną zgodną z treścią zadania instrukcję
R.5.2 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy przewozy
R.5.9 W skryptach zastosowana przynajmniej jedna zmienna oraz nazwy wszystkich zmiennych są w języku polskim lub angielskim i odzwierciedlają cel zastosowania
ok. 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 przewozy.
Kod PHP piszesz między <?php a ?> - serwer wykonuje go, zanim strona trafi do przeglądarki. Połączenie otwierasz raz, na samym początku pliku, i trzymasz w zmiennej $connection (znacząca, angielska nazwa - R.5.9), z której skorzystają oba skrypty. mysqli_connect z tabeli 1 arkusza przyjmuje kolejno: serwer, użytkownika, hasło (puste) i nazwę bazy.
Od tego kroku podgląd to wynik działania strony po stronie serwera (PHP nie wykonuje się w przeglądarce) - na razie wygląda jak wcześniej, bo skrypt niczego nie wypisuje.
Typowe błędy
Kolejność argumentów ma znaczenie: mysqli_connect('localhost', 'root', 'przewozy') podaje „przewozy” jako HASŁO. Nazwa bazy jest czwarta. Plik sprawdzaj przez http://localhost/…, nie otwierając go z dysku.
R.5.4 Skrypty zawierają instrukcje wysyłające do bazy danych co najmniej jedno zapytanie z: zapytanie 1 lub zapytanie 2 zmodyfikowane tak, że zawiera dane z formularza, lub zapytanie 3 zmodyfikowane tak, że usuwany jest rekord o id_zadania odpowiadającym wierszowi tabeli
ok. 2 min
Polecenie z arkusza
Skrypt 1: wysyła do bazy danych zapytanie 1.
Skrypt 1 piszesz wewnątrz<table>, pod wierszem nagłówkowym - tam mają trafić wiersze. mysqli_query($connection, ...) wysyła zapytanie i zwraca wynik, który zapisujesz w $tasks. Treść to dokładnie zapytanie 1 z kwerendy.txt. Kolejność kolumn w SELECT zaraz będzie ważna: $row[0] to id_zadania, $row[1] zadanie, $row[2] data.
R.5.5 Skrypt 1 wyświetla w wierszach tabeli dane zwrócone zapytaniem, dane są wpisane do pierwszych dwóch kolumn tabeli
ok. 2 min
Polecenie z arkusza
Kolejne zwrócone zapytaniem pola zadanie i data są wyświetlane w kolejnych wierszach tabeli w dwóch pierwszych kolumnach.
„Kolejne zwrócone wiersze” to pętla. mysqli_fetch_row($tasks) (tabela 1) oddaje kolejny wiersz wyniku jako tablicę numerowaną od zera, a gdy wierszy zabraknie - false. Zapis while ($row = mysqli_fetch_row($tasks)) przypisuje wiersz do $row i od razu sprawdza, czy coś przyszło. Ciało jest jeszcze puste.
Typowe błędy
Pojedyncze = w warunku while jest tu celowe (przypisanie); == porównałoby zmienną z wynikiem i pętla nie zadziałałaby poprawnie.
Krok 42: Skrypt 1: zadanie i data w dwóch pierwszych kolumnach
Kryteria:
R.5.5 Skrypt 1 wyświetla w wierszach tabeli dane zwrócone zapytaniem, dane są wpisane do pierwszych dwóch kolumn tabeli
ok. 2 min
Polecenie z arkusza
Kolejne zwrócone zapytaniem pola zadanie i data są wyświetlane w kolejnych wierszach tabeli w dwóch pierwszych kolumnach.
echo w pętli dokłada jeden <tr> z dwiema komórkami: zadanie ($row[1]) i data ($row[2]) - dokładnie pod nagłówkami „Zadanie do wykonania” i „Data realizacji”. id_zadania ($row[0]) nie wyświetlasz, ale zaraz się przyda. Klucz R.5.5 sprawdza, że dane są w pierwszych dwóch kolumnach.
Podgląd pokazuje stan bazy po zapytaniach 1-4: 7 zadań, bez „Przewozu telewizora” i ze „spotkaniem firmowym” na końcu - jak na ilustracji 2.
Typowe błędy
echo '<tr><td>$row[1]</td>...' w apostrofach wypisze dosłownie „$row[1]” - PHP nie podstawia zmiennych w apostrofach.
R.5.5 Skrypt 1 wyświetla w wierszach tabeli dane zwrócone zapytaniem, dane są wpisane do pierwszych dwóch kolumn tabeli
ok. 3 min
Polecenie z arkusza
Trzecia kolumna tabeli zawiera odnośnik o treści Usuń. Jego wybranie powoduje usunięcie rekordu z bazy za pomocą zmodyfikowanego zapytania 3, w ten sposób że odnośnik dotyczy id_zadania dla wiersza tabeli w którym jest umieszczony.
Trzecia komórka <td> z odnośnikiem <a>. Żeby odnośnik „dotyczył id_zadania swojego wiersza”, wkładasz ten numer do adresu: href='przewozy.php?delete=8' (dla $row[0] równego 8). Część po ? to parametr adresu - po kliknięciu przeglądarka otworzy tę samą stronę, a PHP przeczyta delete=8 z tablicy $_GET. Atrybut w apostrofach, bo cały napis echo jest w cudzysłowie.
Samo usuwanie dopiszesz w następnym kroku - na razie odnośniki tylko prowadzą na stronę z parametrem.
R.5.8 Skrypt zawiera instrukcję pobierającą dane z tablicy globalnej odpowiadającej zastosowanej metodzie formularza ($_POST, $_GET, $_REQUEST )
ok. 2 min
Polecenie z arkusza
Jego wybranie powoduje usunięcie rekordu z bazy za pomocą zmodyfikowanego zapytania 3, w ten sposób że odnośnik dotyczy id_zadania dla wiersza tabeli w którym jest umieszczony.
Usuwanie piszesz na początku pliku, zaraz po połączeniu - przed tabelą, żeby po kliknięciu tabela od razu pokazała stan bez skasowanego zadania. isset($_GET['delete']) (tabela 1) jest prawdziwe tylko wtedy, gdy adres zawiera parametr delete, czyli po kliknięciu „Usuń”. Przy zwykłym wejściu na stronę blok if jest pomijany. Klucz R.5.8 sprawdza użycie tablicy globalnej odpowiadającej metodzie - dla odnośnika to $_GET.
R.5.4 Skrypty zawierają instrukcje wysyłające do bazy danych co najmniej jedno zapytanie z: zapytanie 1 lub zapytanie 2 zmodyfikowane tak, że zawiera dane z formularza, lub zapytanie 3 zmodyfikowane tak, że usuwany jest rekord o id_zadania odpowiadającym wierszowi tabeli
R.5.6 Skrypt 1 usuwa z bazy danych rekord którego id_zadania odpowiada wierszowi odnośnika (należy sprawdzić w phpMyAdmin)
ok. 3 min
Polecenie z arkusza
Jego wybranie powoduje usunięcie rekordu z bazy za pomocą zmodyfikowanego zapytania 3, w ten sposób że odnośnik dotyczy id_zadania dla wiersza tabeli w którym jest umieszczony.
Numer z adresu trafia do $task_id, a zapytanie 3 z kwerendy.txt dostaje go w miejsce 2: DELETE FROM zadania WHERE id_zadania = $task_id. Napis w cudzysłowie, żeby PHP podstawił zmienną. mysqli_query wykonuje DELETE - nic nie wypisuje, efekt widać w tabeli poniżej i w phpMyAdmin (klucz R.5.6 sprawdza właśnie tam).
W podglądzie kliknięcia są zablokowane, ale na żywym serwerze „Usuń” kasuje wiersz i strona przeładowuje się bez niego.
Typowe błędy
Wartość z adresu wstawiona prosto do DELETE to podatność - ktoś może wpisać w adres delete=1 OR 1=1 i skasować wszystko. Na egzaminie wystarcza, w refaktorze to naprawisz.
R.5.8 Skrypt zawiera instrukcję pobierającą dane z tablicy globalnej odpowiadającej zastosowanej metodzie formularza ($_POST, $_GET, $_REQUEST )
ok. 2 min
Polecenie z arkusza
Skrypt 2: dane pobrane z formularza wstawia do bazy za pomocą zmodyfikowanego zapytania 2.
Skrypt 2 też piszesz na początku pliku, pod usuwaniem - dodany rekord ma być widoczny w tabeli od razu po kliknięciu „Dodaj”. Warunek ma dwie części: isset($_POST['task']) (czy formularz został wysłany) i$_POST['task'] != '' (czy pole nie jest puste - bez tego kliknięcie „Dodaj” z pustym polem wstawiałoby puste zadanie). Formularz ma method="post", więc dane są w $_POST (R.5.8).
R.5.4 Skrypty zawierają instrukcje wysyłające do bazy danych co najmniej jedno zapytanie z: zapytanie 1 lub zapytanie 2 zmodyfikowane tak, że zawiera dane z formularza, lub zapytanie 3 zmodyfikowane tak, że usuwany jest rekord o id_zadania odpowiadającym wierszowi tabeli
R.5.7 Skrypt 2 wstawia do bazy danych przewozy nowy rekord z danymi z formularza. (należy sprawdzić w phpMyAdmin)
ok. 3 min
Polecenie z arkusza
Dane pobrane z formularza wstawia do bazy za pomocą zmodyfikowanego zapytania 2. Zadanie jest przypisane do osoby o id równym 1.
Dwie wartości z $_POST (klucze = atrybuty name pól) do zmiennych $task i $date. Zapytanie 2 z kwerendy.txt dostaje je w miejsce „spotkanie firmowe” i daty, a osoba_id zostaje stałą 1, jak każe arkusz. Napisy w apostrofach wewnątrz cudzysłowu, żeby PHP podstawił zmienne. Pole type="date" wysyła datę w formacie RRRR-MM-DD, więc baza ją przyjmie.
Klucz R.5.7 sprawdza wstawiony rekord w phpMyAdmin; na stronie nowe zadanie pojawia się na końcu tabeli.
Typowe błędy
Zadanie z apostrofem (np. „Przewóz z ul. O'Connora”) rozrywa napis SQL i wstawianie kończy się błędem - objaw tej samej podatności co przy usuwaniu.
R.5.3 Ostatnią operacją na bazie jest jej zamknięcie
ok. 1 min
Polecenie z arkusza
Na końcu działania skrypt zamyka połączenie z serwerem.
mysqli_close($connection) na końcu pliku, po wszystkich operacjach na bazie (usuwanie, wstawianie, pobranie listy). Klucz R.5.3 sprawdza, że zamknięcie jest ostatnią operacją na bazie. Strona spełnia wszystkie kryteria arkusza - dalej trzy kroki „jak w prawdziwym projekcie”.
Typowe błędy
mysqli_close tuż po skryptach na górze pliku sprawia, że zapytanie 1 w tabeli idzie po zamkniętym połączeniu - tabela zostaje pusta.
Kroki 49-51 · po tym etapie masz 41 z 41 kryteriów (pkt)
Krok 49: Refaktor: zapytania przygotowane dla usuwania i wstawiania
Kryteria: , ,
R.5.4 Skrypty zawierają instrukcje wysyłające do bazy danych co najmniej jedno zapytanie z: zapytanie 1 lub zapytanie 2 zmodyfikowane tak, że zawiera dane z formularza, lub zapytanie 3 zmodyfikowane tak, że usuwany jest rekord o id_zadania odpowiadającym wierszowi tabeli
R.5.6 Skrypt 1 usuwa z bazy danych rekord którego id_zadania odpowiada wierszowi odnośnika (należy sprawdzić w phpMyAdmin)
R.5.7 Skrypt 2 wstawia do bazy danych przewozy nowy rekord z danymi z formularza. (należy sprawdzić w phpMyAdmin)
ok. 5 min
Polecenie z arkusza
Jego wybranie powoduje usunięcie rekordu z bazy za pomocą zmodyfikowanego zapytania 3. Dane pobrane z formularza wstawia do bazy za pomocą zmodyfikowanego zapytania 2.
Oba skrypty sklejały zapytanie z tekstem od użytkownika - z adresu (delete=) i z formularza. To SQL injection: delete=1 OR 1=1 kasuje całą tabelę. W prawdziwych projektach dane nigdy nie trafiają do zapytania jako tekst, tylko przez zapytanie przygotowane:
mysqli_prepare dostaje zapytanie z ? w miejscu wartości - baza zna jego strukturę, zanim zobaczy dane,
mysqli_stmt_bind_param podpina zmienne pod ? z typami: 'i' dla id (liczba całkowita), 'ss' dla zadania i daty (dwa napisy); stała 1 dla osoba_id zostaje w zapytaniu,
mysqli_stmt_execute wykonuje.
Stare linie zostają w komentarzu // do porównania. Zadanie z apostrofem teraz zapisze się poprawnie. Podgląd bez zmian.
Typowe błędy
Liczba liter w 'ss' musi równać się liczbie zmiennych, a liczba zmiennych - liczbie ? w zapytaniu. Inaczej mysqli_stmt_bind_param kończy się błędem ArgumentCountError i strona się urywa (sprawdzone w PHP 8.2).
przewozy.phpPHPzmienione: 8 linii
<?php$connection = mysqli_connect('localhost', 'root', '', 'przewozy');if (isset($_GET['delete'])) {$task_id = $_GET['delete'];// mysqli_query($connection, "DELETE FROM zadania WHERE id_zadania = $task_id;");$statement = mysqli_prepare($connection, 'DELETE FROM zadania WHERE id_zadania = ?'); mysqli_stmt_bind_param($statement, 'i', $task_id); mysqli_stmt_execute($statement);}if (isset($_POST['task']) && $_POST['task'] != '') {$task = $_POST['task'];$date = $_POST['date'];// mysqli_query($connection, "INSERT INTO zadania (zadanie, data, osoba_id) VALUES ('$task', '$date', 1);");$statement = mysqli_prepare($connection, 'INSERT INTO zadania (zadanie, data, osoba_id) VALUES (?, ?, 1)'); mysqli_stmt_bind_param($statement, 'ss', $task, $date); mysqli_stmt_execute($statement);}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Firma Przewozowa</title> <linkrel="stylesheet"href="styles.css"></head><body> <header> <h1>Firma przewozowa Półdarmo</h1> </header> <nav> <ahref="kw1.png">kwerenda1</a> <ahref="kw2.png">kwerenda2</a> <ahref="kw3.png">kwerenda3</a> <ahref="kw4.png">kwerenda4</a> </nav> <main> <sectionid="left"> <h2>Zadania do wykonania</h2> <table> <tr><th>Zadanie do wykonania</th><th>Data realizacji</th><th>Akcja</th></tr><?php$tasks = mysqli_query($connection, 'SELECT id_zadania, zadanie, data FROM zadania;');while ($row = mysqli_fetch_row($tasks)) {echo"<tr><td>$row[1]</td><td>$row[2]</td><td><a href='przewozy.php?delete=$row[0]'>Usuń</a></td></tr>";}?> </table> <formmethod="post"> <labelfor="task">Zadanie do wykonania: </label> <inputtype="text"id="task"name="task"><br> <labelfor="date">Data realizacji: </label> <inputtype="date"id="date"name="date"><br> <buttontype="submit">Dodaj</button> </form> </section> <sectionid="right"> <imgsrc="auto.png"alt="auto firmowe"> <h3>Nasza specjalność</h3> <ul> <li>Przeprowadzki</li> <li>Przewóz mebli</li> <li>Przesyłki gabarytowe</li> <li>Wynajem pojazdów</li> <li>Zakupy towarów</li> </ul> </section> </main> <footer> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer><?phpmysqli_close($connection);?></body></html>
Krok 50: Refaktor: escapowanie danych przed wypisaniem
Kryteria:
R.5.5 Skrypt 1 wyświetla w wierszach tabeli dane zwrócone zapytaniem, dane są wpisane do pierwszych dwóch kolumn tabeli
ok. 3 min
Polecenie z arkusza
Kolejne zwrócone zapytaniem pola zadanie i data są wyświetlane w kolejnych wierszach tabeli w dwóch pierwszych kolumnach.
Druga strona tego samego problemu: skrypt 1 wypisuje do HTML to, co ktoś wpisał w formularzu. Zadanie <script>alert(1)</script> wykonałoby się w przeglądarce każdego, kto otworzy stronę (XSS). htmlspecialchars() zamienia <, >, ", & na encje, więc tekst wyświetla się dosłownie i nie staje się kodem.
Opis zadania przechodzi przez htmlspecialchars do zmiennej $task; data i id to wartości z bazy w ustalonym formacie, nie wymagają tego. Stare echo zostaje w komentarzu. Podgląd bez zmian.
Typowe błędy
htmlspecialchars przy zapisie do bazy (w skrypcie 2) zamiast przy wypisywaniu to częsty błąd - baza dostaje zniekształcone dane. Escapujesz na wyjściu.
Na końcu działania skrypt zamyka połączenie z serwerem.
Dwa drobne, ale realne problemy wersji egzaminacyjnej: po kliknięciu „Usuń” adres strony zostaje z ?delete=8, więc odświeżenie (F5) próbuje usunąć jeszcze raz; po „Dodaj” odświeżenie wysyła formularz ponownie i dubluje zadanie. Rozwiązanie z prawdziwych projektów to wzorzec „zapisz i przekieruj”: po udanym DELETE albo INSERT wysyłasz header('Location: przewozy.php') - przeglądarka od razu otwiera czysty adres metodą GET - i exit, żeby nie wypisywać już nic więcej.
header() musi stać przed jakimkolwiek HTML-em - w tym rozwiązaniu skrypty są na początku pliku, więc warunek jest spełniony. Podgląd bez zmian, ale F5 jest teraz bezpieczne.
Typowe błędy
header() po wypisaniu choćby jednej spacji przed <?php daje błąd „headers already sent” - to częsty powód, dla którego przekierowanie „nie działa”.
Krok 52 · po tym etapie masz 41 z 41 kryteriów (pkt)
Krok 52: 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 przeglądarka.txt wpisujesz nazwę przeglądarki, w której sprawdzasz stronę przez http://localhost/….
W folderze z numerem zdającego muszą być: auto.png, import.png, kw1.png, kw2.png, kw3.png, kw4.png, kwerendy.txt, przeglądarka.txt, przewozy.php, styles.css. Nagraj płytę, sprawdź jej odczyt i opisz numerem zdającego. 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.