Sebagian besar aplikasi PHP nyata berhubungan dengan berkas: membaca konfigurasi, mengimpor data CSV, memuat template, hingga menganalisis file log yang terus bertambah besar. PHP menyediakan beberapa fungsi baca dengan tingkat kendali dan konsumsi memori yang berbeda-beda, jadi memilih pendekatan yang tepat adalah keputusan teknis yang perlu kita pahami sejak awal. Pada chapter ini kita akan membaca isi berkas dengan empat pendekatan — file_get_contents, fopen dengan fgets, fread, dan file() — lalu menutup dengan pembahasan path yang aman serta penanganan berkas yang gagal dibaca. Semua contoh membuat berkas latihan sendiri lalu membersihkannya di akhir skrip, sehingga Anda bisa menjalankan setiap potongan kode tanpa persiapan apa pun.

Membaca Seluruh Isi File dengan file_get_contents #

Cara membaca paling ringkas adalah fungsi file_get_contents(): fungsi tersebut menerima sebuah path, membaca seluruh isi berkas, lalu mengembalikannya sebagai satu string. Ketika berkas tidak dapat dibaca, fungsi ini mengembalikan false sekaligus memicu Warning — perilaku ini kita bedah pada bagian akhir chapter. Perhatikan contoh berikut, yang lebih dulu menyiapkan berkas latihan memakai file_put_contents() (mekanisme penulisannya kita bahas tuntas pada chapter Menulis File):

php
<?php
$berkas = __DIR__ . '/daftar-belanja.txt';
file_put_contents($berkas, "Belajar PHP Dasar\nBuku Tulis\nPulpen");

$isi = file_get_contents($berkas);
echo $isi . "\n";
echo "Panjang isi: " . strlen($isi) . " byte\n";

unlink($berkas);

Output yang Dihasilkan:

text
Belajar PHP Dasar
Buku Tulis
Pulpen
Panjang isi: 35 byte

Mari kita telaah apa yang terjadi pada baris-baris kode di atas:

  1. Baris 2 ($berkas = __DIR__ . '/daftar-belanja.txt';): menyimpan path berkas latihan yang dibangun dari __DIR__, yaitu direktori tempat berkas skrip ini berada.
  2. Baris 3 (file_put_contents(...)): membuat berkas latihan berisi tiga baris teks agar contoh ini bisa dijalankan tanpa persiapan.
  3. Baris 5 ($isi = file_get_contents($berkas);): membaca seluruh isi berkas ke memori dan mengikatnya ke variabel $isi sebagai satu string, termasuk semua karakter baris barunya.
  4. Baris 7 (echo $isi . "\n";): mencetak isi string apa adanya; tiga baris teks muncul karena string tersebut memang berisi \n.
  5. Baris 8 (strlen($isi)): menghitung jumlah byte string — ingat bahwa \n juga dihitung sebagai satu byte.
  6. Baris 10 (unlink($berkas);): menghapus berkas latihan agar tidak menyisakan sampah di direktori proyek.

Membaca Baris demi Baris dengan fopen dan fgets #

Banyak berkas — log, daftar peserta, ekspor CSV — paling alami diproses satu baris sekaligus. Pola untuk kebutuhan ini adalah membuka berkas dengan fopen(), lalu memanggil fgets() berulang kali sampai fungsi tersebut mengembalikan false sebagai penanda akhir berkas. Setelah selesai, handle yang dikembalikan fopen() wajib ditutup dengan fclose() agar sistem operasi melepaskan sumber dayanya:

php
<?php
$berkas = __DIR__ . '/catatan.txt';
file_put_contents($berkas, "Baris pertama\nBaris kedua\nBaris ketiga\n");

$handle = fopen($berkas, 'r');
if ($handle === false) {
    exit("Gagal membuka berkas.\n");
}

while (($baris = fgets($handle)) !== false) {
    echo rtrim($baris, "\r\n") . "\n";
}

fclose($handle);
unlink($berkas);

Output yang Dihasilkan:

text
Baris pertama
Baris kedua
Baris ketiga

Mari kita bedah bagian-bagian pentingnya:

  1. Baris 5 (fopen($berkas, 'r')): membuka berkas hanya untuk dibaca (mode 'r') dan mengembalikan sebuah stream resource yang menjadi pegangan kita selama membaca; bila gagal, nilai kembaliannya adalah false.
  2. Baris 9 (while (($baris = fgets($handle)) !== false)): idiom khas PHP — penugasan di dalam kondisi loop. fgets() mengambil satu baris, loop berhenti ketika tidak ada baris lagi (false).
  3. Baris 10 (rtrim($baris, "\r\n")): membuang karakter baris baru di ujung string, lalu echo menambahkannya kembali — hasilnya sama di layar, tetapi nilai $baris sudah bersih untuk diproses lebih lanjut.
  4. Baris 13 (fclose($handle)): menutup handle; langkah ini juga otomatis dipanggil saat skrip berakhir, tetapi menutup secara eksplisit segera setelah selesai adalah kebiasaan yang baik.

Membaca per Potongan Byte dengan fread dan feof #

Tidak semua berkas terstruktur per baris: berkas biner seperti gambar atau arsip lebih cocok dibaca dalam potongan berukuran tetap. Fungsi fread() menerima handle dan jumlah byte maksimum, lalu mengembalikan potongan data; fungsi feof() memberi tahu apakah penunjuk posisi sudah berada di akhir berkas. Kombinasi keduanya menghasilkan loop baca bertahap berikut:

php
<?php
$berkas = __DIR__ . '/kode.txt';
file_put_contents($berkas, 'ABCDEFGH12345678');

$handle = fopen($berkas, 'rb');
while (!feof($handle)) {
    $potongan = fread($handle, 4);
    if ($potongan !== '') {
        echo "Potongan: $potongan\n";
    }
}
fclose($handle);
unlink($berkas);

Output yang Dihasilkan:

text
Potongan: ABCD
Potongan: EFGH
Potongan: 1234
Potongan: 5678

Ada dua hal penting pada contoh di atas. Pertama, mode 'rb' yang dipakai fopen() berarti baca (read) dalam mode biner (binary); pada sistem Linux mode b tidak berefek, tetapi kebiasaan menuliskannya membuat kode tetap benar saat dijalankan di Windows. Kedua, pemeriksaan $potongan !== '' diperlukan karena feof() baru melapor true setelah ada upaya baca yang melewati akhir berkas — pada iterasi terakhir fread() bisa saja mengembalikan string kosong sebelum loop berhenti.

Membaca sebagai Array Baris dengan file() #

Ketika hasil akhir yang kita butuhkan memang berupa daftar baris, fungsi file() memotong langkah: seluruh berkas dibaca sekaligus, lalu setiap baris dikembalikan sebagai elemen array. Perhatikan contoh berikut yang memakai json_encode() untuk menyingkap karakter tersembunyi di dalam elemen array:

php
<?php
$berkas = __DIR__ . '/peserta.txt';
file_put_contents($berkas, "Budi\nSiti\nAndi\n");

$apaAdanya = file($berkas);
echo "Elemen pertama: " . json_encode($apaAdanya[0]) . "\n";

$bersih = file($berkas, FILE_IGNORE_NEW_LINES);
echo "Elemen pertama: " . json_encode($bersih[0]) . "\n";
echo "Jumlah peserta: " . count($bersih) . "\n";

unlink($berkas);

Output yang Dihasilkan:

text
Elemen pertama: "Budi\n"
Elemen pertama: "Budi"
Jumlah peserta: 3

Tanpa flag tambahan, elemen array dari file() masih membawa karakter \n di ujungnya — terlihat dari hasil json_encode() yang mencetak "Budi\n". Flag FILE_IGNORE_NEW_LINES memberi tahu file() untuk membuang karakter baris baru tersebut, sehingga kita mendapatkan array bersih yang langsung siap diolah, misalnya dihitung dengan count() atau dipetakan dengan foreach.

Memilih Cara Membaca yang Tepat #

Empat pendekatan di atas tidak saling menggantikan — masing-masing menempati ceruk yang berbeda. Tabel berikut merangkum panduan memilihnya:

PendekatanBentuk hasilCocok untukMemori
file_get_contents()satu stringberkas kecil: template, konfigurasiseluruh isi dimuat
fopen() + fgets()satu baris per iterasilog besar, pemrosesan per barissatu baris per waktu
fopen() + fread()potongan byte berukuran tetapberkas biner, transfer bertahapsatu potongan per waktu
file()array berisi barisanalisis dan penghitungan barisseluruh isi dimuat

Aturan praktisnya sederhana: jika ukuran berkas bisa dipastikan kecil, pilih bentuk hasil yang paling nyaman diolah; jika ukurannya bisa membengkak, pilih pendekatan streaming dengan fgets() atau fread().

Path Aman: Relatif, Absolut, dan DIR #

Sebelum melangkah lebih jauh, kita perlu berbicara tentang cara menulis path, karena inilah sumber kegagalan baca yang paling sering membingungkan pemula. Path relatif seperti 'config/app.php' diresolve terhadap current working directory (cwd) — direktori tempat proses PHP dijalankan — yang bisa berbeda antara saat Anda menjalankan skrip dari terminal, dari cron, atau saat skrip dilayani oleh web server. Path absolut seperti '/etc/app/config.php' selalu menunjuk lokasi yang sama, tetapi menjadi tidak portabel ketika aplikasi dipindah antar mesin:

php
<?php
// Path relatif: bergantung pada direktori kerja proses PHP berjalan
$config = 'config/app.php';

// Path absolut: menunjuk lokasi yang sama di mana pun proses berjalan
$config = '/etc/app/config.php';

// Praktik terbaik: path dibangun dari __DIR__
$config = __DIR__ . '/../config/app.php';

Konstanta ajaib __DIR__ berisi direktori absolut tempat berkas skrip berada, sehingga __DIR__ . '/../config/app.php' selalu menunjuk folder config yang berada satu tingkat di atas skrip, tidak peduli dari mana skrip dijalankan. Konstanta .. berarti naik satu direktori. Seluruh contoh pada chapter ini memakai pola __DIR__ ini secara konsisten, dan Anda sebaiknya membiasakannya pada kode nyata.

Menangani File yang Tidak Dapat Dibaca #

Pada PHP 8, berkas yang hilang atau tidak dapat dibaca memicu Warning — bukan exception — lalu fungsi pembaca mengembalikan false. Ada dua pola umum untuk menghadapinya. Pola pertama adalah memeriksa kelayakan berkas lebih dulu dengan is_readable():

php
<?php
$berkas = __DIR__ . '/data-hilang.txt';

if (is_readable($berkas)) {
    echo file_get_contents($berkas);
} else {
    echo "Berkas tidak ditemukan atau tidak dapat dibaca.\n";
}

Output yang Dihasilkan:

text
Berkas tidak ditemukan atau tidak dapat dibaca.

Pola kedua mengubah Warning menjadi exception dengan set_error_handler(), sehingga kegagalan baca dapat ditangani oleh blok try/catch seperti error lainnya. Pola ini yang dipakai banyak framework modern:

php
<?php
set_error_handler(function (int $level, string $message): bool {
    throw new ErrorException($message, $level);
});

try {
    file_get_contents(__DIR__ . '/data-hilang.txt');
} catch (ErrorException) {
    echo "Gagal membaca berkas — program tetap berjalan.\n";
} finally {
    restore_error_handler();
}

Output yang Dihasilkan:

text
Gagal membaca berkas — program tetap berjalan.

Mari kita bedah pola kedua ini: handler yang dipasang set_error_handler() menerima setiap Warning yang muncul dan langsung membungkusnya menjadi objek ErrorException lalu melemparnya. Blok catch (ErrorException) menangkap lemparan tersebut — perhatikan bahwa nama exception ditulis tanpa variabel penampung karena pesan aslinya (yang berisi path absolut) tidak kita perlukan. Blok finally memasang kembali error handler bawaan agar handler khusus ini tidak memengaruhi kode di luar blok try.

Ringkasan #

Kita telah menguasai empat cara membaca berkas — file_get_contents() untuk isi utuh, fgets() untuk baris demi baris, fread() untuk potongan byte, dan file() untuk array baris — beserta kriteria memilihnya berdasarkan ukuran berkas dan bentuk data. Kita juga membiasakan path berbasis __DIR__ yang kebal terhadap perbedaan direktori kerja, dan dua pola penanganan berkas yang gagal dibaca. Dengan kemampuan membaca di tangan, langkah berikutnya yang alami adalah menciptakan berkas sendiri: chapter Menulis File akan mengajari file_put_contents(), fwrite(), dan mode-mode pembukaan yang menentukan nasib isi lama.

Referensi #