INF.03-01-26.06-SGCzerwiec 2026 · 52 kroki · ok. 91 min · 37 kryteriów z klucza
Rozwiązanie krok po kroku: Portal turystyczny Islandii (Nowoczesny kod (grid, PDO))
To drugi sposób rozwiązania tego samego arkusza. Wynik jest ten sam - baza islandia, cztery zapytania, dwie strony PHP (galeria islandia.php i opis miejsca obiekty.php?id=…), style w styl.css - i tak samo spełnia każde kryterium klucza. Inna jest droga:
układ na siatce CSS: grid-template-areas rysuje ilustrację 4 tekstem, więc bloki w kodzie stoją w kolejności czytania (nagłówek, treść, blok boczny, stopka), a nie w kolejności wymaganej przez float,
PHP na PDO z logiką na górze każdej strony i szablonem pod nią; tekst z bazy wychodzi przez funkcję e(),
jedno zapytanie przygotowane dla obu list bloku bocznego (wodospady i siedliska różnią się tylko numerem rodzaju),
parametr z adresu jest sprawdzany i nigdy nie trafia do treści SQL: filter_var przepuszcza tylko liczbę całkowitą, a zapytanie dostaje ją jako parametr; nieznane miejsce kończy się komunikatem i kodem 404 zamiast błędu PHP.
Każda strona jest samodzielna - połączenie (z nazwą bazy) i wszystkie skrypty są w jej pliku, tam gdzie egzaminator ich szuka. Wersja z kodem wspólnym wyniesionym do osobnych plików jest w trzeciej zakładce. Na końcu jest krótkie porównanie wersji i podpowiedź, którą wybrać na egzaminie.
Baza i zdjęcia pochodzą z prawdziwego archiwum pliki1 (załącznik na stronie arkusza, hasło w treści zadania), a podgląd każdego kroku z PHP to wynik prawdziwego uruchomienia stron na serwerze z tą bazą. Podgląd obiekty.php pokazuje obiekt z ilustracji 3 (Godafoss, obiekty.php?id=53).
Jak liczą się punkty. Klucz ma 37 kryteriów (R.1.1 - R.5.7), każde za 1 punkt. Próg zdania części praktycznej to 75%, czyli 28 punktów. Przy każdym kroku jest napisane, które kryteria spełnia.
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 37 kryteriów (pkt)
Krok 1: Baza danych islandia
Kryteria:
R.1.1 Wykonany import tabel do bazy danych islandia - plik import w formacie JPEG
ok. 3 min
Polecenie z arkusza
Utwórz bazę danych o nazwie islandia, z zestawem znaków utf8_unicode_ci.
Najpierw rozpakuj archiwum pliki1.7z z pulpitu (hasło isLandi@ z treści zadania) - są w nim baza1.sql i dziewięć zdjęć. W phpMyAdmin kliknij Nowa, wpisz islandia i wybierz utf8_unicode_ci. To samo w SQL:
CREATE DATABASE islandia CHARACTER SET utf8 COLLATE utf8_unicode_ci;
utf8_unicode_ci to ogólne porównywanie Unicode: sortuje polskie („gorące źródło”) i islandzkie litery („Fuglaskoðunarhús”) według zasad Unicode, a przy porównaniach nie rozróżnia wielkości liter (ci - case insensitive). Dziedziczą je jednak tylko tabele zakładane później bez własnego ustawienia - tabele z baza1.sql mają w definicji utf8_polish_ci i to ono działa w zapytaniach.
Typowe błędy
Nazwa bazy musi być dokładnie islandia - skrypty łączą się z bazą po nazwie (R.5.1).
Krok 2: Import tabel z pliku baza1.sql
Kryteria:
R.1.1 Wykonany import tabel do bazy danych islandia - plik import w formacie JPEG
ok. 2 min
Polecenie z arkusza
Do utworzonej bazy zaimportuj tabele z pliku baza1.sql.
Zaznacz bazę islandia, karta Import, plik baza1.sql, Wykonaj. Plik tworzy pięć tabel, ale zadanie korzysta tylko z trzech z ilustracji 1: kontynenty, rodzaje (rodzaje obiektów: wodospad, szczyt, siedlisko zwierząt…) i obiekty (25 obiektów z kluczami obcymi idRodzaj i idKontynent). Nazwy kolumn mają wielkie litery w środku (idObiekt, nazwaCechy, wartoscCechy) - w zapytaniach pisz je dokładnie tak.
Krok 3: Zrzut ekranu import.jpg
Kryteria:
R.1.1 Wykonany import tabel do bazy danych islandia - plik import w formacie JPEG
ok. 2 min
Polecenie z arkusza
Wykonaj zrzut ekranu po imporcie. Zapisz zrzut w formacie JPEG pod nazwą import. Nie kadruj zrzutu. Powinien on obejmować cały ekran monitora, z widocznym paskiem zadań. Na zrzucie powinny być widoczne elementy wskazujące na poprawnie wykonany import tabel.
Cały ekran (Print Screen) z komunikatem o udanym imporcie albo listą tabel, zapis jako import.jpg - w tym arkuszu import jest w JPEG, a zrzuty zapytań w PNG.
Typowe błędy
import.png zamiast import.jpg albo wykadrowany zrzut to strata punktu R.1.1.
Krok 4: Zapytanie 1: obiekty z Islandii
Kryteria: ,
R.1.2 W pliku kwerendy.txt zapisane co najmniej jedno zapytanie SQL, wynikające z treści zadania
R.1.3 Wykonane zapytanie 1 wybierające jedynie pola idObiekt, plik i nazwa z tabeli obiekty dla Islandii. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT idObiekt, plik, nazwa FROM obiekty WHERE panstwo = "Islandia"; oraz na zrzucie widocznych jest dokładnie 9 rekordów o identyfikatorach 46 ÷ 54
ok. 2 min
Polecenie z arkusza
Zapytanie 1: wybierające jedynie pola idObiekt, plik i nazwa z tabeli obiekty dla Islandii.
Każda klauzula w osobnej linii: co (trzy kolumny w kolejności z arkusza), skąd (obiekty), które wiersze (WHERE panstwo = 'Islandia'). Tekst w SQL w apostrofach - to zapis zgodny ze standardem, działający w każdej bazie (MySQL przyjmuje też cudzysłów, jak w kluczu).
Wynik: 9 wierszy o identyfikatorach 46-54 (R.1.3). Zapytanie do kwerendy.txt, zrzut kw1.png.
R.1.4 Wykonane zapytanie 2 wybierające jedynie pola plik, nazwa, nazwaCechy, wartoscCechy, opis z tabeli obiekty oraz odpowiadające im pole rodzaj z tabeli rodzaje dla obiektu o identyfikatorze 46. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT plik, nazwa, nazwaCechy, wartoscCechy, opis, rodzaj FROM obiekty JOIN rodzaje USING(idRodzaj) WHERE idObiekt = 46; (lub INNER JOIN lub porównanie kluczy po ON lub WHERE ) oraz na zrzucie widoczny jest dokładnie 1 rekord dla nazwy Strokkur
ok. 3 min
Polecenie z arkusza
Zapytanie 2: wybierające jedynie pola plik, nazwa, nazwaCechy, wartoscCechy, opis z tabeli obiekty oraz odpowiadające im pole rodzaj z tabeli rodzaje dla obiektu o identyfikatorze 46. Należy posłużyć się relacją.
Złączenie z aliasami tabel (obiekty AS o, rodzaje AS r): każda kolumna mówi, z której tabeli pochodzi, a warunek złączenia r.idRodzaj = o.idRodzaj nie jest dwuznaczny, choć kolumna idRodzaj jest w obu tabelach.
Wynik: jeden wiersz - gejzer Strokkur (R.1.4). Zrzut kw2.png. Strona obiekty.php wyśle to samo zapytanie z identyfikatorem z adresu w miejscu 46.
Typowe błędy
ON idRodzaj = idRodzaj bez nazw tabel to błąd „Column idRodzaj in on clause is ambiguous”.
kwerendy.txtTekstzmienione: 6 linii
-- Zapytanie 1SELECT idObiekt, plik, nazwaFROM obiektyWHERE panstwo = 'Islandia';-- Zapytanie 2SELECT o.plik, o.nazwa, o.nazwaCechy, o.wartoscCechy, o.opis, r.rodzajFROM obiekty AS oJOIN rodzaje AS r ON r.idRodzaj = o.idRodzajWHERE o.idObiekt = 46;
Podgląd strony po tym kroku
Krok 6: Zapytanie 3: wodospady Islandii
Kryteria:
R.1.5 Wykonane zapytanie 3 wybierające jedynie nazwy obiektów z Islandii, których identyfikator rodzaju to 10. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa FROM obiekty WHERE idRodzaj = 10 AND Panstwo = "Islandia"; oraz na zrzucie widoczne są dokładnie 4 rekordy: Skogafoss, Seljalandsfoss, Godafoss, Dettifoss
ok. 2 min
Polecenie z arkusza
Zapytanie 3: wybierające jedynie nazwy obiektów z Islandii, których identyfikator rodzaju to 10.
Dwa warunki naraz - AND. Identyfikator rodzaju jest w samej tabeli obiekty, więc złączenie nie jest potrzebne. Wynik: Skogafoss, Seljalandsfoss, Godafoss, Dettifoss (R.1.5), zrzut kw3.png.
Typowe błędy
OR zamiast AND zwróci wszystkie obiekty z Islandii i wszystkie wodospady z bazy.
kwerendy.txtTekstzmienione: 6 linii
-- Zapytanie 1SELECT idObiekt, plik, nazwaFROM obiektyWHERE panstwo = 'Islandia';-- Zapytanie 2SELECT o.plik, o.nazwa, o.nazwaCechy, o.wartoscCechy, o.opis, r.rodzajFROM obiekty AS oJOIN rodzaje AS r ON r.idRodzaj = o.idRodzajWHERE o.idObiekt = 46;-- Zapytanie 3SELECT nazwaFROM obiektyWHERE panstwo = 'Islandia' AND idRodzaj = 10;
Podgląd strony po tym kroku
Krok 7: Zapytanie 4: liczba obiektów każdego rodzaju
Kryteria:
R.1.6 Wykonane zapytanie 4 wybierające jedynie rodzaj z tabeli rodzaje oraz zliczające liczbę obiektów danego rodzaju. Kolumna z liczbą obiektów powinna mieć nadaną nazwę (alias) „Liczba obiektów”. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT rodzaj, COUNT(obiekty.idObiekt) AS "Liczba obiektów" FROM Rodzaje JOIN obiekty USING(idRodzaj) GROUP BY rodzaj; (Po COUNT dowolna nazwa pola tabeli obiekty, AS opcjonalnie, lub porównanie kluczy po ON lub WHERE ) oraz na zrzucie widocznych jest dokładnie 8 rekordów: gorące źródło 1, jaskinia 2, jezioro 1, park narodowy 1, siedlisko zwierząt 2, szczyt 4, wodospad 12, wulkan 2
ok. 3 min
Polecenie z arkusza
Zapytanie 4: wybierające jedynie rodzaj z tabeli rodzaje oraz zliczające liczbę obiektów (wodospadów, szczytów itd.) danego rodzaju. Kolumna z liczbą obiektów powinna mieć nadaną nazwę (alias) „Liczba obiektów”. Należy posłużyć się grupowaniem i relacją.
JOIN obiekty AS o ON o.idRodzaj = r.idRodzaj - relacja: do każdego rodzaju jego obiekty,
GROUP BY r.idRodzaj, r.rodzaj - grupa to jeden rodzaj wskazany kluczem; dwa różne rodzaje o tej samej nazwie nie zleją się w jeden wiersz,
COUNT(o.idObiekt) AS "Liczba obiektów" - alias ze spacją i polską literą musi być w cudzysłowie (tu cudzysłów oznacza nazwę, nie tekst),
ORDER BY r.rodzaj - kolejność alfabetyczna podana wprost; samo grupowanie jej nie gwarantuje.
Wynik: 8 wierszy - gorące źródło 1, jaskinia 2, jezioro 1, park narodowy 1, siedlisko zwierząt 2, szczyt 4, wodospad 12, wulkan 2 (R.1.6). Zrzut kw4.png.
Typowe błędy
Bez GROUP BYCOUNT policzy wszystkie 25 obiektów w jednym wierszu.
kwerendy.txtTekstzmienione: 7 linii
-- Zapytanie 1SELECT idObiekt, plik, nazwaFROM obiektyWHERE panstwo = 'Islandia';-- Zapytanie 2SELECT o.plik, o.nazwa, o.nazwaCechy, o.wartoscCechy, o.opis, r.rodzajFROM obiekty AS oJOIN rodzaje AS r ON r.idRodzaj = o.idRodzajWHERE o.idObiekt = 46;-- Zapytanie 3SELECT nazwaFROM obiektyWHERE panstwo = 'Islandia' AND idRodzaj = 10;-- Zapytanie 4SELECT r.rodzaj, COUNT(o.idObiekt) AS "Liczba obiektów"FROM rodzaje AS rJOIN obiekty AS o ON o.idRodzaj = r.idRodzajGROUP BY r.idRodzaj, r.rodzajORDER BY r.rodzaj;
Podgląd strony po tym kroku
Etap 2: Strona HTML
Kroki 8-21 · po tym etapie masz 16 z 37 kryteriów (pkt)
Krok 8: Zdjęcia w folderze zdającego
ok. 1 min
Polecenie z arkusza
Witryna składa się z podstron: islandia.php i obiekty.php.
Dziewięć zdjęć z rozpakowanego archiwum kopiujesz do folderu zdającego, obok stron. Skrypty będą je wskazywać samą nazwą pliku z kolumny plik, więc ścieżka względna wystarcza. Arkusz nie wymaga ich obróbki.
Podgląd strony po tym kroku
Krok 9: Plik islandia.php w HTML5
Kryteria:
R.2.1 W pliku islandia.php lub obiekty.php znajduje się deklaracja HTML5 <!DOCTYPE HTML> i deklaracja języka zawartości: polski np. <html lang="pl">
ok. 1 min
Polecenie z arkusza
Cechy wspólne obu stron: Zapisane w języku HTML5.
Plik islandia.php w folderze zdającego. <!DOCTYPE html> włącza tryb standardów HTML5, dalej szkielet: <html>, <head> i <body>. Rozszerzenie .php, bo na początku pliku stanie kod PHP.
R.2.2 W sekcji head znajduje się jawnie podany standard kodowania polskich znaków i tytuł strony "Islandia”
ok. 1 min
Polecenie z arkusza
Jawnie zastosowany właściwy standard kodowania polskich znaków.
<meta charset="UTF-8"> jako pierwszy element <head>. Ten sam standard ustawi połączenie z bazą (charset=utf8mb4 w PDO), więc polskie litery przejdą bez zmian całą drogę: baza → PHP → przeglądarka.
R.2.2 W sekcji head znajduje się jawnie podany standard kodowania polskich znaków i tytuł strony "Islandia”
ok. 1 min
Polecenie z arkusza
Tytuł strony „Islandia”.
<title>Islandia</title> - napis na karcie przeglądarki. Obok <meta name="viewport">: bez niego telefon pokazałby stronę pomniejszoną jak na monitorze. Arkusz tego nie wymaga, a w każdym współczesnym szablonie ta linia jest.
R.2.3 W sekcji body zdefiniowany jest podział na bloki za pomocą semantycznych znaczników HTML5: header, aside, main -> section, footer (dopuszcza się stosowanie bloków pomocniczych)
ok. 2 min
Polecenie z arkusza
Podział strony na bloki: nagłówkowy, boczny, główny, stopki. Blok główny zawiera blok sekcji. Podział zrealizowany za pomocą semantycznych znaczników języka HTML5 tak, aby po uruchomieniu w przeglądarce układ bloków był zgodny z ilustracją 4.
Znaczniki z tabeli 4 arkusza i klucza R.2.3: header, main z section w środku, aside, footer.
Kolejność w kodzie to kolejność czytania: nagłówek, treść główna, dodatki z boku, stopka - tak stronę przeczyta czytnik ekranu i tak zobaczy ją telefon bez stylów. O tym, że blok boczny stanie po prawej od samej góry, zdecyduje siatka CSS, a nie miejsce w kodzie. (Przy układzie na float blok boczny musiałby stać w kodzie przed blokiem głównym - tak jest w wersji podstawowej.)
Krok 15: Nagłówek pierwszego stopnia będący odnośnikiem
Kryteria: ,
R.2.4 W bloku nagłówkowym znajduje się <h1>: "Zwiedzaj Islandię", w bloku bocznym <h3>: "Do zwiedzania" w bloku głównym <h2>: "Galeria" lub "Opis miejsca" oraz <p> z numerem zdającego w stopce
R.3.3 Przynajmniej na jednej stronie kliknięcie odnośnika z bloku nagłówkowego prowadzi do strony islandia.php
ok. 1 min
Polecenie z arkusza
Zawartość bloku nagłówkowego: nagłówek pierwszego stopnia o treści „Zwiedzaj Islandię”. Treść jest odnośnikiem do strony islandia.php.
Odnośnik wewnątrz nagłówka (<h1><a>…</a></h1>) - nagłówek zostaje nagłówkiem, a jego treść da się kliknąć. Ze strony obiekty.php zaprowadzi z powrotem do galerii (R.2.4, R.3.3).
Krok 16: Blok główny: nagłówek „Galeria” i blok sekcji
Kryteria:
R.2.4 W bloku nagłówkowym znajduje się <h1>: "Zwiedzaj Islandię", w bloku bocznym <h3>: "Do zwiedzania" w bloku głównym <h2>: "Galeria" lub "Opis miejsca" oraz <p> z numerem zdającego w stopce
ok. 1 min
Polecenie z arkusza
Zawartość bloku głównego dla strony islandia.php: nagłówek drugiego stopnia o treści: „Galeria”. Blok sekcji z efektem wykonania skryptu 3.
<h2> przed <section> - nagłówek stoi nad ramką sekcji, jak na ilustracji 2. Sekcję wypełni galeria ze skryptu 3.
R.2.4 W bloku nagłówkowym znajduje się <h1>: "Zwiedzaj Islandię", w bloku bocznym <h3>: "Do zwiedzania" w bloku głównym <h2>: "Galeria" lub "Opis miejsca" oraz <p> z numerem zdającego w stopce
ok. 1 min
Polecenie z arkusza
Zawartość bloku bocznego: nagłówek trzeciego stopnia o treści „Do zwiedzania”.
R.2.6 Przynajmniej jeden element listy zawiera zagnieżdżoną jedną listę numerowaną (w skrypcie lub HTML, znaczniki <ol> i <li>, znacznik <ol> zawarto wewnątrz <li> listy z R.2.5)
ok. 2 min
Polecenie z arkusza
Element „Wodospady:” zawiera listę numerowaną (uporządkowaną) wypełnioną skryptem 1. Element „Siedliska zwierząt:” zawiera listę numerowaną (uporządkowaną) wypełnioną skryptem 2.
Lista zagnieżdżona to <ol>wewnątrz<li>, między tekstem a </li> (R.2.6). Elementy dopiszą skrypty 1 i 2.
Typowe błędy
<ol> między </li> a następnym <li> nie jest zagnieżdżona w elemencie - to niepoprawny HTML i brak R.2.6.
R.2.4 W bloku nagłówkowym znajduje się <h1>: "Zwiedzaj Islandię", w bloku bocznym <h3>: "Do zwiedzania" w bloku głównym <h2>: "Galeria" lub "Opis miejsca" oraz <p> z numerem zdającego w stopce
R.2.9 Stopka zawiera linię poziomą (<hr> lub za pomocą obramowania w CSS )
ok. 1 min
Polecenie z arkusza
Zawartość bloku stopki: linia pozioma. Paragraf o treści „Autor: ”, dalej wstawiony numer zdającego.
<hr> (element pusty, bez ukośnika w HTML5) i paragraf (R.2.4, R.2.9). Tu stoi nazwisko autora rozwiązania - na egzaminie wpisujesz swój numer zdającego (PESEL).
R.2.8 Na stronie obiekty.php w bloku głównym lub bloku sekcji znajdują się elementy <h2>, <h3> i przynajmniej jeden <p> (w skrypcie lub HTML )
ok. 2 min
Polecenie z arkusza
Zawartość bloku głównego dla strony obiekty.php: nagłówek drugiego stopnia o treści: „Opis miejsca”. Blok sekcji z efektem działania skryptu 4.
Cechy wspólne są już w islandia.php, więc Zapisz jakoobiekty.php i jedna zmiana: nagłówek bloku głównego „Opis miejsca”. Sekcję wypełni skrypt 4 (R.2.8 - nagłówki i paragrafy w sekcji).
Kroki 22-31 · po tym etapie masz 27 z 37 kryteriów (pkt)
Krok 22: Zmienne CSS z wartościami arkusza
ok. 2 min
Polecenie z arkusza
Styl CSS zdefiniowany jest w całości w zewnętrznym pliku o nazwie styl.css.
Plik styl.css zaczyna się od zmiennych w :root: kolor PaleTurquoise i zaokrąglenie 20px dotyczą dwóch bloków, więc wpisane raz występują w regułach jako var(--accent) i var(--radius). W inspektorze widać obliczone wartości - kryteria sprawdza się tak samo.
R.4.1 domyślne formatowanie selektorów (*): krój czcionki "Comic Sans MS" - jako pierwszy, sans-serif - jako ostatni
ok. 1 min
Polecenie z arkusza
Domyślnie, dla wszystkich selektorów: krój czcionki Comic Sans MS, w przypadku braku sans-serif.
Selektor *, nazwa ze spacjami w cudzysłowie, na końcu rodzina zastępcza sans-serif (R.4.1).
styl.cssCSSzmienione: 4 linie
:root {--accent: PaleTurquoise;--frame: DimGray;--radius: 20px;}* {font-family: "Comic Sans MS", sans-serif;}
Podgląd strony po tym kroku (islandia.php)
Krok 24: Układ bloków zgodny z ilustracją 4: siatka
Kryteria: ,
R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z ilustracją 4 w arkuszu egzaminacyjnym (prawidłowo zastosowane właściwości CSS decydujące o układzie bloków na stronie, np. float i clear albo display flex albo grid albo position; żaden blok nie zachodzi na inne bloki - sprawdzić w inspektorze)
R.4.3 szerokość: blok nagłówkowy, główny - 70%, boczny - 25% (width lub grid-template- columns lub flex ) wysokość: blok nagłówkowy - 100px, boczny - 700px, sekcja - 400px
ok. 4 min
Polecenie z arkusza
Podział zrealizowany za pomocą semantycznych znaczników języka HTML5 tak, aby po uruchomieniu w przeglądarce układ bloków był zgodny z ilustracją 4.
Ciało strony staje się siatką, a grid-template-areas rysuje ilustrację 4 tekstem: w pierwszym wierszu nagłówek i blok boczny, w drugim blok główny i dalej blok boczny (to samo słowo w dwóch wierszach = blok na dwa wiersze), w trzecim stopka na całą szerokość. Cztery krótkie reguły przypisują bloki do obszarów (R.3.1).
grid-template-columns: 70% 25% - szerokości z arkusza: nagłówek i blok główny 70%, blok boczny 25% (klucz R.4.3 przyjmuje je w siatce), column-gap: 5% - reszta szerokości to odstęp,
grid-template-rows: auto 1fr - pierwszy wiersz ma wysokość nagłówka, a drugi bierze resztę wysokości bloku bocznego; bez tego siatka rozdzieliłaby nadmiar po równo i pod nagłówkiem powstałaby dziura,
align-items: start - bloki nie rozciągają się do wysokości wiersza, więc nagłówek ma swoje 100 px.
Bloki nie nachodzą na siebie, a stopka zawsze jest pod najwyższym z nich - bez clear.
Typowe błędy
width: 70% na nagłówku w siatce liczy się od szerokości kolumny, nie strony - szerokości trzymaj w grid-template-columns.
R.4.2 kolor tła: blok nagłówkowy i boczny - PaleTurquoise
R.4.5 wyrównanie tekstu: blok nagłówkowy, główny - do środka
R.4.7 zaokrąglenie rogów (border-radius) : blok nagłówkowy, boczny - 20px
ok. 1 min
Polecenie z arkusza
Dla bloku nagłówkowego i głównego: szerokość 70%, wyrównanie tekstu do środka. Dla bloku nagłówkowego i bocznego: kolor tła PaleTurquoise, zaokrąglenie rogów 20 px.
Szerokość 70% jest już w siatce. Dochodzą wspólne reguły par bloków: wyśrodkowany tekst (R.4.5), tło i zaokrąglenie ze zmiennych (R.4.2, R.4.7).
R.4.3 szerokość: blok nagłówkowy, główny - 70%, boczny - 25% (width lub grid-template- columns lub flex ) wysokość: blok nagłówkowy - 100px, boczny - 700px, sekcja - 400px
R.4.8 wysokość linii (line-hight) : blok boczny - 2
ok. 1 min
Polecenie z arkusza
Dodatkowo dla bloku bocznego: szerokość 25%, wysokość 700 px, wysokość linii (interlinia) 2.
Szerokość 25% to druga kolumna siatki. Wysokość 700 px i line-height: 2 - liczba bez jednostki to dwukrotność rozmiaru czcionki, więc interlinia rośnie razem z tekstem (R.4.3, R.4.8).
R.4.3 szerokość: blok nagłówkowy, główny - 70%, boczny - 25% (width lub grid-template- columns lub flex ) wysokość: blok nagłówkowy - 100px, boczny - 700px, sekcja - 400px
R.4.4 marginesy wewnętrzne (padding ): blok nagłówkowy - 2%, stopka jedynie padding-top: 10px; marginesy zewnętrzne (margin ): sekcja - górny i dolny - 10px, pozostałe - 50px
R.4.6 cechy sekcji: obramowanie 1px dashed DimGray, paski przewijania (overflow) auto
ok. 2 min
Polecenie z arkusza
Dla bloku sekcji: obramowanie linią kreskową o szerokości 1 px i kolorze DimGray, marginesy zewnętrzne górny i dolny 10 px, pozostałe 50 px, wysokość 400 px, paski przewijania pojawiające się tylko w przypadku przepełnienia bloku.
margin: 10px 50px - pierwsza wartość góra i dół, druga lewa i prawa; overflow: auto - paski tylko przy przepełnieniu; dashed - linia kreskowa (R.4.3, R.4.4, R.4.6).
Krok 30: Klasa miniatury: wysokość, przezroczystość, tranzycja
Kryteria:
R.4.9 styl klasy .miniatury : wysokość - 200px, padding: 10px, przezroczystość (opacity) - 0.8, tranzycja trwająca 1s (transition lub transition-duration)
ok. 2 min
Polecenie z arkusza
Dla klasy miniatury przypisanej do obrazów ze strony islandia.php, generowanych skryptem 3: wysokość 200 px, marginesy wewnętrzne 10 px, przezroczystość 0.8, dodana tranzycja trwająca 1 sekundę.
Nazwa klasy z arkusza. transition: opacity 1s podaje, którą właściwość animować i jak długo (tabela 3 arkusza) - przeglądarka nie musi śledzić zmian wszystkich właściwości, a przypadkowa zmiana innej (np. rozmiaru) nie zacznie się nagle animować (R.4.9).
Kroki 32-50 · po tym etapie masz 37 z 37 kryteriów (pkt)
Krok 32: Połączenie z bazą przez PDO na obu stronach
Kryteria: ,
R.5.1 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy islandia oraz ostatnią operacją na bazie jest jej zamknięcie (wystarczy na jednej stronie )
R.5.2 Zastosowana przynajmniej jedna zmienna oraz nazewnictwo jest w języku polskim lub angielskim i odzwierciedla cel zastosowania
ok. 3 min
Polecenie z arkusza
Łączą się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie islandia.
Kod PHP stoi na samym początku obu stron, przed <!DOCTYPE html>: najpierw wszystkie operacje na bazie, potem szablon, który tylko wypisuje gotowe dane. Każda strona to osobne żądanie do serwera, więc każda łączy się sama (R.5.1, R.5.2).
new PDO(...) - połączenie i wybór bazy islandia: pierwszy argument to DSN, czyli adres bazy dla PDO w jednym napisie (mysql:host=localhost;dbname=islandia;charset=utf8mb4), dalej użytkownik root i puste hasło,
ERRMODE_EXCEPTION zamienia błędy SQL w wyjątki, FETCH_ASSOC daje wiersze z nazwami kolumn,
try … catch (PDOException $error) - przy awarii bazy użytkownik widzi ogólny komunikat i kod 500, a nie treść błędu z nazwą serwera.
Od tego kroku podgląd to prawdziwy wynik strony z serwera.
Typowe błędy
Klucz sprawdza połączenie i wybór bazy islandia w kodzie źródłowym strony (R.5.1), więc DSN z nazwą bazy stoi w islandia.php i obiekty.php, a nie w dołączanym pliku. Wypisanie $error->getMessage() na ekran zdradza szczegóły serwera - komunikat dla użytkownika ma być ogólny.
Krok 33: Funkcja e(): bezpieczne wypisywanie tekstu
ok. 2 min
Polecenie z arkusza
Należy stosować znaczące nazewnictwo zmiennych w języku polskim lub angielskim.
Każdy tekst z bazy trafi do HTML-u przez e(): htmlspecialchars z ENT_QUOTES zamienia <, >, & i oba cudzysłowy na encje. Nazwa obiektu z cudzysłowem nie rozbije atrybutu alt, a <script> w opisie pokaże się jako tekst (ochrona przed XSS).
Typ parametru ?string (ze znakiem zapytania) przyjmuje też null: w tej bazie kolumny nazwaCechy, wartoscCechy i opis mogą być puste (DEFAULT NULL), a funkcja z samym string zatrzymałaby stronę błędem typu. $text ?? '' zamienia null na pusty napis.
R.5.3 Skrypty zawierają przynajmniej jedną instrukcję wysyłającą zapytanie do bazy danych: skrypt 2 zmodyfikowane zapytanie 3 - idRodzaj = 14 skrypt 1 zapytanie 3 skrypt 3 - zapytanie 1 skrypt 4 - zmodyfikowane zapytanie 2 - id pobrane z tablicy $_GET
ok. 3 min
Polecenie z arkusza
Skrypt 1 na obu stronach: wysyła do bazy danych zapytanie 3.
Zapytanie 3 jako zapytanie przygotowane: w miejscu numeru rodzaju stoi parametr :typeId, a wartość 10 idzie osobno w execute([...]) (R.5.3). Ten sam szablon zapytania obsłuży za chwilę skrypt 2 z numerem 14 - baza przygotowuje go raz.
fetchAll(PDO::FETCH_COLUMN) zwraca zamiast wierszy od razu listę wartości jedynej kolumny: $waterfalls to ['Skogafoss', 'Seljalandsfoss', 'Godafoss', 'Dettifoss']. Napis z zapytaniem jest w cudzysłowie, bo w środku stoją apostrofy wokół 'Islandia'.
islandia.phpPHPzmienione: 6 linii
<?phpfunctione(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');}try {$db = new PDO('mysql:host=localhost;dbname=islandia;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);$namesQuery = $db->prepare("SELECT nazwa FROM obiekty WHERE panstwo = 'Islandia' AND idRodzaj = :typeId" );$namesQuery->execute(['typeId' => 10]);$waterfalls = $namesQuery->fetchAll(PDO::FETCH_COLUMN);} catch (PDOException $error) {http_response_code(500);exit('Baza danych jest chwilowo niedostępna. Spróbuj ponownie później.');}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Islandia</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1><ahref="islandia.php">Zwiedzaj Islandię</a></h1> </header> <main> <h2>Galeria</h2> <section> </section> </main> <aside> <h3>Do zwiedzania</h3> <ul> <li>Wodospady: <ol> </ol> </li> <li>Siedliska zwierząt: <ol> </ol> </li> </ul> </aside> <footer> <hr> <p>Autor: Bartosz Bryniarski</p> </footer></body></html>
R.5.4 Skrypt 1 lub skrypt 2 wyświetla wszystkie zwrócone zapytaniem nazwy jako elementy listy
ok. 2 min
Polecenie z arkusza
Każda zwrócona zapytaniem nazwa jest wyświetlana jako element listy.
W <ol> przy „Wodospady:” pętla w składni szablonu: foreach (...): i endforeach; zamiast nawiasów klamrowych - HTML elementu listy zostaje zwykłym HTML-em z wcięciem, a wartość wstawia <?= ... ?> (skrót od <?php echo ... ?>) przez e(). Podgląd: cztery wodospady (R.5.4).
R.5.3 Skrypty zawierają przynajmniej jedną instrukcję wysyłającą zapytanie do bazy danych: skrypt 2 zmodyfikowane zapytanie 3 - idRodzaj = 14 skrypt 1 zapytanie 3 skrypt 3 - zapytanie 1 skrypt 4 - zmodyfikowane zapytanie 2 - id pobrane z tablicy $_GET
R.5.4 Skrypt 1 lub skrypt 2 wyświetla wszystkie zwrócone zapytaniem nazwy jako elementy listy
ok. 2 min
Polecenie z arkusza
Skrypt 2 na obu stronach: wysyła do bazy danych zapytanie 3, zmodyfikowane tak, że w warunku sprawdzane jest pole idRodzaj równe 14. Każda zwrócona zapytaniem nazwa jest wyświetlana jako element listy.
„Zmodyfikowane zapytanie” to tu to samo zapytanie przygotowane z inną wartością parametru: execute(['typeId' => 14]) - dwie linie zamiast kopii całego skryptu. Wynik trafia do $habitats, a w drugiej <ol> stoi ta sama pętla co przy wodospadach (R.5.3, R.5.4). Podgląd: dwa siedliska, w tym „Fuglaskoðunarhús” z islandzką literą ð.
Krok 37: Skrypty 1 i 2 również na stronie obiekty.php
Kryteria:
R.5.4 Skrypt 1 lub skrypt 2 wyświetla wszystkie zwrócone zapytaniem nazwy jako elementy listy
ok. 2 min
Polecenie z arkusza
Skrypt 1 na obu stronach. Skrypt 2 na obu stronach.
Ten sam kod w obiekty.php: zapytanie przygotowane wykonane dwa razy na górze pliku i dwie pętle w bloku bocznym (R.5.4). Blok boczny wygląda identycznie na obu stronach.
To jedyne miejsce, w którym ta wersja powtarza kod - w zamian każda strona jest kompletna w jednym pliku. Jak uniknąć tej kopii, pokazuje trzecia zakładka („Wspólne pliki”).
R.5.3 Skrypty zawierają przynajmniej jedną instrukcję wysyłającą zapytanie do bazy danych: skrypt 2 zmodyfikowane zapytanie 3 - idRodzaj = 14 skrypt 1 zapytanie 3 skrypt 3 - zapytanie 1 skrypt 4 - zmodyfikowane zapytanie 2 - id pobrane z tablicy $_GET
ok. 1 min
Polecenie z arkusza
Skrypt 3 na stronie islandia.php: wysyła do bazy danych zapytanie 1.
Zapytanie 1 bez danych od użytkownika - wystarcza query, a fetchAll() odbiera dziewięć wierszy do tablicy $gallery (R.5.3).
islandia.phpPHPzmienione: 4 linie
<?phpfunctione(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');}try {$db = new PDO('mysql:host=localhost;dbname=islandia;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);$namesQuery = $db->prepare("SELECT nazwa FROM obiekty WHERE panstwo = 'Islandia' AND idRodzaj = :typeId" );$namesQuery->execute(['typeId' => 10]);$waterfalls = $namesQuery->fetchAll(PDO::FETCH_COLUMN);$namesQuery->execute(['typeId' => 14]);$habitats = $namesQuery->fetchAll(PDO::FETCH_COLUMN);$gallery = $db->query("SELECT idObiekt, plik, nazwa FROM obiekty WHERE panstwo = 'Islandia'" )->fetchAll();} catch (PDOException $error) {http_response_code(500);exit('Baza danych jest chwilowo niedostępna. Spróbuj ponownie później.');}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Islandia</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1><ahref="islandia.php">Zwiedzaj Islandię</a></h1> </header> <main> <h2>Galeria</h2> <section> </section> </main> <aside> <h3>Do zwiedzania</h3> <ul> <li>Wodospady: <ol><?phpforeach ($waterfallsas$name): ?> <li><?= e($name) ?></li><?phpendforeach; ?> </ol> </li> <li>Siedliska zwierząt: <ol><?phpforeach ($habitatsas$name): ?> <li><?= e($name) ?></li><?phpendforeach; ?> </ol> </li> </ul> </aside> <footer> <hr> <p>Autor: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (islandia.php)
Krok 39: Skrypt 3: obraz z każdego wiersza
Kryteria: ,
R.5.6 Skrypt 3 dla każdego zwróconego zapytaniem wiersza wyświetla obraz, którego źródłem jest zwrócone pole plik
R.2.7 W bloku głównym lub bloku sekcji znajduje się przynajmniej jeden obraz wraz z tekstem alternatywnym (w skrypcie lub HTML )
ok. 2 min
Polecenie z arkusza
Odnośnik jest obrazem, dla którego źródło to zwrócone pole plik, tekst alternatywny to zwrócone pole nazwa.
W sekcji pętla po galerii: <img> ze źródłem z pola plik i tekstem alternatywnym z pola nazwa (R.5.6, R.2.7). Atrybuty są w zwykłym HTML-u - nie trzeba zabezpieczać cudzysłowów ukośnikami jak w napisach dla echo. Zdjęcia mają na razie naturalny rozmiar.
islandia.phpPHPzmienione: 3 linie
<?phpfunctione(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');}try {$db = new PDO('mysql:host=localhost;dbname=islandia;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);$namesQuery = $db->prepare("SELECT nazwa FROM obiekty WHERE panstwo = 'Islandia' AND idRodzaj = :typeId" );$namesQuery->execute(['typeId' => 10]);$waterfalls = $namesQuery->fetchAll(PDO::FETCH_COLUMN);$namesQuery->execute(['typeId' => 14]);$habitats = $namesQuery->fetchAll(PDO::FETCH_COLUMN);$gallery = $db->query("SELECT idObiekt, plik, nazwa FROM obiekty WHERE panstwo = 'Islandia'" )->fetchAll();} catch (PDOException $error) {http_response_code(500);exit('Baza danych jest chwilowo niedostępna. Spróbuj ponownie później.');}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Islandia</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1><ahref="islandia.php">Zwiedzaj Islandię</a></h1> </header> <main> <h2>Galeria</h2> <section><?phpforeach ($galleryas$item): ?> <imgsrc="<?= e($item['plik']) ?>"alt="<?= e($item['nazwa']) ?>"><?phpendforeach; ?> </section> </main> <aside> <h3>Do zwiedzania</h3> <ul> <li>Wodospady: <ol><?phpforeach ($waterfallsas$name): ?> <li><?= e($name) ?></li><?phpendforeach; ?> </ol> </li> <li>Siedliska zwierząt: <ol><?phpforeach ($habitatsas$name): ?> <li><?= e($name) ?></li><?phpendforeach; ?> </ol> </li> </ul> </aside> <footer> <hr> <p>Autor: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (islandia.php)
Krok 40: Skrypt 3: dymek z nazwą i klasa miniatury
Kryteria:
R.3.5 Przynajmniej jeden obraz ze strony islandia.php ma przypisaną klasę miniatury oraz atrybut title
ok. 1 min
Polecenie z arkusza
Dodatkowo pole nazwa jest tekstem pojawiającym się w postaci dymka, gdy kursor znajdzie się na obrazie (ilustracja 2, czwarty obraz). Obraz ma przypisaną klasę miniatury.
Dymek to atrybut title, klasa miniatury podpina regułę z CSS: 200 px wysokości, przezroczystość i płynne przejście po najechaniu (R.3.5).
islandia.phpPHPzmienione: 1 linia
<?phpfunctione(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');}try {$db = new PDO('mysql:host=localhost;dbname=islandia;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);$namesQuery = $db->prepare("SELECT nazwa FROM obiekty WHERE panstwo = 'Islandia' AND idRodzaj = :typeId" );$namesQuery->execute(['typeId' => 10]);$waterfalls = $namesQuery->fetchAll(PDO::FETCH_COLUMN);$namesQuery->execute(['typeId' => 14]);$habitats = $namesQuery->fetchAll(PDO::FETCH_COLUMN);$gallery = $db->query("SELECT idObiekt, plik, nazwa FROM obiekty WHERE panstwo = 'Islandia'" )->fetchAll();} catch (PDOException $error) {http_response_code(500);exit('Baza danych jest chwilowo niedostępna. Spróbuj ponownie później.');}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Islandia</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1><ahref="islandia.php">Zwiedzaj Islandię</a></h1> </header> <main> <h2>Galeria</h2> <section><?phpforeach ($galleryas$item): ?> <imgsrc="<?= e($item['plik']) ?>"alt="<?= e($item['nazwa']) ?>"title="<?= e($item['nazwa']) ?>"class="miniatury"><?phpendforeach; ?> </section> </main> <aside> <h3>Do zwiedzania</h3> <ul> <li>Wodospady: <ol><?phpforeach ($waterfallsas$name): ?> <li><?= e($name) ?></li><?phpendforeach; ?> </ol> </li> <li>Siedliska zwierząt: <ol><?phpforeach ($habitatsas$name): ?> <li><?= e($name) ?></li><?phpendforeach; ?> </ol> </li> </ul> </aside> <footer> <hr> <p>Autor: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (islandia.php)
Krok 41: Skrypt 3: odnośnik z parametrem GET
Kryteria: ,
R.3.4 Na stronie islandia.php umieszczono przynajmniej jeden odnośnik, którego kliknięcie powoduje otwarcie strony obiekty.php oraz zostaje wysłane żądanie metodą GET z jednym parametrem
R.5.5 Skrypt 3 dla każdego zwróconego zapytaniem wiersza wstawia idObiekt jako wartość parametru przesłanego metodą GET
ok. 2 min
Polecenie z arkusza
Dla każdego zwróconego zapytaniem wiersza wypisuje odnośnik prowadzący do strony obiekty.php oraz przekazujący do tej strony metodą GET parametr o wartości równej polu idObiekt.
Obraz otacza odnośnik obiekty.php?id=… - parametr GET to część adresu po znaku zapytania (tabela 2 arkusza), a jego wartość to idObiekt z bazy (R.3.4, R.5.5). Nazwę parametru (id) wybiera się samemu; strona obiekty.php musi odczytać tę samą. Odnośnik i obraz stoją w jednej linii kodu: spacje i znaki nowej linii wewnątrz <a> byłyby częścią odnośnika i przeglądarka podkreśliłaby je jako kreskę obok zdjęcia. W podglądzie kliknięcia są zablokowane.
islandia.phpPHPzmienione: 1 linia
<?phpfunctione(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');}try {$db = new PDO('mysql:host=localhost;dbname=islandia;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);$namesQuery = $db->prepare("SELECT nazwa FROM obiekty WHERE panstwo = 'Islandia' AND idRodzaj = :typeId" );$namesQuery->execute(['typeId' => 10]);$waterfalls = $namesQuery->fetchAll(PDO::FETCH_COLUMN);$namesQuery->execute(['typeId' => 14]);$habitats = $namesQuery->fetchAll(PDO::FETCH_COLUMN);$gallery = $db->query("SELECT idObiekt, plik, nazwa FROM obiekty WHERE panstwo = 'Islandia'" )->fetchAll();} catch (PDOException $error) {http_response_code(500);exit('Baza danych jest chwilowo niedostępna. Spróbuj ponownie później.');}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Islandia</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1><ahref="islandia.php">Zwiedzaj Islandię</a></h1> </header> <main> <h2>Galeria</h2> <section><?phpforeach ($galleryas$item): ?> <ahref="obiekty.php?id=<?= e($item['idObiekt']) ?>"><imgsrc="<?= e($item['plik']) ?>"alt="<?= e($item['nazwa']) ?>"title="<?= e($item['nazwa']) ?>"class="miniatury"></a><?phpendforeach; ?> </section> </main> <aside> <h3>Do zwiedzania</h3> <ul> <li>Wodospady: <ol><?phpforeach ($waterfallsas$name): ?> <li><?= e($name) ?></li><?phpendforeach; ?> </ol> </li> <li>Siedliska zwierząt: <ol><?phpforeach ($habitatsas$name): ?> <li><?= e($name) ?></li><?phpendforeach; ?> </ol> </li> </ul> </aside> <footer> <hr> <p>Autor: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (islandia.php)
Krok 42: Zamknięcie połączenia na stronie islandia.php
Kryteria:
R.5.1 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy islandia oraz ostatnią operacją na bazie jest jej zamknięcie (wystarczy na jednej stronie )
ok. 1 min
Polecenie z arkusza
Na końcu należy zamknąć połączenie z bazą.
W PDO połączenie zamyka się, gdy znika ostatnie odwołanie do niego. Zapytanie przygotowane $namesQuery też je trzyma - dlatego najpierw $namesQuery = null, potem $db = null (R.5.1). To koniec bloku try: wszystkie dane są już w tablicach, a szablon bazy nie potrzebuje.
Typowe błędy
Samo $db = null przy istniejącym $namesQuery zostawia połączenie otwarte do końca skryptu.
islandia.phpPHPzmienione: 3 linie
<?phpfunctione(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');}try {$db = new PDO('mysql:host=localhost;dbname=islandia;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);$namesQuery = $db->prepare("SELECT nazwa FROM obiekty WHERE panstwo = 'Islandia' AND idRodzaj = :typeId" );$namesQuery->execute(['typeId' => 10]);$waterfalls = $namesQuery->fetchAll(PDO::FETCH_COLUMN);$namesQuery->execute(['typeId' => 14]);$habitats = $namesQuery->fetchAll(PDO::FETCH_COLUMN);$gallery = $db->query("SELECT idObiekt, plik, nazwa FROM obiekty WHERE panstwo = 'Islandia'" )->fetchAll();$namesQuery = null;$db = null;} catch (PDOException $error) {http_response_code(500);exit('Baza danych jest chwilowo niedostępna. Spróbuj ponownie później.');}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Islandia</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1><ahref="islandia.php">Zwiedzaj Islandię</a></h1> </header> <main> <h2>Galeria</h2> <section><?phpforeach ($galleryas$item): ?> <ahref="obiekty.php?id=<?= e($item['idObiekt']) ?>"><imgsrc="<?= e($item['plik']) ?>"alt="<?= e($item['nazwa']) ?>"title="<?= e($item['nazwa']) ?>"class="miniatury"></a><?phpendforeach; ?> </section> </main> <aside> <h3>Do zwiedzania</h3> <ul> <li>Wodospady: <ol><?phpforeach ($waterfallsas$name): ?> <li><?= e($name) ?></li><?phpendforeach; ?> </ol> </li> <li>Siedliska zwierząt: <ol><?phpforeach ($habitatsas$name): ?> <li><?= e($name) ?></li><?phpendforeach; ?> </ol> </li> </ul> </aside> <footer> <hr> <p>Autor: Bartosz Bryniarski</p> </footer></body></html>
Krok 43: Skrypt 4: identyfikator z adresu, sprawdzony
ok. 3 min
Polecenie z arkusza
Skrypt 4 na stronie obiekty.php: wysyła do bazy danych zapytanie 2, zmodyfikowane w ten sposób, że w warunku jest sprawdzane id obiektu przesłane jako parametr metody GET ze strony islandia.php.
Parametr z adresu jest w tablicy $_GET (tabela 2 arkusza), ale nic, co przychodzi w adresie, nie jest pewne - każdy może wpisać obiekty.php?id=abc albo nie podać parametru wcale. Dlatego wartość przechodzi najpierw przez filter_var z filtrem FILTER_VALIDATE_INT i min_range 1:
?id=53 → liczba 53,
?id=abc, ?id=0 OR 1=1, ?id=-5 → false,
brak parametru → $_GET['id'] ?? null daje null, a filtr zwraca false, bez ostrzeżenia „Undefined array key”.
Dalej strona pracuje już na liczbie$objectId albo wie, że jej nie ma.
Typowe błędy
$_GET['id'] bez ?? null przy wejściu na obiekty.php bez parametru daje ostrzeżenie „Undefined array key "id"”.
Krok 44: Skrypt 4: zapytanie 2 z parametrem z adresu
Kryteria:
R.5.3 Skrypty zawierają przynajmniej jedną instrukcję wysyłającą zapytanie do bazy danych: skrypt 2 zmodyfikowane zapytanie 3 - idRodzaj = 14 skrypt 1 zapytanie 3 skrypt 3 - zapytanie 1 skrypt 4 - zmodyfikowane zapytanie 2 - id pobrane z tablicy $_GET
ok. 3 min
Polecenie z arkusza
Wysyła do bazy danych zapytanie 2, zmodyfikowane w ten sposób, że w warunku jest sprawdzane id obiektu przesłane jako parametr metody GET ze strony islandia.php.
Zapytanie 2 z kwerendy.txt, w którym liczbę 46 zastępuje parametr:objectId, a identyfikator z adresu idzie osobno w execute (R.5.3). Nawet gdyby filtr z poprzedniego kroku coś przepuścił, wartość nie stanie się częścią polecenia SQL - SQL injection jest niemożliwe.
Zapytanie wykonuje się tylko dla poprawnego identyfikatora. fetch() zwraca jeden wiersz albo false, gdy miejsca o takim numerze nie ma - $place jest false także wtedy, gdy zapytania w ogóle nie było.
Typowe błędy
Wklejenie $_GET['id'] do napisu z zapytaniem ("… WHERE idObiekt = $id") działa, ale adres ?id=0 OR 1=1 zmienia wtedy sens warunku.
obiekty.phpPHPzmienione: 12 linii
<?phpfunctione(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');}try {$db = new PDO('mysql:host=localhost;dbname=islandia;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);$namesQuery = $db->prepare("SELECT nazwa FROM obiekty WHERE panstwo = 'Islandia' AND idRodzaj = :typeId" );$namesQuery->execute(['typeId' => 10]);$waterfalls = $namesQuery->fetchAll(PDO::FETCH_COLUMN);$namesQuery->execute(['typeId' => 14]);$habitats = $namesQuery->fetchAll(PDO::FETCH_COLUMN);$objectId = filter_var($_GET['id'] ?? null, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);$place = false;if ($objectId !== false) {$placeQuery = $db->prepare('SELECT o.plik, o.nazwa, o.nazwaCechy, o.wartoscCechy, o.opis, r.rodzaj FROM obiekty AS o JOIN rodzaje AS r ON r.idRodzaj = o.idRodzaj WHERE o.idObiekt = :objectId' );$placeQuery->execute(['objectId' => $objectId]);$place = $placeQuery->fetch();$placeQuery = null; }} catch (PDOException $error) {http_response_code(500);exit('Baza danych jest chwilowo niedostępna. Spróbuj ponownie później.');}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Islandia</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1><ahref="islandia.php">Zwiedzaj Islandię</a></h1> </header> <main> <h2>Opis miejsca</h2> <section> </section> </main> <aside> <h3>Do zwiedzania</h3> <ul> <li>Wodospady: <ol><?phpforeach ($waterfallsas$name): ?> <li><?= e($name) ?></li><?phpendforeach; ?> </ol> </li> <li>Siedliska zwierząt: <ol><?phpforeach ($habitatsas$name): ?> <li><?= e($name) ?></li><?phpendforeach; ?> </ol> </li> </ul> </aside> <footer> <hr> <p>Autor: Bartosz Bryniarski</p> </footer></body></html>
R.5.7 Skrypt 4 wyświetla obraz oraz dane których id zostało przesłane metodą GET
ok. 1 min
Polecenie z arkusza
Na podstawie zwróconych zapytaniem danych: wyświetla obraz, którego źródłem jest pole plik, a tekstem alternatywnym pole nazwa.
Treść sekcji tylko wtedy, gdy miejsce znaleziono: <?php if ($place): ?> … <?php endif; ?>. Obraz z pól plik i nazwa (R.5.7). W podglądzie Godafoss - obiekty.php?id=53 z ilustracji 3.
obiekty.phpPHPzmienione: 3 linie
<?phpfunctione(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');}try {$db = new PDO('mysql:host=localhost;dbname=islandia;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);$namesQuery = $db->prepare("SELECT nazwa FROM obiekty WHERE panstwo = 'Islandia' AND idRodzaj = :typeId" );$namesQuery->execute(['typeId' => 10]);$waterfalls = $namesQuery->fetchAll(PDO::FETCH_COLUMN);$namesQuery->execute(['typeId' => 14]);$habitats = $namesQuery->fetchAll(PDO::FETCH_COLUMN);$objectId = filter_var($_GET['id'] ?? null, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);$place = false;if ($objectId !== false) {$placeQuery = $db->prepare('SELECT o.plik, o.nazwa, o.nazwaCechy, o.wartoscCechy, o.opis, r.rodzaj FROM obiekty AS o JOIN rodzaje AS r ON r.idRodzaj = o.idRodzaj WHERE o.idObiekt = :objectId' );$placeQuery->execute(['objectId' => $objectId]);$place = $placeQuery->fetch();$placeQuery = null; }} catch (PDOException $error) {http_response_code(500);exit('Baza danych jest chwilowo niedostępna. Spróbuj ponownie później.');}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Islandia</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1><ahref="islandia.php">Zwiedzaj Islandię</a></h1> </header> <main> <h2>Opis miejsca</h2> <section><?phpif ($place): ?> <imgsrc="<?= e($place['plik']) ?>"alt="<?= e($place['nazwa']) ?>"><?phpendif; ?> </section> </main> <aside> <h3>Do zwiedzania</h3> <ul> <li>Wodospady: <ol><?phpforeach ($waterfallsas$name): ?> <li><?= e($name) ?></li><?phpendforeach; ?> </ol> </li> <li>Siedliska zwierząt: <ol><?phpforeach ($habitatsas$name): ?> <li><?= e($name) ?></li><?phpendforeach; ?> </ol> </li> </ul> </aside> <footer> <hr> <p>Autor: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (obiekty.php)
Krok 46: Skrypt 4: nazwa w nagłówku drugiego stopnia
Kryteria:
R.2.8 Na stronie obiekty.php w bloku głównym lub bloku sekcji znajdują się elementy <h2>, <h3> i przynajmniej jeden <p> (w skrypcie lub HTML )
ok. 1 min
Polecenie z arkusza
W nagłówku drugiego stopnia wyświetla wartość pola nazwa.
<h2> z nazwą miejsca (R.2.8).
obiekty.phpPHPzmienione: 1 linia
<?phpfunctione(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');}try {$db = new PDO('mysql:host=localhost;dbname=islandia;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);$namesQuery = $db->prepare("SELECT nazwa FROM obiekty WHERE panstwo = 'Islandia' AND idRodzaj = :typeId" );$namesQuery->execute(['typeId' => 10]);$waterfalls = $namesQuery->fetchAll(PDO::FETCH_COLUMN);$namesQuery->execute(['typeId' => 14]);$habitats = $namesQuery->fetchAll(PDO::FETCH_COLUMN);$objectId = filter_var($_GET['id'] ?? null, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);$place = false;if ($objectId !== false) {$placeQuery = $db->prepare('SELECT o.plik, o.nazwa, o.nazwaCechy, o.wartoscCechy, o.opis, r.rodzaj FROM obiekty AS o JOIN rodzaje AS r ON r.idRodzaj = o.idRodzaj WHERE o.idObiekt = :objectId' );$placeQuery->execute(['objectId' => $objectId]);$place = $placeQuery->fetch();$placeQuery = null; }} catch (PDOException $error) {http_response_code(500);exit('Baza danych jest chwilowo niedostępna. Spróbuj ponownie później.');}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Islandia</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1><ahref="islandia.php">Zwiedzaj Islandię</a></h1> </header> <main> <h2>Opis miejsca</h2> <section><?phpif ($place): ?> <imgsrc="<?= e($place['plik']) ?>"alt="<?= e($place['nazwa']) ?>"> <h2><?= e($place['nazwa']) ?></h2><?phpendif; ?> </section> </main> <aside> <h3>Do zwiedzania</h3> <ul> <li>Wodospady: <ol><?phpforeach ($waterfallsas$name): ?> <li><?= e($name) ?></li><?phpendforeach; ?> </ol> </li> <li>Siedliska zwierząt: <ol><?phpforeach ($habitatsas$name): ?> <li><?= e($name) ?></li><?phpendforeach; ?> </ol> </li> </ul> </aside> <footer> <hr> <p>Autor: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (obiekty.php)
Krok 47: Skrypt 4: rodzaj w nagłówku trzeciego stopnia
Kryteria:
R.2.8 Na stronie obiekty.php w bloku głównym lub bloku sekcji znajdują się elementy <h2>, <h3> i przynajmniej jeden <p> (w skrypcie lub HTML )
ok. 1 min
Polecenie z arkusza
W nagłówku trzeciego stopnia wyświetla wartość pola rodzaj.
<h3> z polem rodzaj z tabeli rodzaje, dołączonej relacją.
obiekty.phpPHPzmienione: 1 linia
<?phpfunctione(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');}try {$db = new PDO('mysql:host=localhost;dbname=islandia;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);$namesQuery = $db->prepare("SELECT nazwa FROM obiekty WHERE panstwo = 'Islandia' AND idRodzaj = :typeId" );$namesQuery->execute(['typeId' => 10]);$waterfalls = $namesQuery->fetchAll(PDO::FETCH_COLUMN);$namesQuery->execute(['typeId' => 14]);$habitats = $namesQuery->fetchAll(PDO::FETCH_COLUMN);$objectId = filter_var($_GET['id'] ?? null, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);$place = false;if ($objectId !== false) {$placeQuery = $db->prepare('SELECT o.plik, o.nazwa, o.nazwaCechy, o.wartoscCechy, o.opis, r.rodzaj FROM obiekty AS o JOIN rodzaje AS r ON r.idRodzaj = o.idRodzaj WHERE o.idObiekt = :objectId' );$placeQuery->execute(['objectId' => $objectId]);$place = $placeQuery->fetch();$placeQuery = null; }} catch (PDOException $error) {http_response_code(500);exit('Baza danych jest chwilowo niedostępna. Spróbuj ponownie później.');}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Islandia</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1><ahref="islandia.php">Zwiedzaj Islandię</a></h1> </header> <main> <h2>Opis miejsca</h2> <section><?phpif ($place): ?> <imgsrc="<?= e($place['plik']) ?>"alt="<?= e($place['nazwa']) ?>"> <h2><?= e($place['nazwa']) ?></h2> <h3><?= e($place['rodzaj']) ?></h3><?phpendif; ?> </section> </main> <aside> <h3>Do zwiedzania</h3> <ul> <li>Wodospady: <ol><?phpforeach ($waterfallsas$name): ?> <li><?= e($name) ?></li><?phpendforeach; ?> </ol> </li> <li>Siedliska zwierząt: <ol><?phpforeach ($habitatsas$name): ?> <li><?= e($name) ?></li><?phpendforeach; ?> </ol> </li> </ul> </aside> <footer> <hr> <p>Autor: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (obiekty.php)
Krok 48: Skrypt 4: cecha i jej wartość w paragrafie
Kryteria:
R.2.8 Na stronie obiekty.php w bloku głównym lub bloku sekcji znajdują się elementy <h2>, <h3> i przynajmniej jeden <p> (w skrypcie lub HTML )
ok. 1 min
Polecenie z arkusza
W paragrafie wyświetla tekst „<nazwaCechy>: <wartoscCechy>”, gdzie w <> umieszczono wartości zwrócone zapytaniem.
Stały dwukropek w HTML-u, dwie wartości z bazy. Dla Godafossu: „wysokość: 30”. wartoscCechy to liczba - e() zamieni ją na tekst sama.
Krok 49: Skrypt 4: opis i strona dla nieznanego miejsca
Kryteria:
R.5.7 Skrypt 4 wyświetla obraz oraz dane których id zostało przesłane metodą GET
ok. 2 min
Polecenie z arkusza
W paragrafie wyświetla pole opis.
Ostatni paragraf to opis miejsca; razem ze zdjęciem nie mieści się w 400 px sekcji, więc pojawia się pasek przewijania (ilustracja 3) - R.5.7.
Gałąź <?php else: ?> obsługuje adres bez poprawnego identyfikatora: zamiast pustych znaczników strona mówi, co się stało, a http_response_code(404) zgłasza przeglądarce i wyszukiwarkom „nie ma takiej strony” - tak jak dla nieistniejącego adresu. Kod odpowiedzi ustawia się w części z logiką, przed wypisaniem HTML-u.
obiekty.phpPHPzmienione: 6 linii
<?phpfunctione(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');}try {$db = new PDO('mysql:host=localhost;dbname=islandia;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);$namesQuery = $db->prepare("SELECT nazwa FROM obiekty WHERE panstwo = 'Islandia' AND idRodzaj = :typeId" );$namesQuery->execute(['typeId' => 10]);$waterfalls = $namesQuery->fetchAll(PDO::FETCH_COLUMN);$namesQuery->execute(['typeId' => 14]);$habitats = $namesQuery->fetchAll(PDO::FETCH_COLUMN);$objectId = filter_var($_GET['id'] ?? null, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);$place = false;if ($objectId !== false) {$placeQuery = $db->prepare('SELECT o.plik, o.nazwa, o.nazwaCechy, o.wartoscCechy, o.opis, r.rodzaj FROM obiekty AS o JOIN rodzaje AS r ON r.idRodzaj = o.idRodzaj WHERE o.idObiekt = :objectId' );$placeQuery->execute(['objectId' => $objectId]);$place = $placeQuery->fetch();$placeQuery = null; }if ($place === false) {http_response_code(404); }} catch (PDOException $error) {http_response_code(500);exit('Baza danych jest chwilowo niedostępna. Spróbuj ponownie później.');}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Islandia</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1><ahref="islandia.php">Zwiedzaj Islandię</a></h1> </header> <main> <h2>Opis miejsca</h2> <section><?phpif ($place): ?> <imgsrc="<?= e($place['plik']) ?>"alt="<?= e($place['nazwa']) ?>"> <h2><?= e($place['nazwa']) ?></h2> <h3><?= e($place['rodzaj']) ?></h3> <p><?= e($place['nazwaCechy']) ?>: <?= e($place['wartoscCechy']) ?></p> <p><?= e($place['opis']) ?></p><?phpelse: ?> <p>Nie znaleziono takiego miejsca - wybierz zdjęcie w galerii.</p><?phpendif; ?> </section> </main> <aside> <h3>Do zwiedzania</h3> <ul> <li>Wodospady: <ol><?phpforeach ($waterfallsas$name): ?> <li><?= e($name) ?></li><?phpendforeach; ?> </ol> </li> <li>Siedliska zwierząt: <ol><?phpforeach ($habitatsas$name): ?> <li><?= e($name) ?></li><?phpendforeach; ?> </ol> </li> </ul> </aside> <footer> <hr> <p>Autor: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (obiekty.php)
Krok 50: Zamknięcie połączenia na stronie obiekty.php
Kryteria:
R.5.1 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy islandia oraz ostatnią operacją na bazie jest jej zamknięcie (wystarczy na jednej stronie )
ok. 1 min
Polecenie z arkusza
Na końcu należy zamknąć połączenie z bazą.
Tak samo jak w galerii: najpierw zapytania przygotowane ($placeQuery jest już zwolnione w bloku if), potem $db = null na końcu bloku try (R.5.1).
obiekty.phpPHPzmienione: 3 linie
<?phpfunctione(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');}try {$db = new PDO('mysql:host=localhost;dbname=islandia;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);$namesQuery = $db->prepare("SELECT nazwa FROM obiekty WHERE panstwo = 'Islandia' AND idRodzaj = :typeId" );$namesQuery->execute(['typeId' => 10]);$waterfalls = $namesQuery->fetchAll(PDO::FETCH_COLUMN);$namesQuery->execute(['typeId' => 14]);$habitats = $namesQuery->fetchAll(PDO::FETCH_COLUMN);$objectId = filter_var($_GET['id'] ?? null, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);$place = false;if ($objectId !== false) {$placeQuery = $db->prepare('SELECT o.plik, o.nazwa, o.nazwaCechy, o.wartoscCechy, o.opis, r.rodzaj FROM obiekty AS o JOIN rodzaje AS r ON r.idRodzaj = o.idRodzaj WHERE o.idObiekt = :objectId' );$placeQuery->execute(['objectId' => $objectId]);$place = $placeQuery->fetch();$placeQuery = null; }if ($place === false) {http_response_code(404); }$namesQuery = null;$db = null;} catch (PDOException $error) {http_response_code(500);exit('Baza danych jest chwilowo niedostępna. Spróbuj ponownie później.');}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Islandia</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1><ahref="islandia.php">Zwiedzaj Islandię</a></h1> </header> <main> <h2>Opis miejsca</h2> <section><?phpif ($place): ?> <imgsrc="<?= e($place['plik']) ?>"alt="<?= e($place['nazwa']) ?>"> <h2><?= e($place['nazwa']) ?></h2> <h3><?= e($place['rodzaj']) ?></h3> <p><?= e($place['nazwaCechy']) ?>: <?= e($place['wartoscCechy']) ?></p> <p><?= e($place['opis']) ?></p><?phpelse: ?> <p>Nie znaleziono takiego miejsca - wybierz zdjęcie w galerii.</p><?phpendif; ?> </section> </main> <aside> <h3>Do zwiedzania</h3> <ul> <li>Wodospady: <ol><?phpforeach ($waterfallsas$name): ?> <li><?= e($name) ?></li><?phpendforeach; ?> </ol> </li> <li>Siedliska zwierząt: <ol><?phpforeach ($habitatsas$name): ?> <li><?= e($name) ?></li><?phpendforeach; ?> </ol> </li> </ul> </aside> <footer> <hr> <p>Autor: Bartosz Bryniarski</p> </footer></body></html>
Krok 51 · po tym etapie masz 37 z 37 kryteriów (pkt)
Krok 51: Co jest inaczej i którą wersję wybrać na egzaminie
ok. 2 min
Polecenie z arkusza
Czas przeznaczony na wykonanie zadania wynosi 150 minut.
Wszystkie trzy wersje dają komplet 37 punktów. Różnice:
wersja podstawowa
ta wersja
wspólne pliki
układ
float + clear, blok boczny w kodzie przed głównym
siatka grid-template-areas
siatka (ten sam CSS)
baza
funkcje mysqli_* z tabeli 1
PDO, połączenie w pliku strony
mysqli obiektowe, config.php
listy w bloku bocznym
dwie kopie skryptu
jedno zapytanie przygotowane, dwa execute
funkcja w baza.php
?id= z adresu
wklejony do SQL (poprawiony w „Dobrych praktykach”)
filter_var + parametr, 404
filter_var + parametr, 404
kod wspólny stron
skopiowany
skopiowany
include
Na egzaminie: wersja podstawowa używa tylko funkcji z tabeli 1 arkusza. Ta wersja ma całe PHP strony w jej pliku, razem z nazwą bazy, więc egzaminator znajdzie połączenie i zapytania tam, gdzie klucz każe ich szukać („sprawdzić w kodzie źródłowym strony”). Poza egzaminem dane logowania do bazy trzyma się zwykle w osobnym pliku (np. config.php), żeby hasło nie trafiało do repozytorium razem z kodem. Wersja z osobnymi plikami jest najbliższa prawdziwym projektom, ale na egzaminie wymaga, żeby egzaminator zajrzał do dołączonych plików.
Krok 52 · po tym etapie masz 37 z 37 kryteriów (pkt)
Krok 52: 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 nazwa przeglądarki, w której strony były sprawdzane przez http://localhost/…. W folderze z numerem zdającego: dziewięć zdjęć, import.jpg, islandia.php, obiekty.php, kw1.png … kw4.png, kwerendy.txt, przeglądarka.txt, styl.css. Płytę sprawdź po nagraniu i opisz numerem zdającego.
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.