Control Flow
Reviewed & published by Brayan K
By the end of this lesson you'll make your PHP make decisions — branching with if, choosing between many options with switch and the safer PHP 8 match, and writing tidy one-liners with the ternary and template if: ... endif; syntax.
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
- Branch with if / elseif / else and pick the first true block
- Combine conditions with && (and) and || (or)
- Match one value against many options with switch and break
- Use the PHP 8 match expression — strict, and it returns a value
- Write compact choices with the ternary ? : and null-coalescing ??
- Use the alternative if: ... endif; syntax inside HTML templates
1️⃣ if / elseif / else
An if statement runs a block of code only when its condition is true. A condition is any expression that's true or false — usually a comparison like $score >= 70. Add elseif branches to test more conditions, and a final else as the catch-all. PHP checks them top to bottom and runs only the first block that's true, then skips the rest — so order matters. You can join conditions with && (both must be true) and || (either is enough).
<?php
// if / elseif / else — PHP runs the FIRST block whose condition is true,
// then skips the rest. Conditions use comparison operators like >= and == .
$score = 78;
echo "Student score: $score\n";
if ($score >= 90) {
echo "Grade: A\n"; // skipped: 78 is not >= 90
} elseif ($score >= 80) {
echo "Grade: B\n"; // skipped: 78 is not >= 80
} elseif ($score >= 70) {
echo "Grade: C\n"; // ✅ true! 78 >= 70 — this runs, rest skipped
} else {
echo "Grade: F\n"; // the catch-all when nothing above matched
}
// Combine conditions with && (AND) and || (OR).
$isWeekend = true;
$temperature = 28;
if ($isWeekend && $temperature > 25) { // BOTH must be true
echo "Beach day!\n";
} elseif ($isWeekend || $temperature > 30) { // EITHER is enough
echo "Day off, but cool\n";
} else {
echo "A normal day\n";
}
?>Note that PHP spells it elseif as one word (else if as two words also works). Because only the first true branch runs, the score 78 prints "Grade: C" and never even checks the lower branches.
2️⃣ switch / case / break / default
When you're comparing one value against a list of fixed options, a long if/elseif chain gets noisy. A switch reads better: you name the value once, then list each possible case. The crucial part is break — it tells PHP to stop once a case has run. Leave it out and PHP "falls through" into the next case and keeps going. default is the catch-all (like else), and you can stack cases to share one block.
<?php
// switch — compare ONE value against many fixed options.
// Each 'case' is a possible value; 'break' stops PHP falling into the next case.
$day = 3; // 1 = Monday ... 7 = Sunday
echo "Day number: $day\n";
switch ($day) {
case 1:
echo "Monday\n";
break; // stop here — don't run case 2
case 6:
case 7: // stacked cases: 6 OR 7 run the same block
echo "Weekend!\n";
break;
default: // runs when no case matched (here: 2,3,4,5)
echo "A weekday\n"; // ✅ $day is 3, so this runs
// no break needed on the LAST case, but it's tidy to add one
}
?>3️⃣ The PHP 8 match Expression
match (added in PHP 8) is a sharper switch. Three things make it nicer: it returns a value you can assign straight to a variable; it needs no break because it never falls through; and it compares with strict ===, so the integer 200 won't accidentally match the string "200". Each arm is value => result, arms are separated by commas, and one arm can list several values. Prefer match over switch for simple value-to-value mapping in PHP 8+.
<?php
// match (PHP 8) — like switch, but it RETURNS a value and uses strict
// comparison (===), so 200 only matches the integer 200, never "200".
// No 'break' needed, and commas separate the arms.
$httpCode = 404;
$message = match ($httpCode) {
200, 201 => "Success", // one arm can list several values
301, 302 => "Redirect",
404 => "Not Found", // ✅ matches — this value is returned
500 => "Server Error",
default => "Unknown", // used if nothing else matched
};
echo "HTTP $httpCode: $message\n"; // the returned value lands in $message
?>4️⃣ Ternary ? : and Null-Coalescing ??
When a choice is small, a full if/else is overkill. The ternary operator packs it into one line that produces a value: condition ? ifTrue : ifFalse. Closely related is null coalescing ??, which says "use the left value, or this fallback if the left is null or unset" — perfect for safe defaults like a guest name when no one is logged in. Keep ternaries to genuinely simple choices; nesting them quickly becomes unreadable.
<?php
// Ternary condition ? valueIfTrue : valueIfFalse — a one-line if/else
// that produces a VALUE. Great for picking between two options.
$age = 20;
$status = ($age >= 18) ? "Adult" : "Minor"; // 20 >= 18 → "Adult"
echo "Status: $status\n";
// ?? is the 'null coalescing' operator: "use the left value, OR this
// fallback if the left is null / not set". Handy for safe defaults.
$username = null;
$display = $username ?? "Guest"; // $username is null → "Guest"
echo "Welcome, $display\n";
?>5️⃣ Alternative if: ... endif; Syntax
PHP offers a second way to write control structures: drop the curly braces, put a colon after the condition, and close with an endif; keyword (there's also endswitch;, endwhile;, and so on). It behaves exactly like the brace version. Its purpose is readability in templates — when you weave PHP through chunks of HTML, a matching endif; is far easier to spot than a lonely } buried in markup.
<?php
// Alternative syntax: swap the { } braces for a colon and an 'end...' keyword.
// It reads cleanly when you weave PHP through HTML in a template.
$loggedIn = true;
?>
<?php if ($loggedIn): ?>
<p>Welcome back!</p>
<?php else: ?>
<p>Please log in.</p>
<?php endif; ?>Because $loggedIn is true, only the first block's HTML is sent; the else markup is skipped entirely.
6️⃣ Your Turn — Fill in the Blanks
Time to practise. Each script below is almost complete — replace every ___ using the 👉 hint, then run it and check it against the Output panel.
<?php
// 🎯 YOUR TURN — finish the if/elseif/else, then run it.
// Pick the band the temperature falls into.
$temp = 30;
if ($temp >= 30) {
echo ___; // 👉 "Hot\n"
} elseif (___) { // 👉 condition for 20 or above: $temp >= 20
echo "Warm\n";
} else {
echo "Cool\n";
}
// ✅ Expected output (with $temp = 30):
// Hot
?>Now a match. Fill in the missing result and the catch-all keyword so it returns the right label.
<?php
// 🎯 YOUR TURN — complete this match so it returns the right label.
// Remember: match uses value => result arms and needs a default.
$grade = "B";
$label = match ($grade) {
"A" => "Excellent",
"B" => ___, // 👉 "Good"
"C" => "Okay",
___ => "Unknown grade", // 👉 the catch-all keyword: default
};
echo "Grade $grade: $label\n";
// ✅ Expected output:
// Grade B: Good
?>Common Errors (and the fix)
- A switch runs the wrong (or every) case — you forgot a break. Without it, PHP keeps running into the next case after a match ("fall-through"). Put a break; at the end of every case, or switch to match, which never falls through.
- "UnhandledMatchError: Unhandled case" — your match had no arm for the value and no default. Unlike switch, match refuses to silently do nothing. Add a default => ... arm to catch anything you didn't list.
- A comparison passes when it shouldn't — you used loose ==, which converts types first, so 0 == "hello" can be true in old PHP and "5" == 5 is true. Use strict === in conditions to compare value and type.
- An if always runs, no matter the value — you wrote if ($x = 5) with one =. That assigns 5 to $x (a truthy result) instead of comparing. To compare, use two equals: if ($x == 5) (or ===).
Pro Tips
- 💡 Default to match for value-to-value mapping in PHP 8+ — no break, strict comparison, and it returns a value you can assign.
- 💡 Always add a default to a match (and usually a switch) so unexpected values are handled, not crashing or silently ignored.
- 💡 Use === in conditions. Strict comparison avoids PHP's loose-typing surprises and makes your intent obvious.
- 💡 Keep ternaries simple. One ? : is readable; nested ternaries are not — reach for a full if/else instead.
📋 Quick Reference — Control Flow
| Structure | Example | Use When |
|---|---|---|
| if / elseif / else | if ($n > 0) { ... } | Different conditions to test, in order |
| switch | switch ($x) { case 1: ... break; } | One value vs many fixed options |
| match | match ($x) { 1 => "a", default => "b" } | Map a value to a value (PHP 8+, strict) |
| ? : | $ok ? "yes" : "no" | A simple one-line either/or value |
| ?? | $name ?? "Guest" | A default when the value is null/unset |
| if: ... endif; | if ($ok): ?> ... <?php endif; | Mixing PHP with HTML in templates |
Mini-Challenge: Ticket Price
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 decide-run-check loop you'll use on every real script.
<?php
// 🎯 MINI-CHALLENGE: a tiny ticket-price calculator.
// No code is filled in — work from the steps, then run it.
//
// 1. Set $age to a number (try 12).
// 2. Use if/elseif/else to choose a price:
// under 5 → "Free"
// 5 to 17 → "Child: $8"
// 18 and over → "Adult: $15"
// 3. echo the result on its own line.
//
// Tip: order matters — test the smallest range first, or use ranges
// like ($age >= 5 && $age <= 17).
//
// ✅ Expected output (with $age = 12):
// Child: $8
// your code here
?>🎉 Lesson Complete!
- ✅ if / elseif / else runs the first true branch; combine conditions with && and ||
- ✅ switch compares one value to many cases — and every case needs a break
- ✅ match (PHP 8) returns a value, uses strict ===, and throws if nothing matches and there's no default
- ✅ The ternary ? : and null-coalescing ?? give you compact one-liners
- ✅ The alternative if: ... endif; syntax keeps PHP-in-HTML templates readable
- ✅ Prefer === in conditions, and watch the single = assignment trap
Practice quiz
In an if / elseif / else chain, how many blocks run?
- Every block whose condition is true
- Always the else block
- Only the FIRST block whose condition is true
- All of them in order
Answer: Only the FIRST block whose condition is true. PHP runs only the first block whose condition is true, then skips the rest.
What keyword stops a switch from 'falling through' into the next case?
- break
- stop
- exit
- end
Answer: break. break ends a case; without it PHP keeps running into the following case.
Which comparison does the PHP 8 match expression use?
- Loose ==
- <=> spaceship
- It depends on the value
- Strict ===
Answer: Strict ===. match always compares with strict ===, so 200 never matches the string "200".
What happens if match has no matching arm and no default?
- It returns null
- It throws an UnhandledMatchError
- It returns false
- It runs the first arm
Answer: It throws an UnhandledMatchError. match throws an UnhandledMatchError rather than silently doing nothing.
Which is a key advantage of match over switch?
- It returns a value you can assign
- It needs more break statements
- It uses loose comparison
- It only works with strings
Answer: It returns a value you can assign. match returns a value, needs no break, and compares strictly.
What does the ternary ($age >= 18) ? "Adult" : "Minor" produce when $age is 20?
- "Minor"
- true
- "Adult"
- 20
Answer: "Adult". 20 >= 18 is true, so the ternary returns the value before the colon: "Adult".
What does $display = $username ?? "Guest" give when $username is null?
- null
- "Guest"
- An error
- false
Answer: "Guest". ?? falls back to "Guest" because $username is null.
How does PHP spell the keyword for an additional condition?
- else if only
- elif
- otherwise
- elseif (one word; else if also works)
Answer: elseif (one word; else if also works). PHP spells it elseif as one word, though 'else if' as two words also works.
What is the alternative if: ... endif; syntax mainly for?
- Faster execution
- Readability inside HTML templates
- Avoiding semicolons
- Defining functions
Answer: Readability inside HTML templates. It replaces braces with a colon and endif; making PHP-in-HTML templates easier to read.
Why can you stack cases like 'case 6: case 7:' in a switch?
- To run different code for each
- It is a syntax error
- So 6 OR 7 run the same shared block
- To compare strictly
Answer: So 6 OR 7 run the same shared block. Stacked cases share one block, so both values trigger the same code.
Continue this course
- Previous: Operators & Expressions
- Next: Loops — Repeat code with for, foreach, while, and do-while loops
- Quick reference: PHP cheat sheet
Frequently asked questions
When should I use match instead of switch in PHP?
Reach for match (PHP 8+) whenever you are picking a value from a fixed set of options. It is shorter, needs no break statements, compares strictly with === so 200 never matches the string "200", and it returns a value you can assign straight to a variable. Use switch when you need to run several statements per branch or you are targeting PHP 7, where match does not exist.
What is the difference between == and === in conditions?
== compares values after PHP converts types for you, so 0 == "0" and even 0 == "" can surprise you. === compares value AND type, so "5" === 5 is false. Prefer === in your if conditions: it is predictable and avoids the loose-comparison traps that bite beginners. match() always uses === for you.
Why does my switch run more than one case?
You almost certainly forgot a break. Without break, PHP keeps running the next case's code too — called fall-through. Sometimes that is intentional (stacking case 6: case 7: to share a block), but usually it is a bug. Put a break at the end of every case, or switch to match(), which never falls through.
What happens if match has no matching arm and no default?
PHP throws an UnhandledMatchError and stops. Unlike switch, match refuses to silently do nothing — it forces you to handle every case. Add a default => arm to catch anything you did not list, exactly like the else on an if. This strictness is a feature: it surfaces values you forgot to handle.
What is the if: ... endif; syntax for?
It is the alternative syntax: replace the { } braces with a colon after the condition and an endif; (or endswitch;, endwhile;) keyword to close. It behaves identically to the brace version. Its purpose is readability inside HTML templates, where opening and closing PHP tags around lots of markup makes stray braces easy to lose.