Przejdź do głównej treści
INF.03-10-26.06-SGCzerwiec 2026 · 53 kroki · ok. 91 min · 41 kryteriów z klucza

Rozwiązanie krok po kroku: Portal wyników laboratorium (Wersja podstawowa)

Rozwiązanie buduje portal, na którym pracownik laboratorium loguje się i przegląda wyniki pacjentów: baza laboratorium (diagnosta, pacjent, zlecenie, wynik) i cztery zapytania - sortowanie malejące, grupowanie po samej dacie GROUP BY DATE(...), złączenie z warunkiem na nazwisko i złączenie zewnętrzne LEFT JOIN, które pokazuje także zlecenia bez wyniku, potem trzy pliki PHP korzystające z sesji: login.php z formularzem logowania, wyniki.php dostępna tylko po zalogowaniu (tabela z bazy z odnośnikami do plików PDF) i wyloguj.php, oraz style w styl.css - z cieniami bloków i półprzezroczystym tłem wiersza pod kursorem.

Kroki idą zdanie po zdaniu za treścią arkusza - każdy ma polecenie z arkusza, a kod pokazuje dokładnie te linie, które je spełniają. Skrypty są najpierw pisane najprościej, funkcjami z tabel 1-3 arkusza (session_start, $_SESSION, header, mysqli_query, mysqli_fetch_row), a na końcu zostają przerobione na wersję z prawdziwych projektów: exit po przekierowaniu (wersja z egzaminu wysyła tabelę z danymi pacjentów także niezalogowanym - sprawdzone), hasło zapisane jako skrót, nowa sesja po zalogowaniu i htmlspecialchars.

Baza i pliki PDF z wynikami pochodzą z prawdziwego archiwum pliki20 (załącznik do arkusza, hasło w treści zadania). Zrzuty wyników zapytań są odtworzone z tej bazy - na egzaminie robisz je sam, z całego ekranu. Podgląd stron PHP to wynik prawdziwego uruchomienia kodu z danego kroku w PHP 8.2 na bazie z arkusza; strona wyników pokazuje stan po zalogowaniu jako admin, jak na ilustracji 3.

To jest wersja podstawowa. W zakładce „Nowoczesny kod (PDO, bezpieczeństwo)” jest drugi sposób rozwiązania tego samego arkusza: PHP na PDO z wyjątkami, exit po każdym przekierowaniu, htmlspecialchars, filter_input i obsługa awarii bazy od pierwszej linii - obie wersje są zgodne z kluczem.

Jak liczą się punkty. Klucz ma 41 kryteriów (R.1.1 - R.5.10), 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.

  1. Etap 1: Baza danych

    Kroki 1-7 · po tym etapie masz 6 z 41 kryteriów (pkt)

    Krok 1: Baza danych laboratorium

    ok. 2 min
    Polecenie z arkusza

    Utwórz bazę danych o nazwie laboratorium, z zestawem polskich znaków (np. utf8_polish_ci).

    Rozpakuj pliki20.7z z pulpitu (hasło LaBoRaToR2um - z cyfrą 2 w środku) do folderu z numerem zdającego: laboratorium.sql i trzy pliki PDF z wynikami badań (nowicki.pdf, kubicka.pdf, tulikowska.pdf). W phpMyAdmin: Nowa, nazwa laboratorium, porównywanie utf8_polish_ci.

  2. Krok 2: Import tabel z pliku laboratorium.sql

    ok. 2 min
    Polecenie z arkusza

    Do utworzonej bazy zaimportuj tabele z pliku laboratorium.sql.

    Import → laboratorium.sql → Wykonaj. Powstają cztery tabele z ilustracji 1: Diagnosta, Pacjent, Zlecenie (data i godzina, pacjent) i Wynik (plik PDF, data wykonania, diagnosta, zlecenie). Wyniki są tylko dla trzech z dwunastu zleceń - to ważne w zapytaniu 4.

  3. Krok 3: Zrzut ekranu import.png

    Kryteria:

    R.1.1 Wykonany import tabel do bazy danych laboratorium - plik 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ń. Na zrzucie powinny być widoczne elementy wskazujące na poprawnie wykonany import tabel.

    Print Screen, wklejasz do edytora grafiki i zapisujesz jako import.png. W tym arkuszu wszystkie zrzuty są w PNG.

    Typowe błędy

    Wykadrowany zrzut albo zrzut bez paska zadań nie jest oceniany (uwaga w kluczu przy R.1).

  4. Krok 4: Zapytanie 1: diagności malejąco po nazwisku

    Kryteria: ,

    R.1.2 W pliku kwerendy.txt zapisane co najmniej jedno zapytanie SQL, wynikające z treści zadania

    R.1.3 Wykonane zapytanie 1 wybierające jedynie imię i nazwisko diagnosty laboratoryjnego z tabeli Diagnosta posortowane malejąco według nazwiska. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT imie,nazwisko FROM Diagnosta ORDER BY nazwisko DESC; oraz na zrzucie widocznych jest dokładnie 10 rekordów

    ok. 1 min
    Polecenie z arkusza

    Zapytanie 1: wybierające jedynie imię i nazwisko diagnosty laboratoryjnego z tabeli Diagnosta posortowane malejąco według nazwiska.

    • SELECT imie, nazwisko FROM Diagnosta - dwie kolumny, bez numeru PWZDL,
    • ORDER BY nazwisko DESC - malejąco, od Z do A.

    Wynik: 10 wierszy, od Anny Zalewskiej do Tomasza Bieleckiego. Zapytania zapisujesz w kwerendy.txt, zrzut jako kw1.png.

    kwerendy.txt Tekst nowy plik
    -- Zapytanie 1
    SELECT imie, nazwisko FROM Diagnosta ORDER BY nazwisko DESC;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 4
  5. Krok 5: Zapytanie 2: liczba zleceń w kolejnych dniach

    Kryteria:

    R.1.4 Wykonane zapytanie 2 zliczające liczbę zleceń pogrupowanych po datach (bez uwzględnienia godziny). W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT DATE(data),COUNT(*)FROM Zlecenie GROUP BY DATE(data); oraz na zrzucie widocznych jest dokładnie 6 rekordów

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 2: zliczające liczbę zleceń pogrupowanych po datach (bez uwzględnienia godziny). Wybierane są jedynie daty i liczby zleceń.

    • DATE(data) (tabela 4) - z pola z datą i godziną zostaje sama data,
    • GROUP BY DATE(data) - grupujesz po tej samej obciętej dacie, więc zlecenia z jednego dnia o różnych godzinach trafiają do jednej grupy,
    • COUNT(*) - liczba zleceń w grupie.

    Wynik: 6 wierszy - po jednym na dzień. Sprawdzone: GROUP BY data bez DATE() daje 12 grup, bo każde zlecenie ma inną godzinę. Zrzut: kw2.png.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    SELECT imie, nazwisko FROM Diagnosta ORDER BY nazwisko DESC;
    
    -- Zapytanie 2
    SELECT DATE(data), COUNT(*) FROM Zlecenie GROUP BY DATE(data);
    Podgląd strony po tym kroku
    Podgląd strony po kroku 5
  6. Krok 6: Zapytanie 3: daty zleceń pacjenta Nowickiego

    Kryteria:

    R.1.5 Wykonane zapytanie 3 wybierające tylko daty zleceń dla pacjenta o nazwisku Nowicki. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT data FROM Zlecenie JOIN Pacjent ON Pacjent.id = idPacjenta WHERE nazwisko = "Nowicki"; (możliwe również porównanie kluczy po WHERE) oraz na zrzucie widoczne są dokładnie 2 rekordy

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 3: wybierające jedynie daty zleceń dla pacjenta o nazwisku Nowicki. Należy posłużyć się relacją.

    • FROM Zlecenie JOIN Pacjent ON Zlecenie.idPacjenta = Pacjent.id - zlecenie zna tylko identyfikator pacjenta, nazwisko jest w drugiej tabeli,
    • WHERE Pacjent.nazwisko = 'Nowicki',
    • SELECT Zlecenie.data - obie tabele mogłyby mieć kolumnę o tej nazwie, więc podajesz tabelę.

    Wynik: 2 wiersze - zlecenia z 8 czerwca 2024 o 10:00 i 11:00. Zrzut: kw3.png.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    SELECT imie, nazwisko FROM Diagnosta ORDER BY nazwisko DESC;
    
    -- Zapytanie 2
    SELECT DATE(data), COUNT(*) FROM Zlecenie GROUP BY DATE(data);
    
    -- Zapytanie 3
    SELECT Zlecenie.data FROM Zlecenie JOIN Pacjent ON Zlecenie.idPacjenta = Pacjent.id WHERE Pacjent.nazwisko = 'Nowicki';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 6
  7. Krok 7: Zapytanie 4: wszystkie zlecenia z wynikiem albo bez

    Kryteria:

    R.1.6 Wykonane zapytanie 4 wybierające jedynie imię, nazwisko oraz pesel pacjenta z tabeli Pacjent, datę z tabeli Zlecenie, datę i plik z tabeli Wynik. W wyniku wyświetlane są wszystkie zlecenia (także te, które nie mają jeszcze wyniku) . W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT imie, nazwisko, PESEL, Zlecenie.data, Wynik.data, plik FROM Pacjent JOIN Zlecenie ON idPacjenta = Pacjent.id LEFT JOIN Wynik ON Zlecenie.id = idZlecenia; (możliwe użycie dwa razy LEFT JOIN) oraz na zrzucie widocznych jest dokładnie 12 rekordów

    ok. 3 min
    Polecenie z arkusza

    Zapytanie 4: wybierające jedynie imię, nazwisko oraz pesel pacjenta z tabeli Pacjent, datę z tabeli Zlecenie, datę i plik z tabeli Wynik. Należy posłużyć się relacją i odpowiednim rodzajem złączenia. Wybierane są wszystkie zlecenia niezależenie od tego czy jest im przypisany wynik.

    • FROM Pacjent JOIN Zlecenie ON Zlecenie.idPacjenta = Pacjent.id - każde zlecenie ma pacjenta, więc tu wystarczy zwykłe złączenie,
    • LEFT JOIN Wynik ON Wynik.idZlecenia = Zlecenie.id - złączenie zewnętrzne: zlecenie bez wyniku też trafia do wyniku, a pola z tabeli Wynik są wtedy NULL,
    • dwie kolumny data (zlecenia i wyniku) rozróżniasz nazwą tabeli.

    Wynik: 12 wierszy, w tym 9 z pustą datą wykonania i plikiem. Sprawdzone: zwykły JOIN zamiast LEFT JOIN daje tylko 3 wiersze. To zapytanie wyświetli skrypt 3. Zrzut: kw4.png.

    Typowe błędy

    JOIN Wynik (wewnętrzne) gubi zlecenia bez wyniku - na zrzucie są 3 wiersze zamiast 12.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    SELECT imie, nazwisko FROM Diagnosta ORDER BY nazwisko DESC;
    
    -- Zapytanie 2
    SELECT DATE(data), COUNT(*) FROM Zlecenie GROUP BY DATE(data);
    
    -- Zapytanie 3
    SELECT Zlecenie.data FROM Zlecenie JOIN Pacjent ON Zlecenie.idPacjenta = Pacjent.id WHERE Pacjent.nazwisko = 'Nowicki';
    
    -- Zapytanie 4
    SELECT Pacjent.imie, Pacjent.nazwisko, Pacjent.pesel, Zlecenie.data, Wynik.data, Wynik.plik FROM Pacjent JOIN Zlecenie ON Zlecenie.idPacjenta = Pacjent.id LEFT JOIN Wynik ON Wynik.idZlecenia = Zlecenie.id;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 7
  8. Etap 2: Strony HTML

    Kroki 8-24 · po tym etapie masz 17 z 41 kryteriów (pkt)

    Krok 8: Plik login.php w HTML5

    ok. 2 min
    Polecenie z arkusza

    Witryna internetowa składa się ze stron login.php, wyniki.php oraz pliku wyloguj.php. Cechy wspólne dla stron login.php i wyniki.php: Zapisana w języku HTML5.

    Zacznij od login.php; wyniki.php powstanie z kopii, bo nagłówek i stopka są wspólne. Rozszerzenie .php, bo dojdą skrypty z sesją; pliki leżą w htdocs XAMPP-a i otwierasz je przez http://localhost/....

    login.php PHP nowy plik
    <!DOCTYPE html>
    <html>
    <head>
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (login.php)
  9. Krok 9: Język strony

    Kryteria:

    R.2.1 W plikach login.php lub wyniki.php znajduje się deklaracja HTML5 <!DOCTYPE HTML>, deklaracja języka zawartości: polski np. <html lang="pl">

    ok. 1 min
    Polecenie z arkusza

    Zadeklarowany polski język zawartości witryny.

    lang="pl" w znaczniku <html> - razem z deklaracją HTML5 to R.2.1.

    login.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (login.php)
  10. Krok 10: Kodowanie polskich znaków

    ok. 1 min
    Polecenie z arkusza

    Jawnie zastosowany właściwy standard kodowania polskich znaków.

    <meta charset="UTF-8"> jako pierwszy element <head>.

    login.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (login.php)
  11. Krok 11: Tytuł strony logowania

    Kryteria:

    R.2.2 W sekcji head znajduje się jawnie podany standard kodowania polskich znaków i stronie login.php nadano tytuł "Zaloguj się" lub stronie wyniki.php tytuł "Przeglądaj wyniki"

    ok. 1 min
    Polecenie z arkusza

    Cechy strony login.php: Tytuł strony widoczny na karcie przeglądarki: „Zaloguj się”.

    <title> w <head>. Kodowanie i tytuł razem to R.2.2.

    login.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Zaloguj się</title>
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (login.php)
  12. Krok 12: Podpięcie arkusza stylów

    Kryteria:

    R.3.2 Strona zawiera działające połączenie z zewnętrznym arkuszem stylów o nazwie styl.css formatowanie CSS pochodzi jedynie z tego arkusza

    ok. 1 min
    Polecenie z arkusza

    Arkusz stylów w pliku o nazwie styl.css prawidłowo połączony z kodem strony.

    Nazwa: styl.css. Całe formatowanie ma pochodzić z tego pliku (R.3.2).

    login.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Zaloguj się</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (login.php)
  13. Krok 13: Bloki: nagłówek, blok główny, stopka

    Kryteria:

    R.2.3 W sekcji body zdefiniowany jest podział na bloki za pomocą semantycznych znaczników HTML 5: header, main, footer (dopuszcza się stosowanie bloków pomocniczych)

    ok. 2 min
    Polecenie z arkusza

    Podział strony na bloki: nagłówkowy, główny oraz stopka. Podział zrealizowany za pomocą semantycznych znaczników języka HTML5, tak aby po uruchomieniu w przeglądarce układ bloków na stronie był zgody z ilustracją 4.

    <header>, <main> i <footer> (R.2.3). Blok główny strony logowania ma inny wygląd niż blok główny strony wyników (szerokość, tło, cień), więc dostaje identyfikator login-box. Bloki stoją jeden pod drugim - dokładnie jak na ilustracji 4.

    login.php PHP zmienione: 6 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Zaloguj się</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
        </header>
        <main id="login-box">
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (login.php)
  14. Krok 14: Nagłówek strony

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku nagłówkowego: nagłówek pierwszego stopnia o treści „Portal laboratorium”.

    <h1> w <header>.

    login.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Zaloguj się</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Portal laboratorium</h1>
        </header>
        <main id="login-box">
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (login.php)
  15. Krok 15: Stopka

    ok. 1 min
    Polecenie z arkusza

    Zwartość bloku stopki: Paragraf o treści „Wykonał:” dalej wstawiony numer zdającego.

    Akapit w <footer>. Na egzaminie po dwukropku wpisujesz swój numer zdającego (PESEL) - tutaj stoi imię i nazwisko autora rozwiązania.

    login.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Zaloguj się</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Portal laboratorium</h1>
        </header>
        <main id="login-box">
        </main>
        <footer>
            <p>Wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (login.php)
  16. Krok 16: Nagłówek formularza

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku głównego: Nagłówek drugiego stopnia o treści „Zaloguj się do systemu”.

    <h2> w bloku głównym.

    login.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Zaloguj się</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Portal laboratorium</h1>
        </header>
        <main id="login-box">
            <h2>Zaloguj się do systemu</h2>
        </main>
        <footer>
            <p>Wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (login.php)
  17. Krok 17: Formularz wysyłany metodą POST do siebie

    Kryteria:

    R.3.3 Formularz wysyła dane metodą POST do tego samego pliku (poprawne też bez atrybutu action)

    ok. 1 min
    Polecenie z arkusza

    Formularz wysyłający dane metodą bezpieczną do pliku login.php.

    method="post" - „metoda bezpieczna” w rozumieniu arkusza: login i hasło idą w treści żądania, a nie w adresie strony (przy get hasło byłoby widać w pasku adresu i w historii). 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. action="login.php" - dane wracają do tego samego pliku, w którym skrypt 1 je sprawdzi (R.3.3).

    Typowe błędy

    method="get" dopisuje hasło do adresu (login.php?login=admin&password=...) - arkusz pod „metodą bezpieczną” ma na myśli POST i klucz uznaje tylko taki zapis.

    login.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Zaloguj się</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Portal laboratorium</h1>
        </header>
        <main id="login-box">
            <h2>Zaloguj się do systemu</h2>
            <form action="login.php" method="post">
            </form>
        </main>
        <footer>
            <p>Wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (login.php)
  18. Krok 18: Etykieta i pole loginu

    ok. 2 min
    Polecenie z arkusza

    Etykieta o treści „Login:”. Pole edycyjne.

    <label for="login"> jest powiązana z polem przez for równe jego id - kliknięcie napisu ustawia kursor w polu, a czytnik ekranu odczyta, czego pole dotyczy. name="login" to klucz, pod którym wpis trafi do $_POST.

    login.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Zaloguj się</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Portal laboratorium</h1>
        </header>
        <main id="login-box">
            <h2>Zaloguj się do systemu</h2>
            <form action="login.php" method="post">
                <label for="login">Login:</label>
                <input type="text" id="login" name="login">
            </form>
        </main>
        <footer>
            <p>Wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (login.php)
  19. Krok 19: Etykieta i pole hasła

    Kryteria:

    R.3.5 Pole z hasłem jest typu password

    ok. 1 min
    Polecenie z arkusza

    Etykieta o treści „Hasło:”. Pole edycyjne służące, do wpisywania hasła, ukrywające wprowadzone znaki.

    type="password" zastępuje wpisywane znaki kropkami (R.3.5). Ukrywa je tylko na ekranie - przy wysyłaniu hasło idzie zwykłym tekstem, dlatego formularz używa POST (a na prawdziwym serwerze także HTTPS).

    login.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Zaloguj się</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Portal laboratorium</h1>
        </header>
        <main id="login-box">
            <h2>Zaloguj się do systemu</h2>
            <form action="login.php" method="post">
                <label for="login">Login:</label>
                <input type="text" id="login" name="login">
                <label for="password">Hasło:</label>
                <input type="password" id="password" name="password">
            </form>
        </main>
        <footer>
            <p>Wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (login.php)
  20. Krok 20: Przycisk „Zaloguj”

    Kryteria:

    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 o treści „Zaloguj” wysyłający dane z formularza do skryptu 1.

    <button type="submit"> wysyła formularz pod adres z action - czyli znów do login.php, gdzie dane odbierze skrypt 1 (R.3.4).

    login.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Zaloguj się</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Portal laboratorium</h1>
        </header>
        <main id="login-box">
            <h2>Zaloguj się do systemu</h2>
            <form action="login.php" method="post">
                <label for="login">Login:</label>
                <input type="text" id="login" name="login">
                <label for="password">Hasło:</label>
                <input type="password" id="password" name="password">
                <button type="submit">Zaloguj</button>
            </form>
        </main>
        <footer>
            <p>Wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (login.php)
  21. Krok 21: Układ pól jak na ilustracji 2

    Kryteria:

    R.2.5 Strona z logowaniem zawiera formularz składający się z dwóch pól edycyjnych i przycisku. Co najmniej jedno pole ma powiązaną etykietę (label z for i id lub zagnieżdżenie kontrolki w label)

    ok. 1 min
    Polecenie z arkusza

    Układ pól zgodny z ilustracją 2. Etykiety są powiązane z odpowiednimi polami edycyjnymi.

    Na ilustracji 2 każda para etykieta - pole stoi w osobnej linii, a przycisk pod nimi. Pola i przycisk są elementami liniowymi, więc łamanie wierszy dają dwa znaczniki <br>. Formularz ma komplet: dwa pola edycyjne, przycisk i etykiety powiązane przez for i id (R.2.5).

    login.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Zaloguj się</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Portal laboratorium</h1>
        </header>
        <main id="login-box">
            <h2>Zaloguj się do systemu</h2>
            <form action="login.php" method="post">
                <label for="login">Login:</label>
                <input type="text" id="login" name="login"><br>
                <label for="password">Hasło:</label>
                <input type="password" id="password" name="password"><br>
                <button type="submit">Zaloguj</button>
            </form>
        </main>
        <footer>
            <p>Wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (login.php)
  22. Krok 22: Strona wyniki.php: tytuł i nagłówek bloku głównego

    Kryteria:

    R.2.4 W bloku nagłówkowym znajduje się <h1>: "Portal laboratorium", w stopce <p> z numerem zdającego, w bloku głównym strony logowania <h2>: "Zaloguj się do systemu", w bloku głównym strony z wynikami <h2>: "Wyniki pacjentów"

    ok. 2 min
    Polecenie z arkusza

    Cechy strony wyniki.php: Tytuł strony widoczny na karcie przeglądarki: „Przeglądaj wyniki”. Zawartość bloku głównego: Nagłówek drugiego stopnia o treści „Wyniki pacjentów”.

    Zapisz login.php jako wyniki.php: nowy tytuł, a blok główny - bez identyfikatora login-box, bo nie ma wyglądu okna logowania - dostaje <h2>. Komplet nagłówków i stopki z kryterium R.2.4: <h1> w nagłówku, <p> z numerem w stopce i oba <h2>.

    wyniki.php PHP nowy plik
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Przeglądaj wyniki</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Portal laboratorium</h1>
        </header>
        <main>
            <h2>Wyniki pacjentów</h2>
        </main>
        <footer>
            <p>Wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (wyniki.php)
  23. Krok 23: Odnośnik „Wyloguj”

    Kryteria:

    R.3.6 Odnośnik o treści "Wyloguj" otwiera stronę wylogowania

    ok. 1 min
    Polecenie z arkusza

    Paragraf z odnośnikiem do pliku wyloguj.php o treści „Wyloguj”.

    Akapit z odnośnikiem w nagłówku. Plik wyloguj.php napiszesz w etapie skryptów - samo kliknięcie otwiera stronę wylogowania (R.3.6). Nad odnośnikiem stanie powitanie ze skryptu 2.

    wyniki.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Przeglądaj wyniki</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Portal laboratorium</h1>
            <p><a href="wyloguj.php">Wyloguj</a></p>
        </header>
        <main>
            <h2>Wyniki pacjentów</h2>
        </main>
        <footer>
            <p>Wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (wyniki.php)
  24. Krok 24: Blok z tabelą i wiersz nagłówków

    Kryteria:

    R.2.7 Pierwszy wiersz tabeli zawiera komórki nagłówkowe o treści: "Imię pacjenta", "Nazwisko pacjenta", "PESEL", "Data zlecenia", "Data wykonania", "Wynik", znaczniki <tr>, <th>

    ok. 2 min
    Polecenie z arkusza

    Blok zawierający tabelę z 6 kolumnami. Pierwszy wiersz zawiera komórki nagłówkowe kolejno: „Imię pacjenta”, „Nazwisko pacjenta”, „PESEL”, „Data zlecenia”, „Data wykonania”, „Wynik”. Zawartość tabeli generowana jest skryptem 3.

    Blok <div id="results"> z tabelą. Pierwszy wiersz to komórki nagłówkowe <th> - przeglądarka pogrubia je i wyśrodkowuje (R.2.7). Pozostałe wiersze wypisze skrypt 3, dlatego stanie on wewnątrz <table>, pod tym wierszem.

    Typowe błędy

    Na ilustracji 3 ostatnia kolumna ma napis „Wyniki”, a w treści arkusza „Wynik” - wpisz jak w treści i w kluczu (R.2.7); drobne różnice w napisach klucz i tak dopuszcza.

    wyniki.php PHP zmienione: 12 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Przeglądaj wyniki</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Portal laboratorium</h1>
            <p><a href="wyloguj.php">Wyloguj</a></p>
        </header>
        <main>
            <h2>Wyniki pacjentów</h2>
            <div id="results">
                <table>
                    <tr>
                        <th>Imię pacjenta</th>
                        <th>Nazwisko pacjenta</th>
                        <th>PESEL</th>
                        <th>Data zlecenia</th>
                        <th>Data wykonania</th>
                        <th>Wynik</th>
                    </tr>
                </table>
            </div>
        </main>
        <footer>
            <p>Wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (wyniki.php)
  25. Etap 3: Styl CSS

    Kroki 25-38 · po tym etapie masz 28 z 41 kryteriów (pkt)

    Krok 25: Plik styl.css: ciało strony

    Kryteria:

    R.4.1 selektor body: krój czcionki Verdana - jako pierwszy, sans-serif - jako ostatni, kolor tła #D4F1F4, kolor czcionki #05445E

    ok. 1 min
    Polecenie z arkusza

    Dla selektora ciała strony: krój czcionki Verdana, w przypadku jego braku sans-serif, kolor tła #D4F1F4, kolor czcionki #05445E.

    Selektor znacznika body (uwaga pod listą stylów). Krój dziedziczą wszystkie elementy; sans-serif to rodzina zastępcza (R.4.1).

    styl.css CSS nowy plik
    body {
        font-family: Verdana, sans-serif;
        background-color: #D4F1F4;
        color: #05445E;
    }
    Podgląd strony po tym kroku (login.php)
  26. Krok 26: Okno logowania: szerokość i wyśrodkowanie

    Kryteria:

    R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z ilustracją 4 w arkuszu egzaminacyjnym (żaden blok nie zachodzi na inne bloki - sprawdzić w inspektorze)

    ok. 1 min
    Polecenie z arkusza

    Dla bloku głównego na stronie login.php: szerokość 50 em, marginesy zewnętrzne automatycznie wyliczane przez przeglądarkę, marginesy wewnętrzne 20 px.

    margin: auto przy ustalonej szerokości dzieli wolne miejsce po równo na lewy i prawy margines - blok staje na środku strony. Bloki i tak stoją jeden pod drugim (zwykły przepływ dokumentu), więc układ z ilustracji 4 nie wymaga ani float, ani flex (R.3.1).

    styl.css CSS zmienione: 5 linii
    body {
        font-family: Verdana, sans-serif;
        background-color: #D4F1F4;
        color: #05445E;
    }
    #login-box {
        width: 50em;
        margin: auto;
        padding: 20px;
    }
    Podgląd strony po tym kroku (login.php)
  27. Krok 27: Stopka do prawej

    ok. 1 min
    Polecenie z arkusza

    Dla bloku stopki: wyrównanie tekstu do prawej strony.

    text-align: right.

    styl.css CSS zmienione: 3 linie
    body {
        font-family: Verdana, sans-serif;
        background-color: #D4F1F4;
        color: #05445E;
    }
    #login-box {
        width: 50em;
        margin: auto;
        padding: 20px;
    }
    footer {
        text-align: right;
    }
    Podgląd strony po tym kroku (login.php)
  28. Krok 28: Nagłówek: marginesy zewnętrzne

    ok. 1 min
    Polecenie z arkusza

    Dla bloku nagłówkowego: marginesy zewnętrzne 40 px.

    Odsuwa pasek nagłówka od krawędzi okna.

    styl.css CSS zmienione: 3 linie
    body {
        font-family: Verdana, sans-serif;
        background-color: #D4F1F4;
        color: #05445E;
    }
    #login-box {
        width: 50em;
        margin: auto;
        padding: 20px;
    }
    footer {
        text-align: right;
    }
    header {
        margin: 40px;
    }
    Podgląd strony po tym kroku (login.php)
  29. Krok 29: Blok z tabelą: marginesy

    ok. 1 min
    Polecenie z arkusza

    Dla bloku z tabelą: marginesy zewnętrzne 40 px, marginesy wewnętrzne 20 px.

    Styl bloku results na stronie wyników - w podglądzie strony logowania nic się nie zmienia.

    styl.css CSS zmienione: 4 linie
    body {
        font-family: Verdana, sans-serif;
        background-color: #D4F1F4;
        color: #05445E;
    }
    #login-box {
        width: 50em;
        margin: auto;
        padding: 20px;
    }
    footer {
        text-align: right;
    }
    header {
        margin: 40px;
    }
    #results {
        margin: 40px;
        padding: 20px;
    }
    Podgląd strony po tym kroku (login.php)
  30. Krok 30: Wspólny wygląd trzech bloków: tło, zaokrąglenie, cień

    Kryteria: , ,

    R.4.2 kolor tła: dla bloku głównego na stronie logowania, dla bloku nagłówkowego, dla bloku z tabelą - #75E6DA

    R.4.7 zaokrąglenie obramowania: blok główny, nagłówkowy, blok z tabelą - 5px

    R.4.8 cień elementu: blok główny, nagłówkowy, blok z tabelą - box-shadow: 10px 10px 5px LightBlue

    ok. 2 min
    Polecenie z arkusza

    Dla bloku głównego na stronie login.php, dla bloku nagłówkowego oraz dla bloku z tabelą: kolor tła #75E6DA, zaokrąglenie obramowania 5 px, cień elementu o przesunięciu poziomym i pionowym 10 px, rozmyciu 5 px i kolorze LightBlue.

    Jedna reguła dla trzech selektorów oddzielonych przecinkami. box-shadow: 10px 10px 5px LightBlue - kolejność z tabeli 5: przesunięcie w poziomie, w pionie, rozmycie i kolor; dodatnie przesunięcia kładą cień w prawo i w dół (R.4.2, R.4.7, R.4.8).

    styl.css CSS zmienione: 5 linii
    body {
        font-family: Verdana, sans-serif;
        background-color: #D4F1F4;
        color: #05445E;
    }
    #login-box {
        width: 50em;
        margin: auto;
        padding: 20px;
    }
    footer {
        text-align: right;
    }
    header {
        margin: 40px;
    }
    #results {
        margin: 40px;
        padding: 20px;
    }
    #login-box, header, #results {
        background-color: #75E6DA;
        border-radius: 5px;
        box-shadow: 10px 10px 5px LightBlue;
    }
    Podgląd strony po tym kroku (login.php)
  31. Krok 31: Nagłówki i formularz na środku

    Kryteria:

    R.4.4 wyrównanie tekstu: blok stopki - do prawej strony, selektory h1, h2, form - do środka

    ok. 1 min
    Polecenie z arkusza

    Dla selektora nagłówka pierwszego stopnia, selektora nagłówka drugiego stopnia oraz selektora formularza: wyrównanie tekstu do środka.

    Selektory znaczników, jak wymaga uwaga. Wyrównanie formularza do środka centruje etykiety, pola i przycisk. Razem ze stopką to R.4.4.

    styl.css CSS zmienione: 3 linie
    body {
        font-family: Verdana, sans-serif;
        background-color: #D4F1F4;
        color: #05445E;
    }
    #login-box {
        width: 50em;
        margin: auto;
        padding: 20px;
    }
    footer {
        text-align: right;
    }
    header {
        margin: 40px;
    }
    #results {
        margin: 40px;
        padding: 20px;
    }
    #login-box, header, #results {
        background-color: #75E6DA;
        border-radius: 5px;
        box-shadow: 10px 10px 5px LightBlue;
    }
    h1, h2, form {
        text-align: center;
    }
    Podgląd strony po tym kroku (login.php)
  32. Krok 32: Formularz: marginesy

    ok. 1 min
    Polecenie z arkusza

    Dla selektora formularza: marginesy zewnętrzne 10 px.

    Osobna reguła dla form - można też dopisać margines do poprzedniej, ale tam są trzy selektory, a margines dotyczy tylko formularza.

    styl.css CSS zmienione: 3 linie
    body {
        font-family: Verdana, sans-serif;
        background-color: #D4F1F4;
        color: #05445E;
    }
    #login-box {
        width: 50em;
        margin: auto;
        padding: 20px;
    }
    footer {
        text-align: right;
    }
    header {
        margin: 40px;
    }
    #results {
        margin: 40px;
        padding: 20px;
    }
    #login-box, header, #results {
        background-color: #75E6DA;
        border-radius: 5px;
        box-shadow: 10px 10px 5px LightBlue;
    }
    h1, h2, form {
        text-align: center;
    }
    form {
        margin: 10px;
    }
    Podgląd strony po tym kroku (login.php)
  33. Krok 33: Przycisk „Zaloguj”: szerokość

    Kryteria:

    R.4.3 szerokość bloku głównego na stronie logowania - 50em, dla przycisku "Zaloguj" - 14em

    ok. 1 min
    Polecenie z arkusza

    Dla przycisku o treści “Zaloguj”: szerokość 14 em.

    Selektor button - to jedyny przycisk w witrynie. Szerokość okna logowania (50 em) i przycisku (14 em) to R.4.3.

    styl.css CSS zmienione: 3 linie
    body {
        font-family: Verdana, sans-serif;
        background-color: #D4F1F4;
        color: #05445E;
    }
    #login-box {
        width: 50em;
        margin: auto;
        padding: 20px;
    }
    footer {
        text-align: right;
    }
    header {
        margin: 40px;
    }
    #results {
        margin: 40px;
        padding: 20px;
    }
    #login-box, header, #results {
        background-color: #75E6DA;
        border-radius: 5px;
        box-shadow: 10px 10px 5px LightBlue;
    }
    h1, h2, form {
        text-align: center;
    }
    form {
        margin: 10px;
    }
    button {
        width: 14em;
    }
    Podgląd strony po tym kroku (login.php)
  34. Krok 34: Tabela: wyśrodkowana, obramowanie połączone

    ok. 1 min
    Polecenie z arkusza

    Dla tabeli: marginesy zewnętrzne automatycznie wyliczone przez przeglądarkę, obramowanie połączone.

    margin: auto centruje tabelę w bloku jak okno logowania, a border-collapse: collapse łączy linie komórek w jedną.

    styl.css CSS zmienione: 4 linie
    body {
        font-family: Verdana, sans-serif;
        background-color: #D4F1F4;
        color: #05445E;
    }
    #login-box {
        width: 50em;
        margin: auto;
        padding: 20px;
    }
    footer {
        text-align: right;
    }
    header {
        margin: 40px;
    }
    #results {
        margin: 40px;
        padding: 20px;
    }
    #login-box, header, #results {
        background-color: #75E6DA;
        border-radius: 5px;
        box-shadow: 10px 10px 5px LightBlue;
    }
    h1, h2, form {
        text-align: center;
    }
    form {
        margin: 10px;
    }
    button {
        width: 14em;
    }
    table {
        margin: auto;
        border-collapse: collapse;
    }
    Podgląd strony po tym kroku (wyniki.php)
  35. Krok 35: Tabela i wiersze: obramowanie

    Kryteria:

    R.4.6 obramowanie dowolną, pojedynczą linią ciągłą o szerokości - 1px i kolorze #189AB4 dla tabeli i wiersza tabeli; obramowanie połączone dla tabeli (border-collapse: collapse;)

    ok. 1 min
    Polecenie z arkusza

    Dla tabeli i wiersza tabeli: obramowanie linią ciągłą 1 px o kolorze #189AB4.

    Obramowanie dla table i tr - nie dla komórek. Przy połączonych obramowaniach ramka wiersza rysuje się w poprzek tabeli, więc widać poziome linie między wierszami i obwódkę, ale bez pionowych linii między kolumnami - jak na ilustracji 3 (R.4.6).

    Typowe błędy

    Ramka wiersza tr działa tylko przy border-collapse: collapse - bez niego przeglądarka jej nie rysuje.

    styl.css CSS zmienione: 3 linie
    body {
        font-family: Verdana, sans-serif;
        background-color: #D4F1F4;
        color: #05445E;
    }
    #login-box {
        width: 50em;
        margin: auto;
        padding: 20px;
    }
    footer {
        text-align: right;
    }
    header {
        margin: 40px;
    }
    #results {
        margin: 40px;
        padding: 20px;
    }
    #login-box, header, #results {
        background-color: #75E6DA;
        border-radius: 5px;
        box-shadow: 10px 10px 5px LightBlue;
    }
    h1, h2, form {
        text-align: center;
    }
    form {
        margin: 10px;
    }
    button {
        width: 14em;
    }
    table {
        margin: auto;
        border-collapse: collapse;
    }
    table, tr {
        border: 1px solid #189AB4;
    }
    Podgląd strony po tym kroku (wyniki.php)
  36. Krok 36: Komórki: marginesy wewnętrzne

    Kryteria:

    R.4.5 marginesy wewnętrzne (padding) : blok główny na stronie logowania, blok z tabelą - 20px, komórki nagłówkowe tabeli i komórki tabeli - 10px, marginesy zewnętrzne (margin) : blok główny na stronie logowania, tabela - auto, blok nagłówkowy, blok z tabelą - 40px, formularz - 10px

    ok. 1 min
    Polecenie z arkusza

    Dla komórek nagłówkowych tabeli i komórek tabeli: marginesy wewnętrzne 10 px.

    Tu domyka się kryterium marginesów zbierane przez kilka reguł: okno logowania, blok z tabelą i komórki (wewnętrzne) oraz okno, tabela, nagłówek, blok z tabelą i formularz (zewnętrzne) - R.4.5.

    styl.css CSS zmienione: 3 linie
    body {
        font-family: Verdana, sans-serif;
        background-color: #D4F1F4;
        color: #05445E;
    }
    #login-box {
        width: 50em;
        margin: auto;
        padding: 20px;
    }
    footer {
        text-align: right;
    }
    header {
        margin: 40px;
    }
    #results {
        margin: 40px;
        padding: 20px;
    }
    #login-box, header, #results {
        background-color: #75E6DA;
        border-radius: 5px;
        box-shadow: 10px 10px 5px LightBlue;
    }
    h1, h2, form {
        text-align: center;
    }
    form {
        margin: 10px;
    }
    button {
        width: 14em;
    }
    table {
        margin: auto;
        border-collapse: collapse;
    }
    table, tr {
        border: 1px solid #189AB4;
    }
    th, td {
        padding: 10px;
    }
    Podgląd strony po tym kroku (wyniki.php)
  37. Krok 37: Odnośniki bez podkreślenia

    Kryteria:

    R.4.9 brak podkreślenia dla odnośnika

    ok. 1 min
    Polecenie z arkusza

    Dla odnośnika: brak podkreślenia.

    Dotyczy „Wyloguj” i odnośników „wynik” w tabeli (R.4.9).

    styl.css CSS zmienione: 3 linie
    body {
        font-family: Verdana, sans-serif;
        background-color: #D4F1F4;
        color: #05445E;
    }
    #login-box {
        width: 50em;
        margin: auto;
        padding: 20px;
    }
    footer {
        text-align: right;
    }
    header {
        margin: 40px;
    }
    #results {
        margin: 40px;
        padding: 20px;
    }
    #login-box, header, #results {
        background-color: #75E6DA;
        border-radius: 5px;
        box-shadow: 10px 10px 5px LightBlue;
    }
    h1, h2, form {
        text-align: center;
    }
    form {
        margin: 10px;
    }
    button {
        width: 14em;
    }
    table {
        margin: auto;
        border-collapse: collapse;
    }
    table, tr {
        border: 1px solid #189AB4;
    }
    th, td {
        padding: 10px;
    }
    a {
        text-decoration: none;
    }
    Podgląd strony po tym kroku (wyniki.php)
  38. Krok 38: Wiersz pod kursorem: półprzezroczysta biel

    Kryteria:

    R.4.10 gdy kursor znajdzie się na wierszu tabeli kolor tła zmienia się na biały o przezroczystości 0.8

    ok. 1 min
    Polecenie z arkusza

    Gdy kursor znajdzie się na wierszu tabeli kolor tła zmienia się na biały o przeźroczystości 0.8.

    rgba(255, 255, 255, 0.8) - trzy składowe koloru (biały) i czwarta, krycie: 0.8 to 80% bieli, przez którą lekko prześwituje turkusowe tło bloku. Wiersze z danymi pojawią się po skrypcie 3; wtedy najedź na nie w podglądzie (R.4.10).

    Typowe błędy

    opacity: 0.8 dla tr:hover robi półprzezroczystym cały wiersz razem z tekstem - arkusz mówi o przezroczystości koloru tła.

    styl.css CSS zmienione: 3 linie
    body {
        font-family: Verdana, sans-serif;
        background-color: #D4F1F4;
        color: #05445E;
    }
    #login-box {
        width: 50em;
        margin: auto;
        padding: 20px;
    }
    footer {
        text-align: right;
    }
    header {
        margin: 40px;
    }
    #results {
        margin: 40px;
        padding: 20px;
    }
    #login-box, header, #results {
        background-color: #75E6DA;
        border-radius: 5px;
        box-shadow: 10px 10px 5px LightBlue;
    }
    h1, h2, form {
        text-align: center;
    }
    form {
        margin: 10px;
    }
    button {
        width: 14em;
    }
    table {
        margin: auto;
        border-collapse: collapse;
    }
    table, tr {
        border: 1px solid #189AB4;
    }
    th, td {
        padding: 10px;
    }
    a {
        text-decoration: none;
    }
    tr:hover {
        background-color: rgba(255, 255, 255, 0.8);
    }
    Podgląd strony po tym kroku (wyniki.php)
  39. Etap 4: Skrypty PHP

    Kroki 39-49 · po tym etapie masz 41 z 41 kryteriów (pkt)

    Krok 39: Skrypt 1: start sesji na początku pliku

    Kryteria:

    R.5.1 Co najmniej jeden ze skryptów realizuje obsługę sesji w języku PHP. Przynajmniej raz użyte polecenia session_start() na początku pliku PHP

    ok. 2 min
    Polecenie z arkusza

    Napisane w języku PHP. Skrypty wykorzystują mechanizm sesji. Efekt działania skryptu 1.

    Skrypt 1 stoi na samym początku login.php, przed <!DOCTYPE html>. session_start() (tabela 2) uruchamia sesję: serwer pamięta dane jednego użytkownika między kolejnymi stronami (rozpoznaje go po ciasteczku z identyfikatorem sesji). Musi być przed jakimkolwiek HTML-em, bo wysyła to ciasteczko w nagłówkach odpowiedzi (R.5.1). Podgląd od tego kroku to wynik prawdziwego uruchomienia strony w PHP.

    Typowe błędy

    session_start() po wypisaniu HTML-a kończy się ostrzeżeniem „headers already sent” i sesja nie działa - zawsze na początku pliku.

    login.php PHP zmienione: 3 linie
    <?php
    session_start();
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Zaloguj się</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Portal laboratorium</h1>
        </header>
        <main id="login-box">
            <h2>Zaloguj się do systemu</h2>
            <form action="login.php" method="post">
                <label for="login">Login:</label>
                <input type="text" id="login" name="login"><br>
                <label for="password">Hasło:</label>
                <input type="password" id="password" name="password"><br>
                <button type="submit">Zaloguj</button>
            </form>
        </main>
        <footer>
            <p>Wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (login.php)
  40. Krok 40: Skrypt 1: dane z formularza

    Kryteria:

    R.5.10 Zastosowana przynajmniej jedna zmienna oraz nazewnictwo jest w języku polskim lub angielskim i odzwierciedla cel zastosowania

    ok. 2 min
    Polecenie z arkusza

    Jeżeli dane zostały wysłane z formularza.

    Przy pierwszym wejściu na stronę formularz nie był jeszcze wysłany i $_POST jest pusta. isset (tabela 1) sprawdza, czy oba pola przyszły - dopiero wtedy przepisujesz je do zmiennych $login i $password (nazwy mówią, co przechowują - R.5.10).

    login.php PHP zmienione: 4 linie
    <?php
    session_start();
    if (isset($_POST["login"]) && isset($_POST["password"])) {
        $login = $_POST["login"];
        $password = $_POST["password"];
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Zaloguj się</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Portal laboratorium</h1>
        </header>
        <main id="login-box">
            <h2>Zaloguj się do systemu</h2>
            <form action="login.php" method="post">
                <label for="login">Login:</label>
                <input type="text" id="login" name="login"><br>
                <label for="password">Hasło:</label>
                <input type="password" id="password" name="password"><br>
                <button type="submit">Zaloguj</button>
            </form>
        </main>
        <footer>
            <p>Wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>

    Podgląd strony bez zmian - taki jak po kroku 39.

  41. Krok 41: Skrypt 1: sprawdzenie loginu i hasła, zmienna sesji

    Kryteria:

    R.5.3 Skrypt 1 (login.php) pobiera dane z formularza. Jeżeli w formularzu podany login admin oraz hasło TajneHaslo321* to ustawiana jest zmienna sesji zawierająca aktualną nazwę użytkownika. Wykorzystana tablica $_SESSION

    ok. 2 min
    Polecenie z arkusza

    Jeżeli dane zostały wysłane z formularza i login to admin, a hasło to TajneHaslo321* ustawiana jest zmienna sesji zawierająca aktualną nazwę użytkownika.

    Oba warunki naraz (&&): login admin i hasło TajneHaslo321* - dokładnie, z gwiazdką na końcu. Gdy się zgadzają, zapisujesz nazwę użytkownika w $_SESSION["user"] (tabela 2). Ta wartość przetrwa przejście na wyniki.php - po to jest sesja (R.5.3).

    login.php PHP zmienione: 3 linie
    <?php
    session_start();
    if (isset($_POST["login"]) && isset($_POST["password"])) {
        $login = $_POST["login"];
        $password = $_POST["password"];
        if ($login == "admin" && $password == "TajneHaslo321*") {
            $_SESSION["user"] = $login;
        }
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Zaloguj się</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Portal laboratorium</h1>
        </header>
        <main id="login-box">
            <h2>Zaloguj się do systemu</h2>
            <form action="login.php" method="post">
                <label for="login">Login:</label>
                <input type="text" id="login" name="login"><br>
                <label for="password">Hasło:</label>
                <input type="password" id="password" name="password"><br>
                <button type="submit">Zaloguj</button>
            </form>
        </main>
        <footer>
            <p>Wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>

    Podgląd strony bez zmian - taki jak po kroku 39.

  42. Krok 42: Skrypt 1: przejście na stronę wyników

    Kryteria:

    R.5.4 Skrypt 1 (login.php) ładuje stronę wyniki.php (np. header("location: wyniki.php"))

    ok. 1 min
    Polecenie z arkusza

    Oraz zostaje załadowana strona wyniki.php.

    header("Location: wyniki.php") (tabela 3) każe przeglądarce przejść na stronę wyników, ale nie zatrzymuje skryptu - bez exit PHP wykonuje plik dalej (wraca do tego etap „Dobre praktyki”). Działa tylko wtedy, gdy przed nim nic nie zostało wypisane - dlatego skrypt 1 stoi przed HTML-em (R.5.4). Przy złym haśle nic się nie dzieje: formularz pokazuje się znowu.

    login.php PHP zmienione: 1 linia
    <?php
    session_start();
    if (isset($_POST["login"]) && isset($_POST["password"])) {
        $login = $_POST["login"];
        $password = $_POST["password"];
        if ($login == "admin" && $password == "TajneHaslo321*") {
            $_SESSION["user"] = $login;
            header("Location: wyniki.php");
        }
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Zaloguj się</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Portal laboratorium</h1>
        </header>
        <main id="login-box">
            <h2>Zaloguj się do systemu</h2>
            <form action="login.php" method="post">
                <label for="login">Login:</label>
                <input type="text" id="login" name="login"><br>
                <label for="password">Hasło:</label>
                <input type="password" id="password" name="password"><br>
                <button type="submit">Zaloguj</button>
            </form>
        </main>
        <footer>
            <p>Wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>

    Podgląd strony bez zmian - taki jak po kroku 39.

  43. Krok 43: Skrypt 2: wpuszcza tylko zalogowanych

    ok. 2 min
    Polecenie z arkusza

    Jeżeli zmienna sesji zawierająca aktualną nazwę użytkownika nie jest ustawiona następuje przekierowanie do strony login.php.

    Na początku wyniki.php ta sama para: session_start(), a potem warunek !isset($_SESSION["user"]) - wykrzyknik odwraca wynik, czyli „zmienna sesji nie istnieje”. Wtedy przekierowanie na logowanie. Podgląd strony wyników pokazuje stan po zalogowaniu jako admin.

    Uwaga: header tylko wysyła przeglądarce polecenie przejścia na inną stronę - nie zatrzymuje skryptu. Na egzaminie to wystarcza (R.5.5), ale reszta pliku wykonuje się dalej; do skutków i poprawki (exit) wraca etap „Dobre praktyki”.

    wyniki.php PHP zmienione: 6 linii
    <?php
    session_start();
    if (!isset($_SESSION["user"])) {
        header("Location: login.php");
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Przeglądaj wyniki</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Portal laboratorium</h1>
            <p><a href="wyloguj.php">Wyloguj</a></p>
        </header>
        <main>
            <h2>Wyniki pacjentów</h2>
            <div id="results">
                <table>
                    <tr>
                        <th>Imię pacjenta</th>
                        <th>Nazwisko pacjenta</th>
                        <th>PESEL</th>
                        <th>Data zlecenia</th>
                        <th>Data wykonania</th>
                        <th>Wynik</th>
                    </tr>
                </table>
            </div>
        </main>
        <footer>
            <p>Wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (wyniki.php)
  44. Krok 44: Skrypt 2: powitanie z nazwą z sesji

    Kryteria: ,

    R.2.8 Na stronie z wynikami nazwa użytkownika jest zapisana w znaczniku <strong> (w skrypcie lub HTML)

    R.5.5 Skrypt 2 (wyniki.php): jeżeli nie jest ustawiona zmienna sesji zawierająca nazwę użytkownika, następuje przekierowanie do strony logowania. Napis w bloku nagłówkowym to: "Witaj <użytkownik>” gdzie <użytkownik> to nazwa użytkownika pobrana ze zmiennej sesji

    ok. 2 min
    Polecenie z arkusza

    W bloku nagłówkowym, w akapicie wyświetlany jest napis „Witaj, <użytkownik>” gdzie <użytkownik> to nazwa użytkownika pobrana ze zmiennej sesji. Nazwa użytkownika jest zapisana w znaczniku semantycznym oznaczającym tekst ważny, formatowany domyślnie jako pogrubiony.

    Druga część skryptu 2 stoi w akapicie nad odnośnikiem „Wyloguj”: napis sklejony kropką z nazwą z $_SESSION["user"], a nazwa w <strong> - tekst ważny, pogrubiony. Podgląd: „Witaj, admin”, jak na ilustracji 3 (R.2.8, R.5.5). Klucz zapisuje napis bez przecinka, arkusz i ilustracja z przecinkiem - piszesz jak w arkuszu.

    wyniki.php PHP zmienione: 1 linia
    <?php
    session_start();
    if (!isset($_SESSION["user"])) {
        header("Location: login.php");
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Przeglądaj wyniki</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Portal laboratorium</h1>
            <p><?php echo "Witaj, <strong>" . $_SESSION["user"] . "</strong>"; ?></p>
            <p><a href="wyloguj.php">Wyloguj</a></p>
        </header>
        <main>
            <h2>Wyniki pacjentów</h2>
            <div id="results">
                <table>
                    <tr>
                        <th>Imię pacjenta</th>
                        <th>Nazwisko pacjenta</th>
                        <th>PESEL</th>
                        <th>Data zlecenia</th>
                        <th>Data wykonania</th>
                        <th>Wynik</th>
                    </tr>
                </table>
            </div>
        </main>
        <footer>
            <p>Wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (wyniki.php)
  45. Krok 45: Skrypt 3: połączenie z bazą i jego zamknięcie

    Kryteria:

    R.5.2 Skrypt 3 (wyniki.php) realizuje połączenie z serwerem bazy danych i wybór bazy laboratorium oraz ostatnią operacją na bazie jest jej zamknięcie

    ok. 2 min
    Polecenie z arkusza

    Łączy się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie laboratorium. Ostatnią operacją skryptu jest zamknięcie połączenia z bazą danych.

    Skrypt 3 piszesz wewnątrz <table>, pod wierszem nagłówków - to, co wypisze, stanie się kolejnymi wierszami. mysqli_connect z czterema argumentami łączy się z serwerem i od razu wybiera bazę, a mysqli_close na końcu je zamyka. Wszystko, co dopiszesz, stanie między tymi dwiema liniami (R.5.2).

    wyniki.php PHP zmienione: 4 linie
    <?php
    session_start();
    if (!isset($_SESSION["user"])) {
        header("Location: login.php");
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Przeglądaj wyniki</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Portal laboratorium</h1>
            <p><?php echo "Witaj, <strong>" . $_SESSION["user"] . "</strong>"; ?></p>
            <p><a href="wyloguj.php">Wyloguj</a></p>
        </header>
        <main>
            <h2>Wyniki pacjentów</h2>
            <div id="results">
                <table>
                    <tr>
                        <th>Imię pacjenta</th>
                        <th>Nazwisko pacjenta</th>
                        <th>PESEL</th>
                        <th>Data zlecenia</th>
                        <th>Data wykonania</th>
                        <th>Wynik</th>
                    </tr>
                    <?php
                        $connection = mysqli_connect("localhost", "root", "", "laboratorium");
                        mysqli_close($connection);
                    ?>
                </table>
            </div>
        </main>
        <footer>
            <p>Wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (wyniki.php)
  46. Krok 46: Skrypt 3: zapytanie 4

    Kryteria:

    R.5.6 Skrypt 3 (wyniki.php) zawiera instrukcję wysyłającą do bazy danych zapytanie 4

    ok. 1 min
    Polecenie z arkusza

    Wysyła do bazy danych zapytanie 4.

    Treść zapytania 4 w zmiennej i mysqli_query. W $result czeka 12 wierszy (R.5.6).

    wyniki.php PHP zmienione: 2 linie
    <?php
    session_start();
    if (!isset($_SESSION["user"])) {
        header("Location: login.php");
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Przeglądaj wyniki</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Portal laboratorium</h1>
            <p><?php echo "Witaj, <strong>" . $_SESSION["user"] . "</strong>"; ?></p>
            <p><a href="wyloguj.php">Wyloguj</a></p>
        </header>
        <main>
            <h2>Wyniki pacjentów</h2>
            <div id="results">
                <table>
                    <tr>
                        <th>Imię pacjenta</th>
                        <th>Nazwisko pacjenta</th>
                        <th>PESEL</th>
                        <th>Data zlecenia</th>
                        <th>Data wykonania</th>
                        <th>Wynik</th>
                    </tr>
                    <?php
                        $connection = mysqli_connect("localhost", "root", "", "laboratorium");
                        $query = "SELECT Pacjent.imie, Pacjent.nazwisko, Pacjent.pesel, Zlecenie.data, Wynik.data, Wynik.plik FROM Pacjent JOIN Zlecenie ON Zlecenie.idPacjenta = Pacjent.id LEFT JOIN Wynik ON Wynik.idZlecenia = Zlecenie.id;";
                        $result = mysqli_query($connection, $query);
                        mysqli_close($connection);
                    ?>
                </table>
            </div>
        </main>
        <footer>
            <p>Wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>

    Podgląd strony bez zmian - taki jak po kroku 45.

  47. Krok 47: Skrypt 3: wiersze tabeli z danymi

    Kryteria: ,

    R.2.6 Strona z wynikami zawiera tabelę składającą się z 6 kolumn. Zastosowano znaczniki <table>, <tr>, <td> (w skrypcie lub HTML)

    R.5.7 Skrypt 3 (wyniki.php) dla każdego zwróconego zapytaniem rekordu tworzy nowy wiersz tabeli i wypełnia go danymi pobranymi z bazy

    ok. 3 min
    Polecenie z arkusza

    Wyniki zapytania wyświetlane są jako kolejne wiersze tabeli (zgodnie z ilustracją 3).

    mysqli_fetch_row oddaje kolejne wiersze jako tablice z numerami kolumn w kolejności z SELECT: [0] imię, [1] nazwisko, [2] PESEL, [3] data zlecenia, [4] data wyniku, [5] plik. Dla każdego wiersza wypisujesz <tr> z komórkami <td>. Pole z NULL (zlecenie bez wyniku) wypisuje się jako pusty napis, bez ostrzeżenia. Podgląd ma 12 wierszy w kolejności z ilustracji 3 - tak oddała je MariaDB, choć bez ORDER BY kolejność nie jest gwarantowana (R.2.6, R.5.7).

    wyniki.php PHP zmienione: 5 linii
    <?php
    session_start();
    if (!isset($_SESSION["user"])) {
        header("Location: login.php");
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Przeglądaj wyniki</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Portal laboratorium</h1>
            <p><?php echo "Witaj, <strong>" . $_SESSION["user"] . "</strong>"; ?></p>
            <p><a href="wyloguj.php">Wyloguj</a></p>
        </header>
        <main>
            <h2>Wyniki pacjentów</h2>
            <div id="results">
                <table>
                    <tr>
                        <th>Imię pacjenta</th>
                        <th>Nazwisko pacjenta</th>
                        <th>PESEL</th>
                        <th>Data zlecenia</th>
                        <th>Data wykonania</th>
                        <th>Wynik</th>
                    </tr>
                    <?php
                        $connection = mysqli_connect("localhost", "root", "", "laboratorium");
                        $query = "SELECT Pacjent.imie, Pacjent.nazwisko, Pacjent.pesel, Zlecenie.data, Wynik.data, Wynik.plik FROM Pacjent JOIN Zlecenie ON Zlecenie.idPacjenta = Pacjent.id LEFT JOIN Wynik ON Wynik.idZlecenia = Zlecenie.id;";
                        $result = mysqli_query($connection, $query);
                        while ($row = mysqli_fetch_row($result)) {
                            echo "<tr>";
                            echo "<td>$row[0]</td><td>$row[1]</td><td>$row[2]</td><td>$row[3]</td><td>$row[4]</td>";
                            echo "</tr>";
                        }
                        mysqli_close($connection);
                    ?>
                </table>
            </div>
        </main>
        <footer>
            <p>Wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (wyniki.php)
  48. Krok 48: Skrypt 3: odnośnik do pliku z wynikiem

    Kryteria: ,

    R.3.7 Tabela zawiera przynajmniej jeden odnośnik, który otwiera plik PDF w nowej karcie przeglądarki

    R.5.8 W ostatniej kolumnie tabeli jest tworzony odnośnik prowadzący do pliku o nazwie zwróconej z bazy tylko wtedy, gdy nazwa jest niepusta

    ok. 3 min
    Polecenie z arkusza

    W ostatniej kolumnie tabeli, gdy z bazy została zwrócona niepusta nazwa pliku wstawiany jest odnośnik o treści „wynik” prowadzący do pliku o nazwie zwróconej z bazy. Odnośnik otwiera się w nowej karcie przeglądarki.

    Warunek $row[5] != "": dla zleceń bez wyniku pole jest NULL, a luźne porównanie != traktuje NULL jak pusty napis - warunek jest fałszywy i wstawiasz pustą komórkę (żeby wiersz miał 6 kolumn). Dla trzech zleceń z wynikiem powstaje odnośnik do pliku PDF z target="_blank" - nowa karta. Pliki PDF z archiwum muszą leżeć obok stron (R.3.7, R.5.8).

    Typowe błędy

    if ($row[5] !== "") (ścisłe porównanie) jest prawdziwe także dla NULL - odnośnik „wynik” pojawi się przy wszystkich 12 zleceniach i będzie prowadził donikąd.

    wyniki.php PHP zmienione: 5 linii
    <?php
    session_start();
    if (!isset($_SESSION["user"])) {
        header("Location: login.php");
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Przeglądaj wyniki</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Portal laboratorium</h1>
            <p><?php echo "Witaj, <strong>" . $_SESSION["user"] . "</strong>"; ?></p>
            <p><a href="wyloguj.php">Wyloguj</a></p>
        </header>
        <main>
            <h2>Wyniki pacjentów</h2>
            <div id="results">
                <table>
                    <tr>
                        <th>Imię pacjenta</th>
                        <th>Nazwisko pacjenta</th>
                        <th>PESEL</th>
                        <th>Data zlecenia</th>
                        <th>Data wykonania</th>
                        <th>Wynik</th>
                    </tr>
                    <?php
                        $connection = mysqli_connect("localhost", "root", "", "laboratorium");
                        $query = "SELECT Pacjent.imie, Pacjent.nazwisko, Pacjent.pesel, Zlecenie.data, Wynik.data, Wynik.plik FROM Pacjent JOIN Zlecenie ON Zlecenie.idPacjenta = Pacjent.id LEFT JOIN Wynik ON Wynik.idZlecenia = Zlecenie.id;";
                        $result = mysqli_query($connection, $query);
                        while ($row = mysqli_fetch_row($result)) {
                            echo "<tr>";
                            echo "<td>$row[0]</td><td>$row[1]</td><td>$row[2]</td><td>$row[3]</td><td>$row[4]</td>";
                            if ($row[5] != "") {
                                echo "<td><a href=\"$row[5]\" target=\"_blank\">wynik</a></td>";
                            } else {
                                echo "<td></td>";
                            }
                            echo "</tr>";
                        }
                        mysqli_close($connection);
                    ?>
                </table>
            </div>
        </main>
        <footer>
            <p>Wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (wyniki.php)
  49. Krok 49: Skrypt 4: wylogowanie

    Kryteria:

    R.5.9 Skrypt 4 (wyloguj.php) po kliknięciu na odnośnik "Wyloguj" usuwa zmienną sesji i przekierowuje do strony login.php

    ok. 3 min
    Polecenie z arkusza

    Skrypt 4 (wyloguj.php): Jeżeli jest ustawiona zmienna sesji zawierająca nazwę użytkownika to jest ona usuwana (dane sesji są czyszczone). Następuje przekierowanie do strony login.php.

    wyloguj.php nie ma HTML-a - sam skrypt. Po session_start(): jeśli użytkownik jest zalogowany, unset($_SESSION["user"]) usuwa zmienną, a session_destroy() kończy całą sesję (tabela 2). Na końcu przekierowanie na logowanie. Sprawdzone uruchomieniem z sesją „admin”: po skrypcie sesja jest pusta. Podgląd pokazuje stronę, na którą trafia użytkownik (R.5.9).

    wyloguj.php PHP nowy plik
    <?php
    session_start();
    if (isset($_SESSION["user"])) {
        unset($_SESSION["user"]);
        session_destroy();
    }
    header("Location: login.php");
    ?>
    Podgląd strony po tym kroku (login.php)
  50. Etap 5: Dobre praktyki (poza kluczem)

    Kroki 50-52 · po tym etapie masz 41 z 41 kryteriów (pkt)

    Krok 50: exit po przekierowaniu: strona wyników naprawdę zamknięta

    ok. 3 min
    Polecenie z arkusza

    Jeżeli zmienna sesji zawierająca aktualną nazwę użytkownika nie jest ustawiona następuje przekierowanie do strony login.php.

    To najważniejsza poprawka w tym arkuszu. header("Location: ...") tylko dopisuje nagłówek do odpowiedzi - nie zatrzymuje skryptu. Wersja z egzaminu po przekierowaniu wykonuje się dalej: łączy z bazą i wysyła całą tabelę z imionami, nazwiskami i numerami PESEL. Przeglądarka posłusznie przejdzie na logowanie, więc nic nie widać - ale wystarczy pobrać stronę narzędziem, które przekierowań nie słucha (np. curl), żeby dostać dane pacjentów bez logowania. Sprawdzone: uruchomienie wyniki.php bez sesji zwraca pełną tabelę.

    exit zaraz po header kończy skrypt - niezalogowany dostaje pustą odpowiedź (sprawdzone). Dopisz go we wszystkich trzech przekierowaniach. Na egzaminie wystarczy wersja wcześniejsza - klucz tego nie sprawdza, ale w prawdziwej aplikacji bez exit zabezpieczenie jest fikcją.

    login.php PHP zmienione: 1 linia
    <?php
    session_start();
    if (isset($_POST["login"]) && isset($_POST["password"])) {
        $login = $_POST["login"];
        $password = $_POST["password"];
        if ($login == "admin" && $password == "TajneHaslo321*") {
            $_SESSION["user"] = $login;
            header("Location: wyniki.php");
            exit;
        }
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Zaloguj się</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Portal laboratorium</h1>
        </header>
        <main id="login-box">
            <h2>Zaloguj się do systemu</h2>
            <form action="login.php" method="post">
                <label for="login">Login:</label>
                <input type="text" id="login" name="login"><br>
                <label for="password">Hasło:</label>
                <input type="password" id="password" name="password"><br>
                <button type="submit">Zaloguj</button>
            </form>
        </main>
        <footer>
            <p>Wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    wyloguj.php PHP zmienione: 1 linia
    <?php
    session_start();
    if (isset($_SESSION["user"])) {
        unset($_SESSION["user"]);
        session_destroy();
    }
    header("Location: login.php");
    exit;
    ?>
    wyniki.php PHP zmienione: 1 linia
    <?php
    session_start();
    if (!isset($_SESSION["user"])) {
        header("Location: login.php");
        exit; // bez tego reszta strony i tak idzie do przeglądarki
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Przeglądaj wyniki</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Portal laboratorium</h1>
            <p><?php echo "Witaj, <strong>" . $_SESSION["user"] . "</strong>"; ?></p>
            <p><a href="wyloguj.php">Wyloguj</a></p>
        </header>
        <main>
            <h2>Wyniki pacjentów</h2>
            <div id="results">
                <table>
                    <tr>
                        <th>Imię pacjenta</th>
                        <th>Nazwisko pacjenta</th>
                        <th>PESEL</th>
                        <th>Data zlecenia</th>
                        <th>Data wykonania</th>
                        <th>Wynik</th>
                    </tr>
                    <?php
                        $connection = mysqli_connect("localhost", "root", "", "laboratorium");
                        $query = "SELECT Pacjent.imie, Pacjent.nazwisko, Pacjent.pesel, Zlecenie.data, Wynik.data, Wynik.plik FROM Pacjent JOIN Zlecenie ON Zlecenie.idPacjenta = Pacjent.id LEFT JOIN Wynik ON Wynik.idZlecenia = Zlecenie.id;";
                        $result = mysqli_query($connection, $query);
                        while ($row = mysqli_fetch_row($result)) {
                            echo "<tr>";
                            echo "<td>$row[0]</td><td>$row[1]</td><td>$row[2]</td><td>$row[3]</td><td>$row[4]</td>";
                            if ($row[5] != "") {
                                echo "<td><a href=\"$row[5]\" target=\"_blank\">wynik</a></td>";
                            } else {
                                echo "<td></td>";
                            }
                            echo "</tr>";
                        }
                        mysqli_close($connection);
                    ?>
                </table>
            </div>
        </main>
        <footer>
            <p>Wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (wyniki.php)
  51. Krok 51: Tabela: kolumny po nazwach, htmlspecialchars, stała kolejność

    ok. 4 min
    Polecenie z arkusza

    Wyniki zapytania wyświetlane są jako kolejne wiersze tabeli (zgodnie z ilustracją 3).

    • mysqli_fetch_assoc i aliasy data_zlecenia, data_wyniku - dwie kolumny data miałyby w tablicy asocjacyjnej ten sam klucz i druga nadpisałaby pierwszą; z aliasami każda ma swoją nazwę,
    • pętla foreach po nazwach kolumn zamiast pięciu ręcznie wpisanych komórek,
    • htmlspecialchars przy każdej wartości z bazy i przy nazwie z sesji - znaki < i " w danych nie zamienią się w kod strony; ?? "" zamienia NULL na pusty napis,
    • ORDER BY Zlecenie.data - kolejność wierszy nie zależy już od planu zapytania,
    • !empty($row["plik"]) - jeden warunek na NULL i pusty napis.

    Na egzaminie wystarczy wersja wcześniejsza - klucz tego nie sprawdza.

    wyniki.php PHP zmienione: 9 linii
    <?php
    session_start();
    if (!isset($_SESSION["user"])) {
        header("Location: login.php");
        exit; // bez tego reszta strony i tak idzie do przeglądarki
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Przeglądaj wyniki</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Portal laboratorium</h1>
            <p><?php echo "Witaj, <strong>" . htmlspecialchars($_SESSION["user"]) . "</strong>"; ?></p>
            <p><a href="wyloguj.php">Wyloguj</a></p>
        </header>
        <main>
            <h2>Wyniki pacjentów</h2>
            <div id="results">
                <table>
                    <tr>
                        <th>Imię pacjenta</th>
                        <th>Nazwisko pacjenta</th>
                        <th>PESEL</th>
                        <th>Data zlecenia</th>
                        <th>Data wykonania</th>
                        <th>Wynik</th>
                    </tr>
                    <?php
                        $connection = mysqli_connect("localhost", "root", "", "laboratorium");
                        $query = "SELECT Pacjent.imie, Pacjent.nazwisko, Pacjent.pesel, Zlecenie.data AS data_zlecenia, Wynik.data AS data_wyniku, Wynik.plik FROM Pacjent JOIN Zlecenie ON Zlecenie.idPacjenta = Pacjent.id LEFT JOIN Wynik ON Wynik.idZlecenia = Zlecenie.id ORDER BY Zlecenie.data;";
                        $result = mysqli_query($connection, $query);
                        while ($row = mysqli_fetch_assoc($result)) {
                            echo "<tr>";
                            foreach (["imie", "nazwisko", "pesel", "data_zlecenia", "data_wyniku"] as $column) {
                                echo "<td>" . htmlspecialchars($row[$column] ?? "") . "</td>";
                            }
                            if (!empty($row["plik"])) {
                                echo "<td><a href=\"" . htmlspecialchars($row["plik"]) . "\" target=\"_blank\">wynik</a></td>";
                            } else {
                                echo "<td></td>";
                            }
                            echo "</tr>";
                        }
                        mysqli_close($connection);
                        // wersja z egzaminu: mysqli_fetch_row i komórki $row[0] ... $row[5], bez ORDER BY
                    ?>
                </table>
            </div>
        </main>
        <footer>
            <p>Wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (wyniki.php)
  52. Krok 52: Logowanie: skrót hasła, nowa sesja, komunikat o błędzie

    ok. 5 min
    Polecenie z arkusza

    Jeżeli dane zostały wysłane z formularza i login to admin, a hasło to TajneHaslo321* ustawiana jest zmienna sesji zawierająca aktualną nazwę użytkownika oraz zostaje załadowana strona wyniki.php.

    Trzy rzeczy, których arkusz nie wymaga, a każda prawdziwa strona logowania ma:

    • hasło nie leży w kodzie wprost: zapisujesz jego skrót z password_hash (wygenerowany raz, w PHP 8.2), a przy logowaniu password_verify sprawdza, czy wpisane hasło pasuje do skrótu. Ktoś, kto zobaczy plik, nie pozna hasła,
    • session_regenerate_id(true) po udanym logowaniu nadaje sesji nowy identyfikator - stary, który mógł ktoś podsunąć użytkownikowi przed zalogowaniem, przestaje działać,
    • komunikat „Nieprawidłowy login lub hasło.” zamiast milczenia - podgląd pokazuje stan po wpisaniu złego hasła.

    Sprawdzone uruchomieniem: dobre hasło zapisuje w sesji „admin”, złe pokazuje komunikat. Stary warunek zostaje w komentarzu. Na egzaminie wystarczy wersja wcześniejsza - klucz tego nie sprawdza.

    login.php PHP zmienione: 8 linii
    <?php
    session_start();
    $error = "";
    $passwordHash = '$2y$10$AfJxgDP8vWoyIX3xZYV77.ro0aiPt3I85un6RjR4pjhqOagKxegLC'; // password_hash("TajneHaslo321*", PASSWORD_DEFAULT)
    if (isset($_POST["login"]) && isset($_POST["password"])) {
        $login = trim($_POST["login"]);
        $password = $_POST["password"];
        if ($login == "admin" && password_verify($password, $passwordHash)) {
            session_regenerate_id(true);
            $_SESSION["user"] = $login;
            header("Location: wyniki.php");
            exit;
        }
        $error = "Nieprawidłowy login lub hasło.";
    }
    /* wersja z egzaminu: if ($login == "admin" && $password == "TajneHaslo321*") { ... } */
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Zaloguj się</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Portal laboratorium</h1>
        </header>
        <main id="login-box">
            <h2>Zaloguj się do systemu</h2>
            <form action="login.php" method="post">
                <label for="login">Login:</label>
                <input type="text" id="login" name="login"><br>
                <label for="password">Hasło:</label>
                <input type="password" id="password" name="password"><br>
                <button type="submit">Zaloguj</button>
                <p><?php echo $error; ?></p>
            </form>
        </main>
        <footer>
            <p>Wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (login.php)
  53. Etap 6: Oddanie pracy

    Krok 53 · po tym etapie masz 41 z 41 kryteriów (pkt)

    Krok 53: 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. Umieść go w folderze z numerem zdającego. Nagraj płytę z rezultatami pracy.

    W przeglądarka.txt wpisujesz nazwę przeglądarki, w której sprawdzasz strony.

    W folderze z numerem zdającego: login.php, wyniki.php, wyloguj.php, styl.css, kwerendy.txt, import.png, kw1.png - kw4.png, trzy pliki PDF z wynikami i przeglądarka.txt. Nagraj płytę, sprawdź jej odczyt i opisz ją numerem zdającego.

    przeglądarka.txt Tekst nowy plik
    Google Chrome

    Podgląd strony bez zmian - taki jak po kroku 52.

Podgląd

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: . 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.