Serwis używa plików cookies do analizy ruchu i poprawy działania. Szczegóły
EE.09-03-20.06-SGCzerwiec 2020 · 37 kroków · ok. 73 min · 34 kryteria z klucza
Rozwiązanie krok po kroku: Kalendarz z wpisami na lipiec (Wersja podstawowa)
Kalendarz na lipiec 2020: baza egzamin5 z tabelą zadania (import pliku baza5.sql), cztery zapytania - miesiąc i rok dla jednej daty, zadania z lipca, nazwy miesięcy bez powtórzeń zaczynające się na „l” (DISTINCT, LIKE) oraz zmiana wpisu jednego dnia (UPDATE). Do tego logo przeskalowane z zachowaniem proporcji do wysokości 150 px, strona kalendarz.php z dwoma blokami banera, blokami dni lipca generowanymi przez skrypt i formularzem w stopce, style w styl5.css oraz skrypt PHP, który wyświetla miesiąc i rok, dni lipca i zapisuje wpis z formularza do bazy.
To arkusz EE.09 z czerwca 2020 - kwalifikacji formuły 2017, którą zastąpiła INF.03. Z tego samego archiwum egzamin5.zip korzysta arkusz EE.09-01 z tej sesji (organizer na sierpień) - baza jest ta sama, ale polecenia i klucz inne. 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ą. Skrypt jest najpierw napisany najprościej, funkcjami mysqli z tabeli 1 arkusza, a na końcu sprawdzony na prawdziwym wstrzyknięciu SQL i zabezpieczony.
To jest wersja podstawowa. W zakładce „Nowoczesny kod (flex, grid, PDO)” jest drugi sposób rozwiązania tego samego arkusza: semantyczne znaczniki, układ bez float, PDO z zapytaniem przygotowanym na górze pliku i przekierowanie po zapisie - obie wersje są zgodne z kluczem.
Baza i grafiki pochodzą z oryginalnego archiwum CKE egzamin5.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, 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; 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 34 kryteriów (pkt)
Krok 1: Folder zdającego i archiwum egzamin5.zip
ok. 2 min
Polecenie z arkusza
Na pulpicie znajdziesz archiwum ZIP o nazwie egzamin5.zip zabezpieczone hasłem: kaL(end)arz% Archiwum należy rozpakować. Na pulpicie konta Egzamin utwórz folder. Jako nazwy folderu użyj swojego numeru PESEL. Umieść w tym folderze rozpakowane pliki.
Na pulpicie tworzysz folder nazwany numerem PESEL - w tym rozwiązaniu w jego miejscu stoi imię i nazwisko autora, Bartosz Bryniarski. Archiwum egzamin5.zip rozpakowujesz (np. 7-Zipem), podając hasło kaL(end)arz% - wielkość liter, nawiasy i znak procentu mają znaczenie. W środku są trzy pliki: baza5.sql (tabela do importu), logo1.png i logo2.png. W tym zadaniu potrzebne jest tylko logo1.png - logo2.png należy do bliźniaczego arkusza z organizerem.
Do tego folderu trafia wszystko, co powstanie: zrzuty, kwerendy.txt, plik strony i plik stylów.
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 egzamin5
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 egzamin5
W XAMPP Control Panel klikasz Start przy Apache i MySQL, potem w przeglądarce otwierasz http://localhost/phpmyadmin. Nowa w lewym panelu, nazwa egzamin5, Utwórz.
Krok 3: Import pliku baza5.sql i zrzut import.png
Kryteria:
R.1.1 Wykonano import tabel do bazy danych egzamin5 , czynność udokumentowano plikiem o nazwie import w formacie PNG. Zrzut zawiera cały obszar ekranu monitora z widocznym paskiem zadań
ok. 3 min
Polecenie z arkusza
Do bazy egzamin5 zaimportuj tabele z pliku baza5.sql z rozpakowanego archiwum. Wykonaj zrzut ekranu po imporcie. Zrzut zapisz w formacie PNG pod nazwą import w folderze opisanym numerem PESEL. Nie kadruj zrzutu. Zrzut powinien obejmować cały ekran monitora, z widocznym paskiem zadań. Na zrzucie powinny być widoczne elementy wskazujące na poprawnie wykonany import tabel
Zaznacz bazę egzamin5, karta Import, Wybierz plik → baza5.sql, Wykonaj. Plik zawiera polecenie CREATE TABLE zadania i dane, więc tabela powstaje razem z importem. Po imporcie kliknij tabelę zadania (karta Przeglądaj): 63 wiersze - każdy dzień lipca i sierpnia 2020 oraz jeden dzień listopada. Zrzut całego ekranu z paskiem zadań: import.png.
Nazwy miesięcy w kolumnie miesiac są zapisane bez polskich liter (lipiec, sierpien, listopad) - zapytania muszą porównywać z tym, co jest w tabeli.
Typowe błędy
Zrzut importu w PNG (import.png), zrzuty kwerend w JPG - zły format albo wykadrowany zrzut nie jest oceniany.
Podgląd strony po tym kroku
Krok 4: Zapytanie 1: miesiąc i rok dla 1 lipca
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 miesiac i rok z tabeli zadania dla zadania z datą 2020-07-01. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT miesiac, rok FROM zadania WHERE dataZadania = '2020-07- 01'; oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczny jest dokładnie jeden rekord z danymi: lipiec 2020
ok. 3 min
Polecenie z arkusza
Zapytanie 1: wybierające jedynie pola miesiac i rok z tabeli zadania dla zadania z datą 2020-07-01
Karta SQL bazy egzamin5. Zapytanie po kolei:
SELECT miesiac, rok - dwie kolumny z arkusza,
FROM zadania,
WHERE dataZadania = '2020-07-01' - datę w SQL zapisuje się jak napis, w formacie RRRR-MM-DD.
Wynik: dokładnie jeden wiersz: lipiec, 2020. Zapytanie zapisujesz w pliku kwerendy.txt, a cały ekran - jako kw1.jpg. Tego zapytania użyje skrypt 1 w banerze.
kwerendy.txtTekstnowy plik
-- Zapytanie 1SELECT miesiac, rok FROM zadania WHERE dataZadania = '2020-07-01';
Podgląd strony po tym kroku
Krok 5: Zapytanie 2: zadania z lipca
Kryteria:
R.1.4 Utworzono zapytanie 2 wybierające jedynie pola dataZadania i wpis z tabeli zadania dla zadań, których miesiąc to lipiec. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT dataZadania, wpis FROM zadania WHERE miesiac = 'lipiec'; oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczne są jedynie pola dataZadania i wpis. Rekordy mają daty jedynie z lipca przy czym jest widoczna albo poprawna kwerenda albo dokładnie 31 rekordów (daty od 1-31 lipca)
ok. 2 min
Polecenie z arkusza
Zapytanie 2: wybierające jedynie pola dataZadania i wpis z tabeli zadania dla zadań, których miesiąc to lipiec
SELECT dataZadania, wpis - dwie kolumny, w tej kolejności (skrypt wyświetli je w bloku dnia),
FROM zadania,
WHERE miesiac = 'lipiec' - napis w apostrofach.
Wynik: 31 wierszy, od 2020-07-01 (Projekt z programowania) do 2020-07-31 (Warszawa) - klucz wymaga dokładnie tylu. Zrzut: kw2.jpg.
kwerendy.txtTekstzmienione: 3 linie
-- Zapytanie 1SELECT miesiac, rok FROM zadania WHERE dataZadania = '2020-07-01';-- Zapytanie 2SELECT dataZadania, wpis FROM zadania WHERE miesiac = 'lipiec';
Podgląd strony po tym kroku
Krok 6: Zapytanie 3: miesiące bez powtórzeń zaczynające się na „l”
Kryteria:
R.1.5 Utworzono zapytanie 3 wybierające jedynie pole miesiac i wyświetlające je bez powtórzeń, z tabeli zadania dla miesięcy zaczynających się na literę „l”. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT DISTINCT miesiac FROM zadania WHERE miesiac LIKE "l%"; oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczne są jedynie dwa rekordy, jedynie dane: lipiec i listopad
ok. 2 min
Polecenie z arkusza
Zapytanie 3: wybierające jedynie pole miesiąc i wyświetlające je bez powtórzeń, z tabeli zadania dla miesięcy zaczynających się na literę „l” (l jak lew, lizak)
SELECT DISTINCT miesiac - DISTINCT usuwa powtórzenia: „lipiec” stoi w 31 wierszach, a w wyniku pojawi się raz. Kolumna nazywa się miesiac (bez „ą”), choć arkusz pisze „miesiąc”,
FROM zadania,
WHERE miesiac LIKE 'l%' - litera l na początku, a po niej %, czyli dowolny ciąg znaków.
Wynik: dwa wiersze - lipiec i listopad, dokładnie jak w kluczu. Zrzut: kw3.jpg.
Typowe błędy
LIKE '%l' szuka miesięcy kończących się na „l”, a bez DISTINCT wynik ma 32 wiersze (31 razy lipiec i listopad).
kwerendy.txtTekstzmienione: 3 linie
-- Zapytanie 1SELECT miesiac, rok FROM zadania WHERE dataZadania = '2020-07-01';-- Zapytanie 2SELECT dataZadania, wpis FROM zadania WHERE miesiac = 'lipiec';-- Zapytanie 3SELECT DISTINCT miesiac FROM zadania WHERE miesiac LIKE 'l%';
Podgląd strony po tym kroku
Krok 7: Zapytanie 4: nowy wpis dla 13 lipca
Kryteria:
R.1.6 Utworzono zapytanie 4 zmieniające pole wpis w tabeli zadania dla zadania z datą 2020- 07-13, nowy wpis to „Wycieczka: jezioro”. W pliku z kwerendami lub na zrzucie istnieje zapis np.: UPDATE zadania SET wpis='Wycieczka: jezioro' WHERE dataZadania='2020-07-13'; oraz wynik działania zapytania udokumentowano zrzutem wskazującym na poprawne wykonanie zapytania
ok. 2 min
Polecenie z arkusza
Zapytanie 4: zmieniające pole wpis w tabeli zadania dla zadania z datą 2020-07-13, nowy wpis to „Wycieczka: jezioro”
UPDATE zadania - zmiana istniejących wierszy,
SET wpis = 'Wycieczka: jezioro' - nowa wartość kolumny,
WHERE dataZadania = '2020-07-13' - tylko ten jeden dzień.
phpMyAdmin odpowiada „1 wiersz zmieniony” - zrzut kw4.jpg. Plik kwerendy.txt ma komplet czterech zapytań. Z tego zapytania skrypt zrobi zapis wpisu z formularza.
Typowe błędy
UPDATE bez WHERE zmienia wszystkie 63 wiersze - phpMyAdmin wykona je bez pytania.
kwerendy.txtTekstzmienione: 3 linie
-- Zapytanie 1SELECT miesiac, rok FROM zadania WHERE dataZadania = '2020-07-01';-- Zapytanie 2SELECT dataZadania, wpis FROM zadania WHERE miesiac = 'lipiec';-- Zapytanie 3SELECT DISTINCT miesiac FROM zadania WHERE miesiac LIKE 'l%';-- Zapytanie 4UPDATE zadania SET wpis = 'Wycieczka: jezioro' WHERE dataZadania = '2020-07-13';
Podgląd strony po tym kroku
Etap 2: Grafika i strona HTML
Kroki 8-19 · po tym etapie masz 16 z 34 kryteriów (pkt)
Krok 8: Grafika: logo1.png o wysokości 150 px
Kryteria:
R.2.1 Plik logo1.png przeskalowano z zachowaniem proporcji do wysokości 150 px, odpowiadająca temu szerokość wynosi 188 px (± 2 px)
ok. 2 min
Polecenie z arkusza
Plik logo1.png, wypakowany z archiwum, należy przeskalować z zachowaniem proporcji tak, aby jego wysokość wynosiła dokładnie 150 px
Oryginał ma 350 × 280 px. Przy zachowaniu proporcji szerokość zmienia się w tym samym stosunku co wysokość: 350 × 150 / 280 = 187,5, czyli 188 px - tyle przyjmuje klucz (188 ± 2 px). W GIMP-ie Obraz → Skaluj obraz, z zamkniętym łańcuchem przy wymiarach wpisujesz tylko wysokość 150, a szerokość przelicza się sama. Potem Plik → Nadpisz logo1.png (R.2.1).
Typowe błędy
Rozpięty łańcuch i ręcznie wpisane 150 × 150 px zniekształcają logo - klucz sprawdza szerokość 188 px.
Podgląd strony po tym kroku
Krok 9: Plik kalendarz.php
ok. 1 min
Polecenie z arkusza
Składa się ze strony o nazwie kalendarz.php
Plik kalendarz.php w folderze zdającego, obok logo1.png. Rozszerzenie .php, bo dojdzie skrypt - na razie szkielet HTML5: <!DOCTYPE html> i lang="pl".
R.3.4 Strona zawiera działające połączenie z zewnętrznym arkuszem stylów o nazwie styl5.css , formatowanie pochodzi jedynie z tego arkusza Uwaga: kryterium to nie jest spełnione również, gdy zdający źle przypisał co najmniej jeden selektor do bloku (np. kropka zamaist #)
ok. 1 min
Polecenie z arkusza
Arkusz stylów w pliku o nazwie styl5.css prawidłowo połączony z kodem strony
<link rel="stylesheet" href="styl5.css"> w <head>. Plik stylów powstanie po HTML-u (R.3.4).
Typowe błędy
Klucz uznaje R.3.4 tylko wtedy, gdy całe formatowanie pochodzi z tego pliku i każdy selektor trafia w swój blok (kropka zamiast # odbiera punkt).
R.2.4 Wygląd strony zdefiniowano dzieląc ją na bloki: dwa bloki banera, poniżej blok główny, na dole blok stopki. Zastosowano znaczniki sekcji
ok. 2 min
Polecenie z arkusza
Podział strony na bloki: dwa bloki banera, poniżej blok główny, w którym znajdują się bloki wyświetlające dane kolejnych dni, na dole blok stopki.
Cztery bloki w kolejności z obrazu 2: dwa bloki banera i blok główny jako <section> z identyfikatorami banner-logo, banner-title i main (nazwy po angielsku - arkusz ich nie narzuca), na dole <footer>. Bloki dni wygeneruje skrypt wewnątrz bloku głównego. Obok siebie bloki banera ustawi CSS (R.2.4).
Krok 14: Pierwszy blok banera: logo z tekstem alternatywnym
Kryteria:
R.2.6 W pierwszym bloku banera umieszczono obraz logo1.png wraz z tekstem alternatywnym "Mój kalendarz"
ok. 1 min
Polecenie z arkusza
Zawartość pierwszego bloku banera: logo1.png z tekstem alternatywnym o treści: „Mój kalendarz”
<img> z src (przeskalowany plik z folderu zdającego) i alt - tekst dla czytnika ekranu i na wypadek braku obrazu (R.2.6). Wysokość obrazu 150 px równa się wysokości bloku banera.
R.2.7 W bloku stopki umieszczono formularz, zastosowano znacznik <form> i jego zamknięcie
R.3.3 Zdefiniowano dla formularza metodę POST
ok. 1 min
Polecenie z arkusza
Zawartość stopki: Formularz wysyłający dane metodą POST do tej samej strony
<form method="post">bez atrybutu action wysyła dane do adresu, z którego został wczytany - czyli do kalendarz.php. Znacznik ma zamknięcie </form> (R.2.7), a metoda POST przesyła wpis w treści żądania; w PHP odczytasz go z tablicy $_POST (R.3.3).
Paragraf (akapit) o treści: „Stronę wykonał: ”, dalej wstawiony numer PESEL zdającego
Na egzaminie po dwukropku wpisujesz swój numer PESEL; w tym rozwiązaniu w jego miejscu stoi Bartosz Bryniarski. To ma być paragraf <p> - pod formularzem, jak na obrazie 2.
R.4.4 Ustawiono wysokość dla wszystkich bloków banera na 150px
R.4.5 Ustawiono szerokość pierwszego bloku banera na 30%, drugiego bloku banera na 70%
ok. 1 min
Polecenie z arkusza
Dla drugiego bloku banera: kolor tła #483D8B, biały kolor czcionki, wysokość 150 px, szerokość 70%
30% + 70% = 100%, więc oba bloki zmieszczą się w jednym rzędzie - bez obramowań i padding, które by je poszerzyły. Wysokość obu bloków banera (R.4.4) i ich szerokości (R.4.5) są ustawione.
R.4.8 Ustawiono dla bloku z danymi z kalendarza: kolor tła #AFEEEE, wysokość 100px, szerokość 150px, marginesy zewnętrzne (margin) 3px, obramowanie o szerokości 1px linią ciągłą
ok. 2 min
Polecenie z arkusza
Dla bloku wyświetlającego dane jednego dnia: kolor tła #AFEEEE, szerokość 150 px, wysokość 100 px, marginesy zewnętrzne 3 px, obramowanie o szerokości 1 px, linią ciągłą
Bloków dni będzie 31, więc selektor klasy.day. border: 1px solid - szerokość i styl linii; arkusz nie podaje koloru, więc obramowanie bierze kolor tekstu (czarny) (R.4.8). Bloki pojawią się dopiero ze skryptem - klucz pozwala sprawdzić to kryterium w pliku CSS.
Ten sam kolor co w banerze: tło obu bloków banera i stopki (R.4.2), biały tekst w drugim bloku banera i stopce (R.4.3), padding: 10px - margines wewnętrzny stopki (R.4.7).
R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest w pełni zgodny z Obrazem 2 w arkuszu egzaminacyjnym (uwaga! Blok główny nie musi zawierać bloków kalendarza)
ok. 3 min
Polecenie z arkusza
Podział zrealizowany za pomocą znaczników sekcji tak, aby po uruchomieniu w przeglądarce wygląd układu bloków był zgodny z Obrazem 2
oba bloki banera dostają float: left i stają obok siebie (30% + 70%),
blok główny z clear: both zaczyna się pod nimi,
bloki dni też pływają w lewo: każdy zajmuje 150 + 2 + 6 = 158 px, więc przeglądarka układa ich w rzędzie tyle, ile się zmieści - na ekranie szerokości ok. 1150 px siedem, jak tydzień w kalendarzu na obrazie 2,
stopka z clear: both staje pod ostatnim rzędem dni.
Klucz przy R.3.1 zaznacza, że blok główny nie musi zawierać bloków dni, żeby układ był uznany.
Typowe błędy
Bez clear przy stopce jej tło wsunie się pod pływające bloki dni.
Kroki 27-33 · po tym etapie masz 34 z 34 kryteriów (pkt)
Krok 27: Połączenie z bazą egzamin5
Kryteria:
R.5.1 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy egzamin5
ok. 3 min
Polecenie z arkusza
Skrypt łączy się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie egzamin5
Na samej górze kalendarz.php, przed <!DOCTYPE html>, piszesz pierwszy blok PHP. Funkcja mysqli_connect z tabeli 1 arkusza przyjmuje kolejno serwer localhost, użytkownika root, puste hasło '' i nazwę bazy egzamin5 - czwarty argument od razu wybiera bazę (R.5.1). Połączenie trafia do zmiennej $connection.
Dlaczego na górze: z połączenia korzystają dwa skrypty w różnych miejscach strony, a formularz stoi w stopce, czyli po bloku głównym. Gdyby zapis wpisu był przy formularzu, blok 13 lipca pokazywałby jeszcze stary wpis. W tym bloku stanie więc też obsługa formularza. Od tego kroku podgląd to wynik prawdziwego uruchomienia w PHP 8.2; stronę sprawdzasz przez http://localhost/….
Krok 28: Skrypt 1: miesiąc i rok w nagłówku trzeciego stopnia
Kryteria: , ,
R.5.3 Skrypt 1 wysyła do bazy danych zapytanie 1 lub Skrypt 2 wysyła do bazy zapytanie 2
R.5.4 Skrypt 1 wyświetla tekst: „miesiąc: <nazwa_miesiąca>, rok: <rok>”, gdzie pola w nawiasach <> oznaczają dane pobrane skryptem
R.2.5 W drugim bloku banera zapisano nagłówek h1: "KALENDARZ" i nagłówek h3 (w skrypcie lub HTML) oraz zapisano przynajmniej jeden paragraf (znacznik <p>)
ok. 3 min
Polecenie z arkusza
Działanie skryptu 1: Wysyła do bazy danych zapytanie 1. Wyświetla w nagłówku trzeciego stopnia tekst: „miesiąc: <nazwa_miesiąca>, rok: <rok>”, gdzie pola w nawiasach <…> oznaczają dane pobrane skryptem
Blok PHP w drugim bloku banera, pod nagłówkiem „KALENDARZ”:
mysqli_query($connection, "SELECT ...") - zapytanie 1 z kwerendy.txt (R.5.3); wynik trafia do $result,
zapytanie zwraca jeden wiersz, więc wystarczy jedno wywołanie mysqli_fetch_row - tablica numerowana od zera: $row[0] to miesiac, $row[1] - rok,
echo nagłówka <h3> ze stałym tekstem i dwiema wartościami. Napis jest w cudzysłowie "…", więc PHP podstawia wartości zmiennych.
Na stronie: „miesiąc: lipiec, rok: 2020” (R.5.4). Drugi blok banera ma teraz <h1> i <h3>, a w stopce jest paragraf (R.2.5).
Krok 29: Skrypt 2: zapytanie 2 i blok dla każdego wiersza
Kryteria:
R.5.5 Skrypt 2 definiuje w bloku głównym blok (za pomocą znaczników sekcji), a w nim wyświetla dane przesłane skryptem. Liczba wygenerowanych bloków odpowiada liczbie wierszy zwróconych zapytaniem
ok. 4 min
Polecenie z arkusza
Działanie skryptu 2: Wysyła do bazy danych zapytanie 2. Definiuje blok (za pomocą znacznika sekcji), a w nim wyświetla dane przesłane skryptem, dotyczące jednego wiersza. Liczba wygenerowanych bloków odpowiada liczbie wierszy zwróconych zapytaniem
Blok PHP w <section id="main">:
zapytanie 2 przez to samo połączenie,
while ($row = mysqli_fetch_row($result)) - mysqli_fetch_row zwraca kolejny wiersz, a gdy wierszy zabraknie - false, i pętla się kończy,
w każdym obrocie echo wypisuje znacznik sekcji z klasą day - tą samą, którą ma reguła w CSS.
Zapytanie zwraca 31 wierszy, więc na stronie jest 31 bloków (R.5.5). Bloki są jeszcze puste, ale już widać kafelki ułożone po siedem w rzędzie.
kalendarz.phpPHPzmienione: 7 linii
<?php$connection = mysqli_connect('localhost', 'root', '', 'egzamin5');?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Mój kalendarz</title> <linkrel="stylesheet"href="styl5.css"></head><body> <sectionid="banner-logo"> <imgsrc="logo1.png"alt="Mój kalendarz"> </section> <sectionid="banner-title"> <h1>KALENDARZ</h1><?php$result = mysqli_query($connection, "SELECT miesiac, rok FROM zadania WHERE dataZadania = '2020-07-01';");$row = mysqli_fetch_row($result);echo"<h3>miesiąc: $row[0], rok: $row[1]</h3>";?> </section> <sectionid="main"><?php$result = mysqli_query($connection, "SELECT dataZadania, wpis FROM zadania WHERE miesiac = 'lipiec';");while ($row = mysqli_fetch_row($result)) {echo'<section class="day">';echo'</section>'; }?> </section> <footer> <formmethod="post"> dodaj wpis: <inputtype="text"name="wpis"> <inputtype="submit"value="DODAJ"> </form> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (kalendarz.php)
Krok 30: Skrypt 2: data w h5, wpis w paragrafie
Kryteria:
R.5.6 Data zadania jest zapisana w nagłówku h5 oraz pole wpis jest zapisane w paragrafie
ok. 2 min
Polecenie z arkusza
Blok jest zgodny z Obrazem 3 oraz jest w nim wyświetlone: Data zadania w nagłówku piątego stopnia. Pole wpis w paragrafie (akapicie)
Wiersz z zapytania 2 ma dwie kolumny: $row[0] to dataZadania, $row[1] - wpis. Data w <h5> (wyśrodkowana regułą z CSS), wpis w <p> - jak w pierwszym bloku na obrazie 3: „2020-07-01” i „Projekt z programowania” (R.5.6). Dni bez wpisu mają w bazie pusty napis, więc pokazują samą datę. Blok 13 lipca ma już „Wycieczka: jezioro” z zapytania 4.
kalendarz.phpPHPzmienione: 2 linie
<?php$connection = mysqli_connect('localhost', 'root', '', 'egzamin5');?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Mój kalendarz</title> <linkrel="stylesheet"href="styl5.css"></head><body> <sectionid="banner-logo"> <imgsrc="logo1.png"alt="Mój kalendarz"> </section> <sectionid="banner-title"> <h1>KALENDARZ</h1><?php$result = mysqli_query($connection, "SELECT miesiac, rok FROM zadania WHERE dataZadania = '2020-07-01';");$row = mysqli_fetch_row($result);echo"<h3>miesiąc: $row[0], rok: $row[1]</h3>";?> </section> <sectionid="main"><?php$result = mysqli_query($connection, "SELECT dataZadania, wpis FROM zadania WHERE miesiac = 'lipiec';");while ($row = mysqli_fetch_row($result)) {echo'<section class="day">';echo"<h5>$row[0]</h5>";echo"<p>$row[1]</p>";echo'</section>'; }?> </section> <footer> <formmethod="post"> dodaj wpis: <inputtype="text"name="wpis"> <inputtype="submit"value="DODAJ"> </form> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (kalendarz.php)
Krok 31: Odczyt wpisu z formularza
Kryteria:
R.5.7 W skrypcie działającym na danych przesłanych przez formularz zastosowano tabelę $_POST, zapis musi być poprawny składniowo i logicznie
ok. 2 min
Polecenie z arkusza
Działanie skryptu, który odbiera dane przesłane z formularza. Skrypt pobiera dane z pola edycyjnego
W bloku na górze pliku, pod połączeniem:
isset($_POST['wpis']) - prawdziwe dopiero po kliknięciu „DODAJ”; przy zwykłym wejściu na stronę ta część się nie wykonuje,
$wpis = $_POST['wpis']; - tekst z pola o nazwie wpis do zmiennej. Kluczem w tablicy $_POST jest atrybut name pola (R.5.7).
Podgląd: formularz wysłany ze słowem „Kajaki” - skrypt je odczytał, ale jeszcze nigdzie nie zapisał.
kalendarz.phpPHPzmienione: 3 linie
<?php$connection = mysqli_connect('localhost', 'root', '', 'egzamin5');if (isset($_POST['wpis'])) {$wpis = $_POST['wpis'];}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Mój kalendarz</title> <linkrel="stylesheet"href="styl5.css"></head><body> <sectionid="banner-logo"> <imgsrc="logo1.png"alt="Mój kalendarz"> </section> <sectionid="banner-title"> <h1>KALENDARZ</h1><?php$result = mysqli_query($connection, "SELECT miesiac, rok FROM zadania WHERE dataZadania = '2020-07-01';");$row = mysqli_fetch_row($result);echo"<h3>miesiąc: $row[0], rok: $row[1]</h3>";?> </section> <sectionid="main"><?php$result = mysqli_query($connection, "SELECT dataZadania, wpis FROM zadania WHERE miesiac = 'lipiec';");while ($row = mysqli_fetch_row($result)) {echo'<section class="day">';echo"<h5>$row[0]</h5>";echo"<p>$row[1]</p>";echo'</section>'; }?> </section> <footer> <formmethod="post"> dodaj wpis: <inputtype="text"name="wpis"> <inputtype="submit"value="DODAJ"> </form> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer></body></html>
R.5.8 Skrypt obsługujący formularz uaktualnia dane w bazie danych. Wpis pobrany z formularza jest wpisany do rekordu o dacie 2020-07-13. Należy sprawdzić w bazie danych czy po wciśnięciu przycisku baza została uaktualniona, nie jest wymagane uaktualnienie na stronie
ok. 3 min
Polecenie z arkusza
Następnie uaktualnia dane w bazie danych. Aby uprościć zadanie skrypt wprowadza wpis zawsze do rekordu o dacie 2020-07-13. Do uaktualnienia danych wykorzystaj zapytanie 4 modyfikując w nim wartość pola wpis
Zapytanie 4 z kwerendy.txt, w którym w miejsce stałego tekstu „Wycieczka: jezioro” stoi '$wpis'. Data zostaje: arkusz każe zawsze zapisywać do 13 lipca. Całe zapytanie jest w cudzysłowie, więc PHP podstawia wartość zmiennej, a apostrofy wewnątrz to ograniczniki napisu w SQL.
Sprawdzenie, jak każe klucz (R.5.8): po wpisaniu „Kajaki” i kliknięciu „DODAJ” w tabeli zadania wiersz z datą 2020-07-13 ma wpis Kajaki. Zapis stoi na górze pliku, przed skryptem 2, więc nowy wpis widać od razu w bloku 13 lipca.
Typowe błędy
Wpis z literami „ś” czy „ż” (np. „Śniardwy”) kończy się błędem „Incorrect string value”: tabela z archiwum ma kodowanie latin1, które tych liter nie zna, a MariaDB z XAMPP działa w trybie ścisłym. To cecha bazy z archiwum - klucz sprawdza import pliku bez zmian, więc tabeli nie przerabiaj, a zapis sprawdź słowem bez polskich liter.
kalendarz.phpPHPzmienione: 1 linia
<?php$connection = mysqli_connect('localhost', 'root', '', 'egzamin5');if (isset($_POST['wpis'])) {$wpis = $_POST['wpis']; mysqli_query($connection, "UPDATE zadania SET wpis = '$wpis' WHERE dataZadania = '2020-07-13';");}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Mój kalendarz</title> <linkrel="stylesheet"href="styl5.css"></head><body> <sectionid="banner-logo"> <imgsrc="logo1.png"alt="Mój kalendarz"> </section> <sectionid="banner-title"> <h1>KALENDARZ</h1><?php$result = mysqli_query($connection, "SELECT miesiac, rok FROM zadania WHERE dataZadania = '2020-07-01';");$row = mysqli_fetch_row($result);echo"<h3>miesiąc: $row[0], rok: $row[1]</h3>";?> </section> <sectionid="main"><?php$result = mysqli_query($connection, "SELECT dataZadania, wpis FROM zadania WHERE miesiac = 'lipiec';");while ($row = mysqli_fetch_row($result)) {echo'<section class="day">';echo"<h5>$row[0]</h5>";echo"<p>$row[1]</p>";echo'</section>'; }?> </section> <footer> <formmethod="post"> dodaj wpis: <inputtype="text"name="wpis"> <inputtype="submit"value="DODAJ"> </form> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (kalendarz.php)
Krok 33: Zamknięcie połączenia z bazą
Kryteria:
R.5.2 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 2, po pętli - to ostatnie zapytanie na stronie (formularz w stopce nie korzysta już z bazy, jego zapis wykonał się na górze pliku), więc zamknięcie jest ostatnią operacją na bazie (R.5.2). Strona spełnia wszystkie kryteria arkusza. Dalej trzy kroki o zabezpieczeniach - klucz ich nie wymaga.
Typowe błędy
mysqli_close w banerze, zaraz po skrypcie 1, sprawia, że skrypt 2 dostaje zamknięte połączenie i PHP kończy się błędem.
kalendarz.phpPHPzmienione: 1 linia
<?php$connection = mysqli_connect('localhost', 'root', '', 'egzamin5');if (isset($_POST['wpis'])) {$wpis = $_POST['wpis']; mysqli_query($connection, "UPDATE zadania SET wpis = '$wpis' WHERE dataZadania = '2020-07-13';");}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Mój kalendarz</title> <linkrel="stylesheet"href="styl5.css"></head><body> <sectionid="banner-logo"> <imgsrc="logo1.png"alt="Mój kalendarz"> </section> <sectionid="banner-title"> <h1>KALENDARZ</h1><?php$result = mysqli_query($connection, "SELECT miesiac, rok FROM zadania WHERE dataZadania = '2020-07-01';");$row = mysqli_fetch_row($result);echo"<h3>miesiąc: $row[0], rok: $row[1]</h3>";?> </section> <sectionid="main"><?php$result = mysqli_query($connection, "SELECT dataZadania, wpis FROM zadania WHERE miesiac = 'lipiec';");while ($row = mysqli_fetch_row($result)) {echo'<section class="day">';echo"<h5>$row[0]</h5>";echo"<p>$row[1]</p>";echo'</section>'; } mysqli_close($connection);?> </section> <footer> <formmethod="post"> dodaj wpis: <inputtype="text"name="wpis"> <inputtype="submit"value="DODAJ"> </form> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (kalendarz.php)
Etap 5: Dobre praktyki (poza kluczem)
Kroki 34-36 · po tym etapie masz 34 z 34 kryteriów (pkt)
Krok 34: Zabezpieczenie: zapytanie przygotowane dla UPDATE
ok. 4 min
Polecenie z arkusza
Do uaktualnienia danych wykorzystaj zapytanie 4 modyfikując w nim wartość pola wpis
Tekst z formularza trafia do zapytania wprost - to SQL injection. Sprawdzone na wersji z poprzednich kroków:
wpis Hakowanie' -- zamienia zapytanie w UPDATE zadania SET wpis = 'Hakowanie' -- ' WHERE .... Dwa myślniki ze spacją zaczynają w SQL komentarz, więc warunek WHERE znika i baza zmienia wszystkie 63 wiersze tabeli - cały kalendarz dostał wpis „Hakowanie”,
zwykły wpis z apostrofem, np. Kino 'Helios', rozbija składnię zapytania - PHP 8.1 i nowsze zgłaszają wyjątek mysqli_sql_exception, a zamiast strony widać „Fatal error”.
Lekarstwo to zapytanie przygotowane: mysqli_prepare z jednym znakiem ? w miejscu wpisu, mysqli_stmt_bind_param z typem 's' (napis) i mysqli_stmt_execute. Baza dostaje treść zapytania i dane osobno, więc dane nigdy nie staną się kodem SQL. Podgląd: ten sam „wpis-atak” zapisał się jako zwykły tekst - tylko w bloku 13 lipca. Stara linia zostaje w komentarzu.
kalendarz.phpPHPzmienione: 4 linie
<?php$connection = mysqli_connect('localhost', 'root', '', 'egzamin5');if (isset($_POST['wpis'])) {$wpis = $_POST['wpis'];// mysqli_query($connection, "UPDATE zadania SET wpis = '$wpis' WHERE dataZadania = '2020-07-13';");$statement = mysqli_prepare($connection, "UPDATE zadania SET wpis = ? WHERE dataZadania = '2020-07-13';"); mysqli_stmt_bind_param($statement, 's', $wpis); mysqli_stmt_execute($statement);}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Mój kalendarz</title> <linkrel="stylesheet"href="styl5.css"></head><body> <sectionid="banner-logo"> <imgsrc="logo1.png"alt="Mój kalendarz"> </section> <sectionid="banner-title"> <h1>KALENDARZ</h1><?php$result = mysqli_query($connection, "SELECT miesiac, rok FROM zadania WHERE dataZadania = '2020-07-01';");$row = mysqli_fetch_row($result);echo"<h3>miesiąc: $row[0], rok: $row[1]</h3>";?> </section> <sectionid="main"><?php$result = mysqli_query($connection, "SELECT dataZadania, wpis FROM zadania WHERE miesiac = 'lipiec';");while ($row = mysqli_fetch_row($result)) {echo'<section class="day">';echo"<h5>$row[0]</h5>";echo"<p>$row[1]</p>";echo'</section>'; } mysqli_close($connection);?> </section> <footer> <formmethod="post"> dodaj wpis: <inputtype="text"name="wpis"> <inputtype="submit"value="DODAJ"> </form> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (kalendarz.php)
Krok 35: Zabezpieczenie: htmlspecialchars przy wyświetlaniu wpisu
ok. 3 min
Polecenie z arkusza
Pole wpis w paragrafie (akapicie)
Zapytanie przygotowane chroni bazę, ale wpis wraca potem na stronę - i każdy, kto otworzy kalendarz, zobaczy go jako kod HTML. Sprawdzone: wpis <b style="color:red">PILNE</b> wyświetlał się w bloku 13 lipca jako czerwone, pogrubione słowo. Tak samo przeszedłby znacznik <script> - kod JavaScript uruchamiałby się u każdego odwiedzającego (stored XSS).
Funkcja htmlspecialchars zamienia <, >, & i cudzysłowy na encje (np. < na <), więc przeglądarka pokazuje znaki dosłownie. Stosuje się ją przy wypisywaniu danych od użytkownika - tutaj kolumna wpis. Podgląd: wpis widać dosłownie, ze znacznikami.
kalendarz.phpPHPzmienione: 2 linie
<?php$connection = mysqli_connect('localhost', 'root', '', 'egzamin5');if (isset($_POST['wpis'])) {$wpis = $_POST['wpis'];// mysqli_query($connection, "UPDATE zadania SET wpis = '$wpis' WHERE dataZadania = '2020-07-13';");$statement = mysqli_prepare($connection, "UPDATE zadania SET wpis = ? WHERE dataZadania = '2020-07-13';"); mysqli_stmt_bind_param($statement, 's', $wpis); mysqli_stmt_execute($statement);}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Mój kalendarz</title> <linkrel="stylesheet"href="styl5.css"></head><body> <sectionid="banner-logo"> <imgsrc="logo1.png"alt="Mój kalendarz"> </section> <sectionid="banner-title"> <h1>KALENDARZ</h1><?php$result = mysqli_query($connection, "SELECT miesiac, rok FROM zadania WHERE dataZadania = '2020-07-01';");$row = mysqli_fetch_row($result);echo"<h3>miesiąc: $row[0], rok: $row[1]</h3>";?> </section> <sectionid="main"><?php$result = mysqli_query($connection, "SELECT dataZadania, wpis FROM zadania WHERE miesiac = 'lipiec';");while ($row = mysqli_fetch_row($result)) {echo'<section class="day">';echo"<h5>$row[0]</h5>";// echo "<p>$row[1]</p>";echo'<p>' . htmlspecialchars($row[1]) . '</p>';echo'</section>'; } mysqli_close($connection);?> </section> <footer> <formmethod="post"> dodaj wpis: <inputtype="text"name="wpis"> <inputtype="submit"value="DODAJ"> </form> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (kalendarz.php)
Krok 36: Zabezpieczenie: pusty formularz nie kasuje wpisu
ok. 3 min
Polecenie z arkusza
Skrypt pobiera dane z pola edycyjnego
isset jest prawdziwe także wtedy, gdy pole jest puste - po kliknięciu „DODAJ” klucz wpis zawsze jest w $_POST. Sprawdzone: kliknięcie przycisku bez wpisania tekstu skasowało „Wycieczka: jezioro” z 13 lipca.
Warunek dostaje drugi człon: trim($_POST['wpis']) !== '' - trim zdejmuje spacje z brzegów, więc wpis z samych spacji też się nie liczy. Do bazy idzie wersja przycięta. Podgląd: formularz wysłany z trzema spacjami - wpis 13 lipca został.
kalendarz.phpPHPzmienione: 4 linie
<?php$connection = mysqli_connect('localhost', 'root', '', 'egzamin5');// if (isset($_POST['wpis'])) {// $wpis = $_POST['wpis'];if (isset($_POST['wpis']) && trim($_POST['wpis']) !== '') {$wpis = trim($_POST['wpis']);// mysqli_query($connection, "UPDATE zadania SET wpis = '$wpis' WHERE dataZadania = '2020-07-13';");$statement = mysqli_prepare($connection, "UPDATE zadania SET wpis = ? WHERE dataZadania = '2020-07-13';"); mysqli_stmt_bind_param($statement, 's', $wpis); mysqli_stmt_execute($statement);}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Mój kalendarz</title> <linkrel="stylesheet"href="styl5.css"></head><body> <sectionid="banner-logo"> <imgsrc="logo1.png"alt="Mój kalendarz"> </section> <sectionid="banner-title"> <h1>KALENDARZ</h1><?php$result = mysqli_query($connection, "SELECT miesiac, rok FROM zadania WHERE dataZadania = '2020-07-01';");$row = mysqli_fetch_row($result);echo"<h3>miesiąc: $row[0], rok: $row[1]</h3>";?> </section> <sectionid="main"><?php$result = mysqli_query($connection, "SELECT dataZadania, wpis FROM zadania WHERE miesiac = 'lipiec';");while ($row = mysqli_fetch_row($result)) {echo'<section class="day">';echo"<h5>$row[0]</h5>";// echo "<p>$row[1]</p>";echo'<p>' . htmlspecialchars($row[1]) . '</p>';echo'</section>'; } mysqli_close($connection);?> </section> <footer> <formmethod="post"> dodaj wpis: <inputtype="text"name="wpis"> <inputtype="submit"value="DODAJ"> </form> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (kalendarz.php)
Etap 6: Oddanie pracy
Krok 37 · po tym etapie masz 34 z 34 kryteriów (pkt)
Krok 37: Plik przeglądarka.txt i nagranie płyty
ok. 5 min
Polecenie z arkusza
Po zakończeniu pracy utwórz plik tekstowy. Zapisz w nim nazwę przeglądarki internetowej, w której weryfikowałeś poprawność działania witryny. Nazwij plik przeglądarka.txt i zapisz go w folderze z numerem PESEL. 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ć: import.png, kalendarz.php, kw1.jpg, kw2.jpg, kw3.jpg, kw4.jpg, kwerendy.txt, logo1.png, przeglądarka.txt i styl5.css. Nagraj płytę, sprawdź jej odczyt, opisz ją numerem PESEL i zostaw w pudełku na stanowisku razem z arkuszem.
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.