Przejdź do głównej treści
EE.09-05-21.01-SGStyczeń 2021 · 42 kroki · ok. 78 min · 37 kryteriów z klucza

Rozwiązanie krok po kroku: Tablica przylotów portu lotniczego (Wersja podstawowa)

Aplikacja internetowa portu lotniczego: baza egzamin z tabelami przyloty, samoloty i odloty, cztery zapytania (sortowanie ORDER BY, zliczanie COUNT w przedziale godzin, relacja JOIN i aktualizacja UPDATE), logo samolotu przeskalowane z zachowaniem przezroczystości oraz strona lotnisko.php: trzy bloki banera, tablica przylotów wypełniana skryptem PHP z bazy i stopka, w której drugi skrypt na podstawie ciasteczka wita gościa przy pierwszej wizycie albo wita go ponownie.

To arkusz EE.09 ze stycznia 2021 - kwalifikacji formuły 2017, którą zastąpiła INF.03. W styczniu 2024 to samo zadanie wróciło jako EE.09-02 z dwoma dodatkowymi wymaganiami - ma własne rozwiązanie na swojej stronie. Kroki idą zdanie po zdaniu za treścią arkusza - przy każdym jest polecenie z arkusza, a kod pokazuje dokładnie te linie, które je spełniają. Skrypty są najpierw napisane najprościej, funkcjami mysqli z tabeli 1 arkusza, a na końcu sprawdzone na prawdziwych błędach i zabezpieczone.

To jest wersja podstawowa. W zakładce „Nowoczesny kod (flex, PDO)” jest drugi sposób rozwiązania tego samego arkusza: znaczniki <header>, <main> i <footer> z układem flex, zmienne CSS, PDO z obsługą wyjątków i ciasteczko z flagami bezpieczeństwa - obie wersje są zgodne z kluczem.

Baza i grafika pochodzą z oryginalnego archiwum CKE z5.zip (załącznik do pobrania na stronie arkusza, hasło w treści zadania). Zapytania i zrzuty są policzone na MariaDB 10.4 - tej samej, która jest w XAMPP. Podgląd od kroków z PHP to wynik prawdziwego uruchomienia kodu w PHP 8.2, także z ciasteczkiem zapamiętanym między dwoma wejściami na stronę.

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 - można stracić 9 kryteriów i nadal zdać. Przy każdym kroku jest napisane, które kryteria spełnia; egzaminator ocenia spełnienie kryteriów z klucza, nie zgodność z tym kodem.

  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 z5.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 z5.zip zabezpieczone hasłem: LotniSko%7 Archiwum należy rozpakować. Na pulpicie konta Egzamin utwórz folder. Jako nazwy folderu użyj swojego numeru PESEL. Umieść w tym folderze rozpakowane pliki.

    Na pulpicie tworzysz folder nazwany numerem PESEL - w tym rozwiązaniu w jego miejscu stoi imię i nazwisko autora, Bartosz Bryniarski. Archiwum z5.zip otwierasz programem 7-Zip i rozpakowujesz do tego folderu, podając hasło LotniSko%7 (wielkość liter i znaki specjalne mają znaczenie). W środku są dwa pliki: egzamin.sql (baza do importu) i zad5.png (logo z samolotem).

    Do tego folderu trafia wszystko, co powstanie: zrzuty, kwerendy.txt, lotnisko.php, styl5.css i przeskalowana grafika.

    Typowe błędy

    Pliki zapisane poza folderem zdającego nie trafią na płytę - a ocenia się tylko to, co jest na płycie.

  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 klikasz Start przy Apache i MySQL, potem w przeglądarce otwierasz http://localhost/phpmyadmin. Nowa w lewym panelu, nazwa egzamin, Utwórz. Następnie zaznaczasz bazę egzamin, karta Import, Wybierz plik → egzamin.sql, Wykonaj.

    Plik tworzy siedem tabel z danymi. Do zadania potrzebne są trzy z obrazu 1: przyloty (8 lotów), odloty (10 lotów) i samoloty (4 typy samolotów z liniami). Pole samoloty_id w obu tabelach lotów wskazuje id samolotu - to relacja, z której skorzysta zapytanie 3.

    Typowe błędy

    Plik importujesz do bazy egzamin - zaznaczonej w lewym panelu. Import bez zaznaczonej bazy kończy się błędem „Nie wybrano bazy danych”.

  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

    Po imporcie kliknij bazę egzamin - karta Struktura pokazuje listę tabel z liczbą rekordów (przyloty 8, odloty 10, samoloty 4). Zielony komunikat o udanym imporcie i lista tabel to „elementy wskazujące na poprawnie wykonany import”. Zrzut całego ekranu (Print Screen, wklejenie do edytora grafiki) zapisujesz jako import.png.

    Typowe błędy

    Ten zrzut jest w PNG, a zrzuty kwerend w JPG - zły format albo wykadrowany zrzut bez paska zadań nie jest oceniany.

    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 Utworzono 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 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 czas, kierunek, nr_rejsu i status_lotu z tabeli przyloty posortowane rosnąco według czasu

    Zaznacz bazę egzamin, karta SQL. Zapytanie klauzula po klauzuli:

    • SELECT czas, kierunek, nr_rejsu, status_lotu - „jedynie” te cztery pola, w tej kolejności (tak samo ułożone będą kolumny tablicy na stronie),
    • FROM przyloty - tabela przylotów,
    • ORDER BY czas - sortowanie według czasu; bez słowa DESC porządek jest rosnący (można dopisać ASC).

    Wynik: 8 przylotów od 09:45:00 (Warszawa) do 10:33:00 (Zurych). Zapytanie wklejasz do pliku kwerendy.txt w folderze zdającego, zrzut całego ekranu z wynikiem - kw1.jpg. To samo zapytanie wyśle później skrypt 1.

    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 Utworzono 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

    • SELECT COUNT(*) - funkcja agregująca: zamiast wierszy zwraca ich liczbę,
    • FROM przyloty,
    • WHERE czas BETWEEN '10:00:00' AND '10:59:59' - kolumna czas ma typ TIME, więc porównujesz ją z godzinami; BETWEEN obejmuje oba końce przedziału.

    Wynik: 7 (wszystkie przyloty poza warszawskim z 09:45). Klucz przyjmuje też zapis WHERE czas LIKE '10%' - porównanie z wzorcem tekstowym działa, bo czas jest wtedy zamieniany na napis 10:03:00. Zrzut: kw2.jpg.

    Typowe błędy

    BETWEEN '10:00' AND '10:59' pomija przyloty z 10:59:30 - koniec przedziału to 10:59:59. COUNT(czas) zamiast COUNT(*) też jest poprawne, bo żaden przylot nie ma pustego czasu.

    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 Utworzono zapytanie 3 wybierające jedynie pola nr_rejsu z tabeli przyloty oraz typ z tabeli samoloty , dla lotów których kierunkiem jest Warszawa. Wykorzystano relację. 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ę

    • SELECT nr_rejsu, typ - numer rejsu jest w tabeli przyloty, typ samolotu w tabeli samoloty,
    • FROM przyloty JOIN samoloty - złączenie dwóch tabel,
    • ON przyloty.samoloty_id = samoloty.id - warunek relacji z obrazu 1: klucz obcy przylotu wskazuje klucz główny samolotu,
    • WHERE kierunek = 'Warszawa'.

    Wynik: jeden wiersz - W63454, Boening 737 (pisownia „Boening” pochodzi z bazy CKE). Zrzut: kw3.jpg.

    Typowe błędy

    Bez warunku ON (samo FROM przyloty, samoloty WHERE kierunek = ...) baza łączy przylot z każdym samolotem - wynik ma 4 wiersze zamiast jednego. Klucz przyjmuje też porównanie kluczy w WHERE zamiast JOIN.

    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 Utworzono 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 na którym widoczne jest poprawne zapytanie lub widoczne są rekordy ze zmienionymi danymi

    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

    Uwaga na tabelę: ta jedyna kwerenda dotyczy odlotów, nie przylotów.

    • UPDATE odloty - która tabela,
    • SET status_lotu = 'opoznienie 20 min' - nowa wartość pola, napis w apostrofach (bez polskich liter, dosłownie jak w arkuszu),
    • WHERE nr_rejsu = 'LX5673' - którego wiersza dotyczy zmiana (lot na Maltę).

    phpMyAdmin odpowiada „1 wiersz zmieniony” - zrzut kw4.jpg z zapytaniem i komunikatem. Plik kwerendy.txt ma komplet czterech zapytań. Tablica przylotów na stronie się nie zmienia, bo zmieniony został odlot.

    Typowe błędy

    UPDATE bez WHERE zmieniłby status wszystkich dziesięciu odlotów. Przed wykonaniem przeczytaj zapytanie jeszcze raz - phpMyAdmin nie ma przycisku „cofnij”.

    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-19 · 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

    Oryginał ma 457 × 300 px i przezroczyste tło (kanał alfa). W GIMP-ie: Obraz → Skaluj obraz, łańcuch między szerokością a wysokością zamknięty (to jest „zachowanie proporcji”), wysokość 200 px - szerokość przelicza się sama: 457 × 200 / 300 = 304,7, czyli 305 px (klucz: 305 px ±5).

    Zapis: Plik → Nadpisz zad5.png albo Eksportuj jako z formatem PNG - tylko on zachowa przezroczystość (JPEG zamieniłby tło na białe albo czarne).

    Typowe błędy

    Eksport do JPG albo spłaszczenie obrazu (Obraz → Spłaszcz) usuwa przezroczystość - na niebieskim tle strony pojawi się białe pole wokół samolotu.

    Podgląd strony po tym kroku
    Podgląd strony po kroku 8
  9. Krok 9: Plik lotnisko.php

    ok. 1 min
    Polecenie z arkusza

    Składa się ze strony o nazwie lotnisko.php

    Plik lotnisko.php w folderze zdającego, obok zad5.png. Rozszerzenie .php, bo dojdą skrypty - na razie to czysty HTML. <!DOCTYPE html> oznacza HTML5, a lang="pl" mówi przeglądarce i czytnikom ekranu, że treść jest po polsku (arkusz tego nie wymaga, ale to jedna krótka linia).

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

    Kryteria:

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

    ok. 1 min
    Polecenie z arkusza

    Zastosowany właściwy standard kodowania polskich znaków

    <meta charset="UTF-8"> jako pierwsza rzecz w <head>. Plik trzeba też zapisać w UTF-8 (w Notepad++ menu Kodowanie). Klucz ocenia nazwę pliku i kodowanie razem (R.2.2) - lotnisko.html albo index.php to strata punktu.

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

    Kryteria:

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

    ok. 1 min
    Polecenie z arkusza

    Tytuł strony widoczny na karcie przeglądarki: „Port Lotniczy”

    <title> w <head> - napis na karcie przeglądarki. Nagłówek widoczny na stronie to inny tekst („Przyloty”).

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

    Kryteria:

    R.3.4 Strona zawiera działające połączenie z zewnętrznym arkuszem stylów o nazwie styl5.css formatowanie pochodzi jedynie z tego arkusza (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. 1 min
    Polecenie z arkusza

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

    <link rel="stylesheet" href="styl5.css"> w <head>. Nazwa ma w sobie cyfrę: styl5.css. Plik stylów powstanie po HTML-u.

    Typowe błędy

    Klucz uznaje R.3.4 tylko przy działającym połączeniu, w którym wszystkie selektory trafiają w bloki - kropka zamiast # (albo literówka w identyfikatorze) też odbiera punkt.

    lotnisko.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Port Lotniczy</title>
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (lotnisko.php)
  13. Krok 13: Sześć bloków: trzy banera, główny, dwa stopki

    Kryteria:

    R.2.4 Wygląd strony zdefiniowano dzieląc ją na bloki: trzy bloki banera, blok główny, 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

    Sześć znaczników <section> w kolejności z obrazu 2. Bloki różnią się wymiarami, więc każdy dostaje identyfikator (nazwy po angielsku - arkusz ich nie narzuca): bloki banera logo, title i links, blok główny main, bloki stopki status (tu skrypt 2 wypisze komunikat o ciasteczku) i author. Na razie stoją jeden pod drugim; obok siebie ustawi je CSS.

    lotnisko.php PHP zmienione: 12 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Port Lotniczy</title>
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <section id="logo">
        </section>
        <section id="title">
        </section>
        <section id="links">
        </section>
        <section id="main">
        </section>
        <section id="status">
        </section>
        <section id="author">
        </section>
    </body>
    </html>
    Podgląd strony po tym kroku (lotnisko.php)
  14. Krok 14: Pierwszy blok banera: logo z tekstem alternatywnym

    Kryteria:

    R.2.6 W pierwszym bloku banera umieszczono obraz zad5.png z tekstem alternatywnym: "logo lotnisko", gdy obraz nie istnieje powinien wyświetlać się tekst alternatywny

    ok. 1 min
    Polecenie z arkusza

    Zawartość pierwszego bloku banera: obraz zad5.png z tekstem alternatywnym „logo lotnisko”

    <img> z atrybutami src (przeskalowany plik z folderu zdającego) i alt - tekst, który czytnik ekranu przeczyta zamiast obrazu i który pokaże się, gdy plik się nie wczyta. W HTML5 <img> nie ma znacznika zamykającego ani ukośnika na końcu.

    lotnisko.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Port Lotniczy</title>
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <section id="logo">
            <img src="zad5.png" alt="logo lotnisko">
        </section>
        <section id="title">
        </section>
        <section id="links">
        </section>
        <section id="main">
        </section>
        <section id="status">
        </section>
        <section id="author">
        </section>
    </body>
    </html>
    Podgląd strony po tym kroku (lotnisko.php)
  15. Krok 15: Drugi blok banera: nagłówek „Przyloty”

    ok. 1 min
    Polecenie z arkusza

    Zawartość drugiego bloku banera: nagłówek pierwszego stopnia o treści „Przyloty”

    Pierwszego stopnia to <h1> - jedyny taki nagłówek na stronie.

    lotnisko.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Port Lotniczy</title>
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <section id="logo">
            <img src="zad5.png" alt="logo lotnisko">
        </section>
        <section id="title">
            <h1>Przyloty</h1>
        </section>
        <section id="links">
        </section>
        <section id="main">
        </section>
        <section id="status">
        </section>
        <section id="author">
        </section>
    </body>
    </html>
    Podgląd strony po tym kroku (lotnisko.php)
  16. Krok 16: Trzeci blok banera: nagłówek „przydatne linki”

    ok. 1 min
    Polecenie z arkusza

    Zawartość trzeciego bloku banera: Nagłówek trzeciego stopnia o treści: „przydatne linki”

    Trzeciego stopnia to <h3>. Na stronie nie ma <h2> - poziomy nagłówków wynikają tu wprost z arkusza. Tekst małymi literami, jak w arkuszu.

    lotnisko.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Port Lotniczy</title>
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <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>
        </section>
        <section id="main">
        </section>
        <section id="status">
        </section>
        <section id="author">
        </section>
    </body>
    </html>
    Podgląd strony po tym kroku (lotnisko.php)
  17. Krok 17: Odnośnik do kwerendy.txt w nowym oknie

    Kryteria: ,

    R.3.2 Na stronie umieszczono odnośnik, którego wybranie prowadzi do pliku tekstowego zawierającego kwerendy

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

    ok. 1 min
    Polecenie z arkusza

    Odnośnik o treści „Pobierz…” otwierający plik kwerendy.txt w nowym oknie

    <a href="kwerendy.txt"> prowadzi do pliku zapytań z tego samego folderu (R.3.2), a atrybut target="_blank" otwiera go w nowym oknie albo karcie (R.3.3). Przeglądarka pokaże plik tekstowy wprost - nie trzeba go pobierać.

    Typowe błędy

    target="blank" (bez podkreślnika) też otwiera nową kartę, ale przy każdym kliknięciu tę samą o nazwie „blank”. Właściwa wartość to _blank.

    lotnisko.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Port Lotniczy</title>
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <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">Pobierz…</a>
        </section>
        <section id="main">
        </section>
        <section id="status">
        </section>
        <section id="author">
        </section>
    </body>
    </html>
    Podgląd strony po tym kroku (lotnisko.php)
  18. Krok 18: Blok główny: tabela z wierszem nagłówkowym

    Kryteria:

    R.2.8 Umieszczono w tabeli wiersz nagłówkowy, zastosowano cztery znaczniki <th> (opcjonalnie <thead> i <td>) oraz ich zamknięcia 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”

    <table>, w nim pierwszy wiersz <tr> z czterema komórkami nagłówkowymi <th> - przeglądarka domyślnie pogrubia je i wyśrodkowuje. Kolejność kolumn jest taka jak pól w zapytaniu 1 (czas, kierunek, numer rejsu, status), więc skrypt wstawi komórki po kolei. Podpisy wpisujesz małymi literami, jak w arkuszu - na obrazie 2 są wielkie, bo zamieni je CSS.

    Kolejne wiersze („Zawartość tabeli jest wypełniana skryptem 1”) wstawi skrypt PHP przed </table>.

    lotnisko.php PHP zmienione: 8 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Port Lotniczy</title>
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <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">Pobierz…</a>
        </section>
        <section id="main">
            <table>
                <tr>
                    <th>czas</th>
                    <th>kierunek</th>
                    <th>numer rejsu</th>
                    <th>status</th>
                </tr>
            </table>
        </section>
        <section id="status">
        </section>
        <section id="author">
        </section>
    </body>
    </html>
    Podgląd strony po tym kroku (lotnisko.php)
  19. Krok 19: Drugi blok stopki: autor strony

    ok. 1 min
    Polecenie z arkusza

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

    Na egzaminie po „Autor: ” wpisujesz swój numer PESEL. W tym rozwiązaniu w jego miejscu stoi imię i nazwisko autora: Bartosz Bryniarski. Pierwszy blok stopki zostaje pusty - wypełni go skrypt 2 („Zawartość pierwszego bloku stopki: efekt działania skryptu 2”).

    lotnisko.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Port Lotniczy</title>
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <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">Pobierz…</a>
        </section>
        <section id="main">
            <table>
                <tr>
                    <th>czas</th>
                    <th>kierunek</th>
                    <th>numer rejsu</th>
                    <th>status</th>
                </tr>
            </table>
        </section>
        <section id="status">
        </section>
        <section id="author">
            Autor: Bartosz Bryniarski
        </section>
    </body>
    </html>
    Podgląd strony po tym kroku (lotnisko.php)
  20. Etap 3: Styl CSS

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

    Krok 20: Plik styl5.css: pierwszy i trzeci blok banera

    ok. 1 min
    Polecenie z arkusza

    Cechy formatowania CSS: Dla pierwszego i trzeciego bloku banera: szerokość 25%, wysokość 200 px

    Nowy plik styl5.css w folderze zdającego - od teraz każda reguła trafia tylko tutaj, bez atrybutów style w HTML-u. Selektory identyfikatorów z krzyżykiem #, dwa rozdzielone przecinkiem dostają wspólną regułę. Wysokość 200 px to dokładnie wysokość logo.

    styl5.css CSS nowy plik
    #logo, #links {
        width: 25%;
        height: 200px;
    }
    Podgląd strony po tym kroku (lotnisko.php)
  21. Krok 21: Drugi blok banera: wyśrodkowanie, szerokość, wysokość, czcionka

    Kryteria:

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

    ok. 1 min
    Polecenie z arkusza

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

    Szerokości banera sumują się do 100% (25 + 50 + 25), a wszystkie trzy bloki mają po 200 px wysokości (R.4.4). font-size: 250% liczy się od czcionki rodzica, a nagłówek <h1> ma domyślnie 2em - jest więc dwa razy większy od tekstu bloku.

    styl5.css CSS zmienione: 6 linii
    #logo, #links {
        width: 25%;
        height: 200px;
    }
    #title {
        text-align: center;
        width: 50%;
        height: 200px;
        font-size: 250%;
    }
    Podgląd strony po tym kroku (lotnisko.php)
  22. Krok 22: Blok główny: tło, marginesy wewnętrzne, wielkie litery

    ok. 2 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)

    Kolor zapisany funkcją rgb(0, 0, 128) - granat (klucz przyjmuje też #000080). padding: 60px odsuwa tabelę od brzegów bloku ze wszystkich stron. text-transform: uppercase to „transformacja na wielkie litery”: w HTML-u i w bazie tekst zostaje bez zmian, a przeglądarka wyświetla go wielkimi literami - dlatego „Warszawa” z bazy wygląda na obrazie 2 jak WARSZAWA.

    Typowe błędy

    Przepisanie tekstu wielkimi literami w HTML-u nie spełnia wymagania - arkusz wprost żąda transformacji w CSS, a dane z bazy i tak przyszłyby małymi literami.

    styl5.css CSS zmienione: 5 linii
    #logo, #links {
        width: 25%;
        height: 200px;
    }
    #title {
        text-align: center;
        width: 50%;
        height: 200px;
        font-size: 250%;
    }
    #main {
        background-color: rgb(0, 0, 128);
        padding: 60px;
        text-transform: uppercase;
    }
    Podgląd strony po tym kroku (lotnisko.php)
  23. Krok 23: Pierwszy blok stopki: wyśrodkowanie i szerokość

    ok. 1 min
    Polecenie z arkusza

    Dla pierwszego bloku stopki: wyrównanie tekstu do środka, szerokość 50%

    Blok #status, w którym skrypt 2 wypisze komunikat o ciasteczku.

    styl5.css CSS zmienione: 4 linie
    #logo, #links {
        width: 25%;
        height: 200px;
    }
    #title {
        text-align: center;
        width: 50%;
        height: 200px;
        font-size: 250%;
    }
    #main {
        background-color: rgb(0, 0, 128);
        padding: 60px;
        text-transform: uppercase;
    }
    #status {
        text-align: center;
        width: 50%;
    }
    Podgląd strony po tym kroku (lotnisko.php)
  24. Krok 24: Drugi blok stopki: do prawej, szerokość, margines górny

    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 drugiego bloku stopki: wyrównanie tekstu do prawej strony, szerokość 50%, margines wewnętrzny górny: 100 px (tylko górny)

    „Tylko górny” to padding-top - właściwość jednej krawędzi. Napis „Autor” zjeżdża dzięki niemu na wysokość dołu paragrafu z lewego bloku, jak na obrazie 2. Wyrównania tekstu banera i obu bloków stopki są gotowe (R.4.6).

    Typowe błędy

    Skrót padding: 100px ustawiłby 100 px ze wszystkich czterech stron - blok miałby wtedy 50% + 200 px szerokości i nie zmieściłby się obok pierwszego.

    styl5.css CSS zmienione: 5 linii
    #logo, #links {
        width: 25%;
        height: 200px;
    }
    #title {
        text-align: center;
        width: 50%;
        height: 200px;
        font-size: 250%;
    }
    #main {
        background-color: rgb(0, 0, 128);
        padding: 60px;
        text-transform: uppercase;
    }
    #status {
        text-align: center;
        width: 50%;
    }
    #author {
        text-align: right;
        width: 50%;
        padding-top: 100px;
    }
    Podgląd strony po tym kroku (lotnisko.php)
  25. Krok 25: Selektor body: krój, tło, biały tekst

    Kryteria: , ,

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

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

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

    ok. 1 min
    Polecenie z arkusza

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

    Reguła body na początku pliku - dotyczy całej strony i dziedziczą ją wszystkie bloki. rgb(30, 144, 255) to jasny niebieski (nazwa CSS DodgerBlue, szesnastkowo #1E90FF), color: white - biały tekst. Tło bloku głównego (granat) już jest, więc R.4.2 ma oba kolory.

    styl5.css CSS zmienione: 5 linii
    body {
        font-family: Garamond;
        background-color: rgb(30, 144, 255);
        color: white;
    }
    #logo, #links {
        width: 25%;
        height: 200px;
    }
    #title {
        text-align: center;
        width: 50%;
        height: 200px;
        font-size: 250%;
    }
    #main {
        background-color: rgb(0, 0, 128);
        padding: 60px;
        text-transform: uppercase;
    }
    #status {
        text-align: center;
        width: 50%;
    }
    #author {
        text-align: right;
        width: 50%;
        padding-top: 100px;
    }
    Podgląd strony po tym kroku (lotnisko.php)
  26. Krok 26: Paragraf: marginesy, czcionka, obramowanie

    Kryteria:

    R.4.10 Ustawiono rozmiar czcionki 250% dla drugiego bloku banera, 200% dla selektora 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

    Selektor znacznika p - jedyny paragraf na stronie wypisze skrypt 2 w pierwszym bloku stopki. Skrót border: 1px solid rgb(192, 192, 192) zawiera grubość, rodzaj linii (solid = ciągła) i kolor - jasnoszary (#C0C0C0, nazwa silver). Rozmiary czcionek banera, paragrafu i wielkie litery bloku głównego są ustawione (R.4.10).

    styl5.css CSS zmienione: 5 linii
    body {
        font-family: Garamond;
        background-color: rgb(30, 144, 255);
        color: white;
    }
    #logo, #links {
        width: 25%;
        height: 200px;
    }
    #title {
        text-align: center;
        width: 50%;
        height: 200px;
        font-size: 250%;
    }
    #main {
        background-color: rgb(0, 0, 128);
        padding: 60px;
        text-transform: uppercase;
    }
    #status {
        text-align: center;
        width: 50%;
    }
    #author {
        text-align: right;
        width: 50%;
        padding-top: 100px;
    }
    p {
        padding: 20px;
        font-size: 200%;
        border: 1px solid rgb(192, 192, 192);
    }
    Podgląd strony po tym kroku (lotnisko.php)
  27. Krok 27: Paragraf po najechaniu kursorem

    Kryteria:

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

    ok. 1 min
    Polecenie z arkusza

    W momencie najechania kursorem na paragraf, jego kolor tła zmienia się na RGB 192, 192, 192

    Pseudoklasa :hover - reguła działa tylko wtedy, gdy kursor jest nad paragrafem. Po zjechaniu kursorem tło wraca do koloru strony.

    styl5.css CSS zmienione: 3 linie
    body {
        font-family: Garamond;
        background-color: rgb(30, 144, 255);
        color: white;
    }
    #logo, #links {
        width: 25%;
        height: 200px;
    }
    #title {
        text-align: center;
        width: 50%;
        height: 200px;
        font-size: 250%;
    }
    #main {
        background-color: rgb(0, 0, 128);
        padding: 60px;
        text-transform: uppercase;
    }
    #status {
        text-align: center;
        width: 50%;
    }
    #author {
        text-align: right;
        width: 50%;
        padding-top: 100px;
    }
    p {
        padding: 20px;
        font-size: 200%;
        border: 1px solid rgb(192, 192, 192);
    }
    p:hover {
        background-color: rgb(192, 192, 192);
    }
    Podgląd strony po tym kroku (lotnisko.php)
  28. Krok 28: Tabela: szerokość i połączone obramowanie

    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%

    ok. 1 min
    Polecenie z arkusza

    Dla selektora tabeli: szerokość 100%, obramowanie tabeli i komórki ma być połączone (collapse)

    Tabela zajmuje całą szerokość bloku głównego (wewnątrz marginesów 60 px). border-collapse: collapse sprawia, że sąsiednie komórki mają jedną wspólną linię zamiast dwóch z odstępem. Szerokości banera, stopki i tabeli są gotowe (R.4.5).

    styl5.css CSS zmienione: 4 linie
    body {
        font-family: Garamond;
        background-color: rgb(30, 144, 255);
        color: white;
    }
    #logo, #links {
        width: 25%;
        height: 200px;
    }
    #title {
        text-align: center;
        width: 50%;
        height: 200px;
        font-size: 250%;
    }
    #main {
        background-color: rgb(0, 0, 128);
        padding: 60px;
        text-transform: uppercase;
    }
    #status {
        text-align: center;
        width: 50%;
    }
    #author {
        text-align: right;
        width: 50%;
        padding-top: 100px;
    }
    p {
        padding: 20px;
        font-size: 200%;
        border: 1px solid rgb(192, 192, 192);
    }
    p:hover {
        background-color: rgb(192, 192, 192);
    }
    table {
        width: 100%;
        border-collapse: collapse;
    }
    Podgląd strony po tym kroku (lotnisko.php)
  29. Krok 29: Komórki tabeli: obramowanie i marginesy

    Kryteria: ,

    R.4.7 Ustawiono marginesy wewnętrzne (padding) na: 60px dla bloku głównego, 20px dla selektora paragrafu, 10px dla selektora td lub th oraz jedynie margines wewnętrzny górny (padding-top) 100px dla drugiego bloku stopki

    R.4.8 Ustawiono obramowanie selektora p oraz 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 komórki danych i komórki nagłówkowej tabeli: obramowanie 1 px, linią ciągłą o kolorze RGB 192, 192, 192; marginesy wewnętrzne 10 px

    Dwa selektory - td (komórka danych) i th (komórka nagłówkowa) - z jedną regułą. Obramowanie (R.4.8) i marginesy wewnętrzne wszystkich elementów (R.4.7) są ustawione.

    styl5.css CSS zmienione: 4 linie
    body {
        font-family: Garamond;
        background-color: rgb(30, 144, 255);
        color: white;
    }
    #logo, #links {
        width: 25%;
        height: 200px;
    }
    #title {
        text-align: center;
        width: 50%;
        height: 200px;
        font-size: 250%;
    }
    #main {
        background-color: rgb(0, 0, 128);
        padding: 60px;
        text-transform: uppercase;
    }
    #status {
        text-align: center;
        width: 50%;
    }
    #author {
        text-align: right;
        width: 50%;
        padding-top: 100px;
    }
    p {
        padding: 20px;
        font-size: 200%;
        border: 1px solid rgb(192, 192, 192);
    }
    p:hover {
        background-color: rgb(192, 192, 192);
    }
    table {
        width: 100%;
        border-collapse: collapse;
    }
    td, th {
        border: 1px solid rgb(192, 192, 192);
        padding: 10px;
    }
    Podgląd strony po tym kroku (lotnisko.php)
  30. Krok 30: Układ bloków zgodny z obrazem 2

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

    Pięć bloków o zadanej szerokości dostaje float: left: trzy bloki banera stają w jednym rzędzie (25% + 50% + 25%), a dwa bloki stopki - w drugim (50% + 50%). Blok główny nie pływa i ma clear: both - zaczyna się dopiero pod banerem i zajmuje całą szerokość, a bloki stopki ustawiają się pod nim.

    Klucz R.3.1 ocenia wygląd w przeglądarce, więc dowolny sposób daje punkt - wersja nowoczesna pokazuje ten sam układ na flex.

    Typowe błędy

    Bloki o sumie szerokości 100% nie mogą mieć bocznych marginesów, padding ani obramowań - każdy piksel więcej zepchnie ostatni blok rzędu niżej. Dlatego blok #author ma wyłącznie padding-top.

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

    Kroki 31-38 · po tym etapie masz 37 z 37 kryteriów (pkt)

    Krok 31: Skrypt 1: połączenie 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 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

    Skrypt 1 ma wypełnić tabelę, więc jego blok PHP stoi wewnątrz <table>, zaraz pod wierszem nagłówkowym - to, co wypisze, trafi dokładnie tam. Kod piszesz między <?php a ?>; serwer wykona go, zanim strona trafi do przeglądarki.

    Pierwsza instrukcja: mysqli_connect z tabeli 1 arkusza - kolejno serwer localhost, użytkownik root, puste hasło '' i nazwa bazy egzamin (połączenie i wybór bazy w jednej funkcji). Połączenie trafia do zmiennej $connection, z której skorzystają kolejne funkcje (R.5.1, R.5.2).

    Od tego kroku podgląd to wynik działania strony po stronie serwera (prawdziwe uruchomienie w PHP 8.2) - na razie wygląda jak wcześniej. Stronę sprawdzasz przez http://localhost/…, a nie otwierając plik z dysku: przeglądarka sama nie wykona PHP.

    lotnisko.php PHP zmienione: 3 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Port Lotniczy</title>
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <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">Pobierz…</a>
        </section>
        <section id="main">
            <table>
                <tr>
                    <th>czas</th>
                    <th>kierunek</th>
                    <th>numer rejsu</th>
                    <th>status</th>
                </tr>
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'egzamin');
    ?>
            </table>
        </section>
        <section id="status">
        </section>
        <section id="author">
            Autor: Bartosz Bryniarski
        </section>
    </body>
    </html>
    Podgląd strony po tym kroku (lotnisko.php)
  32. Krok 32: Skrypt 1: zapytanie 1

    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

    mysqli_query wysyła do bazy zapytanie 1 przepisane z kwerendy.txt (R.5.4). Wynik - zbiór ośmiu wierszy - trafia do zmiennej $result. Samo zapytanie niczego jeszcze nie wypisuje: wiersze trzeba z wyniku pobrać.

    lotnisko.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Port Lotniczy</title>
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <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">Pobierz…</a>
        </section>
        <section id="main">
            <table>
                <tr>
                    <th>czas</th>
                    <th>kierunek</th>
                    <th>numer rejsu</th>
                    <th>status</th>
                </tr>
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'egzamin');
    $result = mysqli_query($connection, 'SELECT czas, kierunek, nr_rejsu, status_lotu FROM przyloty ORDER BY czas;');
    ?>
            </table>
        </section>
        <section id="status">
        </section>
        <section id="author">
            Autor: Bartosz Bryniarski
        </section>
    </body>
    </html>

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

  33. Krok 33: Skrypt 1: pętla po wierszach i pierwsza komórka

    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

    • mysqli_fetch_row($result) pobiera kolejny wiersz wyniku jako tablicę numerowaną od zera: $row[0] to czas, $row[1] kierunek, $row[2] numer rejsu, $row[3] status - w kolejności pól z zapytania,
    • while powtarza to, dopóki funkcja zwraca wiersz; po ostatnim zwraca null i pętla się kończy,
    • echo wypisuje wiersz tabeli <tr> z pierwszą komórką <td>. Napis jest w cudzysłowie "…", więc PHP podstawia w nim wartość $row[0], a \n dokłada znak nowej linii (czytelne źródło strony).

    Podgląd: osiem wierszy z samym czasem, od 09:45:00 - kolejne kolumny dojdą w następnym kroku.

    lotnisko.php PHP zmienione: 3 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Port Lotniczy</title>
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <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">Pobierz…</a>
        </section>
        <section id="main">
            <table>
                <tr>
                    <th>czas</th>
                    <th>kierunek</th>
                    <th>numer rejsu</th>
                    <th>status</th>
                </tr>
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'egzamin');
    $result = mysqli_query($connection, 'SELECT czas, kierunek, nr_rejsu, status_lotu FROM przyloty ORDER BY czas;');
    while ($row = mysqli_fetch_row($result)) {
        echo "            <tr><td>$row[0]</td></tr>\n";
    }
    ?>
            </table>
        </section>
        <section id="status">
        </section>
        <section id="author">
            Autor: Bartosz Bryniarski
        </section>
    </body>
    </html>
    Podgląd strony po tym kroku (lotnisko.php)
  34. Krok 34: Skrypt 1: komplet komórek w wierszu

    Kryteria: ,

    R.2.7 W bloku głównym umieszczono tabelę o czterech kolumnach, zastosowano znaczniki <table>, <tr>, <td> i ich zamknięcia, tabela generowana 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. 2 min
    Polecenie z arkusza

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

    Do wiersza dochodzą trzy kolejne komórki: kierunek, numer rejsu i status. Każdy wiersz tabeli ma teraz cztery komórki <td> pod czterema nagłówkami (R.2.7), a dane stoją w odpowiednich kolumnach - jak na obrazie 2 (R.5.5). Puste statusy z bazy dają puste komórki.

    lotnisko.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Port Lotniczy</title>
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <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">Pobierz…</a>
        </section>
        <section id="main">
            <table>
                <tr>
                    <th>czas</th>
                    <th>kierunek</th>
                    <th>numer rejsu</th>
                    <th>status</th>
                </tr>
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'egzamin');
    $result = mysqli_query($connection, 'SELECT czas, kierunek, nr_rejsu, status_lotu FROM przyloty ORDER BY czas;');
    while ($row = mysqli_fetch_row($result)) {
        echo "            <tr><td>$row[0]</td><td>$row[1]</td><td>$row[2]</td><td>$row[3]</td></tr>\n";
    }
    ?>
            </table>
        </section>
        <section id="status">
        </section>
        <section id="author">
            Autor: Bartosz Bryniarski
        </section>
    </body>
    </html>
    Podgląd strony po tym kroku (lotnisko.php)
  35. Krok 35: Skrypt 1: zamknięcie połączenia

    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

    mysqli_close($connection) po pętli - wszystkie wiersze są już wypisane, więc połączenie nie jest potrzebne (R.5.3).

    Typowe błędy

    mysqli_close przed pętlą zamknie połączenie, zanim wiersze zostaną pobrane - mysqli_fetch_row zgłosi błąd.

    lotnisko.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Port Lotniczy</title>
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <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">Pobierz…</a>
        </section>
        <section id="main">
            <table>
                <tr>
                    <th>czas</th>
                    <th>kierunek</th>
                    <th>numer rejsu</th>
                    <th>status</th>
                </tr>
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'egzamin');
    $result = mysqli_query($connection, 'SELECT czas, kierunek, nr_rejsu, status_lotu FROM przyloty ORDER BY czas;');
    while ($row = mysqli_fetch_row($result)) {
        echo "            <tr><td>$row[0]</td><td>$row[1]</td><td>$row[2]</td><td>$row[3]</td></tr>\n";
    }
    mysqli_close($connection);
    ?>
            </table>
        </section>
        <section id="status">
        </section>
        <section id="author">
            Autor: Bartosz Bryniarski
        </section>
    </body>
    </html>

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

  36. Krok 36: Skrypt 2: 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, np. time() + 7200

    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 to mała porcja danych, którą serwer zapisuje w przeglądarce, a przeglądarka odsyła przy każdym kolejnym wejściu na stronę. W PHP przysłane ciasteczka są w tablicy $_COOKIE.

    • isset($_COOKIE['lotnisko']) - prawdziwe, gdy przeglądarka przysłała ciasteczko o nazwie lotnisko, czyli gość już tu był. Wykrzyknik ! odwraca warunek: blok wykona się przy pierwszej wizycie,
    • setcookie('lotnisko', 'odwiedzono', time() + 7200) - nazwa, wartość i czas wygaśnięcia. Funkcja time() zwraca bieżący czas w sekundach, a 2 godziny to 2 × 60 × 60 = 7200 sekund (R.5.7).

    Ten blok stoi na samej górze pliku, przed <!DOCTYPE html>. Opis funkcji na ostatniej stronie arkusza mówi wprost: ciasteczko jest nagłówkiem HTTP, więc trzeba je wysłać przed jakąkolwiek treścią strony. Komunikaty wypisze drugi blok skryptu, w stopce. Podgląd bez zmian.

    Typowe błędy

    time() + 2 albo samo 7200 (bez time()) to data z 1970 roku - ciasteczko wygaśnie od razu i każda wizyta będzie „pierwsza”.

    lotnisko.php PHP zmienione: 5 linii
    <?php
    if (!isset($_COOKIE['lotnisko'])) {
        setcookie('lotnisko', 'odwiedzono', time() + 7200);
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Port Lotniczy</title>
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <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">Pobierz…</a>
        </section>
        <section id="main">
            <table>
                <tr>
                    <th>czas</th>
                    <th>kierunek</th>
                    <th>numer rejsu</th>
                    <th>status</th>
                </tr>
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'egzamin');
    $result = mysqli_query($connection, 'SELECT czas, kierunek, nr_rejsu, status_lotu FROM przyloty ORDER BY czas;');
    while ($row = mysqli_fetch_row($result)) {
        echo "            <tr><td>$row[0]</td><td>$row[1]</td><td>$row[2]</td><td>$row[3]</td></tr>\n";
    }
    mysqli_close($connection);
    ?>
            </table>
        </section>
        <section id="status">
        </section>
        <section id="author">
            Autor: Bartosz Bryniarski
        </section>
    </body>
    </html>

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

  37. Krok 37: Skrypt 2: komunikat przy pierwszej wizycie

    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)

    ok. 2 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”

    W pierwszym bloku stopki drugi blok PHP z tym samym warunkiem: ciasteczka jeszcze nie ma, więc echo wypisuje komunikat w paragrafie <p> (R.2.5 - paragraf w pierwszym bloku stopki), a pogrubienie daje znacznik <b>.

    Ten sam warunek działa w obu blokach, bo setcookie nie zmienia tablicy $_COOKIE w trakcie bieżącego wykonania - ciasteczko wróci od przeglądarki dopiero przy następnym wejściu. Podgląd: pierwsza wizyta, „Dzień dobry! Strona lotniska używa ciasteczek” pogrubionym krojem.

    lotnisko.php PHP zmienione: 5 linii
    <?php
    if (!isset($_COOKIE['lotnisko'])) {
        setcookie('lotnisko', 'odwiedzono', time() + 7200);
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Port Lotniczy</title>
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <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">Pobierz…</a>
        </section>
        <section id="main">
            <table>
                <tr>
                    <th>czas</th>
                    <th>kierunek</th>
                    <th>numer rejsu</th>
                    <th>status</th>
                </tr>
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'egzamin');
    $result = mysqli_query($connection, 'SELECT czas, kierunek, nr_rejsu, status_lotu FROM przyloty ORDER BY czas;');
    while ($row = mysqli_fetch_row($result)) {
        echo "            <tr><td>$row[0]</td><td>$row[1]</td><td>$row[2]</td><td>$row[3]</td></tr>\n";
    }
    mysqli_close($connection);
    ?>
            </table>
        </section>
        <section id="status">
    <?php
    if (!isset($_COOKIE['lotnisko'])) {
        echo '<p><b>Dzień dobry! Strona lotniska używa ciasteczek</b></p>';
    }
    ?>
        </section>
        <section id="author">
            Autor: Bartosz Bryniarski
        </section>
    </body>
    </html>
    Podgląd strony po tym kroku (lotnisko.php)
  38. Krok 38: Skrypt 2: komunikat przy kolejnej wizycie

    Kryteria: , ,

    R.2.9 W pierwszym bloku stopki gdy wyświetlany jest tekst "Dzień dobry!..." czcionka jest pogrubiona lub gdy wyświetlony jest tekst "Witaj ponownie..." czcionka jest pochylona (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! Strona lotniska używa ciasteczek” w przeciwnym wypadku komunikat „Witaj ponownie na stronie lotniska"

    ok. 3 min
    Polecenie z arkusza

    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”

    Gałąź else - ciasteczko jest, czyli gość wrócił w ciągu 2 godzin. Komunikat w paragrafie, pochylony znacznikiem <i>. Skrypt korzysta z setcookie, isset i $_COOKIE (R.5.6), wypisuje oba komunikaty (R.5.8), a jeden z nich pogrubiony, drugi pochylony (R.2.9).

    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”. Podgląd pokazuje drugą wizytę, jak na obrazie 2. W przeglądarce test powtórzysz odświeżeniem strony (F5), a „pierwszą wizytę” - usuwając ciasteczko w narzędziach deweloperskich (F12, karta Aplikacja / Pamięć) albo w oknie prywatnym.

    Typowe błędy

    setcookie w stopce zamiast na górze pliku zadziała w XAMPP tylko dlatego, że PHP buforuje pierwsze 4 KB strony (output_buffering w php.ini) - przy wyłączonym buforze dostaniesz ostrzeżenie „Cannot modify header information - headers already sent” i ciasteczko się nie zapisze.

    lotnisko.php PHP zmienione: 2 linie
    <?php
    if (!isset($_COOKIE['lotnisko'])) {
        setcookie('lotnisko', 'odwiedzono', time() + 7200);
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Port Lotniczy</title>
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <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">Pobierz…</a>
        </section>
        <section id="main">
            <table>
                <tr>
                    <th>czas</th>
                    <th>kierunek</th>
                    <th>numer rejsu</th>
                    <th>status</th>
                </tr>
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'egzamin');
    $result = mysqli_query($connection, 'SELECT czas, kierunek, nr_rejsu, status_lotu FROM przyloty ORDER BY czas;');
    while ($row = mysqli_fetch_row($result)) {
        echo "            <tr><td>$row[0]</td><td>$row[1]</td><td>$row[2]</td><td>$row[3]</td></tr>\n";
    }
    mysqli_close($connection);
    ?>
            </table>
        </section>
        <section id="status">
    <?php
    if (!isset($_COOKIE['lotnisko'])) {
        echo '<p><b>Dzień dobry! Strona lotniska używa ciasteczek</b></p>';
    } else {
        echo '<p><i>Witaj ponownie na stronie lotniska</i></p>';
    }
    ?>
        </section>
        <section id="author">
            Autor: Bartosz Bryniarski
        </section>
    </body>
    </html>
    Podgląd strony po tym kroku (lotnisko.php)
  39. Etap 5: Dobre praktyki (poza kluczem)

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

    Krok 39: Zabezpieczenie: htmlspecialchars na danych z bazy

    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

    Skrypt wkleja do strony wszystko, co jest w bazie - także znaczniki HTML. Sprawdzone: status lotu LX3447 zmieniony na opoznienie <s>10</s> 20 min wersja z poprzednich kroków wyświetla jako „opoznienie ~~10~~ 20 min” z przekreśloną liczbą. Przekreślenie jest niegroźne, ale tak samo trafiłby na stronę znacznik <script> - kod JavaScript wykonany u każdego gościa tablicy przylotów (atak XSS). Dane w bazie wpisuje wiele osób i programów, więc traktujesz je jak dane od użytkownika.

    Lekarstwo: htmlspecialchars zamienia <, >, & i cudzysłowy na encje (&lt; itd.) - przeglądarka pokazuje je jako tekst. array_map przepuszcza przez tę funkcję wszystkie cztery komórki wiersza naraz. Podgląd: ten sam status widać dosłownie, ze znacznikami jako tekstem. Stara linia zostaje w komentarzu; na egzaminie klucz tego nie sprawdza.

    lotnisko.php PHP zmienione: 3 linie
    <?php
    if (!isset($_COOKIE['lotnisko'])) {
        setcookie('lotnisko', 'odwiedzono', time() + 7200);
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Port Lotniczy</title>
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <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">Pobierz…</a>
        </section>
        <section id="main">
            <table>
                <tr>
                    <th>czas</th>
                    <th>kierunek</th>
                    <th>numer rejsu</th>
                    <th>status</th>
                </tr>
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'egzamin');
    $result = mysqli_query($connection, 'SELECT czas, kierunek, nr_rejsu, status_lotu FROM przyloty ORDER BY czas;');
    while ($row = mysqli_fetch_row($result)) {
        // echo "            <tr><td>$row[0]</td><td>$row[1]</td><td>$row[2]</td><td>$row[3]</td></tr>\n";
        $cells = array_map('htmlspecialchars', $row);
        echo "            <tr><td>$cells[0]</td><td>$cells[1]</td><td>$cells[2]</td><td>$cells[3]</td></tr>\n";
    }
    mysqli_close($connection);
    ?>
            </table>
        </section>
        <section id="status">
    <?php
    if (!isset($_COOKIE['lotnisko'])) {
        echo '<p><b>Dzień dobry! Strona lotniska używa ciasteczek</b></p>';
    } else {
        echo '<p><i>Witaj ponownie na stronie lotniska</i></p>';
    }
    ?>
        </section>
        <section id="author">
            Autor: Bartosz Bryniarski
        </section>
    </body>
    </html>
    Podgląd strony po tym kroku (lotnisko.php)
  40. Krok 40: Zabezpieczenie: błąd bazy bez szczegółów serwera

    ok. 3 min
    Polecenie z arkusza

    Skrypt łączy się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie egzamin

    Od PHP 8.1 funkcje mysqli przy błędzie rzucają wyjątek. Sprawdzone: gdy bazy egzamin nie ma (np. na innym komputerze, bez importu), strona urywa się w połowie tabeli, a zamiast reszty pojawia się „Fatal error: Uncaught mysqli_sql_exception: Unknown database 'egzamin'” razem z pełną ścieżką do pliku na serwerze i numerem linii. Takie informacje pomagają włamywaczowi, a gościom strony nic nie mówią.

    Cały skrypt 1 trafia do bloku try, a catch (mysqli_sql_exception $exception) łapie błąd i wypisuje jeden wiersz tabeli z neutralnym komunikatem (colspan="4" - komórka na całą szerokość). Reszta strony - stopka i komunikat o ciasteczku - działa dalej. Podgląd: strona uruchomiona bez bazy. Na serwerze produkcyjnym dodatkowo wyłącza się display_errors, a błędy trafiają do dziennika.

    lotnisko.php PHP zmienione: 11 linii
    <?php
    if (!isset($_COOKIE['lotnisko'])) {
        setcookie('lotnisko', 'odwiedzono', time() + 7200);
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Port Lotniczy</title>
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <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">Pobierz…</a>
        </section>
        <section id="main">
            <table>
                <tr>
                    <th>czas</th>
                    <th>kierunek</th>
                    <th>numer rejsu</th>
                    <th>status</th>
                </tr>
    <?php
    try {
        $connection = mysqli_connect('localhost', 'root', '', 'egzamin');
        $result = mysqli_query($connection, 'SELECT czas, kierunek, nr_rejsu, status_lotu FROM przyloty ORDER BY czas;');
        while ($row = mysqli_fetch_row($result)) {
            // echo "            <tr><td>$row[0]</td><td>$row[1]</td><td>$row[2]</td><td>$row[3]</td></tr>\n";
            $cells = array_map('htmlspecialchars', $row);
            echo "            <tr><td>$cells[0]</td><td>$cells[1]</td><td>$cells[2]</td><td>$cells[3]</td></tr>\n";
        }
        mysqli_close($connection);
    } catch (mysqli_sql_exception $exception) {
        echo '            <tr><td colspan="4">Tablica przylotów jest chwilowo niedostępna</td></tr>' . "\n";
    }
    ?>
            </table>
        </section>
        <section id="status">
    <?php
    if (!isset($_COOKIE['lotnisko'])) {
        echo '<p><b>Dzień dobry! Strona lotniska używa ciasteczek</b></p>';
    } else {
        echo '<p><i>Witaj ponownie na stronie lotniska</i></p>';
    }
    ?>
        </section>
        <section id="author">
            Autor: Bartosz Bryniarski
        </section>
    </body>
    </html>
    Podgląd strony po tym kroku (lotnisko.php)
  41. Krok 41: Zabezpieczenie: flagi ciasteczka

    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

    Opis setcookie na ostatniej stronie arkusza wymienia parametr httponly: ciasteczko z tą flagą jest dostępne tylko dla serwera, a skrypty JavaScript na stronie go nie odczytają - wstrzyknięty skrypt (patrz dwa kroki wcześniej) nie wykradnie go. Od PHP 7.3 parametry podaje się czytelniej, jako tablicę opcji: expires (te same time() + 7200), httponly i samesite - Lax sprawia, że przeglądarka nie dołącza ciasteczka do żądań wysyłanych z obcych stron.

    Sprawdzone w zapisie ciasteczek przeglądarki testowej: przed zmianą zwykły wpis lotnisko, po zmianie wpis z oznaczeniem HttpOnly. Dwie zasady na przyszłość: w ciasteczku nie zapisuje się danych poufnych (arkusz też to mówi), a jego wartości nie wolno ufać - gość może ją dowolnie zmienić. Tu skrypt sprawdza tylko, czy ciasteczko istnieje, więc podmiana niczego nie psuje. Podgląd: druga wizyta, jak na obrazie 2.

    lotnisko.php PHP zmienione: 6 linii
    <?php
    if (!isset($_COOKIE['lotnisko'])) {
        // setcookie('lotnisko', 'odwiedzono', time() + 7200);
        setcookie('lotnisko', 'odwiedzono', [
            'expires' => time() + 7200,
            'httponly' => true,
            'samesite' => 'Lax',
        ]);
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Port Lotniczy</title>
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <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">Pobierz…</a>
        </section>
        <section id="main">
            <table>
                <tr>
                    <th>czas</th>
                    <th>kierunek</th>
                    <th>numer rejsu</th>
                    <th>status</th>
                </tr>
    <?php
    try {
        $connection = mysqli_connect('localhost', 'root', '', 'egzamin');
        $result = mysqli_query($connection, 'SELECT czas, kierunek, nr_rejsu, status_lotu FROM przyloty ORDER BY czas;');
        while ($row = mysqli_fetch_row($result)) {
            // echo "            <tr><td>$row[0]</td><td>$row[1]</td><td>$row[2]</td><td>$row[3]</td></tr>\n";
            $cells = array_map('htmlspecialchars', $row);
            echo "            <tr><td>$cells[0]</td><td>$cells[1]</td><td>$cells[2]</td><td>$cells[3]</td></tr>\n";
        }
        mysqli_close($connection);
    } catch (mysqli_sql_exception $exception) {
        echo '            <tr><td colspan="4">Tablica przylotów jest chwilowo niedostępna</td></tr>' . "\n";
    }
    ?>
            </table>
        </section>
        <section id="status">
    <?php
    if (!isset($_COOKIE['lotnisko'])) {
        echo '<p><b>Dzień dobry! Strona lotniska używa ciasteczek</b></p>';
    } else {
        echo '<p><i>Witaj ponownie na stronie lotniska</i></p>';
    }
    ?>
        </section>
        <section id="author">
            Autor: Bartosz Bryniarski
        </section>
    </body>
    </html>
    Podgląd strony po tym kroku (lotnisko.php)
  42. Etap 6: Oddanie pracy

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

    Krok 42: 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 wpisujesz nazwę przeglądarki, w której sprawdzasz stronę przez http://localhost/….

    W folderze zdającego muszą być: import.png, kw1.jpg, kw2.jpg, kw3.jpg, kw4.jpg, kwerendy.txt, lotnisko.php, przeglądarka.txt, styl5.css i przeskalowany zad5.png (plik egzamin.sql może zostać). Nagraj płytę, sprawdź jej odczyt i opisz ją numerem zdającego. Pusta lub nieczytelna płyta to zero punktów za wszystkie pięć rezultatów.

    przeglądarka.txt Tekst nowy plik
    Google Chrome

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

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