原文鏈接:https://learnku.com/laravel/t/40926
討論請前往專業(yè)的 Laravel 開發(fā)者論壇:https://learnku.com/Laravel
Laravel將PHP應用到了一個全新的水平,為您打造下一個項目提供了出色的開發(fā)體驗(DX)。因此,一些人將其稱為“魔術(shù)”。
今天,我將向您展示Laravel的一個技巧,魔術(shù)方法。
什么是魔術(shù)方法?
重要的是,要了解魔術(shù)方法并不是Laravel獨有的,而是可以在任何PHP應用中使用。Laravel恰好有一些最有趣的魔術(shù)方法用例。
魔術(shù)方法是在PHP中聲明的任何類中都可以使用的方法,它提供了在類中實現(xiàn)附加功能的方法。
這里有一個很好的定義:
魔術(shù)方法永遠不會被程序員調(diào)用——實際上,PHP將在后臺調(diào)用該方法。這就是為什么它們被稱為“魔術(shù)”方法——因為它們從來沒有被直接調(diào)用,它們允許程序員做一些非常強大的是事情。
總共有15中魔術(shù)方法:
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做過一些面向?qū)ο蟮木幊?,那么您一定知?__construct 方法,這是一個魔術(shù)方法。所以您一直在使用魔術(shù)方法。
您還注意到,所有的魔術(shù)的方法都是以“__”為前綴的。
今天,我們不會深入研究這些方法,而只會深入了解整個Laravel代碼庫中使用的那些有趣的方法。如果其他人對此感興趣,請隨時查看下面的文檔??
PHP: Méthodes magiques - Manual
Laravel是如何使用的魔術(shù)方法
__get()
Laravel中的模型非常特別。它們不將屬性數(shù)據(jù)存儲為類的直接屬性,而是存儲在protected $attributes 屬性中,該屬性是模型所保存的所有數(shù)據(jù)的相關(guān)數(shù)組。
讓我們看看簡單的PHP類和Laravel模型訪問屬性的區(qū)別。
<?php
/**
* PHP中的普通用戶類(非Laravel)將只是一個具有上述屬性的類
*/
class NormalUser
{
public $firstName = 'Alice';
}
$normalUser = new NormalUser;
$normalUser->firstName; // 將返回'Alice'
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
/**
* Laravel中的一個user類
*/
class LaravelUser extends Model
{
/**
* 注意,我們將所有屬性數(shù)據(jù)存儲在一個單獨的數(shù)組中
*/
protected $attributes = [
'firstName' => 'Alice',
];
}
$laravelUser = new LaravelUser;
$laravelUser->firstName; // 依然返回'Alice'
我們可以看到,上面的PHP和Laravel類的行為完全相同。
然而,在Laravel的例子中,屬性不像普通PHP那樣存儲,而是集中在一個名為$attributes的屬性中。我們?nèi)匀辉O法訪問正確的數(shù)據(jù),但是如何訪問呢?
這一切都是可能的,這是因為_get魔術(shù)方法。讓我們自己嘗試實現(xiàn)一個簡單的例子。
<?php
class NormalUser
{
/**
* 像在Laravel中那樣聲明屬性
*/
protected $attributes = [
'firstName' => 'Alice',
];
/**
* __get 函數(shù)接收一個參數(shù)
* 它將會是你想要訪問的屬性名
* 在這個例子中是 $key = "firstName"
*/
public function __get(string $key)
{
return $this->attributes[$key];
}
}
$normalUser = new NormalUser;
$normalUser->firstName; // 將會返回 'Alice'
我們做到了! ??
我們需要注意,只有在類中找不到具有匹配名稱的屬性時,才會調(diào)用魔術(shù)方法_get。這是一種后備方法,當PHP在類中找不到所訪問的屬性時調(diào)用。因此,在下面的示例中,根本不會調(diào)用魔術(shù)方法_get。
<?php
class NormalUser
{
public $firstName = 'Bob';
protected $attributes = [
'firstName' => 'Alice',
];
public function __get($key)
{
return $this->attributes[$key];
}
}
$normalUser = new NormalUser;
/**
* 由于類中存在該屬性,將會返回 Bob
* 所以該例子中沒有調(diào)用到魔術(shù)方法__get
*/
$normalUser->firstName;
有更多的事情在幕后發(fā)生。如果你想更多地了解 Laravel 的模型是如何確切地使用 __get 的,你可以查看下面的源代碼。
__set()
當試圖設置的屬性沒有在類中聲明時,使用魔術(shù)方法_set。讓我們再次看看normal PHP類和Laravel中model模型的區(qū)別。
<?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
如我們所見,在此示例中,我們?nèi)匀粐L試影響Bob的值,該值在類中實際上不存在但位于屬性$ attributes中。讓我們嘗試使用魔術(shù)方法__ 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'
現(xiàn)在我們開始!我們在Laravel中成功實施了__ get和__ set魔術(shù)方法的基本用法!他們只需幾行代碼就能完成!
請記住,這些魔術(shù)方法盡可能簡單,而不必涉及太多細節(jié),因為除了那些還有更多而不僅僅是用例,如果您對它的工作原理感到好奇,我邀請您親自做一些探索! (如果您有任何疑問,也可以隨時在Twitter上與我聯(lián)系)
同樣,如果您想進一步挖掘,請在此處鏈接到源代碼
讓我們繼續(xù)最后一個也是最有趣的一個事! ??
__call() & __callStatic()
當調(diào)用的方法在類中找不到時,__call()會被調(diào)用。 在laravel中,該魔術(shù)方法方法使宏在 php 中成為可能。
我不會深入討論宏的所有細節(jié),但如果您感興趣,這里有一篇很好的文章解釋了如何在 Laravel 應用程序中使用它們??
讓我們試著看看如何編寫一個簡單的宏示例。
<?php
class NormalUser
{
public $firstName = 'Alice';
public $lastName = 'Bob';
}
$normalUser = new NormalUser;
$normalUser->fullName(); // 由于沒有聲明 "fullName" 方法,這將會拋出錯誤
使用 __call ,可以定義一個包含閉包函數(shù)的數(shù)組,在我們開發(fā)時可以程序化地添加到應用里。
<?php
class NormalUser
{
public $firstName = 'Alice';
public $lastName = 'Bob';
/**
* 將我們的宏初始化為一個空數(shù)組,后面再賦值
*/
public static $macros = [];
/**
* 定義一個添加新宏的方法
* 第一個參數(shù)是我們想要定義的宏的名字
* 第二個參數(shù)是調(diào)用宏時將會被執(zhí)行的閉包函數(shù)
*/
public static function addMacro($name, $macro) {
static::$macros[$name] = $macro;
}
/**
* "__call" 接收兩個參數(shù),
* $name 是被調(diào)用的函數(shù)名稱,比如 “fullName”
* $arguments 是傳遞給函數(shù)的所有參數(shù),這里我們使用了一個空數(shù)組,因為我們的函數(shù)不用傳參
*/
public function __call(string $name, array $arguments) {
/**
* 通過名稱獲取到宏
*/
$macro = static::$macros[$name];
/**
* 然后通過參數(shù)執(zhí)行宏
* 備注:調(diào)用之前,我們需要將閉包綁定到 “$this”,從而使宏方法在同樣的上下文中執(zhí)行
*/
return call_user_func_array($macro->bindTo($this, static::class), $arguments);
}
}
$normalUser = new NormalUser;
$normalUser->fullName(); // 這里會中斷,因為我們既沒有定義 “fullName” 宏,也沒有 “fullName” 方法存在。
/**
* 添加 “fullName” 宏方法
*/
NormalUser::addMacro('fullName', function () {
return $this->firstName.' '.$this->lastName;
});
$normalUser->fullName(); // 現(xiàn)在,返回 “Alice Bob”
宏要比那個復雜一些,但是我們設法使用 __call 魔術(shù)方法來創(chuàng)建一個宏的簡單工作版本。
除了對于靜態(tài)方法, __callStatic 和 __call 是完全一樣的。
如果你打算自己再深入研究,這里有宏的特性源代碼。
總結(jié)
所以說碼農(nóng)們,當你第一次用 Laravel 會感覺它神奇是對的,但是通過深入查看源代碼,你會理解魔法是如何在場景背后施展的。
就像現(xiàn)實生活中的魔法,沒有道理的事情是不會發(fā)生的,在代碼中就更加是了。程序運行的背后總是有著一行代碼在執(zhí)行,只不過需要你去發(fā)現(xiàn)它。
原文鏈接:https://learnku.com/laravel/t/40926
討論請前往專業(yè)的 Laravel 開發(fā)者論壇:https://learnku.com/Laravel