Operators & Expressions

Reviewed & published by Brayan K

By the end of this lesson you'll be able to do maths, join text, and compare values in PHP — and you'll know the one comparison rule (=== over ==) that prevents a whole category of bugs.

Part of the free PHP course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

What You'll Learn in This Lesson

1️⃣ Arithmetic Operators

An operator is a symbol that acts on values — the values it works on are called its operands. The arithmetic ones do exactly what you'd expect from a calculator: +, -, *, and /. Two are worth a closer look: % (modulus) gives the remainder after dividing — great for "is this number even?" — and ** raises a number to a power. Note that / can return a float (a decimal) even when both inputs are whole numbers.

<?php
// An OPERATOR is a symbol that does something to values (the "operands").
// $a + $b  ->  +  is the operator, $a and $b are the operands.

$a = 15;
$b = 4;

// The six arithmetic operators. Each line builds a string with the . operator
// (more on . in a moment) and echoes the result.
echo "15 + 4  = " . ($a + $b)  . "\n";   // 19    addition
echo "15 - 4  = " . ($a - $b)  . "\n";   // 11    subtraction
echo "15 * 4  = " . ($a * $b)  . "\n";   // 60    multiplication
echo "15 / 4  = " . ($a / $b)  . "\n";   // 3.75  division ALWAYS returns a float here
echo "15 % 4  = " . ($a % $b)  . "\n";   // 3     modulus = the REMAINDER after dividing
echo "15 ** 4 = " . ($a ** $b) . "\n";   // 50625 exponentiation: 15 to the power of 4
?>

2️⃣ Joining Text: . and .=

This is the one that trips up newcomers from other languages: PHP joins strings with the dot (.), not the plus sign. Writing "5" + "3" tries to add them as numbers (giving 8), whereas "5" . "3" glues them into "53". The .= operator is the text version of += — it appends to a string you already have.

<?php
// PHP joins strings with the DOT ( . ), NOT the + sign.
// (Using + on two strings tries to ADD them as numbers — a classic trap.)

$first = "Ada";
$last  = "Lovelace";

// '.' glues pieces together. Note the literal space " " in the middle.
$fullName = $first . " " . $last;
echo $fullName . "\n";          // Ada Lovelace

// '.=' APPENDS to a string in place — like += but for text.
$greeting = "Hello";
$greeting .= ", " . $first;      // adds ", Ada" onto the end
$greeting .= "!";                // adds "!" onto the end
echo $greeting . "\n";          // Hello, Ada!

// Inside "double quotes" PHP also expands variables directly — handy too.
echo "Welcome back, $first.\n"; // Welcome back, Ada.
?>

3️⃣ Assignment & Increment

A single = is assignment — it stores the value on the right into the variable on the left (it is not "equals"; that's ==, coming next). The compound operators (+= -= *= /= %=) do a calculation and reassign in one step, so $x += 5 just means $x = $x + 5. To bump a value by exactly 1, use ++ or -- — and watch the position: $n++ uses the value then adds, while ++$n adds then uses.

<?php
// '=' is ASSIGNMENT: it puts the value on the right into the variable on the left.
// The compound operators below do a calculation AND reassign in one step.

$x = 10;            echo "start:  \$x = $x\n";   // start:  $x = 10

$x += 5;            echo "+= 5:   \$x = $x\n";   // 15   same as $x = $x + 5
$x -= 3;            echo "-= 3:   \$x = $x\n";   // 12   same as $x = $x - 3
$x *= 2;            echo "*= 2:   \$x = $x\n";   // 24   same as $x = $x * 2
$x /= 4;            echo "/= 4:   \$x = $x\n";   // 6    same as $x = $x / 4
$x %= 4;            echo "%= 4:   \$x = $x\n";   // 2    same as $x = $x % 4

// ++ and -- add or subtract exactly 1. POSITION matters:
$n = 5;
echo "\$n++ gives " . ($n++) . ", then \$n is $n\n"; // gives 5, then 6 (use-then-add)
echo "++\$n gives " . (++$n) . ", then \$n is $n\n"; // gives 7, then 7 (add-then-use)
?>

4️⃣ Comparison: == vs === and the Spaceship

Comparison operators ask a yes/no question and hand back a boolean (true or false). The most important rule in this whole lesson lives here. == is loose equality: it converts types first, so 5 == "5" is true. === is strict equality: the value and the type must match, so 5 === "5" is false. Prefer === almost always — loose comparison ("type juggling") is a famous source of bugs. The other operators are != (not equal), <, >, <=, >=, and the spaceship <=>, which returns -1, 0, or 1 for a three-way compare (ideal for sorting).

<?php
// COMPARISON operators ask a yes/no question and return a bool (true / false).
// var_export() prints true/false literally so you can SEE the answer.

// == is LOOSE equality: it converts types first, so surprises happen.
// === is STRICT equality: values AND types must match. Prefer === almost always.
echo "5 == '5'   -> "; var_export(5 == "5");   echo "\n"; // true  (string "5" becomes 5)
echo "5 === '5'  -> "; var_export(5 === "5");  echo "\n"; // false (int vs string)
echo "0 == 'a'   -> "; var_export(0 == "a");   echo "\n"; // false (modern PHP 8+)
echo "1 != 2     -> "; var_export(1 != 2);     echo "\n"; // true  (not equal)
echo "3 <= 3     -> "; var_export(3 <= 3);     echo "\n"; // true  (less than OR equal)

// The SPACESHIP <=> returns -1, 0, or 1 for a three-way compare.
// Perfect for sorting: it tells you "smaller", "equal", or "larger".
echo "1 <=> 5 -> " . (1 <=> 5) . "\n";   // -1  left is smaller
echo "5 <=> 5 -> " . (5 <=> 5) . "\n";   //  0  they are equal
echo "9 <=> 5 -> " . (9 <=> 5) . "\n";   //  1  left is larger
?>

Now you try. The script below is almost complete — fill in each ___ using the 👉 hint, then run it and check it against the Output panel.

<?php
// 🎯 YOUR TURN — fill in each blank marked ___ , then run it.
// Goal: build a full sentence using arithmetic AND string concatenation.

$price = 8;
$qty   = 3;

// 1) Work out the total cost (price times quantity)
$total = $price ___ $qty;          // 👉 replace ___ with the multiply operator ( * )

// 2) Join the pieces into one string with the DOT operator
echo "Total: " ___ $total ___ " dollars\n"; // 👉 replace each ___ with a dot ( . )

// ✅ Expected output:
//    Total: 24 dollars
?>

5️⃣ Logical Operators

Logical operators combine true/false values so you can ask compound questions. && (AND) is true only when both sides are true; || (OR) is true when either side is true; ! (NOT) flips a boolean. PHP also has word forms and / or — they look friendlier but bind looser than =, which causes a classic surprise shown below. The advice is simple: use && and ||.

<?php
// LOGICAL operators combine true/false values into a single yes/no answer.
//   &&  AND  -> true only if BOTH sides are true
//   ||  OR   -> true if EITHER side is true
//   !   NOT  -> flips true to false and back

$loggedIn = true;
$isAdmin  = false;

var_export($loggedIn && $isAdmin); echo "  (&&  both? no)\n";  // false
var_export($loggedIn || $isAdmin); echo "  (||  either? yes)\n"; // true
var_export(!$isAdmin);             echo "  (!   flip false)\n";  // true

// 'and' / 'or' are word versions that do the same logic BUT bind very loosely —
// looser than '='. This line surprises people:
$result = true and false;   // reads as ($result = true) and false  -> $result is TRUE
var_export($result);        echo "  (gotcha: 'and' is looser than '=')\n";

// Use && and || to stay safe. This behaves as expected:
$result = true && false;    // $result is FALSE, as you'd hope
var_export($result);        echo "  (&& binds tighter than '=')\n";
?>

6️⃣ Null Coalescing & Ternary

These operators let you pick a value in one line. ?? (null coalescing) means "use the left value, but if it's null or unset, use the right" — perfect for defaults, and it won't warn on undefined variables. ??= assigns the fallback only when the variable is currently null. The ternary condition ? a : b is a compact if/else: it returns a when the condition is true, otherwise b. The short ternary ?: returns the left value if it's truthy. The key difference: ?? only reacts to null, while ?: reacts to any falsy value ("", 0, false).

<?php
// ?? NULL COALESCING: "use the left value, but if it's null/unset, use the right."
$username = null;
$name = $username ?? "Guest";     // $username is null, so fall back to "Guest"
echo "name: $name\n";            // name: Guest

// ??= assigns the fallback ONLY if the variable is currently null/unset.
$config = null;
$config ??= "default";            // was null -> becomes "default"
echo "config: $config\n";        // config: default

// TERNARY  condition ? valueIfTrue : valueIfFalse  — a one-line if/else.
$age = 20;
$status = ($age >= 18) ? "adult" : "minor";
echo "status: $status\n";        // status: adult

// SHORT TERNARY ?:  returns the left value if it's "truthy", else the right.
// Careful: ?: checks truthiness ("" and 0 count as false); ?? only checks null.
$nickname = "";
echo "nick: " . ($nickname ?: "Anonymous") . "\n"; // "" is falsy -> Anonymous
?>

One more guided exercise. Pick the operator each comment asks for, then run it and compare with the Output panel.

<?php
// 🎯 YOUR TURN — choose the RIGHT comparison operator.
// var_export prints true / false so you can check yourself.

$input = "5";   // came from a form, so it's a STRING "5", not the number 5

// 1) We want this to be FALSE because a string is not the same TYPE as a number.
//    Use the STRICT equality operator.
var_export($input ___ 5);  echo "\n";   // 👉 replace ___ with strict-equal ( === )

// 2) We want a fallback when $nickname is null. Use null coalescing.
$nickname = null;
echo ($nickname ___ "Guest") . "\n";    // 👉 replace ___ with the ?? operator

// ✅ Expected output:
//    false
//    Guest
?>

7️⃣ Operator Precedence

Precedence is the order operators run in — exactly like school maths, where * happens before +. So 2 + 3 * 4 is 14, not 20. Comparisons run before && and ||, which is usually what you want. When in any doubt, add parentheses ( ) — they force your order and make intent obvious to the next reader (often future you).

<?php
// PRECEDENCE is the order operators run in, like maths: * before +.
echo 2 + 3 * 4, "\n";        // 14, NOT 20 — * happens before +

// Parentheses ( ) force your own order and make intent obvious.
echo (2 + 3) * 4, "\n";      // 20 — the addition is forced first

// Comparison runs before && / ||, so this reads naturally:
$score = 85;
$pass = $score >= 50 && $score <= 100;  // (score>=50) && (score<=100)
var_export($pass); echo "\n";          // true

// When unsure, ADD PARENTHESES. They cost nothing and remove all doubt.
$ok = ($score >= 50) && ($score <= 100);
var_export($ok); echo "\n";            // true
?>

Common Errors (and the fix)

Pro Tips

📋 Quick Reference — PHP Operators

OperatorMeaningExample → Result
+ - * / % **Arithmetic15 % 4 → 3
. .=String join / append"Hi " . $name
= += -= *= /=Assign / update$x += 5
++ --Increment / decrement$n++ → adds 1
== ===Loose / strict equal5 === "5" → false
!= < > <= >=Not-equal / ordering3 <= 3 → true
<=>Spaceship (3-way)1 <=> 5 → -1
&& || !Logical and / or / not$a && $b
?? ??=Null coalescing$x ?? "default"
? : ?:Ternary / short ternary$age >= 18 ? "a" : "b"

Mini-Challenge: Shopping Receipt

No code is filled in this time — just a brief and an outline. Write it yourself, run it on onecompiler.com/php or your own machine, then check your result against the expected output in the comments. This is exactly the write-run-check loop you'll use on every real script.

<?php
// 🎯 MINI-CHALLENGE: A tiny shopping receipt.
// No code is filled in — work from the steps below, then run it.
//
// 1. Make $unitPrice = 12 and $quantity = 4
// 2. Calculate $subtotal  (unit price * quantity)
// 3. Calculate $tax = $subtotal * 0.1   (10% tax)
// 4. Calculate $total = $subtotal + $tax
// 5. echo three lines using the . operator, e.g.:
//       "Subtotal: " . $subtotal
//    Use \n at the end of each so they sit on separate lines.
// 6. Add a discount label with SHORT TERNARY:
//       $label = $total > 50 ? "BIG ORDER" : "small order";
//
// ✅ Expected output:
//    Subtotal: 48
//    Tax: 4.8
//    Total: 52.8
//    Label: BIG ORDER

// your code here
?>

🎉 Lesson Complete!

Practice quiz

What does the modulus operator % return?

  • The quotient of a division
  • A power
  • The remainder after dividing
  • A percentage

Answer: The remainder after dividing. % gives the remainder after dividing, so 15 % 4 is 3.

What is the result of 2 + 3 * 4 in PHP?

  • 14
  • 20
  • 24
  • 9

Answer: 14. Multiplication has higher precedence than addition, so 3 * 4 happens first: 2 + 12 = 14.

Which operator joins two strings together in PHP?

  • +
  • &
  • ||
  • .

Answer: .. PHP uses the dot (.) for concatenation; + is arithmetic only.

What does the expression "5" + "3" evaluate to in PHP?

  • The string "53"
  • The number 8
  • An error
  • The string "5 3"

Answer: The number 8. + is maths only, so PHP converts the numeric strings and adds them, giving 8.

What does the spaceship operator return for 1 <=> 5?

  • -1
  • 0
  • 1
  • true

Answer: -1. <=> returns -1 when the left operand is smaller than the right.

Which comparison checks BOTH value and type?

  • ==
  • <=>
  • ===
  • !=

Answer: ===. === is strict equality: the values must match in value and type.

What is the value of $n after: $n = 5; echo $n++;

  • $n stays 5; it prints 6
  • echo prints 5, then $n becomes 6
  • echo prints 6, then $n becomes 5
  • $n becomes 4

Answer: echo prints 5, then $n becomes 6. Post-increment $n++ uses the value (5) first, then adds 1, so $n becomes 6.

What does $name = $username ?? "Guest" do?

  • Always sets $name to "Guest"
  • Compares the two values
  • Concatenates them
  • Uses $username, but "Guest" if $username is null/unset

Answer: Uses $username, but "Guest" if $username is null/unset. ?? is null coalescing: it falls back to the right side only when the left is null or unset.

Why is && preferred over the word 'and' in conditions?

  • 'and' is invalid PHP
  • 'and' binds looser than = and causes surprises
  • 'and' is much slower
  • 'and' only works on numbers

Answer: 'and' binds looser than = and causes surprises. The word 'and' binds looser than =, so $x = true and false sets $x to true unexpectedly.

What does var_export(0 == "a") output in modern PHP 8?

  • true
  • 0
  • false
  • An error

Answer: false. In PHP 8 a non-numeric string is not juggled to 0, so 0 == "a" is false.

Continue this course

Frequently asked questions

What is the difference between == and === in PHP?

== is loose equality: it converts the two values to a common type before comparing, so 5 == "5" is true and (in older PHP) 0 == "" could be true. === is strict equality: the values must match AND be the same type, so 5 === "5" is false because one is an integer and the other a string. Prefer === almost everywhere — it avoids the surprising type-juggling bugs that loose comparison causes.

Why does PHP use a dot (.) to join strings instead of +?

PHP reserves + for arithmetic only, so it picked the dot (.) as the string concatenation operator. This means "5" + "3" gives the number 8 (PHP converts the strings and adds them), while "5" . "3" gives the string "53". If you come from JavaScript or Python where + joins text, this is the single most common mistake — use . for text and += has a text twin called .= .

When should I use ?? versus the short ternary ?: ?

Use ?? (null coalescing) when you only care whether a value is null or unset — $name = $input ?? "Guest" gives "Guest" only when $input is null, and it does not raise a warning for undefined variables. Use ?: (short ternary) when you want the fallback for ANY falsy value, including 0, "", "0", and false. In short: ?? checks for null; ?: checks for truthiness.

What does the spaceship operator <=> do?

The spaceship operator <=> compares two values and returns -1 if the left is smaller, 0 if they are equal, and 1 if the left is larger. It was added in PHP 7 and is mainly used inside sort callbacks: in usort($arr, fn($x, $y) => $x <=> $y) it tells PHP the correct order in a single expression, replacing the older if/elseif comparison code.

Why does my condition behave strangely with 'and' instead of '&&'?

PHP has two AND operators (&& and the word 'and') and two OR operators (|| and 'or'), and they have different precedence. The word versions bind LOOSER than the assignment operator =, so $x = true and false actually runs as ($x = true) and false, leaving $x as true. To avoid this trap, stick to && and || in conditions — they bind tighter than = and behave the way you expect.

Related lessons