PHP Switch
What Is the switch Statement?
In simple terms: switch compares one value against a list of possible values and runs the code for the one that matches. It is an organized alternative to a long chain of if...elseif statements that all test the same variable.
Technically: switch evaluates an expression once, then compares the result with each case value using loose comparison (==). Execution starts at the first matching case and continues until a break statement (or the end of the switch) is reached.
Why It Exists
Code like this repeats the same variable in every condition:
if ($status === 'pending') { ... }
elseif ($status === 'paid') { ... }
elseif ($status === 'shipped') { ... }
elseif ($status === 'delivered') { ... }
else { ... }
switch names the value once and lists the possible cases, which is easier to scan when there are many options.
Syntax
switch (expression) {
case value1:
// code for value1
break;
case value2:
// code for value2
break;
default:
// code when no case matches
}
| Part | Meaning |
|---|---|
switch (expression) | The value to compare, evaluated once |
case value: | A value to compare against; execution starts here when it matches |
break; | Leaves the switch |
default: | Runs when no case matches (optional) |
Example
<?php
$status = 'shipped';
switch ($status) {
case 'pending':
echo "Awaiting payment\n";
break;
case 'paid':
echo "Payment received, preparing your order\n";
break;
case 'shipped':
echo "Your order is on the way\n";
break;
case 'delivered':
echo "Delivered. Enjoy your purchase!\n";
break;
default:
echo "Unknown order status\n";
}
Output
Your order is on the way
How It Works
$statusis evaluated once:'shipped'.- PHP compares it with each
casevalue from top to bottom:'pending','paid', then'shipped', which matches. - Execution starts at
case 'shipped':and runs its statements. breakends theswitch, so the remaining cases are not executed.- If no case had matched, the
defaultblock would run.
The Role of break: Fall-Through
What Happens Without break
case labels only mark where execution starts. Without a break, PHP continues running the statements of the following cases — this is called fall-through.
<?php
$status = 'pending';
switch ($status) {
case 'pending':
echo "Awaiting payment\n";
case 'paid':
echo "Payment received\n";
break;
case 'shipped':
echo "On the way\n";
break;
}
Output
Awaiting payment
Payment received
The pending case is missing its break, so execution "falls through" into the paid case and prints a message that is wrong for a pending order. Forgetting break is the most common switch bug.
Intentional Fall-Through: Grouping Cases
Fall-through is useful when several values should run the same code. Stack the case labels without statements between them:
<?php
function dayType(string $day): string
{
switch ($day) {
case 'Sat':
case 'Sun':
return 'Weekend';
case 'Fri':
return 'Almost the weekend';
default:
return 'Weekday';
}
}
foreach (['Mon', 'Fri', 'Sun'] as $day) {
echo "$day: " . dayType($day) . "\n";
}
Output
Mon: Weekday
Fri: Almost the weekend
Sun: Weekend
'Sat'and'Sun'share one block.- Inside a function,
returnleaves both theswitchand the function, so nobreakis needed after it.
When fall-through is intentional in a case that does contain statements, add a comment such as // no break so readers know it is deliberate.
The default Case
defaultruns when nocasematches.- It is optional, but including it is recommended — it handles unexpected values explicitly instead of silently doing nothing.
- It can appear anywhere in the list, but it is conventionally placed last.
- A
switchcan have only onedefault.
<?php
$paymentMethod = 'crypto';
switch ($paymentMethod) {
case 'card':
case 'upi':
case 'netbanking':
echo "Redirecting to payment gateway...\n";
break;
case 'cod':
echo "Cash on delivery selected\n";
break;
default:
echo "Payment method '$paymentMethod' is not supported\n";
}
Output
Payment method 'crypto' is not supported
switch Uses Loose Comparison
switch compares with ==, so type juggling applies (see PHP Type Juggling):
<?php
$input = '1e1';
switch ($input) {
case '10':
echo "Matched '10'\n";
break;
default:
echo "No match\n";
}
$flag = '0';
switch ($flag) {
case false:
echo "Matched false\n";
break;
case '0':
echo "Matched '0'\n";
break;
}
Output
Matched '10'
Matched false
'1e1' == '10'is true because both strings are numeric and equal to 10.'0' == falseis true, so the first matching case isfalse, not'0'.
For exact matching, normalize the value's type before the switch (for example, cast to int or compare only strings), or use match, which compares strictly.
switch (true) for Range Conditions
A case can contain any expression. Switching on true runs the first case whose expression is true:
<?php
$score = 82;
switch (true) {
case $score >= 90:
$grade = 'A';
break;
case $score >= 75:
$grade = 'B';
break;
case $score >= 60:
$grade = 'C';
break;
default:
$grade = 'F';
}
echo "Grade: $grade\n";
Output
Grade: B
This works, but if...elseif or match (true) expresses range checks more directly. You will see switch (true) in older code.
Multiple Statements in a Case
Each case can contain as many statements as needed, which is one advantage switch has over match:
<?php
function handleCommand(string $command, array &$cart): string
{
switch ($command) {
case 'add':
$cart[] = 'item-' . (count($cart) + 1);
$message = 'Item added';
break;
case 'remove':
if ($cart === []) {
return 'Cart is already empty';
}
array_pop($cart);
$message = 'Item removed';
break;
case 'clear':
$cart = [];
$message = 'Cart cleared';
break;
default:
return "Unknown command: $command";
}
return "$message. Cart has " . count($cart) . " item(s).";
}
$cart = [];
foreach (['add', 'add', 'remove', 'checkout', 'clear', 'remove'] as $command) {
echo handleCommand($command, $cart) . "\n";
}
Output
Item added. Cart has 1 item(s).
Item added. Cart has 2 item(s).
Item removed. Cart has 1 item(s).
Unknown command: checkout
Cart cleared. Cart has 0 item(s).
Cart is already empty
How It Works
array &$cartpasses the cart by reference, so the function can modify the caller's array (see Pass by Reference).- Each case updates the cart, sets a message, and
breaks to the sharedreturnat the end. - The
removecase contains its ownif, showing that cases can hold any logic. - Unknown commands and the empty-cart situation
returnearly.
switch Inside Loops: break and continue
Inside a switch, break exits the switch, not the surrounding loop. To exit the loop from inside a switch, use break 2; to skip to the next loop iteration, use continue 2.
<?php
$lines = ['INFO start', 'SKIP debug', 'ERROR disk full', 'INFO end'];
foreach ($lines as $line) {
$level = strtok($line, ' ');
switch ($level) {
case 'SKIP':
continue 2; // next line in the foreach
case 'ERROR':
echo "Stopping: $line\n";
break 2; // leave the foreach completely
default:
echo "Logged: $line\n";
}
echo " (processed)\n";
}
Output
Logged: INFO start
(processed)
Stopping: ERROR disk full
How It Works
strtok($line, ' ')returns the first word of the line.continue 2skips the rest of the current foreach iteration, so(processed)is not printed for theSKIPline.break 2exits both theswitchand theforeach, soINFO endis never processed.
A plain continue inside a switch behaves like break (it targets the switch), and since PHP 7.3 PHP warns about it at compile time: "continue" targeting switch is equivalent to "break". Use continue 2 when you mean the loop.
switch vs if...elseif vs match
| Feature | if...elseif | switch | match (PHP 8.0+) |
|---|---|---|---|
| Compares | Any conditions | One value against cases | One value against arms |
| Comparison | Whatever you write | Loose (==) | Strict (===) |
| Fall-through | No | Yes, unless break | No |
| Returns a value | No | No | Yes |
| Multiple statements per branch | Yes | Yes | No (one expression per arm) |
| No match | Nothing happens (unless else) | Nothing happens (unless default) | Throws UnhandledMatchError |
Guidelines:
- Use
matchwhen you are choosing a value based on one input and want strict, safe comparison. - Use
switchwhen each case must run several statements, or when you maintain code that already uses it. - Use
if...elseiffor unrelated conditions or complex ranges.
Same Logic With match
<?php
function fileCategory(string $filename): string
{
return match (strtolower(pathinfo($filename, PATHINFO_EXTENSION))) {
'jpg', 'jpeg', 'png', 'webp' => 'Image',
'pdf' => 'PDF document',
'doc', 'docx' => 'Word document',
'zip', 'rar' => 'Archive',
default => 'Other',
};
}
foreach (['photo.JPG', 'resume.pdf', 'notes.docx', 'backup.zip', 'script.php'] as $file) {
echo "$file: " . fileCategory($file) . "\n";
}
Output
photo.JPG: Image
resume.pdf: PDF document
notes.docx: Word document
backup.zip: Archive
script.php: Other
The equivalent switch would need a case line per extension, a break after each group, and a variable to hold the result. match lists several values per arm with commas and returns the result directly.
Common Mistakes
Forgetting break
Effect: Execution falls through into the next case.
Fix: End every case with break (or return inside functions), and comment any intentional fall-through.
Relying on Loose Comparison
Effect: '0' matches case false; numeric strings match unexpected cases.
Fix: Normalize types first, or use match.
Using continue Instead of continue 2 in Loops
Effect: continue acts like break for the switch; the rest of the loop body still runs.
Fix: continue 2 (or break 2 to leave the loop).
Omitting default
Effect: Unexpected values are silently ignored, and variables assigned in the cases may remain undefined.
Fix: Always include default, even if it only logs or throws an error.
Duplicate Case Values
Effect: Only the first matching case runs; the duplicate is unreachable.
Fix: Keep case values unique.
Best Practices
- End every case with
breakorreturn, and comment intentional fall-through. - Group cases that share behavior by stacking
caselabels. - Always include a
defaultbranch. - Normalize the switch value's type to avoid loose-comparison surprises.
- Prefer
matchfor value selection in PHP 8 code; useswitchwhen cases need multiple statements.
Official reference: https://www.php.net/manual/en/control-structures.switch.php