Penggunaan fungsi MAGICLA pada PHP

PHP menyediakan sejumlah metode 'ajaib' yang memungkinkan anda untuk melakukan beberapa trik yang cukup rapi dalam pemrograman berorientasi objek. Metode-metode ini, yang diidentifikasi oleh dua prefiks garis bawah (__), berfungsi sebagai pencegat yang secara otomatis dipanggil ketika kondisi tertentu terpenuhi. Metode sulap menyediakan beberapa fungsi yang sangat berguna, dan tutorial ini akan mendemonstrasikan penggunaan setiap metode.


Sebelum Kita Mulai

Untuk sepenuhnya memahami metode sulap, ada gunanya melihat mereka beraksi. Jadi mari kita mulai dengan satu set dasar dari kelas yang sangat sederhana. Di sini kita mendefinisikan dua kelas: Perangkat dan Baterai.

<?php
class Device {
    public $name;           // the name of the device
    public $battery;        // holds a Battery object
    public $data = array(); // stores misc. data in an array
    public $connection;     // holds some connection resource

    protected function connect() {
        // connect to some external network
        $this->connection = 'resource';
        echo $this->name . ' connected' . PHP_EOL;
    }

    protected function disconnect() {
        // safely disconnect from network
        $this->connection = null;
        echo $this->name . ' disconnected' . PHP_EOL;
    }
}

class Battery {
    private $charge = 0;

    public function setCharge($charge) {
        $charge = (int)$charge;
        if($charge < 0) {
            $charge = 0;
        }
        elseif($charge > 100) {
            $charge = 100;
        }
        $this->charge = $charge;
    }
}
?>

Jika kata-kata seperti "metode" dan "properti" terdengar asing bagi anda, anda mungkin ingin membaca tentang ini terlebih dahulu.

Objek perangkat akan menyimpan nama, objek Battery, array data, dan pegangan ke beberapa sumber eksternal. Mereka juga memiliki metode untuk menghubungkan dan memutuskan sumber daya eksternal. Objek baterai cukup menyimpan muatan dalam properti pribadi dan memiliki metode untuk mengatur biaya.

Tutorial ini mengasumsikan anda memiliki pemahaman dasar tentang pemrograman berorientasi objek. Jika kata-kata seperti "metode" dan "properti" terdengar asing bagi anda, anda mungkin ingin membaca tentang itu terlebih dahulu.

Kelas-kelas ini sangat tidak berguna, tetapi mereka menjadi contoh yang baik untuk masing-masing metode sihir. Jadi sekarang kita telah membuat kelas sederhana, kita bisa mencoba metode sulap.


Pembangun & Destruktor

Konstruktor dan destruktor disebut ketika suatu objek dibuat dan dihancurkan, masing-masing. Suatu objek "dihancurkan" ketika tidak ada referensi lagi untuk itu, baik karena variabel yang memegangnya tidak disetel / dialihkan atau skrip berakhir eksekusi.

__construct()

Metode __construct() sejauh ini merupakan metode sulap yang paling umum digunakan. Ini adalah tempat anda melakukan inisialisasi apa pun yang Anda perlukan saat sebuah objek dibuat. Anda dapat menentukan sejumlah argumen di sini, yang akan dilewatkan saat membuat objek. Nilai kembali apa pun akan diteruskan melalui kata kunci baru. Pengecualian apa pun yang dilemparkan pada konstruktor akan menghentikan pembuatan objek.

class Device {
    //...
    public function  __construct(Battery $battery, $name) {
        // $battery can only be a valid Battery object
        $this->battery = $battery;
        $this->name = $name;
        // connect to the network
        $this->connect();
    }
    //...
}

Mendeklarasikan metode konstruktor 'pribadi' mencegah kode eksternal secara langsung membuat objek.

Di sini kami telah mendeklarasikan konstruktor yang menerima dua argumen, Baterai dan nama. Konstruktor menetapkan masing-masing properti yang dibutuhkan objek untuk berfungsi dan menjalankan metode connect(). Konstruktor memungkinkan anda memastikan bahwa objek memiliki semua bagian yang diperlukan sebelum dapat ada.

Tip: Mendeklarasikan metode konstruktor 'pribadi' mencegah kode eksternal secara langsung membuat objek. Ini berguna untuk membuat kelas tunggal yang membatasi jumlah objek yang bisa ada.

Dengan konstruktor di atas di tempat, di sini adalah bagaimana anda membuat Perangkat yang disebut 'iMagic':

$device = new Device(new Battery(), 'iMagic');
// iMagic connected
echo $device->name;
// iMagic

Seperti yang Anda lihat, argumen yang dilewatkan ke kelas benar-benar diteruskan ke metode konstruktor. Anda juga dapat mengatakan bahwa metode penghubung dipanggil dan properti $nama dihuni.

Katakanlah kita lupa untuk memberikan nama. Inilah yang terjadi:

$device = new Device(new Battery());
// Result: PHP Warning:  Missing argument 2 for Device::__construct()

__destruct()

Seperti namanya, metode __destruct () dipanggil ketika objek dihancurkan. Ia tidak menerima argumen dan biasanya digunakan untuk melakukan operasi pembersihan seperti menutup koneksi database. Dalam kasus kami, kami akan menggunakannya untuk memutuskan sambungan dari jaringan.

class Device {
    //...
    public function  __destruct() {
        // disconnect from the network
        $this->disconnect();
        echo $this->name . ' was destroyed' . PHP_EOL;
    }
    //...
}

Dengan destruktor di atas di tempat, inilah yang terjadi ketika objek Perangkat dihancurkan:

$device = new Device(new Battery(), 'iMagic');
// iMagic connected
unset($device);
// iMagic disconnected
// iMagic was destroyed

Di sini, kami telah menghancurkan objek menggunakan unset(). Sebelum dihancurkan, destructor memanggil metode disconnect() dan mencetak pesan, yang dapat anda lihat di komentar.


Properti Overloading

Catatan: Versi PHP dari "overloading" tidak sama dengan kebanyakan bahasa lain, meskipun hasil yang sama dapat dicapai.

Set metode sulap berikutnya adalah tentang berurusan dengan akses properti, mendefinisikan apa yang terjadi ketika anda mencoba mengakses properti yang tidak ada (atau tidak dapat diakses). Mereka dapat digunakan untuk membuat properti pseudo. Ini disebut overloading di PHP.

__get()

Metode __get() dipanggil ketika kode mencoba mengakses properti yang tidak dapat diakses. Ia menerima satu argumen, yaitu nama properti. Ini harus mengembalikan nilai, yang akan diperlakukan sebagai nilai properti. Ingat properti $data di kelas Perangkat kami? Kami akan menyimpan "properti pseudo" ini sebagai elemen dalam larik data, dan kami dapat mengizinkan pengguna kelas kami mengaksesnya melalui __get(). Inilah tampilannya:

class Device {
    //...
    public function  __get($name) {
        // check if the named key exists in our array
        if(array_key_exists($name, $this->data)) {
            // then return the value from the array
            return $this->data[$name];
        }
        return null;
    }
    //...
}

Penggunaan populer dari metode __get() adalah untuk memperluas kontrol akses dengan membuat properti "read-only". Ambil kelas Baterai kami, misalnya, yang memiliki properti pribadi. Kami dapat mengizinkan properti $charge pribadi untuk dibaca dari kode luar, tetapi tidak diubah. Kode akan terlihat seperti ini:

class Battery {
    private $charge = 0;

    public function  __get($name) {
        if(isset($this->$name)) {
            return $this->$name;
        }
        return null;
    }
    //...
}

Dalam contoh ini, perhatikan penggunaan variabel variabels untuk mengakses properti secara dinamis. Dengan asumsi nilai 'pengguna' untuk $name, $this ->$name diterjemahkan menjadi $this->user.

__set()

Metode __set() dipanggil ketika kode mencoba mengubah nilai properti yang tidak dapat diakses. Ia menerima dua argumen, yaitu nama properti dan nilainya. Inilah yang terlihat seperti untuk susunan "variabel pseudo" di kelas Perangkat kami:

class Device {
    //...
    public function  __set($name, $value) {
        // use the property name as the array key
        $this->data[$name] = $value;
    }
    //...
}

__isset()

Metode __isset() dipanggil ketika kode panggilan isset() pada properti yang tidak dapat diakses. Ia menerima satu argumen, yaitu nama properti. Ini harus mengembalikan nilai Boolean yang mewakili keberadaan suatu nilai. Sekali lagi menggunakan variabel array kami, inilah yang terlihat seperti:

class Device {
    //...
    public function  __isset($name) {
        // you could also use isset() here
        return array_key_exists($name, $this->data);
    }
    //...
}

__unset()

Metode __unset() dipanggil ketika kode mencoba untuk unset() properti yang tidak dapat diakses. Ia menerima satu argumen, yaitu nama properti. Inilah penampilan kami:

class Device {
    //...
    public function  __unset($name) {
        // forward the unset() to our array element
        unset($this->data[$name]);
    }
    //...
}

Property Overloading in Action

Berikut adalah semua metode ajaib terkait properti yang telah kami nyatakan:

class Device {
    //...
    public $data = array(); // stores misc. data in an array
    //...
    public function  __get($name) {
        // check if the named key exists in our array
        if(array_key_exists($name, $this->data)) {
            // then return the value from the array
            return $this->data[$name];
        }
        return null;
    }

    public function  __set($name, $value) {
        // use the property name as the array key
        $this->data[$name] = $value;
    }

    public function  __isset($name) {
        // you could also use isset() here
        return array_key_exists($name, $this->data);
    }

    public function  __unset($name) {
        // forward the unset() to our array element
        unset($this->data[$name]);
    }
    //...
}

Dengan metode sulap di atas, inilah yang terjadi ketika kita mencoba mengakses properti yang disebut nama. Ingat bahwa sebenarnya tidak ada properti $name yang dideklarasikan, meskipun anda tidak akan pernah tahu itu tanpa melihat kode kelas internal.

$device->user = 'Steve';
echo $device->user;
// Steve

Kami telah menetapkan dan berhasil mengambil nilai dari properti yang tidak ada. Nah di mana itu disimpan?

print_r($device->data);
/*
Array
(
    [user] => Steve
)
*/

Seperti yang anda lihat, properti $data sekarang mengandung elemen 'name' dengan nilai kami.

var_dump(isset($device->user));
// bool(true)

Di atas adalah hasil pemanggilan isset() pada properti palsu.

unset($device->user);
var_dump(isset($device->user));
// bool(false)

Di atas adalah hasil dari unsetting the fake property. Hanya untuk memastikan, inilah larik data kosong kami:

print_r($device->data);
/*
Array
(
)
*/

Mewakili Objek Sebagai Teks

Terkadang anda mungkin ingin mengonversi objek ke representasi string. Jika anda mencoba mencetak objek, kami akan mendapatkan kesalahan, seperti yang di bawah ini:

$device = new Device(new Battery(), 'iMagic');
echo $device;
// Result : PHP Catchable fatal error:  Object of class Device could not be converted to string

__toSrting()

Metode __toString() dipanggil ketika kode mencoba untuk memperlakukan objek seperti string. Ia tidak menerima argumen dan harus mengembalikan string. Ini memungkinkan kita untuk mendefinisikan bagaimana objek akan direpresentasikan. Dalam contoh kami, kami akan membuat ringkasan sederhana:

class Device {
    ...
    public function  __toString() {
        // are we connected?
        $connected = (isset($this->connection)) ? 'connected' : 'disconnected';
        // how much data do we have?
        $count = count($this->data);
        // put it all together
        return $this->name . ' is ' . $connected . ' with ' . $count . ' items in memory' . PHP_EOL;
    }
    ...
}

Dengan metode di atas didefinisikan, inilah yang terjadi ketika kami mencoba mencetak objek Perangkat:

$device = new Device(new Battery(), 'iMagic');
echo $device;
// iMagic is connected with 0 items in memory

Objek Perangkat sekarang diwakili oleh ringkasan singkat yang berisi nama, status, dan jumlah item yang disimpan.

__set_state() (PHP 5.1)

Metode statis __set_state() (tersedia pada versi PHP 5.1) dipanggil ketika fungsi var_export() dipanggil pada objek kita. Fungsi var_export() digunakan untuk mengkonversi variabel ke kode PHP. Metode ini menerima array asosiatif yang berisi nilai properti objek. Demi kesederhanaan, gunakan dengan baik di kelas Baterai kami.

class Battery {
    //...
    public static function  __set_state(array $array) {
        $obj = new self();
        $obj->setCharge($array['charge']);
        return $obj;
    }
    //...
}

Metode kami cukup membuat instance kelas induknya dan menetapkan untuk mengisi ke nilai dalam larik yang diteruskan. Dengan metode di atas didefinisikan, inilah yang terjadi ketika kita menggunakan var_export() pada objek Perangkat:

$device = new Device(new Battery(), 'iMagic');
var_export($device->battery);
/*
Battery::__set_state(array(
   'charge' => 0,
))
*/
eval('$battery = ' . var_export($device->battery, true) . ';');
var_dump($battery);
/*
object(Battery)#3 (1) {
  ["charge:private"]=>
  int(0)
}
*/

Komentar pertama menunjukkan apa yang sebenarnya terjadi, yaitu bahwa var_export() hanya memanggil Battery::__ set_state(). Komentar kedua menunjukkan kita berhasil menciptakan kembali Baterai.


Kloning obyek

Objek, secara default, dilewatkan oleh referensi. Jadi menugaskan variabel lain ke objek tidak akan benar-benar menyalin objek, itu hanya akan membuat referensi baru ke objek yang sama. Untuk benar-benar menyalin objek, kita harus menggunakan kata kunci clone.

Kebijakan 'pass by reference' ini juga berlaku untuk objek dalam objek. Bahkan jika kita mengkloning suatu objek, setiap objek anak yang ada di dalamnya tidak akan dikloning. Jadi kita akan berakhir dengan dua objek yang berbagi objek anak yang sama. Berikut ini contoh yang menggambarkan bahwa:

$device = new Device(new Battery(), 'iMagic');
$device2 = clone $device;

$device->battery->setCharge(65);
echo $device2->battery->charge;
// 65

Di sini, kami telah mengkloning objek Perangkat. Ingat bahwa semua objek Perangkat berisi objek Baterai. Untuk mendemonstrasikan bahwa kedua klon Perangkat berbagi Baterai yang sama, perubahan yang kami lakukan pada Baterai perangkat ini tercermin dalam baterai $device2.

__clone()

Metode __clone() dapat digunakan untuk memecahkan masalah ini. Ini disebut pada salinan objek kloning setelah kloning terjadi. Di sinilah anda dapat mengkloning objek anak.

class Device {
    ...
    public function  __clone() {
        // copy our Battery object
        $this->battery = clone $this->battery;
    }
    ...
}

Dengan metode ini dinyatakan, kita sekarang dapat memastikan Perangkat yang dikloning masing-masing memiliki Baterai sendiri.

$device = new Device(new Battery(), 'iMagic');
$device2 = clone $device;

$device->battery->setCharge(65);
echo $device2->battery->charge;
// 0

Perubahan pada satu Baterai Perangkat tidak mempengaruhi yang lain.


Serialisasi Objek

Serialisasi adalah proses yang mengubah data apa pun menjadi format string. Ini dapat digunakan untuk menyimpan seluruh objek ke dalam file atau basis data. Saat anda melakukan unserialize data yang tersimpan, anda akan memiliki objek aslinya persis seperti sebelumnya. Satu masalah dengan serialisasi, adalah bahwa tidak semuanya bisa diserialkan, seperti koneksi database. Untungnya ada beberapa metode sulap yang memungkinkan kita menangani masalah ini.

__sleep()

Metode __sleep() dipanggil ketika fungsi serialize() dipanggil pada objek. Ini tidak menerima argumen dan harus mengembalikan array dari semua properti yang harus diserialkan. Anda juga dapat menyelesaikan tugas-tugas yang tertunda atau pembersihan yang mungkin diperlukan dalam metode ini.

Tip: Hindari melakukan sesuatu yang merusak dalam __sleep() karena ini akan mempengaruhi objek langsung, dan anda mungkin tidak selalu melakukannya.

Dalam contoh Perangkat kami, properti koneksi mewakili sumber daya eksternal yang tidak dapat diserialkan. Jadi metode __sleep() kami mengembalikan array dari semua properti kecuali $connection.

class Device {
    public $name;           // the name of the device
    public $battery;        // holds a Battery object
    public $data = array(); // stores misc. data in an array
    public $connection;     // holds some connection resource
    //...
    public function  __sleep() {
        // list the properties to save
        return array('name', 'battery', 'data');
    }
    //...
}

__Sleep() kami mengembalikan daftar nama-nama properti yang harus dipertahankan.

__wakeup()

Metode __wakeup() dipanggil ketika fungsi unserialize() dipanggil pada objek yang disimpan. Ia tidak menerima argumen dan tidak perlu mengembalikan apa pun. Gunakan untuk membangun kembali koneksi atau sumber daya basis data apa pun yang hilang dalam serialisasi.

Dalam contoh Perangkat kami, kami hanya perlu membangun kembali koneksi kami dengan memanggil metode connect() kami.

class Device {
    //...
    public function  __wakeup() {
        // reconnect to the network
        $this->connect();
    }
    //...
}

Metode Overloading

Kedua metode terakhir ini untuk menangani metode. Ini adalah konsep yang sama dengan metode overload properti (__get(), __set(), dll), tetapi diterapkan pada metode.

__call()

__call() dipanggil ketika kode mencoba memanggil metode yang tidak dapat diakses atau tidak ada. Ini menerima dua argumen: nama metode yang disebut dan array argumen. Anda dapat menggunakan informasi ini untuk memanggil metode yang sama dalam objek anak, misalnya.

Dalam contoh, perhatikan penggunaan fungsi call_user_func_array(). Fungsi ini memungkinkan kita untuk secara dinamis memanggil fungsi bernama (atau metode) dengan argumen yang disimpan dalam array. Argumen pertama mengidentifikasi fungsi untuk dipanggil. Dalam kasus metode penamaan, argumen pertama adalah larik yang berisi nama kelas atau contoh objek dan nama properti. Argumen kedua selalu berupa rangkaian argumen yang diindeks untuk dilewatkan.

Dalam contoh kami, kami akan meneruskan pemanggilan metode ke properti $connection kami (yang kami anggap sebagai objek). Kami akan mengembalikan hasil dari itu langsung kembali ke kode panggilan.

class Device {
    //...
    public function  __call($name, $arguments) {
        // make sure our child object has this method
        if(method_exists($this->connection, $name)) {
            // forward the call to our child object
            return call_user_func_array(array($this->connection, $name), $arguments);
        }
        return null;
    }
    //...
}

Metode di atas akan dipanggil jika kita mencoba memanggil metode iDontExist():

$device = new Device(new Battery(), 'iMagic');
$device->iDontExist();
// __call() forwards this to $device->connection->iDontExist()

__callStatic() (PHP 5.3)

__callStatic() (tersedia pada versi PHP 5.3) identik dengan __call() kecuali bahwa dipanggil ketika kode mencoba memanggil metode yang tidak dapat diakses atau tidak ada dalam konteks statis.

Satu-satunya perbedaan dalam contoh kami adalah bahwa kami menyatakan metode sebagai static dan kami mereferensikan nama kelas, bukan objek.

class Device {
    //...
    public static function  __callStatic($name, $arguments) {
        // make sure our class has this method
        if(method_exists('Connection', $name)) {
            // forward the static call to our class
            return call_user_func_array(array('Connection', $name), $arguments);
        }
        return null;
    }
    //...
}

Metode di atas akan dipanggil jika kita mencoba memanggil metode statis iDontExist():

Device::iDontExist();
// __callStatic() forwards this to Connection::iDontExist()

Menggunakan Objek sebagai Fungsi

Terkadang anda mungkin ingin menggunakan objek sebagai fungsi. Mampu menggunakan objek sebagai fungsi memungkinkan anda untuk meneruskan fungsi di sekitar sebagai argumen seperti yang anda bisa dalam bahasa lain.

__invoke() (PHP 5.3)

__invoke() (tersedia pada versi PHP 5.3) dipanggil ketika kode mencoba untuk menggunakan objek sebagai fungsi. Setiap argumen yang didefinisikan dalam metode ini akan digunakan sebagai argumen fungsi. Dalam contoh kami, kami hanya akan mencetak argumen yang diterimanya.

class Device {
    //...
    public function __invoke($data) {
        echo $data;
    }
    //...
}

Dengan definisi di atas, inilah yang terjadi ketika kita menggunakan Perangkat sebagai fungsi:

$device = new Device(new Battery(), 'iMagic');
$device('test');
// equiv to $device->__invoke('test')
// Outputs: test

Bonus: __autoload()

Ini bukan metode sulap, tetapi masih sangat berguna. Fungsi __autoload() secara otomatis dipanggil ketika kelas yang tidak ada dirujuk. Ini dimaksudkan untuk memberi anda satu kesempatan terakhir untuk memuat file yang berisi deklarasi kelas sebelum skrip anda gagal. Ini berguna karena anda tidak selalu ingin memuat setiap kelas hanya jika anda membutuhkannya.

Fungsi ini menerima satu argumen: nama kelas yang direferensikan. Katakanlah anda memiliki setiap kelas dalam sebuah file bernama 'classname.class.php' di direktori 'inc'. Berikut adalah tampilan otomatis anda:

function __autoload($class_name) {
    $class_name = strtolower($class_name);
    include_once './inc/' . $class_name . '.class.php';
}

Kesimpulan

Metode-metode ajaib sangat berguna dan menyediakan alat yang kuat untuk mengembangkan kerangka kerja aplikasi yang fleksibel. Mereka membawa objek PHP lebih dekat ke objek lain yang berorientasi bahasa dengan memungkinkan anda untuk mereproduksi beberapa fitur mereka yang lebih bermanfaat. Anda dapat membaca halaman manual PHP tentang metode sulap di sini. Saya harap tutorial ini membantu dan menjelaskan konsepnya dengan jelas. Jika anda memiliki pertanyaan, jangan ragu untuk bertanya di komentar. Terima kasih sudah membaca.