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

Rozwiązanie krok po kroku: Portal turystyczny Islandii (Wspólne pliki (include, mysqli))

To trzeci sposób rozwiązania tego samego arkusza - tak, jak zorganizowano by tę witrynę w prawdziwym projekcie PHP bez frameworka. Wynik jest ten sam co w pozostałych zakładkach i spełnia każde kryterium klucza, ale kod jest rozłożony na pliki według zadań:

  • wspólne części stron w osobnych plikach: nagłówek (naglowek.php), blok boczny z listami (boczny.php) i stopka (stopka.php) istnieją raz i obie strony dołączają je przez require - poprawka bloku bocznego to zmiana w jednym pliku,
  • dostęp do bazy w jednym pliku baza.php: połączenie oraz funkcje objectNames(), gallery() i place(), które wysyłają zapytania i zwracają gotowe tablice; strony nie zawierają ani jednej linii SQL,
  • mysqli w stylu obiektowym z wyjątkami (mysqli_report) i zapytaniami przygotowanymi (prepare, bind_param) - ta sama biblioteka co funkcje z tabeli 1 arkusza, ale zapisana jak w nowoczesnym kodzie; awarię bazy łapie jedna funkcja set_exception_handler,
  • układ i styl - ten sam CSS na siatce co w zakładce „Nowoczesny kod”.

⚠️ Uwaga egzaminacyjna: klucz każe sprawdzać połączenie i zapytania „w kodzie źródłowym strony”. Tu są w baza.php, więc egzaminator musi zajrzeć do dołączonego pliku. Na egzaminie bezpieczniej trzymać PHP w pliku strony (wersja podstawowa albo nowoczesna) - ta wersja pokazuje, dokąd prowadzi dobra organizacja kodu.

Baza i zdjęcia pochodzą z prawdziwego archiwum pliki1, a podgląd każdego kroku z PHP to wynik prawdziwego uruchomienia stron na serwerze z tą bazą.

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

  1. Etap 1: Baza danych

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

    Krok 1: Baza danych islandia

    Kryteria:

    R.1.1 Wykonany import tabel do bazy danych islandia - plik import w formacie JPEG

    ok. 3 min
    Polecenie z arkusza

    Utwórz bazę danych o nazwie islandia, z zestawem znaków utf8_unicode_ci.

    Najpierw rozpakuj archiwum pliki1.7z z pulpitu (hasło isLandi@ z treści zadania) - są w nim baza1.sql i dziewięć zdjęć. W phpMyAdmin kliknij Nowa, wpisz islandia i wybierz utf8_unicode_ci. To samo w SQL:

    CREATE DATABASE islandia CHARACTER SET utf8 COLLATE utf8_unicode_ci;
    

    utf8_unicode_ci to ogólne porównywanie Unicode: sortuje polskie („gorące źródło”) i islandzkie litery („Fuglaskoðunarhús”) według zasad Unicode, a przy porównaniach nie rozróżnia wielkości liter (ci - case insensitive). Dziedziczą je jednak tylko tabele zakładane później bez własnego ustawienia - tabele z baza1.sql mają w definicji utf8_polish_ci i to ono działa w zapytaniach.

    Typowe błędy

    Nazwa bazy musi być dokładnie islandia - skrypty łączą się z bazą po nazwie (R.5.1).

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

    Kryteria:

    R.1.1 Wykonany import tabel do bazy danych islandia - plik import w formacie JPEG

    ok. 2 min
    Polecenie z arkusza

    Do utworzonej bazy zaimportuj tabele z pliku baza1.sql.

    Zaznacz bazę islandia, karta Import, plik baza1.sql, Wykonaj. Plik tworzy pięć tabel, ale zadanie korzysta tylko z trzech z ilustracji 1: kontynenty, rodzaje (rodzaje obiektów: wodospad, szczyt, siedlisko zwierząt…) i obiekty (25 obiektów z kluczami obcymi idRodzaj i idKontynent). Nazwy kolumn mają wielkie litery w środku (idObiekt, nazwaCechy, wartoscCechy) - w zapytaniach pisz je dokładnie tak.

  3. Krok 3: Zrzut ekranu import.jpg

    Kryteria:

    R.1.1 Wykonany import tabel do bazy danych islandia - plik import w formacie JPEG

    ok. 2 min
    Polecenie z arkusza

    Wykonaj zrzut ekranu po imporcie. Zapisz zrzut w formacie JPEG 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.

    Cały ekran (Print Screen) z komunikatem o udanym imporcie albo listą tabel, zapis jako import.jpg - w tym arkuszu import jest w JPEG, a zrzuty zapytań w PNG.

    Typowe błędy

    import.png zamiast import.jpg albo wykadrowany zrzut to strata punktu R.1.1.

  4. Krok 4: Zapytanie 1: obiekty z Islandii

    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 pola idObiekt, plik i nazwa z tabeli obiekty dla Islandii. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT idObiekt, plik, nazwa FROM obiekty WHERE panstwo = "Islandia"; oraz na zrzucie widocznych jest dokładnie 9 rekordów o identyfikatorach 46 ÷ 54

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 1: wybierające jedynie pola idObiekt, plik i nazwa z tabeli obiekty dla Islandii.

    Każda klauzula w osobnej linii: co (trzy kolumny w kolejności z arkusza), skąd (obiekty), które wiersze (WHERE panstwo = 'Islandia'). Tekst w SQL w apostrofach - to zapis zgodny ze standardem, działający w każdej bazie (MySQL przyjmuje też cudzysłów, jak w kluczu).

    Wynik: 9 wierszy o identyfikatorach 46-54 (R.1.3). Zapytanie do kwerendy.txt, zrzut kw1.png.

    kwerendy.txt Tekst nowy plik
    -- Zapytanie 1
    SELECT idObiekt, plik, nazwa
    FROM obiekty
    WHERE panstwo = 'Islandia';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 4
  5. Krok 5: Zapytanie 2: dane obiektu 46 z rodzajem

    Kryteria:

    R.1.4 Wykonane zapytanie 2 wybierające jedynie pola plik, nazwa, nazwaCechy, wartoscCechy, opis z tabeli obiekty oraz odpowiadające im pole rodzaj z tabeli rodzaje dla obiektu o identyfikatorze 46. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT plik, nazwa, nazwaCechy, wartoscCechy, opis, rodzaj FROM obiekty JOIN rodzaje USING(idRodzaj) WHERE idObiekt = 46; (lub INNER JOIN lub porównanie kluczy po ON lub WHERE ) oraz na zrzucie widoczny jest dokładnie 1 rekord dla nazwy Strokkur

    ok. 3 min
    Polecenie z arkusza

    Zapytanie 2: wybierające jedynie pola plik, nazwa, nazwaCechy, wartoscCechy, opis z tabeli obiekty oraz odpowiadające im pole rodzaj z tabeli rodzaje dla obiektu o identyfikatorze 46. Należy posłużyć się relacją.

    Złączenie z aliasami tabel (obiekty AS o, rodzaje AS r): każda kolumna mówi, z której tabeli pochodzi, a warunek złączenia r.idRodzaj = o.idRodzaj nie jest dwuznaczny, choć kolumna idRodzaj jest w obu tabelach.

    Wynik: jeden wiersz - gejzer Strokkur (R.1.4). Zrzut kw2.png. Strona obiekty.php wyśle to samo zapytanie z identyfikatorem z adresu w miejscu 46.

    Typowe błędy

    ON idRodzaj = idRodzaj bez nazw tabel to błąd „Column idRodzaj in on clause is ambiguous”.

    kwerendy.txt Tekst zmienione: 6 linii
    -- Zapytanie 1
    SELECT idObiekt, plik, nazwa
    FROM obiekty
    WHERE panstwo = 'Islandia';
    
    -- Zapytanie 2
    SELECT o.plik, o.nazwa, o.nazwaCechy, o.wartoscCechy, o.opis, r.rodzaj
    FROM obiekty AS o
    JOIN rodzaje AS r ON r.idRodzaj = o.idRodzaj
    WHERE o.idObiekt = 46;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 5
  6. Krok 6: Zapytanie 3: wodospady Islandii

    Kryteria:

    R.1.5 Wykonane zapytanie 3 wybierające jedynie nazwy obiektów z Islandii, których identyfikator rodzaju to 10. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa FROM obiekty WHERE idRodzaj = 10 AND Panstwo = "Islandia"; oraz na zrzucie widoczne są dokładnie 4 rekordy: Skogafoss, Seljalandsfoss, Godafoss, Dettifoss

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 3: wybierające jedynie nazwy obiektów z Islandii, których identyfikator rodzaju to 10.

    Dwa warunki naraz - AND. Identyfikator rodzaju jest w samej tabeli obiekty, więc złączenie nie jest potrzebne. Wynik: Skogafoss, Seljalandsfoss, Godafoss, Dettifoss (R.1.5), zrzut kw3.png.

    Typowe błędy

    OR zamiast AND zwróci wszystkie obiekty z Islandii i wszystkie wodospady z bazy.

    kwerendy.txt Tekst zmienione: 6 linii
    -- Zapytanie 1
    SELECT idObiekt, plik, nazwa
    FROM obiekty
    WHERE panstwo = 'Islandia';
    
    -- Zapytanie 2
    SELECT o.plik, o.nazwa, o.nazwaCechy, o.wartoscCechy, o.opis, r.rodzaj
    FROM obiekty AS o
    JOIN rodzaje AS r ON r.idRodzaj = o.idRodzaj
    WHERE o.idObiekt = 46;
    
    -- Zapytanie 3
    SELECT nazwa
    FROM obiekty
    WHERE panstwo = 'Islandia'
      AND idRodzaj = 10;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 6
  7. Krok 7: Zapytanie 4: liczba obiektów każdego rodzaju

    Kryteria:

    R.1.6 Wykonane zapytanie 4 wybierające jedynie rodzaj z tabeli rodzaje oraz zliczające liczbę obiektów danego rodzaju. Kolumna z liczbą obiektów powinna mieć nadaną nazwę (alias) „Liczba obiektów”. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT rodzaj, COUNT(obiekty.idObiekt) AS "Liczba obiektów" FROM Rodzaje JOIN obiekty USING(idRodzaj) GROUP BY rodzaj; (Po COUNT dowolna nazwa pola tabeli obiekty, AS opcjonalnie, lub porównanie kluczy po ON lub WHERE ) oraz na zrzucie widocznych jest dokładnie 8 rekordów: gorące źródło 1, jaskinia 2, jezioro 1, park narodowy 1, siedlisko zwierząt 2, szczyt 4, wodospad 12, wulkan 2

    ok. 3 min
    Polecenie z arkusza

    Zapytanie 4: wybierające jedynie rodzaj z tabeli rodzaje oraz zliczające liczbę obiektów (wodospadów, szczytów itd.) danego rodzaju. Kolumna z liczbą obiektów powinna mieć nadaną nazwę (alias) „Liczba obiektów”. Należy posłużyć się grupowaniem i relacją.

    • JOIN obiekty AS o ON o.idRodzaj = r.idRodzaj - relacja: do każdego rodzaju jego obiekty,
    • GROUP BY r.idRodzaj, r.rodzaj - grupa to jeden rodzaj wskazany kluczem; dwa różne rodzaje o tej samej nazwie nie zleją się w jeden wiersz,
    • COUNT(o.idObiekt) AS "Liczba obiektów" - alias ze spacją i polską literą musi być w cudzysłowie (tu cudzysłów oznacza nazwę, nie tekst),
    • ORDER BY r.rodzaj - kolejność alfabetyczna podana wprost; samo grupowanie jej nie gwarantuje.

    Wynik: 8 wierszy - gorące źródło 1, jaskinia 2, jezioro 1, park narodowy 1, siedlisko zwierząt 2, szczyt 4, wodospad 12, wulkan 2 (R.1.6). Zrzut kw4.png.

    Typowe błędy

    Bez GROUP BY COUNT policzy wszystkie 25 obiektów w jednym wierszu.

    kwerendy.txt Tekst zmienione: 7 linii
    -- Zapytanie 1
    SELECT idObiekt, plik, nazwa
    FROM obiekty
    WHERE panstwo = 'Islandia';
    
    -- Zapytanie 2
    SELECT o.plik, o.nazwa, o.nazwaCechy, o.wartoscCechy, o.opis, r.rodzaj
    FROM obiekty AS o
    JOIN rodzaje AS r ON r.idRodzaj = o.idRodzaj
    WHERE o.idObiekt = 46;
    
    -- Zapytanie 3
    SELECT nazwa
    FROM obiekty
    WHERE panstwo = 'Islandia'
      AND idRodzaj = 10;
    
    -- Zapytanie 4
    SELECT r.rodzaj, COUNT(o.idObiekt) AS "Liczba obiektów"
    FROM rodzaje AS r
    JOIN obiekty AS o ON o.idRodzaj = r.idRodzaj
    GROUP BY r.idRodzaj, r.rodzaj
    ORDER BY r.rodzaj;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 7
  8. Etap 2: Strona HTML

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

    Krok 8: Zdjęcia w folderze zdającego

    ok. 1 min
    Polecenie z arkusza

    Witryna składa się z podstron: islandia.php i obiekty.php.

    Dziewięć zdjęć z rozpakowanego archiwum kopiujesz do folderu zdającego, obok stron. Skrypty będą je wskazywać samą nazwą pliku z kolumny plik, więc ścieżka względna wystarcza. Arkusz nie wymaga ich obróbki.

    Podgląd strony po tym kroku
    Podgląd strony po kroku 8
  9. Krok 9: Plik islandia.php w HTML5

    Kryteria:

    R.2.1 W pliku islandia.php lub obiekty.php znajduje się deklaracja HTML5 <!DOCTYPE HTML> i deklaracja języka zawartości: polski np. <html lang="pl">

    ok. 1 min
    Polecenie z arkusza

    Cechy wspólne obu stron: Zapisane w języku HTML5.

    Plik islandia.php w folderze zdającego. <!DOCTYPE html> włącza tryb standardów HTML5, dalej szkielet: <html>, <head> i <body>. Rozszerzenie .php, bo na początku pliku stanie kod PHP.

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

    Kryteria:

    R.2.1 W pliku islandia.php lub obiekty.php znajduje się deklaracja HTML5 <!DOCTYPE HTML> i deklaracja języka zawartości: polski np. <html lang="pl">

    ok. 1 min
    Polecenie z arkusza

    Zadeklarowany polski język zawartości witryny.

    lang="pl" w <html> - czytnik ekranu czyta polską wymową, przeglądarka dobiera słownik i dzielenie wyrazów.

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

    Kryteria:

    R.2.2 W sekcji head znajduje się jawnie podany standard kodowania polskich znaków i tytuł strony "Islandia”

    ok. 1 min
    Polecenie z arkusza

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

    <meta charset="UTF-8"> jako pierwszy element <head>. Ten sam standard ustawi połączenie z bazą (charset=utf8mb4 w PDO), więc polskie litery przejdą bez zmian całą drogę: baza → PHP → przeglądarka.

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

    Kryteria:

    R.2.2 W sekcji head znajduje się jawnie podany standard kodowania polskich znaków i tytuł strony "Islandia”

    ok. 1 min
    Polecenie z arkusza

    Tytuł strony „Islandia”.

    <title>Islandia</title> - napis na karcie przeglądarki. Obok <meta name="viewport">: bez niego telefon pokazałby stronę pomniejszoną jak na monitorze. Arkusz tego nie wymaga, a w każdym współczesnym szablonie ta linia jest.

    islandia.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Islandia</title>
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (islandia.php)
  13. Krok 13: Podpięcie arkusza stylów

    Kryteria:

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

    ok. 1 min
    Polecenie z arkusza

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

    <link rel="stylesheet" href="styl.css"> - nazwa dokładnie z arkusza. Cały wygląd będzie w tym pliku, w HTML nie ma ani jednego atrybutu style.

    Typowe błędy

    Zła nazwa w href to brak stylów, a wtedy cały rezultat R.4 przepada.

    islandia.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Islandia</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (islandia.php)
  14. Krok 14: Bloki semantyczne

    Kryteria:

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

    ok. 2 min
    Polecenie z arkusza

    Podział strony na bloki: nagłówkowy, boczny, główny, stopki. Blok główny zawiera blok sekcji. Podział zrealizowany za pomocą semantycznych znaczników języka HTML5 tak, aby po uruchomieniu w przeglądarce układ bloków był zgodny z ilustracją 4.

    Znaczniki z tabeli 4 arkusza i klucza R.2.3: header, main z section w środku, aside, footer.

    Kolejność w kodzie to kolejność czytania: nagłówek, treść główna, dodatki z boku, stopka - tak stronę przeczyta czytnik ekranu i tak zobaczy ją telefon bez stylów. O tym, że blok boczny stanie po prawej od samej góry, zdecyduje siatka CSS, a nie miejsce w kodzie. (Przy układzie na float blok boczny musiałby stać w kodzie przed blokiem głównym - tak jest w wersji podstawowej.)

    islandia.php PHP zmienione: 10 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Islandia</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
        </header>
        <main>
            <section>
            </section>
        </main>
        <aside>
        </aside>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (islandia.php)
  15. Krok 15: Nagłówek pierwszego stopnia będący odnośnikiem

    Kryteria: ,

    R.2.4 W bloku nagłówkowym znajduje się <h1>: "Zwiedzaj Islandię", w bloku bocznym <h3>: "Do zwiedzania" w bloku głównym <h2>: "Galeria" lub "Opis miejsca" oraz <p> z numerem zdającego w stopce

    R.3.3 Przynajmniej na jednej stronie kliknięcie odnośnika z bloku nagłówkowego prowadzi do strony islandia.php

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku nagłówkowego: nagłówek pierwszego stopnia o treści „Zwiedzaj Islandię”. Treść jest odnośnikiem do strony islandia.php.

    Odnośnik wewnątrz nagłówka (<h1><a>…</a></h1>) - nagłówek zostaje nagłówkiem, a jego treść da się kliknąć. Ze strony obiekty.php zaprowadzi z powrotem do galerii (R.2.4, R.3.3).

    islandia.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Islandia</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1><a href="islandia.php">Zwiedzaj Islandię</a></h1>
        </header>
        <main>
            <section>
            </section>
        </main>
        <aside>
        </aside>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (islandia.php)
  16. Krok 16: Blok główny: nagłówek „Galeria” i blok sekcji

    Kryteria:

    R.2.4 W bloku nagłówkowym znajduje się <h1>: "Zwiedzaj Islandię", w bloku bocznym <h3>: "Do zwiedzania" w bloku głównym <h2>: "Galeria" lub "Opis miejsca" oraz <p> z numerem zdającego w stopce

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku głównego dla strony islandia.php: nagłówek drugiego stopnia o treści: „Galeria”. Blok sekcji z efektem wykonania skryptu 3.

    <h2> przed <section> - nagłówek stoi nad ramką sekcji, jak na ilustracji 2. Sekcję wypełni galeria ze skryptu 3.

    islandia.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Islandia</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1><a href="islandia.php">Zwiedzaj Islandię</a></h1>
        </header>
        <main>
            <h2>Galeria</h2>
            <section>
            </section>
        </main>
        <aside>
        </aside>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (islandia.php)
  17. Krok 17: Blok boczny: nagłówek trzeciego stopnia

    Kryteria:

    R.2.4 W bloku nagłówkowym znajduje się <h1>: "Zwiedzaj Islandię", w bloku bocznym <h3>: "Do zwiedzania" w bloku głównym <h2>: "Galeria" lub "Opis miejsca" oraz <p> z numerem zdającego w stopce

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku bocznego: nagłówek trzeciego stopnia o treści „Do zwiedzania”.

    <h3> - stopień nagłówka narzuca arkusz (R.2.4).

    islandia.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Islandia</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1><a href="islandia.php">Zwiedzaj Islandię</a></h1>
        </header>
        <main>
            <h2>Galeria</h2>
            <section>
            </section>
        </main>
        <aside>
            <h3>Do zwiedzania</h3>
        </aside>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (islandia.php)
  18. Krok 18: Lista punktowana z dwoma elementami

    Kryteria:

    R.2.5 W bloku bocznym znajduje się jedna lista punktowana z dwoma elementami „Wodospady:” i „Siedliska zwierząt:” (znaczniki <ul>, <li> )

    ok. 1 min
    Polecenie z arkusza

    Lista punktowana (nieuporządkowana) z dwoma elementami: „Wodospady:” i „Siedliska zwierząt:”.

    <ul> z dwoma <li> (R.2.5). Znacznik </li> w osobnej linii, bo w środku każdego elementu stanie zaraz druga lista.

    islandia.php PHP zmienione: 6 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Islandia</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1><a href="islandia.php">Zwiedzaj Islandię</a></h1>
        </header>
        <main>
            <h2>Galeria</h2>
            <section>
            </section>
        </main>
        <aside>
            <h3>Do zwiedzania</h3>
            <ul>
                <li>Wodospady:
                </li>
                <li>Siedliska zwierząt:
                </li>
            </ul>
        </aside>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (islandia.php)
  19. Krok 19: Zagnieżdżone listy numerowane

    Kryteria:

    R.2.6 Przynajmniej jeden element listy zawiera zagnieżdżoną jedną listę numerowaną (w skrypcie lub HTML, znaczniki <ol> i <li>, znacznik <ol> zawarto wewnątrz <li> listy z R.2.5)

    ok. 2 min
    Polecenie z arkusza

    Element „Wodospady:” zawiera listę numerowaną (uporządkowaną) wypełnioną skryptem 1. Element „Siedliska zwierząt:” zawiera listę numerowaną (uporządkowaną) wypełnioną skryptem 2.

    Lista zagnieżdżona to <ol> wewnątrz <li>, między tekstem a </li> (R.2.6). Elementy dopiszą skrypty 1 i 2.

    Typowe błędy

    <ol> między </li> a następnym <li> nie jest zagnieżdżona w elemencie - to niepoprawny HTML i brak R.2.6.

    islandia.php PHP zmienione: 4 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Islandia</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1><a href="islandia.php">Zwiedzaj Islandię</a></h1>
        </header>
        <main>
            <h2>Galeria</h2>
            <section>
            </section>
        </main>
        <aside>
            <h3>Do zwiedzania</h3>
            <ul>
                <li>Wodospady:
                    <ol>
                    </ol>
                </li>
                <li>Siedliska zwierząt:
                    <ol>
                    </ol>
                </li>
            </ul>
        </aside>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (islandia.php)
  20. Krok 20: Stopka: linia pozioma i autor

    Kryteria: ,

    R.2.4 W bloku nagłówkowym znajduje się <h1>: "Zwiedzaj Islandię", w bloku bocznym <h3>: "Do zwiedzania" w bloku głównym <h2>: "Galeria" lub "Opis miejsca" oraz <p> z numerem zdającego w stopce

    R.2.9 Stopka zawiera linię poziomą (<hr> lub za pomocą obramowania w CSS )

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku stopki: linia pozioma. Paragraf o treści „Autor: ”, dalej wstawiony numer zdającego.

    <hr> (element pusty, bez ukośnika w HTML5) i paragraf (R.2.4, R.2.9). Tu stoi nazwisko autora rozwiązania - na egzaminie wpisujesz swój numer zdającego (PESEL).

    islandia.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Islandia</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1><a href="islandia.php">Zwiedzaj Islandię</a></h1>
        </header>
        <main>
            <h2>Galeria</h2>
            <section>
            </section>
        </main>
        <aside>
            <h3>Do zwiedzania</h3>
            <ul>
                <li>Wodospady:
                    <ol>
                    </ol>
                </li>
                <li>Siedliska zwierząt:
                    <ol>
                    </ol>
                </li>
            </ul>
        </aside>
        <footer>
            <hr>
            <p>Autor: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (islandia.php)
  21. Krok 21: Wspólne części stron w osobnych plikach

    ok. 4 min
    Polecenie z arkusza

    Witryna składa się z podstron: islandia.php i obiekty.php. Cechy wspólne obu stron: podział strony na bloki: nagłówkowy, boczny, główny, stopki.

    Nagłówek, blok boczny i stopka będą identyczne na obu stronach. Zamiast kopiować je do obiekty.php, każdy trafia do własnego pliku: naglowek.php, boczny.php, stopka.php. W islandia.php w ich miejscu stoją trzy linie <?php require 'plik.php'; ?> - serwer wkleja zawartość pliku przy każdym wyświetleniu strony, więc przeglądarka dostaje dokładnie ten sam HTML co wcześniej.

    require, a nie include: gdy pliku brakuje, require zatrzymuje stronę błędem, a include tylko ostrzega i wyświetla stronę bez nagłówka czy stopki. Brakująca część strony to błąd, który ma być widać od razu.

    Pliki dołączane mają rozszerzenie .php i nie mają <!DOCTYPE> ani <head> - to fragmenty, nie strony. Deklaracja HTML5, <head> i bloki główne zostają w plikach stron, bo tam szuka ich klucz (R.2.1, R.2.2). Od tego kroku podgląd to wynik strony z serwera.

    Typowe błędy

    Ścieżka w require liczy się od pliku strony, która go wywołuje - pliki dołączane leżą w tym samym folderze co strony.

    boczny.php PHP nowy plik
        <aside>
            <h3>Do zwiedzania</h3>
            <ul>
                <li>Wodospady:
                    <ol>
                    </ol>
                </li>
                <li>Siedliska zwierząt:
                    <ol>
                    </ol>
                </li>
            </ul>
        </aside>
    islandia.php PHP zmienione: 3 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Islandia</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    <?php require 'naglowek.php'; ?>
        <main>
            <h2>Galeria</h2>
            <section>
            </section>
        </main>
    <?php require 'boczny.php'; ?>
    <?php require 'stopka.php'; ?>
    </body>
    </html>
    naglowek.php PHP nowy plik
        <header>
            <h1><a href="islandia.php">Zwiedzaj Islandię</a></h1>
        </header>
    stopka.php PHP nowy plik
        <footer>
            <hr>
            <p>Autor: Bartosz Bryniarski</p>
        </footer>
    Podgląd strony po tym kroku (islandia.php)
  22. Krok 22: Strona obiekty.php

    Kryteria:

    R.2.8 Na stronie obiekty.php w bloku głównym lub bloku sekcji znajdują się elementy <h2>, <h3> i przynajmniej jeden <p> (w skrypcie lub HTML )

    ok. 2 min
    Polecenie z arkusza

    Zawartość bloku głównego dla strony obiekty.php: nagłówek drugiego stopnia o treści: „Opis miejsca”. Blok sekcji z efektem działania skryptu 4.

    Druga strona to kopia islandia.php z innym nagłówkiem bloku głównego: „Opis miejsca”. Jest krótka - nagłówek, blok boczny i stopkę dołącza z tych samych plików, więc obie strony na pewno wyglądają jak jedna witryna (R.2.8).

    obiekty.php PHP nowy plik
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Islandia</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    <?php require 'naglowek.php'; ?>
        <main>
            <h2>Opis miejsca</h2>
            <section>
            </section>
        </main>
    <?php require 'boczny.php'; ?>
    <?php require 'stopka.php'; ?>
    </body>
    </html>
    Podgląd strony po tym kroku (obiekty.php)
  23. Etap 3: Styl CSS

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

    Krok 23: Zmienne CSS z wartościami arkusza

    ok. 2 min
    Polecenie z arkusza

    Styl CSS zdefiniowany jest w całości w zewnętrznym pliku o nazwie styl.css.

    Plik styl.css zaczyna się od zmiennych w :root: kolor PaleTurquoise i zaokrąglenie 20px dotyczą dwóch bloków, więc wpisane raz występują w regułach jako var(--accent) i var(--radius). W inspektorze widać obliczone wartości - kryteria sprawdza się tak samo.

    styl.css CSS nowy plik
    :root {
        --accent: PaleTurquoise;
        --frame: DimGray;
        --radius: 20px;
    }
    Podgląd strony po tym kroku (islandia.php)
  24. Krok 24: Krój czcionki dla wszystkich selektorów

    Kryteria:

    R.4.1 domyślne formatowanie selektorów (*): krój czcionki "Comic Sans MS" - jako pierwszy, sans-serif - jako ostatni

    ok. 1 min
    Polecenie z arkusza

    Domyślnie, dla wszystkich selektorów: krój czcionki Comic Sans MS, w przypadku braku sans-serif.

    Selektor *, nazwa ze spacjami w cudzysłowie, na końcu rodzina zastępcza sans-serif (R.4.1).

    styl.css CSS zmienione: 4 linie
    :root {
        --accent: PaleTurquoise;
        --frame: DimGray;
        --radius: 20px;
    }
    
    * {
        font-family: "Comic Sans MS", sans-serif;
    }
    Podgląd strony po tym kroku (islandia.php)
  25. Krok 25: Układ bloków zgodny z ilustracją 4: siatka

    Kryteria: ,

    R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z ilustracją 4 w arkuszu egzaminacyjnym (prawidłowo zastosowane 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; żaden blok nie zachodzi na inne bloki - sprawdzić w inspektorze)

    R.4.3 szerokość: blok nagłówkowy, główny - 70%, boczny - 25% (width lub grid-template- columns lub flex ) wysokość: blok nagłówkowy - 100px, boczny - 700px, sekcja - 400px

    ok. 4 min
    Polecenie z arkusza

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

    Ciało strony staje się siatką, a grid-template-areas rysuje ilustrację 4 tekstem: w pierwszym wierszu nagłówek i blok boczny, w drugim blok główny i dalej blok boczny (to samo słowo w dwóch wierszach = blok na dwa wiersze), w trzecim stopka na całą szerokość. Cztery krótkie reguły przypisują bloki do obszarów (R.3.1).

    • grid-template-columns: 70% 25% - szerokości z arkusza: nagłówek i blok główny 70%, blok boczny 25% (klucz R.4.3 przyjmuje je w siatce), column-gap: 5% - reszta szerokości to odstęp,
    • grid-template-rows: auto 1fr - pierwszy wiersz ma wysokość nagłówka, a drugi bierze resztę wysokości bloku bocznego; bez tego siatka rozdzieliłaby nadmiar po równo i pod nagłówkiem powstałaby dziura,
    • align-items: start - bloki nie rozciągają się do wysokości wiersza, więc nagłówek ma swoje 100 px.

    Bloki nie nachodzą na siebie, a stopka zawsze jest pod najwyższym z nich - bez clear.

    Typowe błędy

    width: 70% na nagłówku w siatce liczy się od szerokości kolumny, nie strony - szerokości trzymaj w grid-template-columns.

    styl.css CSS zmienione: 28 linii
    :root {
        --accent: PaleTurquoise;
        --frame: DimGray;
        --radius: 20px;
    }
    
    * {
        font-family: "Comic Sans MS", sans-serif;
    }
    
    body {
        display: grid;
        grid-template-columns: 70% 25%;
        grid-template-rows: auto 1fr;
        grid-template-areas:
            "header aside"
            "main   aside"
            "footer footer";
        column-gap: 5%;
        align-items: start;
    }
    
    header {
        grid-area: header;
    }
    
    main {
        grid-area: main;
    }
    
    aside {
        grid-area: aside;
    }
    
    footer {
        grid-area: footer;
    }
    Podgląd strony po tym kroku (islandia.php)
  26. Krok 26: Wyśrodkowanie, tło i zaokrąglenie

    Kryteria: , ,

    R.4.2 kolor tła: blok nagłówkowy i boczny - PaleTurquoise

    R.4.5 wyrównanie tekstu: blok nagłówkowy, główny - do środka

    R.4.7 zaokrąglenie rogów (border-radius) : blok nagłówkowy, boczny - 20px

    ok. 1 min
    Polecenie z arkusza

    Dla bloku nagłówkowego i głównego: szerokość 70%, wyrównanie tekstu do środka. Dla bloku nagłówkowego i bocznego: kolor tła PaleTurquoise, zaokrąglenie rogów 20 px.

    Szerokość 70% jest już w siatce. Dochodzą wspólne reguły par bloków: wyśrodkowany tekst (R.4.5), tło i zaokrąglenie ze zmiennych (R.4.2, R.4.7).

    styl.css CSS zmienione: 11 linii
    :root {
        --accent: PaleTurquoise;
        --frame: DimGray;
        --radius: 20px;
    }
    
    * {
        font-family: "Comic Sans MS", sans-serif;
    }
    
    body {
        display: grid;
        grid-template-columns: 70% 25%;
        grid-template-rows: auto 1fr;
        grid-template-areas:
            "header aside"
            "main   aside"
            "footer footer";
        column-gap: 5%;
        align-items: start;
    }
    
    header {
        grid-area: header;
    }
    
    main {
        grid-area: main;
    }
    
    aside {
        grid-area: aside;
    }
    
    footer {
        grid-area: footer;
    }
    
    header,
    main {
        text-align: center;
    }
    
    header,
    aside {
        border-radius: var(--radius);
        background-color: var(--accent);
    }
    Podgląd strony po tym kroku (islandia.php)
  27. Krok 27: Nagłówek: wysokość i marginesy wewnętrzne

    Kryteria: ,

    R.4.3 szerokość: blok nagłówkowy, główny - 70%, boczny - 25% (width lub grid-template- columns lub flex ) wysokość: blok nagłówkowy - 100px, boczny - 700px, sekcja - 400px

    R.4.4 marginesy wewnętrzne (padding ): blok nagłówkowy - 2%, stopka jedynie padding-top: 10px; marginesy zewnętrzne (margin ): sekcja - górny i dolny - 10px, pozostałe - 50px

    ok. 1 min
    Polecenie z arkusza

    Dodatkowo dla bloku nagłówkowego: wysokość 100 px, marginesy wewnętrzne 2%.

    Do reguły header, która już przypisuje obszar siatki (R.4.3, R.4.4). Procentowy padding liczy się od szerokości obszaru - także w pionie.

    styl.css CSS zmienione: 2 linie
    :root {
        --accent: PaleTurquoise;
        --frame: DimGray;
        --radius: 20px;
    }
    
    * {
        font-family: "Comic Sans MS", sans-serif;
    }
    
    body {
        display: grid;
        grid-template-columns: 70% 25%;
        grid-template-rows: auto 1fr;
        grid-template-areas:
            "header aside"
            "main   aside"
            "footer footer";
        column-gap: 5%;
        align-items: start;
    }
    
    header {
        grid-area: header;
        height: 100px;
        padding: 2%;
    }
    
    main {
        grid-area: main;
    }
    
    aside {
        grid-area: aside;
    }
    
    footer {
        grid-area: footer;
    }
    
    header,
    main {
        text-align: center;
    }
    
    header,
    aside {
        border-radius: var(--radius);
        background-color: var(--accent);
    }
    Podgląd strony po tym kroku (islandia.php)
  28. Krok 28: Blok boczny: wysokość i interlinia

    Kryteria: ,

    R.4.3 szerokość: blok nagłówkowy, główny - 70%, boczny - 25% (width lub grid-template- columns lub flex ) wysokość: blok nagłówkowy - 100px, boczny - 700px, sekcja - 400px

    R.4.8 wysokość linii (line-hight) : blok boczny - 2

    ok. 1 min
    Polecenie z arkusza

    Dodatkowo dla bloku bocznego: szerokość 25%, wysokość 700 px, wysokość linii (interlinia) 2.

    Szerokość 25% to druga kolumna siatki. Wysokość 700 px i line-height: 2 - liczba bez jednostki to dwukrotność rozmiaru czcionki, więc interlinia rośnie razem z tekstem (R.4.3, R.4.8).

    styl.css CSS zmienione: 2 linie
    :root {
        --accent: PaleTurquoise;
        --frame: DimGray;
        --radius: 20px;
    }
    
    * {
        font-family: "Comic Sans MS", sans-serif;
    }
    
    body {
        display: grid;
        grid-template-columns: 70% 25%;
        grid-template-rows: auto 1fr;
        grid-template-areas:
            "header aside"
            "main   aside"
            "footer footer";
        column-gap: 5%;
        align-items: start;
    }
    
    header {
        grid-area: header;
        height: 100px;
        padding: 2%;
    }
    
    main {
        grid-area: main;
    }
    
    aside {
        grid-area: aside;
        height: 700px;
        line-height: 2;
    }
    
    footer {
        grid-area: footer;
    }
    
    header,
    main {
        text-align: center;
    }
    
    header,
    aside {
        border-radius: var(--radius);
        background-color: var(--accent);
    }
    Podgląd strony po tym kroku (islandia.php)
  29. Krok 29: Stopka: margines wewnętrzny górny

    Kryteria:

    R.4.4 marginesy wewnętrzne (padding ): blok nagłówkowy - 2%, stopka jedynie padding-top: 10px; marginesy zewnętrzne (margin ): sekcja - górny i dolny - 10px, pozostałe - 50px

    ok. 1 min
    Polecenie z arkusza

    Dla bloku stopki: jedynie margines wewnętrzny górny 10 px.

    „Jedynie” dotyczy wyglądu: padding-top: 10px (R.4.4). Przypisanie do obszaru siatki to układ, nie formatowanie stopki.

    styl.css CSS zmienione: 1 linia
    :root {
        --accent: PaleTurquoise;
        --frame: DimGray;
        --radius: 20px;
    }
    
    * {
        font-family: "Comic Sans MS", sans-serif;
    }
    
    body {
        display: grid;
        grid-template-columns: 70% 25%;
        grid-template-rows: auto 1fr;
        grid-template-areas:
            "header aside"
            "main   aside"
            "footer footer";
        column-gap: 5%;
        align-items: start;
    }
    
    header {
        grid-area: header;
        height: 100px;
        padding: 2%;
    }
    
    main {
        grid-area: main;
    }
    
    aside {
        grid-area: aside;
        height: 700px;
        line-height: 2;
    }
    
    footer {
        grid-area: footer;
        padding-top: 10px;
    }
    
    header,
    main {
        text-align: center;
    }
    
    header,
    aside {
        border-radius: var(--radius);
        background-color: var(--accent);
    }
    Podgląd strony po tym kroku (islandia.php)
  30. Krok 30: Blok sekcji: ramka, marginesy, wysokość, przewijanie

    Kryteria: , ,

    R.4.3 szerokość: blok nagłówkowy, główny - 70%, boczny - 25% (width lub grid-template- columns lub flex ) wysokość: blok nagłówkowy - 100px, boczny - 700px, sekcja - 400px

    R.4.4 marginesy wewnętrzne (padding ): blok nagłówkowy - 2%, stopka jedynie padding-top: 10px; marginesy zewnętrzne (margin ): sekcja - górny i dolny - 10px, pozostałe - 50px

    R.4.6 cechy sekcji: obramowanie 1px dashed DimGray, paski przewijania (overflow) auto

    ok. 2 min
    Polecenie z arkusza

    Dla bloku sekcji: obramowanie linią kreskową o szerokości 1 px i kolorze DimGray, marginesy zewnętrzne górny i dolny 10 px, pozostałe 50 px, wysokość 400 px, paski przewijania pojawiające się tylko w przypadku przepełnienia bloku.

    margin: 10px 50px - pierwsza wartość góra i dół, druga lewa i prawa; overflow: auto - paski tylko przy przepełnieniu; dashed - linia kreskowa (R.4.3, R.4.4, R.4.6).

    styl.css CSS zmienione: 7 linii
    :root {
        --accent: PaleTurquoise;
        --frame: DimGray;
        --radius: 20px;
    }
    
    * {
        font-family: "Comic Sans MS", sans-serif;
    }
    
    body {
        display: grid;
        grid-template-columns: 70% 25%;
        grid-template-rows: auto 1fr;
        grid-template-areas:
            "header aside"
            "main   aside"
            "footer footer";
        column-gap: 5%;
        align-items: start;
    }
    
    header {
        grid-area: header;
        height: 100px;
        padding: 2%;
    }
    
    main {
        grid-area: main;
    }
    
    aside {
        grid-area: aside;
        height: 700px;
        line-height: 2;
    }
    
    footer {
        grid-area: footer;
        padding-top: 10px;
    }
    
    header,
    main {
        text-align: center;
    }
    
    header,
    aside {
        border-radius: var(--radius);
        background-color: var(--accent);
    }
    
    section {
        height: 400px;
        margin: 10px 50px;
        overflow: auto;
        border: 1px dashed var(--frame);
    }
    Podgląd strony po tym kroku (islandia.php)
  31. Krok 31: Klasa miniatury: wysokość, przezroczystość, tranzycja

    Kryteria:

    R.4.9 styl klasy .miniatury : wysokość - 200px, padding: 10px, przezroczystość (opacity) - 0.8, tranzycja trwająca 1s (transition lub transition-duration)

    ok. 2 min
    Polecenie z arkusza

    Dla klasy miniatury przypisanej do obrazów ze strony islandia.php, generowanych skryptem 3: wysokość 200 px, marginesy wewnętrzne 10 px, przezroczystość 0.8, dodana tranzycja trwająca 1 sekundę.

    Nazwa klasy z arkusza. transition: opacity 1s podaje, którą właściwość animować i jak długo (tabela 3 arkusza) - przeglądarka nie musi śledzić zmian wszystkich właściwości, a przypadkowa zmiana innej (np. rozmiaru) nie zacznie się nagle animować (R.4.9).

    styl.css CSS zmienione: 7 linii
    :root {
        --accent: PaleTurquoise;
        --frame: DimGray;
        --radius: 20px;
    }
    
    * {
        font-family: "Comic Sans MS", sans-serif;
    }
    
    body {
        display: grid;
        grid-template-columns: 70% 25%;
        grid-template-rows: auto 1fr;
        grid-template-areas:
            "header aside"
            "main   aside"
            "footer footer";
        column-gap: 5%;
        align-items: start;
    }
    
    header {
        grid-area: header;
        height: 100px;
        padding: 2%;
    }
    
    main {
        grid-area: main;
    }
    
    aside {
        grid-area: aside;
        height: 700px;
        line-height: 2;
    }
    
    footer {
        grid-area: footer;
        padding-top: 10px;
    }
    
    header,
    main {
        text-align: center;
    }
    
    header,
    aside {
        border-radius: var(--radius);
        background-color: var(--accent);
    }
    
    section {
        height: 400px;
        margin: 10px 50px;
        overflow: auto;
        border: 1px dashed var(--frame);
    }
    
    .miniatury {
        height: 200px;
        padding: 10px;
        opacity: 0.8;
        transition: opacity 1s;
    }
    Podgląd strony po tym kroku (islandia.php)
  32. Krok 32: Miniatura pod kursorem: pełne krycie

    Kryteria:

    R.4.10 gdy kursor znajdzie się na elemencie z przypisaną klasą .miniatury , jego przezroczystość (opacity) zmienia się na wartość 1

    ok. 1 min
    Polecenie z arkusza

    W przypadku gdy kursor znajdzie się na elemencie z przypisaną klasą miniatury, jego przezroczystość zmienia się na wartość 1.

    :hover zmienia tylko to, co się zmienia - opacity: 1. Tranzycja działa w obie strony (R.4.10).

    styl.css CSS zmienione: 4 linie
    :root {
        --accent: PaleTurquoise;
        --frame: DimGray;
        --radius: 20px;
    }
    
    * {
        font-family: "Comic Sans MS", sans-serif;
    }
    
    body {
        display: grid;
        grid-template-columns: 70% 25%;
        grid-template-rows: auto 1fr;
        grid-template-areas:
            "header aside"
            "main   aside"
            "footer footer";
        column-gap: 5%;
        align-items: start;
    }
    
    header {
        grid-area: header;
        height: 100px;
        padding: 2%;
    }
    
    main {
        grid-area: main;
    }
    
    aside {
        grid-area: aside;
        height: 700px;
        line-height: 2;
    }
    
    footer {
        grid-area: footer;
        padding-top: 10px;
    }
    
    header,
    main {
        text-align: center;
    }
    
    header,
    aside {
        border-radius: var(--radius);
        background-color: var(--accent);
    }
    
    section {
        height: 400px;
        margin: 10px 50px;
        overflow: auto;
        border: 1px dashed var(--frame);
    }
    
    .miniatury {
        height: 200px;
        padding: 10px;
        opacity: 0.8;
        transition: opacity 1s;
    }
    
    .miniatury:hover {
        opacity: 1;
    }
    Podgląd strony po tym kroku (islandia.php)
  33. Etap 4: Skrypty PHP

    Kroki 33-51 · po tym etapie masz 37 z 37 kryteriów (pkt)

    Krok 33: Dane połączenia w pliku config.php

    Kryteria:

    R.5.1 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy islandia oraz ostatnią operacją na bazie jest jej zamknięcie (wystarczy na jednej stronie )

    ok. 2 min
    Polecenie z arkusza

    Wymagania dotyczące skryptów: napisane w języku PHP. Łączą się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie islandia.

    Dane logowania w osobnym pliku, który tylko zwraca tablicę z czterema wartościami dla mysqli: serwer, użytkownik, hasło, nazwa bazy. Na serwerze produkcyjnym podmienia się ten jeden plik, a do repozytorium z kodem go nie dodaje się.

    Typowe błędy

    config.php i baza.php muszą trafić do folderu z rezultatami.

    config.php PHP nowy plik
    <?php
    // Dane połączenia z bazą - w jednym miejscu, poza plikami stron
    return [
        'host' => 'localhost',
        'user' => 'root',
        'password' => '',
        'database' => 'islandia',
    ];

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

  34. Krok 34: Połączenie z bazą w pliku baza.php

    Kryteria: ,

    R.5.1 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy islandia oraz ostatnią operacją na bazie jest jej zamknięcie (wystarczy na jednej stronie )

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

    ok. 4 min
    Polecenie z arkusza

    Łączą się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie islandia.

    Plik baza.php łączy się z bazą, a obie strony zaczynają się od require 'baza.php'; (R.5.1, R.5.2).

    • new mysqli(...) - ta sama biblioteka co mysqli_connect z tabeli 1, ale w stylu obiektowym: połączenie to obiekt $db, a operacje to jego metody ($db->query(), $db->prepare(), $db->close()),
    • mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT) - każdy błąd bazy to wyjątek (w PHP 8.1 i nowszych to zachowanie domyślne, wpisane jawnie nie zależy od wersji),
    • set_charset('utf8mb4') - kodowanie połączenia; bez niego polskie i islandzkie litery mogłyby przyjść z bazy zniekształcone,
    • set_exception_handler - jedna funkcja obsługuje każdy nieobsłużony wyjątek w całej witrynie: szczegóły zapisuje do dziennika błędów serwera (error_log), a użytkownik widzi tylko ogólny komunikat i kod 500. Nie trzeba otaczać try każdego zapytania na każdej stronie.
    Typowe błędy

    mysqli_report musi stać przed new mysqli - inaczej błąd samego połączenia nie będzie wyjątkiem.

    baza.php PHP nowy plik
    <?php
    // Połączenie z bazą i funkcje pobierające dane - wspólne dla obu stron
    $config = require 'config.php';
    
    mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
    
    set_exception_handler(function (Throwable $error): void {
        error_log((string) $error);
        http_response_code(500);
        echo 'Baza danych jest chwilowo niedostępna. Spróbuj ponownie później.';
    });
    
    $db = new mysqli($config['host'], $config['user'], $config['password'], $config['database']);
    $db->set_charset('utf8mb4');
    islandia.php PHP zmienione: 3 linie
    <?php
    require 'baza.php';
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Islandia</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    <?php require 'naglowek.php'; ?>
        <main>
            <h2>Galeria</h2>
            <section>
            </section>
        </main>
    <?php require 'boczny.php'; ?>
    <?php require 'stopka.php'; ?>
    </body>
    </html>
    obiekty.php PHP zmienione: 3 linie
    <?php
    require 'baza.php';
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Islandia</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    <?php require 'naglowek.php'; ?>
        <main>
            <h2>Opis miejsca</h2>
            <section>
            </section>
        </main>
    <?php require 'boczny.php'; ?>
    <?php require 'stopka.php'; ?>
    </body>
    </html>

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

  35. Krok 35: Funkcja e(): bezpieczne wypisywanie tekstu

    ok. 2 min
    Polecenie z arkusza

    Należy stosować znaczące nazewnictwo zmiennych w języku polskim lub angielskim.

    Funkcja e() w baza.php jest dostępna na obu stronach i we wszystkich plikach dołączanych. htmlspecialchars z ENT_QUOTES zamienia <, >, & i cudzysłowy na encje, więc tekst z bazy nie rozbije atrybutu ani nie wykona się jako skrypt (XSS). Typ ?string przyjmuje też null - kolumny nazwaCechy, wartoscCechy i opis mogą być puste.

    baza.php PHP zmienione: 5 linii
    <?php
    // Połączenie z bazą i funkcje pobierające dane - wspólne dla obu stron
    $config = require 'config.php';
    
    mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
    
    set_exception_handler(function (Throwable $error): void {
        error_log((string) $error);
        http_response_code(500);
        echo 'Baza danych jest chwilowo niedostępna. Spróbuj ponownie później.';
    });
    
    $db = new mysqli($config['host'], $config['user'], $config['password'], $config['database']);
    $db->set_charset('utf8mb4');
    
    function e(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');
    }

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

  36. Krok 36: Skrypt 1: zapytanie 3 w funkcji objectNames()

    Kryteria:

    R.5.3 Skrypty zawierają przynajmniej jedną instrukcję wysyłającą zapytanie do bazy danych: skrypt 2 zmodyfikowane zapytanie 3 - idRodzaj = 14 skrypt 1 zapytanie 3 skrypt 3 - zapytanie 1 skrypt 4 - zmodyfikowane zapytanie 2 - id pobrane z tablicy $_GET

    ok. 4 min
    Polecenie z arkusza

    Skrypt 1 na obu stronach: wysyła do bazy danych zapytanie 3.

    Zapytanie 3 mieszka w funkcji objectNames($db, $typeId) w baza.php (R.5.3), a strona tylko ją wywołuje: $waterfalls = objectNames($db, 10);.

    • prepare z ? w miejscu numeru rodzaju, bind_param('i', $typeId) - wartość jako liczba całkowita (i), osobno od treści SQL,
    • execute(), a get_result()->fetch_all(MYSQLI_ASSOC) odbiera wszystkie wiersze naraz,
    • array_column($rows, 'nazwa') wyciąga z wierszy samą kolumnę nazwa - funkcja zwraca zwykłą listę nazw.

    Typy przy parametrach (mysqli $db, int $typeId) i wyniku (array) mówią, czego funkcja oczekuje i co oddaje - pomyłka (np. numer jako napis z adresu) skończy się czytelnym błędem.

    baza.php PHP zmienione: 9 linii
    <?php
    // Połączenie z bazą i funkcje pobierające dane - wspólne dla obu stron
    $config = require 'config.php';
    
    mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
    
    set_exception_handler(function (Throwable $error): void {
        error_log((string) $error);
        http_response_code(500);
        echo 'Baza danych jest chwilowo niedostępna. Spróbuj ponownie później.';
    });
    
    $db = new mysqli($config['host'], $config['user'], $config['password'], $config['database']);
    $db->set_charset('utf8mb4');
    
    function e(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');
    }
    
    function objectNames(mysqli $db, int $typeId): array
    {
        $query = $db->prepare("SELECT nazwa FROM obiekty WHERE panstwo = 'Islandia' AND idRodzaj = ?");
        $query->bind_param('i', $typeId);
        $query->execute();
        $rows = $query->get_result()->fetch_all(MYSQLI_ASSOC);
        return array_column($rows, 'nazwa');
    }
    islandia.php PHP zmienione: 2 linie
    <?php
    require 'baza.php';
    
    $waterfalls = objectNames($db, 10);
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Islandia</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    <?php require 'naglowek.php'; ?>
        <main>
            <h2>Galeria</h2>
            <section>
            </section>
        </main>
    <?php require 'boczny.php'; ?>
    <?php require 'stopka.php'; ?>
    </body>
    </html>

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

  37. Krok 37: Skrypt 1: nazwy jako elementy listy

    Kryteria:

    R.5.4 Skrypt 1 lub skrypt 2 wyświetla wszystkie zwrócone zapytaniem nazwy jako elementy listy

    ok. 2 min
    Polecenie z arkusza

    Każda zwrócona zapytaniem nazwa jest wyświetlana jako element listy.

    Pętla stoi w boczny.php - jedynym miejscu z kodem bloku bocznego: foreach (...): … endforeach; i jeden <li> na nazwę przez e() (R.5.4). Plik dołączany widzi zmienne strony, która go dołącza, więc korzysta z $waterfalls. Dlatego obie strony muszą tę zmienną przygotować - obiekty.php dostaje tę samą linię z objectNames().

    Typowe błędy

    Strona, która dołącza boczny.php bez przygotowania $waterfalls, kończy się ostrzeżeniem „Undefined variable” i błędem pętli.

    boczny.php PHP zmienione: 3 linie
        <aside>
            <h3>Do zwiedzania</h3>
            <ul>
                <li>Wodospady:
                    <ol>
    <?php foreach ($waterfalls as $name): ?>
                        <li><?= e($name) ?></li>
    <?php endforeach; ?>
                    </ol>
                </li>
                <li>Siedliska zwierząt:
                    <ol>
                    </ol>
                </li>
            </ul>
        </aside>
    obiekty.php PHP zmienione: 2 linie
    <?php
    require 'baza.php';
    
    $waterfalls = objectNames($db, 10);
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Islandia</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    <?php require 'naglowek.php'; ?>
        <main>
            <h2>Opis miejsca</h2>
            <section>
            </section>
        </main>
    <?php require 'boczny.php'; ?>
    <?php require 'stopka.php'; ?>
    </body>
    </html>
    Podgląd strony po tym kroku (islandia.php)
  38. Krok 38: Skrypt 2: siedliska zwierząt na obu stronach

    Kryteria: ,

    R.5.3 Skrypty zawierają przynajmniej jedną instrukcję wysyłającą zapytanie do bazy danych: skrypt 2 zmodyfikowane zapytanie 3 - idRodzaj = 14 skrypt 1 zapytanie 3 skrypt 3 - zapytanie 1 skrypt 4 - zmodyfikowane zapytanie 2 - id pobrane z tablicy $_GET

    R.5.4 Skrypt 1 lub skrypt 2 wyświetla wszystkie zwrócone zapytaniem nazwy jako elementy listy

    ok. 2 min
    Polecenie z arkusza

    Skrypt 2 na obu stronach: wysyła do bazy danych zapytanie 3, zmodyfikowane tak, że w warunku sprawdzane jest pole idRodzaj równe 14. Każda zwrócona zapytaniem nazwa jest wyświetlana jako element listy.

    „Zmodyfikowane zapytanie” to ta sama funkcja z innym argumentem: objectNames($db, 14). Jedna linia w każdej stronie i druga pętla w boczny.php (R.5.3, R.5.4) - blok boczny jest gotowy na obu stronach naraz, bo istnieje w jednym pliku.

    boczny.php PHP zmienione: 3 linie
        <aside>
            <h3>Do zwiedzania</h3>
            <ul>
                <li>Wodospady:
                    <ol>
    <?php foreach ($waterfalls as $name): ?>
                        <li><?= e($name) ?></li>
    <?php endforeach; ?>
                    </ol>
                </li>
                <li>Siedliska zwierząt:
                    <ol>
    <?php foreach ($habitats as $name): ?>
                        <li><?= e($name) ?></li>
    <?php endforeach; ?>
                    </ol>
                </li>
            </ul>
        </aside>
    islandia.php PHP zmienione: 1 linia
    <?php
    require 'baza.php';
    
    $waterfalls = objectNames($db, 10);
    $habitats = objectNames($db, 14);
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Islandia</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    <?php require 'naglowek.php'; ?>
        <main>
            <h2>Galeria</h2>
            <section>
            </section>
        </main>
    <?php require 'boczny.php'; ?>
    <?php require 'stopka.php'; ?>
    </body>
    </html>
    obiekty.php PHP zmienione: 1 linia
    <?php
    require 'baza.php';
    
    $waterfalls = objectNames($db, 10);
    $habitats = objectNames($db, 14);
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Islandia</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    <?php require 'naglowek.php'; ?>
        <main>
            <h2>Opis miejsca</h2>
            <section>
            </section>
        </main>
    <?php require 'boczny.php'; ?>
    <?php require 'stopka.php'; ?>
    </body>
    </html>
    Podgląd strony po tym kroku (islandia.php)
  39. Krok 39: Skrypt 3: zapytanie 1 w funkcji gallery()

    Kryteria:

    R.5.3 Skrypty zawierają przynajmniej jedną instrukcję wysyłającą zapytanie do bazy danych: skrypt 2 zmodyfikowane zapytanie 3 - idRodzaj = 14 skrypt 1 zapytanie 3 skrypt 3 - zapytanie 1 skrypt 4 - zmodyfikowane zapytanie 2 - id pobrane z tablicy $_GET

    ok. 1 min
    Polecenie z arkusza

    Skrypt 3 na stronie islandia.php: wysyła do bazy danych zapytanie 1.

    Zapytanie 1 nie ma danych z zewnątrz, więc wystarcza $db->query(...), a fetch_all(MYSQLI_ASSOC) zwraca wszystkie wiersze z nazwami kolumn (R.5.3). Strona woła gallery($db).

    baza.php PHP zmienione: 6 linii
    <?php
    // Połączenie z bazą i funkcje pobierające dane - wspólne dla obu stron
    $config = require 'config.php';
    
    mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
    
    set_exception_handler(function (Throwable $error): void {
        error_log((string) $error);
        http_response_code(500);
        echo 'Baza danych jest chwilowo niedostępna. Spróbuj ponownie później.';
    });
    
    $db = new mysqli($config['host'], $config['user'], $config['password'], $config['database']);
    $db->set_charset('utf8mb4');
    
    function e(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');
    }
    
    function objectNames(mysqli $db, int $typeId): array
    {
        $query = $db->prepare("SELECT nazwa FROM obiekty WHERE panstwo = 'Islandia' AND idRodzaj = ?");
        $query->bind_param('i', $typeId);
        $query->execute();
        $rows = $query->get_result()->fetch_all(MYSQLI_ASSOC);
        return array_column($rows, 'nazwa');
    }
    
    function gallery(mysqli $db): array
    {
        return $db->query("SELECT idObiekt, plik, nazwa FROM obiekty WHERE panstwo = 'Islandia'")
            ->fetch_all(MYSQLI_ASSOC);
    }
    islandia.php PHP zmienione: 1 linia
    <?php
    require 'baza.php';
    
    $waterfalls = objectNames($db, 10);
    $habitats = objectNames($db, 14);
    $gallery = gallery($db);
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Islandia</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    <?php require 'naglowek.php'; ?>
        <main>
            <h2>Galeria</h2>
            <section>
            </section>
        </main>
    <?php require 'boczny.php'; ?>
    <?php require 'stopka.php'; ?>
    </body>
    </html>

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

  40. Krok 40: Skrypt 3: obraz z każdego wiersza

    Kryteria: ,

    R.5.6 Skrypt 3 dla każdego zwróconego zapytaniem wiersza wyświetla obraz, którego źródłem jest zwrócone pole plik

    R.2.7 W bloku głównym lub bloku sekcji znajduje się przynajmniej jeden obraz wraz z tekstem alternatywnym (w skrypcie lub HTML )

    ok. 2 min
    Polecenie z arkusza

    Odnośnik jest obrazem, dla którego źródło to zwrócone pole plik, tekst alternatywny to zwrócone pole nazwa.

    W sekcji pętla szablonu po $gallery: <img> ze źródłem z pola plik i tekstem alternatywnym z pola nazwa (R.5.6, R.2.7).

    islandia.php PHP zmienione: 3 linie
    <?php
    require 'baza.php';
    
    $waterfalls = objectNames($db, 10);
    $habitats = objectNames($db, 14);
    $gallery = gallery($db);
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Islandia</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    <?php require 'naglowek.php'; ?>
        <main>
            <h2>Galeria</h2>
            <section>
    <?php foreach ($gallery as $item): ?>
                <img src="<?= e($item['plik']) ?>" alt="<?= e($item['nazwa']) ?>">
    <?php endforeach; ?>
            </section>
        </main>
    <?php require 'boczny.php'; ?>
    <?php require 'stopka.php'; ?>
    </body>
    </html>
    Podgląd strony po tym kroku (islandia.php)
  41. Krok 41: Skrypt 3: dymek z nazwą i klasa miniatury

    Kryteria:

    R.3.5 Przynajmniej jeden obraz ze strony islandia.php ma przypisaną klasę miniatury oraz atrybut title

    ok. 1 min
    Polecenie z arkusza

    Dodatkowo pole nazwa jest tekstem pojawiającym się w postaci dymka, gdy kursor znajdzie się na obrazie (ilustracja 2, czwarty obraz). Obraz ma przypisaną klasę miniatury.

    Dymek to atrybut title, a klasa miniatury podpina regułę z CSS (R.3.5).

    islandia.php PHP zmienione: 1 linia
    <?php
    require 'baza.php';
    
    $waterfalls = objectNames($db, 10);
    $habitats = objectNames($db, 14);
    $gallery = gallery($db);
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Islandia</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    <?php require 'naglowek.php'; ?>
        <main>
            <h2>Galeria</h2>
            <section>
    <?php foreach ($gallery as $item): ?>
                <img src="<?= e($item['plik']) ?>" alt="<?= e($item['nazwa']) ?>" title="<?= e($item['nazwa']) ?>" class="miniatury">
    <?php endforeach; ?>
            </section>
        </main>
    <?php require 'boczny.php'; ?>
    <?php require 'stopka.php'; ?>
    </body>
    </html>
    Podgląd strony po tym kroku (islandia.php)
  42. Krok 42: Skrypt 3: odnośnik z parametrem GET

    Kryteria: ,

    R.3.4 Na stronie islandia.php umieszczono przynajmniej jeden odnośnik, którego kliknięcie powoduje otwarcie strony obiekty.php oraz zostaje wysłane żądanie metodą GET z jednym parametrem

    R.5.5 Skrypt 3 dla każdego zwróconego zapytaniem wiersza wstawia idObiekt jako wartość parametru przesłanego metodą GET

    ok. 2 min
    Polecenie z arkusza

    Dla każdego zwróconego zapytaniem wiersza wypisuje odnośnik prowadzący do strony obiekty.php oraz przekazujący do tej strony metodą GET parametr o wartości równej polu idObiekt.

    Odnośnik obiekty.php?id=… wokół obrazu, wartość parametru to idObiekt (R.3.4, R.5.5). Odnośnik i obraz w jednej linii kodu - spacje wewnątrz <a> przeglądarka podkreśliłaby jako kreskę.

    islandia.php PHP zmienione: 1 linia
    <?php
    require 'baza.php';
    
    $waterfalls = objectNames($db, 10);
    $habitats = objectNames($db, 14);
    $gallery = gallery($db);
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Islandia</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    <?php require 'naglowek.php'; ?>
        <main>
            <h2>Galeria</h2>
            <section>
    <?php foreach ($gallery as $item): ?>
                <a href="obiekty.php?id=<?= e($item['idObiekt']) ?>"><img src="<?= e($item['plik']) ?>" alt="<?= e($item['nazwa']) ?>" title="<?= e($item['nazwa']) ?>" class="miniatury"></a>
    <?php endforeach; ?>
            </section>
        </main>
    <?php require 'boczny.php'; ?>
    <?php require 'stopka.php'; ?>
    </body>
    </html>
    Podgląd strony po tym kroku (islandia.php)
  43. Krok 43: Zamknięcie połączenia na stronie islandia.php

    Kryteria:

    R.5.1 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy islandia oraz ostatnią operacją na bazie jest jej zamknięcie (wystarczy na jednej stronie )

    ok. 1 min
    Polecenie z arkusza

    Na końcu należy zamknąć połączenie z bazą.

    $db->close() - obiektowy odpowiednik mysqli_close($db) (R.5.1). Stoi zaraz po pobraniu wszystkich danych: szablon i pliki dołączane korzystają już tylko z tablic, więc połączenie nie musi czekać na koniec strony.

    islandia.php PHP zmienione: 1 linia
    <?php
    require 'baza.php';
    
    $waterfalls = objectNames($db, 10);
    $habitats = objectNames($db, 14);
    $gallery = gallery($db);
    $db->close();
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Islandia</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    <?php require 'naglowek.php'; ?>
        <main>
            <h2>Galeria</h2>
            <section>
    <?php foreach ($gallery as $item): ?>
                <a href="obiekty.php?id=<?= e($item['idObiekt']) ?>"><img src="<?= e($item['plik']) ?>" alt="<?= e($item['nazwa']) ?>" title="<?= e($item['nazwa']) ?>" class="miniatury"></a>
    <?php endforeach; ?>
            </section>
        </main>
    <?php require 'boczny.php'; ?>
    <?php require 'stopka.php'; ?>
    </body>
    </html>

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

  44. Krok 44: Skrypt 4: identyfikator z adresu, sprawdzony

    ok. 2 min
    Polecenie z arkusza

    Skrypt 4 na stronie obiekty.php: wysyła do bazy danych zapytanie 2, zmodyfikowane w ten sposób, że w warunku jest sprawdzane id obiektu przesłane jako parametr metody GET ze strony islandia.php.

    Wartość z $_GET['id'] przechodzi przez filter_var z FILTER_VALIDATE_INT i min_range 1: ?id=53 daje liczbę 53, a ?id=abc, ?id=-5 albo brak parametru (?? null) - false, bez ostrzeżeń PHP. Funkcja place() z następnego kroku przyjmuje tylko int, więc do bazy nie da się przekazać niczego innego.

    obiekty.php PHP zmienione: 2 linie
    <?php
    require 'baza.php';
    
    $waterfalls = objectNames($db, 10);
    $habitats = objectNames($db, 14);
    
    $objectId = filter_var($_GET['id'] ?? null, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Islandia</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    <?php require 'naglowek.php'; ?>
        <main>
            <h2>Opis miejsca</h2>
            <section>
            </section>
        </main>
    <?php require 'boczny.php'; ?>
    <?php require 'stopka.php'; ?>
    </body>
    </html>
    Podgląd strony po tym kroku (obiekty.php)
  45. Krok 45: Skrypt 4: zapytanie 2 w funkcji place()

    Kryteria:

    R.5.3 Skrypty zawierają przynajmniej jedną instrukcję wysyłającą zapytanie do bazy danych: skrypt 2 zmodyfikowane zapytanie 3 - idRodzaj = 14 skrypt 1 zapytanie 3 skrypt 3 - zapytanie 1 skrypt 4 - zmodyfikowane zapytanie 2 - id pobrane z tablicy $_GET

    ok. 3 min
    Polecenie z arkusza

    Wysyła do bazy danych zapytanie 2, zmodyfikowane w ten sposób, że w warunku jest sprawdzane id obiektu przesłane jako parametr metody GET ze strony islandia.php.

    Zapytanie 2 z ? w miejscu identyfikatora i bind_param('i', …) (R.5.3) - wartość z adresu nigdy nie staje się częścią polecenia SQL. fetch_assoc() zwraca wiersz albo null, gdy takiego miejsca nie ma; typ wyniku ?array mówi to wprost.

    Na stronie operator warunkowy: dla złego identyfikatora funkcja w ogóle nie jest wołana, a $place jest null.

    baza.php PHP zmienione: 13 linii
    <?php
    // Połączenie z bazą i funkcje pobierające dane - wspólne dla obu stron
    $config = require 'config.php';
    
    mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
    
    set_exception_handler(function (Throwable $error): void {
        error_log((string) $error);
        http_response_code(500);
        echo 'Baza danych jest chwilowo niedostępna. Spróbuj ponownie później.';
    });
    
    $db = new mysqli($config['host'], $config['user'], $config['password'], $config['database']);
    $db->set_charset('utf8mb4');
    
    function e(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');
    }
    
    function objectNames(mysqli $db, int $typeId): array
    {
        $query = $db->prepare("SELECT nazwa FROM obiekty WHERE panstwo = 'Islandia' AND idRodzaj = ?");
        $query->bind_param('i', $typeId);
        $query->execute();
        $rows = $query->get_result()->fetch_all(MYSQLI_ASSOC);
        return array_column($rows, 'nazwa');
    }
    
    function gallery(mysqli $db): array
    {
        return $db->query("SELECT idObiekt, plik, nazwa FROM obiekty WHERE panstwo = 'Islandia'")
            ->fetch_all(MYSQLI_ASSOC);
    }
    
    function place(mysqli $db, int $objectId): ?array
    {
        $query = $db->prepare(
            'SELECT o.plik, o.nazwa, o.nazwaCechy, o.wartoscCechy, o.opis, r.rodzaj
             FROM obiekty AS o
             JOIN rodzaje AS r ON r.idRodzaj = o.idRodzaj
             WHERE o.idObiekt = ?'
        );
        $query->bind_param('i', $objectId);
        $query->execute();
        return $query->get_result()->fetch_assoc();
    }
    obiekty.php PHP zmienione: 1 linia
    <?php
    require 'baza.php';
    
    $waterfalls = objectNames($db, 10);
    $habitats = objectNames($db, 14);
    
    $objectId = filter_var($_GET['id'] ?? null, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
    $place = $objectId === false ? null : place($db, $objectId);
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Islandia</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    <?php require 'naglowek.php'; ?>
        <main>
            <h2>Opis miejsca</h2>
            <section>
            </section>
        </main>
    <?php require 'boczny.php'; ?>
    <?php require 'stopka.php'; ?>
    </body>
    </html>

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

  46. Krok 46: Skrypt 4: obraz obiektu

    Kryteria:

    R.5.7 Skrypt 4 wyświetla obraz oraz dane których id zostało przesłane metodą GET

    ok. 1 min
    Polecenie z arkusza

    Na podstawie zwróconych zapytaniem danych: wyświetla obraz, którego źródłem jest pole plik, a tekstem alternatywnym pole nazwa.

    Treść sekcji w warunku if ($place), obraz z pól plik i nazwa (R.5.7). W podglądzie Godafoss z ilustracji 3.

    obiekty.php PHP zmienione: 3 linie
    <?php
    require 'baza.php';
    
    $waterfalls = objectNames($db, 10);
    $habitats = objectNames($db, 14);
    
    $objectId = filter_var($_GET['id'] ?? null, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
    $place = $objectId === false ? null : place($db, $objectId);
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Islandia</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    <?php require 'naglowek.php'; ?>
        <main>
            <h2>Opis miejsca</h2>
            <section>
    <?php if ($place): ?>
                <img src="<?= e($place['plik']) ?>" alt="<?= e($place['nazwa']) ?>">
    <?php endif; ?>
            </section>
        </main>
    <?php require 'boczny.php'; ?>
    <?php require 'stopka.php'; ?>
    </body>
    </html>
    Podgląd strony po tym kroku (obiekty.php)
  47. Krok 47: Skrypt 4: nazwa w nagłówku drugiego stopnia

    Kryteria:

    R.2.8 Na stronie obiekty.php w bloku głównym lub bloku sekcji znajdują się elementy <h2>, <h3> i przynajmniej jeden <p> (w skrypcie lub HTML )

    ok. 1 min
    Polecenie z arkusza

    W nagłówku drugiego stopnia wyświetla wartość pola nazwa.

    <h2> z nazwą miejsca (R.2.8).

    obiekty.php PHP zmienione: 1 linia
    <?php
    require 'baza.php';
    
    $waterfalls = objectNames($db, 10);
    $habitats = objectNames($db, 14);
    
    $objectId = filter_var($_GET['id'] ?? null, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
    $place = $objectId === false ? null : place($db, $objectId);
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Islandia</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    <?php require 'naglowek.php'; ?>
        <main>
            <h2>Opis miejsca</h2>
            <section>
    <?php if ($place): ?>
                <img src="<?= e($place['plik']) ?>" alt="<?= e($place['nazwa']) ?>">
                <h2><?= e($place['nazwa']) ?></h2>
    <?php endif; ?>
            </section>
        </main>
    <?php require 'boczny.php'; ?>
    <?php require 'stopka.php'; ?>
    </body>
    </html>
    Podgląd strony po tym kroku (obiekty.php)
  48. Krok 48: Skrypt 4: rodzaj w nagłówku trzeciego stopnia

    Kryteria:

    R.2.8 Na stronie obiekty.php w bloku głównym lub bloku sekcji znajdują się elementy <h2>, <h3> i przynajmniej jeden <p> (w skrypcie lub HTML )

    ok. 1 min
    Polecenie z arkusza

    W nagłówku trzeciego stopnia wyświetla wartość pola rodzaj.

    <h3> z polem rodzaj z tabeli dołączonej relacją.

    obiekty.php PHP zmienione: 1 linia
    <?php
    require 'baza.php';
    
    $waterfalls = objectNames($db, 10);
    $habitats = objectNames($db, 14);
    
    $objectId = filter_var($_GET['id'] ?? null, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
    $place = $objectId === false ? null : place($db, $objectId);
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Islandia</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    <?php require 'naglowek.php'; ?>
        <main>
            <h2>Opis miejsca</h2>
            <section>
    <?php if ($place): ?>
                <img src="<?= e($place['plik']) ?>" alt="<?= e($place['nazwa']) ?>">
                <h2><?= e($place['nazwa']) ?></h2>
                <h3><?= e($place['rodzaj']) ?></h3>
    <?php endif; ?>
            </section>
        </main>
    <?php require 'boczny.php'; ?>
    <?php require 'stopka.php'; ?>
    </body>
    </html>
    Podgląd strony po tym kroku (obiekty.php)
  49. Krok 49: Skrypt 4: cecha i jej wartość w paragrafie

    Kryteria:

    R.2.8 Na stronie obiekty.php w bloku głównym lub bloku sekcji znajdują się elementy <h2>, <h3> i przynajmniej jeden <p> (w skrypcie lub HTML )

    ok. 1 min
    Polecenie z arkusza

    W paragrafie wyświetla tekst „<nazwaCechy>: <wartoscCechy>”, gdzie w <> umieszczono wartości zwrócone zapytaniem.

    Dwukropek w HTML-u, dwie wartości z bazy: „wysokość: 30”. mysqli z get_result oddaje liczby jako liczby - e() zamienia je na tekst.

    obiekty.php PHP zmienione: 1 linia
    <?php
    require 'baza.php';
    
    $waterfalls = objectNames($db, 10);
    $habitats = objectNames($db, 14);
    
    $objectId = filter_var($_GET['id'] ?? null, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
    $place = $objectId === false ? null : place($db, $objectId);
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Islandia</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    <?php require 'naglowek.php'; ?>
        <main>
            <h2>Opis miejsca</h2>
            <section>
    <?php if ($place): ?>
                <img src="<?= e($place['plik']) ?>" alt="<?= e($place['nazwa']) ?>">
                <h2><?= e($place['nazwa']) ?></h2>
                <h3><?= e($place['rodzaj']) ?></h3>
                <p><?= e($place['nazwaCechy']) ?>: <?= e($place['wartoscCechy']) ?></p>
    <?php endif; ?>
            </section>
        </main>
    <?php require 'boczny.php'; ?>
    <?php require 'stopka.php'; ?>
    </body>
    </html>
    Podgląd strony po tym kroku (obiekty.php)
  50. Krok 50: Skrypt 4: opis i strona dla nieznanego miejsca

    Kryteria:

    R.5.7 Skrypt 4 wyświetla obraz oraz dane których id zostało przesłane metodą GET

    ok. 2 min
    Polecenie z arkusza

    W paragrafie wyświetla pole opis.

    Ostatni paragraf to opis (R.5.7). Gałąź else mówi wprost, że miejsca nie znaleziono, a http_response_code(404) - ustawiony przed HTML-em - zgłasza przeglądarce i wyszukiwarkom brak strony.

    obiekty.php PHP zmienione: 7 linii
    <?php
    require 'baza.php';
    
    $waterfalls = objectNames($db, 10);
    $habitats = objectNames($db, 14);
    
    $objectId = filter_var($_GET['id'] ?? null, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
    $place = $objectId === false ? null : place($db, $objectId);
    
    if ($place === null) {
        http_response_code(404);
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Islandia</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    <?php require 'naglowek.php'; ?>
        <main>
            <h2>Opis miejsca</h2>
            <section>
    <?php if ($place): ?>
                <img src="<?= e($place['plik']) ?>" alt="<?= e($place['nazwa']) ?>">
                <h2><?= e($place['nazwa']) ?></h2>
                <h3><?= e($place['rodzaj']) ?></h3>
                <p><?= e($place['nazwaCechy']) ?>: <?= e($place['wartoscCechy']) ?></p>
                <p><?= e($place['opis']) ?></p>
    <?php else: ?>
                <p>Nie znaleziono takiego miejsca - wybierz zdjęcie w galerii.</p>
    <?php endif; ?>
            </section>
        </main>
    <?php require 'boczny.php'; ?>
    <?php require 'stopka.php'; ?>
    </body>
    </html>
    Podgląd strony po tym kroku (obiekty.php)
  51. Krok 51: Zamknięcie połączenia na stronie obiekty.php

    Kryteria:

    R.5.1 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy islandia oraz ostatnią operacją na bazie jest jej zamknięcie (wystarczy na jednej stronie )

    ok. 1 min
    Polecenie z arkusza

    Na końcu należy zamknąć połączenie z bazą.

    $db->close() po ostatnim zapytaniu strony (R.5.1). Zapytania przygotowane w funkcjach są zmiennymi lokalnymi - znikają po wyjściu z funkcji, więc nic nie trzyma już połączenia.

    obiekty.php PHP zmienione: 1 linia
    <?php
    require 'baza.php';
    
    $waterfalls = objectNames($db, 10);
    $habitats = objectNames($db, 14);
    
    $objectId = filter_var($_GET['id'] ?? null, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
    $place = $objectId === false ? null : place($db, $objectId);
    $db->close();
    
    if ($place === null) {
        http_response_code(404);
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Islandia</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    <?php require 'naglowek.php'; ?>
        <main>
            <h2>Opis miejsca</h2>
            <section>
    <?php if ($place): ?>
                <img src="<?= e($place['plik']) ?>" alt="<?= e($place['nazwa']) ?>">
                <h2><?= e($place['nazwa']) ?></h2>
                <h3><?= e($place['rodzaj']) ?></h3>
                <p><?= e($place['nazwaCechy']) ?>: <?= e($place['wartoscCechy']) ?></p>
                <p><?= e($place['opis']) ?></p>
    <?php else: ?>
                <p>Nie znaleziono takiego miejsca - wybierz zdjęcie w galerii.</p>
    <?php endif; ?>
            </section>
        </main>
    <?php require 'boczny.php'; ?>
    <?php require 'stopka.php'; ?>
    </body>
    </html>

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

  52. Etap 5: Porównanie z pozostałymi wersjami

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

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

    ok. 3 min
    Polecenie z arkusza

    Czas przeznaczony na wykonanie zadania wynosi 150 minut.

    Wszystkie trzy wersje dają komplet 37 punktów, jeśli egzaminator znajdzie kod. Różnice:

    wersja podstawowanowoczesny kodta wersja
    pliki PHP2 strony2 strony + config.php2 krótkie strony + baza.php, 3 części stron, config.php
    bibliotekafunkcje mysqli_*PDOmysqli obiektowe
    SQLw stronachna górze strontylko w baza.php, w funkcjach
    blok bocznydwie kopiedwie kopiejeden plik boczny.php
    awaria bazybłąd PHPtry / catch na każdej stroniejedno set_exception_handler

    mysqli obiektowe i PDO są równie dobre; mysqli działa tylko z MySQL/MariaDB, PDO także z innymi bazami. Kto zna funkcje z tabeli 1, przesiada się na mysqli obiektowe najłatwiej: mysqli_query($db, $sql) to $db->query($sql).

    Na egzaminie wybierz wersję podstawową albo nowoczesną: klucz każe sprawdzać R.5.1-R.5.3 i R.5.5 „w kodzie źródłowym strony”, a tu połączenie i zapytania są w innym pliku. Tę organizację stosuj w projektach i na zajęciach - dodanie trzeciej podstrony to nowy plik z dwiema linijkami PHP i dwoma require.

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

  53. Etap 6: Oddanie pracy

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

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

    ok. 5 min
    Polecenie z arkusza

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

    W folderze z numerem zdającego oprócz plików z arkusza (dziewięć zdjęć, import.jpg, islandia.php, obiekty.php, kw1.png … kw4.png, kwerendy.txt, przeglądarka.txt, styl.css) muszą być wszystkie pliki dołączane: baza.php, config.php, naglowek.php, boczny.php, stopka.php. Bez nich strony się nie wyświetlą. Płytę sprawdź po nagraniu.

    przeglądarka.txt Tekst nowy plik
    Google Chrome

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

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.