Ada satu kata kunci kendali alur yang paling terkenal — dan paling dihindari — dalam sejarah pemrograman: goto. PHP menyediakan goto untuk melompat langsung ke baris bertanda label di dalam berkas yang sama. Kekuatan seperti ini sering dijadikan contoh buruk karena memungkinkan alur program melompat-lompat tanpa pola yang mudah diikuti pembaca. Meski begitu, memahami goto tetap berguna: kita akan menemukannya di kode lawas, di perdebatan teknis, dan pada beberapa kasus sempit yang sah. Chapter ini membahas sintaksnya, batasan keras yang dipasang PHP, kegunaan yang dianggap wajar, serta alasan kuat untuk memilih alternatif lain.

Sintaks goto dan Label #

Sebuah label ditulis sebagai nama pengenal yang diikuti titik dua, ditempatkan sendiri di barisnya. Perintah goto diikuti nama label target, dan ketika dieksekusi, interpreter memindahkan eksekusi ke baris label tersebut. Mari kita lihat bentuk paling sederhananya — lompatan ke depan:

php
<?php
echo "Menyiapkan laporan...\n";

goto selesai;

echo "Baris ini tidak pernah dijalankan.\n";

selesai:
echo "Laporan selesai dibuat.\n";

Output yang Dihasilkan:

text
Menyiapkan laporan...
Laporan selesai dibuat.

Mari kita telaah jalannya program:

  1. Baris 2: pesan pertama tercetak normal karena berada sebelum perintah lompat.
  2. Baris 4 (goto selesai;): interpreter berhenti mengeksekusi baris demi baris dan langsung mencari label bernama selesai.
  3. Baris 6: seluruh baris di antara goto dan label — dalam contoh ini satu echo — dilewati sepenuhnya.
  4. Baris 8 (selesai:): titik target lompatan; eksekusi bergeser ke sini, dan echo setelah label berjalan normal seperti pernyataan biasa.

Label selesai: sendiri tidak mengganggu eksekusi normal — bila tidak ada goto yang menunjuknya, baris tersebut bagaikan komentar tak terlihat yang dilewati.

Melompat Mundur untuk Mengulang #

goto juga dapat melompat mundur ke label yang berada di atasnya. Efeknya membentuk perulangan buatan — persis seperti yang dilakukan while, tetapi tanpa kata kunci yang menyatakan niat tersebut:

php
<?php
$nomor = 1;

ulangi:
echo "Iterasi ke-$nomor\n";
$nomor++;

if ($nomor <= 3) {
    goto ulangi;
}

echo "Selesai.\n";

Output yang Dihasilkan:

text
Iterasi ke-1
Iterasi ke-2
Iterasi ke-3
Selesai.

Program mencetak tiga iterasi lalu berhenti: setelah echo, counter naik, dan selama $nomor belum melewati 3, goto ulangi mengembalikan eksekusi ke label di atas. Secara hasil, kode ini identik dengan while ($nomor <= 3) yang telah kita pelajari — tetapi pembacanya harus menelusuri label dan kondisi secara manual untuk menyadari bahwa sebuah loop sedang terbentuk. while menyatakan pengulangan secara eksplisit di baris pertama; goto menyembunyikannya.

Batasan Ketat goto #

PHP tidak membuka goto tanpa pagar. Ada tiga batasan keras yang berlaku. Pertama, target label harus berada di berkas yang sama — goto tidak bisa melompat ke berkas lain yang di-include. Kedua, label yang berada di dalam sebuah function hanya terlihat dari dalam function tersebut, sehingga lompatan lintas batas function tidak mungkin dilakukan. Ketiga, lompatan ke dalam loop atau switch dilarang sepenuhnya — PHP menolaknya bahkan sebelum program berjalan:

php
<?php
goto diDalamLoop;

foreach ([1, 2, 3] as $angka) {
    diDalamLoop:
    echo "Angka $angka\n";
}

Output yang Dihasilkan:

text

Fatal error: 'goto' into loop or switch statement is disallowed in /home/z/my-project/php-course/content/php/control-flow/goto-dan-labels/.verify_tmp.php on line 2

Larangan ini masuk akal: melompat ke dalam loop berarti melewati inisialisasi dan kondisi yang membuat loop tersebut valid. Interpreter tidak bisa menentukan nilai apa yang seharusnya dimiliki variabel loop pada titik masuk yang melompati awalnya, sehingga PHP membatalkan eksekusi dengan fatal error saat kompilasi, dan skrip berakhir dengan exit code 255. Perhatikan bahwa arah sebaliknya — keluar dari dalam loop memakai goto — tetap diizinkan, dan justru itulah penggunaan yang sering dianggap wajar.

Kasus yang Dianggap Wajar: Keluar dari Nesting Dalam #

Dokumentasi resmi PHP menyebut satu skenario yang lazim untuk goto: keluar dari loop atau switch yang bersarang dalam dalam satu lompatan. Polanya seperti ini:

php
<?php
$kelompok = [
    ["a1", "b1", "target"],
    ["a2", "b2", "c2"],
];

foreach ($kelompok as $baris) {
    foreach ($baris as $nilai) {
        if ($nilai === "target") {
            goto ditemukan;
        }
    }
}
echo "Nilai target tidak ditemukan.\n";
goto akhir;

ditemukan:
echo "Nilai target ditemukan!\n";

akhir:
echo "Program berakhir.\n";

Output yang Dihasilkan:

text
Nilai target ditemukan!
Program berakhir.

Saat "target" ditemukan di loop terdalam, goto ditemukan melepaskan kedua loop sekaligus dan mendarat tepat di blok penanganan hasil. Cabang gagal tetap tertulis berdekatan dengan loop, dan label akhir: menjadi titik temu bersama. Kebutuhan yang sama persis bisa diselesaikan dengan break 2 dari chapter break dan continue — mari kita bandingkan:

php
<?php
$kelompok = [
    ["a1", "b1", "target"],
    ["a2", "b2", "c2"],
];
$ketemu = false;

foreach ($kelompok as $baris) {
    foreach ($baris as $nilai) {
        if ($nilai === "target") {
            $ketemu = true;
            break 2;
        }
    }
}

echo $ketemu ? "Nilai target ditemukan!\n" : "Nilai target tidak ditemukan.\n";

Output yang Dihasilkan:

text
Nilai target ditemukan!

Versi break 2 membutuhkan satu variabel penanda ($ketemu), tetapi alurnya tetap berbentuk blok beraturan: loop, keluar, lalu rangkum hasil. Versi goto memindahkan titik baca pembaca ke label yang jauh — sederhana di contoh kecil, makin sulit dilacak ketika label dan loop-nya terpisah ratusan baris. Pada loop bersarang dua tingkat, kedua gaya sama-sama bisa diterima; di atas itu, kebanyakan tim memilih mengekstrak loop ke dalam function dan memakai return.

Mengapa goto Nyaris Selalu Dihindari #

Alasan utama penolakan goto adalah keterbacaan jangka panjang. Struktur kendali alur yang telah kita pelajari — if, while, for, foreach, switch — memaksa alur program membentuk blok beraturan yang bisa dipahami dari bentuknya; goto meruntuhkan bentuk tersebut dan membuat setiap baris berpotensi menjadi titik masuk dari arah mana pun. Tabel berikut memetakan kebutuhan yang sering memunculkan goto beserta solusi yang lebih jelas:

KebutuhanSolusi yang lebih jelas
Keluar dari loop bersarangbreak 2 dengan variabel penanda
Melewati satu iterasicontinue
Menghentikan seluruh proses dari dalam loopreturn pada function
Menyediakan jalur pembersihan (cleanup)try/finally pada Section Error Handling
Meniru perulanganwhile, for, atau foreach

Berkas contoh.php pada bagian bawah halaman merangkum lompatan maju, mundur, dan keluar dari nesting dalam dalam satu program utuh yang dapat Anda unduh dan jalankan langsung dengan php contoh.php. Contoh fatal error tidak disertakan karena menghentikan eksekusi keseluruhan berkas.

Ringkasan #

goto nama melompat ke label nama: di berkas yang sama; lompatan bisa maju maupun mundur, tetapi tidak boleh menembus batas function dan dilarang keras masuk ke dalam loop atau switch. Penggunaan yang dianggap wajar terbatas pada keluar dari nesting dalam, dan pun break 2 sering menjadi pilihan yang lebih teratur. Dengan ini, seluruh struktur kendali alur PHP telah kita lalui. Yang tersisa bukan lagi sintaks baru, melainkan keahlian menyusun semuanya menjadi alur yang mudah dibaca — fokus chapter terakhir section ini.

Referensi #

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

contoh.php

Unduh berkas
php
<?php
// contoh.php — rangkuman goto dan label dari chapter "goto dan Labels".
// Contoh fatal error (goto ke dalam loop) sengaja tidak disertakan di sini
// karena menghentikan eksekusi. Jalankan dengan: php contoh.php

echo "== Lompatan maju ke label ==\n";
echo "Menyiapkan laporan...\n";

goto selesai;

echo "Baris ini tidak pernah dijalankan.\n";

selesai:
echo "Laporan selesai dibuat.\n";

echo "\n== Lompatan mundur membentuk perulangan ==\n";
$nomor = 1;

ulangi:
echo "Iterasi ke-$nomor\n";
$nomor++;

if ($nomor <= 3) {
    goto ulangi;
}

echo "Selesai.\n";

echo "\n== Keluar dari nesting dalam dengan goto ==\n";
$kelompok = [
    ["a1", "b1", "target"],
    ["a2", "b2", "c2"],
];

foreach ($kelompok as $baris) {
    foreach ($baris as $nilai) {
        if ($nilai === "target") {
            goto ditemukan;
        }
    }
}
echo "Nilai target tidak ditemukan.\n";
goto akhir;

ditemukan:
echo "Nilai target ditemukan!\n";

akhir:
echo "Program berakhir.\n";

echo "\n== Padanan dengan break 2 ==\n";
$ketemu = false;

foreach ($kelompok as $baris) {
    foreach ($baris as $nilai) {
        if ($nilai === "target") {
            $ketemu = true;
            break 2;
        }
    }
}

echo $ketemu ? "Nilai target ditemukan!\n" : "Nilai target tidak ditemukan.\n";