Junior 5 min · March 06, 2026

PHP Operators — Why Loose Equality Exposes User Accounts

A stored hash starting with '0e' plus digits made any password valid in PHP—a classic loose equality bug that strict === avoids.

N
Naren · Founder
Plain-English first. Then code. Then the interview question.
About
 ● Production Incident 🔎 Debug Guide
Quick Answer
  • PHP operators are symbols that perform operations on values: arithmetic, comparison, logical, assignment, and more.
  • Every comparison has two versions: loose (==) and strict (===). Mixing them causes most security bugs.
  • Assignment operators (+=, -=, .=) combine operation and assignment in one step — use them to signal mutation.
  • The null coalescing operator (??) and null-safe operator (?->) eliminate null errors in modern PHP.
  • Performance insight: Loose comparisons are slower than strict because PHP must coerce types first.
  • Production insight: Using == for password checks can bypass authentication when hash starts with '0e' (PHP type juggling).
Plain-English First

Think of PHP operators as the verbs of your code — they're the action words that tell PHP what to DO with your data. Just like a calculator has buttons for +, -, × and ÷, PHP has symbols that add numbers, compare values, combine conditions, and assign data to variables. Without operators, you'd have data sitting around doing absolutely nothing — operators are what make things happen.

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.

arithmetic_operators.phpPHP
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
<?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";
Output
Item + Shipping: $55.98
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
Watch Out: Division Doesn't Always Return What You Expect
In PHP, 10 / 3 returns 3.3333... (a float), not 3. If you need a whole number result from division, use intdiv(10, 3) which returns 3, or cast with (int)(10 / 3). Never assume PHP integer division truncates like it does in some other languages.
Production Insight
Floating point division can introduce rounding errors in financial calculations.
Use bcdiv() for precise decimal arithmetic in payment systems.
Rule: never compare floats with ==; use a tolerance instead.
Key Takeaway
Arithmetic operators follow standard precedence.
Modulus is great for even/odd checks and cycling values.
Always parenthesize complex expressions to avoid surprises.

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.

assignment_operators.phpPHP
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
<?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";
Output
Starting state — Player: Alex, Score: 0, Level: 1
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.
Pro Tip: Use .= When Building HTML in a Loop
When generating HTML output inside a loop, do NOT echo each line separately — it's slow and messy. Instead, build the full string with .= and echo once at the end: $html = ''; foreach($items as $item) { $html .= "<li>$item</li>"; } echo $html;. This is cleaner, testable, and easier to extend.
Production Insight
Forgetting to use .= and using = instead inside a loop overwrites the variable each iteration.
You'll only see the last item. This bug silently eats data.
Rule: when accumulating strings, always use .=.
Key Takeaway
Compound assignment operators signal mutation.
They make code shorter and clearer.
.= is essential for string building — use it everywhere.

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.

comparison_logical_operators.phpPHP
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
71
72
73
74
75
76
<?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!
Output
bool(true)
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!
Watch Out: == vs === Is PHP's #1 Gotcha
In PHP, 0 == 'hello' evaluates to TRUE in PHP versions before 8.0, because PHP converted the string 'hello' to the integer 0. This caused serious security bugs in login systems where an empty password hash compared loosely to 0. Always use === for comparisons unless you have a specific reason not to. Make strict comparison your default habit from day one.
Production Insight
Loose comparison (==) can cause authentication bypass when string hashes start with '0e'.
PHP converts '0e12345' and '0e67890' to 0, so 0 == 0 is true.
Rule: always use strict (===) for passwords, hashes, and IDs.
Key Takeaway
Strict comparison (===) checks value and type.
Loose comparison (==) coerces types — dangerous.
Use ?? for default values and <=> for custom sorting.

String Operators and Concatenation

The dot operator (.) is PHP's string concatenation operator. It joins two strings into one. 'Hello' . ' World' produces 'Hello World'. You'll use it constantly when building messages, HTML, or any textual output.

There's also the compound assignment version (.=) which appends a string to the end of an existing variable. $str .= ' more text' is equivalent to $str = $str . ' more text'. This is your go-to tool for building strings in loops, constructing SQL queries, or accumulating log messages.

One subtle behavior: PHP automatically converts numbers to strings when using the dot operator. So 42 . ' apples' becomes '42 apples'. But be aware that concatenation has lower precedence than arithmetic, so 'Result: ' . 5 + 3 gives 'Result: 8'? Actually it gives '8' because the addition happens first — always wrap arithmetic in parentheses when concatenating.

string_operators.phpPHP
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
<?php

// --- Basic Concatenation ---
$firstName = "John";
$lastName  = "Doe";
$fullName  = $firstName . " " . $lastName;
echo "Full name: " . $fullName . "\n"; // John Doe

// Building a sentence
$noun = "fox";
$verb = "jumps";
$sentence = "The quick brown " . $noun . " " . $verb . " over the lazy dog.";
echo $sentence . "\n";

// Number to string conversion
$count = 5;
echo "You have " . $count . " new messages.\n"; // You have 5 new messages.

// ----- Compound assignment .= -----
$logMessage = "[START] ";
$logMessage .= "User logged in. ";
$logMessage .= "IP 192.168.1.1. ";
$logMessage .= "[END]";
echo $logMessage . "\n"; // [START] User logged in. IP 192.168.1.1. [END]

// ----- Precedence Gotcha -----
$price = 10;
$tax = 2;
// Intent: "Total: 12"
$result1 = "Total: " . $price + $tax;  // Warning: + has higher precedence than .
echo "Without parentheses: " . $result1 . "\n"; // 12 (but produces a warning in PHP 8)
$result2 = "Total: " . ($price + $tax); // Correct
echo "With parentheses: " . $result2 . "\n"; // Total: 12
Output
Full name: John Doe
The quick brown fox jumps over the lazy dog.
You have 5 new messages.
[START] User logged in. IP 192.168.1.1. [END]
Without parentheses: 12
With parentheses: Total: 12
Precedence Trap: `.` vs `+`
In PHP, the concatenation operator (.) has LOWER precedence than addition (+). So "Total: " . $price + $tax is evaluated as ("Total: " . $price) + $tax, which produces an unexpected result (and a warning in PHP 8). Always wrap arithmetic in parentheses when concatenating: "Total: " . ($price + $tax).
Production Insight
Using .= inside a loop is efficient, but forgetting to initialise the variable with an empty string causes undefined variable warnings.
In PHP 8, this throws a TypeError if the first use is .= with a non-string.
Rule: always initialise string variables before using .=.
Key Takeaway
Dot (.) joins strings; .= appends.
Wrap arithmetic in parentheses when concatenating.
Always initialise string variables before using .=.

Increment, Decrement, Ternary, and Null-Safe Operators

PHP has a few more operator types that you'll use every day, and skipping them would leave real gaps in your understanding.

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.

increment_ternary_null_safe.phpPHP
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
71
72
73
74
75
76
77
<?php

// ===========================
// 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";
Output
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
Pro Tip: Prefer ?? Over Ternary for Null Checks
When you're just checking if a value is null and want a fallback, use ?? instead of a ternary. $name = $input ?? 'default' is cleaner and more intentional than $name = ($input !== null) ? $input : 'default'. PHP 7.4+ even gives you ??= — the null coalescing assignment: $name ??= 'default' only assigns if $name is currently null.
Production Insight
Post-increment inside a comparison can cause off-by-one bugs in pagination.
For example, if ($i++ < $limit) uses the old value, so it may run one extra iteration.
Rule: use pre-increment (++$i) in loop conditions, or increment after the condition.
Key Takeaway
Pre-increment returns the new value; post-increment returns the old.
Use ternary for simple assignments only.
Null-safe operator (?->) prevents errors from null objects.

Bitwise and Type Operators — Low-Level Power and Type Safety

PHP also provides bitwise operators (&, |, ^, ~, <<, >>) that work on the binary representation of integers. These are often used for flags, permissions, or low‑level data manipulation. For example, you can pack multiple boolean flags into a single integer using bitwise OR and check them with AND.

Type operators help you inspect and enforce variable types. instanceof checks whether an object is an instance of a specific class (or implements an interface). The @ error control operator suppresses errors from an expression — but use it sparingly, as it often hides real problems.

Bitwise operators have lower precedence than comparison operators, so always parenthesize your expressions when mixing them.

bitwise_type_operators.phpPHP
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
<?php

// ===========================
// BITWISE OPERATORS
// ===========================

// Define permission flags as powers of 2
$PERMISSION_READ   = 1;   // 001
$PERMISSION_WRITE  = 2;   // 010
$PERMISSION_EXECUTE = 4;  // 100

// Assign user permissions using bitwise OR
$userPermissions = $PERMISSION_READ | $PERMISSION_EXECUTE; // 101 = 5

// Check permissions using bitwise AND
if ($userPermissions & $PERMISSION_READ) {
    echo "User has READ permission.\n";
}
if ($userPermissions & $PERMISSION_WRITE) {
    echo "User has WRITE permission.\n";
} else {
    echo "User does NOT have WRITE permission.\n";
}

// Bitwise shift
$value = 4; // 100 in binary
$shiftedLeft = $value << 1;  // 1000 = 8
$shiftedRight = $value >> 1; // 10   = 2
echo "4 << 1 = " . $shiftedLeft . "\n";
echo "4 >> 1 = " . $shiftedRight . "\n";

// ===========================
// TYPE OPERATORS
// ===========================

class User {};
class Admin extends User {};

$user = new Admin();
echo "Is user an instance of User? " . ($user instanceof User ? "Yes" : "No") . "\n"; // Yes
echo "Is user an instance of Admin? " . ($user instanceof Admin ? "Yes" : "No") . "\n"; // Yes

// Error control operator @ — suppress warnings (use sparingly)
$result = @file_get_contents("nonexistent.txt"); // Suppresses warning, $result = false
if ($result === false) {
    echo "File not found (error suppressed).\n";
}
Output
User has READ permission.
User does NOT have WRITE permission.
4 << 1 = 8
4 >> 1 = 2
Is user an instance of User? Yes
Is user an instance of Admin? Yes
File not found (error suppressed).
When to Use Bitwise vs Permissions Database
Bitwise flags are fast and memory‑efficient for simple yes/no permissions (max ~30 flags per integer). For complex ACLs or role‑based systems with many users, use a database table instead. Bitwise permissions become unreadable when you have more than a handful of options.
Production Insight
Overusing @ error suppression hides critical failures like database connection errors.
It makes debugging a nightmare because no error is logged.
Rule: never suppress errors in production code unless you explicitly handle the failure.
Key Takeaway
Bitwise operators are great for flags and low‑level manipulation.
instanceof ensures type safety for operations.
Avoid @ — it hides bugs that will bite you later.
● Production incidentPOST-MORTEMseverity: high

Loose Equality Bug Exposed User Accounts

Symptom
Users could log in with any password if their stored hash started with '0e' followed by digits.
Assumption
Using == for password comparison is safe because both values are strings.
Root cause
PHP's type juggling: when comparing a string like '0e12345' to another string like '0e67890', PHP treats both as numeric strings and converts to floats, resulting in 0 == 0 (true).
Fix
Replace == with === for all password hash comparisons. Use password_verify() which uses strict comparison internally.
Key lesson
  • Never use loose comparison (==) for security-sensitive checks.
  • Always use strict comparison (===) by default.
  • Use PHP's built-in password functions which handle comparison safely.
Production debug guideSymptom → Action Guide for Common Operator-Related Failures5 entries
Symptom · 01
Condition always evaluates to true regardless of variable value
Fix
Check for assignment (=) inside if instead of comparison (==). Look for if ($var = 'admin') vs if ($var === 'admin').
Symptom · 02
Unexpected type-coercion results (e.g., '0' == 'hello' returns true in PHP 7)
Fix
Replace all ==/!= with ===/!== and test again. Use var_dump() on the operands to see actual types.
Symptom · 03
Null pointer errors when accessing method on possibly null object
Fix
Upgrade to PHP 8+ and use null-safe operator (?->) or wrap in if ($obj !== null) check.
Symptom · 04
String concatenation not working as expected in loop
Fix
Ensure you're using .= (append) not = (assignment) inside the loop. var_dump the string each iteration.
Symptom · 05
Rounding errors in financial calculations
Fix
Use bcmath functions (bcadd, bccomp) for precise decimal arithmetic. Avoid float comparisons with ==.
★ PHP Operator Debug Cheat SheetQuick commands and fixes for the most common operator-related production issues in PHP.
Strict comparison fails unexpectedly
Immediate action
Check both operand types with var_dump().
Commands
var_dump($a, gettype($a)); var_dump($b, gettype($b));
Compare using === after ensuring types match: if ($a === $b) { ... }
Fix now
Cast the operands to the same type: if ((int)$a === (int)$b) { ... } or use is_int(), is_string().
Assignment inside if statement+
Immediate action
Review condition for single = instead of == or ===.
Commands
grep -n 'if (\$[a-zA-Z_][a-zA-Z0-9_]* = ' yourfile.php
Add parentheses to highlight: if (($var = 'admin')) will warn in PHP 8+.
Fix now
Replace = with === in the condition.
Null safe operator not working (PHP 7 and below)+
Immediate action
Check PHP version. ?-> requires PHP 8.0+.
Commands
php -v | grep -E '^PHP'
Replace with null check: if ($obj !== null) { $obj->method(); } else { ... }
Fix now
Upgrade to PHP 8+ or use ternary with null check: $result = $obj ? $obj->method() : null;
PHP Operator Comparison
OperatorSymbolWhat It DoesReturnsWhen To Use It
Loose Equality==Compares values after type coercionboolAlmost never — prefer ===
Strict Equality===Compares value AND type — no coercionboolDefault choice for all equality checks
Loose Inequality!=True if values differ after coercionboolAlmost never — prefer !==
Strict Inequality!==True if value OR type differsboolDefault choice for inequality checks
Ternary?:Compact if/else for simple assignmentsmixedShort, readable one-liners only
Null Coalescing??Returns left side if set and non-null, else rightmixedDefault values, especially from $_GET/$_POST
Spaceship<=>Returns -1, 0, or 1 for orderingint (-1/0/1)Custom usort() comparison callbacks
Null-Safe?->Calls method/property only if object is not nullmixed or nullOptional relationships — e.g. $user?->getProfile()
Concatenation.Joins two strings into onestringBuilding strings, HTML output, messages
Logical AND&&True only if BOTH sides are trueboolMulti-condition if statements
Logical OR||True if AT LEAST ONE side is trueboolFallback conditions, permission checks
InstanceofinstanceofChecks if object is of a specific class/interfaceboolType checks, especially in dependency injection
Bitwise AND&Performs bitwise AND on two integersintFlag checking, low-level data manipulation
Bitwise OR|Performs bitwise OR on two integersintBuilding permission flags from enumerated values
Error Control@Suppresses errors from an expressionmixedRarely; only when you handle failure explicitly

Key takeaways

1
Always use === (strict equality) by default
== silently converts types and causes subtle authentication and logic bugs that are extremely hard to track down.
2
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.
3
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.
4
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.
5
Floating point comparisons with == are unreliable. Use bccomp() or a tolerance for financial calculations.
6
The concatenation operator (.) has lower precedence than addition
always parenthesize arithmetic inside string concatenation.

Common mistakes to avoid

5 patterns
×

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.
×

Confusing = (assignment) with == (comparison) inside an if statement

Symptom
Writing if ($userRole = 'admin') instead of if ($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 like if ('admin' === $userRole) so a typo like if ('admin' = $userRole) causes an obvious parse error.
×

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.
×

Using AND/OR instead of &&/|| due to precedence differences

Symptom
$result = true AND false; assigns true to $result and then evaluates false separately, because AND has lower precedence than =. This leads to logic bugs that are hard to spot.
Fix
Always use && and || for logical operations. Reserve the words and, or, xor only when you need the extremely low precedence (rarely). When in doubt, use parentheses.
×

Assuming floating point comparisons with == are safe

Symptom
0.1 + 0.2 == 0.3 evaluates to false due to IEEE 754 rounding errors. This breaks financial calculations, payment validation, and score comparisons.
Fix
Use bccomp() for decimal comparisons, or compare with a tolerance: abs($a - $b) < 0.0001. Never use == on floats.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01JUNIOR
What is the difference between == and === in PHP, and can you give an ex...
Q02SENIOR
What does the spaceship operator <=> return, and in what real-world scen...
Q03SENIOR
If $a = 0 and $b = 'hello', what does var_dump($a == $b) output in PHP 7...
Q04SENIOR
Explain the difference between post-increment ($x++) and pre-increment (...
Q05JUNIOR
What is the null coalescing operator (??) and how does it differ from th...
Q01 of 05JUNIOR

What is the difference between == and === in PHP, and can you give an example where using == would produce a surprising result?

ANSWER
== performs loose equality with type coercion; === performs strict equality checking both value and type. A surprising example: 0 == 'hello' returns true in PHP 7 because 'hello' is converted to 0. Always use === unless you explicitly need coercion.
FAQ · 5 QUESTIONS

Frequently Asked Questions

01
What is the difference between == and === in PHP?
02
What does the PHP modulus operator % actually do?
03
When should I use the ternary operator vs a full if/else in PHP?
04
What is the null-safe operator (?->) and how is it different from the null coalescing operator (??)?
05
Why does `0.1 + 0.2 == 0.3` return false in PHP?
🔥

That's PHP Basics. Mark it forged?

5 min read · try the examples if you haven't

Previous
PHP Variables and Data Types
3 / 14 · PHP Basics
Next
PHP Control Flow