INF.03-01-25.06-SGCzerwiec 2025 · 58 kroków · ok. 118 min · 41 kryteriów z klucza
Rozwiązanie krok po kroku: Ranking gier komputerowych (Wersja podstawowa)
Rozwiązanie buduje portal z rankingiem gier komputerowych: baza gry z czterema kwerendami (w tym INSERT z danymi z pliku), grafika przeskalowana do 550 px szerokości, strona gry.php w trzech kolumnach ze stylami w styl.css (z zapytaniem @media dla wąskich ekranów) i cztery skrypty PHP: ranking top 5, galeria gier, opis wybranej gry i dodawanie nowej gry z formularza.
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ą. Kod PHP jest najpierw pisany funkcjami z tabeli 1 arkusza (proceduralne mysqli), a na końcu zostaje przerobiony na wersję, jaką pisze się w prawdziwych projektach: zapytania przygotowane z bindowaniem parametrów i escapowanie danych przed wypisaniem. W tym arkuszu nie ma JavaScriptu.
Obok jest druga wersja tego rozwiązania - zakładka Nowoczesny kod (grid, PDO): te same punkty, ale układ na siatce CSS, PHP na PDO z zapytaniami przygotowanymi od pierwszej linijki i logika oddzielona od HTML.
Zdjęcia, baza i plik rekord.txt pochodzą z prawdziwego archiwum pliki1 (załącznik do pobrania na stronie arkusza, hasło w treści zadania). Zrzuty wyników zapytań są odtworzone z tej bazy - na egzaminie robisz je sam, z całego ekranu.
Jak liczą się punkty. Klucz ma 41 kryteriów (R.1.1 - R.5.10), każde za 1 punkt. Próg zdania części praktycznej to 75%, czyli 31 punktów - można stracić 10 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.
Suwak i strzałki obok niego - albo ←→ na klawiaturze - przechodzą krok po kroku; wyjaśnienie, pliki i podgląd zmieniają się razem z krokiem.
Lista „Etap” przenosi od razu do wybranego etapu.
„Rozwiązuję samodzielnie” chowa rozwiązanie kroku - najpierw własna próba, potem odsłonięcie.
„Tryb prezentacji” pokazuje same kroki, bez menu strony - na rzutnik.
Etap 1: Baza danych
Kroki 1-7 · po tym etapie masz 6 z 41 kryteriów (pkt)
Krok 1: Baza danych gry
Kryteria:
R.1.1 Wykonany import tabel do bazy danych gry, czynność udokumentowana w pliku import.png
ok. 2 min
Polecenie z arkusza
Utwórz bazę danych o nazwie gry, z zestawem polskich znaków (np. utf8_unicode_ci).
W przeglądarce otwierasz http://localhost/phpmyadmin, klikasz Nowa w lewym panelu, wpisujesz nazwę gry i z listy metod porównywania wybierasz utf8_unicode_ci (arkusz podaje ją jako przykład; utf8_polish_ci też jest poprawne). Tabela gry z pliku SQL ma jednak zestaw i porównywanie zapisane we własnej definicji (utf8, utf8_unicode_ci), więc opisy gier zachowają ogonki przy każdym wyborze - ustawienie bazy dziedziczą tylko tabele zakładane później bez własnego ustawienia.
Typowe błędy
Nazwa bazy musi być dokładnie gry - skrypty PHP łączą się z bazą po nazwie, więc Gry albo gra odetnie punkty także za skrypty (R.5.1).
Krok 2: Import tabeli z pliku baza.sql
Kryteria:
R.1.1 Wykonany import tabel do bazy danych gry, czynność udokumentowana w pliku import.png
ok. 2 min
Polecenie z arkusza
Do bazy zaimportuj tabele z pliku baza.sql z rozpakowanego archiwum.
Wejdź do bazy gry (musi być zaznaczona w lewym panelu), karta Import, Wybierz plik → baza.sql z rozpakowanego archiwum, Wykonaj.
Po imporcie w bazie jest jedna tabela z ilustracji 1: gry z kolumnami id (klucz główny z autonumeracją), nazwa, opis, punkty, cena i zdjecie - 8 wierszy. Kolumna zdjecie trzyma nazwę pliku graficznego (np. Mag.jpg), a same pliki są w archiwum - dlatego arkusz zaleca trzymać grafiki w tym samym folderze co gry.php.
Typowe błędy
Import bez wcześniejszego wejścia do bazy gry tworzy tabelę w innej bazie (np. test) - skrypty nie znajdą wtedy tabeli.
Krok 3: Zrzut ekranu import.png
Kryteria:
R.1.1 Wykonany import tabel do bazy danych gry, czynność udokumentowana w pliku import.png
ok. 2 min
Polecenie z arkusza
Wykonaj zrzut ekranu po imporcie. Zrzut zapisz w formacie PNG pod nazwą import. Nie kadruj zrzutu. Powinien on obejmować cały ekran monitora, z widocznym paskiem zadań.
Klawisz Print Screen (cały ekran, nie Alt+PrtScr, który łapie tylko aktywne okno), wklejasz do edytora grafiki i zapisujesz jako import.png w folderze z numerem zdającego. Na zrzucie ma być widać komunikat phpMyAdmin o pomyślnym imporcie albo tabelę gry w bazie.
Uwaga na formaty w tym arkuszu: zrzut importu w PNG, zrzuty kwerend w JPEG - odwrotnie niż w wielu innych arkuszach.
Typowe błędy
Wykadrowany zrzut (samo okno przeglądarki, bez paska zadań) nie jest oceniany - klucz mówi to wprost.
Krok 4: Zapytanie 1: id, nazwa i zdjęcie
Kryteria: ,
R.1.2 Zapisany plik kwerendy.txt zawierający co najmniej jedno zapytanie SQL, wynikające z treści zadania
R.1.3 Wykonane zapytanie 1 wybierające jedynie id, nazwę i zdjęcie z tabeli gry. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT id, nazwa, zdjecie FROM gry; oraz wynik działania udokumentowany zrzutem, na którym jest widocznych minimum 8 rekordów, jedynie Pola id, nazwa, zdjecie
ok. 3 min
Polecenie z arkusza
Zapytanie 1: wybierające jedynie id, nazwę i zdjęcie z tabeli gry.
Zakładka SQL w phpMyAdmin (przy zaznaczonej bazie gry). Klauzula po klauzuli:
SELECT id, nazwa, zdjecie - które kolumny ma zwrócić wynik. „Jedynie” oznacza dokładnie te trzy; SELECT * zwróciłby też opis, punkty i cenę.
FROM gry - z której tabeli.
Bez WHERE, bo zapytanie dotyczy wszystkich gier. Wynik: 8 wierszy. Zapytanie kopiujesz do pliku kwerendy.txt (zwykły edytor tekstu) i robisz zrzut całego ekranu: kw1.jpg. To samo zapytanie wyśle później skrypt 2, żeby zbudować galerię.
kwerendy.txtTekstnowy plik
-- Zapytanie 1SELECT id, nazwa, zdjecie FROM gry;
Podgląd strony po tym kroku
Krok 5: Zapytanie 2: gra o id 1 ze skróconym opisem
Kryteria:
R.1.4 Wykonane zapytanie 2 wybierające jedynie nazwę, pierwsze 100 znaków opisu, punkty oraz cenę z tabeli gry dla wiersza o id = 1. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa, LEFT(opis, 100), punkty, cena FROM gry WHERE id=1; oraz wynik działania udokumentowany zrzutem, na którym jest widoczny dokładnie jeden rekord, jedynie pola nazwa, opis, punkty, cena. Zastosowana jest funkcja obcinająca opis do pierwszych 100 znaków
ok. 3 min
Polecenie z arkusza
Zapytanie 2: wybierające jedynie nazwę, pierwsze 100 znaków opisu, punkty oraz cenę z tabeli gry dla wiersza o id równym 1.
SELECT nazwa, LEFT(opis, 100), punkty, cena - cztery wartości. „Pierwsze 100 znaków opisu” to funkcja LEFT(opis, 100) z tabeli 2 arkusza: zwraca 100 znaków od lewej strony napisu.
FROM gry - tabela.
WHERE id = 1 - tylko wiersz o kluczu 1. Liczbę porównujesz bez cudzysłowu.
Wynik: dokładnie jeden wiersz (Czarodziejka, 45 punktów, cena 100). Zrzut: kw2.jpg. Zmodyfikowaną wersję tego zapytania (z id z formularza) wyśle skrypt 3.
Typowe błędy
SUBSTRING(opis, 1, 100) daje ten sam wynik i klucz go przyjmie, ale arkusz podpowiada LEFT w tabeli 2 - nie ma powodu szukać dalej.
kwerendy.txtTekstzmienione: 3 linie
-- Zapytanie 1SELECT id, nazwa, zdjecie FROM gry;-- Zapytanie 2SELECT nazwa, LEFT(opis, 100), punkty, cena FROM gry WHERE id = 1;
Podgląd strony po tym kroku
Krok 6: Zapytanie 3: pięć gier z najwyższą punktacją
Kryteria:
R.1.5 Wykonane zapytanie 3 wybierające jedynie pierwsze pięć wierszy o najwyższej punktacji zawierające jedynie nazwę i punkty z tabeli gry. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa, punkty FROM gry ORDER BY punkty DESC LIMIT 5; oraz wynik działania udokumentowany zrzutem, na którym widocznych jest dokładnie 5 rekordów, jedynie pola nazwa i punkty, punkty posortowane malejąco
ok. 3 min
Polecenie z arkusza
Zapytanie 3: wybierające jedynie pola nazwa i punkty z pięciu pierwszych wierszy o najwyższej punktacji z tabeli gry.
SELECT nazwa, punkty FROM gry - dwie kolumny z tabeli gier.
ORDER BY punkty DESC - sortowanie malejąco po punktach, żeby najwyższa punktacja była na górze. Domyślne ASC dałoby najsłabsze gry.
LIMIT 5 - tylko pięć pierwszych wierszy po posortowaniu.
Wynik: Mag 357, Księżniczka Persji 293, Kalipso 125, Wyprawa Na Księżyc 103, Nieznany Ląd 96. Zrzut: kw3.jpg. To zapytanie wyśle skrypt 1 do listy „Top 5”.
Typowe błędy
LIMIT 5 bez ORDER BY zwraca pięć dowolnych wierszy (w praktyce pierwsze wstawione) - to nie jest „pięć o najwyższej punktacji”.
kwerendy.txtTekstzmienione: 3 linie
-- Zapytanie 1SELECT id, nazwa, zdjecie FROM gry;-- Zapytanie 2SELECT nazwa, LEFT(opis, 100), punkty, cena FROM gry WHERE id = 1;-- Zapytanie 3SELECT nazwa, punkty FROM gry ORDER BY punkty DESC LIMIT 5;
Podgląd strony po tym kroku
Krok 7: Zapytanie 4: nowa gra z pliku rekord.txt
Kryteria:
R.1.6 Wykonane zapytanie 4 wstawiające wiersz do tabeli gry o danych zawartych w pliku rekord.txt. Sprawdzając można pominąć zgodność pola opis. Klucz główny nadawany automatycznie. W pliku z kwerendami lub na zrzucie istnieje zapis np.: INSERT INTO `gry`(`nazwa`, `opis`, `punkty`, `cena`, `zdjecie`) VALUES ('Zamczysko','...','200','50','zamczysko.jpg'); (możliwe różne warianty zapytania) oraz wynik działania udokumentowany zrzutem, na którym jest widoczny komunikat o poprawnym wykonaniu zapytania lub tabela z wstawionym wierszem
ok. 4 min
Polecenie z arkusza
Zapytanie 4: wstawiające do tabeli gry wiersz o danych zawartych w pliku rekord.txt (dane należy skopiować z pliku do zapytania). Klucz główny nadawany automatycznie.
Wstawianie wiersza to INSERT INTO gry (...) VALUES (...). W nawiasie po nazwie tabeli wymieniasz kolumny bez id - klucz główny ma autonumerację, więc baza sama nada mu wartość 9. Wartości w VALUES w tej samej kolejności: nazwa i opis w apostrofach (napisy), punkty i cena jako liczby, nazwa pliku zdjęcia w apostrofach.
Dane kopiujesz z rekord.txt: nazwa „Zamczysko”, opis, „Punkty: 200”, „Cena: 50 zł” (do bazy trafia sama liczba 50) i zamczysko.jpg. Zrzut kw4.jpg ma pokazać komunikat o wstawieniu wiersza albo tabelę z nowym rekordem.
Typowe błędy
Plik rekord.txt w archiwum jest zapisany w kodowaniu Windows-1250 - otwarty w edytorze ustawionym na UTF-8 pokaże krzaki zamiast „ó”, „ę”, „ż”. Otwórz go w Notatniku (rozpozna kodowanie) albo przełącz kodowanie w edytorze, zanim skopiujesz opis. Klucz pozwala pominąć zgodność opisu, ale nazwa i liczby muszą się zgadzać.
kwerendy.txtTekstzmienione: 4 linie
-- Zapytanie 1SELECT id, nazwa, zdjecie FROM gry;-- Zapytanie 2SELECT nazwa, LEFT(opis, 100), punkty, cena FROM gry WHERE id = 1;-- Zapytanie 3SELECT nazwa, punkty FROM gry ORDER BY punkty DESC LIMIT 5;-- Zapytanie 4INSERT INTO gry (nazwa, opis, punkty, cena, zdjecie)VALUES ('Zamczysko', 'Na odludziu stoi opuszczone zamczysko, w którym znajduje się skarb strzeżony przez złowrogie demony i duchy', 200, 50, 'zamczysko.jpg');
Podgląd strony po tym kroku
Etap 2: Grafika i strona HTML
Kroki 8-28 · po tym etapie masz 18 z 41 kryteriów (pkt)
Krok 8: Przeskalowanie grafiki zamczysko.jpg
Kryteria:
R.2.1 Plik zamczysko.jpg przeskalowany zachowując proporcje do szerokości 550 px odpowiadająca temu wysokość wynosi 309 px (± 5 px)
ok. 3 min
Polecenie z arkusza
Plik zamczysko.jpg, wypakowany z archiwum, należy przeskalować zachowując proporcje do szerokości 550 px.
Plik z archiwum ma 640 × 360 px. W edytorze grafiki rastrowej (np. GIMP: Obraz → Skaluj obraz) wpisujesz szerokość 550 px przy zamkniętym łańcuchu proporcji. Wysokość przeliczy się sama na 309 px (360 · 550 / 640 = 309,4) - i dokładnie to sprawdza egzaminator (309 ±5 px). Zapisz jako zamczysko.jpg w folderze zdającego, obok pozostałych zdjęć gier.
Typowe błędy
Rozłączony łańcuch proporcji = obraz ściśnięty do 550 px szerokości przy starej wysokości 360 px. Szerokość się zgadza, punktu nie ma.
Podgląd strony po tym kroku
Krok 9: Plik gry.php w HTML5
Kryteria:
R.2.2 W pliku gry.php zapisana deklaracja HTML5 <!DOCTYPE HTML>, deklaracja języka zawartości: polski np. <html lang="pl">, w sekcji head znajduje się jawnie podany standard kodowania polskich znaków
ok. 2 min
Polecenie z arkusza
Składa się ze strony o nazwie gry.php. Zapisana w języku HTML5.
W edytorze zaznaczającym składnię tworzysz plik gry.php w folderze zdającego, obok zdjęć. Rozszerzenie .php, bo dojdą skrypty PHP - na razie to czysty HTML.
Pierwsza linia <!DOCTYPE html> mówi przeglądarce, że dokument jest w HTML5. Dalej szkielet każdej strony: <html>, a w nim <head> (informacje o stronie) i <body> (to, co widać w oknie).
R.2.2 W pliku gry.php zapisana deklaracja HTML5 <!DOCTYPE HTML>, deklaracja języka zawartości: polski np. <html lang="pl">, w sekcji head znajduje się jawnie podany standard kodowania polskich znaków
ok. 1 min
Polecenie z arkusza
Zadeklarowany polski język zawartości witryny.
Atrybut lang="pl" w znaczniku <html>. Czytnik ekranu czyta wtedy tekst polską wymową, a przeglądarka dobiera słownik. Na stronie nic się nie zmienia - to informacja dla programów.
R.2.2 W pliku gry.php zapisana deklaracja HTML5 <!DOCTYPE HTML>, deklaracja języka zawartości: polski np. <html lang="pl">, w sekcji head znajduje się jawnie podany standard kodowania polskich znaków
ok. 1 min
Polecenie z arkusza
Jawnie zastosowany właściwy standard kodowania polskich znaków.
<meta charset="UTF-8"> jako pierwsza rzecz w <head>. „Jawnie” znaczy: wpisane w kodzie, nie zdane na domysł przeglądarki. Bez tej linii „Księżniczka Persji” z bazy mogłaby wyświetlić się jako krzaki. Plik trzeba też zapisać w UTF-8.
Tytuł strony widoczny na karcie przeglądarki: „Gry komputerowe”.
<title> w <head> - arkusz sam podpowiada, gdzie go szukać: na karcie przeglądarki. To nie jest nagłówek na stronie (ten dojdzie w <header>). Klucz sprawdza dokładnie tekst „Gry komputerowe”.
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"> w <head>. Plik styl.css napiszesz po HTML - połączenie może już być, zadziała, gdy plik pojawi się w tym samym folderze.
Typowe błędy
<link> to znacznik pusty - w HTML5 bez </link> i bez ukośnika />. Bez rel="stylesheet" przeglądarka nie potraktuje pliku jako stylów.
R.2.4 W sekcji body zdefiniowany jest podział na bloki za pomocą semantycznych znaczników HTML 5: header, section + section + section, footer
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.
Z ilustracji 3: na górze blok nagłówkowy, pod nim trzy sekcje obok siebie (lewa, środkowa, prawa), na dole stopka. Klucz R.2.4 wymienia znaczniki wprost: <header>, trzy razy <section>, <footer> - z tabeli 3 arkusza.
Sekcje dostają identyfikatory left, middle, right, bo każda będzie stylowana inaczej. Nazwy identyfikatorów, klas i zmiennych piszesz w tym rozwiązaniu po angielsku (arkusz dopuszcza polski lub angielski, wymaga konsekwencji); wyjątkiem są nazwy narzucone przez arkusz (gry.php, styl.css, kolumny bazy). To, że sekcje staną obok siebie, zrobi CSS.
Typowe błędy
Pięć zwykłych <div> wygląda na stronie identycznie, ale kryterium R.2.4 wymaga znaczników semantycznych - sprawdzane w kodzie źródłowym.
R.2.5 W bloku nagłówkowym zapisany nagłówek <h1>: "Ranking gier komputerowych", w sekcji lewej przynajmniej jeden <h3> z: "Top 5 gier w tym miesiącu" lub "Nasz sklep", lub "Stronę wykonał", w sekcji prawej <h3>: "Dodaj nową grę" oraz zapisany na stronie przynajmniej jeden paragraf
ok. 1 min
Polecenie z arkusza
Zawartość bloku nagłówkowego: nagłówek pierwszego stopnia o treści „Ranking gier komputerowych”.
Pierwszego stopnia to <h1>. Egzaminator sprawdza dokładnie ten znacznik, który wskazał arkusz - w innych sekcjach będą <h3>, więc nie mieszaj stopni.
Krok 16: Sekcja lewa: nagłówek „Top 5 gier w tym miesiącu”
Kryteria:
R.2.5 W bloku nagłówkowym zapisany nagłówek <h1>: "Ranking gier komputerowych", w sekcji lewej przynajmniej jeden <h3> z: "Top 5 gier w tym miesiącu" lub "Nasz sklep", lub "Stronę wykonał", w sekcji prawej <h3>: "Dodaj nową grę" oraz zapisany na stronie przynajmniej jeden paragraf
ok. 1 min
Polecenie z arkusza
Zawartość sekcji lewej: nagłówek trzeciego stopnia o treści: „Top 5 gier w tym miesiącu”.
Trzeciego stopnia to <h3>, w sekcji left. Klucz R.2.5 wymaga w sekcji lewej przynajmniej jednego <h3> z podanych treści - dajesz wszystkie trzy, zgodnie z arkuszem.
gry.phpPHPzmienione: 1 linia
<!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Gry komputerowe</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Ranking gier komputerowych</h1> </header> <sectionid="left"> <h3>Top 5 gier w tym miesiącu</h3> </section> <sectionid="middle"> </section> <sectionid="right"> </section> <footer> </footer></body></html>
Podgląd strony po tym kroku (gry.php)
Krok 17: Sekcja lewa: lista punktowana
Kryteria:
R.2.6 W sekcji lewej umieszczona jedna lista punktowana, zastosowane znaczniki <ul>, <li> (wygenerowana skryptem lub w HTML)
ok. 1 min
Polecenie z arkusza
Lista punktowana (nieuporządkowana) wypełniona przez skrypt 1.
Lista nieuporządkowana to <ul>; jej elementy <li> dopisze skrypt 1 na podstawie zapytania 3, więc na razie jest pusta. Klucz R.2.6 przyjmuje listę wygenerowaną skryptem - ważne, żeby w wynikowym HTML były <ul> i <li>.
Typowe błędy
<ol> to lista uporządkowana (numerowana). Arkusz mówi „punktowana (nieuporządkowana)”, czyli <ul> z kropkami - tak jak na ilustracji 2.
gry.phpPHPzmienione: 2 linie
<!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Gry komputerowe</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Ranking gier komputerowych</h1> </header> <sectionid="left"> <h3>Top 5 gier w tym miesiącu</h3> <ul> </ul> </section> <sectionid="middle"> </section> <sectionid="right"> </section> <footer> </footer></body></html>
Podgląd strony po tym kroku (gry.php)
Krok 18: Sekcja lewa: nagłówek „Nasz sklep”
Kryteria:
R.2.5 W bloku nagłówkowym zapisany nagłówek <h1>: "Ranking gier komputerowych", w sekcji lewej przynajmniej jeden <h3> z: "Top 5 gier w tym miesiącu" lub "Nasz sklep", lub "Stronę wykonał", w sekcji prawej <h3>: "Dodaj nową grę" oraz zapisany na stronie przynajmniej jeden paragraf
ok. 1 min
Polecenie z arkusza
Nagłówek trzeciego stopnia o treści: „Nasz sklep”.
Drugi <h3> w sekcji lewej, pod listą.
gry.phpPHPzmienione: 1 linia
<!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Gry komputerowe</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Ranking gier komputerowych</h1> </header> <sectionid="left"> <h3>Top 5 gier w tym miesiącu</h3> <ul> </ul> <h3>Nasz sklep</h3> </section> <sectionid="middle"> </section> <sectionid="right"> </section> <footer> </footer></body></html>
Podgląd strony po tym kroku (gry.php)
Krok 19: Sekcja lewa: odnośnik do sklepu
Kryteria:
R.3.3 Odnośnik o treści „Tu kupisz gry” inicjuje otwarcie strony http://sklep.gry.pl ( lub https://)
ok. 1 min
Polecenie z arkusza
Odnośnik o treści „Tu kupisz gry” prowadzący do adresu http://sklep.gry.pl.
Odnośnik to <a>: adres w atrybucie href, tekst między znacznikami. Adres przepisujesz z arkusza w całości, z http:// - bez protokołu przeglądarka potraktowałaby sklep.gry.pl jako plik w folderze strony. W podglądzie kliknięcie jest zablokowane (podgląd pokazuje jedną stronę), na egzaminie link otwiera sklep.
Typowe błędy
href="sklep.gry.pl" (bez http://) to ścieżka względna - przeglądarka szuka pliku sklep.gry.pl obok gry.php i dostaje 404. R.3.3 przepada.
gry.phpPHPzmienione: 1 linia
<!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Gry komputerowe</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Ranking gier komputerowych</h1> </header> <sectionid="left"> <h3>Top 5 gier w tym miesiącu</h3> <ul> </ul> <h3>Nasz sklep</h3> <ahref="http://sklep.gry.pl">Tu kupisz gry</a> </section> <sectionid="middle"> </section> <sectionid="right"> </section> <footer> </footer></body></html>
Podgląd strony po tym kroku (gry.php)
Krok 20: Sekcja lewa: nagłówek „Stronę wykonał”
Kryteria:
R.2.5 W bloku nagłówkowym zapisany nagłówek <h1>: "Ranking gier komputerowych", w sekcji lewej przynajmniej jeden <h3> z: "Top 5 gier w tym miesiącu" lub "Nasz sklep", lub "Stronę wykonał", w sekcji prawej <h3>: "Dodaj nową grę" oraz zapisany na stronie przynajmniej jeden paragraf
ok. 1 min
Polecenie z arkusza
Nagłówek trzeciego stopnia o treści: „Stronę wykonał”.
Trzeci <h3> w sekcji lewej.
gry.phpPHPzmienione: 1 linia
<!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Gry komputerowe</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Ranking gier komputerowych</h1> </header> <sectionid="left"> <h3>Top 5 gier w tym miesiącu</h3> <ul> </ul> <h3>Nasz sklep</h3> <ahref="http://sklep.gry.pl">Tu kupisz gry</a> <h3>Stronę wykonał</h3> </section> <sectionid="middle"> </section> <sectionid="right"> </section> <footer> </footer></body></html>
Podgląd strony po tym kroku (gry.php)
Krok 21: Sekcja lewa: paragraf z numerem zdającego
Kryteria:
R.2.5 W bloku nagłówkowym zapisany nagłówek <h1>: "Ranking gier komputerowych", w sekcji lewej przynajmniej jeden <h3> z: "Top 5 gier w tym miesiącu" lub "Nasz sklep", lub "Stronę wykonał", w sekcji prawej <h3>: "Dodaj nową grę" oraz zapisany na stronie przynajmniej jeden paragraf
ok. 1 min
Polecenie z arkusza
Paragraf z numerem zdającego.
Paragraf <p> pod nagłówkiem. W tym rozwiązaniu stoi tu nazwisko autora - na egzaminie wpisujesz w to miejsce swój numer zdającego (PESEL), bo klucz R.2.5 sprawdza paragraf na stronie. Sekcja lewa jest kompletna.
gry.phpPHPzmienione: 1 linia
<!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Gry komputerowe</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Ranking gier komputerowych</h1> </header> <sectionid="left"> <h3>Top 5 gier w tym miesiącu</h3> <ul> </ul> <h3>Nasz sklep</h3> <ahref="http://sklep.gry.pl">Tu kupisz gry</a> <h3>Stronę wykonał</h3> <p>Bartosz Bryniarski</p> </section> <sectionid="middle"> </section> <sectionid="right"> </section> <footer> </footer></body></html>
Podgląd strony po tym kroku (gry.php)
Krok 22: Sekcja prawa: nagłówek „Dodaj nową grę”
Kryteria:
R.2.5 W bloku nagłówkowym zapisany nagłówek <h1>: "Ranking gier komputerowych", w sekcji lewej przynajmniej jeden <h3> z: "Top 5 gier w tym miesiącu" lub "Nasz sklep", lub "Stronę wykonał", w sekcji prawej <h3>: "Dodaj nową grę" oraz zapisany na stronie przynajmniej jeden paragraf
ok. 1 min
Polecenie z arkusza
Zawartość sekcji środkowej: efekt działania skryptu 2. Zawartość sekcji prawej: nagłówek trzeciego stopnia o treści: „Dodaj nową grę”.
Sekcja środkowa zostaje w HTML pusta - wypełni ją w całości skrypt 2 (galeria gier z bazy). Przejdź do sekcji prawej: <h3> z treścią z arkusza.
gry.phpPHPzmienione: 1 linia
<!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Gry komputerowe</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Ranking gier komputerowych</h1> </header> <sectionid="left"> <h3>Top 5 gier w tym miesiącu</h3> <ul> </ul> <h3>Nasz sklep</h3> <ahref="http://sklep.gry.pl">Tu kupisz gry</a> <h3>Stronę wykonał</h3> <p>Bartosz Bryniarski</p> </section> <sectionid="middle"> </section> <sectionid="right"> <h3>Dodaj nową grę</h3> </section> <footer> </footer></body></html>
Podgląd strony po tym kroku (gry.php)
Krok 23: Sekcja prawa: formularz
Kryteria:
R.3.4 Przynajmniej jeden formularz wysyła dane do tego samego pliku metodą POST (poprawne również bez atrybutu action)
ok. 1 min
Polecenie z arkusza
Formularz wysyłający dane do tego samego pliku metodą bezpieczną.
„Metoda bezpieczna” w rozumieniu arkusza to POST - dane nie pojawiają się w adresie strony, jak przy GET. W specyfikacji HTTP „bezpieczne” są co prawda metody, które niczego nie zmieniają na serwerze (GET i HEAD), ale arkusz używa tego słowa w innym sensie, a klucz wymaga POST. „Do tego samego pliku” to brak atrybutu action (przeglądarka wysyła formularz na adres bieżącej strony) albo action="gry.php" - klucz przyjmuje oba warianty.
Typowe błędy
method="get" (albo brak method, bo GET jest domyślny) wyśle dane w adresie - arkusz i klucz wymagają tu POST, a skrypt czytający $_POST nic nie dostanie.
gry.phpPHPzmienione: 2 linie
<!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Gry komputerowe</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Ranking gier komputerowych</h1> </header> <sectionid="left"> <h3>Top 5 gier w tym miesiącu</h3> <ul> </ul> <h3>Nasz sklep</h3> <ahref="http://sklep.gry.pl">Tu kupisz gry</a> <h3>Stronę wykonał</h3> <p>Bartosz Bryniarski</p> </section> <sectionid="middle"> </section> <sectionid="right"> <h3>Dodaj nową grę</h3> <formmethod="post"> </form> </section> <footer> </footer></body></html>
Podgląd strony po tym kroku (gry.php)
Krok 24: Formularz: pierwsze pole z etykietą
Kryteria:
R.2.7 W sekcji prawej umieszczony formularz z czterema polami edycyjnymi i przyciskiem "DODAJ". Zastosowany znacznik <form> oraz przynajmniej jedna etykieta <label>
ok. 2 min
Polecenie z arkusza
Formularz zawiera cztery pola edycyjne podpisane etykietami: nazwa, opis, cena, zdjęcie oraz przycisk DODAJ, rozmieszczone zgodnie z ilustracją 2.
Zacznij od pola „nazwa”. Etykieta to <label>; atrybut for wskazuje id pola, dzięki czemu kliknięcie w napis ustawia kursor w polu (klucz R.2.7 wymaga przynajmniej jednej etykiety <label>). Pole edycyjne to <input type="text"> z atrybutem name - pod tą nazwą wartość trafi do PHP.
Na ilustracji 2 etykieta stoi nad polem, a pola jedno pod drugim - stąd <br> po etykiecie i po polu. Atrybuty id i name po angielsku (name), etykieta po polsku, bo jej treść narzuca arkusz.
Typowe błędy
<label>nazwa</label> bez for jest poprawnym HTML, ale nie łączy się z polem - klucz przyjmie, użytkownik straci klik w etykietę. Zawsze for = id pola.
gry.phpPHPzmienione: 2 linie
<!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Gry komputerowe</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Ranking gier komputerowych</h1> </header> <sectionid="left"> <h3>Top 5 gier w tym miesiącu</h3> <ul> </ul> <h3>Nasz sklep</h3> <ahref="http://sklep.gry.pl">Tu kupisz gry</a> <h3>Stronę wykonał</h3> <p>Bartosz Bryniarski</p> </section> <sectionid="middle"> </section> <sectionid="right"> <h3>Dodaj nową grę</h3> <formmethod="post"> <labelfor="name">nazwa</label><br> <inputtype="text"id="name"name="name"><br> </form> </section> <footer> </footer></body></html>
Podgląd strony po tym kroku (gry.php)
Krok 25: Formularz: pola opis, cena, zdjęcie
Kryteria:
R.2.7 W sekcji prawej umieszczony formularz z czterema polami edycyjnymi i przyciskiem "DODAJ". Zastosowany znacznik <form> oraz przynajmniej jedna etykieta <label>
ok. 2 min
Polecenie z arkusza
Formularz zawiera cztery pola edycyjne podpisane etykietami: nazwa, opis, cena, zdjęcie oraz przycisk DODAJ, rozmieszczone zgodnie z ilustracją 2.
Trzy kolejne pary etykieta + pole według tego samego wzoru: description, price, image. Nazwy pól (name) odpowiadają temu, co skrypt 4 wstawi do kolumn opis, cena, zdjecie w bazie.
gry.phpPHPzmienione: 6 linii
<!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Gry komputerowe</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Ranking gier komputerowych</h1> </header> <sectionid="left"> <h3>Top 5 gier w tym miesiącu</h3> <ul> </ul> <h3>Nasz sklep</h3> <ahref="http://sklep.gry.pl">Tu kupisz gry</a> <h3>Stronę wykonał</h3> <p>Bartosz Bryniarski</p> </section> <sectionid="middle"> </section> <sectionid="right"> <h3>Dodaj nową grę</h3> <formmethod="post"> <labelfor="name">nazwa</label><br> <inputtype="text"id="name"name="name"><br> <labelfor="description">opis</label><br> <inputtype="text"id="description"name="description"><br> <labelfor="price">cena</label><br> <inputtype="text"id="price"name="price"><br> <labelfor="image">zdjęcie</label><br> <inputtype="text"id="image"name="image"><br> </form> </section> <footer> </footer></body></html>
Podgląd strony po tym kroku (gry.php)
Krok 26: Formularz: przycisk DODAJ
Kryteria: ,
R.2.7 W sekcji prawej umieszczony formularz z czterema polami edycyjnymi i przyciskiem "DODAJ". Zastosowany znacznik <form> oraz przynajmniej jedna etykieta <label>
R.3.5 Przycisk z przynajmniej jednego formularza wysyła dane do skryptu (input z typem submit lub button)
ok. 1 min
Polecenie z arkusza
Formularz zawiera cztery pola edycyjne podpisane etykietami: nazwa, opis, cena, zdjęcie oraz przycisk DODAJ, rozmieszczone zgodnie z ilustracją 2.
<button type="submit">DODAJ</button> wewnątrz formularza wysyła go po kliknięciu. Klucz R.3.5 przyjmuje też <input type="submit" value="DODAJ">. Sekcja prawa jest kompletna.
gry.phpPHPzmienione: 1 linia
<!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Gry komputerowe</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Ranking gier komputerowych</h1> </header> <sectionid="left"> <h3>Top 5 gier w tym miesiącu</h3> <ul> </ul> <h3>Nasz sklep</h3> <ahref="http://sklep.gry.pl">Tu kupisz gry</a> <h3>Stronę wykonał</h3> <p>Bartosz Bryniarski</p> </section> <sectionid="middle"> </section> <sectionid="right"> <h3>Dodaj nową grę</h3> <formmethod="post"> <labelfor="name">nazwa</label><br> <inputtype="text"id="name"name="name"><br> <labelfor="description">opis</label><br> <inputtype="text"id="description"name="description"><br> <labelfor="price">cena</label><br> <inputtype="text"id="price"name="price"><br> <labelfor="image">zdjęcie</label><br> <inputtype="text"id="image"name="image"><br> <buttontype="submit">DODAJ</button> </form> </section> <footer> </footer></body></html>
Podgląd strony po tym kroku (gry.php)
Krok 27: Stopka: formularz
Kryteria:
R.3.4 Przynajmniej jeden formularz wysyła dane do tego samego pliku metodą POST (poprawne również bez atrybutu action)
ok. 1 min
Polecenie z arkusza
Zawartość stopki: formularz wysyłający dane do tego samego pliku metodą bezpieczną.
Drugi formularz na stronie, tak samo method="post" bez action. Dwa formularze w jednym pliku to nic złego - PHP rozpozna po nazwach pól, który został wysłany.
R.2.8 W stopce umieszczony formularz z jednym polem edycyjnym i przyciskiem "Pokaż opis". Zastosowany znacznik <form>
R.3.5 Przycisk z przynajmniej jednego formularza wysyła dane do skryptu (input z typem submit lub button)
ok. 1 min
Polecenie z arkusza
Formularz zawiera pole edycyjne i przycisk o treści „Pokaż opis”. Efekt działania skryptu 3.
Jedno pole tekstowe name="game_id" (tu użytkownik wpisze id gry) i przycisk <button type="submit"> z treścią z arkusza. Efekt działania skryptu 3 (nagłówek i opis gry) pojawi się pod formularzem - dopisze go PHP. Struktura HTML strony jest kompletna - dalej style.
Domyślne formatowanie wszystkich selektorów: krój czcionki Garamond, biały kolor czcionki.
Utwórz plik styl.css obok gry.php. „Wszystkie selektory” to selektor uniwersalny *. font-family: Garamond ustawia krój, color: white kolor tekstu (czcionki), nie tła. Biały tekst na razie znika na białym tle - to normalne, tła dojdą w kolejnych krokach.
Uwaga z arkusza: przycisk może nie odziedziczyć białego koloru i zostać czarny - to nie jest błąd.
R.4.2 Ustawiony kolor tła Indigo dla bloków: bloku nagłówkowego, sekcji lewej, prawej i stopki oraz czarny kolor tła dla sekcji środkowej
R.4.6 Ustawione wyrównanie tekstu do środka dla bloku nagłówkowego
R.4.7 Ustawione marginesy wewnętrzne (padding) dla bloku nagłówkowego 2px, dla elementu listy 5px oraz marginesy zewnętrzne (margin) dla pola edycyjnego 10px
ok. 2 min
Polecenie z arkusza
Dla bloku nagłówkowego: kolor tła Indigo, wyrównanie tekstu do środka, marginesy wewnętrzne 2 px.
Selektor header. Kolor nazwany Indigo przepisujesz z arkusza. Margines wewnętrzny to padding (zewnętrzny byłby margin). Nagłówek pojawia się w podglądzie jako fioletowy pas z białym tytułem.
Typowe błędy
margin: 2px zamiast padding: 2px - odstęp będzie na zewnątrz fioletowego pola. Kryterium R.4.7 mówi o marginesach wewnętrznych.
R.4.2 Ustawiony kolor tła Indigo dla bloków: bloku nagłówkowego, sekcji lewej, prawej i stopki oraz czarny kolor tła dla sekcji środkowej
R.4.4 Ustawiona wysokość sekcji lewej, środkowej i prawej 600px oraz stopki 150px
ok. 1 min
Polecenie z arkusza
Dla sekcji lewej i prawej: kolor tła Indigo, wysokość 600 px.
Wspólne cechy dwóch sekcji piszesz raz - selektory identyfikatorów rozdzielone przecinkiem #left, #right. Krzyżyk = id z HTML. Sekcje są jeszcze jedna pod drugą - obok siebie ustawisz je za chwilę.
R.4.2 Ustawiony kolor tła Indigo dla bloków: bloku nagłówkowego, sekcji lewej, prawej i stopki oraz czarny kolor tła dla sekcji środkowej
R.4.4 Ustawiona wysokość sekcji lewej, środkowej i prawej 600px oraz stopki 150px
R.4.8 Ustawione dla sekcji środkowej zawsze widoczne paski przewijania (overflow: scroll;)
ok. 2 min
Polecenie z arkusza
Dla sekcji środkowej: czarny kolor tła, wysokość 600 px, zawsze widoczne paski przewijania.
Selektor #middle. „Zawsze widoczne paski przewijania” to overflow: scroll - klucz R.4.8 podaje tę wartość wprost. auto pokazywałby paski dopiero przy przepełnieniu, a tego arkusz tu nie chce (odwrotnie niż w wielu innych arkuszach - czytaj uważnie).
Typowe błędy
overflow: auto przy pustej sekcji nie pokaże pasków - R.4.8 wymaga scroll.
R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z ilustracją 3 w arkuszu dla ekranów o szerokości większej niż 800 px. Blok środkowy może być pusty. (prawidłowo zastosowane właściwości CSS decydujące o układzie bloków na stronie, np. float i clear albo display flex albo grid)
R.4.5 Sekcje lewa, środkowa i prawa są wyświetlane obok siebie jeśli ekran jest szerszy niż 800px - odpowiednio szerokości 20%, 60%, 20%, a w przeciwnym wypadku są wyświetlane jedna pod drugą (zastosowany np. zapis @media all and (max-width: 800px) - sprawdzić w kodzie i poprzez zmianę szerokości okna przeglądarki)
ok. 3 min
Polecenie z arkusza
Dla wszystkich rodzajów ekranu o szerokości większej niż 800 px szerokość sekcji lewej i prawej wynosi 20%, środkowej 60% (są wyświetlane obok siebie).
Dopisz szerokości do istniejących reguł i float: left - sekcje pływają w lewo i ustawiają się obok siebie w kolejności z HTML (20% + 60% + 20% = 100%, więc mieszczą się w jednej linii). To jedno z dopuszczalnych rozwiązań; klucz przyjmuje też display: flex albo grid.
Na razie ten układ działa na każdej szerokości - warunek „większej niż 800 px” dodasz w następnym kroku zapytaniem @media.
R.4.5 Sekcje lewa, środkowa i prawa są wyświetlane obok siebie jeśli ekran jest szerszy niż 800px - odpowiednio szerokości 20%, 60%, 20%, a w przeciwnym wypadku są wyświetlane jedna pod drugą (zastosowany np. zapis @media all and (max-width: 800px) - sprawdzić w kodzie i poprzez zmianę szerokości okna przeglądarki)
ok. 3 min
Polecenie z arkusza
Dla wszystkich rodzajów ekranu o pozostałej szerokości sekcje lewa, środkowa i prawa są wyświetlane jedna pod drugą.
Zapytanie medialne @media all and (max-width: 800px) { ... } - reguły w środku działają tylko wtedy, gdy okno ma 800 px lub mniej („pozostała szerokość” po „większej niż 800”). Klucz R.4.5 podaje dokładnie ten zapis. W środku odwołujesz układ: pełna szerokość i float: none, więc sekcje wracają jedna pod drugą.
Sprawdź to w podglądzie, zwężając okno przeglądarki - egzaminator robi to samo.
Typowe błędy
@media (min-width: 800px) dla układu obok siebie łapie też dokładnie 800 px, a arkusz mówi „większej niż 800”. Z max-width: 800px dla układu pod sobą granica jest tam, gdzie chce klucz.
R.4.2 Ustawiony kolor tła Indigo dla bloków: bloku nagłówkowego, sekcji lewej, prawej i stopki oraz czarny kolor tła dla sekcji środkowej
R.4.4 Ustawiona wysokość sekcji lewej, środkowej i prawej 600px oraz stopki 150px
R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z ilustracją 3 w arkuszu dla ekranów o szerokości większej niż 800 px. Blok środkowy może być pusty. (prawidłowo zastosowane właściwości CSS decydujące o układzie bloków na stronie, np. float i clear albo display flex albo grid)
ok. 1 min
Polecenie z arkusza
Dla stopki: kolor tła Indigo, wysokość 150 px.
Selektor footer z tłem i wysokością z arkusza. Dołóż clear: both - bez tego stopka wcisnęłaby się pod pływające sekcje zamiast stanąć pod nimi (to element układu z ilustracji 3, R.3.1).
Typowe błędy
Bez clear: both stopka może wjechać obok kolumn albo pod nie - układ przestaje zgadzać się z ilustracją 3.
R.4.9 Ustawiony styl jedynie dla oznaczenia liczby punktów zgodnie z ilustracją 4: kolor tła Tomato, zaokrąglenie rogów 50%, marginesy wewnętrzne (padding) 5px (np. klasą, zastosowanie id jest błędem)
ok. 2 min
Polecenie z arkusza
Dla oznaczenia liczby punktów widocznych na ilustracji 4: kolor tła Tomato, zaokrąglenie rogów 50%, marginesy wewnętrzne 5 px.
Liczba punktów będzie w osobnym <span> generowanym przez skrypt 1, więc stylujesz klasę.points (kropka = klasa). Klucz R.4.9 mówi wprost: klasa, nie id - identyfikator musi być unikalny na stronie, a tych elementów będzie pięć. border-radius: 50% zaokrągla rogi w kółko jak na ilustracji 4, padding: 5px daje tłu miejsce wokół liczby. Efekt zobaczysz, gdy skrypt 1 wypełni listę.
Typowe błędy
#points (id) zamiast klasy to błąd wskazany w kluczu - pięć elementów z tym samym id to niepoprawny HTML.
R.4.7 Ustawione marginesy wewnętrzne (padding) dla bloku nagłówkowego 2px, dla elementu listy 5px oraz marginesy zewnętrzne (margin) dla pola edycyjnego 10px
ok. 1 min
Polecenie z arkusza
Dla elementu listy: marginesy wewnętrzne 5 px.
Selektor znacznika li, padding: 5px - odstęp wewnątrz każdego elementu listy, dzięki czemu kółka z punktami nie nachodzą na siebie.
R.4.10 Ustawiony styl dla bloku gry: bloki są umieszczone jeden obok drugiego, wyrównanie tekstu do środka, marginesy wewnętrzne (padding) 3px (np. klasą, selektorem typu, potomka, zastosowanie id jest błędem)
ok. 2 min
Polecenie z arkusza
Dla bloków gier znajdujących się w sekcji środkowej: bloki są umieszczone jeden obok drugiego, wyrównanie tekstu do środka, marginesy wewnętrzne 3 px.
Bloki gier wygeneruje skrypt 2 jako <div class="game">, więc stylujesz klasę .game. „Jeden obok drugiego” to display: inline-block - blok zachowuje wymiary, ale ustawia się w linii jak tekst (i zawija do następnej linii, gdy zabraknie miejsca, jak na ilustracji 2). Klucz przyjmuje też float albo flex; id byłoby błędem.
Krok 39: Pole edycyjne: margines zewnętrzny i kolor czcionki
Kryteria: ,
R.4.3 Ustawiony kolor czcionki Indigo dla pola edycyjnego
R.4.7 Ustawione marginesy wewnętrzne (padding) dla bloku nagłówkowego 2px, dla elementu listy 5px oraz marginesy zewnętrzne (margin) dla pola edycyjnego 10px
ok. 1 min
Polecenie z arkusza
Dla pola edycyjnego: marginesy zewnętrzne 10 px, kolor czcionki Indigo.
Selektor znacznika input. Tu arkusz mówi o marginesach zewnętrznych, więc margin. color: Indigo nadpisuje biały kolor z * - inaczej wpisywany tekst byłby biały na białym polu. Arkusz stylów jest kompletny.
Kroki 40-54 · po tym etapie masz 41 z 41 kryteriów (pkt)
Krok 40: Połączenie z bazą danych
Kryteria: ,
R.5.1 Skrypt zawiera działające połączenie z serwerem bazy danych i wybór bazy gry
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
Skrypty napisane w języku PHP. Skrypty łączą się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie gry.
Kod PHP piszesz między <?php a ?> - serwer wykonuje go, zanim strona trafi do przeglądarki. Połączenie otwierasz raz, na samym początku pliku, i trzymasz w zmiennej $connection (znacząca, angielska nazwa), z której skorzystają wszystkie cztery skrypty. mysqli_connect z tabeli 1 arkusza przyjmuje kolejno: serwer, użytkownika, hasło (puste) i nazwę bazy.
Od tego kroku podgląd to wynik działania strony po stronie serwera (PHP nie wykonuje się w przeglądarce) - na razie wygląda jak wcześniej, bo skrypt niczego nie wypisuje.
Typowe błędy
Kolejność argumentów ma znaczenie: mysqli_connect('localhost', 'root', 'gry') podaje „gry” jako HASŁO. Nazwa bazy jest czwarta. Plik sprawdzaj przez http://localhost/…, nie otwierając go z dysku - inaczej PHP się nie wykona.
R.5.3 Skrypt 1 wysyła do bazy danych zapytanie 3 lub skrypt 2 wysyła zapytanie 1, lub skrypt 3 wysyła zapytanie 2 zmodyfikowane w ten sposób, że wybierany jest wiersz o id podanym w polu edycyjnym
ok. 2 min
Polecenie z arkusza
Skrypt 1: wysyła do bazy danych zapytanie 3.
Skrypt 1 piszesz wewnątrz<ul>, bo tam mają trafić elementy listy. mysqli_query($connection, ...) wysyła zapytanie i zwraca wynik, który zapisujesz w $top_games. Treść to dokładnie zapytanie 3 z kwerendy.txt - pięć gier z najwyższą punktacją.
gry.phpPHPzmienione: 3 linie
<?php$connection = mysqli_connect('localhost', 'root', '', 'gry');?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Gry komputerowe</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Ranking gier komputerowych</h1> </header> <sectionid="left"> <h3>Top 5 gier w tym miesiącu</h3> <ul><?php$top_games = mysqli_query($connection, 'SELECT nazwa, punkty FROM gry ORDER BY punkty DESC LIMIT 5;');?> </ul> <h3>Nasz sklep</h3> <ahref="http://sklep.gry.pl">Tu kupisz gry</a> <h3>Stronę wykonał</h3> <p>Bartosz Bryniarski</p> </section> <sectionid="middle"> </section> <sectionid="right"> <h3>Dodaj nową grę</h3> <formmethod="post"> <labelfor="name">nazwa</label><br> <inputtype="text"id="name"name="name"><br> <labelfor="description">opis</label><br> <inputtype="text"id="description"name="description"><br> <labelfor="price">cena</label><br> <inputtype="text"id="price"name="price"><br> <labelfor="image">zdjęcie</label><br> <inputtype="text"id="image"name="image"><br> <buttontype="submit">DODAJ</button> </form> </section> <footer> <formmethod="post"> <inputtype="text"name="game_id"> <buttontype="submit">Pokaż opis</button> </form> </footer></body></html>
R.5.6 Skrypt 1 wyświetla nazwę i punkty zwrócone zapytaniem jako elementy listy
ok. 2 min
Polecenie z arkusza
Zwrócone wiersze są wyświetlane w elementach listy z sekcji lewej według wzoru: <nazwa> <punkty>, gdzie nawiasy <> oznaczają wartość pobraną z bazy danych.
„Zwrócone wiersze” to pętla. mysqli_fetch_row($top_games) (tabela 1) oddaje kolejny wiersz wyniku jako tablicę numerowaną od zera, a gdy wierszy zabraknie - false. Zapis while ($row = mysqli_fetch_row(...)) przypisuje wiersz do $row i od razu sprawdza, czy coś przyszło - pętla wykona się pięć razy i sama się zakończy. Ciało jest jeszcze puste.
Typowe błędy
Pojedyncze = w warunku while jest tu celowe (przypisanie); == porównałoby zmienną z wynikiem i pętla nie zadziałałaby poprawnie.
gry.phpPHPzmienione: 2 linie
<?php$connection = mysqli_connect('localhost', 'root', '', 'gry');?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Gry komputerowe</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Ranking gier komputerowych</h1> </header> <sectionid="left"> <h3>Top 5 gier w tym miesiącu</h3> <ul><?php$top_games = mysqli_query($connection, 'SELECT nazwa, punkty FROM gry ORDER BY punkty DESC LIMIT 5;');while ($row = mysqli_fetch_row($top_games)) {}?> </ul> <h3>Nasz sklep</h3> <ahref="http://sklep.gry.pl">Tu kupisz gry</a> <h3>Stronę wykonał</h3> <p>Bartosz Bryniarski</p> </section> <sectionid="middle"> </section> <sectionid="right"> <h3>Dodaj nową grę</h3> <formmethod="post"> <labelfor="name">nazwa</label><br> <inputtype="text"id="name"name="name"><br> <labelfor="description">opis</label><br> <inputtype="text"id="description"name="description"><br> <labelfor="price">cena</label><br> <inputtype="text"id="price"name="price"><br> <labelfor="image">zdjęcie</label><br> <inputtype="text"id="image"name="image"><br> <buttontype="submit">DODAJ</button> </form> </section> <footer> <formmethod="post"> <inputtype="text"name="game_id"> <buttontype="submit">Pokaż opis</button> </form> </footer></body></html>
Krok 43: Skrypt 1: element listy z nazwą i punktami
Kryteria: ,
R.5.6 Skrypt 1 wyświetla nazwę i punkty zwrócone zapytaniem jako elementy listy
R.2.9 Liczba punktów z sekcji lewej zapisana w dodatkowym elemencie, np. znacznik <span> (w skrypcie lub HTML)
ok. 3 min
Polecenie z arkusza
Zwrócone wiersze są wyświetlane w elementach listy z sekcji lewej według wzoru: <nazwa> <punkty>, gdzie nawiasy <> oznaczają wartość pobraną z bazy danych. Liczba punktów jest dodatkowo formatowana stylem, którego efekt jest widoczny na ilustracji 4.
echo w pętli wypisuje jeden <li>: nazwa z $row[0], spacja, a liczba punktów z $row[1] owinięta w <span class='points'> - to ten „dodatkowy element” z kryterium R.2.9, na który czeka reguła .points w CSS (pomarańczowe kółko z ilustracji 4). Wewnątrz napisu w cudzysłowie atrybut HTML piszesz w apostrofach, żeby nie kończyć napisu PHP.
W podglądzie lista ma pięć pozycji z kółkami - w tym Zamczysko, bo zapytanie 4 już je wstawiło.
Typowe błędy
echo '<li>$row[0]</li>' w apostrofach wypisze dosłownie „$row[0]” - PHP nie podstawia zmiennych w apostrofach. Zewnętrzny napis w cudzysłowie, atrybuty w apostrofach.
gry.phpPHPzmienione: 1 linia
<?php$connection = mysqli_connect('localhost', 'root', '', 'gry');?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Gry komputerowe</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Ranking gier komputerowych</h1> </header> <sectionid="left"> <h3>Top 5 gier w tym miesiącu</h3> <ul><?php$top_games = mysqli_query($connection, 'SELECT nazwa, punkty FROM gry ORDER BY punkty DESC LIMIT 5;');while ($row = mysqli_fetch_row($top_games)) {echo"<li>$row[0] <span class='points'>$row[1]</span></li>";}?> </ul> <h3>Nasz sklep</h3> <ahref="http://sklep.gry.pl">Tu kupisz gry</a> <h3>Stronę wykonał</h3> <p>Bartosz Bryniarski</p> </section> <sectionid="middle"> </section> <sectionid="right"> <h3>Dodaj nową grę</h3> <formmethod="post"> <labelfor="name">nazwa</label><br> <inputtype="text"id="name"name="name"><br> <labelfor="description">opis</label><br> <inputtype="text"id="description"name="description"><br> <labelfor="price">cena</label><br> <inputtype="text"id="price"name="price"><br> <labelfor="image">zdjęcie</label><br> <inputtype="text"id="image"name="image"><br> <buttontype="submit">DODAJ</button> </form> </section> <footer> <formmethod="post"> <inputtype="text"name="game_id"> <buttontype="submit">Pokaż opis</button> </form> </footer></body></html>
Podgląd strony po tym kroku (gry.php)
Krok 44: Skrypt 2: zapytanie 1 do bazy
Kryteria:
R.5.3 Skrypt 1 wysyła do bazy danych zapytanie 3 lub skrypt 2 wysyła zapytanie 1, lub skrypt 3 wysyła zapytanie 2 zmodyfikowane w ten sposób, że wybierany jest wiersz o id podanym w polu edycyjnym
ok. 2 min
Polecenie z arkusza
Skrypt 2: wysyła do bazy danych zapytanie 1.
Skrypt 2 piszesz w sekcji środkowej - jego wynik jest jej całą zawartością. Zapytanie 1 z kwerendy.txt (id, nazwa, zdjęcie wszystkich gier) trafia do $games. Kolejność kolumn w SELECT zaraz będzie ważna: $row[0] to id, $row[1] nazwa, $row[2] zdjęcie.
gry.phpPHPzmienione: 3 linie
<?php$connection = mysqli_connect('localhost', 'root', '', 'gry');?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Gry komputerowe</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Ranking gier komputerowych</h1> </header> <sectionid="left"> <h3>Top 5 gier w tym miesiącu</h3> <ul><?php$top_games = mysqli_query($connection, 'SELECT nazwa, punkty FROM gry ORDER BY punkty DESC LIMIT 5;');while ($row = mysqli_fetch_row($top_games)) {echo"<li>$row[0] <span class='points'>$row[1]</span></li>";}?> </ul> <h3>Nasz sklep</h3> <ahref="http://sklep.gry.pl">Tu kupisz gry</a> <h3>Stronę wykonał</h3> <p>Bartosz Bryniarski</p> </section> <sectionid="middle"><?php$games = mysqli_query($connection, 'SELECT id, nazwa, zdjecie FROM gry;');?> </section> <sectionid="right"> <h3>Dodaj nową grę</h3> <formmethod="post"> <labelfor="name">nazwa</label><br> <inputtype="text"id="name"name="name"><br> <labelfor="description">opis</label><br> <inputtype="text"id="description"name="description"><br> <labelfor="price">cena</label><br> <inputtype="text"id="price"name="price"><br> <labelfor="image">zdjęcie</label><br> <inputtype="text"id="image"name="image"><br> <buttontype="submit">DODAJ</button> </form> </section> <footer> <formmethod="post"> <inputtype="text"name="game_id"> <buttontype="submit">Pokaż opis</button> </form> </footer></body></html>
R.5.7 Skrypt 2 wyświetla obrazy oraz ich nazwy zwrócone zapytaniem
R.2.10 W sekcji środkowej umieszczony przynajmniej jeden obraz z tekstem alternatywnym i atrybutem title (w skrypcie lub HTML)
ok. 4 min
Polecenie z arkusza
Zwrócone zapytaniem wiersze są wyświetlone w bloku, który składa się z: obrazu, którego źródłem jest pole zdjecie, tekstem alternatywnym jest pole nazwa, a podpowiedzią (dymek widoczny na ilustracji 2 dla pierwszej grafiki) jest pole id.
Ta sama pętla while co w skrypcie 1. Każdy obieg wypisuje blok <div class='game'> (klasa, na którą czeka reguła .game w CSS) z obrazem <img>: src = nazwa pliku z kolumny zdjecie ($row[2]), alt = nazwa ($row[1]), title = id ($row[0]). Atrybut title to właśnie „dymek” - podpowiedź po najechaniu kursorem, którą pokazuje ilustracja 2 (cyfra 1 przy kursorze na obrazie Czarodziejki). Kryterium R.2.10: obraz z tekstem alternatywnym i atrybutem title.
Pliki graficzne muszą leżeć obok gry.php - w bazie są same nazwy, dokładnie takie jak w archiwum (wielkość liter ma znaczenie na serwerach linuksowych).
Typowe błędy
Zamiana alt z title (alt = id, title = nazwa) to strata R.2.10 i R.5.7 - arkusz mówi wprost, które pole gdzie.
gry.phpPHPzmienione: 5 linii
<?php$connection = mysqli_connect('localhost', 'root', '', 'gry');?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Gry komputerowe</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Ranking gier komputerowych</h1> </header> <sectionid="left"> <h3>Top 5 gier w tym miesiącu</h3> <ul><?php$top_games = mysqli_query($connection, 'SELECT nazwa, punkty FROM gry ORDER BY punkty DESC LIMIT 5;');while ($row = mysqli_fetch_row($top_games)) {echo"<li>$row[0] <span class='points'>$row[1]</span></li>";}?> </ul> <h3>Nasz sklep</h3> <ahref="http://sklep.gry.pl">Tu kupisz gry</a> <h3>Stronę wykonał</h3> <p>Bartosz Bryniarski</p> </section> <sectionid="middle"><?php$games = mysqli_query($connection, 'SELECT id, nazwa, zdjecie FROM gry;');while ($row = mysqli_fetch_row($games)) {echo"<div class='game'>";echo"<img src='$row[2]' alt='$row[1]' title='$row[0]'>";echo'</div>';}?> </section> <sectionid="right"> <h3>Dodaj nową grę</h3> <formmethod="post"> <labelfor="name">nazwa</label><br> <inputtype="text"id="name"name="name"><br> <labelfor="description">opis</label><br> <inputtype="text"id="description"name="description"><br> <labelfor="price">cena</label><br> <inputtype="text"id="price"name="price"><br> <labelfor="image">zdjęcie</label><br> <inputtype="text"id="image"name="image"><br> <buttontype="submit">DODAJ</button> </form> </section> <footer> <formmethod="post"> <inputtype="text"name="game_id"> <buttontype="submit">Pokaż opis</button> </form> </footer></body></html>
Podgląd strony po tym kroku (gry.php)
Krok 46: Skrypt 2: paragraf z nazwą
Kryteria:
R.5.7 Skrypt 2 wyświetla obrazy oraz ich nazwy zwrócone zapytaniem
ok. 1 min
Polecenie z arkusza
Paragrafu z nazwą zdjęcia.
Do bloku dokładasz <p> z nazwą gry ($row[1]) pod obrazem, przed zamknięciem </div>. Sekcja środkowa w podglądzie wygląda jak na ilustracji 2: dziewięć bloków obok siebie, zawijanych, z paskami przewijania.
gry.phpPHPzmienione: 1 linia
<?php$connection = mysqli_connect('localhost', 'root', '', 'gry');?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Gry komputerowe</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Ranking gier komputerowych</h1> </header> <sectionid="left"> <h3>Top 5 gier w tym miesiącu</h3> <ul><?php$top_games = mysqli_query($connection, 'SELECT nazwa, punkty FROM gry ORDER BY punkty DESC LIMIT 5;');while ($row = mysqli_fetch_row($top_games)) {echo"<li>$row[0] <span class='points'>$row[1]</span></li>";}?> </ul> <h3>Nasz sklep</h3> <ahref="http://sklep.gry.pl">Tu kupisz gry</a> <h3>Stronę wykonał</h3> <p>Bartosz Bryniarski</p> </section> <sectionid="middle"><?php$games = mysqli_query($connection, 'SELECT id, nazwa, zdjecie FROM gry;');while ($row = mysqli_fetch_row($games)) {echo"<div class='game'>";echo"<img src='$row[2]' alt='$row[1]' title='$row[0]'>";echo"<p>$row[1]</p>";echo'</div>';}?> </section> <sectionid="right"> <h3>Dodaj nową grę</h3> <formmethod="post"> <labelfor="name">nazwa</label><br> <inputtype="text"id="name"name="name"><br> <labelfor="description">opis</label><br> <inputtype="text"id="description"name="description"><br> <labelfor="price">cena</label><br> <inputtype="text"id="price"name="price"><br> <labelfor="image">zdjęcie</label><br> <inputtype="text"id="image"name="image"><br> <buttontype="submit">DODAJ</button> </form> </section> <footer> <formmethod="post"> <inputtype="text"name="game_id"> <buttontype="submit">Pokaż opis</button> </form> </footer></body></html>
Podgląd strony po tym kroku (gry.php)
Krok 47: Skrypt 3: tylko gdy wpisano id
Kryteria: ,
R.5.4 W przynajmniej jednym skrypcie związanym z formularzem działania następują tylko wtedy, gdy wypełnione pole nazwa (skrypt 4) lub id (skrypt 3)
R.5.5 Skrypt zawiera instrukcję pobierającą dane z tablicy globalnej odpowiadającej zastosowanej metodzie formularza ($_POST, $_GET, $_REQUEST)
ok. 2 min
Polecenie z arkusza
Skrypt 3 związany z formularzem w stopce: jeżeli wpisano id do pola edycyjnego, skrypt wysyła do bazy danych zapytanie 2 zmodyfikowane tak, że wybierany jest wiersz o id podanym w polu edycyjnym.
Skrypt 3 piszesz w stopce, pod formularzem - tam ma się pojawić wynik. Warunek ma dwie części: isset($_POST['game_id']) (czy formularz w ogóle został wysłany) i$_POST['game_id'] != '' (czy pole nie jest puste). „Jeżeli wpisano id” to właśnie ta druga część - klucz R.5.4 sprawdza, że skrypt działa tylko przy wypełnionym polu. Przy pierwszym wejściu na stronę blok if jest pomijany.
Typowe błędy
Samo isset nie wystarczy: po kliknięciu „Pokaż opis” z pustym polem $_POST['game_id'] istnieje (jako pusty napis) i skrypt wysłałby zapytanie WHERE id = - błąd SQL na stronie.
gry.phpPHPzmienione: 4 linie
<?php$connection = mysqli_connect('localhost', 'root', '', 'gry');?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Gry komputerowe</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Ranking gier komputerowych</h1> </header> <sectionid="left"> <h3>Top 5 gier w tym miesiącu</h3> <ul><?php$top_games = mysqli_query($connection, 'SELECT nazwa, punkty FROM gry ORDER BY punkty DESC LIMIT 5;');while ($row = mysqli_fetch_row($top_games)) {echo"<li>$row[0] <span class='points'>$row[1]</span></li>";}?> </ul> <h3>Nasz sklep</h3> <ahref="http://sklep.gry.pl">Tu kupisz gry</a> <h3>Stronę wykonał</h3> <p>Bartosz Bryniarski</p> </section> <sectionid="middle"><?php$games = mysqli_query($connection, 'SELECT id, nazwa, zdjecie FROM gry;');while ($row = mysqli_fetch_row($games)) {echo"<div class='game'>";echo"<img src='$row[2]' alt='$row[1]' title='$row[0]'>";echo"<p>$row[1]</p>";echo'</div>';}?> </section> <sectionid="right"> <h3>Dodaj nową grę</h3> <formmethod="post"> <labelfor="name">nazwa</label><br> <inputtype="text"id="name"name="name"><br> <labelfor="description">opis</label><br> <inputtype="text"id="description"name="description"><br> <labelfor="price">cena</label><br> <inputtype="text"id="price"name="price"><br> <labelfor="image">zdjęcie</label><br> <inputtype="text"id="image"name="image"><br> <buttontype="submit">DODAJ</button> </form> </section> <footer> <formmethod="post"> <inputtype="text"name="game_id"> <buttontype="submit">Pokaż opis</button> </form><?phpif (isset($_POST['game_id']) && $_POST['game_id'] != '') {}?> </footer></body></html>
R.5.3 Skrypt 1 wysyła do bazy danych zapytanie 3 lub skrypt 2 wysyła zapytanie 1, lub skrypt 3 wysyła zapytanie 2 zmodyfikowane w ten sposób, że wybierany jest wiersz o id podanym w polu edycyjnym
R.5.5 Skrypt zawiera instrukcję pobierającą dane z tablicy globalnej odpowiadającej zastosowanej metodzie formularza ($_POST, $_GET, $_REQUEST)
ok. 2 min
Polecenie z arkusza
Skrypt wysyła do bazy danych zapytanie 2 zmodyfikowane tak, że wybierany jest wiersz o id podanym w polu edycyjnym.
Wartość z pola trafia do $game_id ($_POST['game_id'] - klucz to atrybut name pola). Weź zapytanie 2 z kwerendy.txt i w warunku zamiast id = 1 wstaw id = $game_id. Cały napis zapytania w cudzysłowie, żeby PHP podstawił zmienną. Wynik trafia do $result.
gry.phpPHPzmienione: 2 linie
<?php$connection = mysqli_connect('localhost', 'root', '', 'gry');?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Gry komputerowe</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Ranking gier komputerowych</h1> </header> <sectionid="left"> <h3>Top 5 gier w tym miesiącu</h3> <ul><?php$top_games = mysqli_query($connection, 'SELECT nazwa, punkty FROM gry ORDER BY punkty DESC LIMIT 5;');while ($row = mysqli_fetch_row($top_games)) {echo"<li>$row[0] <span class='points'>$row[1]</span></li>";}?> </ul> <h3>Nasz sklep</h3> <ahref="http://sklep.gry.pl">Tu kupisz gry</a> <h3>Stronę wykonał</h3> <p>Bartosz Bryniarski</p> </section> <sectionid="middle"><?php$games = mysqli_query($connection, 'SELECT id, nazwa, zdjecie FROM gry;');while ($row = mysqli_fetch_row($games)) {echo"<div class='game'>";echo"<img src='$row[2]' alt='$row[1]' title='$row[0]'>";echo"<p>$row[1]</p>";echo'</div>';}?> </section> <sectionid="right"> <h3>Dodaj nową grę</h3> <formmethod="post"> <labelfor="name">nazwa</label><br> <inputtype="text"id="name"name="name"><br> <labelfor="description">opis</label><br> <inputtype="text"id="description"name="description"><br> <labelfor="price">cena</label><br> <inputtype="text"id="price"name="price"><br> <labelfor="image">zdjęcie</label><br> <inputtype="text"id="image"name="image"><br> <buttontype="submit">DODAJ</button> </form> </section> <footer> <formmethod="post"> <inputtype="text"name="game_id"> <buttontype="submit">Pokaż opis</button> </form><?phpif (isset($_POST['game_id']) && $_POST['game_id'] != '') {$game_id = $_POST['game_id'];$result = mysqli_query($connection, "SELECT nazwa, LEFT(opis, 100), punkty, cena FROM gry WHERE id = $game_id;");}?> </footer></body></html>
Krok 49: Skrypt 3: nagłówek z nazwą, punktami i ceną
Kryteria:
R.5.8 Skrypt 3 wyświetla napis: „<nazwa>, <punkty> punktów, <cena> zł", gdzie nawiasy <> oznaczają wartość pobraną z bazy danych oraz wyświetla opis
ok. 3 min
Polecenie z arkusza
Zwrócony zapytaniem wiersz jest wyświetlany pod formularzem według wzoru: w nagłówku drugiego stopnia: <nazwa>, <punkty> punktów, <cena> zł.
Zapytanie z warunkiem na klucz główny zwraca jeden wiersz, więc pętla nie jest potrzebna - jedno mysqli_fetch_row($result) do $game. Kolejność pól jak w SELECT: $game[0] nazwa, $game[1] skrócony opis, $game[2] punkty, $game[3] cena. Wzór z arkusza przepisujesz dosłownie do <h2>, z przecinkami i słowami „punktów” oraz „zł”.
Podgląd pokazuje stan z ilustracji 2 - w formularzu wprowadzono 6: „Mag, 357 punktów, 150 zł”.
Typowe błędy
Dla id, którego nie ma w bazie (np. 99), mysqli_fetch_row zwraca null, a odczyt $game[0] wypisuje na stronie ostrzeżenie PHP. Klucz tego nie sprawdza; kto ma chwilę, owija wypisywanie w if ($game) { ... }.
gry.phpPHPzmienione: 2 linie
<?php$connection = mysqli_connect('localhost', 'root', '', 'gry');?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Gry komputerowe</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Ranking gier komputerowych</h1> </header> <sectionid="left"> <h3>Top 5 gier w tym miesiącu</h3> <ul><?php$top_games = mysqli_query($connection, 'SELECT nazwa, punkty FROM gry ORDER BY punkty DESC LIMIT 5;');while ($row = mysqli_fetch_row($top_games)) {echo"<li>$row[0] <span class='points'>$row[1]</span></li>";}?> </ul> <h3>Nasz sklep</h3> <ahref="http://sklep.gry.pl">Tu kupisz gry</a> <h3>Stronę wykonał</h3> <p>Bartosz Bryniarski</p> </section> <sectionid="middle"><?php$games = mysqli_query($connection, 'SELECT id, nazwa, zdjecie FROM gry;');while ($row = mysqli_fetch_row($games)) {echo"<div class='game'>";echo"<img src='$row[2]' alt='$row[1]' title='$row[0]'>";echo"<p>$row[1]</p>";echo'</div>';}?> </section> <sectionid="right"> <h3>Dodaj nową grę</h3> <formmethod="post"> <labelfor="name">nazwa</label><br> <inputtype="text"id="name"name="name"><br> <labelfor="description">opis</label><br> <inputtype="text"id="description"name="description"><br> <labelfor="price">cena</label><br> <inputtype="text"id="price"name="price"><br> <labelfor="image">zdjęcie</label><br> <inputtype="text"id="image"name="image"><br> <buttontype="submit">DODAJ</button> </form> </section> <footer> <formmethod="post"> <inputtype="text"name="game_id"> <buttontype="submit">Pokaż opis</button> </form><?phpif (isset($_POST['game_id']) && $_POST['game_id'] != '') {$game_id = $_POST['game_id'];$result = mysqli_query($connection, "SELECT nazwa, LEFT(opis, 100), punkty, cena FROM gry WHERE id = $game_id;");$game = mysqli_fetch_row($result);echo"<h2>$game[0], $game[2] punktów, $game[3] zł</h2>";}?> </footer></body></html>
Podgląd strony po tym kroku (gry.php)
Krok 50: Skrypt 3: opis w paragrafie
Kryteria:
R.5.8 Skrypt 3 wyświetla napis: „<nazwa>, <punkty> punktów, <cena> zł", gdzie nawiasy <> oznaczają wartość pobraną z bazy danych oraz wyświetla opis
ok. 1 min
Polecenie z arkusza
Opis w paragrafie.
<p> z $game[1] - to opis skrócony do 100 znaków przez LEFT(opis, 100) w zapytaniu, dokładnie tak, jak urwany tekst w stopce na ilustracji 2.
gry.phpPHPzmienione: 1 linia
<?php$connection = mysqli_connect('localhost', 'root', '', 'gry');?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Gry komputerowe</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Ranking gier komputerowych</h1> </header> <sectionid="left"> <h3>Top 5 gier w tym miesiącu</h3> <ul><?php$top_games = mysqli_query($connection, 'SELECT nazwa, punkty FROM gry ORDER BY punkty DESC LIMIT 5;');while ($row = mysqli_fetch_row($top_games)) {echo"<li>$row[0] <span class='points'>$row[1]</span></li>";}?> </ul> <h3>Nasz sklep</h3> <ahref="http://sklep.gry.pl">Tu kupisz gry</a> <h3>Stronę wykonał</h3> <p>Bartosz Bryniarski</p> </section> <sectionid="middle"><?php$games = mysqli_query($connection, 'SELECT id, nazwa, zdjecie FROM gry;');while ($row = mysqli_fetch_row($games)) {echo"<div class='game'>";echo"<img src='$row[2]' alt='$row[1]' title='$row[0]'>";echo"<p>$row[1]</p>";echo'</div>';}?> </section> <sectionid="right"> <h3>Dodaj nową grę</h3> <formmethod="post"> <labelfor="name">nazwa</label><br> <inputtype="text"id="name"name="name"><br> <labelfor="description">opis</label><br> <inputtype="text"id="description"name="description"><br> <labelfor="price">cena</label><br> <inputtype="text"id="price"name="price"><br> <labelfor="image">zdjęcie</label><br> <inputtype="text"id="image"name="image"><br> <buttontype="submit">DODAJ</button> </form> </section> <footer> <formmethod="post"> <inputtype="text"name="game_id"> <buttontype="submit">Pokaż opis</button> </form><?phpif (isset($_POST['game_id']) && $_POST['game_id'] != '') {$game_id = $_POST['game_id'];$result = mysqli_query($connection, "SELECT nazwa, LEFT(opis, 100), punkty, cena FROM gry WHERE id = $game_id;");$game = mysqli_fetch_row($result);echo"<h2>$game[0], $game[2] punktów, $game[3] zł</h2>";echo"<p>$game[1]</p>";}?> </footer></body></html>
Podgląd strony po tym kroku (gry.php)
Krok 51: Skrypt 4: tylko gdy wypełniono pole nazwa
Kryteria:
R.5.4 W przynajmniej jednym skrypcie związanym z formularzem działania następują tylko wtedy, gdy wypełnione pole nazwa (skrypt 4) lub id (skrypt 3)
ok. 2 min
Polecenie z arkusza
Skrypt 4 związany z formularzem z sekcji prawej: jeżeli wypełniono pole nazwa, skrypt wysyła do bazy zmodyfikowane zapytanie 4 w ten sposób, że wstawione są wartości pobrane z formularza, a liczba punktów wynosi 0.
Skrypt 4 nic nie wypisuje, tylko zapisuje do bazy - piszesz go na początku pliku, zaraz po połączeniu. Dzięki temu nowa gra jest w bazie, zanim skrypty 1 i 2 zbudują listę i galerię, i pojawia się na stronie od razu po kliknięciu DODAJ.
Warunek jak w skrypcie 3: isset($_POST['name']) i pole niepuste. Formularz w stopce nie ma pola name, więc wysłanie „Pokaż opis” nie uruchomi skryptu 4 - tak PHP rozróżnia dwa formularze w jednym pliku.
gry.phpPHPzmienione: 3 linie
<?php$connection = mysqli_connect('localhost', 'root', '', 'gry');if (isset($_POST['name']) && $_POST['name'] != '') {}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Gry komputerowe</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Ranking gier komputerowych</h1> </header> <sectionid="left"> <h3>Top 5 gier w tym miesiącu</h3> <ul><?php$top_games = mysqli_query($connection, 'SELECT nazwa, punkty FROM gry ORDER BY punkty DESC LIMIT 5;');while ($row = mysqli_fetch_row($top_games)) {echo"<li>$row[0] <span class='points'>$row[1]</span></li>";}?> </ul> <h3>Nasz sklep</h3> <ahref="http://sklep.gry.pl">Tu kupisz gry</a> <h3>Stronę wykonał</h3> <p>Bartosz Bryniarski</p> </section> <sectionid="middle"><?php$games = mysqli_query($connection, 'SELECT id, nazwa, zdjecie FROM gry;');while ($row = mysqli_fetch_row($games)) {echo"<div class='game'>";echo"<img src='$row[2]' alt='$row[1]' title='$row[0]'>";echo"<p>$row[1]</p>";echo'</div>';}?> </section> <sectionid="right"> <h3>Dodaj nową grę</h3> <formmethod="post"> <labelfor="name">nazwa</label><br> <inputtype="text"id="name"name="name"><br> <labelfor="description">opis</label><br> <inputtype="text"id="description"name="description"><br> <labelfor="price">cena</label><br> <inputtype="text"id="price"name="price"><br> <labelfor="image">zdjęcie</label><br> <inputtype="text"id="image"name="image"><br> <buttontype="submit">DODAJ</button> </form> </section> <footer> <formmethod="post"> <inputtype="text"name="game_id"> <buttontype="submit">Pokaż opis</button> </form><?phpif (isset($_POST['game_id']) && $_POST['game_id'] != '') {$game_id = $_POST['game_id'];$result = mysqli_query($connection, "SELECT nazwa, LEFT(opis, 100), punkty, cena FROM gry WHERE id = $game_id;");$game = mysqli_fetch_row($result);echo"<h2>$game[0], $game[2] punktów, $game[3] zł</h2>";echo"<p>$game[1]</p>";}?> </footer></body></html>
R.5.5 Skrypt zawiera instrukcję pobierającą dane z tablicy globalnej odpowiadającej zastosowanej metodzie formularza ($_POST, $_GET, $_REQUEST)
ok. 2 min
Polecenie z arkusza
Skrypt wysyła do bazy zmodyfikowane zapytanie 4 w ten sposób, że wstawione są wartości pobrane z formularza, a liczba punktów wynosi 0.
Cztery wartości z $_POST do czterech zmiennych o znaczących nazwach: $name, $description, $price, $image. Klucze $_POST to atrybuty name pól z sekcji prawej. Punktów z formularza nie czytasz - arkusz każe wstawić 0.
gry.phpPHPzmienione: 4 linie
<?php$connection = mysqli_connect('localhost', 'root', '', 'gry');if (isset($_POST['name']) && $_POST['name'] != '') {$name = $_POST['name'];$description = $_POST['description'];$price = $_POST['price'];$image = $_POST['image'];}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Gry komputerowe</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Ranking gier komputerowych</h1> </header> <sectionid="left"> <h3>Top 5 gier w tym miesiącu</h3> <ul><?php$top_games = mysqli_query($connection, 'SELECT nazwa, punkty FROM gry ORDER BY punkty DESC LIMIT 5;');while ($row = mysqli_fetch_row($top_games)) {echo"<li>$row[0] <span class='points'>$row[1]</span></li>";}?> </ul> <h3>Nasz sklep</h3> <ahref="http://sklep.gry.pl">Tu kupisz gry</a> <h3>Stronę wykonał</h3> <p>Bartosz Bryniarski</p> </section> <sectionid="middle"><?php$games = mysqli_query($connection, 'SELECT id, nazwa, zdjecie FROM gry;');while ($row = mysqli_fetch_row($games)) {echo"<div class='game'>";echo"<img src='$row[2]' alt='$row[1]' title='$row[0]'>";echo"<p>$row[1]</p>";echo'</div>';}?> </section> <sectionid="right"> <h3>Dodaj nową grę</h3> <formmethod="post"> <labelfor="name">nazwa</label><br> <inputtype="text"id="name"name="name"><br> <labelfor="description">opis</label><br> <inputtype="text"id="description"name="description"><br> <labelfor="price">cena</label><br> <inputtype="text"id="price"name="price"><br> <labelfor="image">zdjęcie</label><br> <inputtype="text"id="image"name="image"><br> <buttontype="submit">DODAJ</button> </form> </section> <footer> <formmethod="post"> <inputtype="text"name="game_id"> <buttontype="submit">Pokaż opis</button> </form><?phpif (isset($_POST['game_id']) && $_POST['game_id'] != '') {$game_id = $_POST['game_id'];$result = mysqli_query($connection, "SELECT nazwa, LEFT(opis, 100), punkty, cena FROM gry WHERE id = $game_id;");$game = mysqli_fetch_row($result);echo"<h2>$game[0], $game[2] punktów, $game[3] zł</h2>";echo"<p>$game[1]</p>";}?> </footer></body></html>
Krok 53: Skrypt 4: wstawienie wiersza z zerem punktów
Kryteria:
R.5.9 Skrypt 4 wstawia do bazy danych rekord z danymi pobranymi z formularza, liczba punktów =0 (sprawdzić w phpMyAdmin)
ok. 3 min
Polecenie z arkusza
Skrypt wysyła do bazy zmodyfikowane zapytanie 4 w ten sposób, że wstawione są wartości pobrane z formularza, a liczba punktów wynosi 0.
Weź zapytanie 4 z kwerendy.txt i zamiast danych Zamczyska wstaw zmienne: napisy w apostrofach ('$name'), a w miejscu punktów liczba 0. Cały napis w cudzysłowie, żeby PHP podstawił wartości. mysqli_query wykonuje INSERT - nic nie zwraca do wyświetlenia, efekt sprawdza się w phpMyAdmin (R.5.9) albo na stronie po odświeżeniu: nowa gra na końcu galerii.
Ta wersja spełnia klucz. Ma jednak wadę, o której mowa przy refaktorze: dane z formularza wchodzą do zapytania bez żadnej kontroli.
Typowe błędy
Wpisanie w formularzu nazwy z apostrofem (np. „Assassin's Creed”) rozrywa napis SQL i zapytanie kończy się błędem - to objaw podatności, którą usuniesz na końcu.
gry.phpPHPzmienione: 1 linia
<?php$connection = mysqli_connect('localhost', 'root', '', 'gry');if (isset($_POST['name']) && $_POST['name'] != '') {$name = $_POST['name'];$description = $_POST['description'];$price = $_POST['price'];$image = $_POST['image']; mysqli_query($connection, "INSERT INTO gry (nazwa, opis, punkty, cena, zdjecie) VALUES ('$name', '$description', 0, '$price', '$image');");}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Gry komputerowe</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Ranking gier komputerowych</h1> </header> <sectionid="left"> <h3>Top 5 gier w tym miesiącu</h3> <ul><?php$top_games = mysqli_query($connection, 'SELECT nazwa, punkty FROM gry ORDER BY punkty DESC LIMIT 5;');while ($row = mysqli_fetch_row($top_games)) {echo"<li>$row[0] <span class='points'>$row[1]</span></li>";}?> </ul> <h3>Nasz sklep</h3> <ahref="http://sklep.gry.pl">Tu kupisz gry</a> <h3>Stronę wykonał</h3> <p>Bartosz Bryniarski</p> </section> <sectionid="middle"><?php$games = mysqli_query($connection, 'SELECT id, nazwa, zdjecie FROM gry;');while ($row = mysqli_fetch_row($games)) {echo"<div class='game'>";echo"<img src='$row[2]' alt='$row[1]' title='$row[0]'>";echo"<p>$row[1]</p>";echo'</div>';}?> </section> <sectionid="right"> <h3>Dodaj nową grę</h3> <formmethod="post"> <labelfor="name">nazwa</label><br> <inputtype="text"id="name"name="name"><br> <labelfor="description">opis</label><br> <inputtype="text"id="description"name="description"><br> <labelfor="price">cena</label><br> <inputtype="text"id="price"name="price"><br> <labelfor="image">zdjęcie</label><br> <inputtype="text"id="image"name="image"><br> <buttontype="submit">DODAJ</button> </form> </section> <footer> <formmethod="post"> <inputtype="text"name="game_id"> <buttontype="submit">Pokaż opis</button> </form><?phpif (isset($_POST['game_id']) && $_POST['game_id'] != '') {$game_id = $_POST['game_id'];$result = mysqli_query($connection, "SELECT nazwa, LEFT(opis, 100), punkty, cena FROM gry WHERE id = $game_id;");$game = mysqli_fetch_row($result);echo"<h2>$game[0], $game[2] punktów, $game[3] zł</h2>";echo"<p>$game[1]</p>";}?> </footer></body></html>
R.5.2 Ostatnią operacją na bazie jest jej zamknięcie
ok. 1 min
Polecenie z arkusza
Na końcu jest zamykane połączenie z serwerem.
mysqli_close($connection) na końcu pliku, gdy wszystkie skrypty już skorzystały z połączenia. Klucz R.5.2 sprawdza, że zamknięcie jest ostatnią operacją na bazie. Strona spełnia wszystkie kryteria arkusza - dalej trzy kroki „jak w prawdziwym projekcie”.
Typowe błędy
mysqli_close po skrypcie 1 sprawia, że skrypty 2 i 3 idą po zamkniętym połączeniu - galeria i opis znikają.
gry.phpPHPzmienione: 3 linie
<?php$connection = mysqli_connect('localhost', 'root', '', 'gry');if (isset($_POST['name']) && $_POST['name'] != '') {$name = $_POST['name'];$description = $_POST['description'];$price = $_POST['price'];$image = $_POST['image']; mysqli_query($connection, "INSERT INTO gry (nazwa, opis, punkty, cena, zdjecie) VALUES ('$name', '$description', 0, '$price', '$image');");}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Gry komputerowe</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Ranking gier komputerowych</h1> </header> <sectionid="left"> <h3>Top 5 gier w tym miesiącu</h3> <ul><?php$top_games = mysqli_query($connection, 'SELECT nazwa, punkty FROM gry ORDER BY punkty DESC LIMIT 5;');while ($row = mysqli_fetch_row($top_games)) {echo"<li>$row[0] <span class='points'>$row[1]</span></li>";}?> </ul> <h3>Nasz sklep</h3> <ahref="http://sklep.gry.pl">Tu kupisz gry</a> <h3>Stronę wykonał</h3> <p>Bartosz Bryniarski</p> </section> <sectionid="middle"><?php$games = mysqli_query($connection, 'SELECT id, nazwa, zdjecie FROM gry;');while ($row = mysqli_fetch_row($games)) {echo"<div class='game'>";echo"<img src='$row[2]' alt='$row[1]' title='$row[0]'>";echo"<p>$row[1]</p>";echo'</div>';}?> </section> <sectionid="right"> <h3>Dodaj nową grę</h3> <formmethod="post"> <labelfor="name">nazwa</label><br> <inputtype="text"id="name"name="name"><br> <labelfor="description">opis</label><br> <inputtype="text"id="description"name="description"><br> <labelfor="price">cena</label><br> <inputtype="text"id="price"name="price"><br> <labelfor="image">zdjęcie</label><br> <inputtype="text"id="image"name="image"><br> <buttontype="submit">DODAJ</button> </form> </section> <footer> <formmethod="post"> <inputtype="text"name="game_id"> <buttontype="submit">Pokaż opis</button> </form><?phpif (isset($_POST['game_id']) && $_POST['game_id'] != '') {$game_id = $_POST['game_id'];$result = mysqli_query($connection, "SELECT nazwa, LEFT(opis, 100), punkty, cena FROM gry WHERE id = $game_id;");$game = mysqli_fetch_row($result);echo"<h2>$game[0], $game[2] punktów, $game[3] zł</h2>";echo"<p>$game[1]</p>";}?> </footer><?phpmysqli_close($connection);?></body></html>
Kroki 55-57 · po tym etapie masz 41 z 41 kryteriów (pkt)
Krok 55: Refaktor: zapytanie przygotowane w skrypcie 3
Kryteria: ,
R.5.3 Skrypt 1 wysyła do bazy danych zapytanie 3 lub skrypt 2 wysyła zapytanie 1, lub skrypt 3 wysyła zapytanie 2 zmodyfikowane w ten sposób, że wybierany jest wiersz o id podanym w polu edycyjnym
R.5.8 Skrypt 3 wyświetla napis: „<nazwa>, <punkty> punktów, <cena> zł", gdzie nawiasy <> oznaczają wartość pobraną z bazy danych oraz wyświetla opis
ok. 5 min
Polecenie z arkusza
Skrypt wysyła do bazy danych zapytanie 2 zmodyfikowane tak, że wybierany jest wiersz o id podanym w polu edycyjnym.
Wersja z "... WHERE id = $game_id" sklejała zapytanie z tekstem wpisanym przez użytkownika - to SQL injection: wpisanie 1 OR 1=1 zmienia sens zapytania. W prawdziwych projektach dane nigdy nie trafiają do zapytania jako tekst, tylko przez zapytanie przygotowane:
mysqli_prepare dostaje zapytanie z ? w miejscu wartości - baza zna jego strukturę, zanim zobaczy dane,
mysqli_stmt_bind_param($statement, 'i', $game_id) podpina zmienną pod ?; litera i mówi, że to liczba całkowita (s napis, d liczba zmiennoprzecinkowa),
mysqli_stmt_execute wykonuje, a mysqli_stmt_get_result oddaje wynik, z którego czytasz jak dotąd.
Stare dwie linie zostają w komentarzu // do porównania. Podgląd bez zmian - to ta sama logika, tylko bezpieczna.
Typowe błędy
Wiersz czyta się z wyniku mysqli_stmt_get_result, nie z samego zapytania przygotowanego: mysqli_fetch_row($statement) kończy się błędem TypeError (oczekuje mysqli_result, dostaje mysqli_stmt) - sprawdzone w PHP 8.2.
gry.phpPHPzmienione: 6 linii
<?php$connection = mysqli_connect('localhost', 'root', '', 'gry');if (isset($_POST['name']) && $_POST['name'] != '') {$name = $_POST['name'];$description = $_POST['description'];$price = $_POST['price'];$image = $_POST['image']; mysqli_query($connection, "INSERT INTO gry (nazwa, opis, punkty, cena, zdjecie) VALUES ('$name', '$description', 0, '$price', '$image');");}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Gry komputerowe</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Ranking gier komputerowych</h1> </header> <sectionid="left"> <h3>Top 5 gier w tym miesiącu</h3> <ul><?php$top_games = mysqli_query($connection, 'SELECT nazwa, punkty FROM gry ORDER BY punkty DESC LIMIT 5;');while ($row = mysqli_fetch_row($top_games)) {echo"<li>$row[0] <span class='points'>$row[1]</span></li>";}?> </ul> <h3>Nasz sklep</h3> <ahref="http://sklep.gry.pl">Tu kupisz gry</a> <h3>Stronę wykonał</h3> <p>Bartosz Bryniarski</p> </section> <sectionid="middle"><?php$games = mysqli_query($connection, 'SELECT id, nazwa, zdjecie FROM gry;');while ($row = mysqli_fetch_row($games)) {echo"<div class='game'>";echo"<img src='$row[2]' alt='$row[1]' title='$row[0]'>";echo"<p>$row[1]</p>";echo'</div>';}?> </section> <sectionid="right"> <h3>Dodaj nową grę</h3> <formmethod="post"> <labelfor="name">nazwa</label><br> <inputtype="text"id="name"name="name"><br> <labelfor="description">opis</label><br> <inputtype="text"id="description"name="description"><br> <labelfor="price">cena</label><br> <inputtype="text"id="price"name="price"><br> <labelfor="image">zdjęcie</label><br> <inputtype="text"id="image"name="image"><br> <buttontype="submit">DODAJ</button> </form> </section> <footer> <formmethod="post"> <inputtype="text"name="game_id"> <buttontype="submit">Pokaż opis</button> </form><?phpif (isset($_POST['game_id']) && $_POST['game_id'] != '') {$game_id = $_POST['game_id'];// $result = mysqli_query($connection, "SELECT nazwa, LEFT(opis, 100), punkty, cena FROM gry WHERE id = $game_id;");// $game = mysqli_fetch_row($result);$statement = mysqli_prepare($connection, 'SELECT nazwa, LEFT(opis, 100), punkty, cena FROM gry WHERE id = ?'); mysqli_stmt_bind_param($statement, 'i', $game_id); mysqli_stmt_execute($statement);$game = mysqli_fetch_row(mysqli_stmt_get_result($statement));echo"<h2>$game[0], $game[2] punktów, $game[3] zł</h2>";echo"<p>$game[1]</p>";}?> </footer><?phpmysqli_close($connection);?></body></html>
Krok 56: Refaktor: zapytanie przygotowane w skrypcie 4
Kryteria:
R.5.9 Skrypt 4 wstawia do bazy danych rekord z danymi pobranymi z formularza, liczba punktów =0 (sprawdzić w phpMyAdmin)
ok. 4 min
Polecenie z arkusza
Skrypt wysyła do bazy zmodyfikowane zapytanie 4 w ten sposób, że wstawione są wartości pobrane z formularza, a liczba punktów wynosi 0.
Tu ryzyko jest największe - cztery pola z formularza szły prosto do INSERT. Ten sam schemat: ? za każdą wartość z formularza (zero punktów zostaje w zapytaniu jako stała), a mysqli_stmt_bind_param z typami 'ssds': nazwa (s), opis (s), cena (d - liczba z częścią ułamkową, jak kolumna float), zdjęcie (s). Litery odpowiadają kolejności ? w zapytaniu.
Bonus: nazwa gry z apostrofem, która wcześniej wywalała zapytanie, teraz zapisze się poprawnie - baza dostaje ją jako dane, nie jako fragment SQL.
Typowe błędy
Liczba liter w 'ssds' musi równać się liczbie ? i liczbie zmiennych - inaczej PHP zgłosi błąd i nic nie zostanie wstawione.
gry.phpPHPzmienione: 4 linie
<?php$connection = mysqli_connect('localhost', 'root', '', 'gry');if (isset($_POST['name']) && $_POST['name'] != '') {$name = $_POST['name'];$description = $_POST['description'];$price = $_POST['price'];$image = $_POST['image'];// mysqli_query($connection, "INSERT INTO gry (nazwa, opis, punkty, cena, zdjecie) VALUES ('$name', '$description', 0, '$price', '$image');");$statement = mysqli_prepare($connection, 'INSERT INTO gry (nazwa, opis, punkty, cena, zdjecie) VALUES (?, ?, 0, ?, ?)'); mysqli_stmt_bind_param($statement, 'ssds', $name, $description, $price, $image); mysqli_stmt_execute($statement);}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Gry komputerowe</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Ranking gier komputerowych</h1> </header> <sectionid="left"> <h3>Top 5 gier w tym miesiącu</h3> <ul><?php$top_games = mysqli_query($connection, 'SELECT nazwa, punkty FROM gry ORDER BY punkty DESC LIMIT 5;');while ($row = mysqli_fetch_row($top_games)) {echo"<li>$row[0] <span class='points'>$row[1]</span></li>";}?> </ul> <h3>Nasz sklep</h3> <ahref="http://sklep.gry.pl">Tu kupisz gry</a> <h3>Stronę wykonał</h3> <p>Bartosz Bryniarski</p> </section> <sectionid="middle"><?php$games = mysqli_query($connection, 'SELECT id, nazwa, zdjecie FROM gry;');while ($row = mysqli_fetch_row($games)) {echo"<div class='game'>";echo"<img src='$row[2]' alt='$row[1]' title='$row[0]'>";echo"<p>$row[1]</p>";echo'</div>';}?> </section> <sectionid="right"> <h3>Dodaj nową grę</h3> <formmethod="post"> <labelfor="name">nazwa</label><br> <inputtype="text"id="name"name="name"><br> <labelfor="description">opis</label><br> <inputtype="text"id="description"name="description"><br> <labelfor="price">cena</label><br> <inputtype="text"id="price"name="price"><br> <labelfor="image">zdjęcie</label><br> <inputtype="text"id="image"name="image"><br> <buttontype="submit">DODAJ</button> </form> </section> <footer> <formmethod="post"> <inputtype="text"name="game_id"> <buttontype="submit">Pokaż opis</button> </form><?phpif (isset($_POST['game_id']) && $_POST['game_id'] != '') {$game_id = $_POST['game_id'];// $result = mysqli_query($connection, "SELECT nazwa, LEFT(opis, 100), punkty, cena FROM gry WHERE id = $game_id;");// $game = mysqli_fetch_row($result);$statement = mysqli_prepare($connection, 'SELECT nazwa, LEFT(opis, 100), punkty, cena FROM gry WHERE id = ?'); mysqli_stmt_bind_param($statement, 'i', $game_id); mysqli_stmt_execute($statement);$game = mysqli_fetch_row(mysqli_stmt_get_result($statement));echo"<h2>$game[0], $game[2] punktów, $game[3] zł</h2>";echo"<p>$game[1]</p>";}?> </footer><?phpmysqli_close($connection);?></body></html>
Krok 57: Refaktor: escapowanie danych przed wypisaniem
Kryteria: , ,
R.5.6 Skrypt 1 wyświetla nazwę i punkty zwrócone zapytaniem jako elementy listy
R.5.7 Skrypt 2 wyświetla obrazy oraz ich nazwy zwrócone zapytaniem
R.5.8 Skrypt 3 wyświetla napis: „<nazwa>, <punkty> punktów, <cena> zł", gdzie nawiasy <> oznaczają wartość pobraną z bazy danych oraz wyświetla opis
ok. 5 min
Polecenie z arkusza
Zwrócony zapytaniem wiersz jest wyświetlany pod formularzem według wzoru: w nagłówku drugiego stopnia: <nazwa>, <punkty> punktów, <cena> zł, gdzie nawiasy <> oznaczają wartość pobraną z bazy danych.
Druga strona tego samego problemu: skrypty 1-3 wypisują do HTML to, co jest w bazie - a do bazy każdy może dodać grę przez formularz. Nazwa <script>alert(1)</script> wykonałaby się w przeglądarce każdego odwiedzającego (XSS). htmlspecialchars() zamienia <, >, ", & na encje, więc tekst wyświetla się dosłownie i nie staje się kodem.
W każdym skrypcie stare echo zostaje w komentarzu, a przed nowym wartości z bazy przechodzą przez htmlspecialchars() (liczby - id, punkty, cena - nie potrzebują). Podgląd wygląda identycznie, bo dane z archiwum nie mają znaków specjalnych - różnicę zobaczysz dopiero przy złośliwej nazwie gry.
Typowe błędy
htmlspecialchars przy wpisywaniu do bazy (w skrypcie 4) zamiast przy wypisywaniu to częsty błąd: baza dostaje zniekształcone dane (& zamiast &), a każdy inny odbiorca danych i tak musi je escapować po swojemu. Escapujesz na wyjściu.
gry.phpPHPzmienione: 15 linii
<?php$connection = mysqli_connect('localhost', 'root', '', 'gry');if (isset($_POST['name']) && $_POST['name'] != '') {$name = $_POST['name'];$description = $_POST['description'];$price = $_POST['price'];$image = $_POST['image'];// mysqli_query($connection, "INSERT INTO gry (nazwa, opis, punkty, cena, zdjecie) VALUES ('$name', '$description', 0, '$price', '$image');");$statement = mysqli_prepare($connection, 'INSERT INTO gry (nazwa, opis, punkty, cena, zdjecie) VALUES (?, ?, 0, ?, ?)'); mysqli_stmt_bind_param($statement, 'ssds', $name, $description, $price, $image); mysqli_stmt_execute($statement);}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Gry komputerowe</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Ranking gier komputerowych</h1> </header> <sectionid="left"> <h3>Top 5 gier w tym miesiącu</h3> <ul><?php$top_games = mysqli_query($connection, 'SELECT nazwa, punkty FROM gry ORDER BY punkty DESC LIMIT 5;');while ($row = mysqli_fetch_row($top_games)) {// echo "<li>$row[0] <span class='points'>$row[1]</span></li>";$name = htmlspecialchars($row[0]);echo"<li>$name <span class='points'>$row[1]</span></li>";}?> </ul> <h3>Nasz sklep</h3> <ahref="http://sklep.gry.pl">Tu kupisz gry</a> <h3>Stronę wykonał</h3> <p>Bartosz Bryniarski</p> </section> <sectionid="middle"><?php$games = mysqli_query($connection, 'SELECT id, nazwa, zdjecie FROM gry;');while ($row = mysqli_fetch_row($games)) {echo"<div class='game'>";// echo "<img src='$row[2]' alt='$row[1]' title='$row[0]'>";// echo "<p>$row[1]</p>";$name = htmlspecialchars($row[1]);$image = htmlspecialchars($row[2]);echo"<img src='$image' alt='$name' title='$row[0]'>";echo"<p>$name</p>";echo'</div>';}?> </section> <sectionid="right"> <h3>Dodaj nową grę</h3> <formmethod="post"> <labelfor="name">nazwa</label><br> <inputtype="text"id="name"name="name"><br> <labelfor="description">opis</label><br> <inputtype="text"id="description"name="description"><br> <labelfor="price">cena</label><br> <inputtype="text"id="price"name="price"><br> <labelfor="image">zdjęcie</label><br> <inputtype="text"id="image"name="image"><br> <buttontype="submit">DODAJ</button> </form> </section> <footer> <formmethod="post"> <inputtype="text"name="game_id"> <buttontype="submit">Pokaż opis</button> </form><?phpif (isset($_POST['game_id']) && $_POST['game_id'] != '') {$game_id = $_POST['game_id'];// $result = mysqli_query($connection, "SELECT nazwa, LEFT(opis, 100), punkty, cena FROM gry WHERE id = $game_id;");// $game = mysqli_fetch_row($result);$statement = mysqli_prepare($connection, 'SELECT nazwa, LEFT(opis, 100), punkty, cena FROM gry WHERE id = ?'); mysqli_stmt_bind_param($statement, 'i', $game_id); mysqli_stmt_execute($statement);$game = mysqli_fetch_row(mysqli_stmt_get_result($statement));// echo "<h2>$game[0], $game[2] punktów, $game[3] zł</h2>";// echo "<p>$game[1]</p>";$name = htmlspecialchars($game[0]);$description = htmlspecialchars($game[1]);echo"<h2>$name, $game[2] punktów, $game[3] zł</h2>";echo"<p>$description</p>";}?> </footer><?phpmysqli_close($connection);?></body></html>
Krok 58 · po tym etapie masz 41 z 41 kryteriów (pkt)
Krok 58: Plik przeglądarka.txt i nagranie płyty
ok. 5 min
Polecenie z arkusza
Po zakończeniu pracy utwórz plik tekstowy o nazwie przeglądarka.txt. Zapisz w nim nazwę przeglądarki internetowej, w której weryfikowana była poprawność działania witryny. Nagraj płytę z rezultatami pracy.
W przeglądarka.txt wpisujesz nazwę przeglądarki, w której sprawdzasz stronę przez http://localhost/….
W folderze z numerem zdającego muszą być: Czarodziejka.jpg, gry.php, import.png, kalipso.jpg, katedra.jpg, krokodyl.jpg, kw1.jpg, kw2.jpg, kw3.jpg, kw4.jpg, kwerendy.txt, Mag.jpg, NieznanyLad.jpg, Persja.jpg, przeglądarka.txt, styl.css, WyprawaNaKsiezyc.jpg, zamczysko.jpg. Nagraj płytę, sprawdź jej odczyt i opisz numerem zdającego. Pusta lub nieczytelna płyta to zero punktów za wszystkie pięć rezultatów.
Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.
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: Bartosz Bryniarski. 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.