背景
前几篇的关于服务提供者、Facade以及Contract中,我们经常会看到,在类文件中有一个共同的方法construct() ,并且是以__为头,这个呐,就是魔术方法
一、什么是魔术方法呐
首先要了解到魔术方法并不是laravel独有的,而且任务php应用中都可以使用
魔术方法是在PHP中声明的任何类中都可以使用的方法,它提供了在类中实现附加功能的方法
魔术方法永远不会被程序员调用 —— 实际上,PHP 将在后台调用该方法。这就是为什么它们被称为 “魔术” 方法 —— 因为它们从来没有被直接调用,但它们允许程序员做一些非常强大的是事情。
魔术方法总共包括15种,如下:
class MyClass
{
public function __construct() {}
public function __destruct() {}
public function __call() {}
public function __callStatic() {}
public function __get() {}
public function __set() {}
public function __isset() {}
public function __unset() {}
public function __sleep() {}
public function __wakeup() {}
public function __toString() {}
public function __invoke() {}
public function __set_state() {}
public function __clone() {}
public function __debuginfo() {}
}
二、 laravel如何使用魔术方法
__construct
正如一开始所说,我们经常在开发使用到的__construct方法,叫构造方法或者构造器,主要用于当一个对象被实例化的时候会被首先调用,在PHP框架种依赖注入以及中间件一般就是这种方法完成的。当然父类的构造方法是可以被子类继承和重写的
<?php
class A {
public function __construct() {
echo "This is A construct\n";
}
}
class B extends A{
// 调用父类构造方法,再调用自己的构造方法
public function __construct() {
parent::__construct();
echo "This is B construct\n";
}
}
class C extends A{
// 重写构造方法,之调用自己的构造方法
public function __construct() {
echo "This is C construct";
}
}
new A();// This is A construct
new B();// This is A construct This is B construct
new C();// This is c construct
由此可以看出,构造方法是可以帮助我们实现数据或属性初始化的任务,方便在实例化类以后调用
__set
和
__get
当试图设置的属性没有在类中声明的时候,会使用魔术方法set。
<?php
class NormalUser
{
public $attributes = [
'firstName' => 'Alice',
];
/**
* The magic method __set receives the $name you want to affect the value on
* and the value
*/
public function __set($key, $value)
{
$this->attributes[$key] = $value;
}
}
$normalUser = new NormalUser;
$normalUser->firstName = 'Bob'; //属性firstName不存在,所以就调用了set方法,修改数组attributes的值
$normalUser->attributes['firstName']; // 'Bob' //调用前面通过set设置好的属性值
__call
和
__callStatic
这也是经常用到的,例子如下:
class Car{
public function __call($method,$params=[]){
echo "car call\n";
}
public function color(){
echo "color red\n";
}
protected function isRed(){
echo "yes is Red\n";
}
public function checkColor(){
$this->color();
$this->isRed();
}
}
$car = new Car();
$car->color(); //color red
$car->isRed();//car call
$car->checkColor(); //color red yes is Red
下面是__callStatic
class Car{
public static function __callStatic($method,$params=[]){
echo "car callStatic\n";
}
public function color(){
echo "color red\n";
}
protected function isRed(){
echo "yes is Red\n";
}
public function checkColor(){
Car::color();
Car::isRed();
}
}
Car::color(); //color red
Car::isRed();//car callStatic
(new Car())->checkColor();//color red yes is Read
你会发现在外部以静态方式调用*Car::color()*时,有Notice错误提示,但是内部调用是没有的,那么假如两个魔术方法都存在时,是什么样子的呐???
class Car{
public static function __callStatic($method,$params=[]){
echo "car callStatic $method\n";
}
public function __call($method,$params=[]){
echo "car call $method\n";
}
public function checkColor(){
Car::color();
Car::isRed();
}
}
$car = new Car();
Car::color();//car callStatic color
Car::isRed();//car callStatic isRed
$car->color();//car call color
$car->isRed();//car call isRed
(new Car())->checkColor(); //car call color car call isRed
从结果可以看出,外部静态调用color和isRed时,上下文是静态方式,所以执行callStatic方法。但是在调用当前类对象内部的chaeckColor时,因为是在当前类中调用,所以即使是以静态方式调用,最终执行的还是call方法
这里就有一个小小的总结:
- __call()方法关注方法能否被访问到,而不仅仅是关注是否存在
- __callStatic()方法关注的是方法能否被静态的访问到,而不是关注方法是否存在,是否是静态方法
- 具体执行__call,__callStatic,是根据调用的上下文。如果处于静态上下文内,则调用__callStatic。如果处于对象的上线文内,则调用__call
写在最后
上述分享了几个常用的魔术方法,其他的可以参考文档,自我扩充学习哟
当时要是好奇魔术方法的背后实现逻辑,也可以学习一下源代码点击学习