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
<?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";
Output
6
start -> step -> end

Increment and decrement

PHP
<?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";
Output
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

OperatorTrue 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
<?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);
Output
-1 0 1
-1
-1
bool(true)
bool(false)

Logical operators

PHP
<?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
Output
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
<?php

$result = true && false;
var_dump($result);

$result = true and false;    // parsed as ($result = true) and false
var_dump($result);
Output
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
<?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";
Output
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
<?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'));
Output
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
<?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
Output
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
<?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";
Output
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.

Try it yourself · main.php
Edit the code, copy it into main.php, then run it with php main.php (PHP 8.5). How to install PHP
Output
1900: common
2000: leap
2024: leap
2026: common