8个最常用的php魔术方法作用及用法教程

kano个人技术博客 php教程 761 次浏览 没有评论

PHP中有一些很独特的方法,只要定义了它们,就会在某些事件发生的时候很神奇的自动调用,也因此它们被称为魔术方法,特点是以__开头,本篇kano个人博客技术文章就给大家介绍一下这些神奇的魔术方法。(以__开头是魔术方法的特点,所以kano建议大家定义自己的方法时不要以__为前缀)。

php魔术方法

※ 对魔术方法较为准确的一个描述就是“事件处理器”,学过js和mysql触发器的同学应该较为容易理解。

魔术方法的方法名和参数是固定的。

1. __set和__get

通常在定义类的时候,会有一些不可直接访问的的私有属性。有时候我们需要有限制的访问它们,会给它们设置属性访问器(即get或set,学过Java的同学应该很熟悉)。在php中,属性访问器由__set和__get统一设置(而不是像Java一样为每个属性单独设置)。在给不可访问的属性赋值时,__set会被调用;在获取不可访问的属性时,__get会被调用。

注意一点:不可访问可能是private也可能是protected,甚至可能是不存在的属性,只要是在当前位置访问不到的属性都会调用起它们。

来看一下两个方法的定义:{

void __set(string name, mixed value)

public __get( string $name) : mixed

}

__set接受两个参数:name是属性名,value为属性值;__get只有一个参数name,为属性名。两个方法都不能是静态的切必须为public。

来看一个例子:

class Person
{
    private $name;
    private $age;
    public $job;

    public function __set($name, $value)
    {
        echo "__set被调用,给{$name}赋值<br>";

        switch ($name) {
            case 'name':
                $this->name = $value;
                break;
            case 'nianling':
                if ($value > 100) {
                    $this->age = 100;
                } else if ($value < 0) {
                    $this->age = 0;
                } else {
                    $this->age = $value;
                }
                break;
            case 'job':
                $this->job = $value;
                break;
        }
    }

    public function __get($name)
    {
        if ($name == 'mess') {
            return ['name' => $this->name, 'age' => $this->age, 'job' => $this->job];
        } else {
            return "{$name}为不合法的属性";
        }
    }

    function say()
    {
        echo "名字为:{$this->name},年龄为:{$this->age},职业为:{$this->job}<br>";
    }
}
 
$person1 = new Person();
$person1->name = '王一';
$person1->nianling = 150;
$person1->job = 'coder';
$person1->say();
print_r($person1->mess);
print_r($person1->aaa);

个人博客php魔术方法

第一行实例化了一个对象:

首先给name赋值,可以看到__set被调用,

然后给不存在的属性nianling赋值,发现__set也是会被调用,

给public属性job赋值,发现__set没有被调用,

调用say方法,可以看到赋值的结果:name成功赋值“王一”,给nianling的赋值经过了数据的处理,赋给了age,可见属性job未经过__set方法,普通的赋上了值,

获取不存在的属性mess,通过__get方法返回了一个数组,

获取不存在的属性aaa,通过__get方法输出其为不合法的属性。

2. __clone

克隆对象时会被调用,比如你想克隆某个对象,但是相对其中的一些值进行初始化:

class Person
{
    public $name;
    public $age;

    static $count = 0;

    public function __construct($name, $age)
    {
        $this->name = $name;
        $this->age = $age;
        self::$count++;
    }

    public function __clone()
    {
        $this->age = 0;
        self::$count++;
    }

    function say(){
        echo "名字为:{$this->name},年龄为:{$this->age},总人数为:" . self::$count . "<br>";
    }
}
 
$p1 = new Person('王一', 15);
$p1->say();
$p2 = clone $p1;
$p2->say();

php魔术方法

通过克隆p1得到p2,可以看到p2的年龄重置为了0;默认总人数只在实例化对象时增加,这里kano个人技术博客把人数自增也写到克隆里,保证了总人数的正确性。

__clone方法没有参数,因为克隆的操作通过关键字clone实现,无法接受参数。

3. __toString

对象被当成一个字符串时自动调用:比如有时候我们想直接打印一下对象的信息,但是如果你用 echo 直接输出一个对象,php会报一个"Object of class XXX could not be converted to string"(XXX类的对象无法转换成字符串)的错误,这时我们就可以定义 __toString 魔术方法并返回一个字符串,这样在直接打印对象的时候则会自动调用 __toString 方法了

Kano还用上面的例子,只贴_toString方法的代码:

public function __toString()
{
    return "名字为:{$this->name},年龄为:{$this->age},附加信息:哈哈哈哈哈哈哈哈哈哈";
}
 
$p1 = new Person('王一', 15);
echo $p1;

kano个人技术博客

4. __call

调用一个不可访问的方法时会自动调用,这里的“不可访问”和上面的不可访问属性一样,可能是方法的可见性,也可能是方法根本就不存在。

定义: public __call( string $name, array $arguments) : mixed

__call方法接受两个参数,第一个是方法的名字,第二个是方法的参数列表。

没有用到__call方法的时候调用没有定义的方法,一定会出现系统报错,所以__call方法可以用作这种情况下的错误处理;也可以用作如一个方法有几个别名,直接用__call处理就不必多写几个方法然后在其中调用了。

当然方法如果是可见的话则不会调用__call方法。

例:

function __call($funName, $params){
    echo "方法{$funName}不存在!<br /><br />";
    echo '参数列表:';
    print_r($params);
}
 
$p1 = new Person('王一', 15);
$p1->fang('这是', '一个', '不存在', '的方法');

php魔术方法教程

如果同学们用过ThinkPHP,可能会注意到一个现象,一般的方法按Ctrl点击会跳转到方法定义,但是有一些方法是无法跳转的,如查询语句中的聚合函数,翻开ThinkPHP源码,就可以看到聚合函数的操作实际上是由__call来完成的:

kano个人技术博客

(可以看到除了聚合函数还有很多其他的方法)

5. __autoload

在尝试调用一个不存在的类的时候自动触发,接受一个参数,即不存在的类名,类名会自动传入到 __autoload 中(当本php文件中没有,并且include的php文件中也没有对应的类的时候才会触发)

这个魔术方法有一点比较特殊的地方——它并不是在类中定义的,而是一个全局函数(所以可能叫“魔术函数”更恰当一点)

例子1:

test1.php中的内容
<?php
class Person{
    function __construct(){
        echo 'test1 Person';
    }
}

function __autoload($className){
echo "类{$className}不存在,__autoload被调用<br>";
    include 'test2.php';
}

$person1 = new Person();
?>

test2.php中的内容

<?php
class Person{
    function __construct(){
        echo 'test2 Person';
    }
}
?>

php魔术方法

输出 "test1 Person",因为在当前文件中能找到 Person

例子2:

test1.php中的内容

<?php
function __autoload($className){
    echo "类{$className}不存在,__autoload被调用<br>";
    include 'test2.php';
}


$person1 = new Person();
?>

test2.php中的内容

<?php
class Person{
    function __construct(){
        echo 'test2 Person';
    }
}
?>

__autoload魔术方法

输出 "test2 Person",__autoload被触发。

这个例子可以很好的看出__autoload的触发方式,不过这并不是__autoload的实际用途,__autoload一般用法为:将php文件规范命名,以达成调用某个类的时候能够自动引入对应文件的目的。

※ 这个函数在PHP7.2.0版本被标记为不鼓励使用的,考虑到这个版本如此之新,且是“不鼓励”而不是“废弃”,看来__autoload还能用很长时间。

6. ____construct和__destruct(构造方法和析构方法)

这两个方分别在对象创建和销毁的时候被调用,在“php对象简介”中已经详细介绍过它们,这里不再重复。

以上就是kano个人博客关于PHP魔术方法简介的教程,可以看到只要定义了魔术方法,它们就会在各种事件发生的时候调用,如果善用的话,可以省去不少的代码和思考负担。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

Go