Przejdź do głównej treści
EE.09-06-21.01-SGStyczeń 2021 · 28 kroków · ok. 58 min · 37 kryteriów z klucza

Rozwiązanie krok po kroku: Tablica odlotów z lotniska (Nowoczesny kod (flex, PDO))

To drugi sposób rozwiązania tego samego arkusza. Wynik jest ten sam - baza egzamin z czterema zapytaniami, przeskalowany logotyp z przezroczystością i strona airport.php z tablicą odlotó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 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 2021 - kwalifikacji formuły 2017, którą zastąpiła INF.03 - pokrewny tablicy przylotów (EE.09-05) z tej samej sesji. Baza i grafika pochodzą z oryginalnego archiwum CKE z6.zip (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 z6.zip

    ok. 2 min
    Polecenie z arkusza

    Aby wykonać zadanie, zaloguj się na konto Egzamin bez hasła. Na pulpicie znajdziesz archiwum ZIP o nazwie z6.zip zabezpieczone hasłem: @SaMolotY9% Archiwum należy rozpakować. Na pulpicie konta Egzamin utwórz folder. Jako nazwy folderu użyj swojego numeru PESEL. Umieść w tym folderze rozpakowane pliki.

    Folder na pulpicie nazwany numerem PESEL - w tym rozwiązaniu Bartosz Bryniarski. Archiwum z6.zip rozpakuj do tego folderu, hasło @SaMolotY9%. W środku: egzamin.sql i zad6.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

    W XAMPP Control Panel Start przy Apache i MySQL, w phpMyAdmin (http://localhost/phpmyadmin) Nowa → nazwa egzamin → Utwórz. Potem przy zaznaczonej bazie karta Import, plik egzamin.sql, Wykonaj. Do zadania potrzebne są trzy tabele z obrazu 1: odloty (10 lotów), przyloty (8) i samoloty (4); pole samoloty_id w tabelach lotów wskazuje id samolotu.

  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 PESEL, 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 pokazuje listę tabel z liczbą rekordów - to „elementy wskazujące na poprawnie wykonany import”. Zrzut całego ekranu z paskiem zadań zapisujesz jako import.png.

    Podgląd strony po tym kroku
    Podgląd strony po kroku 3
  4. Krok 4: Zapytanie 1: odloty od najpóźniejszego

    Kryteria: ,

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

    R.1.3 Utworzono zapytanie 1 wybierające jedynie pola id, nr_rejsu, czas, kierunek i status_lotu z tabeli odloty posortowane malejąco według czasu. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT id, nr_rejsu, czas, kierunek, status_lotu FROM odloty ORDER BY czas DESC; oraz wynik działania zapytania udokumentowano zrzutem, na którym jest widocznych dokładnie 10 rekordów, jedynie pola id, nr_rejsu, czas, kierunek, status_lotu. Rekordy są posortowane maleją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 PESEL. 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 id, nr_rejsu, czas, kierunek i status_lotu z tabeli odloty posortowane malejąco według czasu

    Pięć pól w kolejności kolumn przyszłej tablicy, ORDER BY czas DESC sortuje malejąco. Wynik: 10 odlotów od 10:35:00 do najwcześniejszego. Zapytanie do kwerendy.txt, zrzut kw1.jpg - to samo zapytanie wyśle skrypt.

    kwerendy.txt Tekst nowy plik
    -- Zapytanie 1
    SELECT id, nr_rejsu, czas, kierunek, status_lotu FROM odloty ORDER BY czas DESC;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 4
  5. Krok 5: Zapytanie 2: najwcześniejszy odlot między 10:10 a 10:19

    Kryteria:

    R.1.4 Utworzono zapytanie 2 wyznaczające najwcześniejszy (najmniejszy) czas odlotu dla lotów, których czas odlotu jest pomiędzy 10:10 – 10:19. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT MIN(czas) FROM odloty WHERE czas LIKE "10:1%"; możliwe również BETWEEN oraz wynik działania zapytania udokumentowano zrzutem, na którym jest widoczna jedynie wartość 10:13:00, wynikająca z zadanego zapytania

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 2: wyznaczające najwcześniejszy (najmniejszy) czas odlotu dla lotów, których czas odlotu jest pomiędzy 10:10 ÷ 10:19

    • SELECT MIN(czas) - funkcja agregująca zwraca najmniejszą wartość kolumny, czyli najwcześniejszą godzinę,
    • FROM odloty,
    • WHERE czas BETWEEN '10:10:00' AND '10:19:59' - oba końce przedziału wliczone; klucz przyjmuje też WHERE czas LIKE '10:1%'.

    Wynik: 10:13:00. Zrzut: kw2.jpg.

    Typowe błędy

    ORDER BY czas LIMIT 1 też znajdzie ten odlot, ale arkusz każe „wyznaczyć najmniejszy czas” - do tego służy funkcja MIN, której oczekuje klucz.

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

    Kryteria:

    R.1.5 Utworzono zapytanie 3 wybierające jedynie pola nr_rejsu z tabeli odloty oraz linie z tabeli samoloty dla lotów, dla których kierunkiem jest Malta. Zastosowano relację. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nr_rejsu, samoloty.linie FROM odloty JOIN samoloty ON odloty.samoloty_id = samoloty.id WHERE kierunek = "Malta"; możliwe też 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: LX5673, SWISS

    ok. 3 min
    Polecenie z arkusza

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

    • SELECT nr_rejsu, linie - numer rejsu z tabeli odloty, linia lotnicza z tabeli samoloty,
    • FROM odloty JOIN samoloty ON odloty.samoloty_id = samoloty.id - relacja z obrazu 1,
    • WHERE kierunek = 'Malta'.

    Wynik: jeden wiersz - LX5673, SWISS. Zrzut: kw3.jpg.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    SELECT id, nr_rejsu, czas, kierunek, status_lotu FROM odloty ORDER BY czas DESC;
    
    -- Zapytanie 2
    SELECT MIN(czas) FROM odloty WHERE czas BETWEEN '10:10:00' AND '10:19:59';
    
    -- Zapytanie 3
    SELECT nr_rejsu, linie FROM odloty JOIN samoloty ON odloty.samoloty_id = samoloty.id WHERE kierunek = 'Malta';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 6
  7. Krok 7: Zapytanie 4: status przylotu LH9829

    Kryteria:

    R.1.6 Utworzono zapytanie 4 aktualizujące w tabeli przyloty pole status_lotu na: „planowy” dla lotu o numerze rejsu LH9829. W pliku z kwerendami lub na zrzucie istnieje zapis np.: UPDATE przyloty SET status_lotu="planowy" WHERE nr_rejsu="LH9829"; oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczne jest poprawne zapytanie lub zmienione dane w tabeli

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 4: aktualizujące w tabeli przyloty pole status_lotu na: „planowy” dla lotu o numerze rejsu LH9829

    Uwaga na tabelę: ta jedyna kwerenda dotyczy przylotów. UPDATE przyloty → SET status_lotu = 'planowy' → WHERE nr_rejsu = 'LH9829' (przylot z Frankfurtu). phpMyAdmin odpowiada „1 wiersz zmieniony” - zrzut kw4.jpg. Tablica odlotów na stronie się nie zmienia.

    Typowe błędy

    UPDATE bez WHERE zmieniłby status wszystkich ośmiu przylotów.

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

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

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

    Kryteria:

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

    ok. 2 min
    Polecenie z arkusza

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

    Oryginał ma 640 × 259 px i przezroczyste tło. W GIMP-ie Obraz → Skaluj obraz z zamkniętym łańcuchem proporcji, wysokość 150 px - szerokość: 640 × 150 / 259 = 370,7, czyli 371 px (klucz: 371 ±5). Zapis jako PNG - tylko ten format z dwóch używanych na stronie przechowuje przezroczystość.

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

    Kryteria: , ,

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

    R.2.3 Nadano tytuł strony: "Odloty samolotów”

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

    ok. 2 min
    Polecenie z arkusza

    Składa się ze strony o nazwie airport.php. Zastosowany właściwy standard kodowania polskich znaków. Tytuł strony widoczny na karcie przeglądarki: „Odloty samolotów”. Arkusz stylów w pliku o nazwie styl6.css prawidłowo połączony z kodem strony

    Cała część <head> w jednym kroku:

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

    Kryteria:

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

    ok. 2 min
    Polecenie z arkusza

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

    <header> z blokami banera title i logo, <main> - blok główny (jeden na stronie, więc bez identyfikatora), <footer> z blokami download, status i author. Znaczniki HTML5 mówią, czym jest każda część strony, a kontenery <header> i <footer> w CSS dostaną display: flex.

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

    Kryteria:

    R.2.6 W drugim bloku banera umieszczono obraz zad6.png z tekstem alternatywnym: "logotyp", gdy obraz nie istnieje powinien wyświetlać się tekst alternatywny

    ok. 1 min
    Polecenie z arkusza

    Zawartość pierwszego bloku banera: nagłówek drugiego stopnia o treści „Odloty z lotniska”. Zawartość drugiego bloku banera: obraz zad6.png z tekstem alternatywnym „logotyp”

    <h2> (poziom narzucony przez arkusz) i <img> z tekstem alternatywnym (R.2.6).

    airport.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Odloty samolotów</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="styl6.css">
    </head>
    <body>
        <header>
            <section id="title">
                <h2>Odloty z lotniska</h2>
            </section>
            <section id="logo">
                <img src="zad6.png" alt="logotyp">
            </section>
        </header>
        <main>
        </main>
        <footer>
            <section id="download">
            </section>
            <section id="status">
            </section>
            <section id="author">
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (airport.php)
  12. Krok 12: Blok główny: nagłówek i tabela

    Kryteria:

    R.2.8 Umieszczono w tabeli wiersz nagłówkowy, zastosowano pięć znaczników <th> (opcjonalnie <thead> i <td>) oraz ich zamknięcia wewnątrz pierwszego wiersza tabeli

    ok. 2 min
    Polecenie z arkusza

    Zawartość bloku głównego: Nagłówek czwartego stopnia o treści: „tabela odlotów”. Tabela o pięciu kolumnach. Nagłówki kolumn mają podpisy: „lp.”, „numer rejsu”, „czas”, „kierunek”, „status”

    <h4> i tabela z wierszem nagłówkowym w <thead> (klucz wprost dopuszcza <thead>, R.2.8) oraz pustym <tbody>, w którym skrypt wypisze wiersze. Kolumna „lp.” pokaże pole id.

    airport.php PHP zmienione: 14 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Odloty samolotów</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="styl6.css">
    </head>
    <body>
        <header>
            <section id="title">
                <h2>Odloty z lotniska</h2>
            </section>
            <section id="logo">
                <img src="zad6.png" alt="logotyp">
            </section>
        </header>
        <main>
            <h4>tabela odlotów</h4>
            <table>
                <thead>
                    <tr>
                        <th>lp.</th>
                        <th>numer rejsu</th>
                        <th>czas</th>
                        <th>kierunek</th>
                        <th>status</th>
                    </tr>
                </thead>
                <tbody>
                </tbody>
            </table>
        </main>
        <footer>
            <section id="download">
            </section>
            <section id="status">
            </section>
            <section id="author">
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (airport.php)
  13. Krok 13: Stopka: odnośnik i autor

    Kryteria: ,

    R.3.2 Na stronie umieszczono odnośnik, którego wybranie prowadzi do dowolnego pliku ze zrzutem

    R.3.3 Odnośnik otwiera się w nowym oknie / karcie przeglądarki

    ok. 1 min
    Polecenie z arkusza

    Zawartość pierwszego bloku stopki: Odnośnik o treści „Pobierz obraz” otwierający plik kw1.jpg w nowym oknie. Zawartość trzeciego bloku stopki: tekst: „Autor: ”, dalej wstawiony numer PESEL zdającego

    Odnośnik do zrzutu kw1.jpg (R.3.2) z target="_blank" (R.3.3); rel="noopener" odcina otwartą kartę od strony, z której ją otwarto. Po „Autor: ” na egzaminie stoi numer PESEL, tu Bartosz Bryniarski. Środkowy blok wypełni skrypt.

    airport.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Odloty samolotów</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="styl6.css">
    </head>
    <body>
        <header>
            <section id="title">
                <h2>Odloty z lotniska</h2>
            </section>
            <section id="logo">
                <img src="zad6.png" alt="logotyp">
            </section>
        </header>
        <main>
            <h4>tabela odlotów</h4>
            <table>
                <thead>
                    <tr>
                        <th>lp.</th>
                        <th>numer rejsu</th>
                        <th>czas</th>
                        <th>kierunek</th>
                        <th>status</th>
                    </tr>
                </thead>
                <tbody>
                </tbody>
            </table>
        </main>
        <footer>
            <section id="download">
                <a href="kw1.jpg" target="_blank" rel="noopener">Pobierz obraz</a>
            </section>
            <section id="status">
            </section>
            <section id="author">
                Autor: Bartosz Bryniarski
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (airport.php)
  14. Etap 3: Styl CSS

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

    Krok 14: Zmienne kolorów i selektor body

    Kryteria: ,

    R.4.1 Ustawiono krój czcionki Arial dla selektora body lub kontenera całej strony

    R.4.3 Ustawiono biały kolor czcionki dla selektora body lub kontenera całej strony

    ok. 2 min
    Polecenie z arkusza

    Cechy formatowania CSS: Dla selektora body: krój czcionki Arial, kolor tła RGB 244, 164, 96; biały kolor czcionki

    Reguła :root z zmiennymi CSS - kolory z arkusza dostają nazwy, a reguły odwołują się do nich przez var(--sand). Reguła body: Arial z zapasowym krojem bezszeryfowym, piaskowe tło i biały tekst (R.4.1, R.4.3).

    styl6.css CSS nowy plik
    :root {
        --sand: rgb(244, 164, 96);
        --maroon: rgb(128, 0, 0);
        --dark-gray: rgb(169, 169, 169);
        --silver: rgb(192, 192, 192);
    }
    
    body {
        font-family: Arial, sans-serif;
        background-color: var(--sand);
        color: white;
    }
    Podgląd strony po tym kroku (airport.php)
  15. Krok 15: Układ: rzędy banera i stopki na flex

    Kryteria:

    R.3.1 Wygląd układu bloków po uruchomieniu strony w przeglądarce jest w pełni zgodny z obrazem 2 w arkuszu egzaminacyjnym

    ok. 2 min
    Polecenie z arkusza

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

    display: flex na <header> i <footer> ustawia ich sekcje w rzędzie. <main> stoi między nimi zwykłym blokiem na całą szerokość - bez float i clear. Klucz ocenia układ w przeglądarce (R.3.1), a szerokości 75/25 i 20/60/20 stoją na samych blokach, jak oczekuje R.4.5.

    styl6.css CSS zmienione: 5 linii
    :root {
        --sand: rgb(244, 164, 96);
        --maroon: rgb(128, 0, 0);
        --dark-gray: rgb(169, 169, 169);
        --silver: rgb(192, 192, 192);
    }
    
    body {
        font-family: Arial, sans-serif;
        background-color: var(--sand);
        color: white;
    }
    
    header,
    footer {
        display: flex;
    }
    Podgląd strony po tym kroku (airport.php)
  16. Krok 16: Bloki banera

    Kryteria:

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

    ok. 1 min
    Polecenie z arkusza

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

    Szerokości sumują się do 100%, oba bloki mają 150 px wysokości (R.4.4). Logotyp o szerokości 371 px mieści się w bloku 25% w oknie szerszym niż ok. 1500 px - to wynika z wymiarów z arkusza.

    styl6.css CSS zmienione: 12 linii
    :root {
        --sand: rgb(244, 164, 96);
        --maroon: rgb(128, 0, 0);
        --dark-gray: rgb(169, 169, 169);
        --silver: rgb(192, 192, 192);
    }
    
    body {
        font-family: Arial, sans-serif;
        background-color: var(--sand);
        color: white;
    }
    
    header,
    footer {
        display: flex;
    }
    
    #title {
        width: 75%;
        height: 150px;
        text-align: center;
        font-size: 200%;
    }
    
    #logo {
        width: 25%;
        height: 150px;
    }
    Podgląd strony po tym kroku (airport.php)
  17. Krok 17: Blok główny

    Kryteria:

    R.4.2 Ustawiono kolor tła rgb(244, 164, 96) dla selektora body lub kontenera całej strony, oraz kolor tła rgb(128, 0, 0) dla bloku głównego

    ok. 1 min
    Polecenie z arkusza

    Dla bloku głównego: tło koloru RGB 128, 0, 0; marginesy wewnętrzne 50 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(--maroon) (R.4.2 razem z tłem strony), text-transform: uppercase zmienia tylko wygląd tekstu.

    styl6.css CSS zmienione: 6 linii
    :root {
        --sand: rgb(244, 164, 96);
        --maroon: rgb(128, 0, 0);
        --dark-gray: rgb(169, 169, 169);
        --silver: rgb(192, 192, 192);
    }
    
    body {
        font-family: Arial, sans-serif;
        background-color: var(--sand);
        color: white;
    }
    
    header,
    footer {
        display: flex;
    }
    
    #title {
        width: 75%;
        height: 150px;
        text-align: center;
        font-size: 200%;
    }
    
    #logo {
        width: 25%;
        height: 150px;
    }
    
    main {
        background-color: var(--maroon);
        padding: 50px;
        text-transform: uppercase;
    }
    Podgląd strony po tym kroku (airport.php)
  18. Krok 18: Bloki stopki

    Kryteria:

    R.4.6 Ustawiono wyrównanie tekstu do środka dla pierwszego bloku banera i drugiego bloku stopki

    ok. 1 min
    Polecenie z arkusza

    Dla pierwszego i trzeciego bloku stopki: szerokość 20%, margines wewnętrzny górny: 90 px (tylko górny). Dla drugiego bloku stopki: wyrównanie tekstu do środka, szerokość 60%

    Trzy bloki 20 + 60 + 20 w rzędzie stopki; padding-top dodaje wysokości, nie szerokości. Wyrównania są gotowe (R.4.6).

    styl6.css CSS zmienione: 11 linii
    :root {
        --sand: rgb(244, 164, 96);
        --maroon: rgb(128, 0, 0);
        --dark-gray: rgb(169, 169, 169);
        --silver: rgb(192, 192, 192);
    }
    
    body {
        font-family: Arial, sans-serif;
        background-color: var(--sand);
        color: white;
    }
    
    header,
    footer {
        display: flex;
    }
    
    #title {
        width: 75%;
        height: 150px;
        text-align: center;
        font-size: 200%;
    }
    
    #logo {
        width: 25%;
        height: 150px;
    }
    
    main {
        background-color: var(--maroon);
        padding: 50px;
        text-transform: uppercase;
    }
    
    #download,
    #author {
        width: 20%;
        padding-top: 90px;
    }
    
    #status {
        width: 60%;
        text-align: center;
    }
    Podgląd strony po tym kroku (airport.php)
  19. Krok 19: Paragraf i jego wygląd po najechaniu kursorem

    Kryteria: ,

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

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

    ok. 1 min
    Polecenie z arkusza

    Dla selektora paragrafu (akapitu): rozmiar czcionki 150%, obramowanie 2 px, linią kropkowaną koloru RGB 169, 169, 169. W momencie najechania kursorem na paragraf, jego kolor tła zmienia się na RGB 169, 169, 169

    Linia kropkowana dotted i tło po najechaniu z tej samej zmiennej --dark-gray (R.4.9). Rozmiary czcionek są gotowe (R.4.10).

    styl6.css CSS zmienione: 9 linii
    :root {
        --sand: rgb(244, 164, 96);
        --maroon: rgb(128, 0, 0);
        --dark-gray: rgb(169, 169, 169);
        --silver: rgb(192, 192, 192);
    }
    
    body {
        font-family: Arial, sans-serif;
        background-color: var(--sand);
        color: white;
    }
    
    header,
    footer {
        display: flex;
    }
    
    #title {
        width: 75%;
        height: 150px;
        text-align: center;
        font-size: 200%;
    }
    
    #logo {
        width: 25%;
        height: 150px;
    }
    
    main {
        background-color: var(--maroon);
        padding: 50px;
        text-transform: uppercase;
    }
    
    #download,
    #author {
        width: 20%;
        padding-top: 90px;
    }
    
    #status {
        width: 60%;
        text-align: center;
    }
    
    p {
        font-size: 150%;
        border: 2px dotted var(--dark-gray);
    }
    
    p:hover {
        background-color: var(--dark-gray);
    }
    Podgląd strony po tym kroku (airport.php)
  20. Krok 20: Tabela i jej komórki

    Kryteria: , ,

    R.4.5 Ustawiono szerokość pierwszego bloku banera 75%, drugiego bloku banera na 25%, drugiego bloku stopki 60%, pierwszego i trzeciego bloku stopki 20% oraz szerokość tabeli na 100%

    R.4.7 Ustawiono marginesy wewnętrzne (padding): 50px dla bloku głównego, 5px dla selektora td lub th oraz jedynie margines wewnętrzny górny (padding-top) 90px dla pierwszego i trzeciego bloku stopki

    R.4.8 Ustawiono obramowanie selektora p 2px linią kropkowaną koloru rgb(169, 169, 169); dla td lub 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 1 px, linią ciągłą o kolorze RGB 192, 192, 192; marginesy wewnętrzne 5 px

    Tabela na całą szerokość bloku głównego (R.4.5), wspólne linie komórek dzięki border-collapse, obramowanie w kolorze --silver (R.4.8) i 5 px marginesu komórek (R.4.7).

    styl6.css CSS zmienione: 11 linii
    :root {
        --sand: rgb(244, 164, 96);
        --maroon: rgb(128, 0, 0);
        --dark-gray: rgb(169, 169, 169);
        --silver: rgb(192, 192, 192);
    }
    
    body {
        font-family: Arial, sans-serif;
        background-color: var(--sand);
        color: white;
    }
    
    header,
    footer {
        display: flex;
    }
    
    #title {
        width: 75%;
        height: 150px;
        text-align: center;
        font-size: 200%;
    }
    
    #logo {
        width: 25%;
        height: 150px;
    }
    
    main {
        background-color: var(--maroon);
        padding: 50px;
        text-transform: uppercase;
    }
    
    #download,
    #author {
        width: 20%;
        padding-top: 90px;
    }
    
    #status {
        width: 60%;
        text-align: center;
    }
    
    p {
        font-size: 150%;
        border: 2px dotted var(--dark-gray);
    }
    
    p:hover {
        background-color: var(--dark-gray);
    }
    
    table {
        width: 100%;
        border-collapse: collapse;
    }
    
    td,
    th {
        border: 1px solid var(--silver);
        padding: 5px;
    }
    Podgląd strony po tym kroku (airport.php)
  21. Etap 4: Skrypt PHP

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

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

    Kryteria: ,

    R.5.1 Skrypt napisano w języku PHP i zawiera on co najmniej jedną instrukcję poprawną składniowo

    R.5.2 Skrypt1 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 airport.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 górze pliku, przed <!DOCTYPE html> - najpierw dane, potem HTML, który je wypisuje.

    • e() przepuszcza tekst przez htmlspecialchars, więc znaki <, >, & i cudzysłowy z bazy pokażą się jako tekst; ?string przyjmuje też null,
    • new PDO(...) - localhost, baza egzamin w adresie DSN, root, puste hasło (R.5.1, R.5.2),
    • PDO::ERRMODE_EXCEPTION - błąd bazy rzuca wyjątek, a catch zapisuje neutralny komunikat zamiast pokazywać szczegóły serwera.
    airport.php PHP zmienione: 17 linii
    <?php
    function e(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');
    }
    
    $departures = [];
    $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 odlotów jest chwilowo niedostępna';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Odloty samolotów</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="styl6.css">
    </head>
    <body>
        <header>
            <section id="title">
                <h2>Odloty z lotniska</h2>
            </section>
            <section id="logo">
                <img src="zad6.png" alt="logotyp">
            </section>
        </header>
        <main>
            <h4>tabela odlotów</h4>
            <table>
                <thead>
                    <tr>
                        <th>lp.</th>
                        <th>numer rejsu</th>
                        <th>czas</th>
                        <th>kierunek</th>
                        <th>status</th>
                    </tr>
                </thead>
                <tbody>
                </tbody>
            </table>
        </main>
        <footer>
            <section id="download">
                <a href="kw1.jpg" target="_blank" rel="noopener">Pobierz obraz</a>
            </section>
            <section id="status">
            </section>
            <section id="author">
                Autor: Bartosz Bryniarski
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (airport.php)
  22. Krok 22: 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 (R.5.4) - bez danych od użytkownika, więc bez prepare. fetchAll(PDO::FETCH_ASSOC) pobiera dziesięć wierszy do tablicy $departures, a pola są dostępne po nazwach: $departure['nr_rejsu']. Gdy baza nie odpowiada, tablica zostaje pusta i strona się wyświetli.

    airport.php PHP zmienione: 2 linie
    <?php
    function e(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');
    }
    
    $departures = [];
    $error = null;
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=egzamin;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        ]);
        $departures = $pdo->query('SELECT id, nr_rejsu, czas, kierunek, status_lotu FROM odloty ORDER BY czas DESC;')
            ->fetchAll(PDO::FETCH_ASSOC);
    } catch (PDOException $exception) {
        $error = 'Tablica odlotów jest chwilowo niedostępna';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Odloty samolotów</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="styl6.css">
    </head>
    <body>
        <header>
            <section id="title">
                <h2>Odloty z lotniska</h2>
            </section>
            <section id="logo">
                <img src="zad6.png" alt="logotyp">
            </section>
        </header>
        <main>
            <h4>tabela odlotów</h4>
            <table>
                <thead>
                    <tr>
                        <th>lp.</th>
                        <th>numer rejsu</th>
                        <th>czas</th>
                        <th>kierunek</th>
                        <th>status</th>
                    </tr>
                </thead>
                <tbody>
                </tbody>
            </table>
        </main>
        <footer>
            <section id="download">
                <a href="kw1.jpg" target="_blank" rel="noopener">Pobierz obraz</a>
            </section>
            <section id="status">
            </section>
            <section id="author">
                Autor: Bartosz Bryniarski
            </section>
        </footer>
    </body>
    </html>

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

  23. Krok 23: Wiersze tabeli: foreach w tbody

    Kryteria: ,

    R.2.7 W bloku głównym umieszczono tabelę o pięciu kolumnach, zastosowano znaczniki <table>, <tr>, <td> i ich zamknięcia, generowane skryptem lub HTML

    R.5.5 Dane otrzymane z bazy są wyświetlane 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

    Pętla w składni alternatywnej foreach (...): / endforeach;, między nimi zwykły HTML wiersza, a w komórkach skrócone <?= ... ?> z e(). Pięć komórek w kolejności nagłówków (R.2.7, R.5.5); przy awarii bazy - jeden wiersz z komunikatem. Podgląd: dziesięć odlotów, jak na obrazie 2.

    airport.php PHP zmienione: 14 linii
    <?php
    function e(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');
    }
    
    $departures = [];
    $error = null;
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=egzamin;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        ]);
        $departures = $pdo->query('SELECT id, nr_rejsu, czas, kierunek, status_lotu FROM odloty ORDER BY czas DESC;')
            ->fetchAll(PDO::FETCH_ASSOC);
    } catch (PDOException $exception) {
        $error = 'Tablica odlotów jest chwilowo niedostępna';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Odloty samolotów</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="styl6.css">
    </head>
    <body>
        <header>
            <section id="title">
                <h2>Odloty z lotniska</h2>
            </section>
            <section id="logo">
                <img src="zad6.png" alt="logotyp">
            </section>
        </header>
        <main>
            <h4>tabela odlotów</h4>
            <table>
                <thead>
                    <tr>
                        <th>lp.</th>
                        <th>numer rejsu</th>
                        <th>czas</th>
                        <th>kierunek</th>
                        <th>status</th>
                    </tr>
                </thead>
                <tbody>
    <?php foreach ($departures as $departure): ?>
                    <tr>
                        <td><?= e($departure['id']) ?></td>
                        <td><?= e($departure['nr_rejsu']) ?></td>
                        <td><?= e($departure['czas']) ?></td>
                        <td><?= e($departure['kierunek']) ?></td>
                        <td><?= e($departure['status_lotu']) ?></td>
                    </tr>
    <?php endforeach; ?>
    <?php if ($error !== null): ?>
                    <tr>
                        <td colspan="5"><?= e($error) ?></td>
                    </tr>
    <?php endif; ?>
                </tbody>
            </table>
        </main>
        <footer>
            <section id="download">
                <a href="kw1.jpg" target="_blank" rel="noopener">Pobierz obraz</a>
            </section>
            <section id="status">
            </section>
            <section id="author">
                Autor: Bartosz Bryniarski
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (airport.php)
  24. Krok 24: 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

    $pdo = null usuwa ostatnie odwołanie do obiektu i zamyka połączenie (R.5.3); finally wykonuje się po sukcesie i po błędzie.

    airport.php PHP zmienione: 2 linie
    <?php
    function e(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');
    }
    
    $departures = [];
    $error = null;
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=egzamin;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        ]);
        $departures = $pdo->query('SELECT id, nr_rejsu, czas, kierunek, status_lotu FROM odloty ORDER BY czas DESC;')
            ->fetchAll(PDO::FETCH_ASSOC);
    } catch (PDOException $exception) {
        $error = 'Tablica odlotów jest chwilowo niedostępna';
    } finally {
        $pdo = null;
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Odloty samolotów</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="styl6.css">
    </head>
    <body>
        <header>
            <section id="title">
                <h2>Odloty z lotniska</h2>
            </section>
            <section id="logo">
                <img src="zad6.png" alt="logotyp">
            </section>
        </header>
        <main>
            <h4>tabela odlotów</h4>
            <table>
                <thead>
                    <tr>
                        <th>lp.</th>
                        <th>numer rejsu</th>
                        <th>czas</th>
                        <th>kierunek</th>
                        <th>status</th>
                    </tr>
                </thead>
                <tbody>
    <?php foreach ($departures as $departure): ?>
                    <tr>
                        <td><?= e($departure['id']) ?></td>
                        <td><?= e($departure['nr_rejsu']) ?></td>
                        <td><?= e($departure['czas']) ?></td>
                        <td><?= e($departure['kierunek']) ?></td>
                        <td><?= e($departure['status_lotu']) ?></td>
                    </tr>
    <?php endforeach; ?>
    <?php if ($error !== null): ?>
                    <tr>
                        <td colspan="5"><?= e($error) ?></td>
                    </tr>
    <?php endif; ?>
                </tbody>
            </table>
        </main>
        <footer>
            <section id="download">
                <a href="kw1.jpg" target="_blank" rel="noopener">Pobierz obraz</a>
            </section>
            <section id="status">
            </section>
            <section id="author">
                Autor: Bartosz Bryniarski
            </section>
        </footer>
    </body>
    </html>

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

  25. Krok 25: Ciasteczko na godzinę

    Kryteria:

    R.5.7 Gdy odwiedzający jest na stronie pierwszy raz zakładane jest ciasteczko z czasem trwania 1 godzina od utworzenia, np. time() + 3600

    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 1 godzina od utworzenia oraz wyświetlany jest w paragrafie, czcionką pochyloną, komunikat: „Dzień dobry! Sprawdź regulamin naszej strony”

    setcookie musi się wykonać przed pierwszym znakiem strony - kod na górze pliku spełnia to z natury.

    • isset($_COOKIE[COOKIE_NAME]) - czy to kolejna wizyta; wynik w zmiennej $isReturning dla stopki,
    • setcookie z tablicą opcji: expires = teraz + 60 × 60 = 3600 s (R.5.7), httponly (JavaScript nie odczyta ciasteczka), samesite: Lax (bez ciasteczka w żądaniach z obcych stron),
    • nazwa i czas życia jako stałe - liczba 3600 ma opisaną nazwę.
    airport.php PHP zmienione: 12 linii
    <?php
    const COOKIE_NAME = 'odloty';
    const COOKIE_LIFETIME = 60 * 60; // godzina 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',
        ]);
    }
    
    $departures = [];
    $error = null;
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=egzamin;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        ]);
        $departures = $pdo->query('SELECT id, nr_rejsu, czas, kierunek, status_lotu FROM odloty ORDER BY czas DESC;')
            ->fetchAll(PDO::FETCH_ASSOC);
    } catch (PDOException $exception) {
        $error = 'Tablica odlotów jest chwilowo niedostępna';
    } finally {
        $pdo = null;
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Odloty samolotów</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="styl6.css">
    </head>
    <body>
        <header>
            <section id="title">
                <h2>Odloty z lotniska</h2>
            </section>
            <section id="logo">
                <img src="zad6.png" alt="logotyp">
            </section>
        </header>
        <main>
            <h4>tabela odlotów</h4>
            <table>
                <thead>
                    <tr>
                        <th>lp.</th>
                        <th>numer rejsu</th>
                        <th>czas</th>
                        <th>kierunek</th>
                        <th>status</th>
                    </tr>
                </thead>
                <tbody>
    <?php foreach ($departures as $departure): ?>
                    <tr>
                        <td><?= e($departure['id']) ?></td>
                        <td><?= e($departure['nr_rejsu']) ?></td>
                        <td><?= e($departure['czas']) ?></td>
                        <td><?= e($departure['kierunek']) ?></td>
                        <td><?= e($departure['status_lotu']) ?></td>
                    </tr>
    <?php endforeach; ?>
    <?php if ($error !== null): ?>
                    <tr>
                        <td colspan="5"><?= e($error) ?></td>
                    </tr>
    <?php endif; ?>
                </tbody>
            </table>
        </main>
        <footer>
            <section id="download">
                <a href="kw1.jpg" target="_blank" rel="noopener">Pobierz obraz</a>
            </section>
            <section id="status">
            </section>
            <section id="author">
                Autor: Bartosz Bryniarski
            </section>
        </footer>
    </body>
    </html>

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

  26. Krok 26: Komunikaty w stopce

    Kryteria: , , ,

    R.2.5 W pierwszym bloku banera zapisano nagłówek h2: "Odloty z lotniska", w bloku głównym nagłówek h4: "tabela odlotów" oraz w drugim bloku stopki zapisano paragraf (kodem HTML lub skryptem)

    R.2.9 W drugim bloku stopki gdy wyświetlany jest tekst "Dzień dobry!..." czcionka jest pochylona lub gdy wyświetlony jest tekst "Miło nam..." czcionka jest pogrubiona (dowolną metodą)

    R.5.6 Skrypt 2 wykorzystuje mechanizm ciasteczek, zastosowano funkcje setcookie, isset oraz 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! Sprawdź regulamin naszej strony” w przeciwnym wypadku komunikat „Miło nam że nas znowu odwiedziłeś"

    ok. 3 min
    Polecenie z arkusza

    Gdy odwiedzający wejdzie na stronę pierwszy raz zakładane jest ciasteczko z czasem trwania 1 godzina od utworzenia oraz wyświetlany jest w paragrafie, czcionką pochyloną, komunikat: „Dzień dobry! Sprawdź regulamin naszej strony”. Jeżeli odwiedzający w ciągu godziny od poprzedniego wejścia wejdzie ponownie na stronę wyświetlany jest w paragrafie, czcionką pogrubioną, komunikat „Miło nam, że nas znowu odwiedziłeś”

    W bloku status składnia alternatywna if (...): / else: / endif; z HTML-em obu komunikatów w paragrafach (R.2.5): pierwsza wizyta - <em> (pochylenie), kolejna - <strong> (pogrubienie) (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! Sprawdź regulamin naszej strony”, drugie „Miło nam, że nas znowu odwiedziłeś”; ciasteczko ma flagę HttpOnly i wygasa 3600 s po utworzeniu. Podgląd: druga wizyta, jak na obrazie 2.

    airport.php PHP zmienione: 5 linii
    <?php
    const COOKIE_NAME = 'odloty';
    const COOKIE_LIFETIME = 60 * 60; // godzina 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',
        ]);
    }
    
    $departures = [];
    $error = null;
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=egzamin;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        ]);
        $departures = $pdo->query('SELECT id, nr_rejsu, czas, kierunek, status_lotu FROM odloty ORDER BY czas DESC;')
            ->fetchAll(PDO::FETCH_ASSOC);
    } catch (PDOException $exception) {
        $error = 'Tablica odlotów jest chwilowo niedostępna';
    } finally {
        $pdo = null;
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Odloty samolotów</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="styl6.css">
    </head>
    <body>
        <header>
            <section id="title">
                <h2>Odloty z lotniska</h2>
            </section>
            <section id="logo">
                <img src="zad6.png" alt="logotyp">
            </section>
        </header>
        <main>
            <h4>tabela odlotów</h4>
            <table>
                <thead>
                    <tr>
                        <th>lp.</th>
                        <th>numer rejsu</th>
                        <th>czas</th>
                        <th>kierunek</th>
                        <th>status</th>
                    </tr>
                </thead>
                <tbody>
    <?php foreach ($departures as $departure): ?>
                    <tr>
                        <td><?= e($departure['id']) ?></td>
                        <td><?= e($departure['nr_rejsu']) ?></td>
                        <td><?= e($departure['czas']) ?></td>
                        <td><?= e($departure['kierunek']) ?></td>
                        <td><?= e($departure['status_lotu']) ?></td>
                    </tr>
    <?php endforeach; ?>
    <?php if ($error !== null): ?>
                    <tr>
                        <td colspan="5"><?= e($error) ?></td>
                    </tr>
    <?php endif; ?>
                </tbody>
            </table>
        </main>
        <footer>
            <section id="download">
                <a href="kw1.jpg" target="_blank" rel="noopener">Pobierz obraz</a>
            </section>
            <section id="status">
    <?php if ($isReturning): ?>
                <p><strong>Miło nam, że nas znowu odwiedziłeś</strong></p>
    <?php else: ?>
                <p><em>Dzień dobry! Sprawdź regulamin naszej strony</em></p>
    <?php endif; ?>
            </section>
            <section id="author">
                Autor: Bartosz Bryniarski
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (airport.php)
  27. Etap 5: Porównanie z wersją podstawową

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

    Krok 27: 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 --sand, --maroon, --dark-gray, --silver
    tabelawiersze sklejane w echo<thead> i <tbody>, HTML wiersza z <?= ?>
    bibliotekamysqli (tabela 1 arkusza)PDO z wyjątkami
    wiersz wynikumysqli_fetch_row, $row[1]fetchAll, $departure['nr_rejsu']
    dane z bazywprost (zabezpieczone w Dobrych praktykach)zawsze przez e()
    błąd bazy„Fatal error” ze ścieżką plikucatch i wiersz z komunikatem
    zamknięciemysqli_close po pętli$pdo = null w finally
    ciasteczkosetcookie(nazwa, wartość, time() + 3600)tablica opcji z httponly i samesite
    wyróżnienie<i> i <b> (wygląd)<em> i <strong> (znaczenie)

    Sprawdzone na prawdziwym PHP: status ze znacznikiem <s> ta wersja pokazuje dosłownie, a bez bazy egzamin strona wyświetla tablicę z komunikatem „Tablica odlotó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 i nie wywraca się przy awarii bazy - klucz przyjmuje obie.

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

  28. Etap 6: Oddanie pracy

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

    Krok 28: 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 PESEL, 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: airport.php, import.png, kw1.jpg - kw4.jpg, kwerendy.txt, przeglądarka.txt, styl6.css i przeskalowany zad6.png. Nagraj płytę, sprawdź jej odczyt i opisz ją numerem PESEL.

    przeglądarka.txt Tekst nowy plik
    Google Chrome

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

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