PHP Operators

Every operator PHP supports - arithmetic, comparison, logical, ternary, null coalescing, spaceship, nullsafe, bitwise - explained with practical examples.

Beginner 9 min read 15 examples

Arithmetic Operators

OperatorNameExampleResult
+Addition5 + 38
-Subtraction5 - 32
*Multiplication5 * 315
/Division10 / 42.5
%Modulus10 % 31
**Exponentiation2 ** 101024
intdiv()Integer divisionintdiv(10, 3)3

Assignment Operators

PHP
<?php
$x  = 10;       // assign
$x += 5;        // $x = $x + 5  -> 15
$x -= 3;        // 12
$x *= 2;        // 24
$x /= 4;        // 6
$x %= 4;        // 2
$x **= 3;       // 8

$s  = "Hello";
$s .= " World"; // string concatenation -> "Hello World"

$a = ["a"];
$a += ["b", "c"]; // array union (keeps existing keys)

// Null coalescing assignment (PHP 7.4+)
$opts["timeout"] ??= 30;   // only sets if currently null/missing

Comparison Operators

OperatorNameNotes
==Equal (loose)Type juggles - avoid
===Identical (strict)Type AND value match - prefer this
!= or <>Not equal (loose)Avoid
!==Not identical (strict)Prefer this
< <= > >=Less/greater thanWorks on numbers and strings
<=>Spaceship-1, 0, or 1 - perfect for sorting
PHP
<?php
var_dump(5 == "5");      // true (loose)
var_dump(5 === "5");     // false (types differ)
var_dump(0 == "");       // PHP 7: true, PHP 8: false
var_dump(null == false); // true (loose)
var_dump(null === false);// false

Logical Operators

OperatorResult
$a && $btrue if both true
$a || $btrue if either true
!$atrue if $a is false
$a xor $btrue if exactly one is true
$a and $bSame as && but lower precedence (avoid)
$a or $bSame as || but lower precedence (avoid)
Always use && and ||

The word forms and and or have lower precedence than =. $x = true and false assigns true (then ignores the and false). Use && / || always.

String Operators

PHP
<?php
$a = "Hello";
$b = "World";

// . concatenation
echo $a . " " . $b;       // Hello World

// .= concat-assign
$a .= "!";                // $a is now "Hello!"

// Interpolation (double quotes only)
echo "Greeting: $a";

// Heredoc - alternative for big strings
$html = <<<HTML
<p>$a $b</p>
HTML;

Increment / Decrement

PHP
<?php
$n = 5;
echo ++$n;     // 6  (pre-increment: bump, then return)
echo $n++;     // 6  (post-increment: return, then bump) - now $n is 7
echo --$n;     // 6
echo $n--;     // 6  - now $n is 5

// Works on letters too
$char = "a";
$char++;       // "b"
$char = "z";
$char++;       // "aa"

Ternary & Null Coalescing

PHP
<?php
// Standard ternary
$age = 17;
$type = $age >= 18 ? "Adult" : "Minor";

// Short ternary (Elvis) - returns left if truthy, else right
$name = $input ?: "Anonymous";   // ?: instead of ? $input :

// Null coalescing ?? (PHP 7.0+)
$lang = $_GET["lang"] ?? "en";   // no warning if key missing

// Chained
$value = $a ?? $b ?? $c ?? "default";

// Null coalescing assignment ??= (PHP 7.4+)
$config["debug"] ??= false;      // sets only if null/missing

Spaceship Operator <=>

Returns -1, 0, or 1 depending on whether the left is less than, equal to, or greater than the right. Designed for usort().

PHP
<?php
echo 1 <=> 2;       // -1
echo 1 <=> 1;       //  0
echo 2 <=> 1;       //  1

// Sort an array of users by age
$users = [["age" => 30], ["age" => 22], ["age" => 45]];
usort($users, fn($a, $b) => $a["age"] <=> $b["age"]);
// Result: 22, 30, 45

// Multi-key sort
usort($users, fn($a, $b) =>
    [$a["lastName"], $a["firstName"]]
    <=>
    [$b["lastName"], $b["firstName"]]
);

Nullsafe ?-> (PHP 8.0+)

Chain method/property calls without crashing if any link is null:

PHP
<?php
// Old way - verbose
$country = null;
if ($user !== null) {
    $address = $user->getAddress();
    if ($address !== null) {
        $country = $address->getCountry();
    }
}

// New way (PHP 8.0+)
$country = $user?->getAddress()?->getCountry();
// Short-circuits to null at first null link

Bitwise Operators

OperatorName
&AND
|OR
^XOR
~NOT
<<Left shift
>>Right shift
PHP
<?php
// Permission flags (classic bitmask pattern)
const READ   = 1;   // 0001
const WRITE  = 2;   // 0010
const DELETE = 4;   // 0100

$perms = READ | WRITE;       // 0011 = 3

// Check a flag
if ($perms & WRITE) echo "Can write";

// Remove a flag
$perms &= ~WRITE;            // 0001

Operator Precedence

Some operators bind tighter than others. Add parentheses whenever you're unsure - they cost nothing and make intent obvious.

PHP
<?php
// Without parens - relies on precedence
$result = 2 + 3 * 4;          // 14, not 20

// With parens - intent obvious
$result = ($price * $quantity) + $tax;

// Common gotcha: assignment vs comparison
if (($user = findUser($id)) !== null) {
    // ok - explicit
}

The full precedence table is in the PHP manual - but you rarely need to memorize it if you use parentheses.

Next Steps

Frequently Asked Questions

Always prefer === (strict equality). It compares value and type. == performs type juggling which produces surprising results: "abc" == 0 was true in PHP 7. PHP 8 fixed that case but you still get 0 == "0abc" being true.

Null coalescing - returns the left operand if it's set and not null, otherwise the right. $user['name'] ?? 'Guest' safely reads a maybe-missing key with no warning.

Inside usort() comparators. It returns -1, 0, or 1 - exactly what usort expects. usort($arr, fn($a, $b) => $a->age <=> $b->age); sorts by age ascending.

They behave the same but have different precedence. && binds tighter than =, so $ok = true && false sets $ok to false. $ok = true and false sets it to true because = happens first. Always use && and || to avoid surprises.

Yes. $user?->profile?->avatar?->url returns null as soon as anything in the chain is null. No exception, no warning. Available since PHP 8.0.