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:

FungsiPeran
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
<?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
<?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:

text
Kelas App\Entitas\Pelanggan berhasil dimuat
Nama pelanggan: Budi

Mari kita bedah bagian-bagian pentingnya:

  1. Baris 4-6 (mkdir + file_put_contents): menyiapkan berkas App/Entitas/Pelanggan.php di direktori temporer. Nama subdirektori sengaja meniru struktur namespace.
  2. Baris 13 (spl_autoload_register(...)): mendaftarkan closure sebagai autoloader. Closure meng-capture $direktori lewat use agar tahu di mana harus mencari.
  3. Baris 14-15 (pemetaan nama ke jalur): str_replace('\\', '/', $kelas) mengubah App\Entitas\Pelanggan menjadi App/Entitas/Pelanggan, lalu ditambah akhiran .php. Pola konversi ini adalah cikal bakal standar PSR-4 yang kita bahas di chapter berikutnya.
  4. 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.
  5. 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.
  6. 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
<?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:

text
bool(false)
loader pertama menerima: KelasFiktif
loader kedua menerima: KelasFiktif

Kedua 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
<?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:

text
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
<?php
echo spl_autoload_extensions(), "\n";
echo spl_autoload_extensions('.php,.class.php'), "\n";
echo spl_autoload_extensions(), "\n";

Output yang Dihasilkan:

text
.inc,.php
.php,.class.php
.php,.class.php

Baris 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
<?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:

text
Kelas Faktur termuat lewat spl_autoload
Satuan: lembar

Baris 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
<?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:

text
loader lama: Servis\Email
loader baru: Servis\Email

bool(true)
loader baru: Servis\Sms

Dua 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.

Referensi #