โ๏ธ Lesson 4: Operators & Expressions
Operators are the verbs of programming โ they do things to your data. In this lesson, you'll learn every operator you need to write meaningful PHP programs.
๐ฏ Learning Objectives
By the end of this lesson, you will be able to:
- Perform arithmetic, string, and assignment operations
- Use comparison operators (including the difference between
==and===) - Combine conditions with logical operators
- Write concise code with the ternary and null coalescing operators
- Predict evaluation order using operator precedence rules
Estimated Time: 40 minutes
Prerequisites: Lesson 3 (variables, data types)
๐ In This Lesson
What Are Operators and Expressions?
๐ Definitions
Operator: A symbol that performs an operation on one or more values (called operands).
Expression: Any combination of values, variables, operators, and function calls that PHP can evaluate to produce a result.
<?php
// operand operator operand
// โ โ โ
$result = 10 + 5; // Expression: 10 + 5 evaluates to 15
// Everything that produces a value is an expression:
$x = 42; // 42 is an expression
$y = $x * 2; // $x * 2 is an expression
$z = strlen("hello"); // strlen("hello") is an expression
PHP groups operators into several categories. Let's work through each one.
Arithmetic Operators
These work exactly like math class โ with a couple of programming-specific additions.
| Operator | Name | Example | Result |
|---|---|---|---|
+ |
Addition | 10 + 3 |
13 |
- |
Subtraction | 10 - 3 |
7 |
* |
Multiplication | 10 * 3 |
30 |
/ |
Division | 10 / 3 |
3.3333... |
% |
Modulo (remainder) | 10 % 3 |
1 |
** |
Exponentiation | 2 ** 8 |
256 |
- |
Negation (unary) | -$x |
Negative of $x |
<?php
$a = 17;
$b = 5;
echo $a + $b; // 22
echo $a - $b; // 12
echo $a * $b; // 85
echo $a / $b; // 3.4
echo $a % $b; // 2 (17 divided by 5 = 3 remainder 2)
echo $a ** $b; // 1419857 (17 to the power of 5)
Division Details
<?php
// Division of two integers that divides evenly โ integer result
var_dump(10 / 2); // int(5)
// Division that doesn't divide evenly โ float result
var_dump(10 / 3); // float(3.3333...)
// Integer division (discard remainder) โ use intdiv()
var_dump(intdiv(10, 3)); // int(3)
โ ๏ธ Division by Zero
Dividing by zero throws a DivisionByZeroError in PHP 8. Always check before dividing:
$denominator = 0;
if ($denominator !== 0) {
echo 100 / $denominator;
} else {
echo "Cannot divide by zero!";
}
Common Uses for Modulo (%)
Modulo is more useful than it first appears:
<?php
// Check if a number is even or odd
$num = 42;
if ($num % 2 === 0) {
echo "$num is even"; // This runs
}
// Cycle through values (e.g., alternating row colors)
for ($i = 0; $i < 6; $i++) {
$class = ($i % 2 === 0) ? "even-row" : "odd-row";
echo "<tr class='$class'>Row $i</tr>\n";
}
// Check if a number is divisible by another
$year = 2024;
$isLeapYear = ($year % 4 === 0 && $year % 100 !== 0) || ($year % 400 === 0);
// true โ 2024 is a leap year
Increment and Decrement
<?php
$count = 10;
// Pre-increment: increment, THEN return the value
echo ++$count; // 11 (increments first, then outputs 11)
// Post-increment: return the value, THEN increment
$count = 10;
echo $count++; // 10 (outputs 10, then increments to 11)
echo $count; // 11
// Same idea for decrement
$count = 10;
echo --$count; // 9 (pre-decrement)
$count = 10;
echo $count--; // 10 (post-decrement, $count is now 9)
โ Practical Rule
If you just need to add or subtract 1 and don't care about the return value, $count++ and ++$count do the same thing. The distinction only matters when you use the result inline (like in an echo or array index).
Assignment Operators
The basic assignment operator is =. PHP also has compound assignment operators that combine an operation with assignment:
| Operator | Example | Equivalent To | Description |
|---|---|---|---|
= |
$x = 5 |
โ | Assign |
+= |
$x += 3 |
$x = $x + 3 |
Add and assign |
-= |
$x -= 3 |
$x = $x - 3 |
Subtract and assign |
*= |
$x *= 3 |
$x = $x * 3 |
Multiply and assign |
/= |
$x /= 3 |
$x = $x / 3 |
Divide and assign |
%= |
$x %= 3 |
$x = $x % 3 |
Modulo and assign |
**= |
$x **= 2 |
$x = $x ** 2 |
Exponent and assign |
.= |
$s .= " world" |
$s = $s . " world" |
Concatenate and assign |
??= |
$x ??= 10 |
$x = $x ?? 10 |
Assign if null |
<?php
$score = 100;
$score += 25; // $score is now 125
$score -= 10; // $score is now 115
$score *= 2; // $score is now 230
$message = "Hello";
$message .= ", "; // "Hello, "
$message .= "World!"; // "Hello, World!"
echo $message; // Hello, World!
echo $score; // 230
๐ก Tip: The .= (concatenate-assign) operator is incredibly common in PHP โ you'll use it anytime you build up a string piece by piece, like constructing HTML output.
String Operators
PHP has just two string operators, but you'll use them constantly:
| Operator | Name | Example | Result |
|---|---|---|---|
. |
Concatenation | "Hello" . " " . "World" |
"Hello World" |
.= |
Concatenation assignment | $s .= "!" |
Appends to $s |
<?php
$firstName = "Alice";
$lastName = "Johnson";
// Concatenation โ joining strings together
$fullName = $firstName . " " . $lastName;
echo $fullName; // Alice Johnson
// Building HTML with concatenation
$html = "<div class='user-card'>";
$html .= "<h3>" . $fullName . "</h3>";
$html .= "<p>Member since 2024</p>";
$html .= "</div>";
echo $html;
โ ๏ธ The + Operator Does NOT Concatenate Strings
Unlike JavaScript, PHP's + is always arithmetic. If you use + on strings, PHP type-juggles them to numbers:
echo "Hello" + " World"; // 0 (both become 0, then 0 + 0 = 0)
echo "5" + "3"; // 8 (not "53"!)
echo "5" . "3"; // "53" (concatenation โ this is what you want)
Comparison Operators
Comparison operators compare two values and return a boolean (true or false). They're essential for making decisions in your code.
| Operator | Name | Example | Result |
|---|---|---|---|
== |
Equal (loose) | "5" == 5 |
true |
=== |
Identical (strict) | "5" === 5 |
false |
!= |
Not equal (loose) | "5" != 5 |
false |
!== |
Not identical (strict) | "5" !== 5 |
true |
< |
Less than | 3 < 5 |
true |
> |
Greater than | 3 > 5 |
false |
<= |
Less than or equal | 5 <= 5 |
true |
>= |
Greater than or equal | 5 >= 3 |
true |
<=> |
Spaceship | 1 <=> 2 |
-1 |
== vs. === (Revisited)
We covered this in Lesson 3, but it's worth repeating because it's that important:
<?php
// Loose comparison (==) โ type juggling happens
var_dump(1 == "1"); // true โ string "1" becomes int 1
var_dump(0 == ""); // true โ both become 0/false
var_dump(null == false); // true โ both are falsy
var_dump("" == false); // true โ empty string is falsy
// Strict comparison (===) โ no type juggling
var_dump(1 === "1"); // false โ int vs string
var_dump(0 === ""); // false โ int vs string
var_dump(null === false); // false โ null vs bool
var_dump("" === false); // false โ string vs bool
โ Rule of Thumb
Default to === and !==. Only use == when you specifically want type juggling โ and even then, think twice.
The Spaceship Operator (<=>)
The spaceship operator (PHP 7+) performs a three-way comparison. It returns -1, 0, or 1:
<?php
echo 1 <=> 2; // -1 (left is less)
echo 2 <=> 2; // 0 (equal)
echo 3 <=> 2; // 1 (left is greater)
// Most useful for sorting
$prices = [29.99, 9.99, 49.99, 19.99];
usort($prices, function($a, $b) {
return $a <=> $b; // Sort ascending
});
// $prices is now [9.99, 19.99, 29.99, 49.99]
// For descending, swap $a and $b:
usort($prices, function($a, $b) {
return $b <=> $a; // Sort descending
});
Logical Operators
Logical operators combine boolean expressions. They're the glue that holds complex conditions together.
| Operator | Name | Example | Result |
|---|---|---|---|
&& |
AND | true && false |
false |
|| |
OR | true || false |
true |
! |
NOT | !true |
false |
and |
AND (low precedence) | true and false |
false |
or |
OR (low precedence) | true or false |
true |
xor |
XOR (exclusive or) | true xor true |
false |
Truth Tables
Practical Examples
<?php
$age = 25;
$hasID = true;
$isMember = false;
// AND โ both conditions must be true
if ($age >= 21 && $hasID) {
echo "Entry allowed"; // This runs
}
// OR โ at least one condition must be true
if ($isMember || $age >= 18) {
echo "Can register"; // This runs (age condition is true)
}
// NOT โ inverts the boolean
if (!$isMember) {
echo "Not a member yet"; // This runs
}
// Combining operators
$isAdmin = false;
$isEditor = true;
$isPublished = true;
if (($isAdmin || $isEditor) && $isPublished) {
echo "Can view article"; // This runs
}
Short-Circuit Evaluation
PHP's logical operators are short-circuit โ they stop evaluating as soon as the result is determined:
<?php
// AND (&&): if the left side is false, PHP skips the right side
$x = false;
$result = $x && expensiveFunction(); // expensiveFunction() NEVER runs
// OR (||): if the left side is true, PHP skips the right side
$x = true;
$result = $x || expensiveFunction(); // expensiveFunction() NEVER runs
// This is useful for guard clauses:
if (isset($user) && $user->isActive()) {
// $user->isActive() only runs if $user exists
// Without short-circuiting, this could crash
}
โ ๏ธ and/or vs. &&/||
The word forms (and, or) have lower precedence than the symbol forms (&&, ||). This can cause surprising behavior:
// These are NOT the same:
$result = true || false && false;
// Evaluates as: true || (false && false) โ true
$result = true or false && false;
// Evaluates as: ($result = true) or (false && false)
// $result is true, but for the wrong reason!
// Best practice: always use && and ||
The Ternary Operator
The ternary operator is a compact way to write simple if/else statements. It's called "ternary" because it takes three operands.
Basic Syntax
$result = (condition) ? valueIfTrue : valueIfFalse;
<?php
$age = 20;
// Instead of this:
if ($age >= 18) {
$status = "adult";
} else {
$status = "minor";
}
// You can write this:
$status = ($age >= 18) ? "adult" : "minor";
echo $status; // "adult"
Common Use Cases
<?php
// Setting defaults
$name = isset($_GET['name']) ? $_GET['name'] : "Guest";
// Conditional CSS classes
$rowClass = ($index % 2 === 0) ? "even" : "odd";
echo "<tr class='$rowClass'>";
// Pluralization
$count = 5;
$label = ($count === 1) ? "item" : "items";
echo "$count $label"; // "5 items"
// Display logic
$score = 85;
$grade = ($score >= 90) ? "A" : (($score >= 80) ? "B" : "C");
echo $grade; // "B"
โ ๏ธ Don't Nest Too Deep
Nested ternaries become unreadable fast. If you need more than one level of nesting, use an if/elseif block instead. PHP 8 actually requires parentheses for nested ternaries to prevent confusion.
// BAD โ hard to read
$result = $a ? $b ? "both" : "only a" : "neither";
// GOOD โ use if/elseif for complex logic
if ($a && $b) {
$result = "both";
} elseif ($a) {
$result = "only a";
} else {
$result = "neither";
}
Short Ternary (Elvis Operator)
PHP has a shorthand when you want to use the tested value itself as the "true" result:
<?php
// Normal ternary
$display = $username ? $username : "Anonymous";
// Short ternary (Elvis operator) โ same result
$display = $username ?: "Anonymous";
// If $username is truthy, use it; otherwise use "Anonymous"
The ?: is called the "Elvis operator" because if you tilt your head, the characters look like Elvis's hair. Really.
The Null Coalescing Operator
The null coalescing operator (??) was introduced in PHP 7 and quickly became one of the most-loved features. It provides a default value when something is null or undefined.
Basic Syntax
$result = $value ?? $default;
Translation: "Use $value if it exists and isn't null; otherwise, use $default."
<?php
// Before PHP 7 โ verbose
$name = isset($_GET['name']) ? $_GET['name'] : "Guest";
// PHP 7+ โ clean and concise
$name = $_GET['name'] ?? "Guest";
// They do the same thing, but ?? is much cleaner
Chaining
You can chain multiple ?? operators โ PHP uses the first non-null value it finds:
<?php
// Try multiple sources, use the first non-null value
$color = $_GET['color'] ?? $_SESSION['color'] ?? $defaultColor ?? "blue";
// PHP checks each from left to right:
// 1. Is $_GET['color'] set and not null? If yes, use it.
// 2. Is $_SESSION['color'] set and not null? If yes, use it.
// 3. Is $defaultColor set and not null? If yes, use it.
// 4. Fall back to "blue"
Null Coalescing Assignment (??=)
<?php
// Only assign if the variable is null or undefined
$settings['theme'] ??= "light";
// Equivalent to:
if (!isset($settings['theme'])) {
$settings['theme'] = "light";
}
// Useful for setting defaults:
$config['timeout'] ??= 30;
$config['retries'] ??= 3;
$config['debug'] ??= false;
?? vs. ?:
These look similar but behave differently:
| Operator | Returns default when value is... | Example |
|---|---|---|
?? |
null or undefined only |
0 ?? "default" โ 0 |
?: |
Any falsy value (0, "", false, null, etc.) | 0 ?: "default" โ "default" |
<?php
$count = 0;
echo $count ?? "no data"; // 0 โ ?? only checks for null
echo $count ?: "no data"; // "no data" โ ?: treats 0 as falsy
// ?? is usually what you want for form/API data
// ?: is useful when you want to reject all falsy values
Operator Precedence
When an expression has multiple operators, PHP evaluates them in a specific order โ just like math class where multiplication happens before addition.
Precedence Table (Highest to Lowest)
| Priority | Operators | Description |
|---|---|---|
| 1 (highest) | ** |
Exponentiation |
| 2 | ++ -- ! (type) |
Increment, decrement, not, casting |
| 3 | * / % |
Multiplication, division, modulo |
| 4 | + - . |
Addition, subtraction, concatenation |
| 5 | < <= > >= |
Comparison |
| 6 | == != === !== <=> |
Equality |
| 7 | && |
Logical AND |
| 8 | || |
Logical OR |
| 9 | ?? |
Null coalescing |
| 10 | ?: |
Ternary |
| 11 | = += -= .= etc. |
Assignment |
| 12 (lowest) | and or xor |
Logical (word form) |
Examples
<?php
// Multiplication before addition (like math)
echo 2 + 3 * 4; // 14, not 20
echo (2 + 3) * 4; // 20 โ parentheses override precedence
// && before ||
$result = true || false && false;
// Evaluates as: true || (false && false) โ true
// Comparison before logical
$result = 5 > 3 && 10 < 20;
// Evaluates as: (5 > 3) && (10 < 20) โ true && true โ true
โ The Golden Rule
When in doubt, use parentheses. They make your intent explicit and prevent bugs. Nobody will complain about "too many parentheses" โ they'll complain about code that's hard to understand.
// Ambiguous โ relies on knowing precedence
$canEdit = $isAdmin || $isOwner && !$isLocked;
// Clear โ intent is explicit
$canEdit = $isAdmin || ($isOwner && !$isLocked);
Hands-On Exercises
๐๏ธ Exercise 1: Price Calculator
Objective: Practice arithmetic and assignment operators by building a simple price calculator.
Instructions:
- Create
exercise_04a.phpin your exercises folder - Define a base price, quantity, tax rate, and discount percentage
- Calculate subtotal, discount amount, tax amount, and final total
- Output a formatted receipt
Starter Code:
<?php
$basePrice = 29.99;
$quantity = 3;
$taxRate = 0.0825; // 8.25%
$discountPercent = 10; // 10% off
// TODO: Calculate these
$subtotal = 0;
$discountAmount = 0;
$afterDiscount = 0;
$taxAmount = 0;
$total = 0;
// TODO: Output a formatted receipt
๐ก Hint
Calculate in order: subtotal = price ร quantity, discount = subtotal ร (percent/100), after discount = subtotal - discount, tax = after discount ร rate, total = after discount + tax. Use number_format($value, 2) to format to 2 decimal places.
โ Solution
<?php
$basePrice = 29.99;
$quantity = 3;
$taxRate = 0.0825;
$discountPercent = 10;
$subtotal = $basePrice * $quantity;
$discountAmount = $subtotal * ($discountPercent / 100);
$afterDiscount = $subtotal - $discountAmount;
$taxAmount = $afterDiscount * $taxRate;
$total = $afterDiscount + $taxAmount;
echo "===== RECEIPT =====\n";
echo "Price: $" . number_format($basePrice, 2) . " ร $quantity\n";
echo "Subtotal: $" . number_format($subtotal, 2) . "\n";
echo "Discount: -$" . number_format($discountAmount, 2) . " ($discountPercent%)\n";
echo "After Discount: $" . number_format($afterDiscount, 2) . "\n";
echo "Tax: +$" . number_format($taxAmount, 2) . " (" . ($taxRate * 100) . "%)\n";
echo "-------------------\n";
echo "TOTAL: $" . number_format($total, 2) . "\n";
๐๏ธ Exercise 2: Operator Challenge
Objective: Predict the output of each expression, then verify with var_dump().
<?php
// Predict each result before running!
// 1. Arithmetic precedence
$a = 2 + 3 * 4 - 1;
// 2. String vs arithmetic
$b = "10" + "20 cats";
// 3. Null coalescing
$c = null ?? 0 ?? "default";
// 4. Elvis vs null coalescing
$d = "" ?: "empty";
$e = "" ?? "empty";
// 5. Combined comparison
$f = (10 > 5) && (3 <= 3) || false;
// 6. Spaceship
$g = 5 <=> 5;
// 7. Concatenation with types
$h = "Score: " . 95 . "%";
// 8. Assignment returns a value
$i = $j = $k = 42;
โ Answers
$a = 2 + 3 * 4 - 1; // int(13) โ 2 + 12 - 1
$b = "10" + "20 cats"; // int(30) โ "10" โ 10, "20 cats" โ 20
$c = null ?? 0 ?? "default"; // int(0) โ first non-null is 0
$d = "" ?: "empty"; // string("empty") โ "" is falsy
$e = "" ?? "empty"; // string("") โ "" is not null!
$f = (10 > 5) && (3 <= 3) || false; // bool(true) โ true && true = true
$g = 5 <=> 5; // int(0) โ equal
$h = "Score: " . 95 . "%"; // string("Score: 95%") โ concatenation
$i = $j = $k = 42; // All are int(42) โ right-to-left assignment
๐ฏ Quick Quiz
Question 1: What does "5" + "3" evaluate to in PHP?
Question 2: What does $x = 0 ?? "default" assign to $x?
Question 3: What is the result of 10 % 3?
Question 4: What's the key difference between && and and in PHP?
Question 5: What does the ternary expression ($age >= 18) ? "adult" : "minor" return if $age is 18?
Summary
๐ Key Takeaways
- Arithmetic:
+-*/%**โ standard math, plus modulo for remainders and exponentiation - Assignment:
=plus compound forms (+=,-=,.=, etc.) for cleaner code - Strings: Use
.for concatenation (not+!) and.=to append - Comparison: Always prefer
===over==to avoid type juggling surprises - Logical:
&&,||,!for combining conditions โ use&&/||notand/or - Ternary:
condition ? true : falsefor compact conditionals; don't over-nest - Null coalescing:
??for defaults when null;??=for assign-if-null - Precedence: When in doubt, use parentheses to make intent clear
๐ Additional Resources
๐ What's Next?
You can now calculate, compare, and combine data. In Lesson 5: Control Flow, you'll learn to make decisions with if/elseif/else, switch, and PHP 8's powerful match expression.
๐ Congratulations!
You've mastered PHP's operators โ the tools that make your data do things. Now let's put them to work with control flow!