Lekcja 5: Operatory w PHP

Witaj w piątej lekcji kursu PHP! Po solidnym zrozumieniu zmiennych i typów danych, nadszedł czas, aby nauczyć się, jak na nich operować. Służą do tego operatory – specjalne symbole lub słowa kluczowe, które wykonują określone działania na jednej, dwóch lub trzech wartościach (operandach). PHP oferuje bogaty zestaw operatorów do wykonywania operacji arytmetycznych, przypisania, porównania, logicznych, bitowych i wielu innych. Opanowanie operatorów jest kluczowe do pisania użytecznego i dynamicznego kodu.

Czym są operatory i operandy?

Operator to symbol, który mówi interpreterowi PHP, jaką operację ma wykonać. Na przykład symbol + jest operatorem dodawania.

Operand to wartość lub zmienna, na której operator działa. W wyrażeniu $a + $b, zmienne $a i $b są operandami.

Operatory można podzielić ze względu na liczbę operandów, które przyjmują:

Operatory Arytmetyczne

Operatory arytmetyczne służą do wykonywania podstawowych operacji matematycznych na liczbach.

Operator Nazwa Przykład Wynik (dla $a=10, $b=3) Opis
+ Dodawanie $a + $b 13 Suma dwóch operandów.
- Odejmowanie $a - $b 7 Różnica dwóch operandów.
* Mnożenie $a * $b 30 Iloczyn dwóch operandów.
/ Dzielenie $a / $b 3.333... Iloraz dwóch operandów. Wynik jest zawsze typu float, chyba że oba operandy są liczbami całkowitymi, są podzielne bez reszty i wynik mieści się w zakresie typu int (rzadki przypadek, np. 4 / 2 da int(2)).
% Modulo (Reszta z dzielenia) $a % $b 1 Reszta z dzielenia pierwszego operandu przez drugi. Operandy są konwertowane na liczby całkowite przed wykonaniem operacji. Znak wyniku jest taki sam jak znak dzielnej.
** Potęgowanie (od PHP 5.6) $a ** $b 1000 Pierwszy operand podniesiony do potęgi drugiego operandu.
<?php
$a = 10;
$b = 3;

echo "Dodawanie: " . ($a + $b) . "<br>";         // 13
echo "Odejmowanie: " . ($a - $b) . "<br>";       // 7
echo "Mnożenie: " . ($a * $b) . "<br>";          // 30
echo "Dzielenie: " . ($a / $b) . "<br>";         // 3.333...
echo "Modulo: " . ($a % $b) . "<br>";            // 1
echo "Potęgowanie: " . ($a ** $b) . "<br>";      // 1000 (10 do potęgi 3)

// Dzielenie przez zero
// $wynikDzieleniaPrzezZero = 10 / 0; // Spowoduje błąd Warning: Division by zero (PHP 7) lub Fatal error: Uncaught DivisionByZeroError (PHP 8+)
// echo $wynikDzieleniaPrzezZero;

// Modulo z liczbami ujemnymi
echo "-10 % 3 = " . (-10 % 3) . "<br>"; // -1
echo "10 % -3 = " . (10 % -3) . "<br>"; // 1
?>

Warto zauważyć, że operator + może być również używany do łączenia tablic (unia tablic), ale jego działanie jest inne niż konkatenacja ciągów znaków (do tego służy kropka .). Jeśli spróbujesz użyć operatorów arytmetycznych na nie-numerycznych ciągach znaków, PHP spróbuje je przekonwertować na liczby, co może prowadzić do nieoczekiwanych wyników lub ostrzeżeń (jak widzieliśmy w lekcji o typach danych).

Operatory Przypisania

Podstawowym operatorem przypisania jest =, który przypisuje wartość prawego operandu do lewego operandu (np. zmiennej).

<?php
$wiek = 30;
$nazwisko = "Kowalski";
?>

PHP oferuje również skrócone operatory przypisania, które łączą operację arytmetyczną lub bitową z przypisaniem. Są one bardzo wygodne do modyfikowania wartości zmiennej w miejscu.

Operator Przykład Równoważne z Opis
+= $a += $b $a = $a + $b Dodaj i przypisz.
-= $a -= $b $a = $a - $b Odejmij i przypisz.
*= $a *= $b $a = $a * $b Pomnóż i przypisz.
/= $a /= $b $a = $a / $b Podziel i przypisz.
%= $a %= $b $a = $a % $b Modulo i przypisz.
**= $a **= $b $a = $a ** $b Spotęguj i przypisz (od PHP 5.6).
.= $str .= " tekst" $str = $str . " tekst" Połącz ciągi znaków (konkatenacja) i przypisz.
??= $a ??= $b $a = $a ?? $b (lub if (!isset($a)) { $a = $b; }) Operator przypisania koalescencyjnego (Null Coalesce Assignment Operator, od PHP 7.4). Przypisuje wartość prawego operandu do lewego tylko wtedy, gdy lewy operand jest NULL.

Istnieją również skrócone operatory przypisania dla operatorów bitowych (&=, |=, ^=, <<=, >>=), które omówimy później.

<?php
$licznik = 10;
$licznik += 5; // $licznik teraz wynosi 15
echo "Licznik: $licznik <br>";

$tekst = "Witaj, ";
$tekst .= "świecie!"; // $tekst teraz wynosi "Witaj, świecie!"
echo "Tekst: $tekst <br>";

$domyslnaWartosc = "Brak danych";
// $ustawionaWartosc = "Jakaś wartość"; // Odkomentuj, aby zobaczyć różnicę
$finalnaWartosc = $ustawionaWartosc ?? $domyslnaWartosc; // Operator koalescencyjny
echo "Finalna wartość (??): $finalnaWartosc <br>";

// Operator przypisania koalescencyjnego (??=)
$konfiguracja["port"] = $konfiguracja["port"] ?? 8080; // Starszy sposób
// lub z ??=
$konfiguracja["host"] ??= "localhost";

var_dump($konfiguracja);
?>

Operatory Porównania

Operatory porównania pozwalają na porównywanie dwóch wartości. Wynikiem operacji porównania jest zawsze wartość logiczna (bool): true lub false. Są one niezwykle ważne w instrukcjach warunkowych i pętlach.

Operator Nazwa Przykład Opis
== Równy (luźne porównanie) $a == $b Zwraca true, jeśli $a jest równe $b po konwersji typów (type juggling). Np. 5 == "5" jest true.
=== Identyczny (ścisłe porównanie) $a === $b Zwraca true, jeśli $a jest równe $b ORAZ są tego samego typu. Np. 5 === "5" jest false. Zalecane jest używanie tego operatora, gdy tylko to możliwe, aby uniknąć niejednoznaczności.
!= lub <> Różny (luźne porównanie) $a != $b Zwraca true, jeśli $a nie jest równe $b po konwersji typów.
!== Nieidentyczny (ścisłe porównanie) $a !== $b Zwraca true, jeśli $a nie jest równe $b LUB nie są tego samego typu.
< Mniejszy niż $a < $b Zwraca true, jeśli $a jest mniejsze niż $b.
> Większy niż $a > $b Zwraca true, jeśli $a jest większe niż $b.
<= Mniejszy lub równy $a <= $b Zwraca true, jeśli $a jest mniejsze lub równe $b.
>= Większy lub równy $a >= $b Zwraca true, jeśli $a jest większe lub równe $b.
<=> Operator statku kosmicznego (Spaceship Operator, od PHP 7) $a <=> $b Zwraca int: -1 jeśli $a < $b, 0 jeśli $a == $b, 1 jeśli $a > $b. Przydatny do sortowania.
<?php
$x = 5;
$y = "5";
$z = 10;

var_dump($x == $y);  // bool(true) - luźne porównanie, "5" jest konwertowane na liczbę 5
var_dump($x === $y); // bool(false) - ścisłe porównanie, typy są różne (int vs string)

var_dump($x != $z);  // bool(true)
var_dump($x !== $y); // bool(true)

var_dump($x < $z);   // bool(true)
var_dump($x >= $z);  // bool(false)

$a = 5;
$b = 10;
$c = 5;

echo "$a <=> $b: " . ($a <=> $b) . "<br>"; // -1
echo "$a <=> $c: " . ($a <=> $c) . "<br>"; // 0
echo "$b <=> $a: " . ($b <=> $a) . "<br>"; // 1
?>

Zawsze preferuj ścisłe porównanie (=== i !==), aby uniknąć subtelnych błędów wynikających z automatycznej konwersji typów, chyba że świadomie potrzebujesz luźnego porównania.

Operatory Inkrementacji / Dekrementacji

Te operatory służą do zwiększania (inkrementacja) lub zmniejszania (dekrementacja) wartości zmiennej numerycznej o jeden. Mogą być używane jako pre-inkrementacja/dekrementacja (przed zmienną) lub post-inkrementacja/dekrementacja (po zmiennej).

Operator Nazwa Przykład Opis
++$a Pre-inkrementacja $b = ++$a; Zwiększa $a o jeden, a następnie zwraca nową wartość $a.
$a++ Post-inkrementacja $b = $a++; Zwraca oryginalną wartość $a, a następnie zwiększa $a o jeden.
--$a Pre-dekrementacja $b = --$a; Zmniejsza $a o jeden, a następnie zwraca nową wartość $a.
$a-- Post-dekrementacja $b = $a--; Zwraca oryginalną wartość $a, a następnie zmniejsza $a o jeden.
<?php
$i = 5;
echo "Początkowe i: $i <br>"; // 5

$j = ++$i; // Pre-inkrementacja: $i staje się 6, $j dostaje 6
echo "Po ++i: i=$i, j=$j <br>"; // i=6, j=6

$k = $i++; // Post-inkrementacja: $k dostaje aktualne $i (czyli 6), $i staje się 7
echo "Po i++: i=$i, k=$k <br>"; // i=7, k=6

$m = 10;
echo "Początkowe m: $m <br>"; // 10

$n = --$m; // Pre-dekrementacja: $m staje się 9, $n dostaje 9
echo "Po --m: m=$m, n=$n <br>"; // m=9, n=9

$p = $m--; // Post-dekrementacja: $p dostaje aktualne $m (czyli 9), $m staje się 8
echo "Po m--: m=$m, p=$p <br>"; // m=8, p=9

// Inkrementacja/dekrementacja działa również na ciągach znaków, ale w specyficzny sposób (np. "Z"++ da "AA")
// oraz na wartościach NULL (NULL++ da 1).
$litera = "A";
$litera++;
echo "Litera po inkrementacji: $litera <br>"; // B

$nullVal = null;
$nullVal++;
echo "NULL po inkrementacji: $nullVal, typ: " . gettype($nullVal) . "<br>"; // 1, typ: integer
?>

Operatory te są często używane w pętlach do kontrolowania liczby iteracji.

Operatory Logiczne

Operatory logiczne służą do łączenia wyrażeń warunkowych. Wynikiem operacji logicznej jest zawsze wartość bool (true lub false).

Operator Nazwa Przykład Opis
and lub && Koniunkcja (AND) $a and $b
$a && $b
Zwraca true, jeśli oba operandy ($a i $b) są true. W przeciwnym razie zwraca false. Różnią się priorytetem (operator && ma wyższy priorytet niż and).
or lub || Alternatywa (OR) $a or $b
$a || $b
Zwraca true, jeśli przynajmniej jeden z operandów ($a lub $b) jest true. Zwraca false tylko wtedy, gdy oba są false. Różnią się priorytetem (|| ma wyższy priorytet niż or).
xor Alternatywa Wyłączająca (XOR) $a xor $b Zwraca true, jeśli dokładnie jeden z operandów jest true (ale nie oba).
! Negacja (NOT) !$a Zwraca true, jeśli $a jest false, i false, jeśli $a jest true. (Operator unarny).

Kolejność wykonywania (Short-circuit evaluation): Operatory && i || wykonują tzw. "short-circuit evaluation". Oznacza to, że jeśli wynik całego wyrażenia można określić na podstawie wartości pierwszego operandu, drugi operand nie jest w ogóle obliczany.

Jest to ważne, gdy drugi operand jest np. wywołaniem funkcji, która ma efekty uboczne.

<?php
$wiek = 25;
$maPrawoJazdy = true;
$jestTrzezwy = true;

// && (AND)
if ($wiek >= 18 && $maPrawoJazdy && $jestTrzezwy) {
    echo "Możesz prowadzić samochód.<br>";
} else {
    echo "Nie możesz prowadzić samochodu.<br>";
}

$jestAdminem = false;
$jestModerator = true;

// || (OR)
if ($jestAdminem || $jestModerator) {
    echo "Masz uprawnienia do zarządzania treścią.<br>";
} else {
    echo "Nie masz uprawnień do zarządzania treścią.<br>";
}

// ! (NOT)
$swiatloWlaczone = false;
if (!$swiatloWlaczone) {
    echo "Światło jest wyłączone.<br>";
}

// xor (XOR)
$a = true;
$b = false;
$c = true;

var_dump($a xor $b); // bool(true) - jeden jest true
var_dump($a xor $c); // bool(false) - oba są true

// Różnica w priorytetach między `and` a `&&`
$wynik1 = true && false; // $wynik1 = (true && false) => false
$wynik2 = true and false; // $wynik2 = true; (bo = ma wyższy priorytet niż `and`)
                         // Aby uzyskać zamierzony efekt: $wynik2 = (true and false); => false
var_dump($wynik1, $wynik2);

// Zaleca się używanie && i || zamiast `and` i `or` dla uniknięcia problemów z priorytetami,
// lub stosowanie nawiasów dla jasności.
?>

Operatory Bitowe

Operatory bitowe działają na poszczególnych bitach liczb całkowitych. Są one rzadziej używane w typowym programowaniu webowym, ale mogą być przydatne w niektórych specyficznych zastosowaniach (np. praca z flagami, maskami bitowymi, niskopoziomowe operacje).

Operator Nazwa Przykład Opis
& Bitowe AND (Koniunkcja) $a & $b Ustawia bity w wyniku, które są ustawione w obu operandach.
| Bitowe OR (Alternatywa) $a | $b Ustawia bity w wyniku, które są ustawione w przynajmniej jednym z operandów.
^ Bitowe XOR (Alternatywa Wyłączająca) $a ^ $b Ustawia bity w wyniku, które są ustawione w jednym z operandów, ale nie w obu.
~ Bitowe NOT (Negacja) ~$a Odwraca wszystkie bity w operandzie. (Operator unarny).
<< Przesunięcie Bitowe w Lewo (Left Shift) $a << $b Przesuwa bity $a o $b pozycji w lewo (każde przesunięcie o 1 w lewo jest równoważne mnożeniu przez 2).
>> Przesunięcie Bitowe w Prawo (Right Shift) $a >> $b Przesuwa bity $a o $b pozycji w prawo (każde przesunięcie o 1 w prawo jest równoważne dzieleniu przez 2, z obcięciem części ułamkowej).
<?php
$val1 = 6;  // Binarnie: 0110
$val2 = 3;  // Binarnie: 0011

// 0110 ($val1)
// &
// 0011 ($val2)
// ----
// 0010 (wynik = 2)
echo "$val1 & $val2 = " . ($val1 & $val2) . "<br>"; // 2

// 0110 ($val1)
// |
// 0011 ($val2)
// ----
// 0111 (wynik = 7)
echo "$val1 | $val2 = " . ($val1 | $val2) . "<br>"; // 7

// 0110 ($val1)
// ^
// 0011 ($val2)
// ----
// 0101 (wynik = 5)
echo "$val1 ^ $val2 = " . ($val1 ^ $val2) . "<br>"; // 5

// ~0110 (~$val1) - zależy od rozmiaru int, np. dla 8 bitów z uzupełnieniem do dwóch:
// 0110 => bity odwrócone 1001. Jeśli interpretowane jako liczba ze znakiem (uzupełnienie do 2),
// to 1001 jest reprezentacją -7 (dla 4 bitów). Dla typowego int 32/64 bitowego wynik będzie inny.
// W PHP ~x jest równoważne -(x+1) dla liczb całkowitych.
echo "~$val1 = " . (~$val1) . "<br>"; // -7

// 0110 ($val1) przesunięte o 1 w lewo: 1100 (wynik = 12)
echo "$val1 << 1 = " . ($val1 << 1) . "<br>"; // 12

// 0110 ($val1) przesunięte o 1 w prawo: 0011 (wynik = 3)
echo "$val1 >> 1 = " . ($val1 >> 1) . "<br>"; // 3
?>

Operator Kontroli Błędów (Error Control Operator)

Operator @ (małpa), gdy jest umieszczony przed wyrażeniem PHP (np. wywołaniem funkcji), powoduje, że wszelkie komunikaty o błędach (Notices, Warnings, Errors) wygenerowane przez to wyrażenie zostaną zignorowane. Interpreter PHP nie wyświetli ich ani nie zaloguje.

<?php
// Przykład: próba otwarcia nieistniejącego pliku
$uchwyt1 = fopen("nieistniejacy_plik.txt", "r"); // Wygeneruje Warning
if ($uchwyt1 === false) {
    echo "Nie udało się otworzyć pliku (bez @).<br>";
}

// Ta sama operacja z operatorem @
$uchwyt2 = @fopen("nieistniejacy_plik.txt", "r"); // Warning zostanie stłumiony
if ($uchwyt2 === false) {
    echo "Nie udało się otworzyć pliku (z @), błąd stłumiony.<br>";
}

// Użycie @ z niezdefiniowaną zmienną
// echo $niezdefiniowana; // Wygeneruje Notice: Undefined variable
// echo @$niezdefiniowana; // Notice zostanie stłumiony (wyświetli pusty ciąg)
?>

Uwaga: Używanie operatora @ jest generalnie złą praktyką i powinno być unikane. Tłumienie błędów utrudnia debugowanie i może maskować poważne problemy w aplikacji. Zamiast tego należy stosować odpowiednie mechanizmy obsługi błędów, takie jak sprawdzanie wartości zwracanych przez funkcje, używanie bloków try-catch do obsługi wyjątków (o czym w późniejszej lekcji), oraz konfigurowanie odpowiedniego poziomu raportowania błędów na serwerze deweloperskim.

Są bardzo rzadkie przypadki, gdy @ może być użyteczne (np. gdy funkcja nie oferuje innego sposobu na sprawdzenie błędu niż wygenerowanie ostrzeżenia, a my chcemy ten błąd obsłużyć w specyficzny sposób), ale należy podchodzić do tego z dużą ostrożnością.

Operator Wykonania (Execution Operator)

PHP obsługuje jeden operator wykonania: grawisy (backticks) ``. PHP spróbuje wykonać zawartość grawisów jako polecenie systemowe (shell command), a wynik tego polecenia zostanie zwrócony jako ciąg znaków. Działa to podobnie do funkcji shell_exec().

<?php
// Przykład (może nie działać na wszystkich systemach lub jeśli PHP nie ma uprawnień)
// $listaPlikow = `ls -la`; // Na systemach Unix-like
// $wersjaSystemu = `ver`; // Na systemach Windows

// echo "<pre>$listaPlikow</pre>";

// Lepszym i bardziej kontrolowanym sposobem jest użycie funkcji takich jak shell_exec(), exec(), system(), passthru()
$output = shell_exec("echo Witaj z powłoki!");
echo $output;
?>

Uwaga dotycząca bezpieczeństwa: Używanie operatora wykonania lub funkcji wykonujących polecenia systemowe jest bardzo ryzykowne, jeśli jakiekolwiek dane pochodzące od użytkownika są włączane do tych poleceń bez odpowiedniego oczyszczenia i walidacji. Może to prowadzić do luk bezpieczeństwa typu "command injection". Jeśli to możliwe, unikaj wykonywania poleceń systemowych w ten sposób lub rób to z najwyższą ostrożnością.

Operator Ciągu Znaków (String Operator)

PHP ma jeden operator przeznaczony specjalnie do pracy z ciągami znaków:

<?php
$imie = "Anna";
$nazwisko = "Nowak";
$pelneImie = $imie . " " . $nazwisko; // Łączy imię, spację i nazwisko

echo $pelneImie; // Wyświetli "Anna Nowak"

$powitanie = "Witaj, ";
$powitanie .= $imie . "!"; // Użycie skróconego operatora .= (powitanie = powitanie . $imie . "!")
echo "<br>" . $powitanie; // Wyświetli "Witaj, Anna!"
?>

Operatory Tablicowe

PHP dostarcza specjalne operatory do pracy z tablicami:

Operator Nazwa Przykład Opis
+ Unia $a + $b Łączy tablicę $a z tablicą $b. Jeśli obie tablice mają elementy o tych samych kluczach, używane są elementy z tablicy po lewej stronie operatora ($a), a elementy z $b o tych samych kluczach są ignorowane. Klucze numeryczne nie są переиндексировываются.
== Równość (luźna) $a == $b Zwraca true, jeśli tablica $a ma te same pary klucz-wartość co tablica $b, niezależnie od kolejności i typów wartości (po konwersji).
=== Identyczność (ścisła) $a === $b Zwraca true, jeśli tablica $a ma te same pary klucz-wartość co tablica $b, w tej samej kolejności i wartości są tego samego typu.
!= lub <> Nierówność (luźna) $a != $b Przeciwieństwo ==.
!== Nieidentyczność (ścisła) $a !== $b Przeciwieństwo ===.
<?php
$tab1 = ["a" => "czerwony", "b" => "zielony"];
$tab2 = ["b" => "niebieski", "c" => "żółty"];
$tab3 = [0 => "jabłko", 1 => "banan"];
$tab4 = [1 => "banan", 0 => "jabłko"]; // Ta sama zawartość, inna kolejność kluczy numerycznych

$unia = $tab1 + $tab2;
print_r($unia);
// Wynik: Array ( [a] => czerwony [b] => zielony [c] => żółty )
// Klucz "b" z $tab1 został zachowany.

echo "<br>";

$arr1 = [1, 2, 3];
$arr2 = ["1", "2", "3"];
$arr3 = [1, 2, 3];
$arr4 = [3, 2, 1]; // Inna kolejność wartości dla kluczy numerycznych

var_dump($arr1 == $arr2);  // bool(true) - luźne porównanie wartości
var_dump($arr1 === $arr2); // bool(false) - typy wartości są różne
var_dump($arr1 === $arr3); // bool(true) - identyczne
var_dump($arr1 == $arr4);  // bool(false) - kolejność ma znaczenie dla kluczy numerycznych

$assoc1 = ["a" => 1, "b" => 2];
$assoc2 = ["b" => 2, "a" => 1]; // Ta sama zawartość, inna kolejność kluczy asocjacyjnych
$assoc3 = ["a" => "1", "b" => "2"];

var_dump($assoc1 == $assoc2);  // bool(true) - kolejność kluczy asocjacyjnych nie ma znaczenia dla ==
var_dump($assoc1 === $assoc2); // bool(false) - kolejność kluczy ma znaczenie dla ===
var_dump($assoc1 == $assoc3);  // bool(true)
var_dump($assoc1 === $assoc3); // bool(false)
?>

Szczegółowe omówienie tablic i operacji na nich znajdzie się w dedykowanej lekcji.

Operator Typu (Type Operator)

PHP posiada jeden operator typu:

<?php
class MojaKlasa {}
class InnaKlasa extends MojaKlasa {}
interface MojInterfejs {}
class KlasaImplementujaca implements MojInterfejs {}

$obj1 = new MojaKlasa();
$obj2 = new InnaKlasa();
$obj3 = new KlasaImplementujaca();
$obj4 = new stdClass(); // Generyczny pusty obiekt

var_dump($obj1 instanceof MojaKlasa);          // bool(true)
var_dump($obj2 instanceof MojaKlasa);          // bool(true) - InnaKlasa dziedziczy po MojaKlasa
var_dump($obj2 instanceof InnaKlasa);          // bool(true)
var_dump($obj1 instanceof InnaKlasa);          // bool(false)
var_dump($obj3 instanceof MojInterfejs);       // bool(true)
var_dump($obj4 instanceof MojaKlasa);          // bool(false)

// Można też używać z nazwami klas jako ciągami znaków (od PHP 5.1.0)
$nazwaKlasy = "MojaKlasa";
var_dump($obj1 instanceof $nazwaKlasy);        // bool(true)
?>

Ten operator jest fundamentalny w programowaniu obiektowym, o którym będziemy mówić w drugiej części kursu.

Operator Koalescencji (Null Coalesce Operator)

Wprowadzony w PHP 7, operator ?? (podwójny znak zapytania) jest bardzo użytecznym skrótem do sprawdzania, czy zmienna istnieje (jest ustawiona) i nie jest NULL. Jeśli tak, zwraca jej wartość; w przeciwnym razie zwraca wartość domyślną (prawy operand).

$wynik = $zmienna_do_sprawdzenia ?? $wartosc_domyslna;

Jest to równoważne z:

$wynik = isset($zmienna_do_sprawdzenia) ? $zmienna_do_sprawdzenia : $wartosc_domyslna;

<?php
// Przykład 1: Zmienna istnieje i nie jest NULL
$uzytkownik = "Jan";
$nazwaDoWyswietlenia = $uzytkownik ?? "Gość";
echo "Nazwa do wyświetlenia: $nazwaDoWyswietlenia <br>"; // Jan

// Przykład 2: Zmienna nie istnieje (lub jest NULL)
// unset($nieistniejacyUzytkownik); // Upewniamy się, że nie istnieje
$nazwaDoWyswietlenia2 = $nieistniejacyUzytkownik ?? "Gość";
echo "Nazwa do wyświetlenia 2: $nazwaDoWyswietlenia2 <br>"; // Gość

// Przykład 3: Zmienna jest ustawiona na NULL
$adres = NULL;
$wyswietlAdres = $adres ?? "Brak adresu";
echo "Adres: $wyswietlAdres <br>"; // Brak adresu

// Można łączyć operatory ??
$domyslnyKolor = "czarny";
$kolorUzytkownika = null; // np. z bazy danych
$kolorProduktu = "czerwony";

$finalnyKolor = $kolorUzytkownika ?? $kolorProduktu ?? $domyslnyKolor;
echo "Finalny kolor: $finalnyKolor <br>"; // czerwony
?>

Jak widzieliśmy wcześniej, od PHP 7.4 istnieje również operator przypisania koalescencyjnego ??=.

Priorytet i Łączność Operatorów

Gdy w jednym wyrażeniu występuje wiele operatorów, PHP musi zdecydować, w jakiej kolejności je wykonać. Określa to priorytet operatorów. Na przykład operator mnożenia (*) ma wyższy priorytet niż operator dodawania (+), więc w wyrażeniu 2 + 3 * 4 najpierw zostanie wykonane mnożenie (3 * 4 = 12), a następnie dodawanie (2 + 12 = 14).

Łączność operatorów określa, jak operatory o tym samym priorytecie są grupowane: od lewej do prawej (łączność lewostronna) lub od prawej do lewej (łączność prawostronna).

Pełna tabela priorytetów i łączności operatorów w PHP jest dostępna w oficjalnej dokumentacji (php.net/manual/pl/language.operators.precedence.php). Nie trzeba jej uczyć się na pamięć, ale warto znać ogólne zasady (np. arytmetyczne przed porównaniem, porównanie przed logicznymi).

Aby uniknąć niejednoznaczności i zapewnić czytelność kodu, zawsze zaleca się używanie nawiasów () do jawnego określania kolejności wykonywania operacji, nawet jeśli priorytet jest oczywisty.

<?php
$wynik1 = 2 + 3 * 4; // Wynik: 14 (mnożenie pierwsze)
$wynik2 = (2 + 3) * 4; // Wynik: 20 (dodawanie pierwsze dzięki nawiasom)

echo "Wynik1: $wynik1, Wynik2: $wynik2 <br>";

$a = 5;
$b = 3;
$c = 2;
// Łączność lewostronna dla -, +
$res = $a - $b + $c; // (5 - 3) + 2 = 2 + 2 = 4
echo "$a - $b + $c = $res <br>";

// Łączność prawostronna dla operatora trójargumentowego i przypisań
$x = true ? 1 : false ? 2 : 3; // Interpretowane jako: true ? 1 : (false ? 2 : 3) => 1
$y = ($x = 10) + 5; // Najpierw przypisanie $x = 10, potem $y = 10 + 5 => 15
var_dump($x, $y);
?>

Podsumowanie Lekcji

W tej lekcji dogłębnie omówiliśmy szeroki wachlarz operatorów dostępnych w PHP. Poznaliśmy operatory arytmetyczne, przypisania (w tym skrócone i koalescencyjne), porównania (luźne i ścisłe), inkrementacji/dekrementacji, logiczne (z uwzględnieniem short-circuit evaluation), bitowe, kontroli błędów, wykonania, ciągu znaków, tablicowe, typu oraz koalescencji NULL. Zrozumieliśmy również znaczenie priorytetu i łączności operatorów oraz jak ważne jest stosowanie nawiasów dla czytelności i jednoznaczności kodu.

Operatory są narzędziami, które pozwalają na manipulowanie danymi i podejmowanie decyzji w programach. Ich efektywne wykorzystanie jest niezbędne do tworzenia złożonej logiki. W następnej lekcji zobaczymy, jak operatory porównania i logiczne współdziałają z instrukcjami warunkowymi (if, else, elseif), aby umożliwić programom podejmowanie decyzji w zależności od spełnienia określonych warunków.


Zadanie praktyczne

Napisz skrypt PHP, który:

  1. Zadeklaruj dwie zmienne numeryczne, np. $liczba1 = 15; i $liczba2 = 4;.
  2. Wykonaj i wyświetl wyniki wszystkich podstawowych operacji arytmetycznych (dodawanie, odejmowanie, mnożenie, dzielenie, modulo, potęgowanie) na tych zmiennych.
  3. Zadeklaruj zmienną $tekstPoczatkowy = "PHP jest ";. Użyj operatora konkatenacji i przypisania (.=), aby dodać do niej słowo "super!". Wyświetl finalny tekst.
  4. Zadeklaruj dwie zmienne, jedną typu int (np. $a = 100;) i drugą typu string przechowującą tę samą liczbę (np. $b = "100";). Sprawdź i wyświetl wyniki porównania ich za pomocą operatorów == oraz ===. Wyjaśnij w komentarzu, dlaczego wyniki są różne (lub takie same).
  5. Zadeklaruj zmienną $wiekUzytkownika. Użyj operatora koalescencji NULL (??), aby przypisać do zmiennej $status wartość "Dorosły", jeśli $wiekUzytkownika jest większy lub równy 18, lub "Niepełnoletni" w przeciwnym razie. Jeśli $wiekUzytkownika nie jest ustawiona, $status powinien przyjąć wartość "Wiek nieznany". (Wskazówka: możesz potrzebować połączyć ?? z operatorem trójargumentowym lub instrukcją if).

Pokaż przykładowe rozwiązanie
<?php
// 1. Zmienne numeryczne
$liczba1 = 15;
$liczba2 = 4;

echo "<h3>Operacje arytmetyczne dla $liczba1 i $liczba2:</h3>";
echo "Dodawanie ($liczba1 + $liczba2): " . ($liczba1 + $liczba2) . "<br>";
echo "Odejmowanie ($liczba1 - $liczba2): " . ($liczba1 - $liczba2) . "<br>";
echo "Mnożenie ($liczba1 * $liczba2): " . ($liczba1 * $liczba2) . "<br>";
echo "Dzielenie ($liczba1 / $liczba2): " . ($liczba1 / $liczba2) . "<br>";
echo "Modulo ($liczba1 % $liczba2): " . ($liczba1 % $liczba2) . "<br>";
echo "Potęgowanie ($liczba1 ** $liczba2): " . ($liczba1 ** $liczba2) . "<br>";

// 2. Konkatenacja i przypisanie
$tekstPoczatkowy = "PHP jest ";
$tekstPoczatkowy .= "super!";
echo "<h3>Konkatenacja:</h3>";
echo $tekstPoczatkowy . "<br>";

// 3. Porównanie == vs ===
$a = 100;       // int
$b = "100";     // string

echo "<h3>Porównanie == vs ===:</h3>";
$wynikLuźny = ($a == $b);
$wynikScisly = ($a === $b);

echo "\$a == \$b : "; var_dump($wynikLuźny); // bool(true)
echo "<br>";
echo "\$a === \$b : "; var_dump($wynikScisly); // bool(false)
echo "<br>";
// Wyjaśnienie: Operator == (luźny) konwertuje typy przed porównaniem, więc string \"100\" jest traktowany jak liczba 100.
// Operator === (ścisły) sprawdza zarówno wartość, jak i typ. Ponieważ \$a jest int, a \$b jest string, wynik jest false.

// 4. Operator koalescencji i status wieku
// $wiekUzytkownika = 20; // Odkomentuj i testuj różne wartości lub brak definicji
// $wiekUzytkownika = 15;
// $wiekUzytkownika = null;

echo "<h3>Status wieku:</h3>";
$status = ($wiekUzytkownika ?? "Wiek nieznany");
if ($status !== "Wiek nieznany") { // Jeśli wiek jest znany, sprawdź czy dorosły
    $status = ($wiekUzytkownika >= 18) ? "Dorosły" : "Niepełnoletni";
}
echo "Status użytkownika: $status <br>";

// Alternatywne, bardziej zwięzłe rozwiązanie dla punktu 4, jeśli $wiekUzytkownika może być nieustawiony:
$status2 = isset($wiekUzytkownika) ? ($wiekUzytkownika >= 18 ? "Dorosły" : "Niepełnoletni") : "Wiek nieznany";
echo "Status użytkownika (alternatywnie): $status2 <br>";

?>
            

Zadanie do samodzielnego wykonania

Napisz skrypt, który symuluje prosty system rabatowy. Zadeklaruj zmienną $cenaProduktu oraz zmienną logiczną $posiadaKarteKlienta. Jeśli klient posiada kartę klienta, przyznaj mu 10% rabatu. Jeśli cena produktu po ewentualnym rabacie jest większa niż 100 zł, przyznaj dodatkowy rabat 5% od aktualnej ceny. Wyświetl cenę początkową, informację o przyznanych rabatach (jeśli były) oraz cenę końcową. Użyj operatorów arytmetycznych, przypisania, porównania i logicznych.


FAQ - Operatory w PHP

Jaka jest różnica między =, ==, a ===?

= to operator przypisania (przypisuje wartość). == to operator porównania równości (luźny, konwertuje typy). === to operator porównania identyczności (ścisły, sprawdza wartość i typ). Zawsze staraj się używać === do porównań, aby uniknąć niejednoznaczności.

Czy mogę używać operatorów inkrementacji/dekrementacji na ciągach znaków?

Tak, PHP pozwala na inkrementację ciągów znaków w specyficzny sposób. Na przykład, "Z"++ da "AA", a "a9"++ da "b0". Działa to na zasadzie "przenoszenia" jak w systemach liczbowych, ale tylko dla liter i cyfr. Nie jest to jednak często używana funkcjonalność.

Kiedy używać and/or zamiast &&/||?

Generalnie zaleca się używanie && i ||, ponieważ mają one wyższy i bardziej intuicyjny priorytet w większości sytuacji, zwłaszcza w instrukcjach if. Operatory and i or mają niższy priorytet, co może być przydatne w niektórych specyficznych konstrukcjach, np. $plik = fopen(...) or die("Błąd otwarcia pliku!");, ale często prowadzi do błędów, jeśli nie użyje się nawiasów przy przypisaniach.

Co to jest operator trójargumentowy (ternary operator)?

Operator trójargumentowy ?: to skrócona forma instrukcji if-else. Ma postać (warunek) ? wartosc_jesli_prawda : wartosc_jesli_falsz;. Jeśli warunek jest prawdziwy, całe wyrażenie przyjmuje wartosc_jesli_prawda, w przeciwnym razie wartosc_jesli_falsz.

Czy operator @ wpływa na wydajność?

Tak, użycie operatora kontroli błędów @ może nieznacznie obniżyć wydajność, ponieważ PHP musi wykonać dodatkową pracę, aby stłumić błędy. Jednak głównym powodem, dla którego należy go unikać, jest utrudnianie debugowania, a nie kwestie wydajnościowe w typowych zastosowaniach.

Jak działa operator + dla tablic? Czy łączy elementy jak array_merge()?

Operator + dla tablic (unia) działa inaczej niż array_merge(). Operator + dodaje elementy z prawej tablicy do lewej, ale jeśli klucze się powtarzają, elementy z lewej tablicy są zachowywane, a te z prawej ignorowane. array_merge() nadpisuje wartości dla kluczy stringowych i dodaje na końcu dla kluczy numerycznych (reindeksując je, jeśli są tylko numeryczne).

Czy mogę tworzyć własne operatory w PHP?

Nie, PHP nie pozwala na definiowanie przez użytkownika własnych operatorów ani na przeciążanie istniejących operatorów w taki sposób, jak to jest możliwe w niektórych innych językach (np. C++). Można jednak emulować pewne zachowania za pomocą metod magicznych w klasach (np. dla operacji na obiektach).