Serwis używa plików cookies do analizy ruchu i poprawy działania. Szczegóły
EE.09-01-23.01-SGStyczeń 2023 · 55 kroków · ok. 96 min · 37 kryteriów z klucza
Rozwiązanie krok po kroku: Akta pracownicze dla sekretariatu firmy (Wersja podstawowa)
Aplikacja sekretariatu firmy: baza firma z tabelami pracownicy i stanowiska, cztery zapytania (jeden pracownik, liczba pracowników, złączenie z nazwą stanowiska), zdjęcie pracownika przeskalowane do 400 px i strona index.php z trzema skryptami PHP - karta pracownika z adresem i statusem dokumentów RODO i badań, liczba zatrudnionych oraz zdjęcie, imię i nazwisko i stanowisko. Do tego style w styl.css z cieniem bloków (box-shadow).
To arkusz EE.09 ze stycznia 2023 - kwalifikacji formuły 2017, którą zastąpiła INF.03. Kroki idą zdanie po zdaniu za treścią arkusza - przy każdym jest polecenie z arkusza, a kod pokazuje dokładnie te linie, które je spełniają. Skrypty są najpierw napisane najprościej, funkcjami mysqli z tabeli 1 arkusza, a na końcu sprawdzone na prawdziwych atakach i zabezpieczone.
To jest wersja podstawowa. W zakładce „Nowoczesny kod (grid, PDO)” jest drugi sposób rozwiązania tego samego arkusza: siatka CSS zamiast float, PHP na PDO na górze pliku z zapytaniami przygotowanymi i funkcją do bezpiecznego wypisywania danych - obie wersje są zgodne z kluczem.
Baza, zdjęcie i plik cv.txt pochodzą z oryginalnego archiwum CKE zad1.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 (klucz każe sprawdzać w XAMPP 8.1.2 - użyte funkcje działają w obu wersjach).
Jak liczą się punkty. Klucz ma 37 kryteriów (R.1.1 - R.5.9), każde za 1 punkt. Próg zdania części praktycznej to 75%, czyli 28 punktów - można stracić 9 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 37 kryteriów (pkt)
Krok 1: Folder zdającego i archiwum zad1.zip
ok. 2 min
Polecenie z arkusza
Na pulpicie konta Egzamin należy utworzyć folder. Jako nazwy folderu należy użyć numeru zdającego, którym został podpisany arkusz. Rozpakowane pliki należy umieścić w tym folderze. Zaleca się, aby rozpakowane pliki znajdowały się w tym samym folderze, co pliki strony. Po skończonej pracy wszystkie wyniki należy zapisać w tym folderze.
Na pulpicie tworzysz folder nazwany numerem PESEL - w tym rozwiązaniu w jego miejscu stoi imię i nazwisko autora, Bartosz Bryniarski. Archiwum zad1.zip rozpakowujesz do tego folderu z hasłem AKt@PR@cownicZE (wielkość liter i znaki @ mają znaczenie). W środku: baza.sql (tabele do importu), 2.jpg (zdjęcie pracownika) i cv.txt (plik, do którego poprowadzi odnośnik „Pobierz”).
Strona index.php zadziała tylko przez serwer Apache, czyli z folderu C:\xampp\htdocs. Wygodnie jest więc pracować w folderze w htdocs (np. htdocs\egzamin), a na koniec skopiować całość do folderu z numerem na pulpicie - albo odwrotnie. Zdjęcie i cv.txt muszą leżeć obok index.php, bo strona odwołuje się do nich samą nazwą pliku.
Typowe błędy
Pliki zapisane poza folderem zdającego nie trafią na płytę - a ocenia się tylko to, co jest na płycie.
Krok 2: XAMPP i baza firma
ok. 2 min
Polecenie z arkusza
Za pomocą narzędzia phpMyAdmin wykonaj operacje na bazie danych: Utwórz bazę danych o nazwie firma
W XAMPP Control Panel klikasz Start przy Apache i MySQL, potem http://localhost/phpmyadmin. Nowa w lewym panelu, nazwa firma, porównywanie napisów np. utf8_unicode_ci albo utf8mb4_polish_ci, Utwórz. Nazwę bazy wpisze później skrypt - musi być identyczna.
Krok 3: Import tabel z pliku baza.sql i zrzut import.jpg
Kryteria:
R.1.1 Wykonano import tabel do bazy danych firma , czynność udokumentowano plikiem o nazwie import w formacie JPEG
ok. 3 min
Polecenie z arkusza
Do bazy firma zaimportuj tabele z pliku baza.sql z rozpakowanego archiwum. Wykonaj zrzut ekranu po imporcie. Zrzut zapisz w formacie JPEG i nazwij import. 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ę firma, karta Import, Wybierz plik → baza.sql, Wykonaj. Powstają dwie tabele z obrazu 1: pracownicy (9 osób) i stanowiska (4 stanowiska). Kolumna stanowiska_id w tabeli pracownicy to klucz obcy - numer wiersza z tabeli stanowiska; na nim opiera się zapytanie 4. Pola czyRODO i czyBadania przyjmują tylko 1 albo 0.
Zielony komunikat o sukcesie i dwie tabele w lewym panelu na zrzucie całego ekranu: import.jpg.
Typowe błędy
Zrzut importu w JPEG (import.jpg), zrzuty zapytań w PNG (kw1.png - kw4.png). Wykadrowany zrzut albo zrzut bez paska zadań nie jest oceniany.
Podgląd strony po tym kroku
Krok 4: Zapytanie 1: dane pracownika o id 2
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 imie, nazwisko, adres, miasto, czyRODO, czyBadania z tabeli pracownicy dla wiersza o id równym 2. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT imie, nazwisko, adres, miasto, czyRODO, czyBadania FROM pracownicy WHERE id=2; oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczny jest jedynie rekord z danymi: Anna, Nowak, ul. Piekarska 6, Bytom, 1, 1
ok. 3 min
Polecenie z arkusza
Wykonaj zapytania SQL działające na bazie firma. Zapytania zapisz w pliku kwerendy.txt. 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 imie, nazwisko, adres, miasto, czyRODO, czyBadania z tabeli pracownicy dla wiersza o id równym 2
Zaznacz bazę firma, karta SQL:
SELECT imie, nazwisko, adres, miasto, czyRODO, czyBadania - sześć pól w kolejności z arkusza; „jedynie” oznacza: bez * i bez id,
FROM pracownicy - tabela,
WHERE id = 2 - jeden konkretny wiersz.
Wynik: Anna, Nowak, ul. Piekarska 6, Bytom, 1, 1 - jak w kluczu. Zapytanie kopiujesz do pliku kwerendy.txt (z tego pliku przekopiujesz je później do skryptu), a zrzut całego ekranu zapisujesz jako kw1.png.
kwerendy.txtTekstnowy plik
-- Zapytanie 1SELECT imie, nazwisko, adres, miasto, czyRODO, czyBadania FROM pracownicy WHERE id = 2;
Podgląd strony po tym kroku
Krok 5: Zapytanie 2: liczba pracowników
Kryteria:
R.1.4 Utworzono zapytanie 2 zliczające wszystkie rekordy w tabeli pracownicy . W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT COUNT(*) FROM pracownicy; (możliwa też nazwa dowolnego pola zamiast *) oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczna jest wartość 9
ok. 2 min
Polecenie z arkusza
Zapytanie 2: wybierające liczbę wszystkich rekordów w tabeli pracownicy
Funkcja agregująca COUNT(*) liczy wiersze tabeli. Wynik: jedna komórka z liczbą 9. Zrzut: kw2.png.
Typowe błędy
SELECT * FROM pracownicy pokazuje 9 wierszy, ale nie „wybiera liczby” - skrypt 2 musiałby je sam policzyć, a klucz R.1.4 wymaga COUNT.
kwerendy.txtTekstzmienione: 3 linie
-- Zapytanie 1SELECT imie, nazwisko, adres, miasto, czyRODO, czyBadania FROM pracownicy WHERE id = 2;-- Zapytanie 2SELECT COUNT(*) FROM pracownicy;
Podgląd strony po tym kroku
Krok 6: Zapytanie 3: id, imię i nazwisko pracownika o id 2
Kryteria:
R.1.5 Utworzono zapytanie 3 wybierające jedynie pola id, imie, nazwisko z tabeli pracownicy dla wiersza o id równym 2. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT id, imie, nazwisko FROM pracownicy WHERE id = 2; oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczny jest jedynie jeden rekord z danymi: 2, Anna, Nowak
ok. 2 min
Polecenie z arkusza
Zapytanie 3: wybierające jedynie pola id, imie, nazwisko z tabeli pracownicy dla wiersza o id równym 2
Trzy pola, ten sam warunek co w zapytaniu 1. Wynik: 2, Anna, Nowak. Pole id jest tu potrzebne skryptowi 3 - z niego powstanie nazwa pliku zdjęcia 2.jpg. Zrzut: kw3.png.
kwerendy.txtTekstzmienione: 3 linie
-- Zapytanie 1SELECT imie, nazwisko, adres, miasto, czyRODO, czyBadania FROM pracownicy WHERE id = 2;-- Zapytanie 2SELECT COUNT(*) FROM pracownicy;-- Zapytanie 3SELECT id, imie, nazwisko FROM pracownicy WHERE id = 2;
Podgląd strony po tym kroku
Krok 7: Zapytanie 4: stanowisko pracownika przez relację
Kryteria:
R.1.6 Utworzono zapytanie 4 wybierające jedynie pole id z tabeli pracownicy i odpowiadające mu pola nazwa i opis z tabeli stanowiska dla wiersza tabeli pracownicy o id równym 2. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT pracownicy.id, nazwa, opis FROM pracownicy JOIN stanowiska ON pracownicy.stanowiska_id = stanowiska.id WHERE pracownicy.id = 2; (możliwe też INNER JOIN lub bez JOIN z porównaniem kluczy w sekcji WHERE) oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczny jest jedynie jeden rekord z danymi: 2, kierownik, kierownik grupy programistow
ok. 4 min
Polecenie z arkusza
Zapytanie 4: wybierające jedynie id z tabeli pracownicy i odpowiadające mu pola nazwa i opis z tabeli stanowiska dla wiersza tabeli pracownicy o id równym 2. Należy posłużyć się relacją
SELECT pracownicy.id, nazwa, opis - kolumnę id mają obie tabele, więc przed nią stoi nazwa tabeli; nazwa i opis są tylko w stanowiska,
FROM pracownicy JOIN stanowiska ON pracownicy.stanowiska_id = stanowiska.id - relacja: klucz obcy pracownika równa się kluczowi głównemu stanowiska,
WHERE pracownicy.id = 2 - znów z nazwą tabeli, inaczej baza zgłosi błąd „Column 'id' in where clause is ambiguous”.
Wynik: 2, kierownik, kierownik grupy programistow. Zrzut: kw4.png.
Typowe błędy
Samo id bez nazwy tabeli - w SELECT i w WHERE - kończy się błędem niejednoznacznej kolumny.
kwerendy.txtTekstzmienione: 3 linie
-- Zapytanie 1SELECT imie, nazwisko, adres, miasto, czyRODO, czyBadania FROM pracownicy WHERE id = 2;-- Zapytanie 2SELECT COUNT(*) FROM pracownicy;-- Zapytanie 3SELECT id, imie, nazwisko FROM pracownicy WHERE id = 2;-- Zapytanie 4SELECT pracownicy.id, nazwa, opis FROM pracownicy JOIN stanowiska ON pracownicy.stanowiska_id = stanowiska.id WHERE pracownicy.id = 2;
Podgląd strony po tym kroku
Etap 2: Grafika i strona HTML
Kroki 8-23 · po tym etapie masz 15 z 37 kryteriów (pkt)
Krok 8: Zdjęcie 2.jpg przeskalowane do 400 px
Kryteria:
R.2.1 Plik 2 .jpg przeskalowano z zachowaniem proporcji do szerokości 400 px, odpowiadająca temu wysokość wynosi 267 px (± 5 px)
ok. 2 min
Polecenie z arkusza
Plik 2.jpg, wypakowany z archiwum, należy przeskalować z zachowaniem proporcji do szerokości 400 px
Oryginał ma 500 × 334 px. W GIMP-ie Obraz → Skaluj obraz, łańcuch zamknięty (zachowanie proporcji), szerokość 400 - wysokość przelicza się sama: 334 × 400 / 500 = 267,2, czyli 267 px (klucz: 267 ±5). Zapis pod tą samą nazwą: Plik → Nadpisz 2.jpg - skrypt 3 będzie szukał pliku właśnie o nazwie 2.jpg.
Typowe błędy
Eksport pod nową nazwą (np. 2_male.jpg) zostawia w folderze oryginał - strona pokaże niezmienione zdjęcie, bo skrypt zbuduje nazwę 2.jpg.
Podgląd strony po tym kroku
Krok 9: Plik index.php
Kryteria:
R.2.2 Witrynę internetową zapisano w pliku PHP o nazwie index
ok. 1 min
Polecenie z arkusza
Cechy witryny: Składa się ze strony o nazwie index.php
Nowy plik index.php w folderze ze zdjęciem i cv.txt. Rozszerzenie .php jest konieczne: tylko taki plik serwer przepuszcza przez PHP, zanim wyśle go przeglądarce. Do etapu skryptów plik zawiera sam HTML - podgląd pokazuje go wprost.
index.phpPHPnowy plik
Podgląd strony po tym kroku (index.php)
Krok 10: Szkielet HTML5
ok. 1 min
Polecenie z arkusza
Zapisana w języku HTML5
<!DOCTYPE html> w pierwszej linii i szkielet <html>, <head>, <body>.
Jawnie zastosowany właściwy standard kodowania polskich znaków
<meta charset="UTF-8"> w <head>, a plik zapisany w edytorze w UTF-8. Baza wysyła dane w UTF-8, więc „Liczba zatrudnionych pracowników” i dane z bazy wyświetlą się poprawnie.
R.2.3 Zastosowano deklarację języka HTML5, jawnie zapisano standard kodowania polskich znaków oraz zadeklarowano język witryny (np. <html lang = 'pl'>)
ok. 1 min
Polecenie z arkusza
Zadeklarowany język witryny: polski
lang="pl" w znaczniku <html>. Deklaracja HTML5, kodowanie i język dają razem R.2.3.
R.3.3 Strona zawiera działające połączenie z zewnętrznym arkuszem stylów o nazwie styl.css , formatowanie pochodzi jedynie z tego arkusza Uwaga: kryterium to nie jest spełnione również, gdy zdający źle przypisał co najmniej jeden selektor do bloku (np. kropka zamiast #)
ok. 1 min
Polecenie z arkusza
Arkusz stylów w pliku o nazwie styl.css prawidłowo połączony z kodem strony
<link rel="stylesheet" href="styl.css"> - całe formatowanie strony trafi do tego pliku, bez atrybutów style w HTML-u ani w skryptach (R.3.3).
Typowe błędy
Kropka zamiast # przed identyfikatorem bloku w CSS odbiera punkt R.3.3 - klucz uznaje połączenie tylko wtedy, gdy każdy selektor trafia w swój blok.
R.2.5 Wygląd strony zdefiniowano dzieląc ją na bloki: lewy, prawy i stopkę. Zastosowano znaczniki sekcji
ok. 2 min
Polecenie z arkusza
Podział strony na bloki: lewy, 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
Bloki lewy i prawy to <section> z identyfikatorami left i right (nazwy po angielsku - arkusz ich nie narzuca), stopka - <footer>. Strona nie ma banera. Obok siebie ustawi bloki dopiero CSS.
<hr> - pozioma linia oddzielająca dane pracownika od dokumentów. Na obrazie 2 to trzecia linia w bloku: dwie pierwsze wypisze skrypt 1, między nagłówkiem a tą linią. <hr> nie ma znacznika zamykającego.
Krok 19: Blok lewy: odnośnik „Pobierz” do pliku cv.txt
Kryteria:
R.3.2 Na stronie umieszczono odnośnik, którego kliknięcie otwiera / pobiera plik cv.txt
ok. 1 min
Polecenie z arkusza
odnośnik o treści „Pobierz” do pliku cv.txt
<a href="cv.txt"> - plik z tego samego folderu co strona. Przeglądarka otworzy go jako tekst albo pobierze, zależnie od ustawień - obie reakcje spełniają R.3.2. Atrybut download wymusiłby pobranie, ale arkusz go nie wymaga. Plik cv.txt z archiwum zawiera jedno zdanie zastępujące prawdziwe CV. Na obrazie 2 odnośnik stoi przy samej krawędzi bloku - nie jest w paragrafie, więc nie dostaje wcięcia z reguły dla p.
R.2.9 W stopce umieszczono napis "Autorem aplikacji jest: "
ok. 1 min
Polecenie z arkusza
Zawartość stopki: napis o treści „Autorem aplikacji jest: ”, dalej wstawiony numer zdającego
Na egzaminie po dwukropku wpisujesz swój numer PESEL, w tym rozwiązaniu w jego miejscu stoi Bartosz Bryniarski. Napis stoi w stopce bez paragrafu: reguła dla p dodałaby mu 30 px wcięcia, a na obrazie 2 zaczyna się równo z listą.
Kroki 24-33 · po tym etapie masz 26 z 37 kryteriów (pkt)
Krok 24: Plik styl.css: krój, tło i kolor tekstu strony
Kryteria:
R.4.1 Ustawiono dla selektora strony (body lub html lub kontener strony) krój czcionki Helvetica, kolor tła #DDD, kolor czcionki #546E7A
ok. 1 min
Polecenie z arkusza
Styl CSS powinien być zdefiniowany jedynie w zewnętrznym pliku CSS. Dla selektora strony: krój czcionki Helvetica, kolor tła #DDD, kolor czcionki #546E7A
Nowy plik styl.css. „Selektor strony” to body - krój i kolor tekstu dziedziczą wszystkie elementy w środku. #DDD to skrót od #DDDDDD (jasnoszary): każda z trzech cyfr szesnastkowych jest powtórzona (R.4.1).
styl.cssCSSnowy plik
body {font-family: Helvetica;background-color: #DDD;color: #546E7A;}
Podgląd strony po tym kroku (index.php)
Krok 25: Bloki lewy i prawy: tło, wysokość, marginesy i cień
Kryteria: ,
R.4.4 Ustawiono wysokość bloku lewego i prawego: 600px
R.4.8 Ustawiono dla bloku lewego i prawego cień o cechach: przesunięcie poziome i pionowe 0, rozmycie 20px kolor szary (dowolny odcień) np. box-shadow: 0 0 20px grey; (lub 0 0 20px 0 grey, lub gray, lub inny dowolny szary)
ok. 3 min
Polecenie z arkusza
Wspólne dla bloku lewego i prawego: biały kolor tła, wysokość 600 px, marginesy zewnętrzne 20 px, cień bloku bez przesunięcia pionowego i poziomego, rozmycie 20 px i kolor szary
Jedna reguła dla dwóch selektorów rozdzielonych przecinkiem: #left, #right. Marginesy zewnętrzne to margin, a cień - box-shadow. Kolejność wartości jest w pomocy z w3schools pod tabelą 1 arkusza: przesunięcie poziome, pionowe, rozmycie, kolor - czyli 0 0 20px gray. Zera bez jednostek są poprawne, a cień bez przesunięcia otacza blok równo ze wszystkich stron (R.4.4, R.4.8).
Typowe błędy
box-shadow: 20px gray to przesunięcie poziome 20 px bez rozmycia - cień przesunięty w prawo. Wartość rozmycia jest trzecia, po dwóch przesunięciach.
R.4.5 Ustawiono szerokość bloku lewego: 50%, prawego 30% (width lub grid-template-columns lub flex)
ok. 1 min
Polecenie z arkusza
Dla bloku prawego: szerokość 30%
Szerokości obu bloków ustawione (R.4.5). Razem 80% szerokości okna, a cztery marginesy po 20 px zabierają jeszcze 80 px - do prawej krawędzi zostaje wolny pasek tła strony, jak na obrazie 2.
R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z Obrazem 2 w arkuszu egzaminacyjnym (prawidłowo zastosowano właściwości css układające bloki na stronie, np. float i clear albo display flex albo grid)
ok. 2 min
Polecenie z arkusza
Podział strony na bloki: lewy, 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
Obie sekcje z float: left stają obok siebie, a stopka z clear: both zaczyna się pod nimi, na całej szerokości (R.3.1). Bez clear stopka wsunęłaby się pod pływające bloki.
R.4.9 Ustawiono dla selektora obrazu szerokość 100%
ok. 1 min
Polecenie z arkusza
Dla selektora obrazu: szerokość 100%
Selektor znacznika img - zdjęcie wypełni szerokość bloku prawego i zmniejszy się razem z nim przy węższym oknie (R.4.9). Arkusz zastrzega selektory znaczników dla obrazu, nagłówków i paragrafu, bez klas.
Krok 31: Nagłówek pierwszego stopnia: pasek w kolorze stopki
Kryteria: , ,
R.4.2 Ustawiono kolor tła: biały dla bloku lewego i prawego, #5C6BC0 dla stopki oraz dla selektora h1
R.4.3 Ustawiono biały kolor czcionki dla stopki oraz h1
R.4.10 Ustawiono dla h1 grubość czcionki na 300
ok. 2 min
Polecenie z arkusza
Dla selektora nagłówka pierwszego stopnia: kolor tła #5C6BC0, biały kolor czcionki, marginesy wewnętrzne 10 px, brak marginesów zewnętrznych, grubość czcionki 300
tło #5C6BC0 i biały tekst - komplet kolorów tła (R.4.2) i białej czcionki (R.4.3),
padding: 10px odsuwa napis od brzegów paska, a margin: 0 usuwa domyślne marginesy nagłówka: pasek przylega do góry bloku,
font-weight: 300 - cieńsze litery niż zwykłe (400); nagłówki domyślnie są pogrubione (700) (R.4.10). Jeśli w systemie nie ma cienkiej odmiany kroju, przeglądarka pokaże zwykłą.
Krok 32: Nagłówki trzeciego stopnia i paragrafy: wcięcie 30 px
Kryteria:
R.4.7 Ustawiono marginesy zewnętrzne (margin) 20px dla bloku lewego i prawego; 0 dla h1 oraz marginesy wewnętrzne (padding) 30px dla stopki; 10px dla h1; lewy 30px (padding-left) dla selektora h3 i selektora p (wystarczy 5 z 7 cech)
ok. 1 min
Polecenie z arkusza
Dla selektora nagłówka trzeciego stopnia i paragrafu (akapitu): margines wewnętrzny lewy 30 px
padding-left: 30px - napisy „dane”, „adres”, dane pracownika i liczba odsuwają się od lewej krawędzi bloku. Marginesy zewnętrzne i wewnętrzne z arkusza są kompletne (R.4.7).
Krok 33: Nagłówki drugiego i czwartego stopnia: wyśrodkowanie
Kryteria:
R.4.6 Ustawiono wyrównanie tekstu do środka dla selektora h2 i h4
ok. 1 min
Polecenie z arkusza
Dla selektora nagłówka drugiego i czwartego stopnia: wyrównanie tekstu do środka
Imię i nazwisko (h2) i nazwa stanowiska (h4) w bloku prawym będą na środku; opis stanowiska w h5 zostaje przy lewej krawędzi, jak na obrazie 2 (R.4.6). Efekt zobaczysz po napisaniu skryptu 3.
Kroki 34-51 · po tym etapie masz 37 z 37 kryteriów (pkt)
Krok 34: Skrypt 1: połączenie z bazą firma
Kryteria:
R.5.1 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy firma
ok. 3 min
Polecenie z arkusza
Skrypt: Napisany w języku PHP. Skrypt łączy się z serwerem bazy danych na localhost, użytkownik root bez hasła, baza danych o nazwie firma
Dane pracownika mają stać pod nagłówkiem „Akta Pracownicze”, więc tam zaczyna się skrypt 1 - między <?php a ?>. Pierwsza instrukcja to mysqli_connect z tabeli 1 arkusza: serwer localhost, użytkownik root, puste hasło '' i nazwa bazy firma jako czwarty argument - to od razu wybór bazy, mysqli_select_db nie jest potrzebne (R.5.1). Połączenie trafia do zmiennej $connection; skorzystają z niej wszystkie trzy skrypty.
Od tego kroku podgląd to wynik działania strony na serwerze (prawdziwe uruchomienie w PHP 8.2) - na razie wygląda jak wcześniej. Stronę otwierasz przez http://localhost/…/index.php, a nie z dysku: przeglądarka sama nie wykona PHP.
R.5.3 Przynajmniej jedno zapytanie zostało wysłane do bazy: skrypt 1 - zapytanie 1 lub skrypt 2 - zapytanie 2 lub skrypt 3 - zapytanie 3 lub 4
ok. 3 min
Polecenie z arkusza
Skrypt 1 wysyła do bazy zapytanie 1 i zwrócone zapytaniem dane wypisuje według schematu:
mysqli_query($connection, '…') wysyła zapytanie 1 przekopiowane z kwerendy.txt i zwraca wynik (R.5.3),
mysqli_fetch_row($result) pobiera z wyniku jeden wiersz jako tablicę numerowaną od zera: $row[0] to imię, $row[1] nazwisko, $row[2] adres, $row[3] miasto, $row[4] czyRODO, $row[5] czyBadania - kolejność jak w SELECT.
Zapytanie zwraca jeden wiersz (warunek id = 2), więc pętla nie jest potrzebna. Strona jeszcze niczego nie wypisuje.
W napisie w cudzysłowach PHP podstawia wartości zmiennych: "<p>$row[0] $row[1]</p>" daje <p>Anna Nowak</p>. Spacja między zmiennymi to spacja między imieniem a nazwiskiem. W apostrofach podstawienie nie działa - na stronie pojawiłby się napis $row[0].
R.5.5 W skrypcie 1 zastosowano przynajmniej jedną instrukcję warunkową lub wyboru do sprawdzenia wartości zmiennej czyRODO lub czyBadania. Na stronie wyświetlony jest przynajmniej jeden komunikat zgodnie z wartością zmiennej logicznej
ok. 2 min
Polecenie z arkusza
Paragraf o treści „RODO: podpisano” lub „RODO: niepodpisano” w zależności od wartości pola czyRODO zwróconego zapytaniem. Operacja musi mieć charakter uniwersalny niezależnie od wartości zwracanej kwerendą 1
Instrukcja warunkowa if … else: gdy pole czyRODO ($row[4]) jest równe 1 - „podpisano”, w każdym innym przypadku - „niepodpisano”. To jest ten „uniwersalny charakter”: napis zależy od wartości z bazy, a nie jest wpisany na stałe dla Anny Nowak (R.5.5). Sprawdzone na pracowniku o id 3 (Katarzyna Kowalska, czyRODO = 0): strona wypisuje „RODO: niepodpisano”.
Typowe błędy
Samo echo "<p>RODO: podpisano</p>"; daje dla Anny Nowak ten sam obraz strony, ale nie spełnia R.5.5 - klucz wymaga instrukcji warunkowej.
Paragraf o treści „Badania: aktualne” lub „Badania: nieaktualne” w zależności od wartości pola czyBadania zwróconego zapytaniem. Operacja musi mieć charakter uniwersalny niezależnie od wartości zwracanej kwerendą 1
Ta sama konstrukcja dla $row[5] (czyBadania). Sprawdzone na pracowniku o id 4 (Ewelina Rak, czyBadania = 0): „Badania: nieaktualne”. Pod skryptem stoi linia <hr> z HTML-u - trzecia linia z obrazu 2.
Krok 44: Skrypt 2: liczba pracowników w paragrafie
Kryteria:
R.5.6 Skrypt 2 wyświetla wartość zwróconą zapytaniem 2
ok. 2 min
Polecenie z arkusza
Skrypt 2 wysyła do bazy zapytanie 2 i wypisuje liczbę wybraną zapytaniem
Skrypt 2 stoi wewnątrz przygotowanego paragrafu. Połączenie $connection z poprzedniego skryptu nadal działa - wszystkie bloki <?php jednej strony to jeden program, a zmienne przechodzą z bloku do bloku. Zapytanie 2 zwraca jeden wiersz z jedną kolumną, więc liczba to $row[0]: 9 (R.5.6).
Krok 45: Skrypt 3: zapytanie 3 i nazwa pliku ze zdjęciem
ok. 3 min
Polecenie z arkusza
Zawartość bloku prawego: efekt działania skryptu 3. Skrypt 3 wysyła do bazy zapytanie 3 a następnie: Ustala nazwę pliku obrazu według schematu: „<id>.jpg” gdzie <id> oznacza id zwrócone zapytaniem, a operacja ma charakter uniwersalny niezależnie od zwróconej wartości
Skrypt 3 w bloku prawym wysyła zapytanie 3: $row[0] to id, $row[1] imię, $row[2] nazwisko. Nazwa pliku powstaje z id i rozszerzenia - operator kropka skleja napisy w PHP: $row[0] . ".jpg" daje "2.jpg". Dla pracownika o id 7 byłoby to 7.jpg - stąd „charakter uniwersalny”.
Krok 46: Skrypt 3: zdjęcie z tekstem alternatywnym
Kryteria: ,
R.2.8 W bloku prawym umieszczono obraz z tekstem alternatywnym "pracownik" (w skrypcie lub HTML)
R.5.7 W skrypcie 3 ustalono nazwę pliku na podstawie wartości id zwróconej zapytaniem np. $wiersz[0] = (string) $wiersz[0] . ".jpg"; oraz wyświetlono obraz z pliku o tej nazwie
ok. 2 min
Polecenie z arkusza
Wyświetla obraz o ustalonej nazwie pliku z tekstem alternatywnym o treści „pracownik”
echo wypisuje znacznik <img> ze zmienną $file w atrybucie src. Wartości atrybutów są w apostrofach, bo cały napis jest w cudzysłowach - drugi cudzysłów zakończyłby napis PHP. Przeglądarka dostaje <img src='2.jpg' alt='pracownik'> (R.2.8) i wczytuje przeskalowane zdjęcie z folderu strony (R.5.7).
Kolejne zapytanie w tym samym skrypcie: zmienne $result i $row dostają nowe wartości - $row[0] to id, $row[1] nazwa stanowiska, $row[2] opis. Imię i nazwisko są już wypisane, więc stare wartości nie są potrzebne.
index.phpPHPzmienione: 2 linie
<!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Sekretariat</title> <linkrel="stylesheet"href="styl.css"></head><body> <sectionid="left"> <h1>Akta Pracownicze</h1><?php$connection = mysqli_connect('localhost', 'root', '', 'firma');$result = mysqli_query($connection, 'SELECT imie, nazwisko, adres, miasto, czyRODO, czyBadania FROM pracownicy WHERE id = 2;');$row = mysqli_fetch_row($result);echo"<h3>dane</h3>";echo"<p>$row[0]$row[1]</p>";echo"<hr>";echo"<h3>adres</h3>";echo"<p>$row[2]</p>";echo"<p>$row[3]</p>";echo"<hr>";if ($row[4] == 1) {echo"<p>RODO: podpisano</p>"; } else {echo"<p>RODO: niepodpisano</p>"; }if ($row[5] == 1) {echo"<p>Badania: aktualne</p>"; } else {echo"<p>Badania: nieaktualne</p>"; }?> <hr> <h3>Dokumenty pracownika</h3> <ahref="cv.txt">Pobierz</a> <h1>Liczba zatrudnionych pracowników</h1> <p><?php$result = mysqli_query($connection, 'SELECT COUNT(*) FROM pracownicy;');$row = mysqli_fetch_row($result);echo$row[0];?> </p> </section> <sectionid="right"><?php$result = mysqli_query($connection, 'SELECT id, imie, nazwisko FROM pracownicy WHERE id = 2;');$row = mysqli_fetch_row($result);$file = $row[0] . ".jpg";echo"<img src='$file' alt='pracownik'>";echo"<h2>$row[1]$row[2]</h2>";$result = mysqli_query($connection, 'SELECT pracownicy.id, nazwa, opis FROM pracownicy JOIN stanowiska ON pracownicy.stanowiska_id = stanowiska.id WHERE pracownicy.id = 2;');$row = mysqli_fetch_row($result);?> </section> <footer> Autorem aplikacji jest: Bartosz Bryniarski <ul> <li>skontaktuj się</li> <li>poznaj naszą firmę</li> </ul> </footer></body></html>
Wyświetla nazwę stanowiska w nagłówku czwartego stopnia
<h4> z nazwą „kierownik”, wyśrodkowany.
index.phpPHPzmienione: 1 linia
<!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Sekretariat</title> <linkrel="stylesheet"href="styl.css"></head><body> <sectionid="left"> <h1>Akta Pracownicze</h1><?php$connection = mysqli_connect('localhost', 'root', '', 'firma');$result = mysqli_query($connection, 'SELECT imie, nazwisko, adres, miasto, czyRODO, czyBadania FROM pracownicy WHERE id = 2;');$row = mysqli_fetch_row($result);echo"<h3>dane</h3>";echo"<p>$row[0]$row[1]</p>";echo"<hr>";echo"<h3>adres</h3>";echo"<p>$row[2]</p>";echo"<p>$row[3]</p>";echo"<hr>";if ($row[4] == 1) {echo"<p>RODO: podpisano</p>"; } else {echo"<p>RODO: niepodpisano</p>"; }if ($row[5] == 1) {echo"<p>Badania: aktualne</p>"; } else {echo"<p>Badania: nieaktualne</p>"; }?> <hr> <h3>Dokumenty pracownika</h3> <ahref="cv.txt">Pobierz</a> <h1>Liczba zatrudnionych pracowników</h1> <p><?php$result = mysqli_query($connection, 'SELECT COUNT(*) FROM pracownicy;');$row = mysqli_fetch_row($result);echo$row[0];?> </p> </section> <sectionid="right"><?php$result = mysqli_query($connection, 'SELECT id, imie, nazwisko FROM pracownicy WHERE id = 2;');$row = mysqli_fetch_row($result);$file = $row[0] . ".jpg";echo"<img src='$file' alt='pracownik'>";echo"<h2>$row[1]$row[2]</h2>";$result = mysqli_query($connection, 'SELECT pracownicy.id, nazwa, opis FROM pracownicy JOIN stanowiska ON pracownicy.stanowiska_id = stanowiska.id WHERE pracownicy.id = 2;');$row = mysqli_fetch_row($result);echo"<h4>$row[1]</h4>";?> </section> <footer> Autorem aplikacji jest: Bartosz Bryniarski <ul> <li>skontaktuj się</li> <li>poznaj naszą firmę</li> </ul> </footer></body></html>
Podgląd strony po tym kroku (index.php)
Krok 50: Skrypt 3: opis stanowiska
Kryteria: ,
R.2.6 W bloku lewym zapisano: jeden z nagłówków h1: "Akta Pracownicze" lub "Liczba zatrudnionych pracowników"; przynajmniej jeden nagłówek h3: "dane", "adres", "Dokumenty pracownika" (w skrypcie lub HTML) oraz przynajmniej jeden paragraf. W bloku prawym zapisano nagłówki h2, h4, h5 (w skrypcie lub HTML)
R.5.9 Skrypt 3 wyświetla nazwę stanowiska oraz opis stanowiska zwrócone zapytaniem
ok. 1 min
Polecenie z arkusza
Wyświetla opis stanowiska w nagłówku piątego stopnia
<h5> z opisem - bez wyśrodkowania, przy lewej krawędzi bloku, jak na obrazie 2. Komplet nagłówków w obu blokach jest na stronie (R.2.6, R.5.9).
index.phpPHPzmienione: 1 linia
<!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Sekretariat</title> <linkrel="stylesheet"href="styl.css"></head><body> <sectionid="left"> <h1>Akta Pracownicze</h1><?php$connection = mysqli_connect('localhost', 'root', '', 'firma');$result = mysqli_query($connection, 'SELECT imie, nazwisko, adres, miasto, czyRODO, czyBadania FROM pracownicy WHERE id = 2;');$row = mysqli_fetch_row($result);echo"<h3>dane</h3>";echo"<p>$row[0]$row[1]</p>";echo"<hr>";echo"<h3>adres</h3>";echo"<p>$row[2]</p>";echo"<p>$row[3]</p>";echo"<hr>";if ($row[4] == 1) {echo"<p>RODO: podpisano</p>"; } else {echo"<p>RODO: niepodpisano</p>"; }if ($row[5] == 1) {echo"<p>Badania: aktualne</p>"; } else {echo"<p>Badania: nieaktualne</p>"; }?> <hr> <h3>Dokumenty pracownika</h3> <ahref="cv.txt">Pobierz</a> <h1>Liczba zatrudnionych pracowników</h1> <p><?php$result = mysqli_query($connection, 'SELECT COUNT(*) FROM pracownicy;');$row = mysqli_fetch_row($result);echo$row[0];?> </p> </section> <sectionid="right"><?php$result = mysqli_query($connection, 'SELECT id, imie, nazwisko FROM pracownicy WHERE id = 2;');$row = mysqli_fetch_row($result);$file = $row[0] . ".jpg";echo"<img src='$file' alt='pracownik'>";echo"<h2>$row[1]$row[2]</h2>";$result = mysqli_query($connection, 'SELECT pracownicy.id, nazwa, opis FROM pracownicy JOIN stanowiska ON pracownicy.stanowiska_id = stanowiska.id WHERE pracownicy.id = 2;');$row = mysqli_fetch_row($result);echo"<h4>$row[1]</h4>";echo"<h5>$row[2]</h5>";?> </section> <footer> Autorem aplikacji jest: Bartosz Bryniarski <ul> <li>skontaktuj się</li> <li>poznaj naszą firmę</li> </ul> </footer></body></html>
Podgląd strony po tym kroku (index.php)
Krok 51: Zamknięcie połączenia
Kryteria:
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) jako ostatnia instrukcja skryptu 3 - ostatniego miejsca na stronie, które korzysta z bazy (R.5.2). Gdyby zamknięcie stało w skrypcie 1, skrypty 2 i 3 nie miałyby już połączenia.
index.phpPHPzmienione: 1 linia
<!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Sekretariat</title> <linkrel="stylesheet"href="styl.css"></head><body> <sectionid="left"> <h1>Akta Pracownicze</h1><?php$connection = mysqli_connect('localhost', 'root', '', 'firma');$result = mysqli_query($connection, 'SELECT imie, nazwisko, adres, miasto, czyRODO, czyBadania FROM pracownicy WHERE id = 2;');$row = mysqli_fetch_row($result);echo"<h3>dane</h3>";echo"<p>$row[0]$row[1]</p>";echo"<hr>";echo"<h3>adres</h3>";echo"<p>$row[2]</p>";echo"<p>$row[3]</p>";echo"<hr>";if ($row[4] == 1) {echo"<p>RODO: podpisano</p>"; } else {echo"<p>RODO: niepodpisano</p>"; }if ($row[5] == 1) {echo"<p>Badania: aktualne</p>"; } else {echo"<p>Badania: nieaktualne</p>"; }?> <hr> <h3>Dokumenty pracownika</h3> <ahref="cv.txt">Pobierz</a> <h1>Liczba zatrudnionych pracowników</h1> <p><?php$result = mysqli_query($connection, 'SELECT COUNT(*) FROM pracownicy;');$row = mysqli_fetch_row($result);echo$row[0];?> </p> </section> <sectionid="right"><?php$result = mysqli_query($connection, 'SELECT id, imie, nazwisko FROM pracownicy WHERE id = 2;');$row = mysqli_fetch_row($result);$file = $row[0] . ".jpg";echo"<img src='$file' alt='pracownik'>";echo"<h2>$row[1]$row[2]</h2>";$result = mysqli_query($connection, 'SELECT pracownicy.id, nazwa, opis FROM pracownicy JOIN stanowiska ON pracownicy.stanowiska_id = stanowiska.id WHERE pracownicy.id = 2;');$row = mysqli_fetch_row($result);echo"<h4>$row[1]</h4>";echo"<h5>$row[2]</h5>"; mysqli_close($connection);?> </section> <footer> Autorem aplikacji jest: Bartosz Bryniarski <ul> <li>skontaktuj się</li> <li>poznaj naszą firmę</li> </ul> </footer></body></html>
Kroki 52-54 · po tym etapie masz 37 z 37 kryteriów (pkt)
Krok 52: Zabezpieczenie: htmlspecialchars na danych z bazy
ok. 4 min
Polecenie z arkusza
Skrypt 1 wysyła do bazy zapytanie 1 i zwrócone zapytaniem dane wypisuje według schematu:
Skrypty wypisują dane z bazy wprost. Sprawdzone: gdy w bazie stoi nazwisko Nowak<script>alert("XSS")</script> (wystarczy, że ktoś wpisze je w formularz kadrowy, który zapisuje dane bez kontroli), strona wysyła ten kod do przeglądarki dwa razy - w paragrafie i w nagłówku <h2> - i przeglądarka go wykonuje. To jest XSS (wstrzyknięcie kodu): skrypt z bazy działa w przeglądarce każdej osoby, która otworzy akta - może np. wysłać komuś jej ciasteczka sesji.
htmlspecialchars zamienia znaki specjalne HTML na encje: < na <, > na >, cudzysłów na ". Przeglądarka pokazuje je jako zwykłe znaki i niczego nie wykonuje. Zamiast poprawiać każde echo, cały wiersz przechodzi przez funkcję od razu po pobraniu z bazy: array_map wywołuje ją dla każdego elementu tablicy, a fn($value) => … to krótki zapis funkcji jednej linii. (string) zamienia ewentualne NULL z bazy na pusty napis - nowe PHP ostrzega przy htmlspecialchars(null).
Podgląd tego kroku pokazuje stronę z rekordem-atakiem: kod wyświetla się jako tekst. Dla prawdziwych danych strona wygląda tak samo jak wcześniej.
Typowe błędy
htmlspecialchars przy zapisie do bazy zamiast przy wypisywaniu psuje dane (w bazie lądują encje) i nie chroni przed rekordami dopisanymi inną drogą. Zabezpiecza się wyjście - tam, gdzie dane trafiają do HTML-u.
Na egzaminie numer pracownika jest wpisany w zapytanie na stałe. W prawdziwej aplikacji przyjdzie z adresu, np. index.php?id=2, a doklejony do treści zapytania zamieni się w SQL injection. Sprawdzone w MariaDB: gdy zamiast 2 trafi tam napis 2 OR 1=1, zapytanie 1 ma warunek WHERE id = 2 OR 1=1, który jest prawdziwy dla wszystkich 9 pracowników, a mysqli_fetch_row pokazuje pierwszego z nich - Jana Nowaka zamiast Anny. Atakujący odczytuje w ten sposób akta, do których nie powinien mieć dostępu.
Numer trafia teraz do zmiennej $id, a trzy zapytania z warunkiem idą jako zapytania przygotowane:
mysqli_prepare - treść zapytania ze znakiem ? w miejscu danych,
mysqli_stmt_bind_param($statement, 'i', $id) - dane osobno, z typem i (liczba całkowita),
mysqli_stmt_execute i mysqli_stmt_get_result - wykonanie i wynik, z którego mysqli_fetch_row czyta tak jak wcześniej.
Baza dostaje zapytanie i dane oddzielnie, więc dane nigdy nie staną się kodem SQL. Sprawdzone w PHP: z $id = "2 OR 1=1" strona pokazuje Annę Nowak, bo typ i zamienia napis na liczbę 2. Zapytanie 2 nie ma żadnych danych z zewnątrz i zostaje bez zmian. 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.
index.phpPHPzmienione: 16 linii
<!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Sekretariat</title> <linkrel="stylesheet"href="styl.css"></head><body> <sectionid="left"> <h1>Akta Pracownicze</h1><?php$connection = mysqli_connect('localhost', 'root', '', 'firma');$id = 2;// $result = mysqli_query($connection, 'SELECT imie, nazwisko, adres, miasto, czyRODO, czyBadania FROM pracownicy WHERE id = 2;');$statement = mysqli_prepare($connection, 'SELECT imie, nazwisko, adres, miasto, czyRODO, czyBadania FROM pracownicy WHERE id = ?;'); mysqli_stmt_bind_param($statement, 'i', $id); mysqli_stmt_execute($statement);$result = mysqli_stmt_get_result($statement);$row = mysqli_fetch_row($result);$row = array_map(fn($value) => htmlspecialchars((string) $value), $row);echo"<h3>dane</h3>";echo"<p>$row[0]$row[1]</p>";echo"<hr>";echo"<h3>adres</h3>";echo"<p>$row[2]</p>";echo"<p>$row[3]</p>";echo"<hr>";if ($row[4] == 1) {echo"<p>RODO: podpisano</p>"; } else {echo"<p>RODO: niepodpisano</p>"; }if ($row[5] == 1) {echo"<p>Badania: aktualne</p>"; } else {echo"<p>Badania: nieaktualne</p>"; }?> <hr> <h3>Dokumenty pracownika</h3> <ahref="cv.txt">Pobierz</a> <h1>Liczba zatrudnionych pracowników</h1> <p><?php$result = mysqli_query($connection, 'SELECT COUNT(*) FROM pracownicy;');$row = mysqli_fetch_row($result);echo$row[0];?> </p> </section> <sectionid="right"><?php// $result = mysqli_query($connection, 'SELECT id, imie, nazwisko FROM pracownicy WHERE id = 2;');$statement = mysqli_prepare($connection, 'SELECT id, imie, nazwisko FROM pracownicy WHERE id = ?;'); mysqli_stmt_bind_param($statement, 'i', $id); mysqli_stmt_execute($statement);$result = mysqli_stmt_get_result($statement);$row = mysqli_fetch_row($result);$row = array_map(fn($value) => htmlspecialchars((string) $value), $row);$file = $row[0] . ".jpg";echo"<img src='$file' alt='pracownik'>";echo"<h2>$row[1]$row[2]</h2>";// $result = mysqli_query($connection, 'SELECT pracownicy.id, nazwa, opis FROM pracownicy JOIN stanowiska ON pracownicy.stanowiska_id = stanowiska.id WHERE pracownicy.id = 2;');$statement = mysqli_prepare($connection, 'SELECT pracownicy.id, nazwa, opis FROM pracownicy JOIN stanowiska ON pracownicy.stanowiska_id = stanowiska.id WHERE pracownicy.id = ?;'); mysqli_stmt_bind_param($statement, 'i', $id); mysqli_stmt_execute($statement);$result = mysqli_stmt_get_result($statement);$row = mysqli_fetch_row($result);$row = array_map(fn($value) => htmlspecialchars((string) $value), $row);echo"<h4>$row[1]</h4>";echo"<h5>$row[2]</h5>"; mysqli_close($connection);?> </section> <footer> Autorem aplikacji jest: Bartosz Bryniarski <ul> <li>skontaktuj się</li> <li>poznaj naszą firmę</li> </ul> </footer></body></html>
Podgląd strony po tym kroku (index.php)
Krok 54: Konto root bez hasła tylko na egzaminie
ok. 2 min
Polecenie z arkusza
Skrypt łączy się z serwerem bazy danych na localhost, użytkownik root bez hasła, baza danych o nazwie firma
Arkusz każe łączyć się kontem root bez hasła - w XAMPP to konto administratora całego serwera bazy danych, które może usunąć każdą bazę. Na egzaminie tak zostaje (R.5.1). W prawdziwym serwisie strona dostaje osobne konto z hasłem i tylko z tymi prawami, których potrzebuje - tu wystarczy odczyt:
CREATE USER 'sekretariat'@'localhost' IDENTIFIED BY 'długie-losowe-hasło';
GRANT SELECT ON firma.* TO 'sekretariat'@'localhost';
Wtedy nawet udany atak na stronę nie zmieni ani nie usunie danych. Hasła do bazy trzyma się w osobnym pliku konfiguracyjnym, najlepiej poza folderem dostępnym z przeglądarki - tak, by nie trafiło do repozytorium kodu ani na zrzut ekranu. Plik strony bez zmian.
index.phpPHPzmienione: 0 linii
<!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Sekretariat</title> <linkrel="stylesheet"href="styl.css"></head><body> <sectionid="left"> <h1>Akta Pracownicze</h1><?php$connection = mysqli_connect('localhost', 'root', '', 'firma');$id = 2;// $result = mysqli_query($connection, 'SELECT imie, nazwisko, adres, miasto, czyRODO, czyBadania FROM pracownicy WHERE id = 2;');$statement = mysqli_prepare($connection, 'SELECT imie, nazwisko, adres, miasto, czyRODO, czyBadania FROM pracownicy WHERE id = ?;'); mysqli_stmt_bind_param($statement, 'i', $id); mysqli_stmt_execute($statement);$result = mysqli_stmt_get_result($statement);$row = mysqli_fetch_row($result);$row = array_map(fn($value) => htmlspecialchars((string) $value), $row);echo"<h3>dane</h3>";echo"<p>$row[0]$row[1]</p>";echo"<hr>";echo"<h3>adres</h3>";echo"<p>$row[2]</p>";echo"<p>$row[3]</p>";echo"<hr>";if ($row[4] == 1) {echo"<p>RODO: podpisano</p>"; } else {echo"<p>RODO: niepodpisano</p>"; }if ($row[5] == 1) {echo"<p>Badania: aktualne</p>"; } else {echo"<p>Badania: nieaktualne</p>"; }?> <hr> <h3>Dokumenty pracownika</h3> <ahref="cv.txt">Pobierz</a> <h1>Liczba zatrudnionych pracowników</h1> <p><?php$result = mysqli_query($connection, 'SELECT COUNT(*) FROM pracownicy;');$row = mysqli_fetch_row($result);echo$row[0];?> </p> </section> <sectionid="right"><?php// $result = mysqli_query($connection, 'SELECT id, imie, nazwisko FROM pracownicy WHERE id = 2;');$statement = mysqli_prepare($connection, 'SELECT id, imie, nazwisko FROM pracownicy WHERE id = ?;'); mysqli_stmt_bind_param($statement, 'i', $id); mysqli_stmt_execute($statement);$result = mysqli_stmt_get_result($statement);$row = mysqli_fetch_row($result);$row = array_map(fn($value) => htmlspecialchars((string) $value), $row);$file = $row[0] . ".jpg";echo"<img src='$file' alt='pracownik'>";echo"<h2>$row[1]$row[2]</h2>";// $result = mysqli_query($connection, 'SELECT pracownicy.id, nazwa, opis FROM pracownicy JOIN stanowiska ON pracownicy.stanowiska_id = stanowiska.id WHERE pracownicy.id = 2;');$statement = mysqli_prepare($connection, 'SELECT pracownicy.id, nazwa, opis FROM pracownicy JOIN stanowiska ON pracownicy.stanowiska_id = stanowiska.id WHERE pracownicy.id = ?;'); mysqli_stmt_bind_param($statement, 'i', $id); mysqli_stmt_execute($statement);$result = mysqli_stmt_get_result($statement);$row = mysqli_fetch_row($result);$row = array_map(fn($value) => htmlspecialchars((string) $value), $row);echo"<h4>$row[1]</h4>";echo"<h5>$row[2]</h5>"; mysqli_close($connection);?> </section> <footer> Autorem aplikacji jest: Bartosz Bryniarski <ul> <li>skontaktuj się</li> <li>poznaj naszą firmę</li> </ul> </footer></body></html>
Krok 55 · po tym etapie masz 37 z 37 kryteriów (pkt)
Krok 55: Nagranie płyty
ok. 5 min
Polecenie z arkusza
UWAGA: katalog z rezultatami pracy oraz płytę należy opisać numerem, którym został podpisany arkusz, czyli numerem PESEL lub w przypadku jego braku numerem paszportu.
W folderze z numerem zdającego muszą być: 2.jpg (przeskalowany), cv.txt, import.jpg, kw1.png - kw4.png, kwerendy.txt, index.php i styl.css. Jeśli pracujesz w htdocs, skopiuj aktualne pliki do folderu na pulpicie. Nagraj płytę, sprawdź jej odczyt i opisz ją numerem zdającego.
Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.
To jedno z możliwych rozwiązań. Egzaminator ocenia spełnienie kryteriów z klucza odpowiedzi, a nie zgodność z tym kodem - inna, poprawna droga też daje punkty.
Autor rozwiązania: Bartosz Bryniarski. Licencja CC BY-NC 4.0 - kopiowanie i przeróbki do celów niekomercyjnych, z podaniem autora i źródła (link do tej strony) (zasady wykorzystania). Treść arkusza i klucza oceniania pochodzi z CKE.