Mengonsumsi API berarti memanggil layanan pihak lain dari kode PHP sendiri, membaca status dan isi responsnya, lalu menangani kegagalannya secara eksplisit. Topik ini membangun client dengan ekstensi curl yang tersedia di PHP dan mengujinya terhadap server API jadwal piket yang ikut dibuat. Tiga kegagalan wajib ditangani, yaitu koneksi gagal, kode status bukan 200, dan body yang bukan JSON. Alternatif pustaka Guzzle dibahas singkat pada bagian akhir.
Server API jadwal untuk latihan#
Server latihan berikut melayani data jadwal piket dalam format JSON. Parameter mode=salah disediakan untuk menyimulasikan keadaan buruk, yaitu kode status 500 dengan isi yang bukan JSON, sehingga penanganan error client dapat diuji tanpa layanan luar.
<?php
declare(strict_types=1);
header('Content-Type: application/json; charset=utf-8');
$jadwal = [
['hari' => 'Senin', 'petugas' => 'Ali'],
['hari' => 'Rabu', 'petugas' => 'Citra'],
];
// mode=salah menyimulasikan API yang menjawab 500 dengan isi bukan JSON
if (($_GET['mode'] ?? '') === 'salah') {
http_response_code(500);
echo 'Terjadi kegagalan internal yang tidak dapat dipahami';
return;
}
echo json_encode($jadwal, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT), "\n";Menjalankan berkas ini tanpa parameter mencetak jadwal sebagai JSON, dan isi itulah yang nantinya diminta client.
[
{
"hari": "Senin",
"petugas": "Ali"
},
{
"hari": "Rabu",
"petugas": "Citra"
}
]Pada bagian Menjalankan kedua jalur, berkas ini disajikan lewat server bawaan. Strukturnya sengaja sederhana karena fokus topik ini berada di sisi client, sedangkan pembangunan API lengkap sudah dibahas pada topik Membangun API JSON.
Client dengan curl#
Ekstensi curl bekerja melalui objek handle yang menampung satu koneksi beserta opsinya. Opsi CURLOPT_RETURNTRANSFER wajib disetel, karena tanpanya curl_exec() mencetak isi respons langsung ke keluaran dan mengembalikan true atau false saja. Dua opsi batas waktu disertakan sejak awal sebagai kebiasaan yang baik, dan penjelasan mendalamnya menjadi isi topik Disiplin Client HTTP. Client ini dijalankan oleh blok bash pada bagian Menjalankan kedua jalur, bukan langsung dengan php, karena permintaannya ditujukan ke server yang harus dinyalakan lebih dulu.
| Opsi | Nilai pada contoh | Peran |
|---|---|---|
CURLOPT_RETURNTRANSFER | true | Isi respons dikembalikan sebagai string, bukan dicetak langsung |
CURLOPT_CONNECTTIMEOUT | 2 | Batas detik membuka koneksi |
CURLOPT_TIMEOUT | 5 | Batas detik keseluruhan permintaan |
Alamat layanan ditulis langsung pada kode demi kesederhanaan contoh. Pada proyek nyata, alamat dasar (base URL) dan kredensialnya ditempatkan pada variabel lingkungan atau berkas konfigurasi, sehingga alamat server uji dan server produksi dapat ditukar tanpa mengubah kode client.
// tidak-dijalankan
// client dimuat lewat blok bash setelah server dinyalakan pada port 8150
<?php
declare(strict_types=1);
// alamat API: jalur normal, atau mode=salah untuk menunjukkan penanganan error
$mode = ($argv[1] ?? '') === 'salah' ? '?mode=salah' : '';
$alamat = 'http://localhost:8150/' . $mode;
$ch = curl_init($alamat);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CONNECTTIMEOUT => 2,
CURLOPT_TIMEOUT => 5,
]);
$isi = curl_exec($ch);
$kode = (int) curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
$nomorError = curl_errno($ch);
$pesanError = curl_error($ch);
curl_close($ch);
if ($isi === false) {
echo "Permintaan gagal (error $nomorError): $pesanError", "\n";
return;
}
if ($kode !== 200) {
echo "API menjawab dengan kode status $kode", "\n";
return;
}
try {
$jadwal = json_decode($isi, true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException) {
echo "Isi respons bukan JSON yang valid", "\n";
return;
}
foreach ($jadwal as $baris) {
echo $baris['hari'], ': ', $baris['petugas'], "\n";
}Pemeriksaan tersusun berlapis dari yang paling ringan. Kegagalan transport ditandai nilai kembalian false beserta curl_errno(), kegagalan logis ditandai kode status yang berbeda dari 200, dan isi yang tidak sah ditandai JsonException. Ketiga lapisan tersebut menyaring kegagalan yang berbeda sifatnya, sehingga satu if tunggal tidak cukup.
Data yang lolos ketiga lapisan tetap tidak boleh diperlakukan sebagai kebenaran. Respons dari layanan luar adalah masukan dari pihak ketiga, dan modul Keamanan menegaskan prinsip yang sama untuk data pengguna, yaitu tidak memercayai apa pun tanpa pemeriksaan. Struktur bidang yang diharapkan, misalnya keberadaan kunci hari dan petugas, sebaiknya divalidasi sebelum nilainya dipakai. Perubahan bentuk respons dari layanan luar tidak pernah diumumkan lebih dahulu.
Contoh pada topik ini memakai GET, dan permintaan yang mengirim data menambah dua opsi lagi, yaitu CURLOPT_POST bersama CURLOPT_POSTFIELDS untuk isi yang dikirim serta CURLOPT_HTTPHEADER untuk menandai Content-Type yang sesuai. Parameter pada URL dibangun dengan http_build_query() alih-alih disambung secara manual, karena fungsi itu sekaligus menangani pelolosan (escaping) karakter khusus pada setiap nilai.
Menjalankan kedua jalur#
Server dinyalakan sekali, lalu client dipanggil dua kali dengan argumen berbeda. Pemanggilan pertama mengambil jadwal, sedangkan pemanggilan kedua dengan argumen salah memicu respons 500 yang bukan JSON.
# jalankan server API jadwal pada port 8150
php -S localhost:8150 api-jadwal.php >/dev/null 2>&1 &
sleep 1
# ambil jadwal melalui client
php klien-jadwal.php
# ulangi dengan mode=salah untuk melihat penanganan error
php klien-jadwal.php salah
kill %1Senin: Ali
Rabu: Citra
API menjawab dengan kode status 500Pada jalur kedua, client berhenti pada lapisan status sebelum mencoba json_decode(), sehingga pesan yang muncul menjelaskan kode 500 dan bukan error parsing. Kombinasi ini menunjukkan urutan pemeriksaan yang tepat, yaitu transport lebih dulu, kemudian status, kemudian isi.
Guzzle sebagai alternatif#
Guzzle adalah pustaka HTTP yang paling banyak dipakai di ekosistem PHP dan biasanya dipasang lewat Composer. Client yang sama ditulis lebih ringkas, dan pengecualian RequestException menampung berbagai kegagalan sekaligus.
// tidak-dijalankan
// contoh membutuhkan guzzlehttp/guzzle yang dipasang lewat Composer, dan
// berkas vendor/ tidak dibawa oleh bundle topik ini
use GuzzleHttp\Client;
$client = new Client(['timeout' => 5]);
try {
$respons = $client->get('http://localhost:8150/');
$jadwal = json_decode((string) $respons->getBody(), true, 512, JSON_THROW_ON_ERROR);
} catch (RequestException $e) {
echo "Permintaan gagal: ", $e->getMessage(), "\n";
}Ekstensi curl dan Guzzle menyelesaikan pekerjaan yang sama, sehingga pemilihan biasanya ditentukan kebutuhan proyek. Proyek tanpa Composer cukup memakai curl yang sudah menyertai PHP, sedangkan proyek dengan Composer cenderung memilih Guzzle karena antarmukanya lebih ramah untuk deretan permintaan yang kompleks. Kedua pendekatan sama-sama menuntut disiplin yang dibahas pada topik Disiplin Client HTTP, yaitu batas waktu, percobaan ulang, dan validasi respons.
Kesalahan umum#
- Lupa
CURLOPT_RETURNTRANSFER => true. Isi respons tercetak langsung dancurl_exec()hanya mengembalikan boolean, sehingga data tidak pernah sampai ke variabel. - Membaca isi respons tanpa memeriksa kode status. Body dari respons 500 diproses seolah-olah data yang sah, dan kegagalan baru terlihat jauh kemudian dalam bentuk data kosong atau error parsing.
- Memakai
json_decode()tanpa pemeriksaan. Body HTML dari halaman error menghasilkannulltanpa pesan apa pun, dan perulangan atasnullmelempar error yang mengarahkan pencarian masalah ke tempat yang keliru. - Mengabaikan
curl_errno()pada kegagalan transport. Pesan daricurl_error()menyebut penyebab persisnya, misalnya resolusi nama gagal atau batas waktu terlampaui, sehingga pencarian penyebab berlangsung cepat.
Ringkasan#
- Client curl tersusun atas
curl_init(),curl_setopt_array()denganCURLOPT_RETURNTRANSFER, lalu pembacaan hasil melaluicurl_exec(),curl_getinfo(),curl_errno(), dancurl_error(). - Pemeriksaan berlapis mengurutkan kegagalan transport, kode status, dan validitas JSON, dan setiap lapisan menyaring jenis kegagalan yang berbeda.
- Parameter
mode=salahpada server latihan memungkinkan respons buruk diuji tanpa layanan luar, sehingga penanganan error terbukti lewat keluaran nyata. - Guzzle menyederhanakan kode client untuk proyek berbasis Composer, sedangkan curl tersedia tanpa pemasangan dan cukup untuk kebutuhan sehari-hari.
Referensi#
- PHP: cURL — Manual — Fungsi inti ekstensi curl beserta konstanta opsinya.
- Guzzle Documentation
— Dokumentasi resmi pustaka Guzzle beserta penanganan
RequestException. - PHP: JsonException — Manual
— Pengecualian yang dilempar bersama flag
JSON_THROW_ON_ERROR.