Control Flow
Control Flow เป็นโครงสร้างที่ควบคุมทิศทางการทำงานของโปรแกรม ในบทนี้เราจะเรียนรู้การใช้ if/else, switch และ match expressions ใน PHP 8.4+
If Statement
Section titled “If Statement”ใช้ตรวจสอบเงื่อนไขและทำงานตาม:
<?php$age = 18;
// if พื้นฐานif ($age >= 18) { echo "คุณเป็นผู้ใหญ่แล้ว";}
// if-else$score = 75;if ($score >= 50) { echo "ผ่าน";} else { echo "ไม่ผ่าน";}
// if-elseif-else$grade = 85;if ($grade >= 80) { echo "เกรด A";} elseif ($grade >= 70) { echo "เกรด B";} elseif ($grade >= 60) { echo "เกรด C";} elseif ($grade >= 50) { echo "เกรด D";} else { echo "เกรด F";}// Output: เกรด AAlternative Syntax (สำหรับ templates)
Section titled “Alternative Syntax (สำหรับ templates)”<?php$isLoggedIn = true;?>
<!-- ใช้ใน HTML templates --><?php if ($isLoggedIn): ?> <p>ยินดีต้อนรับ!</p><?php else: ?> <p>กรุณาเข้าสู่ระบบ</p><?php endif; ?>
<!-- elseif ก็ได้ --><?php if ($role === 'admin'): ?> <p>คุณเป็น Admin</p><?php elseif ($role === 'editor'): ?> <p>คุณเป็น Editor</p><?php else: ?> <p>คุณเป็น User</p><?php endif; ?>Nested If
Section titled “Nested If”<?php$age = 25;$hasLicense = true;
// Nested ifif ($age >= 18) { if ($hasLicense) { echo "สามารถขับรถได้"; } else { echo "ยังไม่มีใบขับขี่"; }} else { echo "อายุยังไม่ถึง 18 ปี";}
// ทำให้ flat ได้ด้วย && (อ่านง่ายกว่า)if ($age >= 18 && $hasLicense) { echo "สามารถขับรถได้";} elseif ($age >= 18) { echo "ยังไม่มีใบขับขี่";} else { echo "อายุยังไม่ถึง 18 ปี";}
// Early return pattern (แนะนำ)function canDrive(int $age, bool $hasLicense): string { if ($age < 18) { return "อายุยังไม่ถึง 18 ปี"; }
if (!$hasLicense) { return "ยังไม่มีใบขับขี่"; }
return "สามารถขับรถได้";}Ternary Operator
Section titled “Ternary Operator”รูปแบบสั้นของ if-else:
<?php$age = 20;
// if-else แบบปกติif ($age >= 18) { $status = "ผู้ใหญ่";} else { $status = "เด็ก";}
// Ternary operator$status = ($age >= 18) ? "ผู้ใหญ่" : "เด็ก";
// Nested ternary (ไม่แนะนำ - อ่านยาก)$grade = 75;$result = ($grade >= 80) ? "A" : (($grade >= 60) ? "B" : "F");
// PHP 8.0+: ต้องใส่วงเล็บถ้า nested// $result = $grade >= 80 ? "A" : $grade >= 60 ? "B" : "F"; // Error!$result = $grade >= 80 ? "A" : ($grade >= 60 ? "B" : "F"); // OK
// Elvis operator (?:)$name = "";$displayName = $name ?: "Guest"; // "Guest" (empty string = falsy)
// Null coalescing (??)$username = null;$displayName = $username ?? "Anonymous"; // "Anonymous"
// ต่างกัน:$value = 0;echo $value ?: "default"; // "default" (0 เป็น falsy)echo $value ?? "default"; // 0 (มีค่า ไม่ใช่ null)Switch Statement
Section titled “Switch Statement”ใช้เมื่อมีหลายกรณีที่ต้องตรวจสอบ:
<?php$day = 3;
switch ($day) { case 1: echo "วันจันทร์"; break; case 2: echo "วันอังคาร"; break; case 3: echo "วันพุธ"; break; case 4: echo "วันพฤหัสบดี"; break; case 5: echo "วันศุกร์"; break; case 6: case 7: echo "วันหยุด"; // case 6 และ 7 ทำเหมือนกัน break; default: echo "วันไม่ถูกต้อง";}// Output: วันพุธFall-through Behavior
Section titled “Fall-through Behavior”<?php$grade = "B";
// ลืม break จะ fall through ไปยัง case ถัดไปswitch ($grade) { case "A": echo "ดีเยี่ยม "; // fall through! case "B": echo "ดี "; // fall through! case "C": echo "พอใช้"; break; default: echo "ต้องปรับปรุง";}// ถ้า $grade = "A": Output: "ดีเยี่ยม ดี พอใช้"// ถ้า $grade = "B": Output: "ดี พอใช้"// ถ้า $grade = "C": Output: "พอใช้"Switch กับ String และ Type
Section titled “Switch กับ String และ Type”<?php$color = "red";
switch ($color) { case "red": $code = "#FF0000"; break; case "green": $code = "#00FF00"; break; case "blue": $code = "#0000FF"; break; default: $code = "#000000";}
// Switch ใช้ loose comparison (==)$value = 0;switch ($value) { case "abc": // 0 == "abc" เป็น true! (PHP 7) echo "matched string"; // อาจ match ผิด break; case 0: echo "matched zero"; break;}// PHP 8+: String comparison เป็น false ถ้า compare กับ int// แต่ก็ควรใช้ match() แทนเพื่อความปลอดภัยAlternative Syntax
Section titled “Alternative Syntax”<?php$color = "blue";?>
<?php switch ($color): ?> <?php case "red": ?> <div style="color: red">Red</div> <?php break; ?> <?php case "green": ?> <div style="color: green">Green</div> <?php break; ?> <?php case "blue": ?> <div style="color: blue">Blue</div> <?php break; ?> <?php default: ?> <div>Unknown color</div><?php endswitch; ?>Match Expression (PHP 8.0+)
Section titled “Match Expression (PHP 8.0+)”match เป็น expression ที่ใช้แทน switch ได้ดีกว่าในหลายกรณี:
<?php$day = 3;
// switch (statement)switch ($day) { case 1: $name = "วันจันทร์"; break; // ... อีกหลาย case}
// match (expression) - สั้นกว่า$name = match($day) { 1 => "วันจันทร์", 2 => "วันอังคาร", 3 => "วันพุธ", 4 => "วันพฤหัสบดี", 5 => "วันศุกร์", 6, 7 => "วันหยุด", default => "วันไม่ถูกต้อง",};
echo $name; // "วันพุธ"Match vs Switch
Section titled “Match vs Switch”| Feature | switch | match |
|---|---|---|
| Type | Statement | Expression |
| Comparison | Loose (==) | Strict (===) |
| Fall-through | Default | ไม่มี |
| Return value | ไม่มี | มี |
| Multiple values | หลาย case | comma separated |
| Default required | ไม่บังคับ | Error ถ้าไม่ match |
<?php// match ใช้ strict comparison (===)$value = "1";$result = match($value) { 1 => "integer one", "1" => "string one", default => "other",};echo $result; // "string one" (ไม่ใช่ "integer one")
// match returns value$price = match($membership) { "gold" => 100, "silver" => 200, "bronze" => 300, default => 500,};
// match กับ expression$score = 85;$grade = match(true) { $score >= 80 => "A", $score >= 70 => "B", $score >= 60 => "C", $score >= 50 => "D", default => "F",};
// match throws error ถ้าไม่ match$color = "purple";// match($color) { "red" => 1 }; // UnhandledMatchError!Match กับ Actions
Section titled “Match กับ Actions”<?php$action = "save";
// match สามารถเรียก function ได้$result = match($action) { "save" => saveData($data), "delete" => deleteData($id), "update" => updateData($id, $data), default => throw new InvalidArgumentException("Unknown action: $action"),};
// หรือใช้ callable$operations = [ "add" => fn($a, $b) => $a + $b, "subtract" => fn($a, $b) => $a - $b, "multiply" => fn($a, $b) => $a * $b, "divide" => fn($a, $b) => $b != 0 ? $a / $b : throw new DivisionByZeroError(),];
$op = "add";$result = $operations[$op](10, 5); // 15Null Safe และ Null Coalescing
Section titled “Null Safe และ Null Coalescing”รูปแบบพิเศษสำหรับจัดการค่า null:
<?php// Null coalescing operator (??)$config = [ "timeout" => 30, // "retries" ไม่ได้กำหนด];
$timeout = $config["timeout"] ?? 60; // 30 (มีค่า)$retries = $config["retries"] ?? 3; // 3 (default)
// Chain ได้$value = $a ?? $b ?? $c ?? "default";
// Null coalescing assignment (??=) - PHP 7.4+$array = [];$array["key"] ??= "default"; // กำหนดเฉพาะถ้าไม่มีหรือ nullecho $array["key"]; // "default"
$array["key"] ??= "new value"; // ไม่เปลี่ยน เพราะมีค่าอยู่แล้วecho $array["key"]; // "default"
// Null safe operator (?->) - PHP 8.0+class User { public ?Address $address = null;}
$user = new User();
// วิธีเดิม$city = null;if ($user !== null && $user->address !== null) { $city = $user->address->city;}
// PHP 8+$city = $user?->address?->city; // null (ไม่ error)
// ใช้ร่วมกัน$city = $user?->address?->city ?? "Unknown";Logical Conditions
Section titled “Logical Conditions”การรวมเงื่อนไขหลายข้อ:
<?php$age = 25;$hasLicense = true;$hasInsurance = false;
// AND - ทุกเงื่อนไขต้องเป็นจริงif ($age >= 18 && $hasLicense && $hasInsurance) { echo "สามารถขับรถได้อย่างถูกกฎหมาย";}
// OR - อย่างน้อยหนึ่งเงื่อนไขเป็นจริงif ($isAdmin || $isManager || $isOwner) { echo "มีสิทธิ์เข้าถึง";}
// NOT - กลับค่าif (!$isBlocked) { echo "ผู้ใช้ไม่ถูกบล็อก";}
// ผสมกันif (($age >= 18 && $hasLicense) || $isEmergency) { echo "อนุญาตให้ขับ";}
// Short-circuit evaluation// ถ้าด้านซ้ายของ && เป็น false จะไม่ประเมินด้านขวา// ถ้าด้านซ้ายของ || เป็น true จะไม่ประเมินด้านขวา
$user = null;if ($user && $user->isActive()) { // $user->isActive() ไม่ถูกเรียก echo "User is active";}
// ใช้ประโยชน์จาก short-circuit$data = $cache->get("key") || $data = $db->fetch("key");Guard Clauses (Early Return)
Section titled “Guard Clauses (Early Return)”Pattern ที่ช่วยลด nesting:
<?php// แบบ nested (อ่านยาก)function processOrderNested($order) { if ($order !== null) { if ($order->isValid()) { if ($order->isPaid()) { // process order return "Order processed!"; } else { return "Order not paid"; } } else { return "Invalid order"; } } else { return "No order provided"; }}
// แบบ guard clauses (อ่านง่าย)function processOrder($order) { // Guard clauses - ตรวจสอบเงื่อนไขที่ผิดก่อน if ($order === null) { return "No order provided"; }
if (!$order->isValid()) { return "Invalid order"; }
if (!$order->isPaid()) { return "Order not paid"; }
// Happy path - ทำงานหลัก return "Order processed!";}
// ใช้กับ throw ก็ได้function divide(float $a, float $b): float { if ($b === 0.0) { throw new DivisionByZeroError("Cannot divide by zero"); }
return $a / $b;}Truthiness และ Falsiness
Section titled “Truthiness และ Falsiness”ค่าที่ถือว่า false ใน PHP:
<?php// ค่าที่เป็น falsy (ถือว่า false ใน boolean context)$falsyValues = [ false, // boolean false 0, // integer 0 0.0, // float 0.0 "", // empty string "0", // string "0" [], // empty array null, // null];
foreach ($falsyValues as $value) { if (!$value) { echo gettype($value) . " is falsy\n"; }}
// ค่าอื่นๆ ทั้งหมดเป็น truthy$truthyValues = [ true, 1, -1, 0.1, "hello", "false", // string "false" ≠ boolean false " ", // space [0], // array with element new stdClass(),];
// ระวัง!if ("0") { echo "This won't print"; // "0" เป็น falsy!}
if ("false") { echo "This WILL print"; // "false" เป็น truthy!}แบบฝึกหัด
Section titled “แบบฝึกหัด”แบบฝึกหัดที่ 1: Grade Calculator
Section titled “แบบฝึกหัดที่ 1: Grade Calculator”<?phpfunction getGrade(int $score): string { return match(true) { $score >= 80 => "A", $score >= 70 => "B", $score >= 60 => "C", $score >= 50 => "D", default => "F", };}
function getGradeMessage(int $score): string { $grade = getGrade($score);
return match($grade) { "A" => "ยอดเยี่ยม! คะแนน $score ได้เกรด A", "B" => "ดีมาก! คะแนน $score ได้เกรด B", "C" => "ดี! คะแนน $score ได้เกรด C", "D" => "ผ่าน! คะแนน $score ได้เกรด D", "F" => "ต้องปรับปรุง คะแนน $score ไม่ผ่าน", };}
echo getGradeMessage(85); // "ยอดเยี่ยม! คะแนน 85 ได้เกรด A"echo getGradeMessage(45); // "ต้องปรับปรุง คะแนน 45 ไม่ผ่าน"แบบฝึกหัดที่ 2: User Validation
Section titled “แบบฝึกหัดที่ 2: User Validation”<?phpfunction validateUser(array $data): array { $errors = [];
// Guard clauses if (empty($data["username"])) { $errors[] = "Username is required"; } elseif (strlen($data["username"]) < 3) { $errors[] = "Username must be at least 3 characters"; }
if (empty($data["email"])) { $errors[] = "Email is required"; } elseif (!filter_var($data["email"], FILTER_VALIDATE_EMAIL)) { $errors[] = "Email is invalid"; }
$password = $data["password"] ?? ""; if (strlen($password) < 8) { $errors[] = "Password must be at least 8 characters"; }
return $errors;}
// ทดสอบ$data = [ "username" => "jo", "email" => "invalid-email", "password" => "123",];
$errors = validateUser($data);if (!empty($errors)) { foreach ($errors as $error) { echo "- $error\n"; }}แบบฝึกหัดที่ 3: HTTP Status Handler
Section titled “แบบฝึกหัดที่ 3: HTTP Status Handler”<?phpfunction handleHttpStatus(int $code): string { return match(true) { $code >= 200 && $code < 300 => "Success: " . match($code) { 200 => "OK", 201 => "Created", 204 => "No Content", default => "Success", }, $code >= 300 && $code < 400 => "Redirect: " . match($code) { 301 => "Moved Permanently", 302 => "Found", 304 => "Not Modified", default => "Redirect", }, $code >= 400 && $code < 500 => "Client Error: " . match($code) { 400 => "Bad Request", 401 => "Unauthorized", 403 => "Forbidden", 404 => "Not Found", default => "Client Error", }, $code >= 500 => "Server Error: " . match($code) { 500 => "Internal Server Error", 502 => "Bad Gateway", 503 => "Service Unavailable", default => "Server Error", }, default => "Unknown Status", };}
echo handleHttpStatus(200); // "Success: OK"echo handleHttpStatus(404); // "Client Error: Not Found"echo handleHttpStatus(500); // "Server Error: Internal Server Error"Best Practices
Section titled “Best Practices”1. ใช้ match แทน switch เมื่อทำได้
Section titled “1. ใช้ match แทน switch เมื่อทำได้”<?php// ไม่แนะนำ: switch ยาวและต้องจำ breakswitch ($status) { case 'pending': $label = 'รอดำเนินการ'; break; case 'processing': $label = 'กำลังดำเนินการ'; break; case 'completed': $label = 'เสร็จสิ้น'; break; default: $label = 'ไม่ทราบสถานะ';}
// แนะนำ: match สั้นและปลอดภัยกว่า$label = match($status) { 'pending' => 'รอดำเนินการ', 'processing' => 'กำลังดำเนินการ', 'completed' => 'เสร็จสิ้น', default => 'ไม่ทราบสถานะ',};2. ใช้ Guard Clauses ลด Nesting
Section titled “2. ใช้ Guard Clauses ลด Nesting”<?php// ไม่แนะนำ: nested conditionsfunction process($user, $data) { if ($user !== null) { if ($user->isActive()) { if ($data !== null) { // ทำงาน return true; } } } return false;}
// แนะนำ: guard clausesfunction process($user, $data) { if ($user === null) return false; if (!$user->isActive()) return false; if ($data === null) return false;
// ทำงาน return true;}3. หลีกเลี่ยง Nested Ternary
Section titled “3. หลีกเลี่ยง Nested Ternary”<?php// ไม่แนะนำ: nested ternary อ่านยากมาก$result = $a > $b ? ($c > $d ? 'cd' : 'ab') : ($e > $f ? 'ef' : 'gh');
// แนะนำ: ใช้ match หรือ if-else$result = match(true) { $a > $b && $c > $d => 'cd', $a > $b => 'ab', $e > $f => 'ef', default => 'gh',};4. ใช้ Null Coalescing อย่างถูกต้อง
Section titled “4. ใช้ Null Coalescing อย่างถูกต้อง”<?php// ?? ตรวจสอบ null เท่านั้น$name = $user['name'] ?? 'Guest';
// ?: ตรวจสอบ falsy value ทั้งหมด$name = $user['name'] ?: 'Guest';
// ระวัง: 0 และ '' ก็เป็น falsy$count = 0;echo $count ?: 'default'; // 'default' (อาจไม่ใช่สิ่งที่ต้องการ)echo $count ?? 'default'; // 0 (ถูกต้อง)ปัญหาที่พบบ่อย
Section titled “ปัญหาที่พบบ่อย”1. ลืม break ใน switch
Section titled “1. ลืม break ใน switch”<?php// ปัญหา: fall-through ที่ไม่ตั้งใจ$day = 'Mon';switch ($day) { case 'Mon': echo 'Monday'; // ลืม break! case 'Tue': echo 'Tuesday'; break;}// Output: MondayTuesday
// วิธีแก้: ใช้ match หรืออย่าลืม break2. Comparison กับ null
Section titled “2. Comparison กับ null”<?php$value = null;
// อันตราย: loose comparisonif ($value == false) { // null == false เป็น true!}
// ปลอดภัย: strict comparison หรือ is_null()if ($value === null) { echo 'Value is null';}3. match ไม่มี default
Section titled “3. match ไม่มี default”<?php$status = 'unknown';
// Error! UnhandledMatchError// $label = match($status) {// 'active' => 'Active',// 'inactive' => 'Inactive',// };
// ควรมี default$label = match($status) { 'active' => 'Active', 'inactive' => 'Inactive', default => 'Unknown',};ในบทนี้เราได้เรียนรู้:
- if/else/elseif statements
- Ternary operator (?:) และ Elvis operator
- switch statement และ fall-through behavior
- match expression (PHP 8.0+) และความต่างจาก switch
- Null coalescing (??) และ null safe operator (?->)
- Guard clauses pattern
- Truthiness และ falsiness ใน PHP
- Best practices สำหรับ control flow
แหล่งข้อมูลเพิ่มเติม
Section titled “แหล่งข้อมูลเพิ่มเติม”เข้าสู่ระบบเพื่อดูเนื้อหาเต็ม
ยืนยันตัวตนด้วยบัญชี Google เพื่อปลดล็อกบทความทั้งหมด
Login with Google