BAB 3: STRUKTUR KONTROL

3-4: Match Expression

Objectives

  • Mengenal match expression (PHP 8+)
  • Memahami perbedaannya dengan switch
  • Keuntungan match expression
  • Memahami strict comparison pada match
  • Mengenal UnhandledMatchError dan cara menghindarinya

Pendahuluan: Evolusi dari Switch

switch sudah lama menjadi andalan untuk percabangan banyak nilai, tapi ia punya kelemahan: mudah lupa break, pakai perbandingan loose (==), dan tidak bisa langsung menghasilkan nilai. PHP 8 memperkenalkan match expression yang memperbaiki semua itu.

Aspekswitch (PHP 5+)match (PHP 8+)
BreakWajib break; manualOtomatis, tanpa break
PerbandinganLoose (==)Strict (===)
Nilai hasilStatement, tidak bernilaiExpression, menghasilkan nilai
EksekusiCase dieksekusi berurutanHanya satu arm yang cocok

match mirip switch, tapi alih-alih mengeksekusi blok kode, ia menghasilkan sebuah nilai — seperti ternary yang lebih kuat.

Basic Match

php
<?php

$grade = "A";

$result = match ($grade) {
    "A" => "Excellent",
    "B" => "Good",
    "C" => "Average",
    "D" => "Poor",
    "F" => "Fail",
    default => "Invalid grade",
};

echo $result;  // Excellent

?>

Perhatikan perbedaan bentuk dengan switch:

AspekSwitchMatch
Penanda cabangcase "A":"A" =>
Pemecahbreak;Tidak ada (otomatis)
NilaiTidak menghasilkan=> menghasilkan nilai
Penutup}}; (titik koma!)
Defaultdefault:default =>
💡Tip

Jangan lupa titik koma setelah kurung tutup };match adalah expression, jadi ia diakhiri seperti statement biasa.

Match vs Switch

php
<?php

$day = "Monday";

// Switch
switch ($day) {
    case "Monday":
    case "Tuesday":
    case "Wednesday":
    case "Thursday":
    case "Friday":
        echo "Weekday";
        break;
    case "Saturday":
    case "Sunday":
        echo "Weekend";
        break;
}

// Match (cleaner!)
$result = match ($day) {
    "Monday", "Tuesday", "Wednesday", "Thursday", "Friday" => "Weekday",
    "Saturday", "Sunday" => "Weekend",
};

echo $result;  // Weekday

?>

Dua keunggulan langsung terlihat:

  1. Multi-value per arm — cukup pisahkan dengan koma, tanpa trik fall-through.
  2. Tanpa break — setiap arm otomatis berhenti sendiri, tidak mungkin menetes.

Strict Comparison (===)

match menggunakan perbandingan strict (===). Ini perbedaan paling krusial dengan switch:

php
<?php

$value = "10";  // string

// Switch: loose — case 10 (int) yang cocok!
switch ($value) {
    case 10:
        echo "switch: int 10";
        break;
    case "10":
        echo "switch: string 10";
        break;
}
// Output: switch: int 10

// Match: strict — hanya "10" string yang cocok
$result = match ($value) {
    10 => "match: int 10",
    "10" => "match: string 10",
};

echo $result;  // match: string 10

?>
💡Tip

Karena strict, match aman dipakai untuk membedakan 0, "0", "", dan null — empat nilai yang saling "menabrak" di switch.

Match dengan Conditions

match (true) memungkinkan kondisi boolean seperti if-elseif:

php
<?php

$number = 85;

$result = match (true) {
    $number >= 90 => "A",
    $number >= 80 => "B",
    $number >= 70 => "C",
    $number >= 60 => "D",
    default => "E",
};

echo $result;  // B

?>

Pola ini menggantikan rantai if-elseif dengan visualisasi kasus yang rapi. Kondisi dievaluasi dari atas ke bawah, dan arm pertama yang true yang menang.

Match dengan Ekspresi Kompleks

Karena arm match bisa berisi ekspresi apa pun, kita bisa melakukan lebih dari sekadar memilih literal:

php
<?php

$operasi = "tambah";

$hasil = match ($operasi) {
    "tambah" => 5 + 3,
    "kurang" => 5 - 3,
    "kali"   => 5 * 3,
    "bagi"   => 5 / 3,
    default  => throw new InvalidArgumentException("Operasi tidak dikenal"),
};

echo $hasil;  // 8

?>

Perhatikan arm default yang melempar exception — ini pola yang sah dan sangat berguna. Di switch, kamu tidak bisa melempar exception sebagai "nilai default".

Match Harus Lengkap (Exhaustive)

Perbedaan paling penting dengan switch: jika tidak ada arm yang cocok dan tidak ada default, PHP melempar UnhandledMatchError — program berhenti dengan error.

php
<?php

$status = "pending";

// ❌ Tidak ada default: error UnhandledMatchError!
// $pesan = match ($status) {
//     "active" => "User aktif",
//     "inactive" => "User tidak aktif",
// };

// ✅ Selalu sediakan default
$pesan = match ($status) {
    "active" => "User aktif",
    "inactive" => "User tidak aktif",
    default => "Status tidak dikenal",
};

echo $pesan;  // Status tidak dikenal

?>
php
<?php

// Contoh error yang nyata:
function label_hari(string $hari): string
{
    return match ($hari) {
        "senin" => "Hari ke-1",
        "selasa" => "Hari ke-2",
        // lupa "rabu" sampai "minggu"!
    };
}

// try {
//     echo label_hari("rabu");
// } catch (UnhandledMatchError $e) {
//     echo "Error: " . $e->getMessage();
// }
// Output: Error: Unhandled match value of type string

?>
⚠️Perhatian

Selalu sertakan default di match kecuali kamu yakin semua kemungkinan nilai sudah tercakup. Error UnhandledMatchError sering terjadi saat data datang dari luar (input user, API, database) yang nilainya tidak terduga.

Match Features

  • Returns value
  • No fall-through (semua cases harus covered atau ada default)
  • Uses strict comparison (===)
  • Can use multiple values per case
php
<?php

// Returns value - can assign directly
$color = "red";
$hex = match ($color) {
    "red" => "#FF0000",
    "green" => "#00FF00",
    "blue" => "#0000FF",
};

echo $hex;  // #FF0000

// No fall-through - default required
$status = "unknown";

$result = match ($status) {
    "active" => "User aktif",
    "inactive" => "User tidak aktif",
    default => "Status tidak dikenal",
};

echo $result;  // Status tidak dikenal

// Multiple values per arm
$hari = "Sabtu";
$jenis = match ($hari) {
    "Senin", "Selasa", "Rabu", "Kamis", "Jumat" => "hari kerja",
    "Sabtu", "Minggu" => "akhir pekan",
};

echo $jenis;  // akhir pekan

?>

Match dengan Tipe Data Campuran

Karena match memakai ===, kita bisa membedakan tipe data dengan aman — sesuatu yang mustahil di switch:

php
<?php

function describe($nilai): string
{
    return match (true) {
        $nilai === null      => "null",
        $nilai === 0         => "integer nol",
        $nilai === "0"       => "string nol",
        $nilai === ""        => "string kosong",
        $nilai === false     => "boolean false",
        is_int($nilai)       => "integer: $nilai",
        is_string($nilai)    => "string: $nilai",
        default              => "tipe lain",
    };
}

echo describe(null);      // null
echo describe(0);         // integer nol
echo describe("0");       // string nol
echo describe("");        // string kosong
echo describe(false);     // boolean false
echo describe(42);        // integer: 42

?>

Perhatikan urutan: kasus spesifik (null, 0, "0") diletakkan sebelum kasus umum (is_int, is_string) karena match mengambil arm pertama yang cocok.

Common Mistakes

Mistake 1: Lupa titik koma

php
<?php

$n = 1;

// ❌ Syntax error — match adalah expression, butuh ;
// $hasil = match ($n) { 1 => "satu" }

// ✅
$hasil = match ($n) {
    1 => "satu",
};
echo $hasil;  // satu

?>

Mistake 2: Mengharapkan perilaku fall-through

php
<?php

$nilai = 75;

// ❌ Salah: match TIDAK punya fall-through, setiap arm berdiri sendiri
// $hasil = match (true) {
//     $nilai >= 60 => "Lulus",
//     $nilai >= 80 => "Cumlaude",   // tidak akan pernah tercapai!
// };

// ✅ Benar: urutkan dari kondisi paling spesifik
$hasil = match (true) {
    $nilai >= 80 => "Cumlaude",
    $nilai >= 60 => "Lulus",
    default => "Tidak lulus",
};

echo $hasil;  // Lulus

?>
⚠️Perhatian

Urutan arm sangat penting di match. Sama seperti elseif, arm pertama yang cocok yang menang. Kondisi spesifik harus diletakkan di atas kondisi umum.

Mistake 3: Mengira match bisa mengeksekusi banyak statement per arm

php
<?php

// ❌ Setiap arm hanya bisa menghasilkan SATU expression
// $hasil = match ($x) {
//     1 => { echo "satu"; $x * 2; },  // ❌ bukan syntax PHP
// };

// ✅ Solusi: bungkus logika dalam function/closure
$hitung = function (int $x): int {
    echo "memproses $x\n";
    return $x * 2;
};

$x = 3;
$hasil = match ($x) {
    1 => $hitung(1),
    2 => $hitung(2),
    default => $hitung($x),
};

echo $hasil;  // memproses 3\n6

?>

Match vs If-Else vs Switch — Kapan Pakai Apa?

KebutuhanPilih
Banyak nilai tetap, butuh hasil langsungmatch
Perbandingan strict antar nilaimatch
Membedakan 0, "0", "", nullmatch
Banyak cabang yang mengeksekusi blok kode panjangswitch atau if-elseif
Kondisi kompleks dengan &&, function callsif-elseif
Kode harus jalan di PHP 7switch (match butuh PHP 8+)

Pro Tips

  1. Match menggantikan 90% penggunaan switch di kode baru — lebih aman (strict) dan lebih ringkas.
  2. Selalu tambahkan default, terutama untuk data dari luar sistem.
  3. Urutkan arm dari kondisi paling spesifik ke paling umum.
  4. Gunakan match (true) untuk menggantikan rantai if-elseif yang panjang.
  5. default => throw new ... adalah pola valid untuk menolak nilai tak dikenal secara eksplisit.
  6. Jika satu arm butuh lebih dari satu statement, ekstrak ke function dulu.

Exercise

  1. Convert switch ke match expression
  2. Buat match untuk HTTP status codes
  3. Kenapa match lebih aman dari switch?
  4. Tulis function kategori_umur(int $umur): string menggunakan match (true) dengan aturan: 0-12 "Anak", 13-17 "Remaja", 18-59 "Dewasa", 60+ "Lansia". Sertakan default untuk umur negatif.
  5. Apa output kode berikut, dan kenapa?
php
<?php
$n = "10";
echo match ($n) {
    10 => "int",
    "10" => "string",
};
?>

Summary

KonsepPoin Kunci
matchExpression PHP 8+ yang menghasilkan nilai
Armnilai => hasil, multi-value dipisah koma
StrictPakai ===, aman bedakan 0 vs "0"
ExhaustiveTanpa default bisa lempar UnhandledMatchError
No fall-throughSetiap arm berhenti sendiri, tanpa break
match (true)Gantikan rantai if-elseif dengan kondisi boolean

Klik tombol ini setelah menyelesaikan materi

📝

Quiz Bab 3

Uji pemahamanmu tentang Struktur Kontrol