Beginner

Operators in PHP

Explore PHP arithmetic, comparison, logical, string, bitwise, and assignment operators with runnable Docker examples.

Operators are the verbs of a programming language — they transform values, compare them, and combine them into expressions. PHP inherits much of its operator syntax from C and Perl, but layers on a few quirks of its own: a dedicated string concatenation operator, two flavors of equality (loose and strict), and modern additions like the null coalescing (??) and spaceship (<=>) operators.

Because PHP is dynamically and weakly typed, operators often perform implicit conversions between strings, integers, floats, and booleans. PHP 8 tightened many of these rules — particularly comparisons between numbers and non-numeric strings — so the language now behaves much more predictably than its earlier reputation suggests. Understanding which operator triggers which conversion is the key to writing PHP that does what you mean.

This tutorial walks through every major operator category in PHP, with a single runnable example you can execute in Docker. Along the way, we’ll highlight the operators that distinguish PHP from C-family languages: . for concatenation, ?? for null fallback, <=> for three-way comparison, and the difference between == and ===.

A Tour of PHP Operators

The program below exercises arithmetic, string, comparison, logical, assignment, and bitwise operators in turn. Each section prints labeled output so you can match every expression to its result.

Create a file named operators.php:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
<?php
// --- Arithmetic operators ---
$a = 17;
$b = 5;
echo "Arithmetic ($a, $b):\n";
echo "  a + b      = " . ($a + $b) . "\n";
echo "  a - b      = " . ($a - $b) . "\n";
echo "  a * b      = " . ($a * $b) . "\n";
echo "  a / b      = " . ($a / $b) . "\n";
echo "  a % b      = " . ($a % $b) . "\n";
echo "  intdiv     = " . intdiv($a, $b) . "\n";
echo "  a ** b     = " . ($a ** $b) . "\n";

// --- String operators ---
echo "\nString operators:\n";
$greet = "Hello";
$name  = "PHP";
$msg   = $greet . ", " . $name . "!";
echo "  concat (.) : $msg\n";
$msg .= " Welcome.";
echo "  append(.=) : $msg\n";

// --- Comparison operators ---
echo "\nComparison (loose vs strict):\n";
echo "  0 == \"0\"   : "; var_export(0 == "0");   echo "\n";
echo "  0 == \"\"    : "; var_export(0 == "");    echo "\n";
echo "  \"1\" ==  1  : "; var_export("1" == 1);   echo "\n";
echo "  \"1\" === 1  : "; var_export("1" === 1);  echo "\n";
echo "  1 <=> 2    : " . (1 <=> 2) . "\n";
echo "  2 <=> 2    : " . (2 <=> 2) . "\n";
echo "  3 <=> 2    : " . (3 <=> 2) . "\n";

// --- Logical operators ---
echo "\nLogical:\n";
$t = true;
$f = false;
echo "  t && f     : "; var_export($t && $f);  echo "\n";
echo "  t || f     : "; var_export($t || $f);  echo "\n";
echo "  !t         : "; var_export(!$t);       echo "\n";
echo "  t xor f    : "; var_export($t xor $f); echo "\n";

// --- Null coalescing, ternary, Elvis ---
echo "\nNull coalescing and ternary:\n";
$config = ['debug' => false];
$mode   = $config['mode']  ?? 'production';
$short  = $config['debug'] ?: 'default';
$age    = 20;
$status = ($age >= 18) ? 'adult' : 'minor';
echo "  mode   (??)  : $mode\n";
echo "  short  (?:)  : $short\n";
echo "  status (?:)  : $status\n";

// --- Assignment shortcuts ---
echo "\nAssignment shortcuts (start n = 10):\n";
$n = 10;
$n += 5;  echo "  n += 5   -> $n\n";
$n -= 3;  echo "  n -= 3   -> $n\n";
$n *= 2;  echo "  n *= 2   -> $n\n";
$n /= 4;  echo "  n /= 4   -> $n\n";
$n **= 2; echo "  n **= 2  -> $n\n";
$n %= 7;  echo "  n %= 7   -> $n\n";

// --- Bitwise operators ---
echo "\nBitwise (5 = 0b0101, 3 = 0b0011):\n";
echo "  5 & 3      = " . (5 & 3)   . "\n";
echo "  5 | 3      = " . (5 | 3)   . "\n";
echo "  5 ^ 3      = " . (5 ^ 3)   . "\n";
echo "  ~5         = " . (~5)      . "\n";
echo "  5 << 2     = " . (5 << 2)  . "\n";
echo "  20 >> 1    = " . (20 >> 1) . "\n";

What’s happening here

  • . and .= — PHP uses the dot for string concatenation, because + is reserved for numeric addition. $msg .= " Welcome." appends in place.
  • ** — Exponentiation. 17 ** 5 evaluates to 1419857.
  • intdiv($a, $b) — Integer division. / always returns a float when the result isn’t a whole number; use intdiv (or cast) when you need a truncated integer.
  • == vs === — Loose equality (==) allows type juggling; strict equality (===) requires both type and value to match. Since PHP 8, comparing a number to a non-numeric string converts the number to a string rather than the other way around, so 0 == "" is now false.
  • <=> — The spaceship operator returns -1, 0, or 1. It is most useful in usort() callbacks.
  • ?? vs ?: — Null coalescing returns the right side only when the left is null or undefined. The Elvis operator (?:) returns the right side for any falsy value (false, 0, "", null, []).

Running with Docker

1
2
3
4
5
# Pull the official image
docker pull php:8.4-cli-alpine

# Run the operators example
docker run --rm -v $(pwd):/app -w /app php:8.4-cli-alpine php operators.php

Expected Output

Arithmetic (17, 5):
  a + b      = 22
  a - b      = 12
  a * b      = 85
  a / b      = 3.4
  a % b      = 2
  intdiv     = 3
  a ** b     = 1419857

String operators:
  concat (.) : Hello, PHP!
  append(.=) : Hello, PHP! Welcome.

Comparison (loose vs strict):
  0 == "0"   : true
  0 == ""    : false
  "1" ==  1  : true
  "1" === 1  : false
  1 <=> 2    : -1
  2 <=> 2    : 0
  3 <=> 2    : 1

Logical:
  t && f     : false
  t || f     : true
  !t         : false
  t xor f    : true

Null coalescing and ternary:
  mode   (??)  : production
  short  (?:)  : default
  status (?:)  : adult

Assignment shortcuts (start n = 10):
  n += 5   -> 15
  n -= 3   -> 12
  n *= 2   -> 24
  n /= 4   -> 6
  n **= 2  -> 36
  n %= 7   -> 1

Bitwise (5 = 0b0101, 3 = 0b0011):
  5 & 3      = 1
  5 | 3      = 7
  5 ^ 3      = 6
  ~5         = -6
  5 << 2     = 20
  20 >> 1    = 10

Operator Precedence

PHP evaluates operators in a defined precedence order, similar to most C-family languages. A few highlights:

  1. ** (right-associative) — exponentiation binds tightest among arithmetic ops.
  2. *, /, % — multiplication, division, modulus.
  3. +, - — addition and subtraction.
  4. . — string concatenation (lowered below +/- in PHP 8).
  5. <, <=, >, >= — relational comparisons.
  6. ==, !=, ===, !==, <=> — equality.
  7. &&, || — short-circuit logical operators (higher precedence than and, or).
  8. ? :, ?? — ternary and null coalescing.
  9. =, +=, -=, etc. — assignment is near the bottom.

A subtle gotcha: and/or/xor exist as alternatives to &&/||/xor, but they have lower precedence than =, which means $x = true and false assigns true to $x and then evaluates and false separately. Prefer && and || unless you have a specific reason otherwise.

Create a file named precedence.php:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<?php
// ** is right-associative: 2 ** 3 ** 2 == 2 ** (3 ** 2) == 2 ** 9
echo "2 ** 3 ** 2 = " . (2 ** 3 ** 2) . "\n";

// . and + share precedence; left-to-right
echo "1 + 2 . '+' . 3 + 4 = " . (1 + 2 . '+' . 3 + 4) . "\n";

// && binds tighter than = ; `and` does not
$x = true && false;   // $x = (true && false) -> false
$y = true and false;  // ($y = true) and false -> $y is true
echo "x = "; var_export($x); echo "\n";
echo "y = "; var_export($y); echo "\n";

Run it the same way:

1
docker run --rm -v $(pwd):/app -w /app php:8.4-cli-alpine php precedence.php

Expected Output

2 ** 3 ** 2 = 512
1 + 2 . '+' . 3 + 4 = 3+7
x = false
y = true

The 1 + 2 . '+' . 3 + 4 result is "3+7" because — as of PHP 8 — the . operator has lower precedence than + and -. The arithmetic groups first: (1 + 2) . '+' . (3 + 4), producing "3" . "+" . "7", which concatenates to "3+7". (Rewrite expressions like this with explicit parentheses regardless — clarity beats relying on memorized precedence.)

Key Concepts

  • String concatenation uses . — PHP reserves + for numeric addition; mixing strings and + either fails or coerces unexpectedly.
  • Two equalities — Use === and !== by default; reserve == for cases where type juggling is genuinely what you want.
  • The spaceship operator (<=>) returns -1, 0, or 1 and is purpose-built for comparator callbacks like usort().
  • ?? falls back on null; ?: falls back on any falsy value. They are not interchangeable.
  • PHP 8 fixed loose comparison surprises — Comparing a number to a non-numeric string no longer silently coerces the string to 0.
  • / always returns a float when results aren’t whole. Use intdiv() for integer division.
  • ** is right-associative2 ** 3 ** 2 is 2 ** 9, not 8 ** 2.
  • Prefer &&/|| over and/or — The lower-precedence keyword forms interact badly with assignment.

Running Today

All examples can be run using Docker:

docker pull php:8.4-cli-alpine
Last updated:

Comments

Loading comments...

Leave a Comment

2000 characters remaining