1. Magic method là gì?

Magic method là những phương thức đặc biệt rất đặc biệt trong PHP vì nhiệm vụ của chúng là tùy biến các sự kiện trong PHP, hay hiểu đơn giản là nó đang cung cấp cho ta thêm các để giải quyết vấn đề. Magic method được dùng để xử lý các đối tượng trong lập trình hướng đối tượng. Ví dụ như hàm khởi tạo __construct và hàm hủy __destruct là một trong những phương thức thuộc bộ magic method.

Nhằm sử dụng hiệu quả các object trong đối tượng, PHP cung cấp cho lập trình viên các phương thức magic. Các phương thức magic là phương thức được đặt tên với ký tự đặc biệt, bắt đầu bằng 2 dấu gạch dưới __ . Khi một phương thức được đặt tên với 2 dấu gạch dưới, điều đó để cho PHP nhận biết đó là phương thức magic, và nó sẽ kích hoạt các tính năng đặc biệt của phương thức này.

Thông thường mỗi magic methods được kích hoạt ở một sự kiện nào đó mà ta tác động đến đối tượng. Ví dụ hàm __set() và __get() sẽ được gọi khi ta tiến hành gán hay ta lấy giá trị của các thuộc tính trong đối tượng.

2. Các magic method trong PHP

Tất cả các hàm magic methods được viết trong một class cụ thể mà khi ta thao tác với đối tượng của class đó mà tùy trường hợp các hàm magic method đã khai báo trong class sẽ được thực hiện.

Trong PHP có 15 hàm magic method

2.1. __construct()

Hàm được gọi khi ta khởi tạo một đối tượng. Trong php thì magic method __construct() rất là phổ biến mà chúng ta hay thường gặp nhất. Hàm __construct() sẽ tự động được gọi khi ta khởi tạo 1 đối tượng, lưu ý hàm constructor() không cho phép chúng ta overload.

Ví dụ:

<?php 
class A{
    public function __construct($text){
        echo "$text";
    }
}
$tweet = new A('hello world');
?> 

2.2. __destruct()

Được gọi khi một đối tượng bị hủy. Mặc định khi kết thúc chương trình hoặc khi ta khai báo mới đối tượng đó sẽ bị hủy bỏ và gọi đến method __destruct().

Ví dụ:

<?php  
    class A{
        public $text;
        public function __construct(...$text)
        {
            $this->text = $text;
        }
        public function __destruct()
        {
            echo "<br>";
            echo "huy doi tuong co: ".count($this->)."tham so truyen vao ";
        }
    }
    $var0 = new A();
    $var1 = new A("1");
    $var2 = new A("1","2");
    echo "<br>";
    echo "ket thuc!";
?>

2.3. __set()

Gọi khi ta truyền dữ liệu vào thuộc tính không tồn tại hoặc thuộc tính private trong đối tượng.

Ví dụ:

<?php
class ConNguoi
{
    private $name;
    public function __set($key, $value)
    {
        //kiểm tra xem trong class có tồn tại thuộc tính không
        if (property_exists($this, $key)) {
            //tiến hành gán giá trị
            $this->$key = $value;
        } else {
            die('Không tồn tại thuộc tính');
        }
    }
    public function getName()
    {
        echo $this->name;
    }
}
$connguoi = new ConNguoi();
$connguoi->name = "Thành Nguyễn";
$connguoi->getName();
//Thành Nguyễn
echo "<br>";
$connguoi->age = 19;
//Không tồn tại thuộc tính
?>

2.4. __get()

Gọi khi ta truy cập vào thuộc tính không tồn tại hoặc thuộc tính private trong đối tượng. Tương tự như set, get là việc xử lý khi truy cập đối tượng.

Ví dụ:

<?php
class ConNguoi
{
    private $name = "Thành Nguyễn";
    public function __get($key)
    {
        //kiểm tra xem trong class có tồn tại thuộc tính không
        if (property_exists($this, $key)) {
            //tiến hành lấy giá trị
            return $this->$key;
        } else {
            die('Không tồn tại thuộc tính');
        }
    }
    public function getName()
    {
        echo $this->name;
    }
}
$connguoi = new ConNguoi();
echo $connguoi->name;
//thành nguyễn
echo "</br>";
$connguoi->age;
//Không tồn tại thuộc tính
?>

2.5. __isset()

Được gọi khi chúng ta thực hiện kiểm tra một thuộc tính không được phép truy cập của một đối tượng, hay kiểm tra một thuộc tính không tồn tại trong đối tượng đó. Cụ thể là hàm isset() và hàm empty().

Chú ý: phương thức __isset() không sử dụng được với thuộc tính tĩnh.

Ví dụ:

<?php
class ConNguoi
{
    private static $name;

    public function __isset($name)
    {
        echo 'Bạn vừa kiểm tra thuộc tính: ' . $name;
    }
}
//Khởi tạo đối tượng
$connguoi = new ConNguoi();

isset($connguoi->name);
//Kết quả: Bạn vừa kiểm tra thuộc tính: name
empty($connguoi->name);
//Kết quả Bạn vừa kiểm tra thuộc tính: name
/*kiểm tra thuộc tính không tồn tại trong đối tượng*/
isset($connguoi->age);
//Kết quả: Bạn vừa kiểm tra thuộc tính: age
?>

2.6. __unset()

Được gọi khi hàm unset() được sử dụng trong một thuộc tính không được phép truy cập. Tương tự như hàm isset. Khi ta Unset 1 thuộc tính không tồn tại thì method __unset() sẽ được gọi.

Ví dụ:

<?php
class ConNguoi
{
    private $name;
    public function __unset($name)
    {
        echo 'Bạn vừa hủy thuộc tính: ' . $name;
    }
}
//Khởi tạo đối tượng
$connguoi = new ConNguoi();
unset($connguoi->name);
//Kết quả: Bạn vừa hủy thuộc tính: name
/* unset thuộc tính không tồn tại trong đối tượng*/
unset($connguoi->age);
//Kết quả: Bạn vừa hủy thuộc tính: age
?>

2.7. __call()

Được gọi khi ta gọi một phương thức không được phép truy cập trong phạm vi của một đối tượng. Như vậy thì có thể thấy __get() và __call() cũng gần giống nhau. Có điều __get() gọi khi không có thuộc tính còn __call() khi phương thức không có.

Ví dụ:

<?php
class ConNguoi
{
    private $name = "Thành Nguyễn";
    private $age = 19;
    public function __call($methodName, $arguments)
    {
        echo 'Bạn vừa gọi phương thức: ' . $methodName . ' và có các tham số: ' . implode('-', $arguments);
    }
    private function getInfo()
    {
        echo $this->name . ' + ' . $this->age;
    }
}
$connguoi = new ConNguoi();
//Khi không truyền tham số.
$connguoi->getInfo();
//Kết quả: Bạn vừa gọi phương thức: getInfo và có các tham số:
//Khi truyền tham số
$connguoi->getInfo('name', 'age');
//Kết quả: Bạn vừa gọi phương thức: getInfo 
//và có các tham số: Bạn vừa gọi phương thức: getInfo và có các tham số: name-age
?>

2.8. __callstatic()

Được kích hoạt khi ta gọi một phương thức không được phép truy cập trong phạm vi của một phương thức tĩnh.

Ví dụ:

<?php
class ConNguoi
{
    private $name = "Thành Nguyễn";
    private $age = 19;
    public static function __callStatic($methodName, $arguments)
    {
        echo 'Bạn vừa gọi phương thức: ' . $methodName . ' và có các tham số: ' . implode('-', $arguments);
    }
    private static function getInfo()
    {
        echo $this->name . ' + ' . $this->age;
    }
}
ConNguoi::getInfo();
//Kết Quả: Bạn vừa gọi phương thức: getInfo và có các tham số:
/*Khi truyền tham số*/
ConNguoi::getInfo('name', 'age');
//Kết quả: Bạn vừa gọi phương thức: getInfo và có các tham số: name-age
?>

2.9. __toString

Phương thức này được gọi khi chúng ta in echo đối tượng. __toString() sẽ bắt buộc phải trả về 1 dãy String.

Ví dụ:

<?php
class ConNguoi
{
    private $name = 'Nguyễn Trí Thành';
    private $age = 20;

    public function __toString()
    {
        return 'Phương thức __toString() được gọi';
    }
}
echo new ConNguoi();
//Kết quả: Phương thức __toString() được gọi
?>

2.10. __invoke()

Được gọi khi chúng ta sử dụng đối tượng như một hàm.

Ví dụ:

<?php
class ConNguoi
{
    private $name = 'Thành Nguyễn';
    private $age = 19;

    public function __invoke()
    {
        echo 'Phương thức __invoke() được gọi';
    }
}
$congnuoi = new ConNguoi();
$congnuoi();
//Kết quả: Phương thức __invoke() được gọi
?>

2.11. __Sleep()

Được gọi khi serialize() một đối tượng. Thông thường khi chúng ta serialize() một đối tượng thì nó sẽ trả về tất cả các thuộc tính trong đối tượng đó. Nhưng nếu sử dụng __sleep() thì chúng ta có thể quy định được các thuộc tính có thể trả về.

Ví dụ:

<?php
class ConNguoi
{
    private $name = 'Thành Nguyễn';
    private $age = 19;
    public function __sleep()
    {
        //trả về thuộc tính name
        return array('name');
    }
}
echo serialize(new ConNguoi());
?>

2.12. __wakeup()

Ngược lại với __sleep()

Ví dụ:

<?php
class ConNguoi
{
    private $name = 'Thành Nguyễn';
    private $age = 19;

    public function __sleep()
    {
        return array('name');
    }
    public function getName()
    {
        echo $this->name;
    }
    /**
     * gọi hàm getName khi unserialize()
     */
    public function __wakeup()
    {
        $this->getName();
    }
}
unserialize(serialize(new ConNguoi()));
//Kết quả: Thành Nguyễn.
?>

2.13. __set_state()

Được gọi cho lớp được xuất bởi hàm var_export() và rất ít khi sử dụng đến nó.

Ví dụ:

<?php
class ConNguoi
{
    private $name = "Thành Nguyễn";
    private $age = 19;
 public static function __set_state(array $arr)
    {
        foreach ($arr as $key => $value) {
            echo $key . '->' . $value . '<br/>';
        }
    }
}
$connguoi = new ConNguoi();
eval(var_export($connguoi, true) . ';');
/*
name->Thành Nguyễn
age->19
 */
?>

2.14. __clone()

Được gọi khi 1 bản sao của đối tượng được tạo ra với từ khóa clone.

Ví dụ:

<?php
class ConNguoi
{
    public $name = "Thành Nguyễn";
    public $age = 19;
    public function __clone()
    {
        echo 'Phương thức __clone() được gọi';
    }
}
$connguoi = new ConNguoi();
$connguoi2 = clone $connguoi;
// Phương thức __clone() được gọi
echo $connguoi2->name;
// Thành Nguyễn
?>

2.15. __debugInfo()

Được gọi khi dùng hàm var_dump() để kiểm tra kiểu dữ liệu và giá trị của biến. Thông thường, khi chúng ta var_dump() một đối tượng thì nó sẽ trả về tất cả các thuộc tính và giá trị của nó trong đối tượng đó, nhưng khi chúng ta sử dụng phương thức __debugInfo() thì chúng ta có thể tùy chỉnh thông số trả về.

Ví dụ:

<?php
class ConNguoi
{
    public $name = "Thành Nguyễn";
    public $age = 19;
    public function __debugInfo()
    {
        return [
            'name' => $this->name,
        ];
    }
}
$connguoi = new ConNguoi();
var_dump($connguoi);
?>