Laravel 最厉害的技巧揭秘:魔法方法
什么是魔法方法?
Laravel 如何使用魔法方法
照片由 Mervyn Chan 在 Unsplash 上拍摄
Laravel 将 PHP 的利用提升到了一个全新的高度,为你的下一个项目提供了卓越的开发者体验 (DX)。因此,有些人称之为“魔法”。
今天,我将向你揭示 Laravel 技巧之一,魔术方法。
什么是魔法方法?
重要的是要理解,魔法方法并非 Laravel 独有,而是适用于任何 PHP 应用程序。Laravel 恰好拥有一些最有趣的魔法方法用例。
魔术方法是在 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() {}
}
如果你用 PHP 做过一些面向对象编程,你肯定会认出这个__construct
方法,它也是一个魔法方法。所以你一直在使用魔法方法!
您还会注意到,所有魔术方法都以 为前缀,__
这是惯例。
今天,我们不会深入探讨所有这些特性,而只会介绍 Laravel 代码库中使用的一些有趣的特性。如果您对其他特性感兴趣,欢迎查看下面的文档 👇
Laravel 如何使用魔法方法
__get()
Laravel 中的模型非常特殊。它们不将属性数据存储为类的直接属性,而是存储在一个属性中protected $attributes
,该属性是一个关联数组,包含模型所持有的所有数据。
让我们看看简单的 PHP 类和 Laravel 模型访问属性之间的区别。
<?php
/**
* A normal user class in PHP (without Laravel) will be just a class with the said attributes
*/
class NormalUser
{
public $firstName = 'Alice';
}
$normalUser = new NormalUser;
$normalUser->firstName; // Will return 'Alice'
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
/**
* A user class in Laravel
*/
class LaravelUser extends Model
{
/**
* Note that we store all the attributes data in one and single array
*/
protected $attributes = [
'firstName' => 'Alice',
];
}
$laravelUser = new LaravelUser;
$laravelUser->firstName; // Will return 'Alice' as well
我们可以看到,上面的 PHP 和 Laravel 类的功能完全相同。然而,在 Laravel 中,属性的存储方式与普通 PHP 不同,而是集中在一个名为 的属性中$attributes
。我们仍然可以访问正确的数据,但是该如何操作呢?
这一切都归功于__get
魔法方法。让我们尝试在一个示例中实现我们自己的简化版本。
<?php
class NormalUser
{
/**
* We declare the attributes that same way as in Laravel
*/
protected $attributes = [
'firstName' => 'Alice',
];
/**
* The function __get receive one parameter
* which will be the name of the attribute you want to access
* in this case $key = "firstName"
*/
public function __get(string $key)
{
return $this->attributes[$key];
}
}
$normalUser = new NormalUser;
$normalUser->firstName; // Will return 'Alice'
我们做到了!🎉
需要注意的是,__get
只有当类中找不到名称匹配的属性时,才会调用该魔法方法。它相当于 PHP 在类中找不到所需属性时调用的一种后备方法。因此,在下面的示例中,该魔法方法__get
根本不会被调用。
<?php
class NormalUser
{
public $firstName = 'Bob';
protected $attributes = [
'firstName' => 'Alice',
];
public function __get($key)
{
return $this->attributes[$key];
}
}
$normalUser = new NormalUser;
/**
* Will return 'Bob' as the attribute exists in the class
* so the magic method __get doesn't get call in this case
*/
$normalUser->firstName;
幕后还有很多事情发生。如果你想深入了解 Laravel 模型的具体用法,__get
可以查看下面的源代码。
__set()
__set
当要设置的属性未在类中声明时,将使用魔术方法。让我们再次看看普通 PHP 类和 Laravel 中的模型之间的区别。
<?php
class NormalUser
{
public $firstName = 'Alice';
}
$normalUser = new NormalUser;
$normalUser->firstName = 'Bob';
$normalUser->firstName; // Will return 'Bob'
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class LaravelUser extends Model
{
protected $attributes = [
'firstName' => 'Alice',
];
}
$laravelUser = new LaravelUser;
$laravelUser->firstName = 'Bob';
$laravelUser->firstName; // Will return 'Bob' as well
如我们所见,在这个例子中,我们仍然尝试影响Bob
一个实际上并不存在于类中,但存在于属性内部的属性值$attributes
。让我们尝试用魔法方法来实现这种精确的行为__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';
/**
* As we don't have the __get magic method define in this example for simplicity sake,
* we will access the $attributes directly
*/
$normalUser->attributes['firstName']; // Will return 'Bob'
好了!我们成功实现了Laravel 中__get
和__set
魔法方法的基本用法!而且只需要几行代码!
请记住,这些魔法方法尽可能简单,无需赘述太多细节,因为它的意义远不止这些用例。如果您好奇它究竟是如何运作的,我邀请您亲自深入研究代码进行探索!(如果您有任何疑问,也欢迎在 Twitter 上联系我)
再次强调,如果你想了解更多,这里是源代码的链接
让我们继续讨论最后一个也是最有趣的案例!🙌
__call()
&__callStatic()
__call
当调用某个方法但在特定类中找不到时执行。在 Laravel 中,正是这个魔法方法使得 PHP 中的宏成为可能。
我不会详细介绍宏,但如果你感兴趣的话,这里有一篇很好的文章解释了如何在 Laravel 应用程序中使用它们👇
让我们尝试看看如何在纯 PHP 中重现一个简单的宏示例。
<?php
class NormalUser
{
public $firstName = 'Alice';
public $lastName = 'Bob';
}
$normalUser = new NormalUser;
$normalUser->fullName(); // This will throw an error as no method "fullName" has been declared.
现在__call
,我们可以定义一个数组,其中将包含闭包函数,我们可以在应用程序中以编程方式添加这些闭包函数。
<?php
class NormalUser
{
public $firstName = 'Alice';
public $lastName = 'Bob';
/**
* We initialise our macros as an empty array that we will fill
*/
public static $macros = [];
/**
* We define this method to add new macro as we go
* the first argument will be the name of the macro we want to define
* the second will be a Closure function that will be executed when calling the macro
*/
public static function addMacro($name, $macro) {
static::$macros[$name] = $macro;
}
/**
* "__call" receives two parameters,
* $name which will be the name of the function called in our case "fullName"
* $arguments which will be all the arguments passed in the function in our case it'll just be an empty array as we can't pass any arguments in our function
*/
public function __call(string $name, array $arguments) {
/**
* We retrieve the macro with the right name
*/
$macro = static::$macros[$name];
/**
* Then we execute the macro with the arguments
* Note: we need to bind the Closure with "$this" before calling it to allow the macro method to act in the same context
*/
return call_user_func_array($macro->bindTo($this, static::class), $arguments);
}
}
$normalUser = new NormalUser;
$normalUser->fullName(); // This will still break as we didn't define the macro "fullName" yet and the method "fullName" doesn't exist either
/**
* We add the macro function "fullName"
*/
NormalUser::addMacro('fullName', function () {
return $this->firstName.' '.$this->lastName;
});
$normalUser->fullName(); // Now, returns "Alice Bob"
宏比这稍微复杂一些,但是我们再次设法使用__call
魔术方法创建了宏的简单工作版本。
__callStatic
__call
工作原理与静态方法完全相同。
如果你想自己深入挖掘一下,这里是Macroable
trait 源代码
最终拍摄
女士们,先生们,虽然第一次使用 Laravel 时确实会感觉很神奇,但通过查看源代码本身,您可以了解幕后的“魔法”是如何运作的。
就像现实生活中的魔术一样,任何事情的发生都有其背后的原因,代码更是如此。总有一行代码在某个地方执行着它,只是你需要找到它。
我鼓励您深入研究 Laravel 以发挥其魔力,并且不要忘记在 Twitter 上与我分享您的知识!
这篇博文最初发布在我的个人博客上,请拥抱一下🤗
特别感谢:Samuel、Jean-Baptiste和Jessica
文章来源:https://dev.to/excessivecoding/laravel-greatest-trick-revealed-magic-methods-31om