Mekanisme autoloading yang kita bahas pada chapter sebelumnya
memakai satu fungsi ajaib bernama __autoload(). Satu proyek nyata jarang cukup dengan satu fungsi saja: framework membawa autoloadernya sendiri, paket plugin punya aturan pencarian berkas yang berbeda, dan kode aplikasi kita perlu aturan tambahan lagi. Standard PHP Library (SPL) menyediakan solusi berupa antrean autoloader yang bisa diisi banyak callable, diurutkan, diinspeksi, bahkan dicabut saat runtime. Pada chapter ini kita bedah satu per satu fungsi SPL yang mengatur antrean tersebut.
Dari __autoload() ke Antrean SPL #
Fungsi ajaib __autoload() hanya mengizinkan satu implementasi untuk seluruh proyek; dua pustaka yang sama-sama mendefinisikan __autoload() akan saling menimpa. Karena keterbatasan itulah __autoload() ditandai deprecated sejak PHP 7.2 dan akhirnya dihapus sepenuhnya di PHP 8.0. Penggantinya adalah spl_autoload_register(), yang menyimpan setiap autoloader sebagai callable di dalam sebuah antrean.
SPL menyediakan enam fungsi utama untuk mengelola antrean tersebut:
| Fungsi | Peran |
|---|---|
spl_autoload_register() | menambah callable baru ke akhir antrean |
spl_autoload_unregister() | mencabut callable dari antrean |
spl_autoload_functions() | mengembalikan daftar callable yang terdaftar |
spl_autoload_call() | memicu antrean secara manual untuk satu nama class |
spl_autoload() | autoloader bawaan berbasis ekstensi berkas |
spl_autoload_extensions() | membaca atau mengatur ekstensi berkas yang dicoba |
Parameter pertama spl_autoload_register() menerima callable dalam bentuk apa pun, sama seperti fungsi usort() atau array_map(). Tiga bentuk yang paling sering dipakai:
<?php
// nama fungsi global sebagai string
spl_autoload_register('autoloaderLegacy');
// metode objek sebagai array [objek, namaMetode]
spl_autoload_register([$pusat, 'muat']);
// metode statis sebagai array [namaClass, namaMetode]
spl_autoload_register([PusatAutoload::class, 'muat']);Setiap callable menerima satu argumen: nama class yang sedang dicari, dalam bentuk fully qualified name.
Mendaftarkan Autoloader Pertama #
Contoh berikut menunjukkan autoloader lengkap yang benar-benar memuat class dari berkas. Agar contoh dapat berjalan mandiri tanpa berkas tambahan, skrip membuat berkas class sementara di direktori temporer sistem, lalu membersihkannya kembali di akhir eksekusi:
<?php
$direktori = sys_get_temp_dir() . '/spl-demo-' . getmypid();
mkdir($direktori . '/App/Entitas', 0777, true);
file_put_contents(
$direktori . '/App/Entitas/Pelanggan.php',
"<?php\n\nnamespace App\\Entitas;\n\nclass Pelanggan\n{\n public function __construct(public string \$nama) {}\n}\n"
);
spl_autoload_register(function (string $kelas) use ($direktori): void {
$berkas = $direktori . '/' . str_replace('\\', '/', $kelas) . '.php';
if (is_file($berkas)) {
require $berkas;
}
});
$pelanggan = new \App\Entitas\Pelanggan('Budi');
echo 'Kelas ', $pelanggan::class, " berhasil dimuat\n";
echo 'Nama pelanggan: ', $pelanggan->nama, "\n";
unlink($direktori . '/App/Entitas/Pelanggan.php');
rmdir($direktori . '/App/Entitas');
rmdir($direktori . '/App');
rmdir($direktori);Output yang Dihasilkan:
Kelas App\Entitas\Pelanggan berhasil dimuat
Nama pelanggan: BudiMari kita bedah bagian-bagian pentingnya:
- Baris 4-6 (
mkdir+file_put_contents): menyiapkan berkasApp/Entitas/Pelanggan.phpdi direktori temporer. Nama subdirektori sengaja meniru struktur namespace. - Baris 13 (
spl_autoload_register(...)): mendaftarkan closure sebagai autoloader. Closure meng-capture$direktorilewatuseagar tahu di mana harus mencari. - Baris 14-15 (pemetaan nama ke jalur):
str_replace('\\', '/', $kelas)mengubahApp\Entitas\PelangganmenjadiApp/Entitas/Pelanggan, lalu ditambah akhiran.php. Pola konversi ini adalah cikal bakal standar PSR-4 yang kita bahas di chapter berikutnya. - Baris 16-17 (
is_file+require): autoloader hanya memuat berkas yang benar-benar ada. Jika berkas tidak ditemukan, autoloader diam — antrean boleh melanjutkan ke autoloader berikutnya. - Baris 20 (
new \App\Entitas\Pelanggan): class belum pernah dideklarasikan, sehingga PHP memicu antrean autoloader sebelum memberi up. Autoloader kita memuat berkasnya, deklarasi class pun tersedia. - Baris 24-27 (
unlink+rmdir): membersihkan berkas dan direktori sementara.
Urutan Eksekusi Antrean #
Ketika beberapa autoloader terdaftar, PHP memanggil autoloader dalam urutan pendaftaran: yang didaftarkan lebih dulu dieksekusi lebih dulu. PHP berhenti begitu class yang diminta berhasil dideklarasikan oleh salah satu autoloader. Perhatikan eksperimen berikut, yang memakai class_exists() untuk memicu antrean tanpa perlu menginstansiasi apa pun:
<?php
$jejak = [];
spl_autoload_register(function (string $kelas) use (&$jejak): void {
$jejak[] = "loader pertama menerima: $kelas";
});
spl_autoload_register(function (string $kelas) use (&$jejak): void {
$jejak[] = "loader kedua menerima: $kelas";
});
var_dump(class_exists('KelasFiktif'));
echo implode("\n", $jejak), "\n";Output yang Dihasilkan:
bool(false)
loader pertama menerima: KelasFiktif
loader kedua menerima: KelasFiktifKedua autoloader menerima nama KelasFiktif karena tidak ada yang berhasil mendefinisikannya, dan class_exists() mengembalikan bool(false). Jejak menunjukkan loader pertama dieksekusi sebelum loader kedua — urutan pendaftaran dipertahankan, bukan dibalik.
Mengintip Antrean dengan spl_autoload_functions() #
Fungsi spl_autoload_functions() mengembalikan array berisi seluruh callable terdaftar, berguna untuk men-debug konflik antrean pada proyek besar. Untuk callable berupa fungsi global, elemen array berisi string nama fungsinya:
<?php
function autoloaderProyek(string $kelas): void
{
// dalam proyek nyata: mencari berkas lalu require
}
spl_autoload_register('autoloaderProyek');
spl_autoload_register('spl_autoload');
print_r(spl_autoload_functions());Output yang Dihasilkan:
Array
(
[0] => autoloaderProyek
[1] => spl_autoload
)Output print_r memperlihatkan dua string: autoloaderProyek (loader kita) dan spl_autoload (loader bawaan SPL). Jika yang terdaftar adalah closure atau metode, elemen array berisi objek Closure atau array callable — tetap bisa diperiksa dengan is_callable().
Loader Bawaan spl_autoload dan Ekstensi Berkas #
SPL membawa autoloader sederhana bernama spl_autoload(). Alih-alih mencari di direktori tertentu, loader bawaan ini menggabungkan nama class dengan setiap ekstensi berkas yang diizinkan, lalu mencarinya di include_path. Daftar ekstensi tersebut dibaca dan diatur lewat spl_autoload_extensions():
<?php
echo spl_autoload_extensions(), "\n";
echo spl_autoload_extensions('.php,.class.php'), "\n";
echo spl_autoload_extensions(), "\n";Output yang Dihasilkan:
.inc,.php
.php,.class.php
.php,.class.phpBaris pertama memperlihatkan bawaan PHP: .inc,.php. Baris kedua memasang daftar baru .php,.class.php lalu langsung mengembalikannya sebagai konfirmasi, dan baris ketiga membaca kembali nilai yang tersimpan. Dengan tiga baris saja kita bisa memeriksa, mengubah, dan memastikan konfigurasi ekstensi autoloader.
Contoh berikut memasangkan spl_autoload ke antrean sebagai string, lalu mengandalkan include_path untuk menemukan berkas class:
<?php
$direktori = sys_get_temp_dir() . '/spl-auto-' . getmypid();
mkdir($direktori);
file_put_contents(
$direktori . '/faktur.php',
"<?php\n\nclass Faktur\n{\n public const SATUAN = 'lembar';\n}\n"
);
spl_autoload_extensions('.php');
spl_autoload_register('spl_autoload');
set_include_path($direktori . PATH_SEPARATOR . get_include_path());
$faktur = new Faktur();
echo 'Kelas ', $faktur::class, " termuat lewat spl_autoload\n";
echo 'Satuan: ', Faktur::SATUAN, "\n";
unlink($direktori . '/faktur.php');
rmdir($direktori);Output yang Dihasilkan:
Kelas Faktur termuat lewat spl_autoload
Satuan: lembarBaris spl_autoload_register('spl_autoload') mendaftarkan loader bawaan di akhir antrean — pola klasik untuk menambahkan fallback. Baris set_include_path(...) menyisipkan direktori sementara ke depan include_path yang lama tanpa menghapusnya.
Memicu dan Mencabut Autoloader #
Dua fungsi pelengkap menuntaskan antrean. spl_autoload_call() memicu antrean untuk satu nama class secara manual, dan spl_autoload_unregister() mencabut callable dari antrean serta mengembalikan true bila pencabutan berhasil:
<?php
$jejak = [];
$loaderLama = function (string $kelas) use (&$jejak): void {
$jejak[] = "loader lama: $kelas";
};
$loaderBaru = function (string $kelas) use (&$jejak): void {
$jejak[] = "loader baru: $kelas";
};
spl_autoload_register($loaderLama);
spl_autoload_register($loaderBaru);
spl_autoload_call('Servis\Email');
echo implode("\n", $jejak), "\n\n";
var_dump(spl_autoload_unregister($loaderLama));
$jejak = [];
spl_autoload_call('Servis\Sms');
echo implode("\n", $jejak), "\n";Output yang Dihasilkan:
loader lama: Servis\Email
loader baru: Servis\Email
bool(true)
loader baru: Servis\SmsDua baris pertama keluaran berasal dari spl_autoload_call('Servis\Email'): kedua loader dieksekusi sesuai urutan pendaftaran. Fungsi spl_autoload_unregister($loaderLama) kemudian mengembalikan bool(true). Setelah jejak dikosongkan, pemanggilan kedua hanya menjalankan loader baru — bukti loader lama benar-benar sudah tidak berada di antrean.
Ringkasan #
SPL mengubah autoloading dari satu fungsi ajaib menjadi antrean callable yang terkelola: spl_autoload_register() menambah, spl_autoload_functions() menginspeksi, spl_autoload_unregister() mencabut, dan spl_autoload_call() memicu secara manual. Autoloader dieksekusi sesuai urutan pendaftaran dan berhenti begitu class ditemukan. Loader bawaan spl_autoload() berguna sebagai fallback sederhana, tetapi kebiasaannya menormalkan nama berkas ke huruf kecil membuatnya rawan jebakan di sistem berkas Linux. Kita butuh konvensi yang lebih ketat dan portabel — di chapter berikutnya kita bahas standar industri PSR-4 beserta aturan pemetaan nama class ke jalur berkas.