PHP Operators Explained — Every Type With Real Examples and Gotchas
Every useful program ever written does at least one of three things: it calculates something, it compares something, or it makes a decision. None of those three things are possible without operators. Whether you're building a shopping cart that totals an order, a login form that checks if a password matches, or a news feed that filters posts by date — operators are working silently behind every single line of that logic. They are the engine of your code.
Before operators existed in programming languages, developers had to write verbose machine-level instructions just to add two numbers. Operators solve this by giving us a clean, readable shorthand. Instead of writing a function call like add($price, $tax), you just write $price + $tax. Instead of a function like isGreaterThan($age, 18), you write $age > 18. Operators compress complex intentions into a single character or short symbol — and PHP has a rich set of them covering arithmetic, comparison, logic, assignment, string manipulation and more.
By the end of this article you'll understand every major category of PHP operator, know exactly when to reach for each one, be able to read and write real PHP logic without second-guessing yourself, and you'll know the two or three sneaky mistakes that trip up even experienced developers. Let's build this up from the ground floor.
Arithmetic Operators — PHP as Your Calculator
Arithmetic operators are the ones you already know from primary school maths — they perform mathematical calculations on numbers. PHP gives you addition (+), subtraction (-), multiplication (), division (/), modulus (%), and exponentiation (*).
The one you might not recognise is modulus (%). It gives you the remainder after a division. So 10 % 3 is 1, because 3 goes into 10 three times (that's 9), leaving 1 left over. This is incredibly useful for things like checking whether a number is even or odd — an even number always has a remainder of 0 when divided by 2.
Exponentiation () is also worth highlighting. 2 8 means '2 to the power of 8', which is 256. You'll see this in security calculations, image sizing logic, and anywhere exponential growth matters.
PHP follows standard mathematical order of operations — multiplication and division before addition and subtraction. Use parentheses to override that order and make your intention crystal clear to anyone reading your code later.
<?php // Imagine you're building a simple invoice calculator $itemPrice = 49.99; // price of one item in dollars $quantity = 3; // how many the customer ordered $discountPct = 10; // 10% discount $taxRate = 0.08; // 8% sales tax as a decimal // --- Addition --- $shippingFee = 5.99; $subtotalWithShipping = $itemPrice + $shippingFee; // adds two values echo "Item + Shipping: $" . $subtotalWithShipping . "\n"; // 55.98 // --- Subtraction --- $refundAmount = 10.00; $amountAfterRefund = $itemPrice - $refundAmount; // removes the refund echo "After Refund: $" . $amountAfterRefund . "\n"; // 39.99 // --- Multiplication --- $lineTotal = $itemPrice * $quantity; // price times quantity = line total echo "Line Total: $" . $lineTotal . "\n"; // 149.97 // --- Division --- $discountAmount = ($discountPct / 100) * $lineTotal; // calculate discount value echo "Discount Amount: $" . $discountAmount . "\n"; // 14.997 // --- Modulus (remainder after division) --- $totalItems = 10; $itemsPerBox = 3; $leftoverItems = $totalItems % $itemsPerBox; // 10 divided by 3 = 3 remainder 1 echo "Items that won't fit in a full box: " . $leftoverItems . "\n"; // 1 // A classic use: check if a number is even or odd $orderNumber = 4582; if ($orderNumber % 2 === 0) { echo "Order #" . $orderNumber . " is an even-numbered order.\n"; } else { echo "Order #" . $orderNumber . " is an odd-numbered order.\n"; } // --- Exponentiation --- $base = 2; $power = 10; $result = $base ** $power; // 2 to the power of 10 echo "2 to the power of 10 is: " . $result . "\n"; // 1024 // --- Order of operations matters! --- $wrongTotal = $itemPrice * $quantity + $discountPct / 100; // PHP does * and / first $correctTotal = ($itemPrice * $quantity) + ($discountPct / 100); // parentheses = intention is clear echo "Wrong-looking (but technically same here): $" . $wrongTotal . "\n"; echo "With parentheses (explicit intention): $" . $correctTotal . "\n";
After Refund: $39.99
Line Total: $149.97
Discount Amount: $14.997
Items that won't fit in a full box: 1
Order #4582 is an even-numbered order.
2 to the power of 10 is: 1024
Wrong-looking (but technically same here): $150.07
With parentheses (explicit intention): $150.07
Assignment Operators — Storing and Updating Values in One Move
The basic assignment operator is =. But here's something beginners always get confused about: in PHP (and most programming languages), = does NOT mean 'equals'. It means 'take the value on the right and store it in the variable on the left'. Think of it like putting a label on a jar.
$score = 100 means: create a jar called score, and put the number 100 inside it.
PHP also gives you compound assignment operators that combine a maths operation with an assignment in one step. Instead of writing $score = $score + 10, you can write $score += 10. These aren't just shortcuts — they make your code easier to read because they communicate 'I am changing this existing value' rather than 'I am creating a new value'.
These compound operators exist for all arithmetic operations: +=, -=, =, /=, %=, *=. There's also .= for strings, which appends text to an existing string variable — something you'll use constantly when building HTML output or log messages.
Using these operators correctly is a sign of confident, fluent PHP code.
<?php // --- Basic Assignment --- $playerName = "Alex"; // store the string "Alex" in $playerName $playerScore = 0; // player starts with zero points $playerLevel = 1; // player starts at level 1 echo "Starting state — Player: $playerName, Score: $playerScore, Level: $playerLevel\n"; // --- += (add and assign) --- // Player picks up a coin worth 50 points $playerScore += 50; // same as: $playerScore = $playerScore + 50 echo "After coin pickup — Score: $playerScore\n"; // 50 // Player gets a time bonus of 30 points $playerScore += 30; echo "After time bonus — Score: $playerScore\n"; // 80 // --- -= (subtract and assign) --- // Player takes damage and loses 15 points $playerScore -= 15; // same as: $playerScore = $playerScore - 15 echo "After taking damage — Score: $playerScore\n"; // 65 // --- *= (multiply and assign) --- // Player activates a double-score power-up! $playerScore *= 2; // same as: $playerScore = $playerScore * 2 echo "After double-score power-up — Score: $playerScore\n"; // 130 // --- /= (divide and assign) --- // Score gets halved for using a hint $playerScore /= 2; // same as: $playerScore = $playerScore / 2 echo "After hint penalty — Score: $playerScore\n"; // 65 // --- %= (modulus and assign) --- // Rarely used, but here's a real example: // "Wrap" the level number to stay within 1-5 cycle $currentLevel = 7; $currentLevel %= 5; // 7 % 5 = 2, wraps back into range 0-4 echo "Wrapped level (0-indexed): $currentLevel\n"; // 2 // --- .= (concatenate and assign) — extremely useful for building strings --- $gameLog = "[LOG] Game started. "; $gameLog .= "Player $playerName joined. "; // appends to existing string $gameLog .= "Final score: $playerScore."; // appends again echo $gameLog . "\n";
After coin pickup — Score: 50
After time bonus — Score: 80
After taking damage — Score: 65
After double-score power-up — Score: 130
After hint penalty — Score: 65
Wrapped level (0-indexed): 2
[LOG] Game started. Player Alex joined. Final score: 65.
Comparison and Logical Operators — The Decision-Making Duo
Comparison operators let PHP evaluate whether something is true or false by comparing two values. Every if statement, every loop condition, every filter you write depends on them. They always return a boolean — either true or false.
The most important distinction in PHP comparisons is == versus ===. Double-equals (==) checks if two values are loosely equal — PHP will try to convert types before comparing. Triple-equals (===) checks if two values are strictly equal — same value AND same type. This difference causes some of the nastiest bugs in PHP.
Logical operators combine multiple comparisons into one decision. && (AND) means both conditions must be true. || (OR) means at least one must be true. ! (NOT) flips a boolean — true becomes false and vice versa.
Think of && and || like real-world logic: 'You can enter the club if you have ID AND you're over 18' is &&. 'You get a discount if you're a student OR a senior' is ||.
There's also the spaceship operator <=>, which returns -1, 0, or 1 — perfect for sorting. And the null coalescing operator ??, which is one of PHP's most practical modern features.
<?php // =========================== // COMPARISON OPERATORS // =========================== $userAge = 20; $minimumAge = 18; $membershipTier = "gold"; // == (loose equality — compares VALUE only, converts types) $numericString = "20"; // this is a STRING, not an integer var_dump($userAge == $numericString); // true — PHP converts "20" to 20 before comparing // === (strict equality — compares VALUE and TYPE) var_dump($userAge === $numericString); // false — 20 (int) is not identical to "20" (string) // != (not equal, loose) var_dump($userAge != 18); // true — 20 is not equal to 18 // !== (not identical, strict) var_dump($userAge !== $numericString); // true — different types // > < >= <= var_dump($userAge > $minimumAge); // true — 20 is greater than 18 var_dump($userAge >= $minimumAge); // true — 20 is greater than or equal to 18 var_dump($userAge < 25); // true — 20 is less than 25 var_dump($userAge <= 20); // true — 20 is less than or equal to 20 echo "\n"; // Spaceship operator <=> (returns -1, 0, or 1) // Great for custom sort functions $priceA = 29.99; $priceB = 49.99; $comparison = $priceA <=> $priceB; // -1 means left is LESS THAN right echo "Spaceship result: " . $comparison . "\n"; // -1 // =========================== // LOGICAL OPERATORS // =========================== $isLoggedIn = true; $hasVerifiedEmail = true; $isAdminUser = false; $isBannedUser = false; // && (AND) — both must be true if ($isLoggedIn && $hasVerifiedEmail) { echo "Access granted: user is logged in AND has verified email.\n"; } // || (OR) — at least one must be true if ($isAdminUser || $hasVerifiedEmail) { echo "Can post content: user is admin OR has verified email.\n"; } // ! (NOT) — flips the boolean if (!$isBannedUser) { echo "User is not banned — showing dashboard.\n"; } // Combining conditions $canPublishPost = $isLoggedIn && $hasVerifiedEmail && !$isBannedUser; echo "Can publish post: " . ($canPublishPost ? 'Yes' : 'No') . "\n"; // Yes // Null coalescing operator ?? (PHP 7+) // Returns the LEFT side if it's set and not null, otherwise returns the RIGHT side $username = null; $displayName = $username ?? "Guest"; // $username is null, so use "Guest" echo "Welcome, " . $displayName . "!\n"; // Welcome, Guest! $loggedInUsername = "sarah_dev"; $displayName2 = $loggedInUsername ?? "Guest"; // $loggedInUsername is set, so use it echo "Welcome, " . $displayName2 . "!\n"; // Welcome, sarah_dev!
bool(false)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
Spaceship result: -1
Access granted: user is logged in AND has verified email.
Can post content: user is admin OR has verified email.
User is not banned — showing dashboard.
Can publish post: Yes
Welcome, Guest!
Welcome, sarah_dev!
String, Increment, Ternary and Null-Safe Operators — Rounding Out Your Toolkit
PHP has a few more operator types that you'll use every day, and skipping them would leave real gaps in your understanding.
The string concatenation operator (.) joins two strings together. 'Hello' . ' World' produces 'Hello World'. It's PHP's way of gluing text together — you'll use it constantly.
Increment and decrement operators (++ and --) increase or decrease a number by exactly 1. They come in two flavours: pre (++$count) changes the value first, then returns it. Post ($count++) returns the current value first, then changes it. This subtlety matters inside expressions.
The ternary operator (?:) is a compact if/else on one line. $result = condition ? 'if true' : 'if false'. Use it for simple assignments — not for complex logic where a full if/else is clearer.
The null-safe operator (?->, PHP 8.0+) lets you safely call methods on an object that might be null, without throwing an error. Before it existed, you'd need an if ($obj !== null) check every single time. Now one ?-> handles it gracefully.
<?php // =========================== // STRING CONCATENATION (.) // =========================== $firstName = "Maria"; $lastName = "Chen"; // The dot joins strings together $fullName = $firstName . " " . $lastName; // adds a space between them echo "Full name: " . $fullName . "\n"; // Full name: Maria Chen // Building a welcome message $welcomeMessage = "Hello, " . $firstName . "! Welcome back to TheCodeForge."; echo $welcomeMessage . "\n"; echo "\n"; // =========================== // INCREMENT / DECREMENT // =========================== $pageViews = 100; // Post-increment: RETURNS current value, THEN increments $viewsBeforeIncrement = $pageViews++; // $viewsBeforeIncrement gets 100, THEN $pageViews becomes 101 echo "Returned value (post-increment): " . $viewsBeforeIncrement . "\n"; // 100 echo "pageViews after post-increment: " . $pageViews . "\n"; // 101 // Pre-increment: INCREMENTS first, THEN returns new value $newPageViews = ++$pageViews; // $pageViews becomes 102, THEN $newPageViews gets 102 echo "Returned value (pre-increment): " . $newPageViews . "\n"; // 102 echo "pageViews after pre-increment: " . $pageViews . "\n"; // 102 // Decrement works the same way $stockCount = 5; $stockCount--; // post-decrement — one item sold echo "Stock remaining: " . $stockCount . "\n"; // 4 echo "\n"; // =========================== // TERNARY OPERATOR (?:) // =========================== $cartItemCount = 3; // Long way: if ($cartItemCount > 0) { $cartStatus = "Items in cart"; } else { $cartStatus = "Cart is empty"; } // Ternary — same logic, one line: $cartStatus = ($cartItemCount > 0) ? "Items in cart" : "Cart is empty"; echo "Cart status: " . $cartStatus . "\n"; // Items in cart // Real use — displaying user role label $isAdmin = false; $roleLabel = $isAdmin ? "Administrator" : "Standard User"; echo "Role: " . $roleLabel . "\n"; // Standard User echo "\n"; // =========================== // NULL-SAFE OPERATOR (?->) — PHP 8.0+ // =========================== class UserProfile { public string $bio; public function __construct(string $bio) { $this->bio = $bio; } public function getBio(): string { return $this->bio; } } // Simulate a user that exists and one that doesn't $activeUser = new UserProfile("Full-stack developer from Toronto."); $deletedUser = null; // this user no longer exists // Without null-safe operator, you'd need: // if ($deletedUser !== null) { $bio = $deletedUser->getBio(); } else { $bio = null; } // With null-safe operator (?->) — clean and safe: $activeBio = $activeUser?->getBio(); // works fine, returns the bio $deletedBio = $deletedUser?->getBio(); // $deletedUser is null — returns null safely, no error echo "Active user bio: " . ($activeBio ?? 'No bio available') . "\n"; echo "Deleted user bio: " . ($deletedBio ?? 'No bio available') . "\n";
Hello, Maria! Welcome back to TheCodeForge.
Returned value (post-increment): 100
pageViews after post-increment: 101
Returned value (pre-increment): 102
pageViews after pre-increment: 102
Stock remaining: 4
Cart status: Items in cart
Role: Standard User
Active user bio: Full-stack developer from Toronto.
Deleted user bio: No bio available
| Operator | Symbol | What It Does | Returns | When To Use It |
|---|---|---|---|---|
| Loose Equality | == | Compares values after type coercion | bool | Almost never — prefer === |
| Strict Equality | === | Compares value AND type — no coercion | bool | Default choice for all equality checks |
| Loose Inequality | != | True if values differ after coercion | bool | Almost never — prefer !== |
| Strict Inequality | !== | True if value OR type differs | bool | Default choice for inequality checks |
| Ternary | ?: | Compact if/else for simple assignments | mixed | Short, readable one-liners only |
| Null Coalescing | ?? | Returns left side if set and non-null, else right | mixed | Default values, especially from $_GET/$_POST |
| Spaceship | <=> | Returns -1, 0, or 1 for ordering | int (-1/0/1) | Custom usort() comparison callbacks |
| Null-Safe | ?-> | Calls method/property only if object is not null | mixed or null | Optional relationships — e.g. $user?->getProfile() |
| Concatenation | . | Joins two strings into one | string | Building strings, HTML output, messages |
| Logical AND | && | True only if BOTH sides are true | bool | Multi-condition if statements |
| Logical OR | || | True if AT LEAST ONE side is true | bool | Fallback conditions, permission checks |
🎯 Key Takeaways
- Always use === (strict equality) by default — == silently converts types and causes subtle authentication and logic bugs that are extremely hard to track down.
- The modulus operator % is not just a curiosity — it's the standard way to check even/odd numbers, cycle through arrays by index, and create pagination calculations.
- The null coalescing operator ?? (PHP 7+) and null-safe operator ?-> (PHP 8+) are must-know modern PHP — they eliminate entire classes of null-related crashes with clean, readable syntax.
- Post-increment ($x++) and pre-increment (++$x) both add 1, but return different values inside expressions — post returns the OLD value, pre returns the NEW value. Keep increments on their own line when clarity matters.
⚠ Common Mistakes to Avoid
- ✕Mistake 1: Using == instead of === for equality checks — Symptom:
0 == 'admin'returns true in PHP 7 (because 'admin' converts to 0), which can allow empty-password logins to bypass authentication checks. Fix: Always use === as your default. Only drop to == if you explicitly need type coercion and you understand exactly what conversions will happen. - ✕Mistake 2: Confusing = (assignment) with == (comparison) inside an if statement — Symptom: Writing
if ($userRole = 'admin')instead ofif ($userRole == 'admin'). The first one ASSIGNS 'admin' to $userRole and always evaluates to true — your if block runs for every user regardless of their actual role. Fix: Read your conditions aloud. If you're asking a question, you need == or ===. If you're setting a value, you need =. Many developers write constants on the left likeif ('admin' === $userRole)so a typo likeif ('admin' = $userRole)causes an obvious parse error. - ✕Mistake 3: Misunderstanding post-increment vs pre-increment in expressions — Symptom:
$count = 5; $result = $count++ 2;— beginners expect $result to be 12 (6 × 2), but it's actually 10 (5 × 2), because $count is used before* being incremented. Fix: If the increment timing matters inside an expression, use pre-increment (++$count) or put the increment on its own line before the expression to make the order unambiguous.
Interview Questions on This Topic
- QWhat is the difference between == and === in PHP, and can you give an example where using == would produce a surprising result?
- QWhat does the spaceship operator <=> return, and in what real-world scenario would you use it?
- QIf $a = 0 and $b = 'hello', what does var_dump($a == $b) output in PHP 7 versus PHP 8, and why did the behaviour change?
Frequently Asked Questions
What is the difference between == and === in PHP?
== is the loose equality operator — it compares values but first converts both sides to a common type. === is the strict equality operator — it requires both the value AND the type to match exactly. For example, 0 == false is true, but 0 === false is false because one is an integer and one is a boolean. Always prefer === to avoid unexpected type-coercion bugs.
What does the PHP modulus operator % actually do?
The modulus operator returns the remainder after dividing two numbers. So 10 % 3 is 1, because 3 goes into 10 three times (making 9), with 1 left over. Its most common use is checking if a number is even ($n % 2 === 0) or cycling through a fixed-size range — for example, keeping a counter between 0 and 4 using $counter % 5.
When should I use the ternary operator vs a full if/else in PHP?
Use the ternary operator ?: for simple, short assignments where both the true and false outcomes are concise values — for example, $label = $isAdmin ? 'Admin' : 'User'. If the condition is complex, if you're calling functions in both branches, or if either outcome spans more than a few characters, use a full if/else block. Readability always wins — a ternary that requires a second read is the wrong choice.
Written and reviewed by senior developers with real-world experience across enterprise, startup and open-source projects. Every article on TheCodeForge is written to be clear, accurate and genuinely useful — not just SEO filler.