Przejdź do głównej treści
INF.03-08-26.01-SGStyczeń 2026 · 48 kroków · ok. 91 min · 40 kryteriów z klucza

Rozwiązanie krok po kroku: Portal korony gór polskich (Nowoczesny kod (grid, PDO))

To drugi sposób rozwiązania tego samego arkusza: portal korony gór polskich z bazą korona, logo z przezroczystym tłem, krojem Lato z @font-face i dwiema stronami - index.php z listą 28 szczytów i szczyty.php z opisem jednego szczytu. Obie wersje spełniają wszystkie 40 kryteriów klucza - różni je sposób pisania kodu, nie wynik.

Czym ta wersja różni się od podstawowej:

  • siatka CSS zamiast float + clear: kolumny 30% 70% na <body> dają naraz szerokości bloków nagłówkowych i stopek, a blok główny i sekcja rozciągają się na cały wiersz,
  • kolory w zmiennych CSS, font-display: swap w @font-face, pary rozmiarów zapisane przez :is(),
  • nazwy szczytów w bloku <nav> - numeracja :nth-child liczy tylko spany, niezależnie od reszty bloku głównego,
  • zapytanie 2 z ORDER BY id, zapytanie 3 z aliasami tabel,
  • PHP na PDO: w obu plikach dane pobierane na górze, połączenie zamknięte przed HTML-em, w HTML tylko pętle foreach,
  • identyfikator szczytu z adresu sprawdzony filter_var i wstawiony do zapytania przygotowanego, komunikat dla nieistniejącego szczytu, opis podzielony na akapity, wartości z bazy przez escape().

Kroki nadal idą zdanie po zdaniu za treścią arkusza. Podgląd w każdym kroku z PHP to wynik prawdziwego uruchomienia kodu z tego kroku w PHP 8.2 na bazie z arkusza; szczyty.php pokazuje Rysy, jak na ilustracji 5. Baza, zdjęcia, logo i czcionki pochodzą z prawdziwego archiwum pliki13 (załącznik do arkusza, hasło w treści zadania).

Jak liczą się punkty. Klucz ma 40 kryteriów (R.1.1 - R.5.10), każde za 1 punkt. Próg zdania części praktycznej to 75%, czyli 30 punktów. 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 40 kryteriów (pkt)

    Krok 1: Baza danych korona

    ok. 2 min
    Polecenie z arkusza

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

    Archiwum pliki13.7z ma hasło Gory^PoLskie! (daszek ^ w środku, wykrzyknik na końcu); w środku korona.sql, logo.png, zdjęcia gór i folder czcionka. W phpMyAdmin: Nowa, nazwa korona, porównywanie utf8_polish_ci, albo w zakładce SQL:

    CREATE DATABASE korona CHARACTER SET utf8 COLLATE utf8_polish_ci;
    
    Typowe błędy

    Skrypty łączą się z bazą po nazwie - literówka w korona zabiera także punkt za połączenie (R.5.1).

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

    ok. 2 min
    Polecenie z arkusza

    Z rozpakowanego archiwum zaimportuj tabele z pliku korona.sql do utworzonej bazy

    Zaznacz bazę korona, potem Import → korona.sql → Wykonaj. Powstają dwie tabele z ilustracji 1: szczyty (nazwa, wysokość, pasmo, lokator, plik zdjęcia) i opis - każdy opis wskazuje swój szczyt kluczem obcym szczyty_id.

  3. Krok 3: Zrzut ekranu import.png

    Kryteria:

    R.1.1 Wykonany import tabel do bazy danych korona - plik import.png

    ok. 2 min
    Polecenie z arkusza

    Wykonaj zrzut ekranu po imporcie. Zapisz zrzut w formacie PNG pod nazwą import. Nie kadruj zrzutu. Powinien on obejmować cały ekran monitora, z widocznym paskiem zadań. Na zrzucie powinny być widoczne elementy wskazujące na poprawnie wykonany import tabel

    Print Screen (cały ekran z paskiem zadań), wklej do edytora grafiki i zapisz jako import.png. Zrzuty zapytań w tym arkuszu są w formacie JPEG - tylko import idzie do PNG.

    Typowe błędy

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

  4. Krok 4: Zapytanie 1: szczyty od najwyższego

    Kryteria: ,

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

    R.1.3 Wykonane zapytanie 1: wybierające jedynie id oraz nazwy szczytów posortowane malejąco według ich wysokości. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT id, nazwa FROM szczyty ORDER BY wysokosc DESC; oraz na zrzucie widoczny jest komunikat o wybranych 28 rekordach oraz pierwsze trzy rekordy to 1 Rysy, 9 Babia Góra, 26 Śnieżka

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 1: wybierające jedynie id oraz nazwy wszystkich szczytów posortowane malejąco według ich wysokości

    • SELECT id, nazwa - dwa pola,
    • FROM szczyty,
    • ORDER BY wysokosc DESC - sortowanie po polu spoza listy SELECT jest dozwolone; DESC to kolejność malejąca.

    Wynik: 28 wierszy - 1 Rysy, 9 Babia Góra, 26 Śnieżka, …, Łysica. Każda klauzula w osobnej linii - SQL nie zwraca uwagi na znaki nowej linii, a zapytanie czyta się od góry. Zapisz w kwerendy.txt, zrzut jako kw1.jpg (z komunikatem o liczbie rekordów).

    kwerendy.txt Tekst nowy plik
    -- Zapytanie 1
    SELECT id, nazwa
    FROM szczyty
    ORDER BY wysokosc DESC;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 4
  5. Krok 5: Zapytanie 2: pierwsze 10 rekordów

    Kryteria:

    R.1.4 Wykonane zapytanie 2: wybierające jedynie nazwy plików i nazwy szczytów dla pierwszych 10 rekordów. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT plik, nazwa FROM szczyty LIMIT 10; oraz na zrzucie widocznych jest dokładnie 10 rekordów, ostatni to gora.jpg , Czupel

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 2: wybierające jedynie nazwy plików i nazwy szczytów dla pierwszych 10 rekordów

    LIMIT 10 obcina wynik do dziesięciu wierszy. Bez ORDER BY baza może oddać wiersze w dowolnej kolejności - w praktyce MariaDB podaje je według klucza głównego, ale to nie jest gwarancja. ORDER BY id mówi wprost, które rekordy są „pierwsze”: identyfikatory 1 - 10. Ostatni to gora.jpg, Czupel - kilka szczytów korzysta z tego samego zdjęcia zastępczego. Wynik ten sam co w wersji podstawowej. Zrzut: kw2.jpg.

    kwerendy.txt Tekst zmienione: 6 linii
    -- Zapytanie 1
    SELECT id, nazwa
    FROM szczyty
    ORDER BY wysokosc DESC;
    
    -- Zapytanie 2
    SELECT plik, nazwa
    FROM szczyty
    ORDER BY id
    LIMIT 10;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 5
  6. Krok 6: Zapytanie 3: szczyt o id 1 z opisem

    Kryteria:

    R.1.5 Wykonane zapytanie 3: wybierające jedynie nazwę pliku, nazwę szczytu, wysokość i pasmo oraz odpowiadający im opis dla szczytu o id równym 1. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT plik, nazwa, wysokosc, pasmo, opis FROM szczyty JOIN opis ON szczyty.id = szczyty_id WHERE szczyty.id = 1; (lub porównanie kluczy po WHERE. lub szczyty_id = 1 ) oraz na zrzucie widoczny jest dokładnie 1 rekord dla szczytu Rysy

    ok. 3 min
    Polecenie z arkusza

    Zapytanie 3: wybierające jedynie nazwę pliku, nazwę szczytu, wysokość i pasmo oraz odpowiadający im opis dla szczytu o id równym 1. Należy posłużyć się relacją

    • FROM szczyty AS s JOIN opis AS o - aliasy tabel; przy tabeli opis z polem opis alias szczególnie pomaga: o.opis zamiast opis.opis,
    • ON o.szczyty_id = s.id - relacja: opis wskazuje swój szczyt,
    • WHERE s.id = 1 - Rysy,
    • SELECT s.plik, s.nazwa, s.wysokosc, s.pasmo, o.opis - pięć pól, każde z prefiksem tabeli.

    Wynik: dokładnie jeden wiersz. phpMyAdmin skraca długi opis - pełny pokaże strona. Zrzut: kw3.jpg.

    kwerendy.txt Tekst zmienione: 6 linii
    -- Zapytanie 1
    SELECT id, nazwa
    FROM szczyty
    ORDER BY wysokosc DESC;
    
    -- Zapytanie 2
    SELECT plik, nazwa
    FROM szczyty
    ORDER BY id
    LIMIT 10;
    
    -- Zapytanie 3
    SELECT s.plik, s.nazwa, s.wysokosc, s.pasmo, o.opis
    FROM szczyty AS s
    JOIN opis AS o ON o.szczyty_id = s.id
    WHERE s.id = 1;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 6
  7. Krok 7: Zapytanie 4: szczyty w Beskidach

    Kryteria:

    R.1.6 Wykonane zapytanie 4: wybierające jedynie nazwy szczytów, wysokości oraz pasma gór, dla których nazwa pasma rozpoczyna się od „Beskid”. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa, wysokosc, pasmo FROM szczyty WHERE pasmo LIKE "Beskid%"; (Wielkość litery B nie ma znaczenia ) oraz na zrzucie widocznych jest dokładnie 7 rekordów dla: Beskid Niski, Beskid Sądecki, Beskid Wyspowy, Beskid Makowski, Beskid Żywiecki, Beskid Mały, Beskid Śląski

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 4: wybierające jedynie nazwy szczytów, wysokości oraz pasma gór w Beskidach (nazwa pasma rozpoczyna się od „Beskid”)

    „Rozpoczyna się od” to LIKE 'Beskid%' - % zastępuje dowolny ciąg znaków, ale tylko po słowie „Beskid”. Wynik: 7 wierszy - Beskid Niski, Sądecki, Wyspowy, Makowski, Żywiecki, Mały i Śląski. Zrzut: kw4.jpg.

    Typowe błędy

    LIKE '%Beskid%' szuka słowa w dowolnym miejscu nazwy - tu wynik byłby ten sam, ale zapytanie nie odpowiada poleceniu „rozpoczyna się od”.

    kwerendy.txt Tekst zmienione: 5 linii
    -- Zapytanie 1
    SELECT id, nazwa
    FROM szczyty
    ORDER BY wysokosc DESC;
    
    -- Zapytanie 2
    SELECT plik, nazwa
    FROM szczyty
    ORDER BY id
    LIMIT 10;
    
    -- Zapytanie 3
    SELECT s.plik, s.nazwa, s.wysokosc, s.pasmo, o.opis
    FROM szczyty AS s
    JOIN opis AS o ON o.szczyty_id = s.id
    WHERE s.id = 1;
    
    -- Zapytanie 4
    SELECT nazwa, wysokosc, pasmo
    FROM szczyty
    WHERE pasmo LIKE 'Beskid%';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 7
  8. Etap 2: Grafika i strona HTML

    Kroki 8-22 · po tym etapie masz 16 z 40 kryteriów (pkt)

    Krok 8: Logo: białe tło na przezroczystość

    Kryteria:

    R.2.2 Grafika ma przezroczyste tło (akceptowalne niedokładności pojedynczych pikseli )

    ok. 3 min
    Polecenie z arkusza

    Przygotowanie grafiki logo.png: Białe tło grafiki należy zastąpić przezroczystością (drobne niedokładności pojedynczych pikseli są akceptowalne)

    Grafika jest taka sama w obu wersjach. W GIMP-ie: Warstwa → Przezroczystość → Dodaj kanał alfa, potem Kolory → Kolor na alfę z kolorem białym. Biel znika w całym obrazie - także wewnątrz zboczy, a szare piksele wygładzenia dostają częściową przezroczystość, więc linie nie mają białej obwódki.

    Typowe błędy

    Różdżka zaznaczająca samo zewnętrzne tło zostawia białe wnętrza gór.

  9. Krok 9: Logo: kadr do szczytu po prawej i wysokość 100 px

    Kryteria:

    R.2.1 Grafika logo.png skadrowana, tak że widoczny jest jedynie szczyt po prawej stronie, a krawędzie grafiki przylegają do kształtu (widoczne na niepowiększonym obrazie ) oraz przeskalowana do wysokości 100 px

    ok. 3 min
    Polecenie z arkusza

    Skadrować grafikę tak, aby był widoczny jedynie szczyt po prawej stronie. Kadr powinien obejmować cały szczyt oraz krawędzie grafiki powinny przylegać do kształtu (ilustracja 3). Skadrowaną grafikę należy przeskalować z zachowaniem proporcji do wysokości 100 px

    Zaznacz prostokątem szczyt z prawego górnego rogu z zapasem, Obraz → Przytnij do zaznaczenia, potem Obraz → Przytnij do zawartości - krawędzie dosuną się do linii. Kadr 227 × 144 px. Obraz → Skaluj obraz z zamkniętym łańcuchem: wysokość 100 px, szerokość wychodzi 158 px. Eksport jako logo.png.

    Podgląd strony po tym kroku
    Podgląd strony po kroku 9
  10. Krok 10: Plik index.php w HTML5

    ok. 2 min
    Polecenie z arkusza

    Składa się ze stron index.php oraz szczyty.php. Cechy wspólne dla obu stron: Zapisane w języku HTML5

    Najpierw strona główna: <!DOCTYPE html> i szkielet. Część wspólna obu stron powstaje raz - szczyty.php będzie kopią gotowego szkieletu.

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

    ok. 1 min
    Polecenie z arkusza

    Zadeklarowany polski język zawartości witryny

    Atrybut lang="pl" w znaczniku <html>.

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

    ok. 1 min
    Polecenie z arkusza

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

    <meta charset="UTF-8"> jako pierwszy element <head> - przed pierwszym polskim znakiem w dokumencie.

    index.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 (index.php)
  13. Krok 13: Tytuł strony

    Kryteria:

    R.2.3 W pliku index.php lub szczyty.php , znajduje się deklaracja HTML5 <!DOCTYPE HTML>, deklaracja języka zawartości witryny: polski np. <html lang="pl">. W sekcji head znajduje się jawnie podany standard kodowania polskich znaków i tytuł strony „Korona gór polskich”

    ok. 1 min
    Polecenie z arkusza

    Tytuł strony „Korona gór polskich”

    <title> w <head>. Deklaracja HTML5, język, kodowanie i tytuł to razem jedno kryterium (R.2.3).

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

    Kryteria:

    R.3.2 Strona zawiera działające połączenie z zewnętrznym arkuszem stylów o nazwie styl.css formatowanie CSS pochodzi jedynie z tego arkusza (nie należy brać pod uwagę @font- face)

    ok. 1 min
    Polecenie z arkusza

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

    Plik styl.css (nie style.css). Całe formatowanie pochodzi z tego pliku.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Korona gór polskich</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  15. Krok 15: Bloki: dwa nagłówki, główny, sekcja, dwie stopki

    Kryteria:

    R.2.4 W sekcji body zdefiniowany jest podział na bloki za pomocą semantycznych znaczników HTML5: header+header, main, section, footer+footer (dopuszcza się zastosowanie bloków pomocniczych)

    ok. 2 min
    Polecenie z arkusza

    Podział strony na bloki zrealizowany za pomocą semantycznych znaczników bloków języka HTML5 tak, aby po uruchomieniu w przeglądarce układ bloków na stronie był zgodny z ilustracją 4

    Klucz wymaga wprost dwóch <header>, <main>, <section> i dwóch <footer> (R.2.4). Sekcja stoi poza blokiem głównym - na ilustracji 4 zajmuje osobny pas pod nim. Bloki nie dostają identyfikatorów: siatka CSS ustawi pierwszy z pary w kolumnie 30%, a drugi w kolumnie 70% według kolejności w kodzie.

    index.php PHP zmienione: 12 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Korona gór polskich</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
        </header>
        <header>
        </header>
        <main>
        </main>
        <section>
        </section>
        <footer>
        </footer>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  16. Krok 16: Blok nagłówkowy 1: logo

    Kryteria:

    R.2.6 W bloku nagłówkowym 1 znajduje się grafika z logo z tekstem alternatywnym "Logo"

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku nagłówkowego 1: Obraz logo.png z tekstem alternatywnym „Logo”

    <img> z atrybutem alt, bez ukośnika na końcu (HTML5).

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Korona gór polskich</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <img src="logo.png" alt="Logo">
        </header>
        <header>
        </header>
        <main>
        </main>
        <section>
        </section>
        <footer>
        </footer>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  17. Krok 17: Blok nagłówkowy 2: tytuł

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku nagłówkowego 2: Nagłówek pierwszego stopnia o treści: „Korona Gór Polskich”

    <h1> - tu „Gór Polskich” wielkimi literami, w tytule karty małymi, jak w arkuszu.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Korona gór polskich</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <img src="logo.png" alt="Logo">
        </header>
        <header>
            <h1>Korona Gór Polskich</h1>
        </header>
        <main>
        </main>
        <section>
        </section>
        <footer>
        </footer>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  18. Krok 18: Stopka 1: nagłówek „Kontakt”

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku stopki 1: Nagłówek trzeciego stopnia o treści: „Kontakt”

    <h3> w pierwszej stopce.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Korona gór polskich</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <img src="logo.png" alt="Logo">
        </header>
        <header>
            <h1>Korona Gór Polskich</h1>
        </header>
        <main>
        </main>
        <section>
        </section>
        <footer>
            <h3>Kontakt</h3>
        </footer>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  19. Krok 19: Stopka 1: lista z telefonem

    ok. 1 min
    Polecenie z arkusza

    Lista punktowana (nieuporządkowana) zawierająca dwa elementy: „Zadzwoń do nas: 111 222 333”

    Lista punktowana to <ul>. Numer telefonu jest odnośnikiem tel: - na telefonie dotknięcie od razu wybiera numer (w formacie międzynarodowym, bez spacji). Treść elementu zostaje taka jak w arkuszu.

    index.php PHP zmienione: 3 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Korona gór polskich</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <img src="logo.png" alt="Logo">
        </header>
        <header>
            <h1>Korona Gór Polskich</h1>
        </header>
        <main>
        </main>
        <section>
        </section>
        <footer>
            <h3>Kontakt</h3>
            <ul>
                <li>Zadzwoń do nas: <a href="tel:+48111222333">111 222 333</a></li>
            </ul>
        </footer>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  20. Krok 20: Stopka 1: odsyłacz pocztowy

    Kryteria: ,

    R.2.7 W bloku stopki 1 znajduje się jedna lista punktowana z dwoma elementami

    R.3.3 Odnośnik pocztowy "Napisz do nas" zdefiniowany jako mailto:[email protected]

    ok. 1 min
    Polecenie z arkusza

    „Napisz do nas”, element jest odsyłaczem pocztowym do adresu „[email protected]”

    Odsyłacz pocztowy: adres zaczyna się od mailto:, kliknięcie otwiera program pocztowy z wpisanym odbiorcą. Lista ma dwa elementy (R.2.7).

    Typowe błędy

    Sam adres w href (bez mailto:) przeglądarka potraktuje jak plik o nazwie [email protected].

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Korona gór polskich</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <img src="logo.png" alt="Logo">
        </header>
        <header>
            <h1>Korona Gór Polskich</h1>
        </header>
        <main>
        </main>
        <section>
        </section>
        <footer>
            <h3>Kontakt</h3>
            <ul>
                <li>Zadzwoń do nas: <a href="tel:+48111222333">111 222 333</a></li>
                <li><a href="mailto:[email protected]">Napisz do nas</a></li>
            </ul>
        </footer>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  21. Krok 21: Stopka 2: znak © i autor

    Kryteria: ,

    R.2.5 W bloku nagłówkowym 2 znajduje się <h1>: „Korona Gór Polskich", w blokach stopki <h3> "Kontakt" oraz <h3> z numerem zdającego

    R.2.8 W bloku stopki 2 znajduje się znak copyright ©

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku stopki 2: Nagłówek trzeciego stopnia o treści „© Wykonane przez: ”, dalej wstawiony numer zdającego

    Znak © to encja &copy; (tabela 3 arkusza). Plik jest w UTF-8, więc działa też sam znak „©” wpisany z klawiatury - encja jest pewniejsza, gdy edytor zapisze plik w innym kodowaniu. Na egzaminie po dwukropku wpisujesz swój numer zdającego (PESEL) - tu stoi imię i nazwisko autora rozwiązania. Komplet nagłówków z klucza (R.2.5).

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Korona gór polskich</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <img src="logo.png" alt="Logo">
        </header>
        <header>
            <h1>Korona Gór Polskich</h1>
        </header>
        <main>
        </main>
        <section>
        </section>
        <footer>
            <h3>Kontakt</h3>
            <ul>
                <li>Zadzwoń do nas: <a href="tel:+48111222333">111 222 333</a></li>
                <li><a href="mailto:[email protected]">Napisz do nas</a></li>
            </ul>
        </footer>
        <footer>
            <h3>&copy; Wykonane przez: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  22. Krok 22: Plik szczyty.php: kopia wspólnego szkieletu

    ok. 1 min
    Polecenie z arkusza

    Cechy bloku głównego dla strony index.php: Efekt działania skryptu 1. Cechy bloku głównego dla strony szczyty.php: Efekt działania skryptu 3

    Obie strony mają te same bloki, nagłówki i stopki - różnią się zawartością bloku głównego. Gotowy plik zapisz jeszcze raz jako szczyty.php. Blok główny wypełni skrypt 1 na stronie głównej i skrypt 3 na stronie szczytu.

    szczyty.php PHP nowy plik
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Korona gór polskich</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <img src="logo.png" alt="Logo">
        </header>
        <header>
            <h1>Korona Gór Polskich</h1>
        </header>
        <main>
        </main>
        <section>
        </section>
        <footer>
            <h3>Kontakt</h3>
            <ul>
                <li>Zadzwoń do nas: <a href="tel:+48111222333">111 222 333</a></li>
                <li><a href="mailto:[email protected]">Napisz do nas</a></li>
            </ul>
        </footer>
        <footer>
            <h3>&copy; Wykonane przez: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>

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

  23. Etap 3: Styl CSS

    Kroki 23-33 · po tym etapie masz 27 z 40 kryteriów (pkt)

    Krok 23: Krój Lato: reguły @font-face

    Kryteria:

    R.4.10 czcionka Lato: w stylach zdefiniowana przynajmniej raz dyrektywa @font-face z rzeczywistymi ścieżkami URL

    ok. 3 min
    Polecenie z arkusza

    Styl CSS korzysta z kroju czcionki Lato znajdującej się w folderze czcionka z rozpakowanego archiwum. Czcionkę należy dodać do projektu i zdefiniować jej stosowanie dla wariantu regular i bold. W pliku styl.css należy dodać reguły dla czcionki. Przykład umieszczono w pliku przyklad.css

    Folder czcionka z archiwum trafia do folderu z numerem zdającego. Dwie reguły @font-face pod tą samą nazwą 'Lato' - zwykła i pogrubiona - ze ścieżkami do podfolderu czcionka/ (R.4.10). Różnice wobec przyklad.css:

    • grubości liczbami: 400 to normal, 700 to bold - ta sama skala, której używają nagłówki,
    • font-display: swap - zanim plik kroju się pobierze, tekst wyświetla się krojem zapasowym, zamiast być niewidoczny; potem przeglądarka podmienia krój.

    woff2 stoi pierwszy, bo jest mniejszy; woff to zapas dla starszych przeglądarek.

    Typowe błędy

    Ścieżka z przykładu bez folderu czcionka/ nie znajdzie pliku - strona ładuje się krojem zastępczym. Sprawdź w narzędziach przeglądarki, zakładka Sieć.

    styl.css CSS nowy plik
    @font-face {
        font-family: 'Lato';
        src: url('czcionka/lato-regular.woff2') format('woff2'),
             url('czcionka/lato-regular.woff') format('woff');
        font-weight: 400;
        font-style: normal;
        font-display: swap;
    }
    
    @font-face {
        font-family: 'Lato';
        src: url('czcionka/lato-bold.woff2') format('woff2'),
             url('czcionka/lato-bold.woff') format('woff');
        font-weight: 700;
        font-style: normal;
        font-display: swap;
    }
    Podgląd strony po tym kroku (index.php)
  24. Krok 24: Zmienne kolorów, krój, wyrównanie i kolor dla wszystkich

    Kryteria:

    R.4.1 domyślne formatowanie selektorów (*): krój czcionki Lato, sans-serif (dozwolona tylko taka kolejność) , wyrównanie tekstu do środka oraz kolor czcionki #333

    ok. 2 min
    Polecenie z arkusza

    Domyślnie dla wszystkich selektorów: krój czcionki Lato, w przypadku braku sans-serif, wyrównanie tekstu do środka, kolor czcionki #333

    Kolory z arkusza stoją w zmiennych CSS w :root - w jednym miejscu na górze pliku, z nazwami mówiącymi, do czego służą. Reguła *: Lato, sans-serif w tej kolejności (klucz innej nie uznaje), środkowanie i kolor var(--text) (R.4.1).

    styl.css CSS zmienione: 11 linii
    @font-face {
        font-family: 'Lato';
        src: url('czcionka/lato-regular.woff2') format('woff2'),
             url('czcionka/lato-regular.woff') format('woff');
        font-weight: 400;
        font-style: normal;
        font-display: swap;
    }
    
    @font-face {
        font-family: 'Lato';
        src: url('czcionka/lato-bold.woff2') format('woff2'),
             url('czcionka/lato-bold.woff') format('woff');
        font-weight: 700;
        font-style: normal;
        font-display: swap;
    }
    
    :root {
        --text: #333;
        --main-background: #E0F2F1;
    }
    
    * {
        font-family: Lato, sans-serif;
        text-align: center;
        color: var(--text);
    }
    Podgląd strony po tym kroku (index.php)
  25. Krok 25: Bloki nagłówkowe i stopki: wysokość

    ok. 1 min
    Polecenie z arkusza

    Dla bloków nagłówkowych i stopek: wysokość 100 px

    Jedna reguła dla czterech bloków. Logo ma dokładnie 100 px wysokości.

    styl.css CSS zmienione: 5 linii
    @font-face {
        font-family: 'Lato';
        src: url('czcionka/lato-regular.woff2') format('woff2'),
             url('czcionka/lato-regular.woff') format('woff');
        font-weight: 400;
        font-style: normal;
        font-display: swap;
    }
    
    @font-face {
        font-family: 'Lato';
        src: url('czcionka/lato-bold.woff2') format('woff2'),
             url('czcionka/lato-bold.woff') format('woff');
        font-weight: 700;
        font-style: normal;
        font-display: swap;
    }
    
    :root {
        --text: #333;
        --main-background: #E0F2F1;
    }
    
    * {
        font-family: Lato, sans-serif;
        text-align: center;
        color: var(--text);
    }
    
    header,
    footer {
        height: 100px;
    }
    Podgląd strony po tym kroku (index.php)
  26. Krok 26: Szerokości 30% i 70% z kolumn siatki

    Kryteria:

    R.4.5 szerokość: blok nagłówkowy 1, stopka 1: 30%, blok nagłówkowy 2, stopka 2: 70% (width lub grid-template-columns lub flex )

    ok. 2 min
    Polecenie z arkusza

    Dodatkowo dla bloku nagłówkowego 1 i bloku stopki 1: szerokość 30%. Dodatkowo dla bloku nagłówkowego 2 i bloku stopki 2: szerokość 70%

    <body> staje się siatką o dwóch kolumnach: grid-template-columns: 30% 70%. Bloki układają się w komórkach po kolei, więc pierwszy nagłówek i pierwsza stopka trafiają do kolumny 30%, a drugie z pary - do kolumny 70%. Jedna linia daje oba zdania arkusza naraz; klucz dopuszcza szerokości z grid-template-columns (R.4.5).

    Blok główny i sekcja wpadają na razie do pojedynczych komórek - rozciągnie je następny krok.

    styl.css CSS zmienione: 5 linii
    @font-face {
        font-family: 'Lato';
        src: url('czcionka/lato-regular.woff2') format('woff2'),
             url('czcionka/lato-regular.woff') format('woff');
        font-weight: 400;
        font-style: normal;
        font-display: swap;
    }
    
    @font-face {
        font-family: 'Lato';
        src: url('czcionka/lato-bold.woff2') format('woff2'),
             url('czcionka/lato-bold.woff') format('woff');
        font-weight: 700;
        font-style: normal;
        font-display: swap;
    }
    
    :root {
        --text: #333;
        --main-background: #E0F2F1;
    }
    
    * {
        font-family: Lato, sans-serif;
        text-align: center;
        color: var(--text);
    }
    
    header,
    footer {
        height: 100px;
    }
    
    body {
        display: grid;
        grid-template-columns: 30% 70%;
    }
    Podgląd strony po tym kroku (index.php)
  27. Krok 27: Układ bloków zgodny z ilustracją 4

    Kryteria:

    R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z ilustracją 4 w arkuszu egzaminacyjnym (prawidłowo zastosowano właściwości CSS decydujące o układzie bloków na stronie, np. float i clear albo display flex albo grid albo position)

    ok. 2 min
    Polecenie z arkusza

    Podział strony na bloki zrealizowany za pomocą semantycznych znaczników bloków języka HTML5 tak, aby po uruchomieniu w przeglądarce układ bloków na stronie był zgodny z ilustracją 4

    grid-column: 1 / -1 rozciąga blok główny i sekcję od pierwszej do ostatniej linii siatki - każde zajmuje cały wiersz. Stopki wracają do pary 30% / 70% w ostatnim wierszu, jak na ilustracji 4 (R.3.1). Bez float nie trzeba clear ani pilnować, czy bloki nie zachodzą na siebie.

    Typowe błędy

    Bez tej reguły blok główny zostaje w kolumnie 30%, a sekcja w kolumnie 70% - układ nie zgadza się z ilustracją.

    styl.css CSS zmienione: 5 linii
    @font-face {
        font-family: 'Lato';
        src: url('czcionka/lato-regular.woff2') format('woff2'),
             url('czcionka/lato-regular.woff') format('woff');
        font-weight: 400;
        font-style: normal;
        font-display: swap;
    }
    
    @font-face {
        font-family: 'Lato';
        src: url('czcionka/lato-bold.woff2') format('woff2'),
             url('czcionka/lato-bold.woff') format('woff');
        font-weight: 700;
        font-style: normal;
        font-display: swap;
    }
    
    :root {
        --text: #333;
        --main-background: #E0F2F1;
    }
    
    * {
        font-family: Lato, sans-serif;
        text-align: center;
        color: var(--text);
    }
    
    header,
    footer {
        height: 100px;
    }
    
    body {
        display: grid;
        grid-template-columns: 30% 70%;
    }
    
    main,
    section {
        grid-column: 1 / -1;
    }
    Podgląd strony po tym kroku (index.php)
  28. Krok 28: Blok główny: tło i marginesy wewnętrzne

    Kryteria:

    R.4.2 kolor tła: blok główny - #E0F2F1

    ok. 1 min
    Polecenie z arkusza

    Dla bloku głównego: kolor tła #E0F2F1, marginesy wewnętrzne 100 px

    Marginesy wewnętrzne to padding - tło sięga do krawędzi bloku, treść zaczyna się 100 px od nich (R.4.2).

    styl.css CSS zmienione: 5 linii
    @font-face {
        font-family: 'Lato';
        src: url('czcionka/lato-regular.woff2') format('woff2'),
             url('czcionka/lato-regular.woff') format('woff');
        font-weight: 400;
        font-style: normal;
        font-display: swap;
    }
    
    @font-face {
        font-family: 'Lato';
        src: url('czcionka/lato-bold.woff2') format('woff2'),
             url('czcionka/lato-bold.woff') format('woff');
        font-weight: 700;
        font-style: normal;
        font-display: swap;
    }
    
    :root {
        --text: #333;
        --main-background: #E0F2F1;
    }
    
    * {
        font-family: Lato, sans-serif;
        text-align: center;
        color: var(--text);
    }
    
    header,
    footer {
        height: 100px;
    }
    
    body {
        display: grid;
        grid-template-columns: 30% 70%;
    }
    
    main,
    section {
        grid-column: 1 / -1;
    }
    
    main {
        background-color: var(--main-background);
        padding: 100px;
    }
    Podgląd strony po tym kroku (index.php)
  29. Krok 29: Blok sekcji: wysokość i ukrywanie nadmiaru

    Kryteria: ,

    R.4.3 wysokość: bloki nagłówkowe, stopki 100px, blok sekcji 90px

    R.4.6 przepełnienie bloków: blok sekcji - overfow: hidden

    ok. 1 min
    Polecenie z arkusza

    Dla bloku sekcji: wysokość 90 px, w przypadku przepełnienia bloku zawartość, która się w nim nie mieści jest ukryta

    overflow: hidden obcina to, co wystaje poza 90 px - z dziesięciu miniatur widać pierwszy rząd, jak na ilustracji 2. Komplet wysokości (R.4.3). Druga reguła section w tym samym pliku nie przeszkadza - przeglądarka łączy właściwości obu.

    styl.css CSS zmienione: 5 linii
    @font-face {
        font-family: 'Lato';
        src: url('czcionka/lato-regular.woff2') format('woff2'),
             url('czcionka/lato-regular.woff') format('woff');
        font-weight: 400;
        font-style: normal;
        font-display: swap;
    }
    
    @font-face {
        font-family: 'Lato';
        src: url('czcionka/lato-bold.woff2') format('woff2'),
             url('czcionka/lato-bold.woff') format('woff');
        font-weight: 700;
        font-style: normal;
        font-display: swap;
    }
    
    :root {
        --text: #333;
        --main-background: #E0F2F1;
    }
    
    * {
        font-family: Lato, sans-serif;
        text-align: center;
        color: var(--text);
    }
    
    header,
    footer {
        height: 100px;
    }
    
    body {
        display: grid;
        grid-template-columns: 30% 70%;
    }
    
    main,
    section {
        grid-column: 1 / -1;
    }
    
    main {
        background-color: var(--main-background);
        padding: 100px;
    }
    
    section {
        height: 90px;
        overflow: hidden;
    }
    Podgląd strony po tym kroku (index.php)
  30. Krok 30: Klasa miniatury

    Kryteria:

    R.4.4 styl klasy .miniatury : wysokość 90px

    ok. 1 min
    Polecenie z arkusza

    Dla klasy miniatury przypisanej do obrazów generowanych skryptem 2: wysokość 90 px

    Selektor klasy .miniatury - nazwę narzuca klucz, więc zostaje po polsku. Szerokość wynika z proporcji zdjęcia.

    styl.css CSS zmienione: 4 linie
    @font-face {
        font-family: 'Lato';
        src: url('czcionka/lato-regular.woff2') format('woff2'),
             url('czcionka/lato-regular.woff') format('woff');
        font-weight: 400;
        font-style: normal;
        font-display: swap;
    }
    
    @font-face {
        font-family: 'Lato';
        src: url('czcionka/lato-bold.woff2') format('woff2'),
             url('czcionka/lato-bold.woff') format('woff');
        font-weight: 700;
        font-style: normal;
        font-display: swap;
    }
    
    :root {
        --text: #333;
        --main-background: #E0F2F1;
    }
    
    * {
        font-family: Lato, sans-serif;
        text-align: center;
        color: var(--text);
    }
    
    header,
    footer {
        height: 100px;
    }
    
    body {
        display: grid;
        grid-template-columns: 30% 70%;
    }
    
    main,
    section {
        grid-column: 1 / -1;
    }
    
    main {
        background-color: var(--main-background);
        padding: 100px;
    }
    
    section {
        height: 90px;
        overflow: hidden;
    }
    
    .miniatury {
        height: 90px;
    }
    Podgląd strony po tym kroku (index.php)
  31. Krok 31: Elementy listy do lewej

    Kryteria:

    R.4.8 wyrównanie tekstu: selektor li - do lewej strony

    ok. 1 min
    Polecenie z arkusza

    Dla selektora elementu listy: wyrównanie tekstu do lewej strony

    Selektor znacznika li (uwaga arkusza) nadpisuje środkowanie z *.

    styl.css CSS zmienione: 4 linie
    @font-face {
        font-family: 'Lato';
        src: url('czcionka/lato-regular.woff2') format('woff2'),
             url('czcionka/lato-regular.woff') format('woff');
        font-weight: 400;
        font-style: normal;
        font-display: swap;
    }
    
    @font-face {
        font-family: 'Lato';
        src: url('czcionka/lato-bold.woff2') format('woff2'),
             url('czcionka/lato-bold.woff') format('woff');
        font-weight: 700;
        font-style: normal;
        font-display: swap;
    }
    
    :root {
        --text: #333;
        --main-background: #E0F2F1;
    }
    
    * {
        font-family: Lato, sans-serif;
        text-align: center;
        color: var(--text);
    }
    
    header,
    footer {
        height: 100px;
    }
    
    body {
        display: grid;
        grid-template-columns: 30% 70%;
    }
    
    main,
    section {
        grid-column: 1 / -1;
    }
    
    main {
        background-color: var(--main-background);
        padding: 100px;
    }
    
    section {
        height: 90px;
        overflow: hidden;
    }
    
    .miniatury {
        height: 90px;
    }
    
    li {
        text-align: left;
    }
    Podgląd strony po tym kroku (index.php)
  32. Krok 32: Odstęp przed nazwami szczytów

    Kryteria:

    R.4.7 marginesy wewnętrzne (padding ): blok główny 100px, selektor span padding-left: 30px

    ok. 1 min
    Polecenie z arkusza

    Dla selektora span: margines wewnętrzny lewy 30 px

    Margines wewnętrzny lewy to padding-left (R.4.7).

    styl.css CSS zmienione: 4 linie
    @font-face {
        font-family: 'Lato';
        src: url('czcionka/lato-regular.woff2') format('woff2'),
             url('czcionka/lato-regular.woff') format('woff');
        font-weight: 400;
        font-style: normal;
        font-display: swap;
    }
    
    @font-face {
        font-family: 'Lato';
        src: url('czcionka/lato-bold.woff2') format('woff2'),
             url('czcionka/lato-bold.woff') format('woff');
        font-weight: 700;
        font-style: normal;
        font-display: swap;
    }
    
    :root {
        --text: #333;
        --main-background: #E0F2F1;
    }
    
    * {
        font-family: Lato, sans-serif;
        text-align: center;
        color: var(--text);
    }
    
    header,
    footer {
        height: 100px;
    }
    
    body {
        display: grid;
        grid-template-columns: 30% 70%;
    }
    
    main,
    section {
        grid-column: 1 / -1;
    }
    
    main {
        background-color: var(--main-background);
        padding: 100px;
    }
    
    section {
        height: 90px;
        overflow: hidden;
    }
    
    .miniatury {
        height: 90px;
    }
    
    li {
        text-align: left;
    }
    
    span {
        padding-left: 30px;
    }
    Podgląd strony po tym kroku (index.php)
  33. Krok 33: Rozmiary nazw: :nth-child w :is()

    Kryteria:

    R.4.9 rozmiar czcionki dla pseudoelementu nth-child: Babia Góra(2), Rudawiec(11) - 200%, Śnieżka(3), Lackowa(15) - 150%, Śnieżnik(4), Jagodna(17) - 120%, Tarnica(5), Szczeliniec Wielki(21) - 170%, Radziejowa(7), Ślęża(27) - 130%, Mogielica(9), Kłodzka Góra(25) - 160% (wystarczy dla 4 z 6 par)

    ok. 3 min
    Polecenie z arkusza

    Styl dla kolejnych potomków selektora span: dla 2 i 11: rozmiar czcionki 200%, dla 3 i 15: rozmiar czcionki 150%, dla 4 i 17: rozmiar czcionki 120%, dla 5 i 21: rozmiar czcionki 170%, dla 7 i 27: rozmiar czcionki 130%, dla 9 i 25: rozmiar czcionki 160%

    :nth-child(n) wybiera element, który jest n-tym dzieckiem swojego rodzica (tabela 4 arkusza). Para o tym samym rozmiarze to jedna reguła z pseudoklasą :is(): span:is(:nth-child(2), :nth-child(11)) znaczy „span, który jest drugim albo jedenastym dzieckiem”. Działa jak lista span:nth-child(2), span:nth-child(11), ale span pisze się raz (R.4.9).

    Spany wypisze skrypt 1 do bloku <nav> - poza nimi nic tam nie będzie, więc drugi span to drugie dziecko.

    Typowe błędy

    Klucz nazywa :nth-child pseudoelementem, ale to pseudoklasa (jeden dwukropek); pseudoelementy, jak ::first-letter, mają dwa.

    styl.css CSS zmienione: 24 linie
    @font-face {
        font-family: 'Lato';
        src: url('czcionka/lato-regular.woff2') format('woff2'),
             url('czcionka/lato-regular.woff') format('woff');
        font-weight: 400;
        font-style: normal;
        font-display: swap;
    }
    
    @font-face {
        font-family: 'Lato';
        src: url('czcionka/lato-bold.woff2') format('woff2'),
             url('czcionka/lato-bold.woff') format('woff');
        font-weight: 700;
        font-style: normal;
        font-display: swap;
    }
    
    :root {
        --text: #333;
        --main-background: #E0F2F1;
    }
    
    * {
        font-family: Lato, sans-serif;
        text-align: center;
        color: var(--text);
    }
    
    header,
    footer {
        height: 100px;
    }
    
    body {
        display: grid;
        grid-template-columns: 30% 70%;
    }
    
    main,
    section {
        grid-column: 1 / -1;
    }
    
    main {
        background-color: var(--main-background);
        padding: 100px;
    }
    
    section {
        height: 90px;
        overflow: hidden;
    }
    
    .miniatury {
        height: 90px;
    }
    
    li {
        text-align: left;
    }
    
    span {
        padding-left: 30px;
    }
    
    span:is(:nth-child(2), :nth-child(11)) {
        font-size: 200%;
    }
    
    span:is(:nth-child(3), :nth-child(15)) {
        font-size: 150%;
    }
    
    span:is(:nth-child(4), :nth-child(17)) {
        font-size: 120%;
    }
    
    span:is(:nth-child(5), :nth-child(21)) {
        font-size: 170%;
    }
    
    span:is(:nth-child(7), :nth-child(27)) {
        font-size: 130%;
    }
    
    span:is(:nth-child(9), :nth-child(25)) {
        font-size: 160%;
    }
    Podgląd strony po tym kroku (index.php)
  34. Etap 4: Skrypty PHP

    Kroki 34-45 · po tym etapie masz 40 z 40 kryteriów (pkt)

    Krok 34: index.php: połączenie PDO na górze pliku

    Kryteria:

    R.5.1 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy korona oraz ostatnią operacją na bazie jest jej zamknięcie

    ok. 4 min
    Polecenie z arkusza

    Napisane w języku PHP. Należy stosować znaczące nazewnictwo zmiennych w języku polskim lub angielskim. Łączą się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie korona. Na końcu skrypt zamyka połączenie z bazą

    Praca z bazą stoi przed <!DOCTYPE html>: najpierw dane, potem HTML, który je tylko wypisuje. Połączenie przez PDO (klucz R.5 przyjmuje MySQLi i PDO):

    • DSN 'mysql:host=localhost;dbname=korona;charset=utf8mb4' - serwer, baza i kodowanie; 'root', '' - użytkownik i puste hasło,
    • ERRMODE_EXCEPTION - błąd bazy rzuca wyjątek, FETCH_ASSOC - wiersze jako tablice z nazwami kolumn,
    • $pdo = null zamyka połączenie - zapytania trafią przed tę linię, więc zamknięcie zostanie ostatnią operacją na bazie (R.5.1),
    • try / catch - gdy baza nie działa, szczegóły idą do dziennika serwera (error_log), a w bloku głównym stoi ogólny komunikat z $dbError,
    • funkcja escape() to krótka nazwa htmlspecialchars(..., ENT_QUOTES) - posłuży każdej wartości z bazy.

    Zmienne $peaks, $thumbnails i $dbError mają wartości początkowe, żeby HTML działał także po błędzie bazy.

    index.php PHP zmienione: 24 linie
    <?php
    function escape(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES);
    }
    
    $peaks = [];
    $thumbnails = [];
    $dbError = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=korona;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać danych o szczytach. Spróbuj ponownie za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Korona gór polskich</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <img src="logo.png" alt="Logo">
        </header>
        <header>
            <h1>Korona Gór Polskich</h1>
        </header>
        <main>
    <?php if ($dbError !== ''): ?>
            <p><?= $dbError ?></p>
    <?php endif; ?>
        </main>
        <section>
        </section>
        <footer>
            <h3>Kontakt</h3>
            <ul>
                <li>Zadzwoń do nas: <a href="tel:+48111222333">111 222 333</a></li>
                <li><a href="mailto:[email protected]">Napisz do nas</a></li>
            </ul>
        </footer>
        <footer>
            <h3>&copy; Wykonane przez: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  35. Krok 35: Skrypt 1: zapytanie 1

    Kryteria:

    R.5.2 Skrypt 1 zawiera instrukcję wysyłającą do bazy danych zapytanie 1 lub skrypt 2 zawiera instrukcję wysyłającą do bazy danych zapytanie 2

    ok. 2 min
    Polecenie z arkusza

    Skrypt 1, na stronie index.php: Wysyła do bazy danych zapytanie 1

    $pdo->query(...) wysyła zapytanie 1, a ->fetchAll() od razu odbiera wszystkie 28 wierszy do tablicy $peaks (R.5.2). Zapytanie nie zawiera danych od użytkownika, więc wystarcza query.

    index.php PHP zmienione: 1 linia
    <?php
    function escape(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES);
    }
    
    $peaks = [];
    $thumbnails = [];
    $dbError = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=korona;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        $peaks = $pdo->query('SELECT id, nazwa FROM szczyty ORDER BY wysokosc DESC')->fetchAll();
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać danych o szczytach. Spróbuj ponownie za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Korona gór polskich</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <img src="logo.png" alt="Logo">
        </header>
        <header>
            <h1>Korona Gór Polskich</h1>
        </header>
        <main>
    <?php if ($dbError !== ''): ?>
            <p><?= $dbError ?></p>
    <?php endif; ?>
        </main>
        <section>
        </section>
        <footer>
            <h3>Kontakt</h3>
            <ul>
                <li>Zadzwoń do nas: <a href="tel:+48111222333">111 222 333</a></li>
                <li><a href="mailto:[email protected]">Napisz do nas</a></li>
            </ul>
        </footer>
        <footer>
            <h3>&copy; Wykonane przez: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>

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

  36. Krok 36: Skrypt 1: nazwy szczytów w znacznikach span

    Kryteria: ,

    R.2.9 Na stronie index.php znajduje się przynajmniej jeden znacznik <span> (w HTML lub skrypcie )

    R.5.3 Skrypt 1 wyświetla wszystkie zwrócone zapytaniem nazwy szczytów zapisanych w kolejnych znacznikach <span> (od Rysów do Łysicy)

    ok. 3 min
    Polecenie z arkusza

    Każdą zwróconą zapytaniem nazwę szczytu wyświetla w znaczniku <span>, tak że jest ona odnośnikiem do pliku szczyty.php oraz przekazuje do tego pliku metodą GET id szczytu

    Najpierw same nazwy. W bloku głównym stoi <nav> z opisem aria-label (czytnik ekranu ogłosi „nawigacja: Szczyty Korony Gór Polskich”), a w nim pętla foreach ($peaks as $peak): w składni alternatywnej: jeden <span> na szczyt, od Rysów do Łysicy (R.2.9, R.5.3). <?= ... ?> to skrót od <?php echo ... ?>.

    Blok <nav> ma też zaletę dla CSS: rodzicem spanów jest <nav>, a nie <main>, więc komunikat o błędzie bazy nad nim nie przesunie numeracji :nth-child - Babia Góra i Rudawiec są największe.

    index.php PHP zmienione: 5 linii
    <?php
    function escape(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES);
    }
    
    $peaks = [];
    $thumbnails = [];
    $dbError = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=korona;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        $peaks = $pdo->query('SELECT id, nazwa FROM szczyty ORDER BY wysokosc DESC')->fetchAll();
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać danych o szczytach. Spróbuj ponownie za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Korona gór polskich</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <img src="logo.png" alt="Logo">
        </header>
        <header>
            <h1>Korona Gór Polskich</h1>
        </header>
        <main>
    <?php if ($dbError !== ''): ?>
            <p><?= $dbError ?></p>
    <?php endif; ?>
            <nav aria-label="Szczyty Korony Gór Polskich">
    <?php foreach ($peaks as $peak): ?>
                <span><?= escape($peak['nazwa']) ?></span>
    <?php endforeach; ?>
            </nav>
        </main>
        <section>
        </section>
        <footer>
            <h3>Kontakt</h3>
            <ul>
                <li>Zadzwoń do nas: <a href="tel:+48111222333">111 222 333</a></li>
                <li><a href="mailto:[email protected]">Napisz do nas</a></li>
            </ul>
        </footer>
        <footer>
            <h3>&copy; Wykonane przez: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  37. Krok 37: Skrypt 1: odnośnik z identyfikatorem w adresie

    Kryteria: ,

    R.3.4 Przynajmniej jeden odnośnik ze strony głównej otwiera stronę szczyty.php (w skrypcie lub w HTML )

    R.5.4 Po kliknięciu nazwy szczytu na stronie głównej otwierana jest podstrona, do której przekazywany jest parametr metodą GET. Adres URL ma postać: szczyty.php?id=<wartość> i działa co najmniej dla 2 szczytów

    ok. 2 min
    Polecenie z arkusza

    Każdą zwróconą zapytaniem nazwę szczytu wyświetla w znaczniku <span>, tak że jest ona odnośnikiem do pliku szczyty.php oraz przekazuje do tego pliku metodą GET id szczytu

    Nazwa w <a> wewnątrz spana, parametr GET po znaku zapytania: szczyty.php?id=1 dla Rysów (tabela 2 arkusza; R.3.4, R.5.4). (int) przy identyfikatorze gwarantuje, że do adresu trafi liczba. W szablonie HTML cudzysłowy atrybutu stoją zwyczajnie - nie trzeba ich poprzedzać ukośnikiem jak w napisie PHP. Odnośnik jest ciemnoszary, bo selektor * ustawia kolor #333 każdemu elementowi, także <a>.

    index.php PHP zmienione: 1 linia
    <?php
    function escape(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES);
    }
    
    $peaks = [];
    $thumbnails = [];
    $dbError = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=korona;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        $peaks = $pdo->query('SELECT id, nazwa FROM szczyty ORDER BY wysokosc DESC')->fetchAll();
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać danych o szczytach. Spróbuj ponownie za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Korona gór polskich</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <img src="logo.png" alt="Logo">
        </header>
        <header>
            <h1>Korona Gór Polskich</h1>
        </header>
        <main>
    <?php if ($dbError !== ''): ?>
            <p><?= $dbError ?></p>
    <?php endif; ?>
            <nav aria-label="Szczyty Korony Gór Polskich">
    <?php foreach ($peaks as $peak): ?>
                <span><a href="szczyty.php?id=<?= (int) $peak['id'] ?>"><?= escape($peak['nazwa']) ?></a></span>
    <?php endforeach; ?>
            </nav>
        </main>
        <section>
        </section>
        <footer>
            <h3>Kontakt</h3>
            <ul>
                <li>Zadzwoń do nas: <a href="tel:+48111222333">111 222 333</a></li>
                <li><a href="mailto:[email protected]">Napisz do nas</a></li>
            </ul>
        </footer>
        <footer>
            <h3>&copy; Wykonane przez: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  38. Krok 38: Skrypt 2: zapytanie 2

    ok. 1 min
    Polecenie z arkusza

    Skrypt 2: Wysyła do bazy danych zapytanie 2

    Drugie zapytanie przy otwartym połączeniu, przed $pdo = null: dziesięć par plik - nazwa do tablicy $thumbnails. Wszystkie dane strony są już pobrane, zanim zacznie się HTML.

    index.php PHP zmienione: 1 linia
    <?php
    function escape(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES);
    }
    
    $peaks = [];
    $thumbnails = [];
    $dbError = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=korona;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        $peaks = $pdo->query('SELECT id, nazwa FROM szczyty ORDER BY wysokosc DESC')->fetchAll();
        $thumbnails = $pdo->query('SELECT plik, nazwa FROM szczyty ORDER BY id LIMIT 10')->fetchAll();
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać danych o szczytach. Spróbuj ponownie za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Korona gór polskich</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <img src="logo.png" alt="Logo">
        </header>
        <header>
            <h1>Korona Gór Polskich</h1>
        </header>
        <main>
    <?php if ($dbError !== ''): ?>
            <p><?= $dbError ?></p>
    <?php endif; ?>
            <nav aria-label="Szczyty Korony Gór Polskich">
    <?php foreach ($peaks as $peak): ?>
                <span><a href="szczyty.php?id=<?= (int) $peak['id'] ?>"><?= escape($peak['nazwa']) ?></a></span>
    <?php endforeach; ?>
            </nav>
        </main>
        <section>
        </section>
        <footer>
            <h3>Kontakt</h3>
            <ul>
                <li>Zadzwoń do nas: <a href="tel:+48111222333">111 222 333</a></li>
                <li><a href="mailto:[email protected]">Napisz do nas</a></li>
            </ul>
        </footer>
        <footer>
            <h3>&copy; Wykonane przez: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>

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

  39. Krok 39: Skrypt 2: miniatury z bazy

    Kryteria: , ,

    R.2.10 Przynajmniej jedna grafika z bloku sekcji ma przypisaną klasę miniatury (w HTML lub skrypcie )

    R.5.5 Skrypt 2 zawiera pętlę działającą na wszystkich rekordach zwróconych zapytaniem

    R.5.6 Na stronie głównej są wyświetlane obrazy z plików o nazwach zwróconych w wyniku zapytania oraz tekstach alternatywnych zawierających nazwę szczytu (część obrazów może być ukryta)

    ok. 2 min
    Polecenie z arkusza

    Dla każdego zwróconego zapytaniem rekordu wyświetla obraz, w którym nazwa pliku jest źródłem a nazwa szczytu – tekstem alternatywnym. Każdy obraz ma przypisaną klasę miniatury

    W sekcji pętla foreach po $thumbnails (R.5.5): src z pola plik, alt z pola nazwa, klasa miniatury (R.2.10, R.5.6). Obie wartości przez escape() - stoją w atrybutach w cudzysłowach, więc cudzysłów w nazwie rozbiłby znacznik. Drugi rząd zdjęć znika za overflow: hidden, jak na ilustracji 2.

    index.php PHP zmienione: 3 linie
    <?php
    function escape(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES);
    }
    
    $peaks = [];
    $thumbnails = [];
    $dbError = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=korona;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        $peaks = $pdo->query('SELECT id, nazwa FROM szczyty ORDER BY wysokosc DESC')->fetchAll();
        $thumbnails = $pdo->query('SELECT plik, nazwa FROM szczyty ORDER BY id LIMIT 10')->fetchAll();
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać danych o szczytach. Spróbuj ponownie za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Korona gór polskich</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <img src="logo.png" alt="Logo">
        </header>
        <header>
            <h1>Korona Gór Polskich</h1>
        </header>
        <main>
    <?php if ($dbError !== ''): ?>
            <p><?= $dbError ?></p>
    <?php endif; ?>
            <nav aria-label="Szczyty Korony Gór Polskich">
    <?php foreach ($peaks as $peak): ?>
                <span><a href="szczyty.php?id=<?= (int) $peak['id'] ?>"><?= escape($peak['nazwa']) ?></a></span>
    <?php endforeach; ?>
            </nav>
        </main>
        <section>
    <?php foreach ($thumbnails as $thumbnail): ?>
            <img src="<?= escape($thumbnail['plik']) ?>" alt="<?= escape($thumbnail['nazwa']) ?>" class="miniatury">
    <?php endforeach; ?>
        </section>
        <footer>
            <h3>Kontakt</h3>
            <ul>
                <li>Zadzwoń do nas: <a href="tel:+48111222333">111 222 333</a></li>
                <li><a href="mailto:[email protected]">Napisz do nas</a></li>
            </ul>
        </footer>
        <footer>
            <h3>&copy; Wykonane przez: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  40. Krok 40: szczyty.php: połączenie i ten sam skrypt 2

    ok. 2 min
    Polecenie z arkusza

    Zawartość bloku sekcji: Efekt działania skryptu 2

    Sekcja jest wspólna dla obu stron, więc do szczyty.php trafia ta sama górna część PHP (funkcja escape(), połączenie, zapytanie 2) i ta sama pętla miniatur. Podgląd pokazuje stronę szczytu z pustym blokiem głównym i paskiem miniatur.

    szczyty.php PHP zmienione: 27 linii
    <?php
    function escape(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES);
    }
    
    $thumbnails = [];
    $dbError = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=korona;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        $thumbnails = $pdo->query('SELECT plik, nazwa FROM szczyty ORDER BY id LIMIT 10')->fetchAll();
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać danych o szczytach. Spróbuj ponownie za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Korona gór polskich</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <img src="logo.png" alt="Logo">
        </header>
        <header>
            <h1>Korona Gór Polskich</h1>
        </header>
        <main>
    <?php if ($dbError !== ''): ?>
            <p><?= $dbError ?></p>
    <?php endif; ?>
        </main>
        <section>
    <?php foreach ($thumbnails as $thumbnail): ?>
            <img src="<?= escape($thumbnail['plik']) ?>" alt="<?= escape($thumbnail['nazwa']) ?>" class="miniatury">
    <?php endforeach; ?>
        </section>
        <footer>
            <h3>Kontakt</h3>
            <ul>
                <li>Zadzwoń do nas: <a href="tel:+48111222333">111 222 333</a></li>
                <li><a href="mailto:[email protected]">Napisz do nas</a></li>
            </ul>
        </footer>
        <footer>
            <h3>&copy; Wykonane przez: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (szczyty.php)
  41. Krok 41: Skrypt 3: id z adresu i zapytanie przygotowane

    Kryteria:

    R.5.7 Skrypt 3 (podstrona) zawiera instrukcję wysyłającą do bazy danych zapytanie 3, zmodyfikowane w ten sposób, że wybierany jest rekord o id przesłanym do strony

    ok. 4 min
    Polecenie z arkusza

    Skrypt 3, na stronie szczyty.php (ilustracja 5): Wysyła do bazy danych zapytanie 3, zmodyfikowane w ten sposób, że sprawdzane jest id wysłane z pliku index.php metodą GET

    Identyfikator przychodzi w adresie (szczyty.php?id=1), czyli w $_GET['id']. Najpierw filter_var z FILTER_VALIDATE_INT i minimum 1: oddaje liczbę albo false (dla abc, -3, 1 OR 1=1). Operator ?? podstawia pusty napis, gdy parametru nie ma wcale - wtedy też wychodzi false, bez ostrzeżenia „Undefined array key”.

    Zapytanie 3 z aliasami, w którym zamiast 1 stoi parametr :id: prepare wysyła strukturę zapytania, execute(['id' => $peakId]) - wartość osobno (R.5.7). fetch() oddaje jeden wiersz albo false, gdy szczytu nie ma; ?: null zamienia false na null, które czytelniej znaczy „brak”. Od tego kroku podgląd to strona otwarta kliknięciem „Rysy”.

    szczyty.php PHP zmienione: 12 linii
    <?php
    function escape(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES);
    }
    
    $thumbnails = [];
    $peak = null;
    $dbError = '';
    $peakId = filter_var($_GET['id'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=korona;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        if ($peakId !== false) {
            $statement = $pdo->prepare(
                'SELECT s.plik, s.nazwa, s.wysokosc, s.pasmo, o.opis
                 FROM szczyty AS s
                 JOIN opis AS o ON o.szczyty_id = s.id
                 WHERE s.id = :id'
            );
            $statement->execute(['id' => $peakId]);
            $peak = $statement->fetch() ?: null;
        }
        $thumbnails = $pdo->query('SELECT plik, nazwa FROM szczyty ORDER BY id LIMIT 10')->fetchAll();
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać danych o szczytach. Spróbuj ponownie za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Korona gór polskich</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <img src="logo.png" alt="Logo">
        </header>
        <header>
            <h1>Korona Gór Polskich</h1>
        </header>
        <main>
    <?php if ($dbError !== ''): ?>
            <p><?= $dbError ?></p>
    <?php endif; ?>
        </main>
        <section>
    <?php foreach ($thumbnails as $thumbnail): ?>
            <img src="<?= escape($thumbnail['plik']) ?>" alt="<?= escape($thumbnail['nazwa']) ?>" class="miniatury">
    <?php endforeach; ?>
        </section>
        <footer>
            <h3>Kontakt</h3>
            <ul>
                <li>Zadzwoń do nas: <a href="tel:+48111222333">111 222 333</a></li>
                <li><a href="mailto:[email protected]">Napisz do nas</a></li>
            </ul>
        </footer>
        <footer>
            <h3>&copy; Wykonane przez: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>

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

  42. Krok 42: Skrypt 3: zdjęcie szczytu

    ok. 1 min
    Polecenie z arkusza

    Wyświetla dane zwrócone zapytaniem: Obraz z pliku, którego nazwa została zwrócona, z tekstem alternatywnym „szczyt”

    W bloku głównym warunek if ($peak !== null): - dane szczytu trafiają na stronę tylko wtedy, gdy rekord się znalazł. Zdjęcie z pola plik (rysy.jpg), tekst alternatywny stały: „szczyt”. Bez klasy miniatury, więc w naturalnym rozmiarze, jak na ilustracji 5.

    szczyty.php PHP zmienione: 3 linie
    <?php
    function escape(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES);
    }
    
    $thumbnails = [];
    $peak = null;
    $dbError = '';
    $peakId = filter_var($_GET['id'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=korona;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        if ($peakId !== false) {
            $statement = $pdo->prepare(
                'SELECT s.plik, s.nazwa, s.wysokosc, s.pasmo, o.opis
                 FROM szczyty AS s
                 JOIN opis AS o ON o.szczyty_id = s.id
                 WHERE s.id = :id'
            );
            $statement->execute(['id' => $peakId]);
            $peak = $statement->fetch() ?: null;
        }
        $thumbnails = $pdo->query('SELECT plik, nazwa FROM szczyty ORDER BY id LIMIT 10')->fetchAll();
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać danych o szczytach. Spróbuj ponownie za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Korona gór polskich</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <img src="logo.png" alt="Logo">
        </header>
        <header>
            <h1>Korona Gór Polskich</h1>
        </header>
        <main>
    <?php if ($dbError !== ''): ?>
            <p><?= $dbError ?></p>
    <?php endif; ?>
    <?php if ($peak !== null): ?>
            <img src="<?= escape($peak['plik']) ?>" alt="szczyt">
    <?php endif; ?>
        </main>
        <section>
    <?php foreach ($thumbnails as $thumbnail): ?>
            <img src="<?= escape($thumbnail['plik']) ?>" alt="<?= escape($thumbnail['nazwa']) ?>" class="miniatury">
    <?php endforeach; ?>
        </section>
        <footer>
            <h3>Kontakt</h3>
            <ul>
                <li>Zadzwoń do nas: <a href="tel:+48111222333">111 222 333</a></li>
                <li><a href="mailto:[email protected]">Napisz do nas</a></li>
            </ul>
        </footer>
        <footer>
            <h3>&copy; Wykonane przez: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (szczyty.php)
  43. Krok 43: Skrypt 3: nazwa, wysokość i pasmo

    Kryteria:

    R.5.9 Na podstronie nazwa szczytu jest wyświetlana w <h2>, oraz w <h3>: "wysokość: <wysokosc> metrów n.p.m." i "pasmo górskie: <pasmo>", wartości w <> są pobrane z bazy danych

    ok. 2 min
    Polecenie z arkusza

    Nagłówek drugiego stopnia z nazwą szczytu. Nagłówek trzeciego stopnia o treści: „wysokość: <wysokosc> metrów n.p.m.”. Nagłówek trzeciego stopnia o treści: „pasmo górskie: <pasmo>”, pola <> zostały pobrane z bazy danych

    Pola po nazwach kolumn: $peak['nazwa'] w <h2>, wysokość i pasmo w dwóch <h3> ze stałym tekstem dookoła (R.5.9). Dla Rysów: „wysokość: 2499 metrów n.p.m.” i „pasmo górskie: Tatry”.

    szczyty.php PHP zmienione: 3 linie
    <?php
    function escape(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES);
    }
    
    $thumbnails = [];
    $peak = null;
    $dbError = '';
    $peakId = filter_var($_GET['id'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=korona;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        if ($peakId !== false) {
            $statement = $pdo->prepare(
                'SELECT s.plik, s.nazwa, s.wysokosc, s.pasmo, o.opis
                 FROM szczyty AS s
                 JOIN opis AS o ON o.szczyty_id = s.id
                 WHERE s.id = :id'
            );
            $statement->execute(['id' => $peakId]);
            $peak = $statement->fetch() ?: null;
        }
        $thumbnails = $pdo->query('SELECT plik, nazwa FROM szczyty ORDER BY id LIMIT 10')->fetchAll();
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać danych o szczytach. Spróbuj ponownie za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Korona gór polskich</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <img src="logo.png" alt="Logo">
        </header>
        <header>
            <h1>Korona Gór Polskich</h1>
        </header>
        <main>
    <?php if ($dbError !== ''): ?>
            <p><?= $dbError ?></p>
    <?php endif; ?>
    <?php if ($peak !== null): ?>
            <img src="<?= escape($peak['plik']) ?>" alt="szczyt">
            <h2><?= escape($peak['nazwa']) ?></h2>
            <h3>wysokość: <?= escape($peak['wysokosc']) ?> metrów n.p.m.</h3>
            <h3>pasmo górskie: <?= escape($peak['pasmo']) ?></h3>
    <?php endif; ?>
        </main>
        <section>
    <?php foreach ($thumbnails as $thumbnail): ?>
            <img src="<?= escape($thumbnail['plik']) ?>" alt="<?= escape($thumbnail['nazwa']) ?>" class="miniatury">
    <?php endforeach; ?>
        </section>
        <footer>
            <h3>Kontakt</h3>
            <ul>
                <li>Zadzwoń do nas: <a href="tel:+48111222333">111 222 333</a></li>
                <li><a href="mailto:[email protected]">Napisz do nas</a></li>
            </ul>
        </footer>
        <footer>
            <h3>&copy; Wykonane przez: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (szczyty.php)
  44. Krok 44: Skrypt 3: opis w akapicie

    Kryteria: ,

    R.5.8 Na podstronie wyświetlany jest obraz z pliku o nazwie zwróconej w wyniku zapytania i tekście alternatywnym "szczyt" oraz opis szczytu

    R.5.10 W skryptach zastosowana przynajmniej jedna zmienna oraz nazwy wszystkich zmiennych są w języku polskim lub angielskim i odzwierciedlają cel zastosowania

    ok. 1 min
    Polecenie z arkusza

    Paragraf z opisem

    Opis z tabeli opis w <p> - strona szczytu wygląda jak na ilustracji 5 (R.5.8). Nazwy zmiennych i funkcji mówią, co przechowują (R.5.10): $pdo, $peaks, $peak, $peakId, $thumbnails, $thumbnail, $statement, $dbError, escape().

    szczyty.php PHP zmienione: 1 linia
    <?php
    function escape(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES);
    }
    
    $thumbnails = [];
    $peak = null;
    $dbError = '';
    $peakId = filter_var($_GET['id'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=korona;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        if ($peakId !== false) {
            $statement = $pdo->prepare(
                'SELECT s.plik, s.nazwa, s.wysokosc, s.pasmo, o.opis
                 FROM szczyty AS s
                 JOIN opis AS o ON o.szczyty_id = s.id
                 WHERE s.id = :id'
            );
            $statement->execute(['id' => $peakId]);
            $peak = $statement->fetch() ?: null;
        }
        $thumbnails = $pdo->query('SELECT plik, nazwa FROM szczyty ORDER BY id LIMIT 10')->fetchAll();
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać danych o szczytach. Spróbuj ponownie za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Korona gór polskich</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <img src="logo.png" alt="Logo">
        </header>
        <header>
            <h1>Korona Gór Polskich</h1>
        </header>
        <main>
    <?php if ($dbError !== ''): ?>
            <p><?= $dbError ?></p>
    <?php endif; ?>
    <?php if ($peak !== null): ?>
            <img src="<?= escape($peak['plik']) ?>" alt="szczyt">
            <h2><?= escape($peak['nazwa']) ?></h2>
            <h3>wysokość: <?= escape($peak['wysokosc']) ?> metrów n.p.m.</h3>
            <h3>pasmo górskie: <?= escape($peak['pasmo']) ?></h3>
            <p><?= escape($peak['opis']) ?></p>
    <?php endif; ?>
        </main>
        <section>
    <?php foreach ($thumbnails as $thumbnail): ?>
            <img src="<?= escape($thumbnail['plik']) ?>" alt="<?= escape($thumbnail['nazwa']) ?>" class="miniatury">
    <?php endforeach; ?>
        </section>
        <footer>
            <h3>Kontakt</h3>
            <ul>
                <li>Zadzwoń do nas: <a href="tel:+48111222333">111 222 333</a></li>
                <li><a href="mailto:[email protected]">Napisz do nas</a></li>
            </ul>
        </footer>
        <footer>
            <h3>&copy; Wykonane przez: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (szczyty.php)
  45. Krok 45: Opis w akapitach i nieistniejący szczyt

    ok. 3 min
    Polecenie z arkusza

    Paragraf z opisem

    10 opisów ma w środku puste wiersze, ale HTML zamienia znaki końca linii na spacje - opis Babiej Góry zlewa się w jeden blok. preg_split('/\R+/u', ...) dzieli tekst w miejscach końców linii (\R to dowolny koniec linii: \n, \r\n), a pętla daje każdemu kawałkowi własny <p>. trim usuwa puste wiersze na brzegach.

    Flaga u na końcu wzorca jest konieczna: każe czytać tekst jako UTF-8. Bez niej \R pasuje też do pojedynczego bajtu 0x85, a ten bajt jest drugą połową litery „ą” w UTF-8 - wzorzec przecinał „znajdujący” w środku litery, a htmlspecialchars zwracało pusty napis dla uszkodzonego kawałka. Z opisu Rysów zostawał sam koniec (sprawdzone).

    Gałąź else obsługuje adres bez szczytu: ?id=99, ?id=abc, ?id=1 OR 1=1 i samo szczyty.php dają „Nie znaleziono szczytu” zamiast pustych nagłówków albo ostrzeżeń PHP (sprawdzone). Podgląd: Babia Góra (?id=9). Klucz tego nie sprawdza.

    szczyty.php PHP zmienione: 5 linii
    <?php
    function escape(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES);
    }
    
    $thumbnails = [];
    $peak = null;
    $dbError = '';
    $peakId = filter_var($_GET['id'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=korona;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        if ($peakId !== false) {
            $statement = $pdo->prepare(
                'SELECT s.plik, s.nazwa, s.wysokosc, s.pasmo, o.opis
                 FROM szczyty AS s
                 JOIN opis AS o ON o.szczyty_id = s.id
                 WHERE s.id = :id'
            );
            $statement->execute(['id' => $peakId]);
            $peak = $statement->fetch() ?: null;
        }
        $thumbnails = $pdo->query('SELECT plik, nazwa FROM szczyty ORDER BY id LIMIT 10')->fetchAll();
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać danych o szczytach. Spróbuj ponownie za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Korona gór polskich</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <img src="logo.png" alt="Logo">
        </header>
        <header>
            <h1>Korona Gór Polskich</h1>
        </header>
        <main>
    <?php if ($dbError !== ''): ?>
            <p><?= $dbError ?></p>
    <?php endif; ?>
    <?php if ($peak !== null): ?>
            <img src="<?= escape($peak['plik']) ?>" alt="szczyt">
            <h2><?= escape($peak['nazwa']) ?></h2>
            <h3>wysokość: <?= escape($peak['wysokosc']) ?> metrów n.p.m.</h3>
            <h3>pasmo górskie: <?= escape($peak['pasmo']) ?></h3>
    <?php foreach (preg_split('/\R+/u', trim($peak['opis'])) as $paragraph): ?>
            <p><?= escape($paragraph) ?></p>
    <?php endforeach; ?>
    <?php else: ?>
            <h2>Nie znaleziono szczytu</h2>
    <?php endif; ?>
        </main>
        <section>
    <?php foreach ($thumbnails as $thumbnail): ?>
            <img src="<?= escape($thumbnail['plik']) ?>" alt="<?= escape($thumbnail['nazwa']) ?>" class="miniatury">
    <?php endforeach; ?>
        </section>
        <footer>
            <h3>Kontakt</h3>
            <ul>
                <li>Zadzwoń do nas: <a href="tel:+48111222333">111 222 333</a></li>
                <li><a href="mailto:[email protected]">Napisz do nas</a></li>
            </ul>
        </footer>
        <footer>
            <h3>&copy; Wykonane przez: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (szczyty.php)
  46. Etap 5: Porównanie z wersją podstawową

    Kroki 46-47 · po tym etapie masz 40 z 40 kryteriów (pkt)

    Krok 46: Co jest inaczej i dlaczego

    ok. 3 min
    Polecenie z arkusza

    Ocenie podlegać będzie 5 rezultatów: operacje na bazie danych, zawartość witryny internetowej, działanie witryny internetowej, styl CSS witryny internetowej, skrypt połączenia z bazą.

    Obie wersje dają te same punkty. Różnice:

    wersja podstawowata wersja
    układfloat: left + clear: both, szerokości przez #logo, #contact…siatka 30% 70% na <body>, grid-column: 1 / -1
    identyfikatory blokówlogo, title, contact, authorniepotrzebne
    rozmiary nazwspan:nth-child(2), span:nth-child(11)span:is(:nth-child(2), :nth-child(11))
    spanyprosto w <main>w <nav> - numeracja niezależna od reszty bloku
    zapytanie 2LIMIT 10ORDER BY id LIMIT 10
    bazamysqli_* w miejscu wypisywaniaPDO na górze każdego pliku
    id z adresuwstawiony do SQLfilter_var i parametr :id
    zły adresostrzeżenia PHP, puste nagłówki„Nie znaleziono szczytu”
    opisjeden akapitakapity z preg_split

    Obie wersje powtarzają w dwóch plikach połączenie i skrypt 2. W większym projekcie trafiłyby do osobnego pliku dołączanego przez require - tu arkusz każe pisać skrypty na stronach, a klucz sprawdza je w kodzie strony.

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

  47. Krok 47: Którą wersję wybrać na egzaminie

    ok. 2 min
    Polecenie z arkusza

    Czas przeznaczony na wykonanie zadania wynosi 150 minut.

    • Wersja podstawowa, gdy liczy się czas albo PDO i siatka nie są opanowane: funkcje mysqli_* są w tabeli 1 arkusza, a każdy skrypt stoi tam, gdzie widać jego efekt.
    • Ta wersja, gdy PDO i siatka są pisane z pamięci. :is() warto znać, ale zwykła lista selektorów po przecinku daje ten sam punkt.

    Mieszanie też daje pełne punkty: siatka z tej wersji i mysqli_* z podstawowej. Zdjęcie zastępcze dla szczytu bez pliku (file_exists) jest pokazane w etapie „Dobre praktyki” wersji podstawowej.

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

  48. Etap 6: Oddanie pracy

    Krok 48 · po tym etapie masz 40 z 40 kryteriów (pkt)

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

    ok. 5 min
    Polecenie z arkusza

    UWAGA: po zakończeniu pracy utwórz plik tekstowy o nazwie przeglądarka.txt. Zapisz w nim nazwę przeglądarki internetowej, w której weryfikowana była poprawność działania witryny. Umieść go w folderze z numerem zdającego. Nagraj płytę z rezultatami pracy.

    W przeglądarka.txt nazwa przeglądarki, w której strona była sprawdzana przez http://localhost/…/index.php.

    W folderze z numerem zdającego: folder czcionka z plikami Lato, zdjęcia gór, import.png, index.php, kw1.jpg - kw4.jpg, kwerendy.txt, logo.png, przeglądarka.txt, styl.css, szczyty.php. Po nagraniu sprawdź odczyt płyty.

    przeglądarka.txt Tekst nowy plik
    Google Chrome

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

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.