Przejdź do głównej treści
EE.09-02-24.01-SGStyczeń 2024 · 29 kroków · ok. 59 min · 37 kryteriów z klucza

Rozwiązanie krok po kroku: Tablica przylotów portu lotniczego (Nowoczesny kod (flex, PDO))

To drugi sposób rozwiązania tego samego arkusza. Wynik jest ten sam - baza egzamin z czterema zapytaniami, przeskalowane logo z przezroczystością i strona lotnisko.php z tablicą przylotów oraz komunikatem zależnym od ciasteczka - i tak samo spełnia każde kryterium klucza. Inna jest droga:

  • znaczniki <header>, <main> i <footer>: bloki banera i stopki to sekcje w nagłówku i stopce strony, a w rząd ustawia je display: flex zamiast float i clear,
  • zmienne CSS dla trzech kolorów z arkusza,
  • PHP na PDO na górze pliku, przed HTML-em: ciasteczko, połączenie, zapytanie i obsługa błędów w jednym miejscu, a w tabeli i stopce zostaje tylko wypisanie,
  • funkcja e() (htmlspecialchars) przy każdej wartości z bazy, try/catch/finally z zamknięciem połączenia w każdym przypadku,
  • ciasteczko z flagami httponly i samesite.

To arkusz EE.09 ze stycznia 2024 (kwalifikacja formuły 2017, którą zastąpiła INF.03) - powtórka zadania EE.09-05 ze stycznia 2021 z dwoma dodatkowymi wymaganiami: zapis w HTML5 i język strony. Klucz z 2024 każe sprawdzać skrypty w XAMPP 8.1.2 - użyte konstrukcje działają w PHP 8.1 i 8.2. Baza i grafika pochodzą z oryginalnego archiwum CKE dane1.7z (załącznik na stronie arkusza), zapytania i zrzuty są policzone na MariaDB 10.4, a podgląd od kroków z PHP to wynik prawdziwego uruchomienia kodu w PHP 8.2 - także dwóch kolejnych wejść tej samej przeglądarki z ciasteczkiem.

Jak liczą się punkty. Klucz ma 37 kryteriów (R.1.1 - R.5.8), każde za 1 punkt. Próg zdania części praktycznej to 75%, czyli 28 punktów. Przy każdym kroku jest napisane, które kryteria spełnia; klucz przyjmuje zarówno mysqli, jak i PDO.

  1. Etap 1: Baza danych

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

    Krok 1: Folder zdającego i archiwum dane1.7z

    ok. 2 min
    Polecenie z arkusza

    Aby wykonać zadanie, zaloguj się na konto Egzamin bez hasła. Na pulpicie znajdziesz archiwum ZIP o nazwie dane1.7z zabezpieczone hasłem: $@molot# Archiwum należy rozpakować. Na pulpicie konta Egzamin utwórz folder. Jako nazwy folderu należy użyć numeru zdającego. Umieść w tym folderze rozpakowane pliki.

    Folder na pulpicie nazwany numerem zdającego (PESEL) - w tym rozwiązaniu Bartosz Bryniarski. Archiwum dane1.7z rozpakuj 7-Zipem do tego folderu, hasło $@molot#. W środku: egzamin.sql i zad5.png.

  2. Krok 2: XAMPP, baza egzamin i import egzamin.sql

    ok. 2 min
    Polecenie z arkusza

    Uruchom usługi MySQL i Apache za pomocą XAMPP Control Panel. Za pomocą narzędzia phpMyAdmin wykonaj podane operacje na bazie danych: Utwórz bazę danych o nazwie egzamin. Do bazy egzamin zaimportuj plik egzamin.sql z rozpakowanego archiwum

    Start Apache i MySQL, w phpMyAdmin nowa baza egzamin, potem karta Import przy zaznaczonej bazie i plik egzamin.sql. Do zadania potrzebne są tabele przyloty (8 wierszy), odloty (10) i samoloty (4), połączone polem samoloty_id.

  3. Krok 3: Zrzut import.png

    Kryteria:

    R.1.1 Wykonano import tabel do bazy danych egzamin , czynność udokumentowano plikiem o nazwie import w formacie PNG

    ok. 2 min
    Polecenie z arkusza

    Wykonaj zrzut ekranu po imporcie. Zrzut zapisz w folderze z numerem zdającego, w formacie PNG 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

    Karta Struktura bazy egzamin z listą tabel i liczbą rekordów - zrzut całego ekranu z paskiem zadań, format PNG, nazwa import.

    Podgląd strony po tym kroku
    Podgląd strony po kroku 3
  4. Krok 4: Zapytanie 1: przyloty posortowane według czasu

    Kryteria: ,

    R.1.2 Zapisano plik tekstowy o nazwie kwerendy z zapytaniami SQL zawierający co najmniej jedno zapytanie SQL, wynikające z treści zadania

    R.1.3 Wykonano zapytanie 1 wybierające jedynie pola czas, kierunek, nr_rejsu i status_lotu z tabeli przyloty posortowane rosnąco według czasu. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT czas, kierunek, nr_rejsu, status_lotu FROM przyloty ORDER BY czas; (też gdy zapisano ASC) oraz wynik działania zapytania udokumentowano zrzutem, na którym jest widocznych dokładnie 8 rekordów, jedynie pola czas, kierunek, nr_rejsu, status_lotu. Rekordy są posortowane rosnąco wg. czasu

    ok. 3 min
    Polecenie z arkusza

    Zapisz i wykonaj zapytania SQL działające na bazie egzamin. Zapytania zapisz w pliku kwerendy.txt, w folderze z numerem zdającego. Wykonaj zrzuty ekranu przedstawiające wyniki działania kwerend. Zrzuty zapisz w formacie JPG 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 czas, kierunek, nr_rejsu i status_lotu z tabeli przyloty posortowane rosnąco według czasu

    Cztery pola w kolejności kolumn przyszłej tablicy, ORDER BY czas sortuje rosnąco. Wynik: 8 przylotów od 09:45:00 (Warszawa) do 10:33:00 (Zurych). Zapytanie do kwerendy.txt, zrzut kw1.jpg - to samo zapytanie wyśle skrypt.

    kwerendy.txt Tekst nowy plik
    -- Zapytanie 1
    SELECT czas, kierunek, nr_rejsu, status_lotu FROM przyloty ORDER BY czas;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 4
  5. Krok 5: Zapytanie 2: przyloty między 10:00 a 10:59

    Kryteria:

    R.1.4 Wykonano zapytanie 2 zliczające przyloty, które odbyły się w godzinach 10:00 – 10:59. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT COUNT(*) FROM przyloty WHERE czas LIKE "10%"; możliwe inne warianty, np. z BETWEEN oraz wynik działania zapytania udokumentowano zrzutem, na którym jest widoczna wartość 7, wynikająca z zadanego zapytania

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 2: zliczające przyloty, które odbyły się w godzinach 10:00 ÷ 10:59

    COUNT(*) zlicza wiersze, BETWEEN '10:00:00' AND '10:59:59' obejmuje oba końce przedziału kolumny typu TIME. Wynik: 7. Zrzut kw2.jpg.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    SELECT czas, kierunek, nr_rejsu, status_lotu FROM przyloty ORDER BY czas;
    
    -- Zapytanie 2
    SELECT COUNT(*) FROM przyloty WHERE czas BETWEEN '10:00:00' AND '10:59:59';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 5
  6. Krok 6: Zapytanie 3: relacja przyloty - samoloty

    Kryteria:

    R.1.5 Wykonano zapytanie 3 wybierające jedynie pola nr_rejsu z tabeli przyloty oraz typ z tabeli samoloty , dla lotów których kierunkiem jest Warszawa. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nr_rejsu, samoloty.typ FROM przyloty JOIN samoloty ON przyloty.samoloty_id = samoloty.id WHERE kierunek = "Warszawa"; możliwe INNER JOIN lub bez JOIN (porównanie kluczy w warunku) oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczny jest dokładnie jeden rekord jedynie z danymi: W63454, Boening 737

    ok. 3 min
    Polecenie z arkusza

    Zapytanie 3: wybierające jedynie pola nr_rejsu z tabeli przyloty oraz typ z tabeli samoloty dla lotów, których kierunkiem jest Warszawa; zapytanie wykorzystuje relację

    JOIN samoloty ON przyloty.samoloty_id = samoloty.id łączy przylot z jego samolotem, WHERE kierunek = 'Warszawa' zostawia jeden wiersz: W63454, Boening 737 (pisownia z bazy CKE). Zrzut kw3.jpg.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    SELECT czas, kierunek, nr_rejsu, status_lotu FROM przyloty ORDER BY czas;
    
    -- Zapytanie 2
    SELECT COUNT(*) FROM przyloty WHERE czas BETWEEN '10:00:00' AND '10:59:59';
    
    -- Zapytanie 3
    SELECT nr_rejsu, typ FROM przyloty JOIN samoloty ON przyloty.samoloty_id = samoloty.id WHERE kierunek = 'Warszawa';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 6
  7. Krok 7: Zapytanie 4: zmiana statusu odlotu LX5673

    Kryteria:

    R.1.6 Wykonano zapytanie 4 aktualizujące w tabeli odloty pole status_lotu na: „opoznienie 20 min” dla lotu o numerze rejsu LX5673. W pliku z kwerendami lub na zrzucie istnieje zapis np.: UPDATE odloty SET status_lotu="opoznienie 20 min" WHERE nr_rejsu="LX5673"; oraz wynik działania zapytania udokumentowano zrzutem, dokumentującym poprawne wykonanie zapytania 4

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 4: aktualizujące w tabeli odloty pole status_lotu na: „opoznienie 20 min” dla lotu o numerze rejsu LX5673

    To jedyna kwerenda na tabeli odloty. SET ustawia nowy status, WHERE nr_rejsu = 'LX5673' ogranicza zmianę do jednego wiersza - phpMyAdmin odpowiada „1 wiersz zmieniony”. Zrzut kw4.jpg.

    Typowe błędy

    UPDATE bez WHERE zmienia wszystkie dziesięć odlotów.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    SELECT czas, kierunek, nr_rejsu, status_lotu FROM przyloty ORDER BY czas;
    
    -- Zapytanie 2
    SELECT COUNT(*) FROM przyloty WHERE czas BETWEEN '10:00:00' AND '10:59:59';
    
    -- Zapytanie 3
    SELECT nr_rejsu, typ FROM przyloty JOIN samoloty ON przyloty.samoloty_id = samoloty.id WHERE kierunek = 'Warszawa';
    
    -- Zapytanie 4
    UPDATE odloty SET status_lotu = 'opoznienie 20 min' WHERE nr_rejsu = 'LX5673';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 7
  8. Etap 2: Grafika i strona HTML

    Kroki 8-14 · po tym etapie masz 15 z 37 kryteriów (pkt)

    Krok 8: Grafika: wysokość 200 px z przezroczystością

    Kryteria:

    R.2.1 Plik zad5.png przeskalowano z zachowaniem proporcji do wysokości 200 px, odpowiadająca temu szerokość wynosi 305 px (± 5 px); przezroczystość została zachowana

    ok. 2 min
    Polecenie z arkusza

    Plik zad5.png, wypakowany z archiwum, należy przeskalować z zachowaniem proporcji tak, aby jego wysokość wynosiła dokładnie 200 px. Należy zachować przezroczystość obrazu

    W GIMP-ie Obraz → Skaluj obraz z zamkniętym łańcuchem: 457 × 300 px → 305 × 200 px. Zapis jako PNG, który przechowuje kanał przezroczystości.

    Podgląd strony po tym kroku
    Podgląd strony po kroku 8
  9. Krok 9: Plik lotnisko.php: nagłówek dokumentu

    Kryteria: , ,

    R.2.2 Witrynę internetową zapisano w pliku PHP o nazwie lotnisko oraz jawnie zastosowano właściwy standard kodowania polskich znaków

    R.2.3 Nadano tytuł strony: "Port Lotniczy”

    R.3.4 Strona zawiera działające połączenie z zewnętrznym arkuszem stylów o nazwie styl5.css , formatowanie CSS pochodzi jedynie z tego arkusza Uwaga: kryterium to nie jest spełnione również, gdy źle przypisano co najmniej jeden selektor do bloku (np. kropka zamiast #)

    ok. 2 min
    Polecenie z arkusza

    Składa się ze strony o nazwie lotnisko.php. Zapisana w języku HTML 5. Jawnie zastosowany właściwy standard kodowania polskich znaków. Tytuł strony widoczny na karcie przeglądarki: „Port Lotniczy”. Arkusz stylów w pliku o nazwie styl5.css prawidłowo połączony z kodem strony. Ustawiony język zawartości strony na polski

    Cała część <head> w jednym kroku - te same linie co w wersji podstawowej, plus jedna:

    • <!DOCTYPE html> - HTML5, lang="pl" - język treści,
    • <meta charset="UTF-8"> jako pierwszy element <head> (R.2.2 - razem z nazwą pliku lotnisko.php),
    • <title>Port Lotniczy</title> (R.2.3),
    • <meta name="viewport"> - przeglądarka telefonu pokazuje stronę w swojej szerokości, zamiast udawać szeroki ekran (arkusz tego nie wymaga),
    • <link rel="stylesheet" href="styl5.css"> (R.3.4 - punkt przyznawany razem z poprawnymi selektorami).
    lotnisko.php PHP nowy plik
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Port Lotniczy</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (lotnisko.php)
  10. Krok 10: Bloki: nagłówek z trzema sekcjami, część główna, stopka z dwiema

    Kryteria:

    R.2.4 Wygląd strony zdefiniowano dzieląc ją na bloki: trzy bloki banera, poniżej blok główny, następnie dwa bloki stopki. Zastosowano znaczniki sekcji

    ok. 2 min
    Polecenie z arkusza

    Podział strony na bloki: trzy bloki banera, poniżej blok główny, następnie dwa bloki stopki. Podział zrealizowany za pomocą znaczników sekcji, zgodnie z obrazem 2

    Znaczniki sekcji HTML5 mówią, czym jest każda część strony:

    • <header> - nagłówek strony, w nim trzy bloki banera jako <section> z identyfikatorami logo, title i links,
    • <main> - blok główny z tablicą przylotów; na stronie jest jeden <main>, więc nie potrzebuje identyfikatora,
    • <footer> - stopka z dwoma blokami status (komunikat o ciasteczku) i author.

    Kontenery <header> i <footer> przydadzą się w CSS: wystarczy im display: flex, żeby ich sekcje stanęły w rzędzie.

    lotnisko.php PHP zmienione: 16 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Port Lotniczy</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <header>
            <section id="logo">
            </section>
            <section id="title">
            </section>
            <section id="links">
            </section>
        </header>
        <main>
        </main>
        <footer>
            <section id="status">
            </section>
            <section id="author">
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (lotnisko.php)
  11. Krok 11: Banery: logo i nagłówek „Przyloty”

    Kryteria:

    R.2.6 W pierwszym bloku banera umieszczono obraz zad5.png z tekstem alternatywnym: "logo lotnisko"

    ok. 1 min
    Polecenie z arkusza

    Zawartość pierwszego bloku banera: obraz zad5.png z tekstem alternatywnym „logo lotnisko”. Zawartość drugiego bloku banera: nagłówek pierwszego stopnia o treści „Przyloty”

    <img> z tekstem alternatywnym (R.2.6) i <h1> - jedyny nagłówek pierwszego stopnia na stronie.

    lotnisko.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Port Lotniczy</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <header>
            <section id="logo">
                <img src="zad5.png" alt="logo lotnisko">
            </section>
            <section id="title">
                <h1>Przyloty</h1>
            </section>
            <section id="links">
            </section>
        </header>
        <main>
        </main>
        <footer>
            <section id="status">
            </section>
            <section id="author">
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (lotnisko.php)
  12. Krok 12: Trzeci blok banera: nagłówek i odnośnik do kwerend

    Kryteria: ,

    R.3.2 Na stronie umieszczono odnośnik, którego wybranie prowadzi do pliku kwerendy.txt

    R.3.3 Odnośnik otwiera się w nowym oknie

    ok. 1 min
    Polecenie z arkusza

    Zawartość trzeciego bloku banera: Nagłówek trzeciego stopnia o treści: „przydatne linki”. Odnośnik o treści „Pobierz…” otwierający plik kwerendy.txt w nowym oknie

    <h3> i odnośnik <a href="kwerendy.txt"> (R.3.2) z target="_blank" - nowe okno albo karta (R.3.3). Dodatkowy rel="noopener" odcina otwartą stronę od karty, z której ją otwarto (bez niego mogłaby przez window.opener podmienić adres pierwszej karty). Współczesne przeglądarki robią to już same, ale zapis pokazuje zamiar wprost.

    lotnisko.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Port Lotniczy</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <header>
            <section id="logo">
                <img src="zad5.png" alt="logo lotnisko">
            </section>
            <section id="title">
                <h1>Przyloty</h1>
            </section>
            <section id="links">
                <h3>przydatne linki</h3>
                <a href="kwerendy.txt" target="_blank" rel="noopener">Pobierz…</a>
            </section>
        </header>
        <main>
        </main>
        <footer>
            <section id="status">
            </section>
            <section id="author">
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (lotnisko.php)
  13. Krok 13: Blok główny: tabela z nagłówkiem kolumn

    Kryteria:

    R.2.8 Umieszczono w tabeli wiersz nagłówkowy, zastosowano cztery znaczniki <th> wewnątrz pierwszego wiersza tabeli

    ok. 2 min
    Polecenie z arkusza

    Zawartość bloku głównego: Tabela o czterech kolumnach. Nagłówki kolumn mają podpisy: „czas”, „kierunek”, „numer rejsu”, „status”

    Wiersz z czterema komórkami <th> trafia do <thead>, a miejsce na wiersze danych to <tbody> - przeglądarka i czytnik ekranu wiedzą, który wiersz opisuje kolumny (przy wydruku długiej tabeli <thead> powtarza się na każdej stronie). Klucz dopuszcza <thead> wprost (R.2.8). Wiersze danych wstawi skrypt w <tbody>.

    lotnisko.php PHP zmienione: 12 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Port Lotniczy</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <header>
            <section id="logo">
                <img src="zad5.png" alt="logo lotnisko">
            </section>
            <section id="title">
                <h1>Przyloty</h1>
            </section>
            <section id="links">
                <h3>przydatne linki</h3>
                <a href="kwerendy.txt" target="_blank" rel="noopener">Pobierz…</a>
            </section>
        </header>
        <main>
            <table>
                <thead>
                    <tr>
                        <th>czas</th>
                        <th>kierunek</th>
                        <th>numer rejsu</th>
                        <th>status</th>
                    </tr>
                </thead>
                <tbody>
                </tbody>
            </table>
        </main>
        <footer>
            <section id="status">
            </section>
            <section id="author">
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (lotnisko.php)
  14. Krok 14: Drugi blok stopki: autor strony

    ok. 1 min
    Polecenie z arkusza

    Zawartość drugiego bloku stopki: tekst: „Autor: ”, dalej wstawiony swój numer zdającego

    Na egzaminie po „Autor: ” wpisujesz swój numer zdającego (PESEL), tu stoi imię i nazwisko autora: Bartosz Bryniarski. Blok status wypełni skrypt.

    lotnisko.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Port Lotniczy</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <header>
            <section id="logo">
                <img src="zad5.png" alt="logo lotnisko">
            </section>
            <section id="title">
                <h1>Przyloty</h1>
            </section>
            <section id="links">
                <h3>przydatne linki</h3>
                <a href="kwerendy.txt" target="_blank" rel="noopener">Pobierz…</a>
            </section>
        </header>
        <main>
            <table>
                <thead>
                    <tr>
                        <th>czas</th>
                        <th>kierunek</th>
                        <th>numer rejsu</th>
                        <th>status</th>
                    </tr>
                </thead>
                <tbody>
                </tbody>
            </table>
        </main>
        <footer>
            <section id="status">
            </section>
            <section id="author">
                Autor: Bartosz Bryniarski
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (lotnisko.php)
  15. Etap 3: Styl CSS

    Kroki 15-21 · po tym etapie masz 26 z 37 kryteriów (pkt)

    Krok 15: Zmienne kolorów i selektor body

    Kryteria: ,

    R.4.1 Ustawiono krój czcionki Garamond dla body

    R.4.3 Ustawiono biały kolor czcionki dla body

    ok. 2 min
    Polecenie z arkusza

    Cechy formatowania CSS: Dla selektora body: krój czcionki Garamond, kolor tła RGB 30, 144, 255; biały kolor czcionki

    Na początku pliku styl5.css reguła :root z trzema zmiennymi CSS - kolory z arkusza dostają nazwy (--sky, --navy, --silver), a reguły odwołują się do nich przez var(--sky). Szary 192, 192, 192 pojawia się w arkuszu trzy razy, więc zmiana odcienia to jedna linia.

    Reguła body: krój Garamond z zapasowym serif (Garamond instaluje się w Windows z pakietem Office - na komputerze bez niego przeglądarka weźmie inny krój szeryfowy, a nie domyślny), tło var(--sky) i biały tekst (R.4.1, R.4.3).

    styl5.css CSS nowy plik
    :root {
        --sky: rgb(30, 144, 255);
        --navy: rgb(0, 0, 128);
        --silver: rgb(192, 192, 192);
    }
    
    body {
        font-family: Garamond, serif;
        background-color: var(--sky);
        color: white;
    }
    Podgląd strony po tym kroku (lotnisko.php)
  16. Krok 16: Układ: rzędy banera i stopki na flex

    Kryteria:

    R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest w pełni 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 albo position)

    ok. 2 min
    Polecenie z arkusza

    Podział strony na bloki: trzy bloki banera, poniżej blok główny, następnie dwa bloki stopki. Podział zrealizowany za pomocą znaczników sekcji, zgodnie z obrazem 2

    display: flex na <header> i <footer> ustawia ich sekcje w jednym rzędzie, od lewej. <main> stoi między nimi zwykłym blokiem na całą szerokość - nie trzeba ani float, ani clear. Szerokości bloków ustawią kolejne reguły.

    Klucz wymienia flex wprost wśród sposobów ułożenia bloków (R.3.1); szerokości 25%, 50% i 50% stoją na samych blokach, tak jak oczekuje R.4.5.

    styl5.css CSS zmienione: 5 linii
    :root {
        --sky: rgb(30, 144, 255);
        --navy: rgb(0, 0, 128);
        --silver: rgb(192, 192, 192);
    }
    
    body {
        font-family: Garamond, serif;
        background-color: var(--sky);
        color: white;
    }
    
    header,
    footer {
        display: flex;
    }
    Podgląd strony po tym kroku (lotnisko.php)
  17. Krok 17: Bloki banera: szerokości, wysokość, nagłówek

    Kryteria:

    R.4.4 Ustawiono wysokość wszystkich bloków banera na 200px

    ok. 1 min
    Polecenie z arkusza

    Dla pierwszego i trzeciego bloku banera: szerokość 25%, wysokość 200 px. Dla drugiego bloku banera: wyrównanie tekstu do środka, szerokość 50%, wysokość 200 px, rozmiar czcionki 250%

    Szerokości banera sumują się do 100%, każdy blok ma 200 px wysokości (R.4.4). Elementy flex domyślnie mogą się kurczyć, ale tu nie muszą - bez marginesów i obramowań nic nie przekracza szerokości rzędu.

    styl5.css CSS zmienione: 13 linii
    :root {
        --sky: rgb(30, 144, 255);
        --navy: rgb(0, 0, 128);
        --silver: rgb(192, 192, 192);
    }
    
    body {
        font-family: Garamond, serif;
        background-color: var(--sky);
        color: white;
    }
    
    header,
    footer {
        display: flex;
    }
    
    #logo,
    #links {
        width: 25%;
        height: 200px;
    }
    
    #title {
        width: 50%;
        height: 200px;
        text-align: center;
        font-size: 250%;
    }
    Podgląd strony po tym kroku (lotnisko.php)
  18. Krok 18: Blok główny: tło, marginesy wewnętrzne, wielkie litery

    Kryteria:

    R.4.2 Ustawiono kolor tła rgb(30, 144, 255) dla body, oraz kolor tła rgb(0, 0, 128) dla bloku głównego

    ok. 1 min
    Polecenie z arkusza

    Dla bloku głównego: tło koloru RGB 0, 0, 128; marginesy wewnętrzne 60 px, tekst wielkimi literami (niezależnie od tego jak tekst został zapisany w HTML, wymagana transformacja na wielkie litery)

    Selektor znacznika main, tło var(--navy) - granat 0, 0, 128 (R.4.2 razem z tłem strony). text-transform: uppercase zmienia tylko wygląd, dane w HTML-u i w bazie zostają bez zmian.

    styl5.css CSS zmienione: 6 linii
    :root {
        --sky: rgb(30, 144, 255);
        --navy: rgb(0, 0, 128);
        --silver: rgb(192, 192, 192);
    }
    
    body {
        font-family: Garamond, serif;
        background-color: var(--sky);
        color: white;
    }
    
    header,
    footer {
        display: flex;
    }
    
    #logo,
    #links {
        width: 25%;
        height: 200px;
    }
    
    #title {
        width: 50%;
        height: 200px;
        text-align: center;
        font-size: 250%;
    }
    
    main {
        background-color: var(--navy);
        padding: 60px;
        text-transform: uppercase;
    }
    Podgląd strony po tym kroku (lotnisko.php)
  19. Krok 19: Bloki stopki

    Kryteria:

    R.4.6 Ustawiono wyrównanie tekstu do środka dla drugiego bloku banera, pierwszego bloku stopki oraz wyrównanie do prawej strony dla drugiego bloku stopki

    ok. 1 min
    Polecenie z arkusza

    Dla pierwszego bloku stopki: wyrównanie tekstu do środka, szerokość 50%. Dla drugiego bloku stopki: wyrównanie tekstu do prawej strony, szerokość 50%, margines wewnętrzny górny: 100 px (tylko górny)

    Dwa bloki po 50% w rzędzie stopki. padding-top to margines tylko od góry - dodaje wysokości, a nie szerokości, więc blok dalej mieści się obok pierwszego. Wyrównania tekstu banera i stopki są gotowe (R.4.6).

    styl5.css CSS zmienione: 11 linii
    :root {
        --sky: rgb(30, 144, 255);
        --navy: rgb(0, 0, 128);
        --silver: rgb(192, 192, 192);
    }
    
    body {
        font-family: Garamond, serif;
        background-color: var(--sky);
        color: white;
    }
    
    header,
    footer {
        display: flex;
    }
    
    #logo,
    #links {
        width: 25%;
        height: 200px;
    }
    
    #title {
        width: 50%;
        height: 200px;
        text-align: center;
        font-size: 250%;
    }
    
    main {
        background-color: var(--navy);
        padding: 60px;
        text-transform: uppercase;
    }
    
    #status {
        width: 50%;
        text-align: center;
    }
    
    #author {
        width: 50%;
        text-align: right;
        padding-top: 100px;
    }
    Podgląd strony po tym kroku (lotnisko.php)
  20. Krok 20: Paragraf i jego wygląd po najechaniu kursorem

    Kryteria: ,

    R.4.9 W momencie najechania kursorem na paragraf, jego kolor tła zmienia się na rgb(192, 192, 192)

    R.4.10 Ustawiono rozmiar czcionki 250% dla drugiego bloku banera, 200% dla paragrafu p oraz tekst wielkimi literami (text-transform: uppercase;) dla bloku głównego

    ok. 1 min
    Polecenie z arkusza

    Dla selektora paragrafu (akapitu): marginesy wewnętrzne 20 px, rozmiar czcionki 200%, obramowanie 1 px, linią ciągłą koloru RGB 192, 192, 192. W momencie najechania kursorem na paragraf, jego kolor tła zmienia się na RGB 192, 192, 192

    Obramowanie i tło po najechaniu korzystają z tej samej zmiennej --silver. Pseudoklasa :hover działa, dopóki kursor jest nad paragrafem (R.4.9). Rozmiary czcionek są ustawione (R.4.10).

    styl5.css CSS zmienione: 10 linii
    :root {
        --sky: rgb(30, 144, 255);
        --navy: rgb(0, 0, 128);
        --silver: rgb(192, 192, 192);
    }
    
    body {
        font-family: Garamond, serif;
        background-color: var(--sky);
        color: white;
    }
    
    header,
    footer {
        display: flex;
    }
    
    #logo,
    #links {
        width: 25%;
        height: 200px;
    }
    
    #title {
        width: 50%;
        height: 200px;
        text-align: center;
        font-size: 250%;
    }
    
    main {
        background-color: var(--navy);
        padding: 60px;
        text-transform: uppercase;
    }
    
    #status {
        width: 50%;
        text-align: center;
    }
    
    #author {
        width: 50%;
        text-align: right;
        padding-top: 100px;
    }
    
    p {
        padding: 20px;
        font-size: 200%;
        border: 1px solid var(--silver);
    }
    
    p:hover {
        background-color: var(--silver);
    }
    Podgląd strony po tym kroku (lotnisko.php)
  21. Krok 21: Tabela i jej komórki

    Kryteria: , ,

    R.4.5 Ustawiono szerokość pierwszego i trzeciego bloku banera na 25%, drugiego bloku banera oraz obu bloków stopki na 50% oraz szerokość tabeli na 100%

    R.4.7 Ustawiono marginesy wewnętrzne (padding) na: 60px dla bloku głównego, 20px dla paragrafu, 10px dla komórki tabeli i komórki nagłówkowej oraz jedynie margines wewnętrzny górny (padding-top) 100px dla drugiego bloku stopki

    R.4.8 Ustawiono obramowanie selektora p, td, th: 1px linią ciągłą rgb(192, 192, 192) oraz połączone obramowanie dla selektora tabeli (border-collapse: collapse;)

    ok. 1 min
    Polecenie z arkusza

    Dla selektora tabeli: szerokość 100%, obramowanie tabeli i komórki ma być połączone (collapse). Dla selektora komórki danych i komórki nagłówkowej tabeli: obramowanie 1px, linią ciągłą o kolorze RGB 192, 192, 192; marginesy wewnętrzne 10 px

    Tabela na całą szerokość bloku głównego (R.4.5), jedna wspólna linia między komórkami dzięki border-collapse i obramowanie komórek w kolorze --silver (R.4.8). Marginesy wewnętrzne wszystkich elementów są gotowe (R.4.7).

    styl5.css CSS zmienione: 11 linii
    :root {
        --sky: rgb(30, 144, 255);
        --navy: rgb(0, 0, 128);
        --silver: rgb(192, 192, 192);
    }
    
    body {
        font-family: Garamond, serif;
        background-color: var(--sky);
        color: white;
    }
    
    header,
    footer {
        display: flex;
    }
    
    #logo,
    #links {
        width: 25%;
        height: 200px;
    }
    
    #title {
        width: 50%;
        height: 200px;
        text-align: center;
        font-size: 250%;
    }
    
    main {
        background-color: var(--navy);
        padding: 60px;
        text-transform: uppercase;
    }
    
    #status {
        width: 50%;
        text-align: center;
    }
    
    #author {
        width: 50%;
        text-align: right;
        padding-top: 100px;
    }
    
    p {
        padding: 20px;
        font-size: 200%;
        border: 1px solid var(--silver);
    }
    
    p:hover {
        background-color: var(--silver);
    }
    
    table {
        width: 100%;
        border-collapse: collapse;
    }
    
    td,
    th {
        border: 1px solid var(--silver);
        padding: 10px;
    }
    Podgląd strony po tym kroku (lotnisko.php)
  22. Etap 4: Skrypt PHP

    Kroki 22-27 · po tym etapie masz 37 z 37 kryteriów (pkt)

    Krok 22: Połączenie PDO z bazą egzamin

    Kryteria: ,

    R.5.1 Skrypt napisano w języku PHP oraz zawiera przynajmniej jedną poprawną składniowo instrukcję, wynikającą z treści zadania

    R.5.2 Skrypt 1 realizuje połączenie z serwerem bazy danych i wybór bazy egzamin

    ok. 3 min
    Polecenie z arkusza

    Oba skrypty napisane w języku PHP, w pliku lotnisko.php. Działanie skryptu 1: Skrypt łączy się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie egzamin

    Logika PHP stoi na samej górze pliku, przed <!DOCTYPE html> - najpierw skrypt zbiera dane, potem HTML je wypisuje. W tabeli i w stopce zostaną tylko krótkie wstawki.

    • funkcja e() - skrót od escape: przepuszcza tekst przez htmlspecialchars, więc znaki <, >, & i cudzysłowy z bazy pokażą się jako tekst, a nie jako kod strony. Typ ?string przyjmuje też null (puste pole w bazie),
    • new PDO(...) - serwer localhost, baza egzamin w adresie DSN, użytkownik root, puste hasło (R.5.1, R.5.2); charset=utf8mb4 ustala kodowanie połączenia,
    • PDO::ERRMODE_EXCEPTION - każdy błąd bazy rzuca wyjątek, który łapie catch i zapisuje neutralny komunikat zamiast pokazywać szczegóły serwera.

    Podgląd to wynik prawdziwego uruchomienia - strona wygląda jak przed dodaniem PHP.

    lotnisko.php PHP zmienione: 17 linii
    <?php
    function e(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');
    }
    
    $flights = [];
    $error = null;
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=egzamin;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        ]);
    } catch (PDOException $exception) {
        $error = 'Tablica przylotów jest chwilowo niedostępna';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Port Lotniczy</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <header>
            <section id="logo">
                <img src="zad5.png" alt="logo lotnisko">
            </section>
            <section id="title">
                <h1>Przyloty</h1>
            </section>
            <section id="links">
                <h3>przydatne linki</h3>
                <a href="kwerendy.txt" target="_blank" rel="noopener">Pobierz…</a>
            </section>
        </header>
        <main>
            <table>
                <thead>
                    <tr>
                        <th>czas</th>
                        <th>kierunek</th>
                        <th>numer rejsu</th>
                        <th>status</th>
                    </tr>
                </thead>
                <tbody>
                </tbody>
            </table>
        </main>
        <footer>
            <section id="status">
            </section>
            <section id="author">
                Autor: Bartosz Bryniarski
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (lotnisko.php)
  23. Krok 23: Zapytanie 1 i wynik w tablicy

    Kryteria:

    R.5.4 Skrypt 1 wysyła do bazy zapytanie nr 1

    ok. 2 min
    Polecenie z arkusza

    Skrypt wysyła do bazy danych zapytanie 1

    $pdo->query(...) wysyła zapytanie 1 z kwerendy.txt (R.5.4) - nie ma w nim żadnych danych od użytkownika, więc nie trzeba go przygotowywać (prepare). fetchAll(PDO::FETCH_ASSOC) od razu pobiera wszystkie osiem wierszy do tablicy $flights; każdy wiersz to tablica z kluczami jak nazwy pól: $flight['czas'], $flight['kierunek']… Czytelniej niż numery $row[0].

    Gdy baza nie odpowiada, $flights zostaje pustą tablicą i strona nadal się wyświetli. Podgląd bez zmian - wiersze wypisze następny krok.

    lotnisko.php PHP zmienione: 2 linie
    <?php
    function e(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');
    }
    
    $flights = [];
    $error = null;
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=egzamin;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        ]);
        $flights = $pdo->query('SELECT czas, kierunek, nr_rejsu, status_lotu FROM przyloty ORDER BY czas;')
            ->fetchAll(PDO::FETCH_ASSOC);
    } catch (PDOException $exception) {
        $error = 'Tablica przylotów jest chwilowo niedostępna';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Port Lotniczy</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <header>
            <section id="logo">
                <img src="zad5.png" alt="logo lotnisko">
            </section>
            <section id="title">
                <h1>Przyloty</h1>
            </section>
            <section id="links">
                <h3>przydatne linki</h3>
                <a href="kwerendy.txt" target="_blank" rel="noopener">Pobierz…</a>
            </section>
        </header>
        <main>
            <table>
                <thead>
                    <tr>
                        <th>czas</th>
                        <th>kierunek</th>
                        <th>numer rejsu</th>
                        <th>status</th>
                    </tr>
                </thead>
                <tbody>
                </tbody>
            </table>
        </main>
        <footer>
            <section id="status">
            </section>
            <section id="author">
                Autor: Bartosz Bryniarski
            </section>
        </footer>
    </body>
    </html>

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

  24. Krok 24: Wiersze tabeli: foreach w tbody

    Kryteria: ,

    R.2.7 W bloku głównym umieszczono tabelę o czterech kolumnach, zastosowano znaczniki <table>, <tr>, <td>, tabela generowana skryptem lub HTML

    R.5.5 Dane otrzymane z bazy są umieszczane w kolejnych wierszach tabeli, w odpowiednich komórkach, tak jak na Obrazie 2

    ok. 3 min
    Polecenie z arkusza

    Dane otrzymane z bazy są umieszczane w kolejnych wierszach tabeli, w odpowiednich komórkach, tak jak na Obrazie 2

    W <tbody> pętla w składni alternatywnej - foreach (...): i endforeach; w osobnych wstawkach PHP. Między nimi stoi zwykły HTML wiersza, a w komórkach skrócone <?= ... ?> (to samo co <?php echo ... ?>) z funkcją e(). HTML zostaje czytelny - nie trzeba sklejać znaczników w napisach echo.

    Każdy przylot to wiersz z czterema komórkami w kolejności nagłówków (R.2.7, R.5.5). Pod pętlą warunek if ($error !== null) - przy awarii bazy tabela dostaje jeden wiersz z komunikatem na całą szerokość. Podgląd: osiem przylotów, jak na obrazie 2.

    lotnisko.php PHP zmienione: 13 linii
    <?php
    function e(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');
    }
    
    $flights = [];
    $error = null;
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=egzamin;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        ]);
        $flights = $pdo->query('SELECT czas, kierunek, nr_rejsu, status_lotu FROM przyloty ORDER BY czas;')
            ->fetchAll(PDO::FETCH_ASSOC);
    } catch (PDOException $exception) {
        $error = 'Tablica przylotów jest chwilowo niedostępna';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Port Lotniczy</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <header>
            <section id="logo">
                <img src="zad5.png" alt="logo lotnisko">
            </section>
            <section id="title">
                <h1>Przyloty</h1>
            </section>
            <section id="links">
                <h3>przydatne linki</h3>
                <a href="kwerendy.txt" target="_blank" rel="noopener">Pobierz…</a>
            </section>
        </header>
        <main>
            <table>
                <thead>
                    <tr>
                        <th>czas</th>
                        <th>kierunek</th>
                        <th>numer rejsu</th>
                        <th>status</th>
                    </tr>
                </thead>
                <tbody>
    <?php foreach ($flights as $flight): ?>
                    <tr>
                        <td><?= e($flight['czas']) ?></td>
                        <td><?= e($flight['kierunek']) ?></td>
                        <td><?= e($flight['nr_rejsu']) ?></td>
                        <td><?= e($flight['status_lotu']) ?></td>
                    </tr>
    <?php endforeach; ?>
    <?php if ($error !== null): ?>
                    <tr>
                        <td colspan="4"><?= e($error) ?></td>
                    </tr>
    <?php endif; ?>
                </tbody>
            </table>
        </main>
        <footer>
            <section id="status">
            </section>
            <section id="author">
                Autor: Bartosz Bryniarski
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (lotnisko.php)
  25. Krok 25: Zamknięcie połączenia w finally

    Kryteria:

    R.5.3 Po wykonaniu operacji na bazie zastosowano funkcję zamknięcia bazy

    ok. 1 min
    Polecenie z arkusza

    Po wykonaniu operacji na bazie skrypt zamyka połączenie z serwerem

    Połączenie PDO zamyka się, gdy znika ostatnie odwołanie do obiektu - stąd $pdo = null (R.5.3). Blok finally wykonuje się zawsze: po udanym zapytaniu i po błędzie. Gdy połączenie się nie udało, $pdo nie istnieje - przypisanie null niczego wtedy nie psuje.

    lotnisko.php PHP zmienione: 2 linie
    <?php
    function e(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');
    }
    
    $flights = [];
    $error = null;
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=egzamin;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        ]);
        $flights = $pdo->query('SELECT czas, kierunek, nr_rejsu, status_lotu FROM przyloty ORDER BY czas;')
            ->fetchAll(PDO::FETCH_ASSOC);
    } catch (PDOException $exception) {
        $error = 'Tablica przylotów jest chwilowo niedostępna';
    } finally {
        $pdo = null;
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Port Lotniczy</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <header>
            <section id="logo">
                <img src="zad5.png" alt="logo lotnisko">
            </section>
            <section id="title">
                <h1>Przyloty</h1>
            </section>
            <section id="links">
                <h3>przydatne linki</h3>
                <a href="kwerendy.txt" target="_blank" rel="noopener">Pobierz…</a>
            </section>
        </header>
        <main>
            <table>
                <thead>
                    <tr>
                        <th>czas</th>
                        <th>kierunek</th>
                        <th>numer rejsu</th>
                        <th>status</th>
                    </tr>
                </thead>
                <tbody>
    <?php foreach ($flights as $flight): ?>
                    <tr>
                        <td><?= e($flight['czas']) ?></td>
                        <td><?= e($flight['kierunek']) ?></td>
                        <td><?= e($flight['nr_rejsu']) ?></td>
                        <td><?= e($flight['status_lotu']) ?></td>
                    </tr>
    <?php endforeach; ?>
    <?php if ($error !== null): ?>
                    <tr>
                        <td colspan="4"><?= e($error) ?></td>
                    </tr>
    <?php endif; ?>
                </tbody>
            </table>
        </main>
        <footer>
            <section id="status">
            </section>
            <section id="author">
                Autor: Bartosz Bryniarski
            </section>
        </footer>
    </body>
    </html>

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

  26. Krok 26: Ciasteczko na 2 godziny

    Kryteria:

    R.5.7 Gdy odwiedzający jest na stronie pierwszy raz zakładane jest ciasteczko z czasem trwania 2 godziny od utworzenia

    ok. 3 min
    Polecenie z arkusza

    Działanie skryptu 2: W oparciu o mechanizm ciasteczek skrypt wypisuje na stronie komunikaty: Gdy odwiedzający wejdzie na stronę pierwszy raz zakładane jest ciasteczko z czasem trwania 2 godziny od utworzenia oraz wyświetlany jest w paragrafie, czcionką pogrubioną, komunikat: „Dzień dobry! Strona lotniska używa ciasteczek”

    Ciasteczko jest nagłówkiem odpowiedzi HTTP, więc setcookie musi się wykonać, zanim serwer wyśle pierwszy znak strony - kod na górze pliku spełnia to z natury.

    • isset($_COOKIE[COOKIE_NAME]) - czy przeglądarka przysłała ciasteczko, czyli czy to kolejna wizyta; wynik trafia do zmiennej $isReturning, z której skorzysta stopka,
    • setcookie z tablicą opcji (PHP 7.3+): expires = teraz + 2 × 60 × 60 = 7200 s (R.5.7), httponly - skrypty JavaScript nie odczytają ciasteczka, samesite: Lax - przeglądarka nie dołączy go do żądań z obcych stron,
    • nazwa i czas życia jako stałe (const) na górze pliku - liczba 7200 ma opisaną nazwę.

    Podgląd bez zmian - komunikat wypisze stopka.

    lotnisko.php PHP zmienione: 12 linii
    <?php
    const COOKIE_NAME = 'lotnisko';
    const COOKIE_LIFETIME = 2 * 60 * 60; // 2 godziny w sekundach
    
    function e(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');
    }
    
    $isReturning = isset($_COOKIE[COOKIE_NAME]);
    if (!$isReturning) {
        setcookie(COOKIE_NAME, 'odwiedzono', [
            'expires' => time() + COOKIE_LIFETIME,
            'httponly' => true,
            'samesite' => 'Lax',
        ]);
    }
    
    $flights = [];
    $error = null;
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=egzamin;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        ]);
        $flights = $pdo->query('SELECT czas, kierunek, nr_rejsu, status_lotu FROM przyloty ORDER BY czas;')
            ->fetchAll(PDO::FETCH_ASSOC);
    } catch (PDOException $exception) {
        $error = 'Tablica przylotów jest chwilowo niedostępna';
    } finally {
        $pdo = null;
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Port Lotniczy</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <header>
            <section id="logo">
                <img src="zad5.png" alt="logo lotnisko">
            </section>
            <section id="title">
                <h1>Przyloty</h1>
            </section>
            <section id="links">
                <h3>przydatne linki</h3>
                <a href="kwerendy.txt" target="_blank" rel="noopener">Pobierz…</a>
            </section>
        </header>
        <main>
            <table>
                <thead>
                    <tr>
                        <th>czas</th>
                        <th>kierunek</th>
                        <th>numer rejsu</th>
                        <th>status</th>
                    </tr>
                </thead>
                <tbody>
    <?php foreach ($flights as $flight): ?>
                    <tr>
                        <td><?= e($flight['czas']) ?></td>
                        <td><?= e($flight['kierunek']) ?></td>
                        <td><?= e($flight['nr_rejsu']) ?></td>
                        <td><?= e($flight['status_lotu']) ?></td>
                    </tr>
    <?php endforeach; ?>
    <?php if ($error !== null): ?>
                    <tr>
                        <td colspan="4"><?= e($error) ?></td>
                    </tr>
    <?php endif; ?>
                </tbody>
            </table>
        </main>
        <footer>
            <section id="status">
            </section>
            <section id="author">
                Autor: Bartosz Bryniarski
            </section>
        </footer>
    </body>
    </html>

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

  27. Krok 27: Komunikaty w stopce

    Kryteria: , , ,

    R.2.5 W drugim bloku banera zapisano nagłówek h1: "Przyloty", w trzecim bloku banera nagłówek h3: "przydatne linki" oraz w pierwszym bloku stopki zapisano paragraf (w kodzie HTML lub skrypcie)

    R.2.9 W pierwszym bloku stopki zastosowano znaczniki <i> </i> lub <em> </em> do pochylenia oraz <b>, </b> lub <strong> </strong> do pogrubienia tekstu (w skrypcie lub w kodzie HTML). Możliwe alternatywne rozwiązanie za pomocą stylów CSS

    R.5.6 Strona wykorzystuje mechanizm ciasteczek, zastosowano funkcję PHP setcookie, sprawdzono czy ciasteczko jest ustawione (np. isset, empty) oraz zastosowano tablicę $_COOKIE, zapis musi być poprawny składniowo i logicznie

    R.5.8 Gdy odwiedzający jest na stronie pierwszy raz wyświetlany jest komunikat "Dzień dobry! Strona lotniska używa ciasteczek” w przeciwnym wypadku wyświetlany jest komunikat „Witaj ponownie na stronie lotniska"

    ok. 3 min
    Polecenie z arkusza

    Gdy odwiedzający wejdzie na stronę pierwszy raz zakładane jest ciasteczko z czasem trwania 2 godziny od utworzenia oraz wyświetlany jest w paragrafie, czcionką pogrubioną, komunikat: „Dzień dobry! Strona lotniska używa ciasteczek”. Jeżeli odwiedzający w ciągu 2 godzin od poprzedniego wejścia wejdzie ponownie na stronę wyświetlany jest w paragrafie, czcionką pochyloną, komunikat „Witaj ponownie na stronie lotniska”

    W bloku status składnia alternatywna if (...): / else: / endif; z gotowym HTML-em obu komunikatów, każdy w paragrafie (R.2.5): pierwsza wizyta - <strong> (pogrubienie, „ważne”), kolejna - <em> (pochylenie, „z naciskiem”) (R.2.9). Strona korzysta z setcookie, isset i $_COOKIE (R.5.6) i wypisuje właściwy komunikat (R.5.8).

    Sprawdzone na prawdziwym serwerze dwoma wejściami tej samej przeglądarki: pierwsze dało „Dzień dobry! Strona lotniska używa ciasteczek”, drugie „Witaj ponownie na stronie lotniska”; ciasteczko ma flagę HttpOnly i wygasa 7200 s po utworzeniu. Podgląd: druga wizyta, jak na obrazie 2.

    lotnisko.php PHP zmienione: 5 linii
    <?php
    const COOKIE_NAME = 'lotnisko';
    const COOKIE_LIFETIME = 2 * 60 * 60; // 2 godziny w sekundach
    
    function e(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');
    }
    
    $isReturning = isset($_COOKIE[COOKIE_NAME]);
    if (!$isReturning) {
        setcookie(COOKIE_NAME, 'odwiedzono', [
            'expires' => time() + COOKIE_LIFETIME,
            'httponly' => true,
            'samesite' => 'Lax',
        ]);
    }
    
    $flights = [];
    $error = null;
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=egzamin;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        ]);
        $flights = $pdo->query('SELECT czas, kierunek, nr_rejsu, status_lotu FROM przyloty ORDER BY czas;')
            ->fetchAll(PDO::FETCH_ASSOC);
    } catch (PDOException $exception) {
        $error = 'Tablica przylotów jest chwilowo niedostępna';
    } finally {
        $pdo = null;
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Port Lotniczy</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <header>
            <section id="logo">
                <img src="zad5.png" alt="logo lotnisko">
            </section>
            <section id="title">
                <h1>Przyloty</h1>
            </section>
            <section id="links">
                <h3>przydatne linki</h3>
                <a href="kwerendy.txt" target="_blank" rel="noopener">Pobierz…</a>
            </section>
        </header>
        <main>
            <table>
                <thead>
                    <tr>
                        <th>czas</th>
                        <th>kierunek</th>
                        <th>numer rejsu</th>
                        <th>status</th>
                    </tr>
                </thead>
                <tbody>
    <?php foreach ($flights as $flight): ?>
                    <tr>
                        <td><?= e($flight['czas']) ?></td>
                        <td><?= e($flight['kierunek']) ?></td>
                        <td><?= e($flight['nr_rejsu']) ?></td>
                        <td><?= e($flight['status_lotu']) ?></td>
                    </tr>
    <?php endforeach; ?>
    <?php if ($error !== null): ?>
                    <tr>
                        <td colspan="4"><?= e($error) ?></td>
                    </tr>
    <?php endif; ?>
                </tbody>
            </table>
        </main>
        <footer>
            <section id="status">
    <?php if ($isReturning): ?>
                <p><em>Witaj ponownie na stronie lotniska</em></p>
    <?php else: ?>
                <p><strong>Dzień dobry! Strona lotniska używa ciasteczek</strong></p>
    <?php endif; ?>
            </section>
            <section id="author">
                Autor: Bartosz Bryniarski
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (lotnisko.php)
  28. Etap 5: Porównanie z wersją podstawową

    Krok 28 · po tym etapie masz 37 z 37 kryteriów (pkt)

    Krok 28: Co jest inaczej i którą wersję wybrać na egzaminie

    ok. 3 min

    Obie wersje dają komplet 37 punktów. Różnice:

    wersja podstawowawersja nowoczesna
    blokisześć <section> jedna pod drugą<header>, <main>, <footer> z sekcjami w środku
    układfloat: left i clear: bothdisplay: flex na <header> i <footer>
    kolorywpisane w każdej regulezmienne --sky, --navy, --silver
    tabelawiersze sklejane w echo<thead> i <tbody>, HTML wiersza z <?= ?>
    bibliotekamysqli (tabela 1 arkusza)PDO z wyjątkami
    gdzie jest skryptskrypt 1 w tabeli, skrypt 2 na górze i w stopcelogika na górze pliku, w HTML-u tylko wypisanie
    wiersz wynikumysqli_fetch_row, $row[0]fetchAll, $flight['czas']
    dane z bazywprost (zabezpieczone w Dobrych praktykach)zawsze przez e()
    błąd bazy„Fatal error” ze ścieżką pliku (naprawione w Dobrych praktykach)catch i wiersz z komunikatem
    zamknięciemysqli_close po pętli$pdo = null w finally
    ciasteczkosetcookie(nazwa, wartość, time() + 7200)tablica opcji z httponly i samesite
    wyróżnienie<b> i <i> (wygląd)<strong> i <em> (znaczenie)

    Sprawdzone na prawdziwym PHP: status z wpisanym znacznikiem <s> ta wersja pokazuje dosłownie, a strona uruchomiona bez bazy egzamin wyświetla tablicę z komunikatem „Tablica przylotów jest chwilowo niedostępna” i działającą stopką.

    Na egzaminie wersja podstawowa trzyma się funkcji z tabeli 1 arkusza i łatwiej ją napisać bez dokumentacji. Nowoczesna oddziela logikę od wyglądu, nie wpuszcza kodu z bazy na stronę i nie wywraca się przy awarii serwera - a klucz przyjmuje obie.

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

  29. Etap 6: Oddanie pracy

    Krok 29 · po tym etapie masz 37 z 37 kryteriów (pkt)

    Krok 29: Plik przeglądarka.txt i nagranie płyty

    ok. 5 min
    Polecenie z arkusza

    Po zakończeniu pracy utwórz plik tekstowy. Zapisz w nim nazwę przeglądarki internetowej, w której weryfikowałeś poprawność działania witryny, zapisz go w folderze z numerem zdającego, jako przeglądarka.txt. Nagraj płytę z rezultatami pracy.

    W przeglądarka.txt nazwa przeglądarki, w której sprawdzana była strona. W folderze zdającego: import.png, kw1.jpg - kw4.jpg, kwerendy.txt, lotnisko.php, przeglądarka.txt, styl5.css i przeskalowany zad5.png. 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 27.

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.

Potwierdzenie

Informacja