Operators
Operators combine values: arithmetic, joining strings, comparing, combining conditions, choosing between values and dealing with null. This lesson collects PHP's operators in one place, including the spaceship operator, the null-safe operator and a precedence trap with and/or.
Assignment operators
= assigns. Compound forms update a variable using its current value — including .= for appending to strings:
<?php $points = 10; $points += 5; // 15 $points -= 3; // 12 $points *= 2; // 24 $points /= 4; // 6 $points **= 2; // 36 $points %= 10; // 6 echo $points, "\n"; $log = "start"; $log .= " -> step"; $log .= " -> end"; echo $log, "\n";
6 start -> step -> end
Increment and decrement
<?php $i = 5; echo $i++, "\n"; // prints 5, then $i becomes 6 echo $i, "\n"; echo ++$i, "\n"; // $i becomes 7, then prints 7 echo $i--, "\n"; // prints 7, then $i becomes 6 echo $i, "\n";
5 6 7 7 6
Prefix (++$i) changes the variable first; postfix ($i++) uses the old value first. On their own line, both just add one.
Comparison operators
| Operator | True when |
|---|---|
=== / !== | Same type and value / not |
== / != | Equal after type juggling / not (lesson 9) |
< > <= >= | Ordering |
<=> | Spaceship: returns -1, 0 or 1 |
The spaceship operator compares two values and returns a negative number, zero, or a positive number — exactly what sorting functions need (lesson 24):
<?php echo 1 <=> 2, " ", 2 <=> 2, " ", 3 <=> 2, "\n"; echo "apple" <=> "banana", "\n"; echo [1, 2] <=> [1, 3], "\n"; var_dump("abc" < "abd"); var_dump(10 < 9.5);
-1 0 1 -1 -1 bool(true) bool(false)
Logical operators
<?php $age = 25; $hasTicket = true; $isVip = false; var_dump($age >= 18 && $hasTicket); var_dump($isVip || $age > 65); var_dump(!$isVip); var_dump($hasTicket xor $isVip); // exactly one is true
bool(true) bool(false) bool(true) bool(true)
&& and || short-circuit: the right side only runs if it's needed. That lets you guard a risky check with a safe one, such as isset($row['price']) && $row['price'] > 0.
The and/or precedence trap
PHP also has and and or keywords. They mean the same as && and || but have lower precedence than assignment, which leads to a classic bug:
<?php $result = true && false; var_dump($result); $result = true and false; // parsed as ($result = true) and false var_dump($result);
bool(false) bool(true)
Use && and || in conditions and assignments. (The or keyword survives in old idioms like $file = fopen(...) or die(...).)
Choosing a value
The ternary operator condition ? a : b picks one of two values. The short form a ?: b (the "Elvis" operator) returns a if it's truthy, otherwise b:
<?php $stock = 0; echo $stock > 0 ? "In stock" : "Sold out", "\n"; $n = 3; echo "$n item" . ($n === 1 ? "" : "s") . "\n"; $username = ""; echo $username ?: "Guest", "\n";
Sold out 3 items Guest
Don't nest ternaries without parentheses — PHP 8 makes unparenthesised nesting a compile error because it was so often misread. Use match (lesson 13) for several choices.
Null operators
?? returns its left side unless it's null or undefined; ??= assigns only in that case. The nullsafe operator ?-> calls a method or reads a property only if the object isn't null; otherwise the whole chain is null:
<?php $settings = ['theme' => 'dark']; echo $settings['language'] ?? 'en', "\n"; // missing key: no warning echo $settings['theme'] ?? 'light', "\n"; $settings['fontSize'] ??= 14; echo $settings['fontSize'], "\n"; $date = null; var_dump($date?->format('Y')); $date = new DateTimeImmutable('2026-10-08'); var_dump($date?->format('Y'));
en dark 14 NULL string(4) "2026"
Note the difference from ?:: ?? only replaces null, so 0 ?? 5 is 0, while 0 ?: 5 is 5.
Precedence
When operators mix, higher-precedence ones run first: **, then * / %, then + -, then . … comparison, &&, ||, ??, ternary, assignment. When in doubt, add parentheses:
<?php echo 2 + 3 * 4, "\n"; echo (2 + 3) * 4, "\n"; echo -2 ** 2, "\n"; // -(2 ** 2) echo "Total: " . 2 + 3, "\n"; // + binds tighter than . in PHP 8
14 20 -4 Total: 5
Since PHP 8, arithmetic binds more tightly than ., so "Total: " . 2 + 3 means "Total: " . (2 + 3). Parentheses make the intent obvious either way.
Bitwise operators
&, |, ^, ~, << and >> work on the bits of integers. You'll see them with flag constants such as E_ALL & ~E_NOTICE or JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES:
<?php $read = 0b001; $write = 0b010; $exec = 0b100; $perms = $read | $write; echo decbin($perms), "\n"; var_dump(($perms & $write) !== 0); var_dump(($perms & $exec) !== 0); echo 1 << 10, "\n";
11 bool(true) bool(false) 1024
Try it yourself
A year is a leap year if it's divisible by 4, except century years, which must be divisible by 400.
1900: common 2000: leap 2024: leap 2026: common