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

Rozwiązanie krok po kroku: Portal Zdrowy bazarek (Nowoczesny kod (grid, PDO))

To drugi sposób rozwiązania tego samego arkusza: portal „Zdrowy bazarek”, baza bazar, jedna strona index.php z paskiem owoców, szyldem i formularzem zamówienia oraz trzy skrypty PHP. 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 (display: grid) w bloku głównym: blok boczny i sekcja dostają po 50% szerokości w jednej linii grid-template-columns,
  • kolory z arkusza w zmiennych CSS (--color-green: ForestGreen), więc każdy kolor jest wpisany raz,
  • PHP na PDO z wyjątkami i trybem pobierania tablic asocjacyjnych - $product['nazwa'] zamiast $row[1],
  • logika PHP na górze pliku, HTML na dole: najpierw wszystkie zapytania i zamknięcie połączenia, a w HTML tylko pętle foreach wypisujące gotowe dane,
  • bezpieczeństwo od pierwszej linijki: filter_var sprawdza identyfikator i wagę, zanim cokolwiek trafi do bazy, zapytania przygotowane z parametrami nazwanymi, htmlspecialchars przy wyjściu (krótka funkcja escape()).

Kroki nadal idą zdanie po zdaniu za treścią arkusza. Podgląd strony w każdym kroku z PHP to wynik prawdziwego uruchomienia kodu z tego kroku w PHP 8.2 na bazie z arkusza; od Skryptu 3 pokazuje zamówienie z ilustracji 5 (pietruszka, 30 kg). Zdjęcia i baza pochodzą z prawdziwego archiwum pliki3-1.

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 bazar

    ok. 2 min
    Polecenie z arkusza

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

    Archiwum pliki3-1.7z z pulpitu ma hasło baz@rEk100% (małpa zamiast „a”, wielkie E, znak procentu na końcu). W phpMyAdmin: Nowa, nazwa bazar, porównywanie utf8_polish_ci. To samo daje jedno polecenie w zakładce SQL:

    CREATE DATABASE bazar CHARACTER SET utf8 COLLATE utf8_polish_ci;
    

    Nazwa bazy to bazar, nie „bazarek” - „Zdrowy bazarek” to tytuł strony.

    Typowe błędy

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

  2. Krok 2: Import tabel z pliku baza-nowa.sql

    ok. 2 min
    Polecenie z arkusza

    Z rozpakowanego archiwum zaimportuj tabele z pliku baza-nowa.sql do utworzonej bazy

    Zaznacz bazę bazar w lewym panelu, potem Import → baza-nowa.sql → Wykonaj. Powstają trzy tabele z ilustracji 1: towar (rodzaj, nazwa, cena i nazwa pliku ze zdjęciem), sklep i zamowienie - ta ostatnia kluczami obcymi wskazuje towar i sklep i zapisuje liczbę kilogramów.

    Typowe błędy

    Import bez zaznaczonej bazy kończy się błędem „nie wybrano bazy danych” albo ląduje w innej bazie.

  3. Krok 3: Zrzut ekranu import.png

    Kryteria:

    R.1.1 Wykonany import tabel do bazy danych bazar - 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. Na zrzucie ma być widać komunikat o udanym imporcie albo trzy tabele w drzewku bazy. W tym arkuszu wszystkie zrzuty są w 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: dziesięć pierwszych towarów

    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 10 pierwszych rekordów z tabeli towar , jedynie pola nazwa i plik. W pliku z kwerendami lub na zrzucie istnieje zapis np. SELECT nazwa, plik FROM towar LIMIT 10; oraz na zrzucie widocznych jest dokładnie 10 wierszy z nazwami: gruszka, jabłko, banan, ananas, mango, winogrona, jagody, maliny, pomarańcza, cytryna

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 1: wybierające 10 pierwszych rekordów z tabeli towar, jedynie pola nazwa i plik

    • SELECT nazwa, plik - dokładnie dwa pola, bez *,
    • FROM towar,
    • LIMIT 10 - tylko dziesięć pierwszych wierszy.

    Każda klauzula w osobnej linii - dla bazy bez różnicy, a zapytanie czyta się od góry do dołu. Wynik: gruszka, jabłko, banan, ananas, mango, winogrona, jagody, maliny, pomarańcza, cytryna. Zapytania zapisuj w kwerendy.txt z komentarzem -- nad każdym (to też poprawny SQL), zrzut jako kw1.png.

    Typowe błędy

    „Pierwsze” znaczy tu „w kolejności, w jakiej baza je oddaje” - bez ORDER BY to kolejność wstawiania. Dopisanie ORDER BY nazwa dałoby inne dziesięć towarów niż w kluczu.

    kwerendy.txt Tekst nowy plik
    -- Zapytanie 1
    SELECT nazwa, plik
    FROM towar
    LIMIT 10;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 4
  5. Krok 5: Zapytanie 2: identyfikatory i nazwy towarów

    Kryteria:

    R.1.4 Wykonane zapytanie 2 wybierające jedynie id i nazwę towarów. W pliku z kwerendami lub na zrzucie istnieje zapis np. SELECT id, nazwa FROM towar; oraz na zrzucie widoczne są wyniki działania zapytania (nie wszystkie rekordy będą widoczne, 39 lub 40 rekordów)

    ok. 1 min
    Polecenie z arkusza

    Zapytanie 2: wybierające jedynie identyfikator i nazwę towarów

    Dwa pola bez warunku: 39 wierszy (klucz: 39 lub 40). Cały wynik nie zmieści się na ekranie - klucz o tym wie. Z tego zapytania Skrypt 2 zrobi listę rozwijaną: id trafi do value opcji, nazwa będzie jej widoczną treścią. Zrzut: kw2.png.

    kwerendy.txt Tekst zmienione: 4 linie
    -- Zapytanie 1
    SELECT nazwa, plik
    FROM towar
    LIMIT 10;
    
    -- Zapytanie 2
    SELECT id, nazwa
    FROM towar;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 5
  6. Krok 6: Zapytanie 3: towar o identyfikatorze 1

    Kryteria:

    R.1.5 Wykonane zapytanie 3 wybierające jedynie rodzaj, nazwę i cenę towaru dla id = 1. W pliku z kwerendami lub na zrzucie istnieje zapis np. SELECT rodzaj, nazwa, cena FROM towar WHERE id = 1; oraz na zrzucie widoczny jest dokładnie jeden rekord: owoc gruszka 5.00

    ok. 1 min
    Polecenie z arkusza

    Zapytanie 3: wybierające jedynie rodzaj, nazwę i cenę towaru o identyfikatorze 1

    Warunek na klucz główny zwraca najwyżej jeden wiersz: owoc gruszka 5.00. Kolumna cena ma typ DECIMAL, dlatego baza pokazuje dwa miejsca po przecinku. W Skrypcie 3 w miejscu 1 stanie parametr z identyfikatorem z formularza. Zrzut: kw3.png.

    kwerendy.txt Tekst zmienione: 5 linii
    -- Zapytanie 1
    SELECT nazwa, plik
    FROM towar
    LIMIT 10;
    
    -- Zapytanie 2
    SELECT id, nazwa
    FROM towar;
    
    -- Zapytanie 3
    SELECT rodzaj, nazwa, cena
    FROM towar
    WHERE id = 1;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 6
  7. Krok 7: Zapytanie 4: zamówienie 20 kg pomarańczy

    Kryteria:

    R.1.6 Wykonane zapytanie 4 wstawiające do tabeli zamowienie rekord o wartościach: towar o id 9, sklep o id 2, liczba kilogramów 20, klucz główny nadawany automatycznie. W pliku z kwerendami lub na zrzucie istnieje zapis np. INSERT INTO zamowienie(id_towar, id_sklep, liczba_kg) VALUES (9, 2, 20); (klucz główny pominięty lub NULL) oraz na zrzucie jest widoczny komunikat o poprawnie wykonanym zapytaniu lub tabela z dodanym rekordem

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 4: wstawiające do tabeli zamowienie rekord o wartościach: towar o identyfikatorze 9, sklep o identyfikatorze 2, liczba kilogramów 20, klucz główny nadawany automatycznie

    Lista kolumn bez id - AUTO_INCREMENT nada następny numer (tu 49). Wymienienie kolumn z nazwy zamiast INSERT INTO zamowienie VALUES (...) chroni przed pomyłką w kolejności i działa nawet wtedy, gdy ktoś później doda kolumnę do tabeli. Zrzut kw4.png: komunikat o wstawieniu wiersza albo tabela z nowym rekordem.

    Typowe błędy

    Wpisany ręcznie klucz (VALUES (49, 9, 2, 20)) przeczy „kluczowi nadawanemu automatycznie” - pomiń id albo podaj NULL.

    kwerendy.txt Tekst zmienione: 4 linie
    -- Zapytanie 1
    SELECT nazwa, plik
    FROM towar
    LIMIT 10;
    
    -- Zapytanie 2
    SELECT id, nazwa
    FROM towar;
    
    -- Zapytanie 3
    SELECT rodzaj, nazwa, cena
    FROM towar
    WHERE id = 1;
    
    -- Zapytanie 4
    INSERT INTO zamowienie (id_towar, id_sklep, liczba_kg)
    VALUES (9, 2, 20);
    Podgląd strony po tym kroku
    Podgląd strony po kroku 7
  8. Etap 2: Grafika i strona HTML

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

    Krok 8: Grafika jabłka z przezroczystym tłem

    Kryteria:

    R.2.1 Grafika jablko.png lub jabłko.png ma przezroczyste tło

    ok. 4 min
    Polecenie z arkusza

    Dla grafiki jablko.png należy nadać tło przezroczyste i zapisać pod tą samą nazwą

    W GIMP-ie: Warstwa → Przezroczystość → Dodaj kanał alfa (bez kanału alfa usunięte piksele zamalowałyby się kolorem tła), potem różdżka kliknięta w kremowe tło i Delete. Różdżka zaznacza tylko piksele połączone z miejscem kliknięcia i podobne kolorem, więc jabłko, liść i cień zostają. Eksport do PNG. W podglądzie: oryginał i wynik na szachownicy oznaczającej przezroczystość - przezroczystych jest 14907 z 22 500 pikseli.

    Nazwa pliku: arkusz pisze jablko.png, ale w archiwum i w polu plik bazy jest jabłko.png z „ł”. Zapisz pod nazwą z archiwum - tej szuka Skrypt 1; klucz R.2.1 uznaje obie.

    Typowe błędy

    JPEG nie ma kanału przezroczystości - eksport do .jpg zamieni przezroczyste tło na białe.

    Podgląd strony po tym kroku
    Podgląd strony po kroku 8
  9. Krok 9: Grafika market.png: szerokość 300 px

    Kryteria:

    R.2.2 Grafika market.png przeskalowana z zachowaniem proporcji do szerokości dokładnie 300 px, odpowiadająca temu wysokość to 378 px ( ± 5 px)

    ok. 3 min
    Polecenie z arkusza

    Grafikę market.png należy przeskalować z zachowaniem proporcji do szerokości 300 px

    Oryginał ma 508 × 640 px. Obraz → Skaluj obraz, szerokość 300 px przy zamkniętym łańcuchu - wysokość przeliczy się na 378 px (640 · 300 / 508; klucz: 378 ±5). Eksport w PNG, bo szyld ma przezroczyste tło. Skalowanie w samym CSS (width: 300px przy obrazie) nie zmienia pliku i punktu nie daje.

    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 strony o nazwie index.php. Zapisana w języku HTML5

    <!DOCTYPE html> to deklaracja HTML5 - bez niej przeglądarka przechodzi w tryb zgodności ze starymi stronami i inaczej liczy wymiary bloków. Plik ma rozszerzenie .php, bo dojdą skrypty; na egzaminie leży w htdocs XAMPP-a i otwiera się go przez http://localhost/..., a nie przez dwuklik.

    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

    Kryteria:

    R.2.3 W pliku index.php znajduje się deklaracja HTML5 <!DOCTYPE HTML> i deklaracja języka zawartości witryny: 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 stronę polską wymową, a przeglądarka dobiera słownik i dzielenie wyrazów. Z <!DOCTYPE html> to jedno kryterium (R.2.3).

    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> - przeglądarka musi znać kodowanie, zanim przeczyta tytuł z polskimi literami. Sam plik też zapisz w UTF-8 (bez BOM - znak BOM przed <?php to wypisany tekst, który później blokuje nagłówki HTTP).

    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 i viewport

    Kryteria:

    R.2.4 W sekcji head znajduje się jawnie podany standard kodowania polskich znaków i tytuł strony "Zdrowy bazarek"

    ok. 1 min
    Polecenie z arkusza

    Tytuł strony „Zdrowy bazarek”

    <title> to tekst karty przeglądarki (z kodowaniem - R.2.4). Obok <meta name="viewport">: bez niej telefon udaje ekran szeroki na ok. 980 px i pomniejsza całą stronę. Arkusz tego nie wymaga, a nowoczesna strona zawsze ją ma.

    index.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>Zdrowy bazarek</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

    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"> - znacznik pusty, w HTML5 bez ukośnika />. Całe formatowanie ma pochodzić z tego pliku (R.3.2), więc w HTML nie ma ani jednego atrybutu style.

    index.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>Zdrowy bazarek</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  15. Krok 15: Bloki semantyczne

    Kryteria:

    R.2.5 W sekcji body zdefiniowany jest podział na bloki za pomocą semantycznych znaczników HTML5: header, nav, main->aside+section, 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ą 3

    Klucz R.2.5 wymienia znaczniki: header, nav, main z aside i section w środku, footer. Każdy niesie znaczenie: <nav> to blok nawigacyjny (tu pasek owoców, które w rozbudowanej wersji staną się odnośnikami), <aside> - treść poboczna, <main> - główna treść strony, jedna na stronie.

    Blok boczny i sekcja są jedynymi dziećmi <main> - dlatego siatka ustawiona na <main> rozłoży je w dwie kolumny bez żadnych dodatkowych klas.

    Typowe błędy

    Te same bloki zrobione z <div> wyglądają identycznie, ale R.2.5 sprawdza znaczniki w kodzie.

    index.php PHP zmienione: 12 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Zdrowy bazarek</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
        </header>
        <nav>
        </nav>
        <main>
            <aside>
            </aside>
            <section>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  16. Krok 16: Nagłówek strony

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku nagłówkowego: nagłówek pierwszego stopnia o treści „Zdrowy bazarek”

    <h1> - jedyny nagłówek pierwszego stopnia na stronie. Blok nawigacyjny zostaje pusty, zdjęcia wypisze Skrypt 1.

    index.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>Zdrowy bazarek</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zdrowy bazarek</h1>
        </header>
        <nav>
        </nav>
        <main>
            <aside>
            </aside>
            <section>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  17. Krok 17: Blok boczny: szyld

    Kryteria:

    R.2.7 W bloku bocznym umieszczony obraz z pliku market.png , z tekstem alternatywnym "bazarek"

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku bocznego: obraz market.png z tekstem alternatywnym „bazarek”

    Przeskalowany plik z alt="bazarek". Wymiarów w HTML nie trzeba podawać - plik ma już 300 px szerokości.

    index.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>Zdrowy bazarek</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zdrowy bazarek</h1>
        </header>
        <nav>
        </nav>
        <main>
            <aside>
                <img src="market.png" alt="bazarek">
            </aside>
            <section>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  18. Krok 18: Sekcja: akapit z poleceniem

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku sekcji: Paragraf o treści: „Wybierz owoc lub warzywo i podaj jego wagę:”

    Zwykły akapit <p> na początku sekcji - styl nada mu reguła selektora p.

    index.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>Zdrowy bazarek</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zdrowy bazarek</h1>
        </header>
        <nav>
        </nav>
        <main>
            <aside>
                <img src="market.png" alt="bazarek">
            </aside>
            <section>
                <p>Wybierz owoc lub warzywo i podaj jego wagę:</p>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  19. Krok 19: Formularz wysyłany metodą POST

    Kryteria: ,

    R.2.8 W bloku sekcji umieszczony formularz

    R.3.3 Formularz wysyła dane metodą POST do tego samego pliku (poprawne również bez atrybutu action)

    ok. 1 min
    Polecenie z arkusza

    Formularz wysyłający dane do tego samego pliku metodą bezpieczną

    <form method="post"> bez action - brak action oznacza „ten sam adres”, czyli dokładnie „do tego samego pliku” (R.3.3). „Metoda bezpieczna” w arkuszu to POST: dane jadą w treści żądania, a nie w adresie, więc nie zostają w historii przeglądarki. Jest też drugi powód, ważniejszy w tym zadaniu: formularz zapisuje zamówienie w bazie, a zmiany na serwerze wysyła się POST-em - GET jest od pobierania stron.

    index.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>Zdrowy bazarek</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zdrowy bazarek</h1>
        </header>
        <nav>
        </nav>
        <main>
            <aside>
                <img src="market.png" alt="bazarek">
            </aside>
            <section>
                <p>Wybierz owoc lub warzywo i podaj jego wagę:</p>
                <form method="post">
                </form>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  20. Krok 20: Lista rozwijana towarów

    ok. 1 min
    Polecenie z arkusza

    Lista rozwijana wypełniona skryptem 2

    <select name="product"> - pod nazwą product wybrany identyfikator trafi do $_POST. Na ilustracji lista nie ma podpisu, więc zamiast widocznej etykiety jest aria-label="Towar": czytnik ekranu przeczyta „Towar, lista rozwijana”, a wygląd się nie zmienia. Opcje wypisze Skrypt 2.

    index.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>Zdrowy bazarek</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zdrowy bazarek</h1>
        </header>
        <nav>
        </nav>
        <main>
            <aside>
                <img src="market.png" alt="bazarek">
            </aside>
            <section>
                <p>Wybierz owoc lub warzywo i podaj jego wagę:</p>
                <form method="post">
                    <select name="product" aria-label="Towar">
                    </select>
                </form>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  21. Krok 21: Pole liczby kilogramów

    Kryteria:

    R.3.5 Pole edycyjne jest typu number

    ok. 2 min
    Polecenie z arkusza

    Pole edycyjne przeznaczone do wpisywania jedynie liczb całkowitych

    type="number" (R.3.5) z trzema atrybutami, które opisują dane:

    • step="1" - krok 1 to wartość domyślna, ale zapisany jawnie mówi czytelnikowi kodu „tylko liczby całkowite”; przy próbie wysłania 2,5 przeglądarka zgłosi błąd,
    • min="1" - zamówienie na 0 albo -5 kg nie ma sensu,
    • required - formularz bez wagi nie wyjdzie z przeglądarki.

    To wygoda dla kupującego, nie zabezpieczenie: żądanie POST da się wysłać z pominięciem formularza, dlatego Skrypt 3 sprawdzi wagę jeszcze raz.

    index.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>Zdrowy bazarek</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zdrowy bazarek</h1>
        </header>
        <nav>
        </nav>
        <main>
            <aside>
                <img src="market.png" alt="bazarek">
            </aside>
            <section>
                <p>Wybierz owoc lub warzywo i podaj jego wagę:</p>
                <form method="post">
                    <select name="product" aria-label="Towar">
                    </select>
                    <input type="number" name="weight" min="1" step="1" required aria-label="Liczba kilogramów">
                </form>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  22. Krok 22: Przycisk „Zamów”

    Kryteria:

    R.3.4 Przycisk realizuje funkcję wysłania danych z formularza (input lub button z typem submit lub button bez typu)

    ok. 1 min
    Polecenie z arkusza

    Przycisk „Zamów” wysyłający dane z formularza do skryptu 3

    <button type="submit"> - typ zapisany jawnie, choć submit jest domyślny: przycisk bez typu wewnątrz formularza też wysyła dane, a jawny zapis nie zostawia wątpliwości (R.3.4).

    index.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>Zdrowy bazarek</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zdrowy bazarek</h1>
        </header>
        <nav>
        </nav>
        <main>
            <aside>
                <img src="market.png" alt="bazarek">
            </aside>
            <section>
                <p>Wybierz owoc lub warzywo i podaj jego wagę:</p>
                <form method="post">
                    <select name="product" aria-label="Towar">
                    </select>
                    <input type="number" name="weight" min="1" step="1" required aria-label="Liczba kilogramów">
                    <button type="submit">Zamów</button>
                </form>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  23. Krok 23: Stopka

    Kryteria:

    R.2.6 W bloku nagłówkowym umieszczony <h1>: "Zdrowy bazarek", w bloku sekcji <p>, w stopce <p> z numerem zdającego

    ok. 1 min
    Polecenie z arkusza

    Zawartość stopki: paragraf o treści: „Stronę opracował: ”, dalej wstawiony numer zdającego

    Akapit w <footer>. Tu stoi imię i nazwisko autora rozwiązania - na egzaminie wpisujesz w to miejsce swój numer zdającego (PESEL). Na stronie są już elementy z R.2.6: <h1>, akapit w sekcji i akapit w stopce.

    index.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>Zdrowy bazarek</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zdrowy bazarek</h1>
        </header>
        <nav>
        </nav>
        <main>
            <aside>
                <img src="market.png" alt="bazarek">
            </aside>
            <section>
                <p>Wybierz owoc lub warzywo i podaj jego wagę:</p>
                <form method="post">
                    <select name="product" aria-label="Towar">
                    </select>
                    <input type="number" name="weight" min="1" step="1" required aria-label="Liczba kilogramów">
                    <button type="submit">Zamów</button>
                </form>
            </section>
        </main>
        <footer>
            <p>Stronę opracował: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  24. Etap 3: Styl CSS

    Kroki 24-31 · po tym etapie masz 29 z 40 kryteriów (pkt)

    Krok 24: Zmienne CSS z kolorami 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 własnych właściwości (zmiennych CSS) w :root. ForestGreen pojawi się w trzech miejscach (nagłówek, stopka, ramka szyldu) - wpisany raz w --color-green, dalej jest używany jako var(--color-green). Zmiana koloru strony to wtedy jedna linia.

    Nazwy zmiennych opisują kolor, bo w tym arkuszu każdy kolor ma jedną rolę. W inspektorze egzaminator i tak widzi kolor obliczony (ForestGreen = rgb(34, 139, 34)).

    styl.css CSS nowy plik
    :root {
        --color-green: ForestGreen;
        --color-dark-khaki: DarkKhaki;
        --color-khaki: Khaki;
    }
    Podgląd strony po tym kroku (index.php)
  25. Krok 25: Krój Georgia dla wszystkich selektorów

    Kryteria:

    R.4.1 domyślne formatowanie wszystkich selektorów (*): krój czcionki Georgia, serif (dozwolona tylko taka kolejność)

    ok. 1 min
    Polecenie z arkusza

    Domyślnie dla wszystkich selektorów: krój czcionki Georgia, w przypadku braku serif

    Selektor uniwersalny * (tego wymaga R.4.1) obejmuje też pola formularza i przycisk, które domyślnie mają krój systemowy. Po Georgia stoi rodzina zapasowa serif - klucz dopuszcza tylko taką kolejność.

    styl.css CSS zmienione: 4 linie
    :root {
        --color-green: ForestGreen;
        --color-dark-khaki: DarkKhaki;
        --color-khaki: Khaki;
    }
    
    * {
        font-family: Georgia, serif;
    }
    Podgląd strony po tym kroku (index.php)
  26. Krok 26: Nagłówek i stopka

    Kryteria: , ,

    R.4.3 kolor czcionki: blok nagłówkowy i stopka - biały

    R.4.6 marginesy wewnętrzne (padding) : blok nagłówkowy i stopka - 2px

    R.4.7 wyrównanie tekstu do środka: blok nagłówkowy i stopka

    ok. 1 min
    Polecenie z arkusza

    Wspólne dla bloku nagłówkowego i stopki: kolor tła ForestGreen, biały kolor czcionki, marginesy wewnętrzne 2 px, wyrównanie tekstu do środka

    Jedna reguła dla dwóch selektorów, każdy w osobnej linii. Kolor tła ze zmiennej, „marginesy wewnętrzne” to padding.

    styl.css CSS zmienione: 8 linii
    :root {
        --color-green: ForestGreen;
        --color-dark-khaki: DarkKhaki;
        --color-khaki: Khaki;
    }
    
    * {
        font-family: Georgia, serif;
    }
    
    header,
    footer {
        background-color: var(--color-green);
        color: white;
        padding: 2px;
        text-align: center;
    }
    Podgląd strony po tym kroku (index.php)
  27. Krok 27: Blok nawigacyjny: ukryty nadmiar

    Kryteria:

    R.4.8 przepełnienie bloku: blok nawigacyjny - hidden

    ok. 2 min
    Polecenie z arkusza

    Dla bloku nawigacyjnego: kolor tła DarkKhaki, wysokość 150 px, w przypadku przepełnienia bloku pozostała jego zawartość jest ukryta (na ilustracji 2 jest 8 owoców – reszta nie zmieściła się w bloku i została ukryta)

    Zdjęcia owoców mają po 150 px wysokości - tyle co blok, więc mieści się dokładnie jeden rząd. Obrazy są elementami liniowymi i zawijają się do następnego rzędu, gdy brakuje szerokości; overflow: hidden ten drugi rząd ucina (R.4.8). Dlatego na szerokim ekranie widać więcej owoców, a na wąskim mniej - jak mówi podpis ilustracji 2.

    Typowe błędy

    overflow: auto dodałby pasek przewijania zamiast ukryć nadmiar - R.4.8 wymaga hidden.

    styl.css CSS zmienione: 6 linii
    :root {
        --color-green: ForestGreen;
        --color-dark-khaki: DarkKhaki;
        --color-khaki: Khaki;
    }
    
    * {
        font-family: Georgia, serif;
    }
    
    header,
    footer {
        background-color: var(--color-green);
        color: white;
        padding: 2px;
        text-align: center;
    }
    
    nav {
        background-color: var(--color-dark-khaki);
        height: 150px;
        overflow: hidden;
    }
    Podgląd strony po tym kroku (index.php)
  28. Krok 28: Blok boczny i sekcja: tło i wysokość

    Kryteria: ,

    R.4.2 kolor tła: blok nagłówkowy i stopka - ForestGreen, blok nawigacyjny - DarkKhaki, bloki boczny i sekcji - Khaki

    R.4.4 wysokość: blok nawigacyjny - 150px, bloki boczny i sekcji - 400px

    ok. 1 min
    Polecenie z arkusza

    Wspólne dla bloku bocznego i sekcji: kolor tła Khaki, szerokość 50%, wysokość 400 px

    Tło i wysokość w jednej regule. Szerokości tu nie ma - w tej wersji obie kolumny po 50% wyznacza siatka w następnym kroku. Klucz R.4.5 dopuszcza to wprost: „szerokość 50% (width lub grid-template-columns lub flex)”.

    Z tą regułą są komplet kolorów tła (R.4.2) i wysokości (R.4.4).

    styl.css CSS zmienione: 6 linii
    :root {
        --color-green: ForestGreen;
        --color-dark-khaki: DarkKhaki;
        --color-khaki: Khaki;
    }
    
    * {
        font-family: Georgia, serif;
    }
    
    header,
    footer {
        background-color: var(--color-green);
        color: white;
        padding: 2px;
        text-align: center;
    }
    
    nav {
        background-color: var(--color-dark-khaki);
        height: 150px;
        overflow: hidden;
    }
    
    aside,
    section {
        background-color: var(--color-khaki);
        height: 400px;
    }
    Podgląd strony po tym kroku (index.php)
  29. Krok 29: Układ: siatka w bloku głównym

    Kryteria: ,

    R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z ilustracją 3 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)

    R.4.5 szerokość: bloki boczny i sekcji - 50% (width lub grid-template-columns lub flex)

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

    <main> staje się siatką o dwóch kolumnach po 50% - blok boczny wpada do pierwszej, sekcja do drugiej, w kolejności z HTML. Nagłówek, nawigacja i stopka są zwykłymi blokami poza siatką, więc same stoją jeden pod drugim, a stopka pod blokiem głównym - bez float i bez clear (R.3.1).

    Zapis 50% 50% odpowiada słowom arkusza; 1fr 1fr („po równej części wolnego miejsca”) dałby ten sam wynik. Odstępu gap tu nie ma: przy kolumnach w procentach odstęp dołożyłby się do 100% i siatka wyszłaby poza ekran.

    Typowe błędy

    grid-template-columns działa tylko razem z display: grid na tym samym elemencie - sama lista kolumn bez display: grid nic nie zmienia.

    styl.css CSS zmienione: 5 linii
    :root {
        --color-green: ForestGreen;
        --color-dark-khaki: DarkKhaki;
        --color-khaki: Khaki;
    }
    
    * {
        font-family: Georgia, serif;
    }
    
    header,
    footer {
        background-color: var(--color-green);
        color: white;
        padding: 2px;
        text-align: center;
    }
    
    nav {
        background-color: var(--color-dark-khaki);
        height: 150px;
        overflow: hidden;
    }
    
    aside,
    section {
        background-color: var(--color-khaki);
        height: 400px;
    }
    
    main {
        display: grid;
        grid-template-columns: 50% 50%;
    }
    Podgląd strony po tym kroku (index.php)
  30. Krok 30: Szyld w bloku bocznym

    Kryteria:

    R.4.9 styl jedynie dla obrazu w bloku bocznym: marginesy wewnętrzne - 2px, zewnętrzne - 10px, obramowanie 2px dotted ForestGreen, zaokrąglenie rogów 40px (dowolną metodą: id, klasa, potomek)

    ok. 2 min
    Polecenie z arkusza

    Jedynie dla obrazu w bloku bocznym: marginesy wewnętrzne 2 px, zewnętrzne 10 px, obramowanie linią kropkową o szerokości 2 px i kolorze ForestGreen, zaokrąglenie rogów 40 px

    Selektor potomka aside img trafia tylko w obrazy wewnątrz <aside> - zdjęcia owoców w <nav> zostają bez ramki („jedynie dla obrazu w bloku bocznym”). Klucz dopuszcza też klasę albo identyfikator (R.4.9); selektor potomka nie wymaga zmiany w HTML. Skrót border: 2px dotted to grubość, styl (kropki) i kolor ze zmiennej.

    Typowe błędy

    Sam selektor img objąłby też dziesięć zdjęć owoców - wtedy styl nie jest „jedynie dla obrazu w bloku bocznym”.

    styl.css CSS zmienione: 7 linii
    :root {
        --color-green: ForestGreen;
        --color-dark-khaki: DarkKhaki;
        --color-khaki: Khaki;
    }
    
    * {
        font-family: Georgia, serif;
    }
    
    header,
    footer {
        background-color: var(--color-green);
        color: white;
        padding: 2px;
        text-align: center;
    }
    
    nav {
        background-color: var(--color-dark-khaki);
        height: 150px;
        overflow: hidden;
    }
    
    aside,
    section {
        background-color: var(--color-khaki);
        height: 400px;
    }
    
    main {
        display: grid;
        grid-template-columns: 50% 50%;
    }
    
    aside img {
        padding: 2px;
        margin: 10px;
        border: 2px dotted var(--color-green);
        border-radius: 40px;
    }
    Podgląd strony po tym kroku (index.php)
  31. Krok 31: Akapity: większa kursywa

    Kryteria:

    R.4.10 styl selektora p: rozmiar czcionki 120%, kursywa

    ok. 1 min
    Polecenie z arkusza

    Dla selektora paragrafu: rozmiar czcionki 120%, kursywa

    Selektor znacznika p, jak każe uwaga arkusza (R.4.10). Działa na akapit w sekcji, stopkę i akapit z wartością zamówienia, który wypisze Skrypt 3. 120% liczy się od rozmiaru czcionki rodzica.

    styl.css CSS zmienione: 5 linii
    :root {
        --color-green: ForestGreen;
        --color-dark-khaki: DarkKhaki;
        --color-khaki: Khaki;
    }
    
    * {
        font-family: Georgia, serif;
    }
    
    header,
    footer {
        background-color: var(--color-green);
        color: white;
        padding: 2px;
        text-align: center;
    }
    
    nav {
        background-color: var(--color-dark-khaki);
        height: 150px;
        overflow: hidden;
    }
    
    aside,
    section {
        background-color: var(--color-khaki);
        height: 400px;
    }
    
    main {
        display: grid;
        grid-template-columns: 50% 50%;
    }
    
    aside img {
        padding: 2px;
        margin: 10px;
        border: 2px dotted var(--color-green);
        border-radius: 40px;
    }
    
    p {
        font-size: 120%;
        font-style: italic;
    }
    Podgląd strony po tym kroku (index.php)
  32. Etap 4: Skrypty PHP

    Kroki 32-44 · po tym etapie masz 40 z 40 kryteriów (pkt)

    Krok 32: Połączenie PDO z obsługą błędu i zamknięcie

    Kryteria:

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

    ok. 4 min
    Polecenie z arkusza

    Skrypty łączą się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie bazar. Po wykonaniu wszystkich operacji połączenie z bazą jest zamykane

    W tej wersji cały PHP stoi na górze pliku, przed <!DOCTYPE html>: najpierw dane, potem HTML, który je tylko wypisuje. new PDO(...) otwiera połączenie; napis mysql:host=localhost;dbname=bazar;charset=utf8mb4 to sterownik, serwer, baza i od razu kodowanie połączenia, więc polskie nazwy towarów przychodzą poprawnie. Dwie opcje:

    • PDO::ERRMODE_EXCEPTION - błąd SQL rzuca wyjątek zamiast po cichu zwracać false,
    • PDO::FETCH_ASSOC - wiersz to tablica z nazwami kolumn: $product['nazwa'] zamiast $row[1].

    try/catch (PDOException ...) łapie nieudane połączenie i pokazuje ogólny komunikat z kodem 500. Treść wyjątku ($exception->getMessage()) zawiera nazwę serwera i użytkownika - takich szczegółów nie pokazuje się odwiedzającym.

    Na dole bloku od razu $pdo = null; - w PDO tak zamyka się połączenie. Zapytania będą dopisywane nad tą linią, więc zamknięcie zawsze zostanie ostatnią operacją na bazie (R.5.1). Klucz dopuszcza PDO wprost („tylko w przypadku użycia bibliotek MySQLi lub PDO”). Od tego kroku podgląd to wynik prawdziwego uruchomienia strony w PHP 8.2.

    Typowe błędy

    PDO nie ma w tabeli 1 arkusza - nazwy stałych trzeba znać na pamięć. Literówka w PDO::ATTR_ERRMODE to błąd krytyczny i biała strona; kto nie jest pewny składni, pisze funkcjami mysqli_* z tabeli 1 (wersja podstawowa).

    index.php PHP zmienione: 15 linii
    <?php
    // Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumn
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=bazar;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    } catch (PDOException $exception) {
        http_response_code(500);
        exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');
    }
    
    // Wszystkie dane pobrane i zapisane - zamknięcie połączenia
    $pdo = null;
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Zdrowy bazarek</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zdrowy bazarek</h1>
        </header>
        <nav>
        </nav>
        <main>
            <aside>
                <img src="market.png" alt="bazarek">
            </aside>
            <section>
                <p>Wybierz owoc lub warzywo i podaj jego wagę:</p>
                <form method="post">
                    <select name="product" aria-label="Towar">
                    </select>
                    <input type="number" name="weight" min="1" step="1" required aria-label="Liczba kilogramów">
                    <button type="submit">Zamów</button>
                </form>
            </section>
        </main>
        <footer>
            <p>Stronę opracował: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  33. Krok 33: Funkcja escape() do bezpiecznego wypisywania

    ok. 2 min
    Polecenie z arkusza

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

    Zanim cokolwiek z bazy trafi na stronę, powstaje krótka funkcja escape() - „zabezpiecz znaki”. W frameworkach PHP ta sama funkcja nazywa się często po prostu e(), ale arkusz wymaga nazw, które mówią, do czego służą, więc tu jest pełne słowo. htmlspecialchars zamienia <, >, & i - dzięki ENT_QUOTES - oba cudzysłowy na encje. Nazwa towaru z apostrofem albo cudzysłowem nie rozerwie wtedy atrybutu alt="...", a wpis <script> w bazie wyświetli się jako tekst, zamiast wykonać się w przeglądarce (atak XSS).

    Typy w nagłówku (string|int|float|null, wynik : string) dokumentują, co funkcja przyjmuje: z bazy przychodzą napisy, liczby albo NULL.

    index.php PHP zmienione: 5 linii
    <?php
    // Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumn
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=bazar;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    } catch (PDOException $exception) {
        http_response_code(500);
        exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');
    }
    
    function escape(string|int|float|null $value): string
    {
        return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
    }
    
    // Wszystkie dane pobrane i zapisane - zamknięcie połączenia
    $pdo = null;
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Zdrowy bazarek</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zdrowy bazarek</h1>
        </header>
        <nav>
        </nav>
        <main>
            <aside>
                <img src="market.png" alt="bazarek">
            </aside>
            <section>
                <p>Wybierz owoc lub warzywo i podaj jego wagę:</p>
                <form method="post">
                    <select name="product" aria-label="Towar">
                    </select>
                    <input type="number" name="weight" min="1" step="1" required aria-label="Liczba kilogramów">
                    <button type="submit">Zamów</button>
                </form>
            </section>
        </main>
        <footer>
            <p>Stronę opracował: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>

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

  34. Krok 34: Skrypt 1: zapytanie 1 do bazy

    Kryteria:

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

    ok. 2 min
    Polecenie z arkusza

    Skrypt 1: wysyła do bazy danych zapytanie 1

    $pdo->query(...) wysyła zapytanie 1, a fetchAll() od razu odbiera wszystkie dziesięć wierszy do tablicy $fruits (R.5.2). Zwykłe query, bez prepare, jest tu na miejscu: w zapytaniu nie ma żadnej wartości od użytkownika, więc nie ma czego wstrzyknąć.

    index.php PHP zmienione: 3 linie
    <?php
    // Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumn
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=bazar;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    } catch (PDOException $exception) {
        http_response_code(500);
        exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');
    }
    
    function escape(string|int|float|null $value): string
    {
        return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
    }
    
    // Skrypt 1: dziesięć pierwszych towarów do paska zdjęć (zapytanie 1)
    $fruits = $pdo->query('SELECT nazwa, plik FROM towar LIMIT 10')->fetchAll();
    
    // Wszystkie dane pobrane i zapisane - zamknięcie połączenia
    $pdo = null;
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Zdrowy bazarek</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zdrowy bazarek</h1>
        </header>
        <nav>
        </nav>
        <main>
            <aside>
                <img src="market.png" alt="bazarek">
            </aside>
            <section>
                <p>Wybierz owoc lub warzywo i podaj jego wagę:</p>
                <form method="post">
                    <select name="product" aria-label="Towar">
                    </select>
                    <input type="number" name="weight" min="1" step="1" required aria-label="Liczba kilogramów">
                    <button type="submit">Zamów</button>
                </form>
            </section>
        </main>
        <footer>
            <p>Stronę opracował: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>

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

  35. Krok 35: Skrypt 1: zdjęcie każdego owocu

    Kryteria:

    R.5.4 Skrypt 1 wyświetla obrazy dla każdego wiersza pobranego z bazy, źródłem jest pole plik, tekstem alternatywnym jest nazwa (obrazy mogą być ukryte za sprawą właściwości CSS overflow, zamiast obrazu z jabłkiem może się wyświetlać tekst alternatywny)

    ok. 3 min
    Polecenie z arkusza

    Skrypt 1: dla każdego wiersza pobranego z bazy wyświetla obraz o pobranej z bazy nazwie pliku i tekście alternatywnym odpowiadającym nazwie owocu

    W HTML zostaje sam szablon: pętla foreach ($fruits as $fruit): ... endforeach; - składnia alternatywna PHP, pomyślana do przeplatania z HTML (dwukropek i endforeach zamiast klamer, które łatwo zgubić między znacznikami). <?= ... ?> to skrót od <?php echo ... ?>.

    src dostaje pole plik, alt - pole nazwa (R.5.4). Atrybuty HTML w zwykłych cudzysłowach, bez \", bo PHP nie skleja już napisów z HTML-em. W podglądzie pasek owoców; nadmiar ucina overflow: hidden.

    index.php PHP zmienione: 3 linie
    <?php
    // Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumn
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=bazar;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    } catch (PDOException $exception) {
        http_response_code(500);
        exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');
    }
    
    function escape(string|int|float|null $value): string
    {
        return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
    }
    
    // Skrypt 1: dziesięć pierwszych towarów do paska zdjęć (zapytanie 1)
    $fruits = $pdo->query('SELECT nazwa, plik FROM towar LIMIT 10')->fetchAll();
    
    // Wszystkie dane pobrane i zapisane - zamknięcie połączenia
    $pdo = null;
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Zdrowy bazarek</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zdrowy bazarek</h1>
        </header>
        <nav>
    <?php foreach ($fruits as $fruit): ?>
            <img src="<?= escape($fruit['plik']) ?>" alt="<?= escape($fruit['nazwa']) ?>">
    <?php endforeach; ?>
        </nav>
        <main>
            <aside>
                <img src="market.png" alt="bazarek">
            </aside>
            <section>
                <p>Wybierz owoc lub warzywo i podaj jego wagę:</p>
                <form method="post">
                    <select name="product" aria-label="Towar">
                    </select>
                    <input type="number" name="weight" min="1" step="1" required aria-label="Liczba kilogramów">
                    <button type="submit">Zamów</button>
                </form>
            </section>
        </main>
        <footer>
            <p>Stronę opracował: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  36. Krok 36: Skrypt 2: zapytanie 2 do bazy

    ok. 1 min
    Polecenie z arkusza

    Skrypt 2: wysyła do bazy danych zapytanie 2

    To samo dla listy towarów: zapytanie 2 i cały wynik w tablicy $products. Każde zapytanie ma własną zmienną o nazwie mówiącej, co w niej jest - zamiast jednego $result nadpisywanego przez kolejne skrypty.

    index.php PHP zmienione: 3 linie
    <?php
    // Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumn
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=bazar;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    } catch (PDOException $exception) {
        http_response_code(500);
        exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');
    }
    
    function escape(string|int|float|null $value): string
    {
        return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
    }
    
    // Skrypt 1: dziesięć pierwszych towarów do paska zdjęć (zapytanie 1)
    $fruits = $pdo->query('SELECT nazwa, plik FROM towar LIMIT 10')->fetchAll();
    
    // Skrypt 2: wszystkie towary do listy rozwijanej (zapytanie 2)
    $products = $pdo->query('SELECT id, nazwa FROM towar')->fetchAll();
    
    // Wszystkie dane pobrane i zapisane - zamknięcie połączenia
    $pdo = null;
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Zdrowy bazarek</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zdrowy bazarek</h1>
        </header>
        <nav>
    <?php foreach ($fruits as $fruit): ?>
            <img src="<?= escape($fruit['plik']) ?>" alt="<?= escape($fruit['nazwa']) ?>">
    <?php endforeach; ?>
        </nav>
        <main>
            <aside>
                <img src="market.png" alt="bazarek">
            </aside>
            <section>
                <p>Wybierz owoc lub warzywo i podaj jego wagę:</p>
                <form method="post">
                    <select name="product" aria-label="Towar">
                    </select>
                    <input type="number" name="weight" min="1" step="1" required aria-label="Liczba kilogramów">
                    <button type="submit">Zamów</button>
                </form>
            </section>
        </main>
        <footer>
            <p>Stronę opracował: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>

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

  37. Krok 37: Skrypt 2: opcje z identyfikatorem w value

    Kryteria: ,

    R.5.5 Skrypt 2 wypełnia listę rozwijaną z danymi pobranymi z bazy, atrybut value to id, wyświetlana wartość opcji to nazwa towaru

    R.2.9 Na stronie umieszczona lista rozwijana (w HTML lub skrypcie), zastosowane znaczniki < select> i <option> , pole edycyjne i przycisk "Zamów"

    ok. 2 min
    Polecenie z arkusza

    Skrypt 2: dla każdego wiersza pobranego z bazy generuje opcję listy rozwijanej, gdzie w atrybucie value znajduje się zwrócone pole id, a w treści opcji znajduje się nazwa towaru

    Jedna <option> na towar: value = id, treść = nazwa (R.5.5). Kupujący widzi nazwę, a formularz wyśle identyfikator - po nim Skrypt 3 znajdzie cenę. Lista ma 39 pozycji, pierwsza to gruszka, jak na ilustracji 4. Z opcjami jest komplet R.2.9: <select> z <option>, pole edycyjne i przycisk „Zamów”.

    index.php PHP zmienione: 3 linie
    <?php
    // Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumn
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=bazar;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    } catch (PDOException $exception) {
        http_response_code(500);
        exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');
    }
    
    function escape(string|int|float|null $value): string
    {
        return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
    }
    
    // Skrypt 1: dziesięć pierwszych towarów do paska zdjęć (zapytanie 1)
    $fruits = $pdo->query('SELECT nazwa, plik FROM towar LIMIT 10')->fetchAll();
    
    // Skrypt 2: wszystkie towary do listy rozwijanej (zapytanie 2)
    $products = $pdo->query('SELECT id, nazwa FROM towar')->fetchAll();
    
    // Wszystkie dane pobrane i zapisane - zamknięcie połączenia
    $pdo = null;
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Zdrowy bazarek</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zdrowy bazarek</h1>
        </header>
        <nav>
    <?php foreach ($fruits as $fruit): ?>
            <img src="<?= escape($fruit['plik']) ?>" alt="<?= escape($fruit['nazwa']) ?>">
    <?php endforeach; ?>
        </nav>
        <main>
            <aside>
                <img src="market.png" alt="bazarek">
            </aside>
            <section>
                <p>Wybierz owoc lub warzywo i podaj jego wagę:</p>
                <form method="post">
                    <select name="product" aria-label="Towar">
    <?php foreach ($products as $product): ?>
                        <option value="<?= escape($product['id']) ?>"><?= escape($product['nazwa']) ?></option>
    <?php endforeach; ?>
                    </select>
                    <input type="number" name="weight" min="1" step="1" required aria-label="Liczba kilogramów">
                    <button type="submit">Zamów</button>
                </form>
            </section>
        </main>
        <footer>
            <p>Stronę opracował: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  38. Krok 38: Skrypt 3: tylko po wysłaniu formularza

    ok. 2 min
    Polecenie z arkusza

    Skrypt 3, wykonywany tylko wtedy, gdy wysłano dane z formularza

    $_SERVER['REQUEST_METHOD'] mówi, jak przyszło żądanie: zwykłe wejście na stronę to GET, kliknięcie „Zamów” - POST. Warunek na metodę obejmuje cały formularz naraz, zamiast sprawdzać isset na jednym z pól.

    Przed warunkiem dwie zmienne z wartościami „nic się nie stało”: $order (zamówiony towar) i $message (komunikat dla kupującego). Szablon niżej zawsze może z nich skorzystać - także przy zwykłym wejściu, kiedy blok if się nie wykona.

    index.php PHP zmienione: 6 linii
    <?php
    // Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumn
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=bazar;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    } catch (PDOException $exception) {
        http_response_code(500);
        exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');
    }
    
    function escape(string|int|float|null $value): string
    {
        return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
    }
    
    // Skrypt 1: dziesięć pierwszych towarów do paska zdjęć (zapytanie 1)
    $fruits = $pdo->query('SELECT nazwa, plik FROM towar LIMIT 10')->fetchAll();
    
    // Skrypt 2: wszystkie towary do listy rozwijanej (zapytanie 2)
    $products = $pdo->query('SELECT id, nazwa FROM towar')->fetchAll();
    
    // Skrypt 3: zamówienie - tylko po wysłaniu formularza (zapytania 3 i 4)
    $order = null;
    $message = '';
    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    }
    
    // Wszystkie dane pobrane i zapisane - zamknięcie połączenia
    $pdo = null;
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Zdrowy bazarek</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zdrowy bazarek</h1>
        </header>
        <nav>
    <?php foreach ($fruits as $fruit): ?>
            <img src="<?= escape($fruit['plik']) ?>" alt="<?= escape($fruit['nazwa']) ?>">
    <?php endforeach; ?>
        </nav>
        <main>
            <aside>
                <img src="market.png" alt="bazarek">
            </aside>
            <section>
                <p>Wybierz owoc lub warzywo i podaj jego wagę:</p>
                <form method="post">
                    <select name="product" aria-label="Towar">
    <?php foreach ($products as $product): ?>
                        <option value="<?= escape($product['id']) ?>"><?= escape($product['nazwa']) ?></option>
    <?php endforeach; ?>
                    </select>
                    <input type="number" name="weight" min="1" step="1" required aria-label="Liczba kilogramów">
                    <button type="submit">Zamów</button>
                </form>
            </section>
        </main>
        <footer>
            <p>Stronę opracował: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>

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

  39. Krok 39: Skrypt 3: wartości z formularza po sprawdzeniu

    Kryteria:

    R.5.6 Skrypt 3 stosuje tablicę $_POST dla danych wysłanych z formularza

    ok. 3 min
    Polecenie z arkusza

    Pobiera wartości z formularza: id towaru i liczbę kilogramów

    Obie wartości przychodzą w $_POST pod nazwami pól product i weight (R.5.6) - jako tekst, bo HTTP przesyła tylko napisy. Zanim trafią dalej, sprawdza je filter_var(..., FILTER_VALIDATE_INT, ...): zwraca liczbę całkowitą albo false - dla pustego pola, liter, „2.5”, „12 OR 1=1” i liczb spoza zakresu (min_range 1, dla wagi też max_range 10 000 kg). Operator ?? podstawia pusty napis, gdy pola w ogóle nie wysłano.

    Przy błędzie $message dostaje komunikat, a szablon wypisuje go w akapicie pod formularzem. Tu widać różnicę z wersją podstawową z egzaminu: tam puste pole wagi kończyło stronę błędem „Unsupported operand types”, a -5 kg trafiało do bazy. Podgląd pokazuje stronę po zamówieniu pietruszki (30 kg) - na razie nic się nie zmienia, wynik dojdzie w kolejnych krokach.

    Typowe błędy

    filter_input(INPUT_POST, ...) sprawdza to samo, ale w kodzie nie ma wtedy $_POST, a tego dotyczy kryterium R.5.6. Przy filter_var zawsze porównuj wynik ściśle: === false - przy innych filtrach poprawne 0 wzięte za błąd.

    index.php PHP zmienione: 9 linii
    <?php
    // Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumn
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=bazar;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    } catch (PDOException $exception) {
        http_response_code(500);
        exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');
    }
    
    function escape(string|int|float|null $value): string
    {
        return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
    }
    
    // Skrypt 1: dziesięć pierwszych towarów do paska zdjęć (zapytanie 1)
    $fruits = $pdo->query('SELECT nazwa, plik FROM towar LIMIT 10')->fetchAll();
    
    // Skrypt 2: wszystkie towary do listy rozwijanej (zapytanie 2)
    $products = $pdo->query('SELECT id, nazwa FROM towar')->fetchAll();
    
    // Skrypt 3: zamówienie - tylko po wysłaniu formularza (zapytania 3 i 4)
    $order = null;
    $message = '';
    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        $productId = filter_var($_POST['product'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
        $weight = filter_var($_POST['weight'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'max_range' => 10000]]);
        if ($productId === false || $weight === false) {
            $message = 'Wybierz towar i podaj liczbę kilogramów - liczbę całkowitą większą od zera.';
        } else {
        }
    }
    
    // Wszystkie dane pobrane i zapisane - zamknięcie połączenia
    $pdo = null;
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Zdrowy bazarek</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zdrowy bazarek</h1>
        </header>
        <nav>
    <?php foreach ($fruits as $fruit): ?>
            <img src="<?= escape($fruit['plik']) ?>" alt="<?= escape($fruit['nazwa']) ?>">
    <?php endforeach; ?>
        </nav>
        <main>
            <aside>
                <img src="market.png" alt="bazarek">
            </aside>
            <section>
                <p>Wybierz owoc lub warzywo i podaj jego wagę:</p>
                <form method="post">
                    <select name="product" aria-label="Towar">
    <?php foreach ($products as $product): ?>
                        <option value="<?= escape($product['id']) ?>"><?= escape($product['nazwa']) ?></option>
    <?php endforeach; ?>
                    </select>
                    <input type="number" name="weight" min="1" step="1" required aria-label="Liczba kilogramów">
                    <button type="submit">Zamów</button>
                </form>
    <?php if ($message !== ''): ?>
                <p><?= escape($message) ?></p>
    <?php endif; ?>
            </section>
        </main>
        <footer>
            <p>Stronę opracował: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>

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

  40. Krok 40: Skrypt 3: zapytanie 3 przygotowane

    Kryteria:

    R.5.3 Skrypt zawiera instrukcję wysyłającą do bazy danych zmodyfikowane zapytanie 3: sprawdzane jest id przesłane formularzem lub zmodyfikowane zapytanie 4: wstawiany jest rekord z id towaru i liczbą kg przesłaną zapytaniem

    ok. 3 min
    Polecenie z arkusza

    Wysyła do bazy danych zmodyfikowane zapytanie 3, tak że wybierany jest rekord o id wskazanym w formularzu

    Zapytanie przygotowane: w treści stoi parametr nazwany :id, a wartość idzie osobno w execute(['id' => $productId]) (R.5.3). Baza dostaje najpierw strukturę zapytania, a dane dopiero potem - wpisany tekst nigdy nie stanie się częścią SQL. To druga warstwa ochrony obok filter_var: walidacja pilnuje sensu danych, parametr pilnuje składni zapytania.

    fetch() zwraca jeden wiersz albo false, gdy towaru o takim id nie ma; ?: null zamienia false na null, żeby „brak zamówienia” miał jedną wartość.

    index.php PHP zmienione: 3 linie
    <?php
    // Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumn
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=bazar;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    } catch (PDOException $exception) {
        http_response_code(500);
        exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');
    }
    
    function escape(string|int|float|null $value): string
    {
        return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
    }
    
    // Skrypt 1: dziesięć pierwszych towarów do paska zdjęć (zapytanie 1)
    $fruits = $pdo->query('SELECT nazwa, plik FROM towar LIMIT 10')->fetchAll();
    
    // Skrypt 2: wszystkie towary do listy rozwijanej (zapytanie 2)
    $products = $pdo->query('SELECT id, nazwa FROM towar')->fetchAll();
    
    // Skrypt 3: zamówienie - tylko po wysłaniu formularza (zapytania 3 i 4)
    $order = null;
    $message = '';
    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        $productId = filter_var($_POST['product'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
        $weight = filter_var($_POST['weight'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'max_range' => 10000]]);
        if ($productId === false || $weight === false) {
            $message = 'Wybierz towar i podaj liczbę kilogramów - liczbę całkowitą większą od zera.';
        } else {
            $statement = $pdo->prepare('SELECT rodzaj, nazwa, cena FROM towar WHERE id = :id');
            $statement->execute(['id' => $productId]);
            $order = $statement->fetch() ?: null;
        }
    }
    
    // Wszystkie dane pobrane i zapisane - zamknięcie połączenia
    $pdo = null;
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Zdrowy bazarek</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zdrowy bazarek</h1>
        </header>
        <nav>
    <?php foreach ($fruits as $fruit): ?>
            <img src="<?= escape($fruit['plik']) ?>" alt="<?= escape($fruit['nazwa']) ?>">
    <?php endforeach; ?>
        </nav>
        <main>
            <aside>
                <img src="market.png" alt="bazarek">
            </aside>
            <section>
                <p>Wybierz owoc lub warzywo i podaj jego wagę:</p>
                <form method="post">
                    <select name="product" aria-label="Towar">
    <?php foreach ($products as $product): ?>
                        <option value="<?= escape($product['id']) ?>"><?= escape($product['nazwa']) ?></option>
    <?php endforeach; ?>
                    </select>
                    <input type="number" name="weight" min="1" step="1" required aria-label="Liczba kilogramów">
                    <button type="submit">Zamów</button>
                </form>
    <?php if ($message !== ''): ?>
                <p><?= escape($message) ?></p>
    <?php endif; ?>
            </section>
        </main>
        <footer>
            <p>Stronę opracował: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>

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

  41. Krok 41: Skrypt 3: wartość zamówienia

    Kryteria:

    R.5.7 Skrypt 3 oblicza wartość iloczynu ceny zwróconej zapytaniem oraz liczby kilogramów wysłanej z formularza

    ok. 2 min
    Polecenie z arkusza

    Oblicza wartość na podstawie zwróconej zapytaniem ceny oraz liczby kilogramów wysłanej z formularza

    Cena z bazy razy kilogramy z formularza (R.5.7). PDO oddaje kolumnę DECIMAL jako tekst „14.70” - celowo, żeby nie zgubić groszy w liczbie zmiennoprzecinkowej - a $weight jest już liczbą całkowitą z filter_var, więc mnożenie jest bezpieczne: 14,70 × 30 = 441.

    Gdy towaru nie ma (np. identyfikator 999 wysłany z pominięciem formularza), zamiast mnożenia przez nieistniejącą cenę jest komunikat „Nie ma takiego towaru.”.

    index.php PHP zmienione: 5 linii
    <?php
    // Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumn
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=bazar;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    } catch (PDOException $exception) {
        http_response_code(500);
        exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');
    }
    
    function escape(string|int|float|null $value): string
    {
        return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
    }
    
    // Skrypt 1: dziesięć pierwszych towarów do paska zdjęć (zapytanie 1)
    $fruits = $pdo->query('SELECT nazwa, plik FROM towar LIMIT 10')->fetchAll();
    
    // Skrypt 2: wszystkie towary do listy rozwijanej (zapytanie 2)
    $products = $pdo->query('SELECT id, nazwa FROM towar')->fetchAll();
    
    // Skrypt 3: zamówienie - tylko po wysłaniu formularza (zapytania 3 i 4)
    $order = null;
    $message = '';
    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        $productId = filter_var($_POST['product'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
        $weight = filter_var($_POST['weight'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'max_range' => 10000]]);
        if ($productId === false || $weight === false) {
            $message = 'Wybierz towar i podaj liczbę kilogramów - liczbę całkowitą większą od zera.';
        } else {
            $statement = $pdo->prepare('SELECT rodzaj, nazwa, cena FROM towar WHERE id = :id');
            $statement->execute(['id' => $productId]);
            $order = $statement->fetch() ?: null;
            if ($order === null) {
                $message = 'Nie ma takiego towaru.';
            } else {
                $value = $order['cena'] * $weight;
            }
        }
    }
    
    // Wszystkie dane pobrane i zapisane - zamknięcie połączenia
    $pdo = null;
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Zdrowy bazarek</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zdrowy bazarek</h1>
        </header>
        <nav>
    <?php foreach ($fruits as $fruit): ?>
            <img src="<?= escape($fruit['plik']) ?>" alt="<?= escape($fruit['nazwa']) ?>">
    <?php endforeach; ?>
        </nav>
        <main>
            <aside>
                <img src="market.png" alt="bazarek">
            </aside>
            <section>
                <p>Wybierz owoc lub warzywo i podaj jego wagę:</p>
                <form method="post">
                    <select name="product" aria-label="Towar">
    <?php foreach ($products as $product): ?>
                        <option value="<?= escape($product['id']) ?>"><?= escape($product['nazwa']) ?></option>
    <?php endforeach; ?>
                    </select>
                    <input type="number" name="weight" min="1" step="1" required aria-label="Liczba kilogramów">
                    <button type="submit">Zamów</button>
                </form>
    <?php if ($message !== ''): ?>
                <p><?= escape($message) ?></p>
    <?php endif; ?>
            </section>
        </main>
        <footer>
            <p>Stronę opracował: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>

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

  42. Krok 42: Skrypt 3: akapit z wartością

    Kryteria:

    R.5.8 Skrypt 3 wyświetla w paragrafie tekst: „<rodzaj> <nazwa> wartość: <wartość> zł”, gdzie w znakach <> umieszczono zwrócone zapytaniem pola i wyliczoną wartość

    ok. 2 min
    Polecenie z arkusza

    Wypisuje w dodatkowym paragrafie pod formularzem tekst: „<rodzaj> <nazwa> wartość: <wartość> zł”, gdzie w znakach <> umieszczono zwrócone zapytaniem pola i wyliczoną wartość

    Pod formularzem if ($order !== null): - akapit według wzoru z arkusza (R.5.8), a w gałęzi elseif komunikat o błędzie. Kwota przez number_format($value, 2, ',', ' '): dwa miejsca po przecinku, polski przecinek dziesiętny i spacja między tysiącami - „441,00 zł”, a dla 3 kg ananasa „24,60 zł” zamiast „24.6 zł”.

    Podgląd: „warzywo pietruszka wartość: 441,00 zł” - ilustracja 5 pokazuje „441 zł”, bo tam wartość jest wypisana bez formatowania. Klucz ocenia tekst z polami z bazy i wyliczoną wartością, a format liczby zostawia piszącemu.

    index.php PHP zmienione: 3 linie
    <?php
    // Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumn
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=bazar;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    } catch (PDOException $exception) {
        http_response_code(500);
        exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');
    }
    
    function escape(string|int|float|null $value): string
    {
        return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
    }
    
    // Skrypt 1: dziesięć pierwszych towarów do paska zdjęć (zapytanie 1)
    $fruits = $pdo->query('SELECT nazwa, plik FROM towar LIMIT 10')->fetchAll();
    
    // Skrypt 2: wszystkie towary do listy rozwijanej (zapytanie 2)
    $products = $pdo->query('SELECT id, nazwa FROM towar')->fetchAll();
    
    // Skrypt 3: zamówienie - tylko po wysłaniu formularza (zapytania 3 i 4)
    $order = null;
    $message = '';
    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        $productId = filter_var($_POST['product'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
        $weight = filter_var($_POST['weight'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'max_range' => 10000]]);
        if ($productId === false || $weight === false) {
            $message = 'Wybierz towar i podaj liczbę kilogramów - liczbę całkowitą większą od zera.';
        } else {
            $statement = $pdo->prepare('SELECT rodzaj, nazwa, cena FROM towar WHERE id = :id');
            $statement->execute(['id' => $productId]);
            $order = $statement->fetch() ?: null;
            if ($order === null) {
                $message = 'Nie ma takiego towaru.';
            } else {
                $value = $order['cena'] * $weight;
            }
        }
    }
    
    // Wszystkie dane pobrane i zapisane - zamknięcie połączenia
    $pdo = null;
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Zdrowy bazarek</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zdrowy bazarek</h1>
        </header>
        <nav>
    <?php foreach ($fruits as $fruit): ?>
            <img src="<?= escape($fruit['plik']) ?>" alt="<?= escape($fruit['nazwa']) ?>">
    <?php endforeach; ?>
        </nav>
        <main>
            <aside>
                <img src="market.png" alt="bazarek">
            </aside>
            <section>
                <p>Wybierz owoc lub warzywo i podaj jego wagę:</p>
                <form method="post">
                    <select name="product" aria-label="Towar">
    <?php foreach ($products as $product): ?>
                        <option value="<?= escape($product['id']) ?>"><?= escape($product['nazwa']) ?></option>
    <?php endforeach; ?>
                    </select>
                    <input type="number" name="weight" min="1" step="1" required aria-label="Liczba kilogramów">
                    <button type="submit">Zamów</button>
                </form>
    <?php if ($order !== null): ?>
                <p><?= escape($order['rodzaj']) ?> <?= escape($order['nazwa']) ?> wartość: <?= number_format($value, 2, ',', ' ') ?> zł</p>
    <?php elseif ($message !== ''): ?>
                <p><?= escape($message) ?></p>
    <?php endif; ?>
            </section>
        </main>
        <footer>
            <p>Stronę opracował: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  43. Krok 43: Skrypt 3: zapis zamówienia

    Kryteria: ,

    R.5.9 Skrypt 3 wstawia do tabeli zamowienie rekord z danymi pobranymi z formularza (należy sprawdzić w phpMyAdmin)

    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. 3 min
    Polecenie z arkusza

    Wstawia do bazy rekord za pomocą zmodyfikowanego zapytania 4, w ten sposób, że w miejscu id towaru i liczby kilogramów są wpisywane odpowiednie wartości wysłane z formularza (id sklepu pozostaje bez zmian lub jest pomijane)

    Zapytanie 4 jako przygotowane, z parametrami :product i :weight; sklep 2 jest stałą w treści zapytania, bo nie pochodzi od użytkownika. Zapis stoi za sprawdzeniem, że towar istnieje - identyfikator spoza bazy nie dochodzi do INSERT, więc nie ma błędu klucza obcego. Sprawdzone tak, jak każe klucz, w bazie: po zamówieniu pietruszki w tabeli zamowienie przybył rekord 50 (towar 12, sklep 2, 30 kg) - R.5.9.

    Przy zamknięciu doszła linia $statement = $insert = null;: obiekt zapytania przygotowanego trzyma odwołanie do połączenia, więc samo $pdo = null zamknęłoby je dopiero na końcu skryptu. Teraz zamknięcie naprawdę jest ostatnią operacją na bazie.

    Nazwy zmiennych mówią, co przechowują: $pdo, $fruits, $products, $productId, $weight, $order, $value, $statement, $insert, $message (R.5.10).

    Typowe błędy

    Bez wzorca PRG (przekierowania po zapisie) odświeżenie strony klawiszem F5 wysyła formularz ponownie i dopisuje drugie zamówienie - przeglądarka pyta wtedy o ponowne przesłanie danych. Tu przekierowania nie ma celowo: arkusz każe pokazać wartość w odpowiedzi na wysłany formularz.

    index.php PHP zmienione: 3 linie
    <?php
    // Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumn
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=bazar;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    } catch (PDOException $exception) {
        http_response_code(500);
        exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');
    }
    
    function escape(string|int|float|null $value): string
    {
        return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
    }
    
    // Skrypt 1: dziesięć pierwszych towarów do paska zdjęć (zapytanie 1)
    $fruits = $pdo->query('SELECT nazwa, plik FROM towar LIMIT 10')->fetchAll();
    
    // Skrypt 2: wszystkie towary do listy rozwijanej (zapytanie 2)
    $products = $pdo->query('SELECT id, nazwa FROM towar')->fetchAll();
    
    // Skrypt 3: zamówienie - tylko po wysłaniu formularza (zapytania 3 i 4)
    $order = null;
    $message = '';
    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        $productId = filter_var($_POST['product'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
        $weight = filter_var($_POST['weight'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'max_range' => 10000]]);
        if ($productId === false || $weight === false) {
            $message = 'Wybierz towar i podaj liczbę kilogramów - liczbę całkowitą większą od zera.';
        } else {
            $statement = $pdo->prepare('SELECT rodzaj, nazwa, cena FROM towar WHERE id = :id');
            $statement->execute(['id' => $productId]);
            $order = $statement->fetch() ?: null;
            if ($order === null) {
                $message = 'Nie ma takiego towaru.';
            } else {
                $value = $order['cena'] * $weight;
                $insert = $pdo->prepare('INSERT INTO zamowienie (id_towar, id_sklep, liczba_kg) VALUES (:product, 2, :weight)');
                $insert->execute(['product' => $productId, 'weight' => $weight]);
            }
        }
    }
    
    // Wszystkie dane pobrane i zapisane - zamknięcie połączenia
    $statement = $insert = null;   // obiekty zapytań też trzymają połączenie
    $pdo = null;
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Zdrowy bazarek</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zdrowy bazarek</h1>
        </header>
        <nav>
    <?php foreach ($fruits as $fruit): ?>
            <img src="<?= escape($fruit['plik']) ?>" alt="<?= escape($fruit['nazwa']) ?>">
    <?php endforeach; ?>
        </nav>
        <main>
            <aside>
                <img src="market.png" alt="bazarek">
            </aside>
            <section>
                <p>Wybierz owoc lub warzywo i podaj jego wagę:</p>
                <form method="post">
                    <select name="product" aria-label="Towar">
    <?php foreach ($products as $product): ?>
                        <option value="<?= escape($product['id']) ?>"><?= escape($product['nazwa']) ?></option>
    <?php endforeach; ?>
                    </select>
                    <input type="number" name="weight" min="1" step="1" required aria-label="Liczba kilogramów">
                    <button type="submit">Zamów</button>
                </form>
    <?php if ($order !== null): ?>
                <p><?= escape($order['rodzaj']) ?> <?= escape($order['nazwa']) ?> wartość: <?= number_format($value, 2, ',', ' ') ?> zł</p>
    <?php elseif ($message !== ''): ?>
                <p><?= escape($message) ?></p>
    <?php endif; ?>
            </section>
        </main>
        <footer>
            <p>Stronę opracował: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>

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

  44. Krok 44: Lista pamięta zamówiony towar

    ok. 2 min
    Polecenie z arkusza

    Lista rozwijana wypełniona skryptem 2

    Na ilustracji 5 po zamówieniu na liście nadal stoi „pietruszka”. Strona ładuje się od nowa, więc lista sama wróciłaby do gruszki - dlatego opcja, której id jest równe wysłanemu, dostaje atrybut selected. Porównanie ścisłe === działa, bo PDO w PHP 8.2 oddaje kolumny INT jako liczby całkowite, a $productId po filter_var też jest liczbą.

    $productId = null; przed warunkiem sprawia, że przy zwykłym wejściu porównanie ma z czym się wykonać i żadna opcja nie jest zaznaczona na siłę. Klucz tego nie ocenia - to zgodność z ilustracją i wygoda kupującego.

    index.php PHP zmienione: 2 linie
    <?php
    // Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumn
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=bazar;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    } catch (PDOException $exception) {
        http_response_code(500);
        exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');
    }
    
    function escape(string|int|float|null $value): string
    {
        return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
    }
    
    // Skrypt 1: dziesięć pierwszych towarów do paska zdjęć (zapytanie 1)
    $fruits = $pdo->query('SELECT nazwa, plik FROM towar LIMIT 10')->fetchAll();
    
    // Skrypt 2: wszystkie towary do listy rozwijanej (zapytanie 2)
    $products = $pdo->query('SELECT id, nazwa FROM towar')->fetchAll();
    
    // Skrypt 3: zamówienie - tylko po wysłaniu formularza (zapytania 3 i 4)
    $order = null;
    $message = '';
    $productId = null;
    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        $productId = filter_var($_POST['product'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
        $weight = filter_var($_POST['weight'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'max_range' => 10000]]);
        if ($productId === false || $weight === false) {
            $message = 'Wybierz towar i podaj liczbę kilogramów - liczbę całkowitą większą od zera.';
        } else {
            $statement = $pdo->prepare('SELECT rodzaj, nazwa, cena FROM towar WHERE id = :id');
            $statement->execute(['id' => $productId]);
            $order = $statement->fetch() ?: null;
            if ($order === null) {
                $message = 'Nie ma takiego towaru.';
            } else {
                $value = $order['cena'] * $weight;
                $insert = $pdo->prepare('INSERT INTO zamowienie (id_towar, id_sklep, liczba_kg) VALUES (:product, 2, :weight)');
                $insert->execute(['product' => $productId, 'weight' => $weight]);
            }
        }
    }
    
    // Wszystkie dane pobrane i zapisane - zamknięcie połączenia
    $statement = $insert = null;   // obiekty zapytań też trzymają połączenie
    $pdo = null;
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Zdrowy bazarek</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zdrowy bazarek</h1>
        </header>
        <nav>
    <?php foreach ($fruits as $fruit): ?>
            <img src="<?= escape($fruit['plik']) ?>" alt="<?= escape($fruit['nazwa']) ?>">
    <?php endforeach; ?>
        </nav>
        <main>
            <aside>
                <img src="market.png" alt="bazarek">
            </aside>
            <section>
                <p>Wybierz owoc lub warzywo i podaj jego wagę:</p>
                <form method="post">
                    <select name="product" aria-label="Towar">
    <?php foreach ($products as $product): ?>
                        <option value="<?= escape($product['id']) ?>"<?= $product['id'] === $productId ? ' selected' : '' ?>><?= escape($product['nazwa']) ?></option>
    <?php endforeach; ?>
                    </select>
                    <input type="number" name="weight" min="1" step="1" required aria-label="Liczba kilogramów">
                    <button type="submit">Zamów</button>
                </form>
    <?php if ($order !== null): ?>
                <p><?= escape($order['rodzaj']) ?> <?= escape($order['nazwa']) ?> wartość: <?= number_format($value, 2, ',', ' ') ?> zł</p>
    <?php elseif ($message !== ''): ?>
                <p><?= escape($message) ?></p>
    <?php endif; ?>
            </section>
        </main>
        <footer>
            <p>Stronę opracował: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  45. Etap 5: Porównanie z wersją podstawową

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

    Krok 45: Co jest inaczej i dlaczego

    ok. 3 min
    Polecenie z arkusza

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

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

    wersja podstawowata wersja
    układdisplay: flex na <main>, width: 50% w regule blokówdisplay: grid i grid-template-columns: 50% 50%
    kolorywpisane w każdej regulezmienne w :root
    bazamysqli_* z tabeli 1PDO, wyjątki, FETCH_ASSOC
    dane w zapytaniachsklejane z napisemparametry nazwane :id, :product, :weight
    wejście$_POST wprostfilter_var z zakresem, komunikat przy błędzie
    wypisywanieecho w pętli whilelogika na górze, szablon z foreach i escape()

    Czego tu jeszcze nie ma, a w prawdziwym sklepie by było: token CSRF w formularzu (ukryte pole z losową wartością z sesji, sprawdzane przy zapisie - blokuje wysłanie zamówienia z cudzej strony), logowanie kupującego zamiast stałego sklepu 2 i PRG - przekierowanie po zapisie z wynikiem pokazanym na kolejnej stronie, żeby F5 nie dublował zamówień. Klucz tego nie wymaga.

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

  46. Krok 46: 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 nie jest jeszcze opanowane: funkcje mysqli_* są w tabeli 1 arkusza, a każdy kawałek PHP stoi tam, gdzie widać jego efekt.
    • Ta wersja, gdy PDO i siatka CSS są pisane z pamięci. Kodu jest podobnie dużo, a błąd łatwiej znaleźć: wyjątek PDO wskazuje złe zapytanie, zamiast zostawiać pustą listę.

    Mieszanie też daje pełne punkty: siatka z tej wersji i mysqli_* z podstawowej. Najważniejsze, żeby pisać to, co się umie bez zaglądania do dokumentacji - na egzaminie jej nie ma.

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

  47. Etap 6: Oddanie pracy

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

    Krok 47: 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. 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/….

    W folderze z numerem zdającego: import.png, index.php, jabłko.png, kw1.png - kw4.png, kwerendy.txt, market.png, przeglądarka.txt, styl.css oraz pozostałe zdjęcia owoców z archiwum (Skrypt 1 ich używa). Po nagraniu sprawdź odczyt płyty.

    przeglądarka.txt Tekst nowy plik
    Google Chrome

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

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.