Magic Method

__toString, __get/__set, dan __invoke.

Daftar isi

Magic method adalah metode bernama khusus yang PHP panggil secara otomatis pada saat-saat tertentu, misalnya ketika objek dikonversi menjadi teks, ketika propertinya diakses dari luar jangkauan, atau ketika objek dipanggil seolah fungsi. Topik ini membahas tiga anggotanya yang paling sering dipakai, yaitu __toString(), __get() dan __set(), serta __invoke(). Nama-nama yang diawali dua garis bawah itu dicadangkan untuk PHP, sehingga tidak boleh dipakai sebagai nama metode biasa, dan contoh pada topik ini kembali menggunakan domain perpustakaan.

Mengubah objek menjadi teks dengan __toString#

Metode __toString() menentukan teks apa yang mewakili sebuah objek ketika objek itu dipakai dalam konteks string, misalnya pada echo, interpolasi, atau konkatenasi. PHP memanggilnya secara otomatis tanpa perlu menyebut namanya, dan metode ini wajib dideklarasikan public serta wajib mengembalikan nilai string. Tanpa __toString(), percobaan echo terhadap objek memicu error karena PHP tidak tahu cara mengubah objek menjadi teks.

Metode ini cocok untuk representasi ringkas yang mudah dibaca manusia, misalnya label koleksi pada daftar peminjaman atau pesan konfirmasi. Logika yang berat sebaiknya tidak ditaruh di dalamnya, karena setiap konteks string akan memanggilnya berulang kali tanpa terlihat di titik pemanggilan.

Skrip berikut mendefinisikan __toString() pada kelas Buku, lalu memakai objeknya di tiga konteks string yang berbeda, yaitu echo, konkatenasi, dan interpolasi.

php to-string.php
<?php
declare(strict_types=1);

class Buku
{
    public function __construct(
        private string $judul,
        private string $penulis,
    ) {
    }

    public function __toString(): string
    {
        return "{$this->judul} ({$this->penulis})";
    }
}

$buku = new Buku('Algoritma Dasar', 'Citra');

echo $buku, "\n";
echo 'Dipinjam: ' . $buku . "\n";
echo "Kartu: [{$buku}]", "\n";
text Keluaran
Algoritma Dasar (Citra)
Dipinjam: Algoritma Dasar (Citra)
Kartu: [Algoritma Dasar (Citra)]

Ketiga baris keluaran berasal dari sumber yang sama, yaitu nilai kembalian __toString(), dan tidak ada satu pun pemanggilan yang menyebut nama metodenya. Konversi eksplisit dengan (string) $buku juga menjalankan metode yang sama, sehingga representasi teks objek tetap konsisten di semua jalur.

Menangkap akses properti dengan __get dan __set#

Metode __get() dan __set() berjalan ketika properti yang dibaca atau ditulis tidak dapat dijangkau, misalnya karena bersifat private, protected, atau memang tidak ada. Mekanisme ini disebut property overloading, yaitu PHP meneruskan akses tersebut ke metode khusus milik kelas, dengan nama properti yang diminta diteruskan sebagai argumen. Apabila properti bersangkutan public dan benar-benar ada, aksesnya berjalan langsung dan kedua metode ini tidak pernah dipanggil.

Pola pemakaiannya adalah menyimpan data internal dalam keadaan tersembunyi, lalu menetapkan aturan pada jalur aksesnya. __set() menjadi tempat validasi sebelum nilai diterima, sedangkan __get() menjadi tempat penyaringan sebelum nilai dibaca. Metode pendampingnya, yaitu __isset() dan __unset(), menangani pemeriksaan isset() dan penghapusan unset() dengan mekanisme yang sama.

Skrip berikut menyimpan data kartu anggota dalam properti private bertipe array dan membuka aksesnya lewat __get() dan __set(). Dua aturan ditetapkan, yaitu nilai poin harus bilangan bulat nonnegatif dan nama properti harus dikenal. Pelanggaran atas keduanya dilempar sebagai exception dan ditangkap agar pesannya terlihat.

php get-set.php
<?php
declare(strict_types=1);

class KartuAnggota
{
    private array $data = [
        'nama' => 'Dewi',
        'poin' => 10,
    ];

    public function __get(string $nama): mixed
    {
        if (!array_key_exists($nama, $this->data)) {
            throw new OutOfRangeException("Properti {$nama} tidak dikenal.");
        }
        return $this->data[$nama];
    }

    public function __set(string $nama, mixed $nilai): void
    {
        if ($nama === 'poin' && (!is_int($nilai) || $nilai < 0)) {
            throw new InvalidArgumentException('poin harus bilangan bulat nonnegatif.');
        }
        if (!array_key_exists($nama, $this->data)) {
            throw new OutOfRangeException("Properti {$nama} tidak dikenal.");
        }
        $this->data[$nama] = $nilai;
    }
}

$kartu = new KartuAnggota();

echo $kartu->nama, "\n";
$kartu->poin = 15;
echo $kartu->poin, "\n";

try {
    $kartu->poin = -3;
} catch (Throwable $e) {
    echo get_class($e), ': ', $e->getMessage(), "\n";
}

try {
    echo $kartu->alamat;
} catch (Throwable $e) {
    echo get_class($e), ': ', $e->getMessage(), "\n";
}
text Keluaran
Dewi
15
InvalidArgumentException: poin harus bilangan bulat nonnegatif.
OutOfRangeException: Properti alamat tidak dikenal.

Baris Dewi dan 15 membuktikan jalur baca dan tulis yang sah, sedangkan dua baris terakhir menunjukkan aturan yang ditegakkan di dalam metode penangkapnya. Tanpa __set(), penulisan $kartu->poin = -3; pada properti yang tersembunyi akan memicu error akses langsung, dan dengan __set() penulisan itu berubah menjadi pemeriksaan yang dapat menolak nilai sebelum disimpan.

Memanggil objek seperti fungsi dengan __invoke#

Metode __invoke() membuat sebuah objek dapat dipanggil seperti fungsi, yaitu $objek(argumen) menjalankan metode tersebut dengan argumen yang diteruskan. Pemeriksaan is_callable() terhadap objek semacam itu bernilai true, dan objeknya dapat diteruskan ke tempat yang menerima callable, misalnya parameter bertipe callable. Metode ini berguna ketika satu tindakan utama mewakili seluruh objek, misalnya pencari yang selalu menerima kata kunci dan mengembalikan hasil.

Skrip berikut membuat kelas Pencari yang menyimpan katalog di constructor. Metode __invoke() miliknya menyaring katalog menurut kata kunci yang diberikan. Objeknya dipanggil langsung seperti fungsi, dan keterpanggilannya diperiksa dengan is_callable().

php invoke.php
<?php
declare(strict_types=1);

class Pencari
{
    public function __construct(private array $katalog)
    {
    }

    public function __invoke(string $kata): array
    {
        $hasil = [];
        foreach ($this->katalog as $judul) {
            if (str_contains(strtolower($judul), strtolower($kata))) {
                $hasil[] = $judul;
            }
        }
        return $hasil;
    }
}

$pencari = new Pencari([
    'Algoritma Dasar',
    'Struktur Data',
    'Jaringan Komputer',
]);

$hasil = $pencari('da');
foreach ($hasil as $judul) {
    echo $judul, "\n";
}
var_dump(is_callable($pencari));
text Keluaran
Algoritma Dasar
Struktur Data
bool(true)

Dua judul pertama lolos karena keduanya memuat rangkaian huruf da, sedangkan judul ketiga tidak memuatnya. Nilai true pada baris terakhir menegaskan bahwa objek itu kini termasuk dalam kategori callable, sehingga pola ini menyatukan keadaan yang disimpan objek dengan perilaku yang dapat dipanggil.

Kesalahan umum#

  • Mengharapkan __get() dan __set() untuk properti yang ada. Kedua metode hanya menangkap akses ke properti yang tidak terjangkau atau tidak ada, sehingga properti public yang dideklarasikan tetap diakses langsung tanpa melewati validasi. Simpan data di properti private dan serahkan seluruh aksesnya kepada kedua metode tersebut.
  • Memicu rekursi di dalam metode penangkap. Menuliskan $this->nama di dalam __get() untuk properti yang tidak terjangkau memanggil __get() lagi secara berulang hingga memori habis. Akses properti internal di dalam kedua metode ini dilakukan pada penyimpanan yang terjangkau, misalnya array private milik kelasnya.
  • Mengembalikan nilai bukan string dari __toString(). Metode ini wajib menghasilkan string, dan nilai lain memicu Error yang menyebut nama metodenya pada saat konversi dilakukan. Pastikan semua jalur di dalam metode itu mengembalikan nilai string, termasuk jalur percabangannya.
  • Menyembunyikan logika penting di balik metode magic. Akses melalui __get(), __set(), dan __invoke() tidak terlihat di titik pemanggilan, sehingga pencarian pemakaian sebuah metode menjadi sulit dan alat analisis kode kehilangan petunjuk. Gunakan metode magic bila kebutuhannya memang cocok, dan pilih metode biasa untuk operasi yang sebaiknya eksplisit.

Ringkasan#

  • __toString() menyediakan representasi teks objek dan dipanggil otomatis pada echo, interpolasi, konkatenasi, dan konversi eksplisit ke string.
  • __get() dan __set() menangkap akses ke properti yang tidak terjangkau, sehingga validasi dan penyaringan dapat ditegakkan pada jalur aksesnya, dengan __isset() dan __unset() sebagai pendampingnya.
  • __invoke() menjadikan objek dapat dipanggil seperti fungsi, terdeteksi oleh is_callable(), dan dapat dipakai di tempat yang menerima callable.
  • Nama berawalan dua garis bawah dicadangkan untuk PHP, sehingga metode magic dipakai secara sengaja dan properti yang dikelolannya harus benar-benar tersembunyi.

Referensi#