5. PHP – pętle oraz składnia alternatywna


Pętle

TaskQuest

Dodatek

Szybki quiz – przypomnienie poprzedniej lekcji

1. Co zostanie wyświetlone? Dlaczego?

$x = 0;
if ($x = 5) {
    echo "pięć";
} else {
    echo "inna";
}

2. Co zostanie wyświetlone?

$poziom = 2;
switch ($poziom) {
    case 1:
        echo "A";
    case 2:
        echo "B";
    case 3:
        echo "C";
        break;
    default:
        echo "D";
}

3. Zmienna $ocena zawiera tekst "5". Co wyświetli match, a co wyświetliłby switch z case 5:? Z czego wynika różnica?

$ocena = "5";
echo match ($ocena) {
    5 => "bdb",
    default => "inna"
};

4. Co stanie się po wykonaniu kodu?

$ranga = 3;
echo match ($ranga) {
    1 => "Brąz",
    2 => "Srebro"
};

5. Co zostanie wyświetlone w każdej z trzech linii?

$imie = null;
$nick = "";
echo $imie ?? "Gość";
echo $nick ?? "Gość";
echo $nick ?: "Gość";

6. Jaki tytuł otrzyma użytkownik z $xp = 150? Co by się stało, gdyby zamienić kolejność dwóch pierwszych warunków?

$tytul = match (true) {
    $xp >= 200 => "Mistrz",
    $xp >= 100 => "Wojownik",
    default => "Nowicjusz"
};

7. TaskQuest: uzupełnij jedną linię tak, aby $status przyjmował wartość "Aktywny" lub "Nieaktywny" w zależności od $wykonalDzisiajZadanie. Użyj operatora warunkowego.

$status = ______________________________;
Odpowiedzi
  1. pięć. W warunku użyto = (przypisanie) zamiast ==. Zmienna dostaje wartość 5, a 5 jest traktowane jako true, więc warunek zawsze jest spełniony.
  2. BC. W case 2 brakuje break, więc wykonanie „przechodzi” do case 3 (fall-through) i zatrzymuje się dopiero na jego break.
  3. match wyświetli inna, switch wyświetliłby bdb. match porównuje ściśle (===) – tekst "5" to nie liczba 5. switch porównuje luźno (==).
  4. Błąd UnhandledMatchError – żadna wartość nie pasuje, a nie ma default.
  5. Gość, pusty tekst (nic się nie wyświetli), Gość. Operator ?? reaguje tylko na null lub nieistniejącą zmienną. Operator ?: reaguje na każdą wartość traktowaną jako fałsz, także na pusty tekst.
  6. Wojownik. match (true) wybiera pierwszy prawdziwy warunek. Po zamianie kolejności użytkownik z 250 XP dostałby Wojownik zamiast Mistrz, bo warunek $xp >= 100 byłby sprawdzany jako pierwszy.
  7. $status = $wykonalDzisiajZadanie ? "Aktywny" : "Nieaktywny";

Pętle (ang. loops, iterations) to instrukcje, które pozwalają wielokrotnie powtórzyć fragment programu. W PHP dostępne są następujące rodzaje pętli:

for – gdy znamy liczbę iteracji
foreach – do iteracji po elementach tablicy
while – gdy warunek sprawdzamy przed wykonaniem iteracji
do…while – gdy warunek sprawdzamy po wykonaniu iteracji

Pętle for… i foreach…

Budowa pętli for wygląda następująco

for( inicjalizacja zmiennych; sprawdzenie warunku; modyfikacja zmiennych) 
{
   instrukcja1;
   instrukcja...;
}

Elementy pętli for:

  1. Inicjalizacja – wykonywana raz przed pierwszą iteracją
  2. Warunek – sprawdzany przed każdą iteracją; jeśli false, pętla kończy działanie
  3. Modyfikacja – wykonywana po każdej iteracji

Przykład 1: Napisz skrypt, który wyświetli 100 kolejnych liczb całkowitych począwszy od 3.

for ($i = 3; $i <= 102; $i++) {
echo $i;
if ($i < 102) {
echo ', ';
}
}

Przykład 2: Napisz skrypt, obliczający n! (n silnia).

$n = 5;

if ($n == 0 || $n == 1) {
$silnia = 1;
} else {
$silnia = 1;
for ($i = 2; $i <= $n; $i++) {
$silnia *= $i;
}
}

echo "Silnia z $n = $silnia";
// Wynik: Silnia z 5 = 120

Przykład 3: Napisz skrypt, który za pomocą znaków * (gwiazdki) narysuje trójkąt.

define('X', 15); // liczba musi być nieparzysta

echo '<pre>';
for ($w = 1; $w <= X; $w++) {
for ($k = 1; $k <= X + $w; $k++) {
if ($k == X + $w) {
echo "\n";
} elseif ($k > X - $w && $k < X + $w) {
echo '*';
} else {
echo ' ';
}
}
}
echo '</pre>';

//uproszczona wersja
$wysokosc = 8;

echo '<pre>';
for ($i = 1; $i <= $wysokosc; $i++) {
echo str_repeat(' ', $wysokosc - $i);
echo str_repeat('*', 2 * $i - 1);
echo "\n";
}
echo '</pre>';

Przykład 4: Napisz skrypt wyświetlający ciąg Fibonacciego i wypisz wartość ostatniego elementu.

$n = 10;

if ($n <= 0) {
    echo "n musi być liczbą dodatnią";
} elseif ($n == 1) {
    echo "1. element ciągu Fibonacciego to: 0";
} elseif ($n == 2) {
    echo "2. element ciągu Fibonacciego to: 1";
} else {
    $f1 = 0; // pierwszy element
    $f2 = 1; // drugi element
    
    echo "0 1 ";
    
    for ($i = 3; $i <= $n; $i++) {
        $fn = $f1 + $f2;
        echo $fn . " ";
        $f1 = $f2;
        $f2 = $fn;
    }
    
    echo "<br>$n. elementem ciągu Fibonacciego jest $f2";
}

Pętla for może operować na literach, zakres literowy jest podobny jak podział kolumn w Excelu

for ($i = 'a'; $i <= 'z'; $i++) {
    echo $i . ' ';
}
// Wynik: a b c d e f g h i j k l m n o p q r s t u v w x y z

Pętla for może mieć postać nieskończoną:

$i = 1;
for (;;) {
    echo "Wykonuję się $i raz<br>";
    if ($i == 10) {
        break;
    }
    $i++;
}
//Uwaga: Zawsze upewnij się, że pętla nieskończona ma warunek przerwania (break), inaczej spowoduje zawieszenie skryptu!

Pętla for może przyjmować wiele warunków, będą one wykonywane po sobie

for ($i = 1, $j = 0; $i <= 10; $j += $i, print "i wynosi = $i, a j = $j<br/>", $i++);

Możemy z pętli wyciągnąć warunki:

for ($i = 1; ; $i++) {
    if ($i > 10) {
        break;
    }
    echo $i;
}

foreach

Pętla foreach służy do wykonywania operacji na kolejnych elementach tablicy.

$tablica = ['jabłko', 'banan', 'gruszka'];

foreach ($tablica as $wartosc) {
    echo $wartosc . '<br>';
}

jeżeli chcemy operować również na indeksach, możemy je pobrać:

$tablica = [
    'a' => 'jabłko',
    'b' => 'banan',
    'c' => 'gruszka'
];

foreach ($tablica as $klucz => $wartosc) {
    echo "$klucz -> $wartosc<br>";
}

Pętle while…

Pętla while wykonuje instrukcje tak długo, jak długo warunek jest prawdziwy. Warunek sprawdzany jest przed każdą iteracją.

while (warunek) {
    // instrukcje
}

Przykład 1: Wyświetl wszystkie liczby całkowite nieujemne, mniejsze od podanej liczby.

$x = 10;
$min = 0;

echo "Liczby całkowite mniejsze od $x, to:<br>";

while ($min < $x) {
    echo $min;
    if ($min < $x - 1) {
        echo ' ';
    }
    $min++;
}
// Wynik: 0 1 2 3 4 5 6 7 8 9

Przykład 2: Napisz skrypt wyznaczający NWD (największy wspólny dzielnik).

$x = 15; 
$y = 10; 
$a=$x;
$b=$y;
while($a!=$b)
{
	if($a>$b) $a-=$b;
	else $b-=$a;
} 
$nwd = $a; // lub b - obie zmienne przechowują wynik NWD(a,b)       
echo('Dla a='.$x.' i b='.$y.' NWD = '.$nwd);

Pętle do… while…

Pętla do…while wykonuje instrukcje co najmniej raz, a następnie sprawdza warunek. Warunek sprawdzany jest po każdej iteracji.

do {
    // instrukcje
} while (warunek);

Przykład 1: Napisz skrypt wyświetlający wszystkie liczby całkowite od zera do danej liczby.

$x = 15;
$min = 0;

do {
    echo $min;
    if ($min < $x) {
        echo ' ';
    }
    $min++;
} while ($min <= $x);
// Wynik: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

Różnica między while a do…while

// while - może w ogóle się nie wykonać
$i = 10;
while ($i < 5) {
    echo "Nie wypisze się";
}

// do...while - wykona się przynajmniej raz
$i = 10;
do {
    echo "Wypisze się raz"; // wykona się mimo że warunek jest fałszywy
} while ($i < 5);

Kontrola przepływu pętli

Działanie pętli lub skryptu możemy przerwać w dowolnej chwili lub możemy pominąć część instrukcji do wykonania:

  • instrukcja break; powoduje natychmiastowe wyjście z pętli
  • instrukcja continue; powoduje przeskoczenie do następnego obiegu pętli
  • instrukcja exit; powoduje zakończenie wykonywania skryptu

Przerywanie działania pętli za pomocą instrukcji break;

for ($i = 3; $i <= 100; $i++) {
if ($i > 90) {
break; // wyjście z pętli
}
echo $i . ' ';
}
echo '<br>Ta instrukcja zostanie wykonana';
// Wypisze liczby 3-90, a następnie tekst

// Przerwij dwie pętle naraz
for ($i = 0; $i < 5; $i++) {
for ($j = 0; $j < 5; $j++) {
if ($j == 2) {
break 2; // wyjdź z obu pętli
}
echo "[$i][$j] ";
}
}

Przeskok do następnej iteracji (obiegu pętli) za pomocą instrukcji continue;

// Wypisz tylko liczby parzyste od 1 do 100
for ($i = 1; $i <= 100; $i++) {
if ($i % 2 != 0) {
continue; // pomiń nieparzyste
}
echo $i;
if ($i < 100) {
echo ', ';
}
}

Przerywanie wykonywania skryptu za pomocą instrukcji exit;

for ($i = 3; $i <= 10; $i++) {
if ($i > 9) {
exit('Koniec skryptu'); // lub die()
}
echo $i . ' ';
}
echo '<br>Ta instrukcja NIE zostanie wykonana';

Tabela porównawcza pętli

Pętla Warunek sprawdzany Min. wykonań Przypadek użycia
for Przed iteracją 0 Znana liczba iteracji
foreach Automatycznie 0 (jeśli tablica pusta) Iteracja po tablicy/obiekcie
while Przed iteracją 0 Nieznana liczba iteracji, warunek przed
do…while Po iteracji 1 Nieznana liczba iteracji, warunek po

Kiedy używać poszczególnych pętli:

  • for – gdy znasz dokładną liczbę iteracji lub potrzebujesz indeksu
  • foreach – gdy iterujesz po tablicy lub kolekcji
  • while – gdy nie znasz liczby iteracji, warunek sprawdzany przed wykonaniem
  • do…while – gdy pętla musi wykonać się przynajmniej raz

Pamiętaj:

  • Zawsze upewnij się, że pętla ma warunek zakończenia
  • Używaj break i continue świadomie
  • Preferuj wbudowane funkcje (array_map, array_filter) zamiast ręcznych pętli
  • Unset referencji po foreach z &
  • Unikaj modyfikacji tablicy podczas iteracji foreach
  • Używaj składni alternatywnej w szablonach HTML

Zadania do samodzielnego wykonania:

Zadanie 1 (dla klas, które miały już HTMLa): Za pomocą pętli for stwórz tabelę w HTML składająca się z 2 wierszy i 10 kolumn.

Zadanie 1 (dla klas, które nie miały HTMLa): Za pomocą pętli for wypisz na ekranie dowolne znaki, tak aby układały się w 2 wiersze po 10 znaków.

Zadanie 2: Za pomocą pętli for, while i do… while napisz skrypt, który będzie zwiększał zmienną $i od 1 do 100 o 2, a następnie zmniejszał ją do 1 o 4.

Zadanie 3: Napisz skrypt, który dla zmiennej $n=10 i $n>0 będzie ją zmniejszał o 1 i wypisywał: wartości kwadratów zmiennej $n; następnie wartości kwadratów zmiennej $n dla $n parzystych.

Zadanie 4: Za pomocą pętli for, while i do… while napisz skrypt, który wypisze ilość iteracji pętli dla zmiennej $i<=10. Tam gdzie to możliwe zastosuj $i++, ++$i, $i- -, – -$i.

Zadanie 5: Napisz skrypt, który sprawdzi, czy podana liczba jest liczbą pierwszą.

Zadanie 6: Za pomocą pętli for stwórz tabliczkę mnożenia wg przykładu:

3

Zadanie 7: Napisz skrypt, który za pomocą znaków * (gwiazdki) narysuje kwadrat. Gwiazdki leżące na obwodzie mają mieć kolor niebieski, a gwiazdki leżące na przekątnych zielone.

Zadanie 8: Napisz skrypt, który po podaniu przez użytkownika promienia koła, za pomocą znaków * (gwiazdki), narysuje je.

Zadanie 9: Oblicz ilość bloków z jakich został zbudowany sześcian o boku długości 6.

Zadanie 10: Wyświetl kolejno liczby od 1 do 10 (każda w nowej linii). Wykorzystaj pętle for

Zadanie 11: Wyświetl liczby malejąco od 10 do 0. Użyj pętli for.

Zadanie 12: Wyświetl kolejno liczby od 10 do 0 z krokiem 0,5. Użyj pętli for.

Zadanie 13: Wyświetl: „PHP1 PHP2 PHP3 PHP4 PHP5 PHP6 PHP7 PHP8 PHP9 PHP10”. Użyj pętli while

Zadanie 14: Napisz skrypt z użyciem pętli do…while wyświetlający następującą listę:

• Element nr 1

• Element nr 2

• Element nr 3

• Element nr 4

Zadanie 15: Napisz skrypt sumujący wszystkie liczby nieparzyste z przedziału od 0 do 10. Użyj instrukcji continue.

Zadanie 16: Napisz skrypt wyświetlający: “1-2-3-5-6-7-8-9”. Użyj dowolnej pętli i instrukcji przerywającej (wykluczono wartość 4).

Zadanie 17: Napisz skrypt wyświetlający szachownicę.

TaskQuest — Twój projekt na cały semestr

KARTA PRACY

Karta pracy — lekcja 5

PHP — pętle oraz składnia alternatywna

Projekt: TaskQuest — grywalizowana aplikacja do zarządzania zadaniami. Użytkownik loguje się, dodaje zadania, za ich wykonanie zdobywa punkty XP, awansuje na poziomy, zdobywa odznaki i widzi swoją pozycję w rankingu klasy.

Każda lekcja rozbudowuje tę samą aplikację o kolejny element.


Cel lekcji

Wykorzystać pętle do rozwiązania problemów, których nie dało się poprawnie obsłużyć pojedynczą instrukcją warunkową.

Po tej lekcji TaskQuest:

  • poprawnie obsłuży zdobycie dużej liczby XP,

  • pozwoli awansować o kilka poziomów jednocześnie,

  • automatycznie wygeneruje wizualny pasek postępu XP,

  • wykorzysta pętlę while tam, gdzie nie znamy wcześniej liczby powtórzeń,

  • wykorzysta pętlę for tam, gdzie liczba powtórzeń jest znana.


Co dziś budujesz?

TaskQuest 0.5 — poprawny system poziomów

Na poprzedniej lekcji nauczyliśmy TaskQuest podejmować decyzję:

Czy użytkownik zdobył już wystarczająco dużo XP, aby awansować?

Wykorzystaliśmy do tego instrukcję:

if

Działała dobrze, dopóki użytkownik przekraczał tylko jeden próg.

Dzisiaj naprawimy to ograniczenie.


1. Punkt startowy — kontynuujemy kod z lekcji 4

Nie twórz nowego projektu.

Otwórz dotychczasowy plik index.php projektu TaskQuest.

Po lekcji 4 logika związana z XP może wyglądać mniej więcej tak:

<?php
    echo "<h1>TaskQuest</h1>";
    echo "<p>Zdobywaj punkty za wykonane zadania!</p>";

    define('XP_NA_POZIOM', 100);
    $imieUzytkownika = "Kasia";
    $xp = 250;
    $poziom = 1;

    // Próg potrzebny do awansu
    $progNastepnegoPoziomu = $poziom * XP_NA_POZIOM;

    // Sprawdzenie awansu
    if ($xp >= $progNastepnegoPoziomu) {
        $poziom++;
        echo "Awansowałeś na poziom $poziom!<br>";
    }

    // Tytuł użytkownika
    switch ($poziom) {
        case 1:
            $tytul = "Nowicjusz";
            break;
        case 2:
            $tytul = "Wojownik";
            break;
        default:
            $tytul = "Mistrz";
    }

    // Aktywność — nadal ustawiana ręcznie.
    // Prawdziwe sprawdzanie aktywności zrobimy przy obsłudze dat.
    $wykonalDzisiajZadanie = true;

    $status = $wykonalDzisiajZadanie ? "Aktywny" : "Nieaktywny";

    // Postęp XP w aktualnym poziomie
    $procentPostepu = (($xp - ($poziom - 1) * XP_NA_POZIOM) / XP_NA_POZIOM) * 100;

    echo "Witaj, $imieUzytkownika! Twój poziom: $poziom. Status: $status. Tytuł: $tytul<br>";
    echo "Masz $xp XP z " . XP_NA_POZIOM . " potrzebnych  do poziomu ".++$poziom." (" . $procentPostepu . "%)"; 
?>

Jeżeli Twój dotychczasowy kod zawiera więcej HTML lub CSS — nie usuwaj go.

Modyfikujemy tylko mechanizm odpowiedzialny za poziomy i dodajemy pasek XP.


2. Najpierw znajdź problem

Spójrz na dane:

$xp = 250;
$poziom = 1;

Progi TaskQuest wyglądają następująco:

AwansWymagane łączne XP
poziom 1 → 2100 XP
poziom 2 → 3200 XP
poziom 3 → 4300 XP
poziom 4 → 5400 XP

Użytkownik posiada:

250 XP

Powinien więc znaleźć się na:

poziomie 3

Ale nasz kod wykona instrukcję:

$poziom++;

tylko jeden raz.

Wynikiem będzie:

poziom 2

mimo że użytkownik ma już wystarczająco dużo XP na poziom 3.


Pytanie

Dlaczego zwykły if nie rozwiązuje tego problemu?

Zastanów się, zanim przejdziesz dalej.


3. SAMODZIELNIE — napraw system awansu

Twoim pierwszym zadaniem jest zmodyfikowanie programu tak, aby sprawdzał próg awansu:

tak długo, jak długo użytkownik posiada wystarczająco dużo XP.

Nie wiemy wcześniej, ile razy trzeba będzie wykonać sprawdzenie.

Dla:

$xp = 40;

nie wykonamy żadnego awansu.

Dla:

$xp = 150;

wykonamy jeden.

Dla:

$xp = 250;

wykonamy dwa.

Dla:

$xp = 550;

wykonamy pięć.

Zadanie

Znajdź w swoim kodzie:

if ($xp >= $progNastepnegoPoziomu) {
    $poziom++;
    echo "Awansowałeś na poziom $poziom!<br>";
}

i zastąp ten mechanizm odpowiednią pętlą.


HINT 1

Potrzebujemy pętli, której liczba wykonań nie jest znana przed uruchomieniem programu.

Warunek ma być sprawdzany przed każdą kolejną iteracją.

Która pętla najlepiej tutaj pasuje?

for
foreach
while
do...while

HINT 2

Warunek możemy zapisać bezpośrednio:

$xp >= $poziom * XP_NA_POZIOM

Pętla powinna działać mniej więcej tak:

while (WARUNEK) {

    // awans

}

HINT 3

W każdej iteracji musi zmienić się coś, co wpływa na warunek.

W naszym przypadku zmienia się:

$poziom

czyli:

$poziom++;

POMOC

Spróbuj uzupełnić:

while ($xp >= __________________________) {

    ___________________;

}

Po każdej iteracji wartość $poziom rośnie.

To powoduje, że automatycznie rośnie również następny próg XP.


UWAGA — bardzo częsty błąd

Nie rób tego:

$progNastepnegoPoziomu = $poziom * XP_NA_POZIOM;

while ($xp >= $progNastepnegoPoziomu) {
    $poziom++;
}

Dlaczego?

Jeżeli przed pętlą:

$progNastepnegoPoziomu = 100;

to jego wartość nie zmieni się tylko dlatego, że zwiększyliśmy $poziom.

Dla:

$xp = 250;

warunek:

250 >= 100

będzie zawsze prawdziwy.

Powstanie pętla nieskończona.

Dlatego możemy:

  • obliczać próg bezpośrednio w warunku,

albo

  • aktualizować $progNastepnegoPoziomu wewnątrz pętli.

W naszym projekcie wybieramy prostszy pierwszy wariant.


4. Sprawdź swój mechanizm

Przetestuj kolejno:

$xp = 40;
$xp = 100;
$xp = 250;
$xp = 550;

Oczekiwane wyniki:

XPPoziom
401
1002
2503
5506

Jeżeli dla:

250 XP

otrzymujesz:

poziom 3

pierwsza część zadania działa poprawnie.


5. Policz, ile poziomów zdobył użytkownik

Chcemy również wiedzieć, czy podczas obliczeń nastąpił awans.

Przed pętlą zapamiętaj poziom startowy:

$poziomStartowy = $poziom;

Po zakończeniu pętli możesz policzyć:

$liczbaAwansow = $poziom - $poziomStartowy;

Jeżeli:

$liczbaAwansow > 0

wyświetl komunikat.

Przykład:

Awansowałeś o 2 poziomy!

6. Zaktualizuj tytuł użytkownika

Dopiero po zakończeniu pętli ustal tytuł użytkownika.

Pozostaw mechanizm poznany w poprzedniej lekcji:

switch ($poziom) {
    case 1:
        $tytul = "Nowicjusz";
        break;
    case 2:
        $tytul = "Wojownik";
        break;
    default:
        $tytul = "Mistrz";
}

lub zastosuj nowocześniejsze podejście:

$tytul = match ($poziom) {
        1 => "Nowicjusz",
        2 => "Wojownik",
        default => "Mistrz"
    };

Dzięki temu:

40 XP  → Nowicjusz
100 XP → Wojownik
250 XP → Mistrz

7. Oblicz postęp wewnątrz aktualnego poziomu

Poziom został już poprawnie ustalony.

Teraz możemy policzyć postęp.

Najpierw ustal XP wymagane na początku aktualnego poziomu:

$xpNaPoczatkuPoziomu = ($poziom - 1) * XP_NA_POZIOM;

Następnie oblicz XP zdobyte już w tym poziomie:

$xpWAktualnymPoziomie = $xp - $xpNaPoczatkuPoziomu;

I zaktualizuj procent:

$procentPostepu = ($xpWAktualnymPoziomie / XP_NA_POZIOM) * 100;

Przykład

Dla:

XP = 250
poziom = 3

początek poziomu 3 to:

200 XP

Użytkownik ma więc:

250 - 200 = 50 XP

w aktualnym poziomie.

Czyli:

50 / 100 = 50%

8. Druga pętla — budujemy pasek XP

Teraz wykorzystamy for.

Dlaczego?

Chcemy zawsze wyświetlić dokładnie:

10 segmentów

Czyli znamy liczbę iteracji.


Pasek dla 50%

Powinien wyglądać np.:

█████░░░░░

lub graficznie:

[■■■■■□□□□□]

SAMODZIELNIE

Oblicz liczbę aktywnych segmentów:

$liczbaAktywnychSegmentow =
    (int) ($procentPostepu / 10);

Następnie wykonaj pętlę:

for ($segment = 1; $segment <= 10; $segment++) {

    // jeżeli segment mieści się w postępie:
    // wyświetl segment aktywny █

    // w przeciwnym razie:
    // wyświetl segment pusty ░

}

Wykorzystaj wiedzę o if z poprzedniej lekcji. Jeżeli chcesz możesz dodać style i elementy HTML.


HINT

Dla:

$liczbaAktywnychSegmentow = 5;

warunek:

$segment <= $liczbaAktywnychSegmentow

będzie prawdziwy dla:

1
2
3
4
5

i fałszywy dla:

6
7
8
9
10

9. Składnia alternatywna

W głównej części lekcji poznałeś/aś również składnię alternatywną.

W HTML zamiast:

for (...) {

}

możemy zapisać:

<?php for (...): ?>

    <!-- HTML -->

<?php endfor; ?>

Zadanie

Spróbuj wygenerować segmenty właśnie w ten sposób. W dalszej części stosuj składnię, która najbardziej Ci odpowiada.

Przykładowy schemat:

<div class="pasek-xp">

    <?php for ($segment = 1; $segment <= 10; $segment++): ?>

        <!-- jeden segment -->

    <?php endfor; ?>

</div>

10. Oczekiwany efekt

Dla danych:

$imieUzytkownika = "Kasia";
$xp = 250;
$poziom = 1;
$wykonalDzisiajZadanie = true;

TaskQuest powinien ostatecznie pokazać mniej więcej:

Nie musisz uzyskać identycznego wyglądu graficznego.

Najważniejsze jest poprawne działanie logiki i wystąpienie niezbędnych (widocznych na zdjęciu) elementów.


11. PRZYKŁADOWE ROZWIĄZANIE

Rozwiń

Spróbuj wykonać zadanie samodzielnie przed otwarciem rozwiązania. Logika aplikacji powinna znajdować się na górze dokumentu, a elementy odpowiedzialne za wyświetlanie wewnątrz <body>. Spróbuj dodać własne style.

<?php
define('XP_NA_POZIOM', 100);
$imieUzytkownika = "Kama";
$xp = 250;
$poziom = 1; 

$poziomStartowy = $poziom;
while ($xp >= $poziom * XP_NA_POZIOM) {
    $poziom++;
}
$liczbaAwansow = $poziom - $poziomStartowy;


$tytul = match ($poziom) {
    1 => "Nowicjusz",
    2 => "Wojownik",
    default => "Mistrz"
};

//postęp wewnątrz aktualnego poziomu
$xpNaPoczatkuPoziomu = ($poziom - 1) * XP_NA_POZIOM;
$xpWAktualnymPoziomie = $xp - $xpNaPoczatkuPoziomu;
$procentPostepu = ($xpWAktualnymPoziomie / XP_NA_POZIOM) * 100;

// Tymczasowo ustawiane ręcznie - w lekcji 7 zastąpimy to sprawdzeniem daty
$wykonalDzisiajZadanie = true;
$status = $wykonalDzisiajZadanie ? "Aktywny" : "Nieaktywny";

//pasek postępu
$liczbaAktywnychSegmentow = (int) ($procentPostepu / 10);
$progNastepnegoPoziomu = $poziom * XP_NA_POZIOM;
?>

<!DOCTYPE html>
<html lang="pl">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>TaskQuest</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 700px;
            margin: 40px auto;
            padding: 20px;
        }
        .panel {
            border: 1px solid #ccc;
            padding: 24px;
            border-radius: 12px;
        }
        .pasek-xp {
            display: flex;
            gap: 4px;
            margin: 10px 0;
        }
        .segment {
            width: 32px;
            height: 18px;
            background: #ddd;
            border-radius: 3px;
        }
        .segment.aktywny {
            background: #555;
        }
        .awans {
            font-weight: bold;
        }
    </style>
</head>
<body>
    <div class="panel">
        <h1>TaskQuest</h1>
        <p>Zdobywaj punkty za wykonane zadania!</p>

        <h2>Witaj, <?= $imieUzytkownika; ?>!</h2>

        <p>Poziom: <strong><?= $poziom; ?></strong></p>

        <p>Tytuł: <strong><?= $tytul; ?></strong></p>

        <p>Łączne XP: <strong><?= $xp; ?></strong></p>
 
        <?php 
        if ($liczbaAwansow > 0){
            echo "<p class=\"awans\">Awansowałeś o $liczbaAwansow poziomy!</p>";
        } 
        ?>

        <h3>
            <?= "Masz $xpWAktualnymPoziomie XP z " . XP_NA_POZIOM . " XP potrzebnych  do poziomu ".++$poziom." (" . round($procentPostepu, 2) . "%)"; ?> 
        </h3>

        <div class="pasek-xp">
            <?php 
            for ($segment = 1; $segment <= 10; $segment++) {
                if ($segment <= $liczbaAktywnychSegmentow) {
                    echo "█";
                } else {
                    echo "░";
                }
                // lub z wykorzystaniem CSS
                // if ($segment <= $liczbaAktywnychSegmentow) {
                //     echo "<span class=\"segment aktywny\"></span>";
                // } else {
                //     echo "<span class=\"segment\"></span>";
                // }
            }
            ?>
        </div>

        <p>Kolejny próg: <?= $progNastepnegoPoziomu; ?> XP</p>

        <p>
            Status:<strong><?= $status; ?></strong>
        </p>

    </div>
</body>
</html>

12. Przetestuj rozwiązanie

Samo uruchomienie kodu jeden raz nie wystarcza.

Zmień tylko:

$xp

i sprawdź poniższe przypadki.

TestXPOczekiwany poziomXP w poziomiePostęp
10100%
24014040%
39919999%
4100200%
515025050%
619929999%
7200300%
825035050%
955065050%

13. Obowiązkowy eksperyment

Na chwilę wróć do starej wersji:

if ($xp >= $poziom * XP_NA_POZIOM) {
    $poziom++;
}

Ustaw:

$xp = 550;

Zapisz otrzymany poziom.

Następnie przywróć:

while

i sprawdź wynik ponownie.

Odpowiedz:

Dlaczego wyniki są różne?


14. Mini-zadanie — co się stanie?

Przeanalizuj kod bez uruchamiania go:

$xp = 250;
$poziom = 1;

while ($xp >= $poziom * 100) {
    $poziom++;
}

echo $poziom;

Zapisz przewidywany wynik.

Dopiero później uruchom program i sprawdź swoją odpowiedź.



Do samodzielnego sprawdzenia

  • Kontynuuję projekt z poprzedniej lekcji — nie stworzyłem/am TaskQuest od początku.

  • Pojedynczy if odpowiedzialny za awans został zastąpiony pętlą while.

  • Użytkownik z 250 XP otrzymuje poziom 3.

  • Użytkownik z 550 XP otrzymuje poziom 6.

  • Potrafię wyjaśnić, dlaczego while może wykonać się kilka razy.

  • Potrafię wskazać warunek zakończenia pętli.

  • Mój while nie tworzy pętli nieskończonej.

  • Po awansie ponownie prawidłowo wyliczany jest procent postępu.

  • Pasek XP składa się z 10 segmentów.

  • Segmenty generowane są przez for, a nie wpisane ręcznie.

  • Przetestowałem/am minimum cztery różne wartości XP.

  • Potrafię powiedzieć, dlaczego do awansu wybraliśmy while, a do paska for.


Pytania kontrolne

  1. Dlaczego pojedynczy if z lekcji 4 nie obsługiwał poprawnie użytkownika posiadającego 250 XP?

  2. Dlaczego while lepiej pasuje do systemu wielokrotnego awansu niż for?

  3. Co w naszej pętli sprawia, że kiedyś przestaje się wykonywać?

  4. Jaki poziom otrzyma użytkownik mający 550 XP?

  5. Co stanie się, jeżeli zapiszemy:

$prog = $poziom * XP_NA_POZIOM;

while ($xp >= $prog) {
    $poziom++;
}

i nie zmienimy $prog wewnątrz pętli?

  1. Dlaczego do wygenerowania dziesięciu segmentów paska XP dobrze nadaje się for?

  2. Czym różni się while od do...while?

  3. Do czego służy foreach?

  4. Jak działa break?

  5. Jak działa continue?



PODSUMOWANIE LEKCJI

W skrócie

  • for – trzy części w nawiasie: inicjalizacja (wykonywana raz), warunek (sprawdzany przed każdym obiegiem), modyfikacja (wykonywana po każdym obiegu). Części oddzielamy średnikami, nie przecinkami.
  • while – warunek sprawdzany przed obiegiem, więc pętla może nie wykonać się ani razu. W ciele pętli musi zmieniać się coś, od czego zależy warunek – inaczej powstaje pętla nieskończona.
  • do…while – wykonuje się co najmniej raz, warunek sprawdzany jest po obiegu. Po while (warunek) stawiamy średnik.
  • foreach – służy do przechodzenia po tablicy. as $wartosc daje same wartości, as $klucz => $wartosc – klucze i wartości.
  • Wartość obliczona przed pętlą nie zmieni się sama w trakcie jej działania. Jeżeli warunek ma zależeć od zmieniającej się zmiennej, licz go w warunku albo aktualizuj wewnątrz pętli (pułapka z $progNastepnegoPoziomu w TaskQuest).
  • Zmienne sumujące i liczące ($suma, $licznik) ustawiamy przed pętlą. Ustawione wewnątrz – zerują się w każdym obiegu.
  • < a <= w warunku to często różnica jednego obiegu. Przy każdej pętli sprawdź, jaka jest pierwsza i ostatnia wartość licznika.
  • break wychodzi tylko z najbliższej pętli (break 2 – z dwóch zagnieżdżonych). continue pomija resztę bieżącego obiegu i przechodzi do następnego. exit kończy cały skrypt – kod po nim się nie wykona.
  • Wybór pętli: znana liczba powtórzeń → for, tablica → foreach, powtarzanie „dopóki warunek jest spełniony” → while, „najpierw wykonaj, potem sprawdź” → do…while.
  • Składnia alternatywna (: zamiast {, słowo kończące zamiast }) istnieje dla for, foreach i while – dla do…while nie. Stosujemy ją, gdy pętla generuje fragmenty HTML. Nie wolno mieszać obu zapisów w jednej pętli.

Co trzeba umieć na INF.03?

  • dobrać rodzaj pętli do problemu i uzasadnić wybór,
  • napisać pętlę, która generuje powtarzalny kod HTML: wiersze tabeli (<tr><td>), opcje listy (<option>), elementy listy (<li>), odnośniki (<a>),
  • wstawiać wartości zmiennych do generowanego HTML (w echo albo przez <?= ?> w składni alternatywnej),
  • przewidzieć, ile razy wykona się pętla i co wyświetli,
  • rozpoznać pętlę nieskończoną i wskazać jej przyczynę,
  • zaimplementować proste algorytmy z użyciem pętli: sumowanie, zliczanie, szukanie wartości, silnia, NWD, sprawdzanie liczby pierwszej.

Minimum, które powinno wejść w pamięć

Jeżeli masz zapamiętać tylko kilka rzeczy z tej lekcji, zapamiętaj te:

  1. for ($i = 1; $i <= 10; $i++) – średniki, nie przecinki.
  2. while może nie wykonać się ani razu, do…while wykona się zawsze co najmniej raz.
  3. W while coś w środku musi zmieniać warunek, inaczej pętla nigdy się nie skończy.
  4. foreach ($tablica as $klucz => $wartosc) – najpierw tablica, potem as.
  5. $suma = 0; przed pętlą, nie w środku.
  6. break – wyjdź z pętli, continue – przejdź do następnego obiegu.
  7. Składnia alternatywna: for (...): … endfor;, foreach (...): … endforeach;, while (...): … endwhile;.

Ściągawka – najważniejsze polecenia i składnia

Rodzaje pętli

for ($i = 1; $i <= 10; $i++) { ... }      // znana liczba powtórzeń

foreach ($tablica as $wartosc) { ... }     // tylko wartości
foreach ($tablica as $klucz => $wartosc) { ... }

while ($warunek) { ... }                   // 0 lub więcej obiegów

do { ... } while ($warunek);               // 1 lub więcej obiegów – średnik!

Sterowanie pętlą

InstrukcjaDziałanie
break;wyjście z najbliższej pętli
break 2;wyjście z dwóch zagnieżdżonych pętli
continue;przejście do następnego obiegu
exit; / exit('tekst');zakończenie całego skryptu

Składnia alternatywna

ZwykłaAlternatywna
for (...) { }for (...): … endfor;
foreach (...) { }foreach (...): … endforeach;
while (...) { }while (...): … endwhile;
do { } while (...);brak

Schemat: generowanie HTML w pętli

<select name="liczba">
<?php for ($i = 1; $i <= 5; $i++): ?>
    <option value="<?= $i ?>"><?= $i ?></option>
<?php endfor; ?>
</select>

To samo w zwykłej składni:

for ($i = 1; $i <= 5; $i++) {
    echo "<option value=\"$i\">$i</option>";
}

Schemat: akumulator (suma, licznik)

$suma = 0;                  // przed pętlą
foreach ($liczby as $liczba) {
    $suma += $liczba;       // w pętli
}
echo $suma;                 // po pętli

Quiz – sprawdź się

1. Dobierz najlepszą pętlę do każdej sytuacji i krótko uzasadnij:

a) wyświetlenie dokładnie 10 segmentów paska XP,
b) wypisanie wszystkich imion zapisanych w tablicy $uczniowie,
c) awansowanie użytkownika, dopóki ma wystarczająco dużo XP,
d) wyświetlenie komunikatu, który musi pojawić się przynajmniej raz, a potem powtarza się zależnie od warunku.

2. Co zostanie wyświetlone?

for ($i = 10; $i > 0; $i -= 3) {
    echo $i . " ";
}

3. Co wyświetli każdy z fragmentów?

// Fragment A
$i = 5;
while ($i < 5) {
    echo $i;
    $i++;
}

// Fragment B
$i = 5;
do {
    echo $i;
    $i++;
} while ($i < 5);

4. Znajdź błąd. Co stanie się po uruchomieniu tego kodu i jak go poprawić?

$i = 1;
while ($i <= 5) {
    echo $i;
}

5. Co zostanie wyświetlone?

for ($i = 1; $i <= 6; $i++) {
    if ($i % 3 == 0) {
        continue;
    }
    echo $i;
}

6. Co zostanie wyświetlone? Czy napis „koniec” się pojawi?

for ($i = 1; $i <= 10; $i++) {
    if ($i == 4) {
        break;
    }
    echo $i;
}
echo " koniec";

7. Co zostanie wyświetlone? Jak zmieni się wynik po zamianie break; na break 2;?

for ($i = 1; $i <= 3; $i++) {
    for ($j = 1; $j <= 3; $j++) {
        if ($j == 2) {
            break;
        }
        echo "$i$j ";
    }
}

8. Uczeń chciał zsumować XP z tablicy, ale program wyświetla 30 zamiast 60. Dlaczego? Popraw kod.

foreach ([10, 20, 30] as $xp) {
    $suma = 0;
    $suma += $xp;
}
echo $suma;

9. Uzupełnij kod tak, aby dla tablicy poniżej wyświetlił się tekst Ala: 120 XP i Olek: 80 XP (każdy w nowej linii).

$ranking = ['Ala' => 120, 'Olek' => 80];

foreach (__________________________) {
    echo __________________________;
}

10. Czego brakuje w miejscu ______?

<ul>
<?php foreach ($zadania as $zadanie): ?>
    <li><?= $zadanie ?></li>
<?php ______ ?>
</ul>

11. Dlaczego ten kod powoduje błąd składni?

<?php for ($i = 1; $i <= 3; $i++): ?>
    <p>Zadanie nr <?= $i ?></p>
<?php } ?>

12. TaskQuest: przeanalizuj kod bez uruchamiania go. Jaki poziom otrzyma użytkownik? Ile XP ma w aktualnym poziomie i jak będzie wyglądał jego 10-segmentowy pasek (█ – aktywny, ░ – pusty)?

define('XP_NA_POZIOM', 100);
$xp = 420;
$poziom = 1;

while ($xp >= $poziom * XP_NA_POZIOM) {
    $poziom++;
}

13. Ile razy wykona się ciało pętli?

for ($i = 0; $i < 10; $i += 2) {
    // ...
}

14. Zadanie praktyczne. Napisz pętlę, która wygeneruje tabelę HTML z jednym wierszem nagłówka Liczba | Kwadrat oraz pięcioma wierszami danych dla liczb od 1 do 5. Możesz użyć zwykłej lub alternatywnej składni.


Odpowiedzi
  1. a) for – znana liczba powtórzeń; b) foreach – przechodzimy po tablicy; c) while – nie wiemy, ile będzie awansów, warunek trzeba sprawdzić przed pierwszym awansem (może nie być żadnego); d) do…while – ciało musi wykonać się co najmniej raz.
  2. 10 7 4 1
  3. Fragment A nie wyświetli nic (warunek fałszywy od razu). Fragment B wyświetli 5 – ciało wykonuje się przed sprawdzeniem warunku.
  4. Pętla nieskończona: $i się nie zmienia, więc 1 <= 5 jest zawsze prawdziwe. Poprawka: dodać $i++; w ciele pętli.
  5. 1245 – dla 3 i 6 continue pomija echo.
  6. 123 koniec. break kończy tylko pętlę, kod po pętli wykonuje się normalnie.
  7. 11 21 31 – break przerywa tylko pętlę wewnętrzną, zewnętrzna działa dalej. Z break 2; wynik to 11 – obie pętle kończą się przy pierwszym $j == 2.
  8. $suma = 0; jest wewnątrz pętli, więc suma zeruje się w każdym obiegu i zostaje tylko ostatnia wartość. Przenieś $suma = 0; przed pętlę.
  9. Przykładowo:
    foreach ($ranking as $imie => $punkty) {
    echo "$imie: $punkty XP<br>";
    }
  10. endforeach;
  11. Pętla otwarta w składni alternatywnej (:) musi zostać zamknięta przez endfor;, a nie }. Nie wolno mieszać obu zapisów.
  12. Poziom 5 (progi 100, 200, 300, 400 zostały przekroczone, 500 – nie). XP w aktualnym poziomie: 420 − 400 = 20, czyli 20%. Pasek: ██░░░░░░░░.
  13. 5 razy ($i = 0, 2, 4, 6, 8).
  14. Przykładowe rozwiązanie:
    <table>
    <tr><th>Liczba</th><th>Kwadrat</th></tr>
    <?php for ($i = 1; $i <= 5; $i++): ?>
    <tr><td><?= $i ?></td><td><?= $i * $i ?></td></tr>
    <?php endfor; ?>
    </table>

    Sprawdź: tabela ma 6 wierszy (1 nagłówek + 5 danych), ostatni wiersz to 5 | 25.