Przejdź do głównej treści
EE.09-03-22.06-SGCzerwiec 2022 · 44 kroki · ok. 84 min · 36 kryteriów z klucza

Rozwiązanie krok po kroku: Opinie klientów hurtowni spożywczej (Wersja podstawowa)

Fragment aplikacji hurtowni z opiniami klientów: baza hurtownia zaimportowana z pliku baza.sql (tabele klienci, opinie, typy) i cztery zapytania - trzech klientów z największą liczbą punktów (ORDER BY … DESC LIMIT 3), liczba klientów każdego typu (COUNT z GROUP BY), opinie klientów złotych i platynowych (JOIN) oraz nowa kolumna adres (ALTER TABLE). Do tego zdjęcie ewa.jpg przeskalowane do szerokości 150 px, strona opinie.php z banerem, przewijanym blokiem opinii i czterema blokami stopki, arkusz styl3.css z cieniem bloków i zaokrąglonymi zdjęciami oraz dwa skrypty PHP: bloki opinii i lista najlepszych klientów.

To arkusz EE.09 z czerwca 2022 - kwalifikacji formuły 2017, którą zastąpiła INF.03. Kroki idą zdanie po zdaniu za treścią arkusza - przy każdym jest polecenie z arkusza, a kod pokazuje dokładnie te linie, które je spełniają. Style bloków opinii są dopisane po skryptach, bo dopiero skrypt tworzy te bloki i dopiero wtedy w podglądzie widać efekt. Skrypty są napisane najprościej, funkcjami mysqli z tabeli 1 arkusza, a na końcu sprawdzone na prawdziwym ataku (skrypt wpisany w treść opinii i w nazwę zdjęcia) i na awarii bazy - i zabezpieczone.

To jest wersja podstawowa. W zakładce „Nowoczesny kod (grid, PDO)” jest drugi sposób rozwiązania tego samego arkusza: siatka CSS, zmienne CSS, PDO z obsługą błędów na górze pliku i htmlspecialchars przy każdym wypisaniu - obie wersje są zgodne z kluczem.

Baza i zdjęcia pochodzą z oryginalnego archiwum CKE zad3.zip (załącznik do pobrania na stronie arkusza, hasło w treści zadania). Zapytania i zrzuty są policzone na MariaDB 10.4 - tej samej, która jest w XAMPP. Podgląd od kroków z PHP to wynik prawdziwego uruchomienia kodu w PHP 8.2 (klucz każe sprawdzać w XAMPP 8.1.2 - użyte funkcje działają w obu wersjach tak samo).

Jak liczą się punkty. Klucz ma 36 kryteriów (R.1.1 - R.5.8), każde za 1 punkt. Próg zdania części praktycznej to 75%, czyli 27 punktów - można stracić 9 kryteriów i nadal zdać. Przy każdym kroku jest napisane, które kryteria spełnia; egzaminator ocenia spełnienie kryteriów z klucza, nie zgodność z tym kodem.

  1. Etap 1: Baza danych

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

    Krok 1: Folder zdającego i archiwum zad3.zip

    ok. 2 min
    Polecenie z arkusza

    Na pulpicie znajduje się archiwum o nazwie zad3.zip zabezpieczone hasłem: #_Opini3% Archiwum należy rozpakować. Na pulpicie konta Egzamin należy utworzyć folder. Jako nazwy folderu należy użyć numeru zdającego, którym został podpisany arkusz. Rozpakowane pliki należy umieścić w tym folderze.

    Na pulpicie tworzysz folder nazwany numerem PESEL - w tym rozwiązaniu w jego miejscu stoi imię i nazwisko autora, Bartosz Bryniarski. Archiwum zad3.zip rozpakowujesz do tego folderu z hasłem #_Opini3% (krzyżyk na początku i procent na końcu należą do hasła, w środku jest cyfra 3, nie litera). W środku: baza.sql i pięć zdjęć klientów - anna.jpg, ewa.jpg, janek.jpg, john.jpg, judy.jpg.

    Arkusz zaleca, żeby zdjęcia leżały w tym samym folderze co strona - skrypt wpisze ich nazwy z bazy wprost do atrybutu src. Stronę uruchamiasz przez XAMPP, więc folder kopiujesz też do C:\xampp\htdocs (albo pracujesz w htdocs i na koniec kopiujesz wyniki do folderu zdającego).

    Typowe błędy

    Pliki zapisane tylko w htdocs nie trafią na płytę - a ocenia się wyłącznie to, co jest na płycie.

  2. Krok 2: XAMPP i pusta baza hurtownia

    ok. 2 min
    Polecenie z arkusza

    Za pomocą narzędzia phpMyAdmin należy wykonać operacje na bazie danych: Utworzenie bazy danych o nazwie: hurtownia z zestawem polskich znaków (np. utf8_unicode_ci)

    W XAMPP Control Panel klikasz Start przy Apache i MySQL, potem w przeglądarce otwierasz http://localhost/phpmyadmin. Nowa w lewym panelu, nazwa hurtownia, porównywanie napisów utf8_unicode_ci, Utwórz.

    Porównywanie decyduje o tym, jak baza zapisuje i sortuje polskie litery - typy klientów to „zwykły” i „złoty”, a opinie są pełne „ś” i „ż”.

    Typowe błędy

    Nazwa bazy musi brzmieć dokładnie hurtownia - skrypt PHP wybiera bazę po nazwie (R.5.1).

  3. Krok 3: Import pliku baza.sql i zrzut import.jpg

    Kryteria:

    R.1.1 Wykonano import tabel do bazy danych hurtownia , czynność udokumentowano plikiem o nazwie import w formacie JPEG

    ok. 3 min
    Polecenie z arkusza

    Import tabel do bazy hurtownia z pliku baza.sql. Czynność należy udokumentować zrzutem ekranu, na którym powinny być widoczne elementy wskazujące na poprawnie wykonany import tabel. Zrzut należy zapisać w formacie JPEG, pod nazwą import. Powinien on obejmować cały ekran monitora, z widocznym paskiem zadań.

    Zaznacz bazę hurtownia w lewym panelu, karta Import, Wybierz plik → baza.sql, Wykonaj. Plik tworzy trzy tabele, wypełnia je danymi i ustawia klucze główne z AUTO_INCREMENT.

    Po imporcie na karcie Struktura bazy widać: klienci (9 rekordów), opinie (6) i typy (3). Zrzut całego ekranu z zielonym komunikatem o sukcesie i paskiem zadań zapisujesz jako import.jpg (R.1.1).

    Typowe błędy

    Ten zrzut ma format JPEG, a zrzuty zapytań - PNG. Wykadrowany zrzut albo zrzut bez paska zadań nie jest oceniany (uwaga w kluczu przy R.1).

    Podgląd strony po tym kroku
    Podgląd strony po kroku 3
  4. Krok 4: Zapytanie 1: trzech klientów z największą liczbą punktów

    Kryteria: ,

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

    R.1.3 Utworzono zapytanie 1 wybierające posortowane malejąco według punktów jedynie imię, nazwisko i punkty trzech klientów, których liczba punktów jest najwyższa. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT imie, nazwisko, punkty FROM klienci ORDER BY punkty DESC LIMIT 3; oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczne są jedynie 3 wiersze w kolejności: Jan Nowak 3305, Judy Beck 3055, Ewa Nowakowska 2045

    ok. 3 min
    Polecenie z arkusza

    Zapytania SQL na bazie hurtownia zapisane do pliku kwerendy.txt. Wykonanie kwerend należy udokumentować zrzutami w formacie PNG o nazwach kw1, kw2, kw3, kw4. Zrzuty powinny obejmować cały ekran monitora z widocznym paskiem zadań. Zapytanie 1: wybierające z tabeli Klienci posortowane malejąco według punktów jedynie imię, nazwisko i punkty trzech klientów, których liczba punktów jest najwyższa

    Karta SQL bazy hurtownia, zapytanie klauzula po klauzuli:

    • SELECT imie, nazwisko, punkty - tylko trzy kolumny,
    • FROM klienci,
    • ORDER BY punkty DESC - sortowanie według punktów, malejąco (DESC, od największej liczby),
    • LIMIT 3 - z posortowanej listy tylko trzy pierwsze wiersze, czyli trzech klientów z największą liczbą punktów.

    Wynik: Jan Nowak 3305, Judy Beck 3055, Ewa Nowakowska 2045 - jak w kluczu. Zapytanie kopiujesz do nowego pliku kwerendy.txt (R.1.2), a cały ekran zapisujesz jako kw1.png (R.1.3). To zapytanie wykona skrypt 2.

    Typowe błędy

    Bez DESC sortowanie jest rosnące, a LIMIT 3 bierze klientów z najmniejszą liczbą punktów (Kris Smith, Jacek Jackowski, Krystyna Nowakowska).

    kwerendy.txt Tekst nowy plik
    -- Zapytanie 1
    SELECT imie, nazwisko, punkty
    FROM klienci
    ORDER BY punkty DESC
    LIMIT 3;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 4
  5. Krok 5: Zapytanie 2: liczba klientów każdego typu

    Kryteria:

    R.1.4 Utworzono zapytanie 2 wybierające nazwę z tabeli Typy oraz liczbę klientów przypisanych do każdego typu. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa, COUNT(nazwa) FROM klienci JOIN typy ON Typy_id = typy.id GROUP BY nazwa; (możliwe COUNT(*), GROUP BY(Typy_id), GROUP BY (Typy.id), porównanie kluczy w WHERE) oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczne są dokładnie 3 wiersze: platynowy 2, złoty 3, zwykły 4

    ok. 3 min
    Polecenie z arkusza

    Zapytanie 2: wybierające nazwę z tabeli Typy oraz liczbę klientów przypisanych do każdego typu. Należy posłużyć się relacją

    „Posłużyć się relacją” znaczy połączyć tabele po kluczach: pole Typy_id w tabeli klienci wskazuje id w tabeli typy.

    • SELECT nazwa, COUNT(*) - nazwa typu i funkcja agregująca COUNT, która liczy wiersze,
    • FROM typy JOIN klienci ON typy.id = klienci.Typy_id - każdy klient dostaje swój typ,
    • GROUP BY nazwa - wiersze z tą samą nazwą typu tworzą jedną grupę, a COUNT(*) liczy klientów w każdej grupie osobno.

    Wynik: platynowy 2, złoty 3, zwykły 4 (razem 9 klientów). Zrzut: kw2.png (R.1.4).

    Typowe błędy

    Bez GROUP BY funkcja COUNT(*) liczy wszystkich klientów naraz - wynik to jeden wiersz z liczbą 9 i przypadkową nazwą typu.

    kwerendy.txt Tekst zmienione: 6 linii
    -- Zapytanie 1
    SELECT imie, nazwisko, punkty
    FROM klienci
    ORDER BY punkty DESC
    LIMIT 3;
    
    -- Zapytanie 2
    SELECT nazwa, COUNT(*)
    FROM typy
    JOIN klienci ON typy.id = klienci.Typy_id
    GROUP BY nazwa;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 5
  6. Krok 6: Zapytanie 3: zdjęcia, imiona i opinie klientów typu 2 i 3

    Kryteria:

    R.1.5 Utworzono zapytanie 3 wybierające jedynie pola zdjecie, imie z tabeli klienci oraz odpowiadające im pole opinia z tabeli opinie dla klientów, których typ ma id o wartości 2, 3. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT zdjecie, imie, opinia FROM klienci JOIN opinie ON klienci.id = klienci_id WHERE Typy_id=2 OR Typy_id=3; (lub porównanie kluczy po WHERE) oraz wynik działania zapytania udokumentowano zrzutem, na którym widocznych jest dokładnie 5 wierszy, jedynie pola zdjecie, imie, opinia dla imion: Anna, Ewa, Jan, John, Judy

    ok. 3 min
    Polecenie z arkusza

    Zapytanie 3: wybierające jedynie pola zdjecie, imie z tabeli Klienci oraz odpowiadające im pole opinia z tabeli opinie dla klientów, których typ ma id o wartościach 2, 3. Należy posłużyć się relacją

    Relacja tabel klienci i opinie: pole Klienci_id w tabeli opinie wskazuje id klienta.

    • SELECT zdjecie, imie, opinia - dwie kolumny z klienci, jedna z opinie,
    • FROM klienci JOIN opinie ON klienci.id = opinie.Klienci_id - każda opinia z jej autorem,
    • WHERE Typy_id IN (2, 3) - tylko klienci typu 2 (złoty) i 3 (platynowy); IN to krótszy zapis Typy_id = 2 OR Typy_id = 3.

    Wynik: 5 wierszy - Anna, Ewa, Jan, John i Judy z nazwami plików zdjęć i treścią opinii. Krystyna też ma opinię, ale jest klientką typu 1. Zrzut: kw3.png (R.1.5). To zapytanie wykona skrypt 1.

    Typowe błędy

    WHERE Typy_id = 2 AND Typy_id = 3 nie zwraca nic - klient ma jeden typ, więc nie może mieć naraz obu. Potrzebne jest OR albo IN.

    kwerendy.txt Tekst zmienione: 6 linii
    -- Zapytanie 1
    SELECT imie, nazwisko, punkty
    FROM klienci
    ORDER BY punkty DESC
    LIMIT 3;
    
    -- Zapytanie 2
    SELECT nazwa, COUNT(*)
    FROM typy
    JOIN klienci ON typy.id = klienci.Typy_id
    GROUP BY nazwa;
    
    -- Zapytanie 3
    SELECT zdjecie, imie, opinia
    FROM klienci
    JOIN opinie ON klienci.id = opinie.Klienci_id
    WHERE Typy_id IN (2, 3);
    Podgląd strony po tym kroku
    Podgląd strony po kroku 6
  7. Krok 7: Zapytanie 4: nowa kolumna adres

    Kryteria:

    R.1.6 Utworzono zapytanie 4 dodające do tabeli klienci kolumnę o nazwie adres i typie napisowym, maksymalnie 50-znakowym. W pliku z kwerendami lub na zrzucie istnieje zapis np.: ALTER TABLE klienci ADD adres VARCHAR(50); (lub ADD COLUMN, typ text jest błędem) oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczny jest komunikat o pomyślnie wykonanym zapytaniu lub tabela z dodaną kolumną

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 4: dodające do tabeli Klienci kolumnę o nazwie adres i typie napisowym, maksymalnie 50-znakowym

    To zapytanie zmienia strukturę tabeli, nie dane:

    • ALTER TABLE klienci - modyfikacja istniejącej tabeli,
    • ADD adres VARCHAR(50) - nowa kolumna adres; typ VARCHAR(50) to napis o zmiennej długości, najwyżej 50 znaków.

    phpMyAdmin odpowiada komunikatem „MySQL zwrócił pusty wynik (zero wierszy)”, a na karcie Struktura tabeli klienci kolumna adres stoi na końcu, z wartościami NULL. Zrzut: kw4.png (R.1.6). Plik kwerendy.txt ma komplet czterech zapytań.

    Typowe błędy

    Klucz uznaje typ TEXT za błąd - nie ma limitu 50 znaków. Zapytanie wykonane drugi raz kończy się błędem #1060 „Zdublowana nazwa kolumny” - kolumna już istnieje.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    SELECT imie, nazwisko, punkty
    FROM klienci
    ORDER BY punkty DESC
    LIMIT 3;
    
    -- Zapytanie 2
    SELECT nazwa, COUNT(*)
    FROM typy
    JOIN klienci ON typy.id = klienci.Typy_id
    GROUP BY nazwa;
    
    -- Zapytanie 3
    SELECT zdjecie, imie, opinia
    FROM klienci
    JOIN opinie ON klienci.id = opinie.Klienci_id
    WHERE Typy_id IN (2, 3);
    
    -- Zapytanie 4
    ALTER TABLE klienci ADD adres VARCHAR(50);
    Podgląd strony po tym kroku
    Podgląd strony po kroku 7
  8. Etap 2: Grafika i strona HTML

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

    Krok 8: Zdjęcie ewa.jpg: szerokość 150 px z zachowaniem proporcji

    Kryteria:

    R.2.1 Obraz w pliku ewa .jpg przeskalowano z zachowaniem proporcji do szerokości 150 px, odpowiadająca temu wysokość wynosi 200 px (± 2 px)

    ok. 2 min
    Polecenie z arkusza

    Cechy grafiki ewa.jpg: Przeskalowana z zachowaniem proporcji do szerokości 150 px

    Cztery zdjęcia z archiwum mają już 150 × 200 px, a ewa.jpg - 317 × 423 px. W GIMP-ie: Obraz → Skaluj obraz, łańcuch zamknięty (zachowanie proporcji), szerokość 150 px - wysokość wylicza się sama: 423 × 150 / 317 ≈ 200 px. Plik → Wyeksportuj jako pod tą samą nazwą ewa.jpg (R.2.1).

    Teraz wszystkie pięć zdjęć ma ten sam rozmiar, więc bloki opinii będą równe.

    Typowe błędy

    Przy otwartym łańcuchu wysokość zostaje 423 px i zdjęcie jest ściśnięte w poziomie. Plik → Zapisz zapisuje format .xcf - do JPEG służy Wyeksportuj jako.

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

    Kryteria:

    R.2.2 Witrynę internetową zapisano w pliku o nazwie opinie.php

    ok. 1 min
    Polecenie z arkusza

    Cechy witryny: Składa się ze strony o nazwie opinie.php

    W edytorze zaznaczającym składnię tworzysz plik opinie.php w folderze zdającego (R.2.2) - na razie szkielet HTML: <html>, w nim <head> i <body>. Rozszerzenie .php, bo dojdą skrypty. Dopóki w pliku nie ma kodu PHP, podgląd pokazuje go jak zwykłą stronę.

    opinie.php PHP nowy plik
    <html>
    <head>
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (opinie.php)
  10. Krok 10: Deklaracja HTML5

    ok. 1 min
    Polecenie z arkusza

    Zapisana w języku HTML5

    <!DOCTYPE html> w pierwszej linii pliku. Bez niej przeglądarka przechodzi w tryb zgodności ze starymi stronami i inaczej liczy wysokości bloków.

    opinie.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html>
    <head>
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (opinie.php)
  11. Krok 11: 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>, a plik zapisany w UTF-8 (w Notepad++ menu Kodowanie).

    opinie.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (opinie.php)
  12. Krok 12: Język strony: polski

    Kryteria:

    R.2.3 Zapisano jawnie standard kodowania polskich znaków i deklarację HTML5 <!DOCTYPE HTML> oraz zadeklarowano dla witryny język polski (np. lang = 'pl')

    ok. 1 min
    Polecenie z arkusza

    Zadeklarowany język witryny: polski

    Atrybut lang="pl" w znaczniku <html> - przeglądarka i czytnik ekranu wiedzą, w jakim języku jest tekst. Deklaracja HTML5, kodowanie i język są gotowe (R.2.3).

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

    Kryteria:

    R.2.4 Nadano tytuł strony: Opinie klientów

    ok. 1 min
    Polecenie z arkusza

    Tytuł strony widoczny na karcie przeglądarki: „Opinie klientów”

    <title> w <head> - napis na karcie przeglądarki (R.2.4).

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

    Kryteria:

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

    ok. 1 min
    Polecenie z arkusza

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

    <link rel="stylesheet" href="styl3.css"> w <head>. Plik stylów powstanie po HTML-u; całe formatowanie strony ma pochodzić z niego (R.3.3).

    Typowe błędy

    Klucz odbiera R.3.3 także wtedy, gdy choć jeden selektor nie trafia w swój blok (np. kropka zamiast #).

    opinie.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Opinie klientów</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (opinie.php)
  15. Krok 15: Bloki: baner, główny i cztery bloki stopki

    Kryteria:

    R.2.5 Wygląd strony zdefiniowano dzieląc ją na bloki: baner, główny i cztery bloki stopki. Zastosowano znaczniki sekcji

    ok. 2 min
    Polecenie z arkusza

    Podział strony na bloki: baner, główny i cztery bloki stopki. Podział zrealizowany za pomocą znaczników sekcji tak, aby wygląd układu bloków strony po uruchomieniu w przeglądarce, był zgodny z Obrazem 2

    Sześć bloków w kolejności z obrazu 2: baner <header>, blok główny <main> i cztery bloki stopki <footer> (R.2.5). Znacznik <footer> może wystąpić na stronie wiele razy, a wszystkie cztery bloki mają ten sam styl - CSS obejmie je jednym selektorem footer, bez klas i identyfikatorów. Na razie bloki stoją jeden pod drugim; cztery bloki stopki obok siebie ustawi CSS.

    opinie.php PHP zmienione: 12 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Opinie klientów</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
        </header>
        <main>
        </main>
        <footer>
        </footer>
        <footer>
        </footer>
        <footer>
        </footer>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (opinie.php)
  16. Krok 16: Baner: nagłówek pierwszego stopnia

    ok. 1 min
    Polecenie z arkusza

    Zawartość banera: nagłówek pierwszego stopnia o treści: „Hurtownia spożywcza”

    <h1> w <header>. Kryterium R.2.6 sprawdza nagłówki i paragraf razem - punkt przyjdzie, gdy skrypt 1 wypisze nagłówki <h4>.

    opinie.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Opinie klientów</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia spożywcza</h1>
        </header>
        <main>
        </main>
        <footer>
        </footer>
        <footer>
        </footer>
        <footer>
        </footer>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (opinie.php)
  17. Krok 17: Blok główny: nagłówek drugiego stopnia

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku głównego: Nagłówek drugiego stopnia o treści: „Opinie naszych klientów”

    <h2> w <main>. Pod nim arkusz każe umieścić „efekt działania skryptu 1” - bloki opinii dopisze skrypt w części o PHP.

    opinie.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Opinie klientów</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia spożywcza</h1>
        </header>
        <main>
            <h2>Opinie naszych klientów</h2>
        </main>
        <footer>
        </footer>
        <footer>
        </footer>
        <footer>
        </footer>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (opinie.php)
  18. Krok 18: Pierwszy blok stopki: nagłówek trzeciego stopnia

    ok. 1 min
    Polecenie z arkusza

    Zawartość pierwszego bloku stopki: Nagłówek trzeciego stopnia o treści: „Współpracują z nami”

    <h3> w pierwszym <footer> - zamiana dotyczy pierwszego z czterech pustych bloków.

    opinie.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Opinie klientów</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia spożywcza</h1>
        </header>
        <main>
            <h2>Opinie naszych klientów</h2>
        </main>
        <footer>
            <h3>Współpracują z nami</h3>
        </footer>
        <footer>
        </footer>
        <footer>
        </footer>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (opinie.php)
  19. Krok 19: Odnośnik „Sklep 1”

    Kryteria:

    R.3.2 Na stronie umieszczono odnośnik, którego kliknięcie inicjuje otwarcie strony http://sklep.pl/

    ok. 1 min
    Polecenie z arkusza

    Odnośnik o treści „Sklep 1” prowadzący do adresu http://sklep.pl/

    Odnośnik <a> z pełnym adresem w href, razem z http:// - bez niego przeglądarka szukałaby pliku sklep.pl w folderze strony (R.3.2).

    opinie.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Opinie klientów</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia spożywcza</h1>
        </header>
        <main>
            <h2>Opinie naszych klientów</h2>
        </main>
        <footer>
            <h3>Współpracują z nami</h3>
            <a href="http://sklep.pl/">Sklep 1</a>
        </footer>
        <footer>
        </footer>
        <footer>
        </footer>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (opinie.php)
  20. Krok 20: Drugi blok stopki: nagłówek i lista numerowana

    ok. 1 min
    Polecenie z arkusza

    Zawartość drugiego bloku stopki: Nagłówek trzeciego stopnia o treści: „Nasi top klienci” Lista numerowana (uporządkowana), a w niej efekt działania skryptu 2

    <h3> i pusta lista <ol> (ordered list - numerowana). Elementy <li> wypisze skrypt 2, więc w HTML-u lista zostaje pusta - podgląd pokazuje na razie sam nagłówek.

    opinie.php PHP zmienione: 3 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Opinie klientów</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia spożywcza</h1>
        </header>
        <main>
            <h2>Opinie naszych klientów</h2>
        </main>
        <footer>
            <h3>Współpracują z nami</h3>
            <a href="http://sklep.pl/">Sklep 1</a>
        </footer>
        <footer>
            <h3>Nasi top klienci</h3>
            <ol>
            </ol>
        </footer>
        <footer>
        </footer>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (opinie.php)
  21. Krok 21: Trzeci blok stopki: nagłówek i telefon

    ok. 1 min
    Polecenie z arkusza

    Zawartość trzeciego bloku stopki: Nagłówek trzeciego stopnia o treści: „Skontaktuj się” Paragraf o treści: „telefon: 111222333”

    <h3> i paragraf <p> z numerem telefonu.

    opinie.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Opinie klientów</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia spożywcza</h1>
        </header>
        <main>
            <h2>Opinie naszych klientów</h2>
        </main>
        <footer>
            <h3>Współpracują z nami</h3>
            <a href="http://sklep.pl/">Sklep 1</a>
        </footer>
        <footer>
            <h3>Nasi top klienci</h3>
            <ol>
            </ol>
        </footer>
        <footer>
            <h3>Skontaktuj się</h3>
            <p>telefon: 111222333</p>
        </footer>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (opinie.php)
  22. Krok 22: Czwarty blok stopki: autor

    ok. 1 min
    Polecenie z arkusza

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

    Też <h3>, a nie paragraf. Na egzaminie po dwukropku wpisujesz swój numer PESEL; tu stoi Bartosz Bryniarski.

    opinie.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Opinie klientów</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia spożywcza</h1>
        </header>
        <main>
            <h2>Opinie naszych klientów</h2>
        </main>
        <footer>
            <h3>Współpracują z nami</h3>
            <a href="http://sklep.pl/">Sklep 1</a>
        </footer>
        <footer>
            <h3>Nasi top klienci</h3>
            <ol>
            </ol>
        </footer>
        <footer>
            <h3>Skontaktuj się</h3>
            <p>telefon: 111222333</p>
        </footer>
        <footer>
            <h3>Autor: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (opinie.php)
  23. Etap 3: Styl CSS

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

    Krok 23: Plik styl3.css: krój dla wszystkich selektorów

    Kryteria:

    R.4.1 Ustawiono domyślną wartość dla wszystkich selektorów stylu CSS (*): krój czcionki Tahoma, sans-serif (czcionka Tahoma jako pierwsza)

    ok. 1 min
    Polecenie z arkusza

    Styl CSS zdefiniowany w całości w zewnętrznym pliku o nazwie styl3.css. Domyślna wartość dla wszystkich selektorów stylu CSS: krój czcionki Tahoma, w przypadku braku sans-serif

    Plik styl3.css obok opinie.php. Selektor * (gwiazdka) obejmuje każdy element strony. Tahoma, sans-serif - najpierw Tahoma, a gdy jej nie ma w systemie, dowolny krój bezszeryfowy (R.4.1).

    styl3.css CSS nowy plik
    * {
        font-family: Tahoma, sans-serif;
    }
    Podgląd strony po tym kroku (opinie.php)
  24. Krok 24: Baner: tło Tomato, biały tekst, marginesy wewnętrzne

    ok. 1 min
    Polecenie z arkusza

    Dla banera: kolor tła Tomato, biały kolor czcionki, marginesy wewnętrzne 10 px

    Nazwy kolorów CSS (Tomato, white) wolno pisać dowolną wielkością liter. Marginesy wewnętrzne to padding.

    styl3.css CSS zmienione: 6 linii
    * {
        font-family: Tahoma, sans-serif;
    }
    
    header {
        background-color: Tomato;
        color: white;
        padding: 10px;
    }
    Podgląd strony po tym kroku (opinie.php)
  25. Krok 25: Blok główny: tło Seashell, wysokość i przewijanie

    ok. 2 min
    Polecenie z arkusza

    Dla bloku głównego: kolor tła Seashell, wysokość 500 px, paski przewijania dodawane są, gdy zawartość bloku jest większa od jego rozmiaru

    Wysokość 500 px jest stała, a pięć bloków opinii zajmie znacznie więcej. overflow: auto dodaje pasek przewijania tylko wtedy, gdy zawartość się nie mieści - dokładnie to opisuje arkusz. overflow: scroll pokazywałby paski zawsze, także pusty poziomy.

    styl3.css CSS zmienione: 6 linii
    * {
        font-family: Tahoma, sans-serif;
    }
    
    header {
        background-color: Tomato;
        color: white;
        padding: 10px;
    }
    
    main {
        background-color: Seashell;
        height: 500px;
        overflow: auto;
    }
    Podgląd strony po tym kroku (opinie.php)
  26. Krok 26: Bloki stopki: tło, kolor, szerokość i wysokość

    Kryteria: ,

    R.4.2 Ustawiono kolor tła Tomato dla banera i wszystkich bloków stopki, Seashell dla bloku głównego

    R.4.4 Ustawiono wysokość bloku głównego 500px i wszystkich bloków stopki 160px oraz szerokość wszystkich bloków stopki 25% (width lub grid-template-columns lub flex). Dla bloku głównego ustawiono paski przewijania overflow:auto;

    ok. 2 min
    Polecenie z arkusza

    Wspólne dla wszystkich bloków stopki: kolor tła Tomato, biały kolor czcionki, szerokość 25%, wysokość 160 px

    Selektor znacznika footer obejmuje wszystkie cztery bloki naraz. Kolory tła banera, bloku głównego i stopki są gotowe (R.4.2), tak samo wysokości, szerokość bloków stopki i przewijanie bloku głównego (R.4.4). Bloki stopki mają już po 25% szerokości, ale stoją jeden pod drugim.

    styl3.css CSS zmienione: 7 linii
    * {
        font-family: Tahoma, sans-serif;
    }
    
    header {
        background-color: Tomato;
        color: white;
        padding: 10px;
    }
    
    main {
        background-color: Seashell;
        height: 500px;
        overflow: auto;
    }
    
    footer {
        background-color: Tomato;
        color: white;
        width: 25%;
        height: 160px;
    }
    Podgląd strony po tym kroku (opinie.php)
  27. Krok 27: Układ: cztery bloki stopki obok siebie

    Kryteria:

    R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z Obrazem 2 w arkuszu egzaminacyjnym, nie należy uwzględniać bloków z opiniami (prawidłowo zastosowano właściwości CSS układające bloki na stronie, np. float albo display flex albo grid)

    ok. 2 min
    Polecenie z arkusza

    Podział zrealizowany za pomocą znaczników sekcji tak, aby wygląd układu bloków strony po uruchomieniu w przeglądarce, był zgodny z Obrazem 2

    float: left w regule footer: pierwszy blok staje przy lewej krawędzi, każdy następny zaraz obok poprzedniego - 4 × 25% = 100% szerokości strony. Za stopką nie ma już żadnego bloku, więc clear nie jest potrzebne. Klucz R.3.1 przyjmuje float, flex albo grid; bloków opinii przy tym kryterium nie ocenia.

    Typowe błędy

    Marginesy albo ramki dodane blokom stopki powiększają je ponad 25% - czwarty blok nie zmieści się w wierszu i spadnie pod spód.

    styl3.css CSS zmienione: 1 linia
    * {
        font-family: Tahoma, sans-serif;
    }
    
    header {
        background-color: Tomato;
        color: white;
        padding: 10px;
    }
    
    main {
        background-color: Seashell;
        height: 500px;
        overflow: auto;
    }
    
    footer {
        background-color: Tomato;
        color: white;
        width: 25%;
        height: 160px;
        float: left;
    }
    Podgląd strony po tym kroku (opinie.php)
  28. Krok 28: Nagłówki h1 i h2: wyśrodkowanie

    ok. 1 min
    Polecenie z arkusza

    Dla selektorów nagłówka pierwszego i drugiego stopnia: wyrównanie tekstu do środka

    Dwa selektory po przecinku dostają ten sam styl. Selektory znaczników h1 i h2 - arkusz zastrzega, że style nagłówków, obrazu i cytatu mają być zapisane wyłącznie selektorami znaczników.

    styl3.css CSS zmienione: 5 linii
    * {
        font-family: Tahoma, sans-serif;
    }
    
    header {
        background-color: Tomato;
        color: white;
        padding: 10px;
    }
    
    main {
        background-color: Seashell;
        height: 500px;
        overflow: auto;
    }
    
    footer {
        background-color: Tomato;
        color: white;
        width: 25%;
        height: 160px;
        float: left;
    }
    
    h1,
    h2 {
        text-align: center;
    }
    Podgląd strony po tym kroku (opinie.php)
  29. Krok 29: Odnośnik: biały kolor

    Kryteria:

    R.4.3 Ustawiono biały kolor czcionki dla banera, wszystkich bloków stopki i odnośnika

    ok. 1 min
    Polecenie z arkusza

    Dla odnośnika: biały kolor czcionki

    Odnośnik nie dziedziczy koloru z bloku stopki - przeglądarka ma dla niego własny niebieski, dlatego potrzebna jest osobna reguła a. Biały kolor banera, bloków stopki i odnośnika jest gotowy (R.4.3).

    styl3.css CSS zmienione: 4 linie
    * {
        font-family: Tahoma, sans-serif;
    }
    
    header {
        background-color: Tomato;
        color: white;
        padding: 10px;
    }
    
    main {
        background-color: Seashell;
        height: 500px;
        overflow: auto;
    }
    
    footer {
        background-color: Tomato;
        color: white;
        width: 25%;
        height: 160px;
        float: left;
    }
    
    h1,
    h2 {
        text-align: center;
    }
    
    a {
        color: white;
    }
    Podgląd strony po tym kroku (opinie.php)
  30. Etap 4: Skrypty PHP

    Kroki 30-36 · po tym etapie masz 30 z 36 kryteriów (pkt)

    Krok 30: Połączenie z bazą hurtownia

    Kryteria:

    R.5.1 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy hurtownia

    ok. 3 min
    Polecenie z arkusza

    Skrypty łączą się z serwerem bazy danych na localhost, użytkownik root bez hasła, baza danych o nazwie hurtownia

    Dwa skrypty arkusza pracują na jednej bazie, więc połączenie wystarczy otworzyć raz - przed pierwszym z nich. Skrypt 1 stoi w bloku głównym pod nagłówkiem <h2>, dlatego blok PHP z połączeniem zaczyna się właśnie tam.

    mysqli_connect z tabeli 1 arkusza - kolejno serwer localhost, użytkownik root, puste hasło '' i nazwa bazy hurtownia: jedno wywołanie łączy z serwerem i wybiera bazę (R.5.1). Połączenie trafia do zmiennej $connection, z której skorzysta też skrypt 2 w stopce - zmienne PHP żyją przez cały plik.

    Od tego kroku podgląd to wynik działania strony po stronie serwera (prawdziwe uruchomienie w PHP 8.2) - na razie wygląda jak wcześniej.

    opinie.php PHP zmienione: 3 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Opinie klientów</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia spożywcza</h1>
        </header>
        <main>
            <h2>Opinie naszych klientów</h2>
            <?php
            $connection = mysqli_connect('localhost', 'root', '', 'hurtownia');
            ?>
        </main>
        <footer>
            <h3>Współpracują z nami</h3>
            <a href="http://sklep.pl/">Sklep 1</a>
        </footer>
        <footer>
            <h3>Nasi top klienci</h3>
            <ol>
            </ol>
        </footer>
        <footer>
            <h3>Skontaktuj się</h3>
            <p>telefon: 111222333</p>
        </footer>
        <footer>
            <h3>Autor: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (opinie.php)
  31. Krok 31: Skrypt 1: zapytanie 3, blok opinii i zdjęcie

    Kryteria: , ,

    R.5.3 Przynajmniej jedno zapytanie zostało wysłane do bazy: skrypt 1 - zapytanie 3 lub skrypt 2 - zapytanie 1

    R.5.5 Skrypt 1 dla każdego zwróconego wiersza wyświetla obraz, którego nazwa pliku jest pobrana z bazy

    R.2.8 W bloku głównym umieszczono przynajmniej jeden obraz z tekstem alternatywnym "klient" (w skrypcie lub HTML)

    ok. 4 min
    Polecenie z arkusza

    Działanie skryptu 1: Wysyła do bazy zapytanie 3. Każdy zwrócony zapytaniem wiersz wypisuje w osobnym bloku opinii, według wzoru: Obraz, którego nazwa pliku została pobrana z bazy danych, z tekstem alternatywnym „klient”

    • mysqli_query($connection, '…') - zapytanie 3 z kwerendy.txt, w jednej linii (R.5.3),
    • while ($row = mysqli_fetch_row($result)) - pętla bierze kolejne wiersze wyniku jako tablicę numerowaną od zera: $row[0] to nazwa pliku zdjęcia, $row[1] - imię, $row[2] - opinia (kolejność kolumn z SELECT). Gdy wierszy zabraknie, funkcja zwraca false i pętla się kończy,
    • każdy wiersz to osobny blok <div class='opinion'> - klasa, bo bloków jest kilka i wszystkie mają ten sam styl (klucz przy R.4.8: identyfikator jest błędem),
    • w bloku <img src='$row[0]' alt='klient'> - nazwa pliku z bazy w atrybucie src (R.5.5) i tekst alternatywny z arkusza (R.2.8). Napis w echo jest w cudzysłowie, więc PHP podstawia $row[0], a wartości atrybutów są w apostrofach, żeby nie zamykać cudzysłowu.

    Podgląd: pięć zdjęć jedno pod drugim - bloki nie mają jeszcze stylu.

    Typowe błędy

    Zdjęcia muszą leżeć w tym samym folderze co opinie.php - w bazie są same nazwy plików, bez ścieżek. Zdjęcie w podfolderze wymaga zmiany na src='img/$row[0]'.

    opinie.php PHP zmienione: 6 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Opinie klientów</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia spożywcza</h1>
        </header>
        <main>
            <h2>Opinie naszych klientów</h2>
            <?php
            $connection = mysqli_connect('localhost', 'root', '', 'hurtownia');
            $result = mysqli_query($connection, 'SELECT zdjecie, imie, opinia FROM klienci JOIN opinie ON klienci.id = opinie.Klienci_id WHERE Typy_id IN (2, 3);');
            while ($row = mysqli_fetch_row($result)) {
                echo "<div class='opinion'>";
                echo "<img src='$row[0]' alt='klient'>";
                echo "</div>";
            }
            ?>
        </main>
        <footer>
            <h3>Współpracują z nami</h3>
            <a href="http://sklep.pl/">Sklep 1</a>
        </footer>
        <footer>
            <h3>Nasi top klienci</h3>
            <ol>
            </ol>
        </footer>
        <footer>
            <h3>Skontaktuj się</h3>
            <p>telefon: 111222333</p>
        </footer>
        <footer>
            <h3>Autor: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (opinie.php)
  32. Krok 32: Skrypt 1: opinia w cytacie

    Kryteria: ,

    R.2.9 W bloku głównym umieszczono przynajmniej jeden cytat, zastosowano znacznik <blockquote> lub <q> lub <cite> (w skrypcie lub HTML)

    R.5.4 Skrypt 1 dla każdego zwróconego wiersza tworzy blok, w którym znajdują się obraz i tekst

    ok. 2 min
    Polecenie z arkusza

    Cytat (element HTML) zawierający opinię, której treść została pobrana z bazy

    <blockquote> to znacznik dłuższego cytatu - tu słowa klienta (R.2.9). Treść opinii z $row[2]. Każdy blok ma teraz obraz i tekst (R.5.4).

    Klucz dopuszcza też <q> albo <cite>, ale <q> służy do krótkich cytatów w środku zdania (przeglądarka dokłada cudzysłowy), a <cite> - do tytułu dzieła. Styl cytatu ustawisz później selektorem blockquote.

    opinie.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Opinie klientów</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia spożywcza</h1>
        </header>
        <main>
            <h2>Opinie naszych klientów</h2>
            <?php
            $connection = mysqli_connect('localhost', 'root', '', 'hurtownia');
            $result = mysqli_query($connection, 'SELECT zdjecie, imie, opinia FROM klienci JOIN opinie ON klienci.id = opinie.Klienci_id WHERE Typy_id IN (2, 3);');
            while ($row = mysqli_fetch_row($result)) {
                echo "<div class='opinion'>";
                echo "<img src='$row[0]' alt='klient'>";
                echo "<blockquote>$row[2]</blockquote>";
                echo "</div>";
            }
            ?>
        </main>
        <footer>
            <h3>Współpracują z nami</h3>
            <a href="http://sklep.pl/">Sklep 1</a>
        </footer>
        <footer>
            <h3>Nasi top klienci</h3>
            <ol>
            </ol>
        </footer>
        <footer>
            <h3>Skontaktuj się</h3>
            <p>telefon: 111222333</p>
        </footer>
        <footer>
            <h3>Autor: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (opinie.php)
  33. Krok 33: Skrypt 1: imię klienta w nagłówku czwartego stopnia

    Kryteria: ,

    R.5.6 Skrypt 1 dla każdego zwróconego wiersza wyświetla opinię oraz imię pobrane z bazy

    R.2.6 W banerze zapisano nagłówek h1: "Hurtownia spożywcza", w bloku głównym zapisano nagłówek h2: "Opinie naszych klientów" oraz przynajmniej jeden h4 (w skrypcie lub HTML), w blokach stopki przynajmniej jeden nagłówek h3 "Współpracują z nami" lub "Nasi top klienci", lub "Skontaktuj się", lub "Autor:" oraz paragraf "telefon 111222333"

    ok. 2 min
    Polecenie z arkusza

    Nagłówek czwartego stopnia, a w nim pobrane z bazy imię klienta

    <h4> z imieniem z $row[1], pod cytatem. Skrypt 1 wypisuje dla każdego wiersza opinię i imię (R.5.6). Strona ma komplet nagłówków: <h1>, <h2>, <h3> w stopce i <h4> w blokach opinii, a do tego paragraf z telefonem (R.2.6).

    Podgląd: pięć opinii - Anna, Ewa, Jan, John i Judy, w kolejności z zapytania 3.

    opinie.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Opinie klientów</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia spożywcza</h1>
        </header>
        <main>
            <h2>Opinie naszych klientów</h2>
            <?php
            $connection = mysqli_connect('localhost', 'root', '', 'hurtownia');
            $result = mysqli_query($connection, 'SELECT zdjecie, imie, opinia FROM klienci JOIN opinie ON klienci.id = opinie.Klienci_id WHERE Typy_id IN (2, 3);');
            while ($row = mysqli_fetch_row($result)) {
                echo "<div class='opinion'>";
                echo "<img src='$row[0]' alt='klient'>";
                echo "<blockquote>$row[2]</blockquote>";
                echo "<h4>$row[1]</h4>";
                echo "</div>";
            }
            ?>
        </main>
        <footer>
            <h3>Współpracują z nami</h3>
            <a href="http://sklep.pl/">Sklep 1</a>
        </footer>
        <footer>
            <h3>Nasi top klienci</h3>
            <ol>
            </ol>
        </footer>
        <footer>
            <h3>Skontaktuj się</h3>
            <p>telefon: 111222333</p>
        </footer>
        <footer>
            <h3>Autor: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (opinie.php)
  34. Krok 34: Skrypt 2: zapytanie 1 i elementy listy

    Kryteria: ,

    R.5.8 Skrypt 2 wyświetla zwrócone wiersze jako elementy listy

    R.2.7 W drugim bloku stopki umieszczono listę numerowaną, zastosowano znaczniki <ol>, <li> (w skrypcie lub HTML)

    ok. 3 min
    Polecenie z arkusza

    Działanie skryptu 2: Wysyła do bazy zapytanie 1 i wypisuje zwrócone wiersze jako elementy listy

    Blok PHP wewnątrz <ol> w drugim bloku stopki - skrypt wypisuje same elementy <li>, a lista dookoła jest w HTML-u:

    • mysqli_query z zapytaniem 1 - trzech klientów z największą liczbą punktów. Wynik nadpisuje zmienną $result ze skryptu 1, który jest już skończony,
    • ta sama pętla while co w skrypcie 1,
    • echo "<li>$row[0] $row[1]</li>"; - na razie imię i nazwisko (R.5.8, R.2.7).

    Podgląd: lista „1. Jan Nowak, 2. Judy Beck, 3. Ewa Nowakowska” - numery dokłada sama lista <ol>.

    opinie.php PHP zmienione: 6 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Opinie klientów</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia spożywcza</h1>
        </header>
        <main>
            <h2>Opinie naszych klientów</h2>
            <?php
            $connection = mysqli_connect('localhost', 'root', '', 'hurtownia');
            $result = mysqli_query($connection, 'SELECT zdjecie, imie, opinia FROM klienci JOIN opinie ON klienci.id = opinie.Klienci_id WHERE Typy_id IN (2, 3);');
            while ($row = mysqli_fetch_row($result)) {
                echo "<div class='opinion'>";
                echo "<img src='$row[0]' alt='klient'>";
                echo "<blockquote>$row[2]</blockquote>";
                echo "<h4>$row[1]</h4>";
                echo "</div>";
            }
            ?>
        </main>
        <footer>
            <h3>Współpracują z nami</h3>
            <a href="http://sklep.pl/">Sklep 1</a>
        </footer>
        <footer>
            <h3>Nasi top klienci</h3>
            <ol>
                <?php
                $result = mysqli_query($connection, 'SELECT imie, nazwisko, punkty FROM klienci ORDER BY punkty DESC LIMIT 3;');
                while ($row = mysqli_fetch_row($result)) {
                    echo "<li>$row[0] $row[1]</li>";
                }
                ?>
            </ol>
        </footer>
        <footer>
            <h3>Skontaktuj się</h3>
            <p>telefon: 111222333</p>
        </footer>
        <footer>
            <h3>Autor: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (opinie.php)
  35. Krok 35: Skrypt 2: punkty według wzoru

    Kryteria:

    R.5.7 Skrypt 2 wyświetla zwrócone wiersze według wzoru: „<imie> <nazwisko>, <punkty> pkt.”, gdzie pola <> są pobrane z bazy

    ok. 1 min
    Polecenie z arkusza

    według wzoru: „<imie> <nazwisko>, <punkty> pkt.”, gdzie pola <> są pobrane z bazy

    Po nazwisku przecinek, spacja, liczba punktów z $row[2] i „pkt.” z kropką - znak po znaku jak we wzorze (R.5.7). Podgląd: „Jan Nowak, 3305 pkt.”, jak na obrazie 2.

    opinie.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Opinie klientów</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia spożywcza</h1>
        </header>
        <main>
            <h2>Opinie naszych klientów</h2>
            <?php
            $connection = mysqli_connect('localhost', 'root', '', 'hurtownia');
            $result = mysqli_query($connection, 'SELECT zdjecie, imie, opinia FROM klienci JOIN opinie ON klienci.id = opinie.Klienci_id WHERE Typy_id IN (2, 3);');
            while ($row = mysqli_fetch_row($result)) {
                echo "<div class='opinion'>";
                echo "<img src='$row[0]' alt='klient'>";
                echo "<blockquote>$row[2]</blockquote>";
                echo "<h4>$row[1]</h4>";
                echo "</div>";
            }
            ?>
        </main>
        <footer>
            <h3>Współpracują z nami</h3>
            <a href="http://sklep.pl/">Sklep 1</a>
        </footer>
        <footer>
            <h3>Nasi top klienci</h3>
            <ol>
                <?php
                $result = mysqli_query($connection, 'SELECT imie, nazwisko, punkty FROM klienci ORDER BY punkty DESC LIMIT 3;');
                while ($row = mysqli_fetch_row($result)) {
                    echo "<li>$row[0] $row[1], $row[2] pkt.</li>";
                }
                ?>
            </ol>
        </footer>
        <footer>
            <h3>Skontaktuj się</h3>
            <p>telefon: 111222333</p>
        </footer>
        <footer>
            <h3>Autor: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (opinie.php)
  36. Krok 36: Zamknięcie połączenia z bazą

    Kryteria:

    R.5.2 Ostatnią operacją na bazie jest jej zamknięcie (zgodne ze składnią)

    ok. 1 min
    Polecenie z arkusza

    Po wykonaniu operacji na bazie danych zamykane jest połączenie z serwerem

    mysqli_close($connection) po pętli skryptu 2 - to ostatnia operacja na bazie w całym pliku: skrypt 1 jest wcześniej, w bloku głównym (R.5.2). Strona ma już całą treść; zostały style bloków opinii.

    Typowe błędy

    mysqli_close na końcu skryptu 1 zamknie połączenie, zanim skrypt 2 z niego skorzysta - PHP zgłosi błąd „mysqli object is already closed” i lista w stopce zostanie pusta.

    opinie.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Opinie klientów</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia spożywcza</h1>
        </header>
        <main>
            <h2>Opinie naszych klientów</h2>
            <?php
            $connection = mysqli_connect('localhost', 'root', '', 'hurtownia');
            $result = mysqli_query($connection, 'SELECT zdjecie, imie, opinia FROM klienci JOIN opinie ON klienci.id = opinie.Klienci_id WHERE Typy_id IN (2, 3);');
            while ($row = mysqli_fetch_row($result)) {
                echo "<div class='opinion'>";
                echo "<img src='$row[0]' alt='klient'>";
                echo "<blockquote>$row[2]</blockquote>";
                echo "<h4>$row[1]</h4>";
                echo "</div>";
            }
            ?>
        </main>
        <footer>
            <h3>Współpracują z nami</h3>
            <a href="http://sklep.pl/">Sklep 1</a>
        </footer>
        <footer>
            <h3>Nasi top klienci</h3>
            <ol>
                <?php
                $result = mysqli_query($connection, 'SELECT imie, nazwisko, punkty FROM klienci ORDER BY punkty DESC LIMIT 3;');
                while ($row = mysqli_fetch_row($result)) {
                    echo "<li>$row[0] $row[1], $row[2] pkt.</li>";
                }
                mysqli_close($connection);
                ?>
            </ol>
        </footer>
        <footer>
            <h3>Skontaktuj się</h3>
            <p>telefon: 111222333</p>
        </footer>
        <footer>
            <h3>Autor: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>

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

  37. Etap 5: Styl bloków opinii

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

    Krok 37: Bloki opinii: tło, kolor, wymiary i wyśrodkowanie

    Kryteria:

    R.4.8 Ustawiono styl dla bloków opinii (np. klasą, id jest błędem): biały kolor tła, kolor czcionki DimGray, szerokość 60%, wysokość 220px, marginesy zewnętrzne (margin) ustawione są parami na 40px i auto

    ok. 2 min
    Polecenie z arkusza

    Dla bloków opinii generowanych skryptem: biały kolor tła, kolor czcionki DimGray, szerokość 60%, wysokość 220 px, marginesy zewnętrzne pionowe 40 px, poziome automatycznie wyliczane przez przeglądarkę

    Selektor klasy .opinion (z kropką) - tej, którą skrypt 1 dał każdemu blokowi. Skrót margin: 40px auto: pierwsza wartość to góra i dół, druga - lewa i prawa strona. auto z lewej i prawej przy szerokości 60% stawia blok na środku bloku głównego (R.4.8).

    Styl jest dopisany po skryptach, żeby w podglądzie było widać jego efekt - na egzaminie kolejność pisania nie ma znaczenia.

    Typowe błędy

    margin: auto 40px odwraca kierunki - bloki przykleją się do siebie i staną 40 px od lewej krawędzi.

    styl3.css CSS zmienione: 8 linii
    * {
        font-family: Tahoma, sans-serif;
    }
    
    header {
        background-color: Tomato;
        color: white;
        padding: 10px;
    }
    
    main {
        background-color: Seashell;
        height: 500px;
        overflow: auto;
    }
    
    footer {
        background-color: Tomato;
        color: white;
        width: 25%;
        height: 160px;
        float: left;
    }
    
    h1,
    h2 {
        text-align: center;
    }
    
    a {
        color: white;
    }
    
    .opinion {
        background-color: white;
        color: DimGray;
        width: 60%;
        height: 220px;
        margin: 40px auto;
    }
    Podgląd strony po tym kroku (opinie.php)
  38. Krok 38: Bloki opinii: cień

    Kryteria:

    R.4.9 Ustawiono cień dla bloku opinii bez przesunięcia, rozmyty o 10 px, o rozmiarze 5 px, kolorze DimGray np. box-shadow: 0 0 10px 5px DimGray

    ok. 2 min
    Polecenie z arkusza

    cień bez przesunięcia, rozmyty o 10 px, o rozmiarze 5 px, kolorze DimGray

    Kolejność wartości z pomocy w3schools na stronie 5 arkusza: box-shadow: h-offset v-offset blur spread color.

    • 0 0 - bez przesunięcia w poziomie i w pionie: cień rozchodzi się równo dookoła bloku,
    • 10px - rozmycie (blur),
    • 5px - rozmiar (spread) - o tyle cień wystaje poza blok,
    • DimGray - kolor (R.4.9).
    Typowe błędy

    box-shadow: 10px 5px DimGray to przesunięcie o 10 px w prawo i 5 px w dół, bez rozmycia - zupełnie inny cień.

    styl3.css CSS zmienione: 1 linia
    * {
        font-family: Tahoma, sans-serif;
    }
    
    header {
        background-color: Tomato;
        color: white;
        padding: 10px;
    }
    
    main {
        background-color: Seashell;
        height: 500px;
        overflow: auto;
    }
    
    footer {
        background-color: Tomato;
        color: white;
        width: 25%;
        height: 160px;
        float: left;
    }
    
    h1,
    h2 {
        text-align: center;
    }
    
    a {
        color: white;
    }
    
    .opinion {
        background-color: white;
        color: DimGray;
        width: 60%;
        height: 220px;
        margin: 40px auto;
        box-shadow: 0 0 10px 5px DimGray;
    }
    Podgląd strony po tym kroku (opinie.php)
  39. Krok 39: Zdjęcie: opływanie tekstem, marginesy, zaokrąglone rogi

    Kryteria:

    R.4.10 Ustawiono dla obrazu: opływanie tekstem po prawej jego stronie (np. float: left) i zaokrąglenie rogów o 10px, np. border-radius: 10px;

    ok. 2 min
    Polecenie z arkusza

    Dla selektora obrazu: opływany tekstem po jego prawej stronie, marginesy zewnętrzne 10 px, rogi obrazu zaokrąglone o 10 px

    • float: left - obraz przy lewej krawędzi bloku, a tekst opływa go po prawej stronie (o stronie decyduje to, gdzie jest tekst, nie obraz),
    • margin: 10px - odstęp od krawędzi bloku i od tekstu,
    • border-radius: 10px - zaokrąglone rogi (R.4.10).

    Zdjęcie ma 200 px wysokości, a z marginesami 220 px - tyle co blok opinii, więc mieści się w nim dokładnie.

    Typowe błędy

    float: right stawia obraz po prawej, a tekst opływa go z lewej - odwrotnie niż na obrazie 2.

    styl3.css CSS zmienione: 6 linii
    * {
        font-family: Tahoma, sans-serif;
    }
    
    header {
        background-color: Tomato;
        color: white;
        padding: 10px;
    }
    
    main {
        background-color: Seashell;
        height: 500px;
        overflow: auto;
    }
    
    footer {
        background-color: Tomato;
        color: white;
        width: 25%;
        height: 160px;
        float: left;
    }
    
    h1,
    h2 {
        text-align: center;
    }
    
    a {
        color: white;
    }
    
    .opinion {
        background-color: white;
        color: DimGray;
        width: 60%;
        height: 220px;
        margin: 40px auto;
        box-shadow: 0 0 10px 5px DimGray;
    }
    
    img {
        float: left;
        margin: 10px;
        border-radius: 10px;
    }
    Podgląd strony po tym kroku (opinie.php)
  40. Krok 40: Cytat: marginesy wewnętrzne, rozmiar i pochylenie

    ok. 2 min
    Polecenie z arkusza

    Dla selektora cytatu: marginesy wewnętrzne 10 px, czcionka o rozmiarze 150%, pochylona

    Selektor znacznika blockquote. font-size: 150% - półtora raza większa czcionka niż w bloku, font-style: italic - kursywa. Przeglądarka sama dodaje cytatowi marginesy zewnętrzne (40 px z lewej i z prawej), ale tekst i tak zaczyna się zaraz za zdjęciem - linie tekstu omijają pływający obraz.

    styl3.css CSS zmienione: 6 linii
    * {
        font-family: Tahoma, sans-serif;
    }
    
    header {
        background-color: Tomato;
        color: white;
        padding: 10px;
    }
    
    main {
        background-color: Seashell;
        height: 500px;
        overflow: auto;
    }
    
    footer {
        background-color: Tomato;
        color: white;
        width: 25%;
        height: 160px;
        float: left;
    }
    
    h1,
    h2 {
        text-align: center;
    }
    
    a {
        color: white;
    }
    
    .opinion {
        background-color: white;
        color: DimGray;
        width: 60%;
        height: 220px;
        margin: 40px auto;
        box-shadow: 0 0 10px 5px DimGray;
    }
    
    img {
        float: left;
        margin: 10px;
        border-radius: 10px;
    }
    
    blockquote {
        padding: 10px;
        font-size: 150%;
        font-style: italic;
    }
    Podgląd strony po tym kroku (opinie.php)
  41. Krok 41: Nagłówek h4: do prawej, margines, rozmiar i pochylenie

    Kryteria: , ,

    R.4.5 Ustawiono cechy czcionki dla selektora cytatu i h4: pochylenie i rozmiar czcionki 150%

    R.4.6 Ustawiono wyrównanie tekstu do środka dla selektora h1 i h2 oraz do prawej strony dla nagłówka h4

    R.4.7 Ustawiono marginesy zewnętrzne (margin) 10px dla selektora img oraz prawy (margin- right) 40px dla nagłówka h4 oraz marginesy wewnętrzne (padding) 10px dla banera i cytatu

    ok. 2 min
    Polecenie z arkusza

    Dla selektora nagłówka czwartego stopnia: wyrównanie tekstu do prawej strony, margines zewnętrzny prawy 40 px, czcionka o rozmiarze 150%, pochylona

    Selektor znacznika h4. Imię klienta stoi przy prawej krawędzi, odsunięte od niej o 40 px - jak podpis pod opinią.

    Gotowe są cechy czcionki cytatu i h4 (R.4.5), wyrównanie h1, h2 i h4 (R.4.6) oraz marginesy obrazu, h4, banera i cytatu (R.4.7). Podgląd odpowiada obrazowi 2: białe bloki z cieniem, zdjęcie z zaokrąglonymi rogami, kursywą opinia i imię, a blok główny przewija się w pionie.

    Typowe błędy

    Arkusz zastrzega selektory znaczników: .opinion h4 albo klasa przy nagłówku nie dają punktów R.4.5 - R.4.7 (uwaga w kluczu przy R.4).

    styl3.css CSS zmienione: 7 linii
    * {
        font-family: Tahoma, sans-serif;
    }
    
    header {
        background-color: Tomato;
        color: white;
        padding: 10px;
    }
    
    main {
        background-color: Seashell;
        height: 500px;
        overflow: auto;
    }
    
    footer {
        background-color: Tomato;
        color: white;
        width: 25%;
        height: 160px;
        float: left;
    }
    
    h1,
    h2 {
        text-align: center;
    }
    
    a {
        color: white;
    }
    
    .opinion {
        background-color: white;
        color: DimGray;
        width: 60%;
        height: 220px;
        margin: 40px auto;
        box-shadow: 0 0 10px 5px DimGray;
    }
    
    img {
        float: left;
        margin: 10px;
        border-radius: 10px;
    }
    
    blockquote {
        padding: 10px;
        font-size: 150%;
        font-style: italic;
    }
    
    h4 {
        text-align: right;
        margin-right: 40px;
        font-size: 150%;
        font-style: italic;
    }
    Podgląd strony po tym kroku (opinie.php)
  42. Etap 6: Dobre praktyki (poza kluczem)

    Kroki 42-43 · po tym etapie masz 36 z 36 kryteriów (pkt)

    Krok 42: Zabezpieczenie: htmlspecialchars przy wypisywaniu danych z bazy

    ok. 4 min
    Polecenie z arkusza

    Każdy zwrócony zapytaniem wiersz wypisuje w osobnym bloku opinii

    W prawdziwej hurtowni opinie piszą klienci - przez formularz, który arkusz zapowiada jako „późniejszą rozbudowę witryny”. Skrypt 1 wypisuje je każdemu odwiedzającemu tak, jak są w bazie. Sprawdzone na wersji z poprzednich kroków, po wpisaniu do bazy dwóch wartości:

    • opinia <script>document.title = "Strona przejęta";</script>Świetna obsługa. - przeglądarka wykonała skrypt i zmieniła tytuł karty na „Strona przejęta”,
    • nazwa zdjęcia x' onerror='z=7 - mieści się w polu zdjecie (20 znaków), a po wklejeniu w src='$row[0]' apostrof zamyka atrybut i dokłada nowy, onerror. Obrazu x nie ma, więc przeglądarka wykonała kod z onerror (ustawił zmienną z).

    Tu kod tylko zmieniał tytuł i zmienną - równie dobrze mógłby podmienić stronę albo wysłać komuś ciasteczka zalogowanego użytkownika. To atak XSS (cross-site scripting), zapisany w bazie, więc trafia w każdego odwiedzającego.

    Lekarstwo: htmlspecialchars() na każdej wartości z bazy w chwili wypisywania. Funkcja zamienia <, >, &, cudzysłów i - od PHP 8.1 - także apostrof na encje (&lt;, &#039; …), więc znaczniki stają się zwykłym tekstem, a apostrof nie zamknie atrybutu. Napisy łączy operator .. Zabezpieczone są oba skrypty, a liczba punktów przechodzi przez (int) - liczba całkowita nie zawiera znaczników. Stare linie zostają w komentarzu.

    Podgląd: te same dane po poprawce - skrypt w opinii widać jako tekst, a zamiast pierwszego zdjęcia przeglądarka pokazuje tekst alternatywny „klient”.

    Typowe błędy

    W PHP starszym niż 8.1 htmlspecialchars bez drugiego argumentu nie zamienia apostrofu - wtedy trzeba pisać htmlspecialchars($row[0], ENT_QUOTES). Zamiana przy zapisie do bazy zamiast przy wypisywaniu psuje dane i nie chroni tekstów wpisanych inną drogą, np. importem.

    opinie.php PHP zmienione: 8 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Opinie klientów</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia spożywcza</h1>
        </header>
        <main>
            <h2>Opinie naszych klientów</h2>
            <?php
            $connection = mysqli_connect('localhost', 'root', '', 'hurtownia');
            $result = mysqli_query($connection, 'SELECT zdjecie, imie, opinia FROM klienci JOIN opinie ON klienci.id = opinie.Klienci_id WHERE Typy_id IN (2, 3);');
            while ($row = mysqli_fetch_row($result)) {
                echo "<div class='opinion'>";
                // echo "<img src='$row[0]' alt='klient'>";
                echo "<img src='" . htmlspecialchars($row[0]) . "' alt='klient'>";
                // echo "<blockquote>$row[2]</blockquote>";
                echo '<blockquote>' . htmlspecialchars($row[2]) . '</blockquote>';
                // echo "<h4>$row[1]</h4>";
                echo '<h4>' . htmlspecialchars($row[1]) . '</h4>';
                echo "</div>";
            }
            ?>
        </main>
        <footer>
            <h3>Współpracują z nami</h3>
            <a href="http://sklep.pl/">Sklep 1</a>
        </footer>
        <footer>
            <h3>Nasi top klienci</h3>
            <ol>
                <?php
                $result = mysqli_query($connection, 'SELECT imie, nazwisko, punkty FROM klienci ORDER BY punkty DESC LIMIT 3;');
                while ($row = mysqli_fetch_row($result)) {
                    // echo "<li>$row[0] $row[1], $row[2] pkt.</li>";
                    echo '<li>' . htmlspecialchars($row[0]) . ' ' . htmlspecialchars($row[1]) . ', ' . (int) $row[2] . ' pkt.</li>';
                }
                mysqli_close($connection);
                ?>
            </ol>
        </footer>
        <footer>
            <h3>Skontaktuj się</h3>
            <p>telefon: 111222333</p>
        </footer>
        <footer>
            <h3>Autor: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (opinie.php)
  43. Krok 43: Zabezpieczenie: błąd połączenia bez danych serwera

    ok. 4 min
    Polecenie z arkusza

    Skrypty łączą się z serwerem bazy danych na localhost, użytkownik root bez hasła, baza danych o nazwie hurtownia

    Sprawdzone na wersji z poprzedniego kroku: gdy bazy hurtownia nie ma (usunięta, niezaimportowana, zła nazwa), mysqli_connect w PHP 8.1 i nowszym rzuca wyjątek mysqli_sql_exception. Nikt go nie łapie, więc strona urywa się pod nagłówkiem „Opinie naszych klientów”, a w jej miejscu stoi komunikat „Fatal error: Uncaught mysqli_sql_exception: Unknown database 'hurtownia'” z pełną ścieżką pliku na serwerze, numerem linii i fragmentem kodu. Stopka znika. Takie komunikaty podpowiadają włamywaczowi, jak zbudowany jest serwis.

    Poprawka:

    • try / catch (mysqli_sql_exception $e) wokół połączenia - wyjątek zostaje złapany,
    • error_log($e->getMessage()) zapisuje szczegóły do dziennika błędów serwera (dla administratora), a odwiedzający widzi zwykłe zdanie „Opinie są chwilowo niedostępne.”,
    • $connection = false i warunek if ($connection) w obu skryptach - zapytania i mysqli_close wykonują się tylko przy działającym połączeniu.

    Podgląd: strona przy usuniętej bazie - komunikat w bloku głównym, pusta lista w stopce, a reszta strony na swoim miejscu. Przy działającej bazie strona wygląda jak przed poprawką.

    Typowe błędy

    Wyłączenie wyświetlania błędów (display_errors = Off w php.ini) ukrywa komunikat, ale strona i tak urywa się w połowie - dopiero try / catch pozwala dokończyć stronę.

    opinie.php PHP zmienione: 26 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Opinie klientów</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia spożywcza</h1>
        </header>
        <main>
            <h2>Opinie naszych klientów</h2>
            <?php
            try {
                $connection = mysqli_connect('localhost', 'root', '', 'hurtownia');
            } catch (mysqli_sql_exception $e) {
                $connection = false;
                error_log($e->getMessage());
                echo '<p>Opinie są chwilowo niedostępne.</p>';
            }
            if ($connection) {
                $result = mysqli_query($connection, 'SELECT zdjecie, imie, opinia FROM klienci JOIN opinie ON klienci.id = opinie.Klienci_id WHERE Typy_id IN (2, 3);');
                while ($row = mysqli_fetch_row($result)) {
                    echo "<div class='opinion'>";
                    // echo "<img src='$row[0]' alt='klient'>";
                    echo "<img src='" . htmlspecialchars($row[0]) . "' alt='klient'>";
                    // echo "<blockquote>$row[2]</blockquote>";
                    echo '<blockquote>' . htmlspecialchars($row[2]) . '</blockquote>';
                    // echo "<h4>$row[1]</h4>";
                    echo '<h4>' . htmlspecialchars($row[1]) . '</h4>';
                    echo "</div>";
                }
            }
            ?>
        </main>
        <footer>
            <h3>Współpracują z nami</h3>
            <a href="http://sklep.pl/">Sklep 1</a>
        </footer>
        <footer>
            <h3>Nasi top klienci</h3>
            <ol>
                <?php
                if ($connection) {
                    $result = mysqli_query($connection, 'SELECT imie, nazwisko, punkty FROM klienci ORDER BY punkty DESC LIMIT 3;');
                    while ($row = mysqli_fetch_row($result)) {
                        // echo "<li>$row[0] $row[1], $row[2] pkt.</li>";
                        echo '<li>' . htmlspecialchars($row[0]) . ' ' . htmlspecialchars($row[1]) . ', ' . (int) $row[2] . ' pkt.</li>';
                    }
                    mysqli_close($connection);
                }
                ?>
            </ol>
        </footer>
        <footer>
            <h3>Skontaktuj się</h3>
            <p>telefon: 111222333</p>
        </footer>
        <footer>
            <h3>Autor: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (opinie.php)
  44. Etap 7: Oddanie pracy

    Krok 44 · po tym etapie masz 36 z 36 kryteriów (pkt)

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

    ok. 5 min
    Polecenie z arkusza

    UWAGA: po zakończeniu pracy utwórz w folderze z numerem, którym został podpisany arkusz plik tekstowy o nazwie przeglądarka.txt. Zapisz w nim nazwę przeglądarki internetowej, w której weryfikowałeś poprawność działania witryny. Nagraj płytę z rezultatami pracy.

    W przeglądarka.txt wpisujesz nazwę przeglądarki, w której sprawdzasz stronę przez http://localhost/….

    W folderze z numerem zdającego muszą być: anna.jpg, ewa.jpg (przeskalowany), import.jpg, janek.jpg, john.jpg, judy.jpg, kw1.png - kw4.png, kwerendy.txt, opinie.php, przeglądarka.txt i styl3.css (baza.sql może zostać). Nagraj płytę, sprawdź jej odczyt i opisz ją numerem zdającego. Pusta lub nieczytelna płyta to zero punktów za wszystkie pięć rezultatów.

    przeglądarka.txt Tekst nowy plik
    Google Chrome

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

Podgląd

To jedno z możliwych rozwiązań. Egzaminator ocenia spełnienie kryteriów z klucza odpowiedzi, a nie zgodność z tym kodem - inna, poprawna droga też daje punkty.

Autor rozwiązania: . Licencja CC BY-NC 4.0 - kopiowanie i przeróbki do celów niekomercyjnych, z podaniem autora i źródła (link do tej strony) (zasady wykorzystania). Treść arkusza i klucza oceniania pochodzi z CKE.

Potwierdzenie

Informacja