Banyak proses di aplikasi membutuhkan “kanvas sesaat”: hasil ekspor yang sedang dirakit sebelum dikirim ke pengguna, data antara saat memproses berkas besar, atau berkas upload yang baru tiba dan menunggu divalidasi. Membuat berkas biasa dengan fopen() lalu lupa menghapusnya membuat sampah menumpuk di server dari waktu ke waktu. PHP menyediakan mekanisme khusus untuk berkas semacam ini — mulai dari fungsi yang menghapus otomatis sampai pola pembersihan yang tetap berjalan saat skrip mati di tengah jalan. Pada Chapter ini kita pelajari semuanya secara praktis.

Direktori Temporer Sistem #

Setiap sistem operasi menyediakan direktori khusus untuk berkas sementara, dan PHP membuka lokasinya melalui sys_get_temp_dir(). Fungsi ini penting karena lokasi tersebut tidak sama di semua lingkungan: Linux umumnya memakai /tmp, sedangkan Windows memakai folder di dalam C:\Windows. Dengan memakai fungsi ini, kode Anda tidak perlu menebak lokasi tersebut secara manual.

php
<?php
$direktori = sys_get_temp_dir();
echo "Direktori temporer: " . $direktori . "\n";
var_dump(is_dir($direktori));
var_dump(is_writable($direktori));

Output yang Dihasilkan:

text
Direktori temporer: /tmp
bool(true)
bool(true)

Direktori ini dapat ditulis oleh semua proses, sehingga cocok sebagai lokasi kerja sementara. Ingat pula bahwa banyak sistem Linux membersihkan /tmp saat komputer dinyalakan ulang — jangan pernah menyimpan data penting jangka panjang di sini. Direktori inilah juga yang dipakai PHP untuk menampung berkas upload sebelum dipindahkan, topik yang akan kita bahas tuntas pada Chapter Uploads dan File Storage.

tmpfile() untuk Berkas Tanpa Nama #

Jika berkas sementara hanya dipakai di dalam skrip yang sama, tmpfile() adalah pilihan paling praktis. Fungsi ini langsung mengembalikan sebuah stream handle untuk berkas baru — tanpa nama yang perlu Anda kelola — dengan jaminan berkasnya terhapus otomatis saat handle ditutup dengan fclose() ataupun saat eksekusi skrip berakhir, mana yang terjadi lebih dahulu.

php
<?php
$handle = tmpfile();

fwrite($handle, "Data ekspor sementara\n");
rewind($handle);
echo stream_get_contents($handle);

$uri = stream_get_meta_data($handle)["uri"];
var_dump(file_exists($uri));

fclose($handle);
var_dump(file_exists($uri));

Output yang Dihasilkan:

text
Data ekspor sementara
bool(true)
bool(false)

Mari kita telaah baris-barisnya: tmpfile() mengembalikan handle yang langsung bisa ditulis dengan fwrite(), lalu rewind() memutar posisi baca-tulis kembali ke awal agar isi bisa dibaca. Fungsi stream_get_meta_data($handle)["uri"] mengembalikan path berkas temporer di balik handle tersebut — di sini kita memakainya untuk membuktikan siklus hidupnya. Hasilnya tampak jelas: berkasnya ada selama handle terbuka, lalu lenyap begitu fclose() dipanggil, tanpa satu baris kode pembersihan pun dari kita.

tempnam() untuk Berkas Temporer Bernama #

Kadang kita justru membutuhkan path nyata: berkasnya akan dibaca oleh program eksternal, dipakai library yang hanya menerima nama berkas, atau diproses pada tahap berbeda dari pembuatannya. Untuk situasi seperti ini, tempnam() membuat berkas temporer baru dan mengembalikan path-nya sebagai string. Nama berkasnya tersusun dari prefix yang Anda berikan ditambah rangkaian karakter acak, dan berkasnya langsung dibuat dengan permission 0600 — hanya pemilik proses yang dapat membacanya.

php
<?php
$berkas = tempnam(sys_get_temp_dir(), "ekspor-");
var_dump(is_string($berkas));

file_put_contents($berkas, "nama,kota\n");
var_dump(file_exists($berkas));

unlink($berkas);
var_dump(file_exists($berkas));

Output yang Dihasilkan:

text
bool(true)
bool(true)
bool(false)

Berbeda dengan tmpfile(), PHP tidak menghapus berkas hasil tempnam() secara otomatis — pembersihan menjadi tanggung jawab kode Anda, seperti baris unlink() di atas. Karena itu, berkas semacam ini hanya boleh dibuat ketika Anda tahu persis di titik mana berkasnya akan dihapus.

Studi Kasus: Membuat Laporan Sementara #

Mari kita rangkai kedua fungsi tadi dalam alur yang realistis: sebuah fungsi menerima data, menuliskannya sebagai laporan ke berkas temporer, dan mengembalikan path-nya ke pemanggil. Pemanggil membaca laporan tersebut, lalu membersihkannya setelah selesai.

php
<?php
function buatLaporanSementara(array $barisLaporan): string
{
    $berkas = tempnam(sys_get_temp_dir(), "laporan-");
    $handle = fopen($berkas, "w");

    foreach ($barisLaporan as $baris) {
        fwrite($handle, $baris . "\n");
    }

    fclose($handle);
    return $berkas;
}

$berkasSementara = buatLaporanSementara([
    "Penjualan Januari: 150 item",
    "Penjualan Februari: 175 item",
]);

foreach (file($berkasSementara) as $baris) {
    echo "Dibaca: " . $baris;
}

unlink($berkasSementara);
var_dump(file_exists($berkasSementara));

Output yang Dihasilkan:

text
Dibaca: Penjualan Januari: 150 item
Dibaca: Penjualan Februari: 175 item
bool(false)

Perhatikan pembagian tanggung jawabnya: fungsi buatLaporanSementara() hanya membuat dan menulis, sedangkan pemanggil yang memegang path bertugas membaca lalu menghapus dengan unlink(). Kontrak sederhana seperti ini penting supaya tidak ada pihak yang lupa membersihkan. Pemeriksaan file_exists() di akhir memangkas keraguan: setelah unlink(), berkasnya benar-benar sudah tidak ada.

Nama Unik: uniqid() versus random_bytes() #

Ada kalanya kita sendiri yang menyusun nama berkas temporer atau berkas hasil proses — misalnya menamai berkas upload yang akan disimpan permanen. Dua fungsi yang sering dipakai untuk ini punya karakter yang sangat berbeda. uniqid() menyusun nama dari waktu dalam satuan mikrodetik: ringkas dan berurutan, tetapi mudah ditebak karena siapa pun yang tahu kira-kira kapan nama itu dibuat bisa menebak nilainya. Sementara itu, random_bytes() menghasilkan byte acak kriptografis yang tidak bisa diprediksi — diubah ke string heksadesimal dengan bin2hex().

php
<?php
$namaWaktu = uniqid("img-");
$namaAman = bin2hex(random_bytes(16)) . ".jpg";

var_dump(str_starts_with($namaWaktu, "img-"));
var_dump(strlen($namaAman) === 36);

Output yang Dihasilkan:

text
bool(true)
bool(true)

Nilai acak dari kedua fungsi tersebut berubah pada setiap eksekusi, sehingga contoh di atas sengaja tidak mencetak namanya — yang diuji hanyalah properti yang pasti: prefix img- selalu menempel di depan hasil uniqid(), dan bin2hex(random_bytes(16)) selalu menghasilkan 32 karakter heksadesimal yang bila ditambah .jpg berjumlah 36 karakter. Untuk label internal dan kunci cache yang tidak sensitif, uniqid() memadai; untuk semua hal yang bisa dilihat atau ditebak pihak luar, gunakan random_bytes().

Pembersuhan Terjamin dengan register_shutdown_function() #

Kode pembersihan biasa tidak selalu sempat berjalan — exit(), die(), atau fatal error dapat menghentikan skrip sebelum baris unlink() tercapai. PHP menyediakan register_shutdown_function() untuk mengatasi hal ini: fungsi ini mendaftarkan sebuah callback yang tetap dieksekusi PHP saat eksekusi berakhir, apa pun sebab berakhirnya.

php
<?php
$berkasSementara = tempnam(sys_get_temp_dir(), "pekerjaan-");
file_put_contents($berkasSementara, "status: sedang diproses\n");

register_shutdown_function(function () use ($berkasSementara): void {
    if (file_exists($berkasSementara)) {
        unlink($berkasSementara);
        echo "Berkas temporer dibersihkan\n";
    }
});

echo "Skrip selesai bekerja\n";

Output yang Dihasilkan:

text
Skrip selesai bekerja
Berkas temporer dibersihkan

Urutan output di atas membuktikan bahwa callback shutdown berjalan setelah kode utama selesai. Closure menangkap variabel $berkasSementara lewat use supaya path berkas tetap terjaga sampai akhir, dan pemeriksaan file_exists() menjaga agar unlink() tidak memicu warning bila berkasnya ternyata sudah terhapus lebih awal. Pola ini adalah jaring pengaman terakhir untuk berkas tempnam() yang pemiliknya tersebar di banyak titik kode.

Ringkasan #

Berkas temporer adalah bagian normal dari banyak alur kerja: hasil ekspor, data antara, hingga berkas upload yang baru tiba. sys_get_temp_dir() menunjukkan lokasinya, tmpfile() memberi berkas tanpa nama yang terhapus sendiri, tempnam() memberi berkas bernama yang wajib Anda bersihkan sendiri, dan register_shutdown_function() memastikan pembersihan tetap terjadi walau skrip berakhir secara tak terduga. Nama unik terbaik lahir dari random_bytes(), bukan uniqid(). Alur yang sama persis berada di jantung mekanisme upload PHP — kita bedah tuntas pada Chapter Uploads dan File Storage.

Referensi #