Master conditional statements, loops, and flow control to build dynamic and intelligent PHP applications
Control structures are the backbone of programming logic, allowing your PHP applications to make decisions and repeat tasks efficiently. They transform static code into dynamic, intelligent programs that can respond to different conditions and process data iteratively.
PHP offers a comprehensive set of control structures including traditional conditional statements (if/else, switch), modern pattern matching (match expression in PHP 8), and versatile loops (while, for, foreach). Understanding these structures is essential for building any non-trivial application.
In this lesson, you'll learn how to control the flow of your programs, handle different conditions elegantly, iterate over data structures, and build complex logic that responds intelligently to user input and data states.
Conditional statements allow your program to execute different code blocks based on whether conditions are true or false. PHP provides several ways to implement conditional logic:
If Statement:
The most basic conditional structure evaluates a boolean expression and executes code only if the condition is true:
1$age = 25;
2
3if ($age >= 18) {
4 echo "You are an adult";
5}If-Else Statement:
Provides an alternative code path when the condition is false:
1$temperature = 15;
2
3if ($temperature > 25) {
4 echo "It's hot outside";
5} else {
6 echo "It's cool outside";
7}If-Elseif-Else Chain:
Evaluates multiple conditions in sequence:
1$score = 85;
2
3if ($score >= 90) {
4 $grade = 'A';
5} elseif ($score >= 80) {
6 $grade = 'B';
7} elseif ($score >= 70) {
8 $grade = 'C';
9} elseif ($score >= 60) {
10 $grade = 'D';
11} else {
12 $grade = 'F';
13}
14
15echo "Your grade is: $grade";Nested Conditionals:
You can nest conditions for complex decision trees:
1$is_logged_in = true;
2$is_admin = false;
3
4if ($is_logged_in) {
5 if ($is_admin) {
6 echo "Welcome, Administrator";
7 } else {
8 echo "Welcome, User";
9 }
10} else {
11 echo "Please log in";
12}Alternative Syntax:
PHP provides a colon syntax useful in templates:
1<?php if ($condition): ?>
2 <div>Condition is true</div>
3<?php else: ?>
4 <div>Condition is false</div>
5<?php endif; ?>Best Practices:
When you need to compare a single value against multiple possible values, switch and match statements provide cleaner syntax than long if-elseif chains.
Switch Statement:
The traditional multi-way branch statement:
1$day = 'Monday';
2
3switch ($day) {
4 case 'Monday':
5 echo "Start of the work week";
6 break;
7 case 'Tuesday':
8 case 'Wednesday':
9 case 'Thursday':
10 echo "Midweek days";
11 break;
12 case 'Friday':
13 echo "TGIF!";
14 break;
15 case 'Saturday':
16 case 'Sunday':
17 echo "Weekend!";
18 break;
19 default:
20 echo "Invalid day";
21}Important Switch Behaviors:
break to prevent fall-throughdefault case is optional but recommendedSwitch with Fall-Through:
Sometimes fall-through is intentional:
1$permission_level = 3;
2$capabilities = [];
3
4switch ($permission_level) {
5 case 3:
6 $capabilities[] = 'delete';
7 // Fall through
8 case 2:
9 $capabilities[] = 'edit';
10 // Fall through
11 case 1:
12 $capabilities[] = 'read';
13 break;
14 default:
15 $capabilities[] = 'none';
16}Match Expression (PHP 8+):
A modern, safer alternative to switch with several advantages:
1$status_code = 404;
2
3$message = match ($status_code) {
4 200, 201 => 'Success',
5 400, 401 => 'Client Error',
6 404 => 'Not Found',
7 500, 502, 503 => 'Server Error',
8 default => 'Unknown Status'
9};
10
11echo $message; // "Not Found"Match Expression Features:
break neededMatch with Expressions:
1$age = 25;
2
3$category = match (true) {
4 $age < 13 => 'child',
5 $age < 20 => 'teenager',
6 $age < 65 => 'adult',
7 default => 'senior'
8};When to Use Which:
Loops allow you to execute code repeatedly, either a specific number of times or while a condition remains true. PHP provides four main loop types, each suited for different scenarios.
While Loop:
Executes code as long as a condition is true. The condition is checked before each iteration:
1$count = 1;
2
3while ($count <= 5) {
4 echo "Count: $count\n";
5 $count++;
6}
7// Output: Count: 1, Count: 2, Count: 3, Count: 4, Count: 5Use while when: You don't know the number of iterations in advance.
Do-While Loop:
Similar to while, but checks the condition after each iteration, guaranteeing at least one execution:
1$number = 0;
2
3do {
4 echo "This executes at least once\n";
5 $number++;
6} while ($number > 10); // Condition is false, but code ran onceFor Loop:
Best for counting iterations when you know the start, end, and step:
1// Syntax: for (initialization; condition; increment)
2for ($i = 0; $i < 10; $i++) {
3 echo "Iteration $i\n";
4}
5
6// Counting down
7for ($i = 10; $i >= 0; $i--) {
8 echo "$i... ";
9}
10echo "Liftoff!\n";
11
12// Custom step
13for ($i = 0; $i <= 100; $i += 10) {
14 echo "$i "; // 0 10 20 30 ... 100
15}Foreach Loop:
The most powerful and commonly used loop for iterating over arrays and objects:
1// Iterate over values
2$colors = ['red', 'green', 'blue'];
3
4foreach ($colors as $color) {
5 echo "Color: $color\n";
6}
7
8// Iterate over keys and values
9$user = [
10 'name' => 'John',
11 'email' => '[email protected]',
12 'age' => 30
13];
14
15foreach ($user as $key => $value) {
16 echo "$key: $value\n";
17}Foreach with References:
Modify array values in place using references:
1$numbers = [1, 2, 3, 4, 5];
2
3foreach ($numbers as &$number) {
4 $number *= 2; // Double each value
5}
6unset($number); // Important: unset reference after loop
7
8print_r($numbers); // [2, 4, 6, 8, 10]Nested Loops:
Loops can be nested for multi-dimensional processing:
1// Multiplication table
2for ($i = 1; $i <= 5; $i++) {
3 for ($j = 1; $j <= 5; $j++) {
4 echo str_pad($i * $j, 4);
5 }
6 echo "\n";
7}Alternative Syntax:
Useful in templates:
1<?php foreach ($items as $item): ?>
2 <li><?= htmlspecialchars($item) ?></li>
3<?php endforeach; ?>Performance Tips:
for ($i = 0, $n = count($array); $i < $n; $i++)array_map(), array_filter() for simple transformationsPHP provides special keywords to control loop execution flow, allowing you to skip iterations or exit loops early based on conditions.
Break Statement:
Immediately exits the current loop:
1// Find first number divisible by 7
2for ($i = 1; $i <= 100; $i++) {
3 if ($i % 7 === 0) {
4 echo "First number divisible by 7: $i";
5 break; // Exit loop
6 }
7}Break with Nested Loops:
You can specify how many loop levels to break out of:
1for ($i = 0; $i < 5; $i++) {
2 for ($j = 0; $j < 5; $j++) {
3 if ($i === 2 && $j === 2) {
4 break 2; // Exit both loops
5 }
6 echo "($i, $j) ";
7 }
8}Continue Statement:
Skips the rest of the current iteration and moves to the next:
1// Print only odd numbers
2for ($i = 1; $i <= 10; $i++) {
3 if ($i % 2 === 0) {
4 continue; // Skip even numbers
5 }
6 echo "$i "; // 1 3 5 7 9
7}Continue with Level:
Skip outer loop iterations:
1for ($i = 0; $i < 3; $i++) {
2 for ($j = 0; $j < 3; $j++) {
3 if ($j === 1) {
4 continue 2; // Skip to next iteration of outer loop
5 }
6 echo "($i, $j) ";
7 }
8}Practical Example - Data Validation:
1$users = [
2 ['name' => 'John', 'age' => 25, 'active' => true],
3 ['name' => 'Jane', 'age' => 17, 'active' => true],
4 ['name' => 'Bob', 'age' => 30, 'active' => false],
5 ['name' => 'Alice', 'age' => 22, 'active' => true]
6];
7
8$active_adults = [];
9
10foreach ($users as $user) {
11 // Skip inactive users
12 if (!$user['active']) {
13 continue;
14 }
15
16 // Skip minors
17 if ($user['age'] < 18) {
18 continue;
19 }
20
21 $active_adults[] = $user['name'];
22}
23
24print_r($active_adults); // ['John', 'Alice']Goto Statement:
Jumps to a labeled point in code. Generally discouraged but has specific uses:
1for ($i = 0; $i < 10; $i++) {
2 for ($j = 0; $j < 10; $j++) {
3 if ($i * $j > 50) {
4 goto end; // Jump to label
5 }
6 }
7}
8
9end:
10echo "Loop exited via goto";Goto Restrictions:
Best Practices:
break to exit early when a condition is metcontinue to skip invalid/unwanted itemsgoto unless breaking out of deeply nested structuresReturn in Loops:
You can use return in functions to exit both the loop and function:
1function findUser(array $users, string $email): ?array {
2 foreach ($users as $user) {
3 if ($user['email'] === $email) {
4 return $user; // Exit loop and function
5 }
6 }
7 return null; // Not found
8}This approach is often cleaner than using flags or break statements.
Building a Complete User Authentication System
1<?php
2declare(strict_types=1);
3
4/**
5 * User Authentication and Access Control System
6 *
7 * Demonstrates comprehensive use of control structures:
8 * - Input validation with conditionals
9 * - User lookup with loops
10 * - Permission checking with switch/match
11 * - Early returns and error handling
12 */
13
14class UserAuthSystem {
15 private array $users = [
16 [
17 'id' => 1,
18 'username' => 'admin',
19 'password' => '$2y$10$abcd1234', // Hashed password
20 'role' => 'admin',
21 'active' => true,
22 'login_attempts' => 0
23 ],
24 [
25 'id' => 2,
26 'username' => 'john_doe',
27 'password' => '$2y$10$efgh5678',
28 'role' => 'user',
29 'active' => true,
30 'login_attempts' => 0
31 ],
32 [
33 'id' => 3,
34 'username' => 'jane_smith',
35 'password' => '$2y$10$ijkl9012',
36 'role' => 'moderator',
37 'active' => false,
38 'login_attempts' => 0
39 ]
40 ];
41
42 private const MAX_LOGIN_ATTEMPTS = 3;
43
44 /**
45 * Validate login credentials
46 */
47 public function login(string $username, string $password): array {
48 // Input validation
49 if (empty($username) || empty($password)) {
50 return [
51 'success' => false,
52 'message' => 'Username and password required'
53 ];
54 }
55
56 // Find user
57 $user = null;
58 foreach ($this->users as &$u) {
59 if ($u['username'] === $username) {
60 $user = &$u;
61 break; // Found user, exit loop
62 }
63 }
64
65 // User not found
66 if ($user === null) {
67 return [
68 'success' => false,
69 'message' => 'Invalid credentials'
70 ];
71 }
72
73 // Check if account is locked
74 if ($user['login_attempts'] >= self::MAX_LOGIN_ATTEMPTS) {
75 return [
76 'success' => false,
77 'message' => 'Account locked due to too many failed attempts'
78 ];
79 }
80
81 // Check if account is active
82 if (!$user['active']) {
83 return [
84 'success' => false,
85 'message' => 'Account is deactivated'
86 ];
87 }
88
89 // Verify password (simplified - would use password_verify in real code)
90 if ($password !== 'correct_password') {
91 $user['login_attempts']++;
92
93 $remaining = self::MAX_LOGIN_ATTEMPTS - $user['login_attempts'];
94
95 if ($remaining > 0) {
96 return [
97 'success' => false,
98 'message' => "Invalid password. $remaining attempts remaining"
99 ];
100 } else {
101 return [
102 'success' => false,
103 'message' => 'Account locked'
104 ];
105 }
106 }
107
108 // Success - reset attempts
109 $user['login_attempts'] = 0;
110
111 return [
112 'success' => true,
113 'message' => 'Login successful',
114 'user' => [
115 'id' => $user['id'],
116 'username' => $user['username'],
117 'role' => $user['role']
118 ]
119 ];
120 }
121
122 /**
123 * Check user permissions using match expression
124 */
125 public function checkPermission(string $role, string $action): bool {
126 $permissions = match ($role) {
127 'admin' => ['read', 'write', 'delete', 'manage_users'],
128 'moderator' => ['read', 'write', 'delete'],
129 'user' => ['read', 'write'],
130 'guest' => ['read'],
131 default => []
132 };
133
134 return in_array($action, $permissions);
135 }
136
137 /**
138 * Generate user report with loops
139 */
140 public function generateReport(): string {
141 $report = "USER SYSTEM REPORT\n";
142 $report .= str_repeat("=", 50) . "\n\n";
143
144 $total = count($this->users);
145 $active = 0;
146 $locked = 0;
147 $by_role = [];
148
149 foreach ($this->users as $user) {
150 // Count active users
151 if ($user['active']) {
152 $active++;
153 }
154
155 // Count locked accounts
156 if ($user['login_attempts'] >= self::MAX_LOGIN_ATTEMPTS) {
157 $locked++;
158 }
159
160 // Count by role
161 if (!isset($by_role[$user['role']])) {
162 $by_role[$user['role']] = 0;
163 }
164 $by_role[$user['role']]++;
165 }
166
167 $report .= "Total Users: $total\n";
168 $report .= "Active: $active\n";
169 $report .= "Locked: $locked\n\n";
170
171 $report .= "Users by Role:\n";
172 foreach ($by_role as $role => $count) {
173 $report .= " " . ucfirst($role) . ": $count\n";
174 }
175
176 $report .= "\n" . str_repeat("=", 50) . "\n";
177
178 return $report;
179 }
180}
181
182// Demonstration
183$auth = new UserAuthSystem();
184
185echo "š User Authentication System\n\n";
186
187// Test various login scenarios
188$scenarios = [
189 ['admin', 'correct_password'],
190 ['admin', 'wrong_password'],
191 ['john_doe', 'correct_password'],
192 ['jane_smith', 'correct_password'], // Inactive account
193 ['nonexistent', 'password']
194];
195
196foreach ($scenarios as [$username, $password]) {
197 echo "Testing: $username / $password\n";
198 $result = $auth->login($username, $password);
199 echo " Result: " . ($result['success'] ? 'ā' : 'ā') . " " . $result['message'] . "\n\n";
200}
201
202// Test permissions
203echo "\nš Permission Tests:\n\n";
204
205$roles = ['admin', 'moderator', 'user', 'guest'];
206$actions = ['read', 'write', 'delete', 'manage_users'];
207
208foreach ($roles as $role) {
209 echo ucfirst($role) . " permissions:\n";
210 foreach ($actions as $action) {
211 $allowed = $auth->checkPermission($role, $action);
212 echo " $action: " . ($allowed ? 'ā' : 'ā') . "\n";
213 }
214 echo "\n";
215}
216
217// Generate report
218echo $auth->generateReport();
219?>This authentication system demonstrates professional use of all control structures:
Conditional Logic:
if (empty($username) || empty($password))Loop Usage:
foreach to search for users with early breakMatch Expression:
Break and Continue:
Error Handling:
Real-World Patterns:
The code structure prioritizes:
Your progress is saved in this browser, on this device only.