Dari seluruh fitur PHP 8.1, fiber adalah yang paling eksotis: block kode yang bisa menghentikan dirinya sendiri di tengah eksekusi, menyerahkan kendali ke pemanggil, lalu melanjutkan persis dari titik berhenti tersebut. Generator dengan yield sebenarnya sudah memberi kesan serupa, tetapi generator bersifat setengah jalan — yield hanya sah di dalam function generator itu sendiri, sedangkan fiber bisa berhenti dari kedalaman call stack berlapis tanpa function perantara mengetahuinya. Perlu jujur sejak awal: ini adalah fitur tingkat lanjut yang nyaris selalu dipakai tidak langsung — event loop seperti Revolt dan AMPHP membangun asynchronous I/O di atasnya, dan kode aplikasi sehari-hari cukup menikmati hasilnya lewat framework. Meski demikian, memahami mekanismenya membuat kita paham batas sebenarnya dari “PHP non-blocking”. Chapter ini menguji perilaku fiber lewat contoh yang bisa dijalankan langsung.
Menghentikan Eksekusi di Tengah Function #
Fiber dibuat dengan new Fiber($callback) — constructor menerima sebuah callable. Method start() menjalankan callback tersebut sampai Fiber::suspend() dipanggil atau callback selesai; resume() melanjutkan dari titik suspend. Perhatikan urutan cetakan berikut:
<?php
$fiber = new Fiber(function (): void {
echo "2. Di dalam fiber, sebelum suspend\n";
Fiber::suspend();
echo "4. Di dalam fiber, setelah resume\n";
});
echo "1. Sebelum start\n";
$fiber->start();
echo "3. Kembali di pemanggil setelah suspend\n";
$fiber->resume();
echo "5. Selesai\n";Output yang Dihasilkan:
1. Sebelum start
2. Di dalam fiber, sebelum suspend
3. Kembali di pemanggil setelah suspend
4. Di dalam fiber, setelah resume
5. SelesaiUrutan output membuktikan alurnya: start() menjalankan callback sampai Fiber::suspend() (cetakan 2), lalu kendali kembali ke baris setelah start() (cetakan 3). Pemanggil bekerja seolah memanggil function biasa yang berkata “nanti saya lanjut”. Saat resume() dipanggil, eksekusi fiber menyala kembali dari baris tepat setelah suspend() (cetakan 4), dengan state lokal — variabel, posisi loop, semuanya — utuh seperti ditinggalkan.
Mengirim dan Menerima Nilai Antar Fiber #
Kanal komunikasi fiber dua arah. Nilai yang dikirim ke Fiber::suspend() menjadi nilai balik start() atau resume() di sisi pemanggil; sebaliknya, nilai yang dikirim ke resume() menjadi nilai balik Fiber::suspend() di dalam fiber. Nilai balik akhir function fiber diambil lewat getReturn() setelah fiber selesai:
<?php
$fiber = new Fiber(function (string $salam): string {
// Nilai argument suspend() dikembalikan ke start()/resume() pemanggil
$pertanyaan = Fiber::suspend("$salam, apa kabar?");
// Nilai argument resume() menjadi nilai balik suspend()
return "Fiber menerima: $pertanyaan";
});
$balasan = $fiber->start('Halo');
echo "Pemanggil menerima: $balasan\n";
$fiber->resume('Kabar baik');
echo $fiber->getReturn(), "\n";
echo 'Fiber selesai? ', var_export($fiber->isTerminated(), true), "\n";Output yang Dihasilkan:
Pemanggil menerima: Halo, apa kabar?
Fiber menerima: Kabar baik
Fiber selesai? trueMari kita bedah aliran datanya:
$fiber->start('Halo'): string'Halo'masuk sebagai parameter$salamcallback; hasilstart()adalah'Halo, apa kabar?'— nilai yang dioper kesuspend().$fiber->resume('Kabar baik'): string tersebut menjadi nilai balikFiber::suspend(), mengisi$pertanyaan; function lalureturnstring gabungannya.$fiber->getReturn(): mengambil nilai balik function fiber setelah selesai —isTerminated()yang mengembalikantruemenandakan fiber sudah tidak bisa di-resume.
Menghentikan dari Kedalaman Call Stack #
Inilah perbedaan fundamental dari generator. Fiber::suspend() sah dipanggil di function berlapis berapa pun di bawah titik masuk fiber — perantara tidak perlu tahu bahwa suatu saat eksekusi akan dibekukan:
<?php
function lapisanTiga(): void
{
echo " lapisan tiga: berhenti di sini\n";
Fiber::suspend('jeda dari kedalaman tiga lapis');
echo " lapisan tiga: lanjut lagi\n";
}
function lapisanDua(): void
{
lapisanTiga();
echo " lapisan dua: selesai\n";
}
function lapisanSatu(): void
{
lapisanDua();
echo " lapisan satu: selesai\n";
}
$fiber = new Fiber('lapisanSatu');
$pesan = $fiber->start();
echo "Pemanggil menerima: $pesan\n";
$fiber->resume();Output yang Dihasilkan:
lapisan tiga: berhenti di sini
Pemanggil menerima: jeda dari kedalaman tiga lapis
lapisan tiga: lanjut lagi
lapisan dua: selesai
lapisan satu: selesaiFiber berhenti di lapisanTiga() — dua lapis di bawah function yang didaftarkan — lalu seluruh call stack-nya dibekukan utuh dan kendali kembali ke pemanggil. Setelah resume(), kelanjutan lapisanTiga(), lapisanDua(), dan lapisanSatu() berjalan berurutan seperti tidak pernah terputus. Bandingkan dengan generator: yield hanya bisa mem-pause function generator langsung, sehingga nilai harus “ditimba” manual dengan yield from di setiap lapisan perantara. Kemampuan menembus call stack inilah yang dimaksud istilah full-stack interruptible functions.
Eksekusi Lokal: Setiap Fiber Punya Stack Sendiri #
Setiap fiber membawa call stack dan variabel lokalnya sendiri. Dua fiber yang menjalankan callback serupa bisa dijeda dan dilanjutkan bergantian tanpa saling menginjak state:
<?php
function buatFiber(string $nama, int $awal): Fiber
{
return new Fiber(function () use ($nama, $awal): void {
$hitung = $awal;
echo "$nama: hitung = $hitung\n";
Fiber::suspend();
$hitung += 10;
echo "$nama: hitung = $hitung\n";
});
}
$a = buatFiber('Fiber A', 1);
$b = buatFiber('Fiber B', 100);
$a->start();
$b->start();
$a->resume();
$b->resume();Output yang Dihasilkan:
Fiber A: hitung = 1
Fiber B: hitung = 100
Fiber A: hitung = 11
Fiber B: hitung = 110Variabel $hitung milik masing-masing fiber bernilai 1 dan 100 saat dijeda, dan setelah resume() nilai-nilai tersebut bertambah sepuluh dari titik masing-masing — bukan dari nilai fiber lain. Perlu dicatat bahwa variabel global dan static tetap dibagikan antar fiber; yang lokal per-fiber hanyalah call stack beserta variabel lokalnya.
Fiber vs Generator #
Keduanya sering disamakan karena sama-sama bisa “pause”. Pembeda utamanya adalah arah kendali:
| Aspek | Generator | Fiber |
|---|---|---|
| Model kendali | Pull: pemanggil menarik nilai lewat foreach/current() | Push: fiber menyerahkan kendali sendiri lewat Fiber::suspend() |
| Titik jeda | Hanya yield di body function generator | Fiber::suspend() di kedalaman call stack mana pun |
| Perantara | Harus diberi tahu (yield from tiap lapisan) | Tidak perlu tahu |
| Komunikasi data | Menghasilkan nilai berurutan ke pemanggil | Dua arah: nilai bolak-balik antara fiber dan pemanggil |
| Kasus pakai wajar | Iterator, stream data bertahap | Primitif untuk event loop dan scheduler |
Aturan praktisnya sederhana: kalau kebutuhannya mengiterasi rangkaian data, generator sudah cukup dan jauh lebih sederhana — bahasannya ada di chapter generators di Section Advanced Object Model . Fiber masuk akal ketika kita membangun infrastruktur yang harus menjadwalkan eksekusi banyak tugas.
Siapa yang Sebaiknya Memakai Fiber #
Jawaban jujurnya: hampir tidak ada kode aplikasi yang perlu menyentuh class Fiber secara langsung. Fiber sengaja dihadirkan sebagai fondasi bagi pustaka concurrency — Revolt event loop (basis dari AMPHP v3) dan berbagai komponen asynchronous memakainya untuk menjadwalkan tugas I/O tanpa menulis ulang kode menjadi callback berantai. Ketika kita memakai HTTP client asynchronous lewat framework, kemampuan “menunda” eksekusi yang kita rasakan di balik layar adalah fiber yang bekerja.
Ringkasan #
Fiber memberi PHP kemampuan berhenti dan melanjutkan eksekusi di titik mana pun dalam call stack, dengan kanal nilai dua arah antara fiber dan pemanggilnya, serta stack lokal yang terisolasi per fiber. Bedanya dari generator terletak pada arah kendali: generator ditarik pemanggil, fiber mendorong sendiri keputusannya berhenti. Untuk aplikasi biasa, fiber adalah lapisan fondasi yang cukup dipahami konsepnya — event loop seperti Revolt dan AMPHP yang berdiri di atasnya. Kita turun dari lapisan bahasa paling eksotis ini ke fitur yang menyentuh kode sehari-hari secara langsung: property hooks, cara baru mendefinisikan getter dan setter di PHP 8.4.