前言

__construct()__destruct()__call()__callStatic()__get()__set()__isset()__unset()__sleep()__wakeup()__serialize(), __unserialize(), __toString()__invoke()__set_state()__clone()__debugInfo() 等方法在 PHP 中被称为魔术方法(Magic methods)。在命名自己的类方法时不能使用这些方法名,除非是想使用其魔术功能。

参考文献

魔术方法官方文档:https://www.php.net/manual/zh/language.oop5.magic.php

演示

__construct:构造方法

__destruct:析构函数

<?php
class Magic
{
    // 构造函数
    public function __construct() {
        echo '对象被实例化 new 的时候调用此方法' . PHP_EOL;
    }

    // 析构函数
    public function __destruct() {
        echo '对象删除或者销毁时被调用' . PHP_EOL;
    }
}
new Magic;

/*输出:
对象被实例化 new 的时候调用此方法 对
象删除或者销毁时被调用
*/

__call(),在对象中调用一个不可访问方法时调用

__callStatic(),用静态方式中调用一个不可访问方法时调用

<?php
class Magic {
    public function __call($funName, $arguments) {
        echo "你所调用的函数:" . $funName . "(参数:";  // 输出调用不存在的方法名
        print_r($arguments); // 输出调用不存在的方法时的参数列表
        echo ")不存在!\n" . PHP_EOL; // 结束换行
    }

    // 声明此方法用来处理调用对象中不存在的方法
    public static function __callStatic($funName, $arguments) {
        echo "你所调用的静态方法:" . $funName . "(参数:";  // 输出调用不存在的方法名
        print_r($arguments); // 输出调用不存在的方法时的参数列表
        echo ")不存在!\n" . PHP_EOL; // 结束换行
    }
}
$magic = new Magic;
var_dump($magic->testCall('names'));
var_dump(Magic::testCall('names'));

/*
输出:
你所调用的函数:testCall(参数:Array ( [0] => names ) )不存在! 
NULL 

你所调用的静态方法:testCall(参数:Array ( [0] => names ) )不存在! 
NULL
*/

__get(),获得一个类的成员私有的变量时调用

<?php
class Magic {
    // 私有变量
    private $value = 100;

    // 读取不可访问属性的值时调用
    public function __get($propertyName) {
        return $this->$propertyName = 200;
    }
}
$magic = new Magic;
echo $magic->value;

/*输出:
200
*/

__set(),设置一个类的成员私有的变量时调用

<?php
class Magic {
    // 私有变量
    private $value = 100;

    // 设置一个类的成员变量时调用
    public function __set($property, $value){
        $this->$property = $value;
    }

    // 输出私有方法
    public function run(){
        return $this->value;
    }
}
$magic = new Magic;
$magic->value = 666;
echo $magic->run();

/*输出:
666
*/

__isset(),当对不可访问属性调用isset()或empty()时调用

__unset(),当对不可访问属性调用unset()时被调用

<?php
class Magic {
    public $name = '张三';
    private $area = '北京';

    // 当对不可访问属性调用isset()或empty()时调用
    public function  __isset($content) {
        echo "当在类外部使用isset()函数测定私有成员{$content}时,自动调用<br>";
        echo  isset($this->$content) . "<br>";
    }

    // 当对不可访问属性调用时被调用
    public function  __unset($content) {
        echo "当在类外部使用unset()函数来删除私有成员时自动调用的<br>";
        echo  isset($this->$content) . "<br>";
    }
}
$magic = new Magic;
echo isset($magic->name), "<br>"; // 返回:1
echo isset($magic->area), "<br>";
/*输出:
1
当在类外部使用isset()函数测定私有成员sex时,自动调用
1
*/

unset($magic->name); // 返回:1
unset($magic->area);
/*输出:
当在类外部使用unset()函数来删除私有成员时自动调用的
1
 */

__sleep(),执行serialize()时,先会调用这个函数

__wakeup(),执行unserialize()时,先会调用这个函数

<?php
class Magic {
    public $name = '张三';
    private $area = '北京';

    public function __sleep(){
        echo "当在类外部使用serialize()时会调用这里的__sleep()方法<br>";
        $this->name = base64_encode($this->name);
        return array('name', 'age'); // 这里必须返回一个数值,里边的元素表示返回的属性名称
    }

    public function __wakeup(){
        echo "当在类外部使用unserialize()时会调用这里的__wakeup()方法<br>";
        $this->name = '李四';
        $this->area = '上海';
    }
}
$magic = new Magic;
var_dump(serialize($magic));
/*输出:
当在类外部使用serialize()时会调用这里的__sleep()方法
O:5:"Magic":2:{s:4:"name";s:8:"5byg5LiJ";s:3:"age";N;}
*/

var_dump(unserialize(serialize($magic)));
/*输出:
当在类外部使用serialize()时会调用这里的__sleep()方法
当在类外部使用unserialize()时会调用这里的__wakeup()方法
object(Magic)#2 (3) { ["name"]=> string(6) "李四" ["area":"Magic":private]=> string(6) "上海" ["age"]=> NULL }
*/

__toString(),类被当成字符串时的回应方法

<?php
class Magic{
    public function __toString(){
        return  '我是类呀,不是字符串';
    }
}
$magic = new Magic;
echo $magic;
/*输出:
我是类呀,不是字符串
*/

__invoke(),调用函数的方式调用一个对象时的回应方法

<?php
class Magic{
    public function __invoke(){
        echo '这可是一个对象哦';
    }
}
$magic = new Magic;
$magic();
/*输出:
这可是一个对象哦
*/

__set_state(),调用var_export()导出类时,此静态方法会被调用

<?php
class Magic{
    public $name;
    public $age;

    public function __construct($name = "",  $age = 25){
        $this->name = $name;
        $this->age  = $age;
    }

    public static function __set_state($an_array){
        $a = new Magic();
        $a->name = $an_array['name'];
        return $a;
    }
}
$person = new magic('小明'); // 初始赋值
$person->name = '小红';
var_export($person);
/*输出:
Magic::__set_state(array( 'name' => '小红', 'age' => 25, ))
*/

__clone(),当对象复制完成时调用

<?php
class Magic{
    public $name = '张三';
    public function __clone() {
        echo __METHOD__ . "你正在克隆对象<br>";
    }
}
$magic = new magic();
$magic2 = clone $magic;

$magic->name = '李四';
echo $magic->name . "<br>"; // 李四
echo $magic2->name . "<br>"; // 张三

$magic2->name = '王五';
echo $magic->name . "<br>"; // 李四
echo $magic2->name . "<br>"; // 王五

/*输出:
Magic::__clone你正在克隆对象
李四
张三
李四
王五
*/

__autoload(),尝试加载未定义的类

警告 本函数已自 PHP 7.2.0 起被废弃,并自 PHP 8.0.0 起被移除。 强烈建议不要依赖本函数。spl_autoload_register — 注册给定的函数作为 __autoload 的实现

<?php
function  __autoload($className)
{
    $filePath = "project/class/{$className}.php";
    die($filePath);
    if (is_readable($filePath)) {
        require($filePath);
    }
}
$a = new TestClass();  

/*返回:
Deprecated: __autoload() is deprecated, use spl_autoload_register() instead in F:\www\aaa.php on line 2
project/class/TestClass.php
 */

__debugInfo(),打印所需调试信息

<?php
class Magic{
    private $prop;

    public function __construct($val){
        $this->prop = $val;
    }

    public function __debugInfo(){
        return [
            'propSquared' => $this->prop ** 2, // 这里的 `**` 是乘方的意思
        ];
    }
}
var_dump(new Magic(666));

/*输出:
object(Magic)#1 (1) { ["propSquared"]=> int(443556) }
*/