原文鏈接:https://learnku.com/laravel/t/40926
討論請(qǐng)前往專(zhuān)業(yè)的 Laravel 開(kāi)發(fā)者論壇:https://learnku.com/Laravel
Laravel將PHP應(yīng)用到了一個(gè)全新的水平,為您打造下一個(gè)項(xiàng)目提供了出色的開(kāi)發(fā)體驗(yàn)(DX)枷畏。因此银萍,一些人將其稱(chēng)為“魔術(shù)”茴扁。
今天,我將向您展示Laravel的一個(gè)技巧悦穿,魔術(shù)方法吸申。
什么是魔術(shù)方法?
重要的是丛楚,要了解魔術(shù)方法并不是Laravel獨(dú)有的,而是可以在任何PHP應(yīng)用中使用憔辫。Laravel恰好有一些最有趣的魔術(shù)方法用例趣些。
魔術(shù)方法是在PHP中聲明的任何類(lèi)中都可以使用的方法,它提供了在類(lèi)中實(shí)現(xiàn)附加功能的方法贰您。
這里有一個(gè)很好的定義:
魔術(shù)方法永遠(yuǎn)不會(huì)被程序員調(diào)用——實(shí)際上坏平,PHP將在后臺(tái)調(diào)用該方法。這就是為什么它們被稱(chēng)為“魔術(shù)”方法——因?yàn)樗鼈儚膩?lái)沒(méi)有被直接調(diào)用锦亦,它們?cè)试S程序員做一些非常強(qiáng)大的是事情舶替。
總共有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做過(guò)一些面向?qū)ο蟮木幊蹋敲茨欢ㄖ?__construct
方法孽亲,這是一個(gè)魔術(shù)方法坎穿。所以您一直在使用魔術(shù)方法。
您還注意到返劲,所有的魔術(shù)的方法都是以“__”為前綴的玲昧。
今天,我們不會(huì)深入研究這些方法篮绿,而只會(huì)深入了解整個(gè)Laravel代碼庫(kù)中使用的那些有趣的方法孵延。如果其他人對(duì)此感興趣,請(qǐng)隨時(shí)查看下面的文檔??
PHP: Méthodes magiques - Manual
Laravel是如何使用的魔術(shù)方法
__get()
Laravel中的模型非常特別亲配。它們不將屬性數(shù)據(jù)存儲(chǔ)為類(lèi)的直接屬性尘应,而是存儲(chǔ)在protected $attributes
屬性中惶凝,該屬性是模型所保存的所有數(shù)據(jù)的相關(guān)數(shù)組。
讓我們看看簡(jiǎn)單的PHP類(lèi)和Laravel模型訪問(wèn)屬性的區(qū)別犬钢。
<?php
/**
* PHP中的普通用戶(hù)類(lèi)(非Laravel)將只是一個(gè)具有上述屬性的類(lèi)
*/
class NormalUser
{
public $firstName = 'Alice';
}
$normalUser = new NormalUser;
$normalUser->firstName; // 將返回'Alice'
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
/**
* Laravel中的一個(gè)user類(lèi)
*/
class LaravelUser extends Model
{
/**
* 注意苍鲜,我們將所有屬性數(shù)據(jù)存儲(chǔ)在一個(gè)單獨(dú)的數(shù)組中
*/
protected $attributes = [
'firstName' => 'Alice',
];
}
$laravelUser = new LaravelUser;
$laravelUser->firstName; // 依然返回'Alice'
我們可以看到,上面的PHP和Laravel類(lèi)的行為完全相同玷犹。
然而混滔,在Laravel的例子中,屬性不像普通PHP那樣存儲(chǔ)歹颓,而是集中在一個(gè)名為$attributes
的屬性中坯屿。我們?nèi)匀辉O(shè)法訪問(wèn)正確的數(shù)據(jù),但是如何訪問(wèn)呢?
這一切都是可能的巍扛,這是因?yàn)?code>_get魔術(shù)方法领跛。讓我們自己嘗試實(shí)現(xiàn)一個(gè)簡(jiǎn)單的例子。
<?php
class NormalUser
{
/**
* 像在Laravel中那樣聲明屬性
*/
protected $attributes = [
'firstName' => 'Alice',
];
/**
* __get 函數(shù)接收一個(gè)參數(shù)
* 它將會(huì)是你想要訪問(wèn)的屬性名
* 在這個(gè)例子中是 $key = "firstName"
*/
public function __get(string $key)
{
return $this->attributes[$key];
}
}
$normalUser = new NormalUser;
$normalUser->firstName; // 將會(huì)返回 'Alice'
我們做到了撤奸! ??
我們需要注意吠昭,只有在類(lèi)中找不到具有匹配名稱(chēng)的屬性時(shí),才會(huì)調(diào)用魔術(shù)方法_get
寂呛。這是一種后備方法怎诫,當(dāng)PHP在類(lèi)中找不到所訪問(wèn)的屬性時(shí)調(diào)用。因此贷痪,在下面的示例中,根本不會(huì)調(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;
/**
* 由于類(lèi)中存在該屬性劫拢,將會(huì)返回 Bob
* 所以該例子中沒(méi)有調(diào)用到魔術(shù)方法__get
*/
$normalUser->firstName;
有更多的事情在幕后發(fā)生。如果你想更多地了解 Laravel 的模型是如何確切地使用 __get
的强胰,你可以查看下面的源代碼舱沧。
__set()
當(dāng)試圖設(shè)置的屬性沒(méi)有在類(lèi)中聲明時(shí),使用魔術(shù)方法_set
偶洋。讓我們?cè)俅慰纯磏ormal PHP類(lèi)和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
如我們所見(jiàn),在此示例中玄窝,我們?nèi)匀粐L試影響Bob
的值牵寺,該值在類(lèi)中實(shí)際上不存在但位于屬性$ 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)在我們開(kāi)始恩脂!我們?cè)贚aravel中成功實(shí)施了__ get
和__ set
魔術(shù)方法的基本用法帽氓!他們只需幾行代碼就能完成!
請(qǐng)記住俩块,這些魔術(shù)方法盡可能簡(jiǎn)單黎休,而不必涉及太多細(xì)節(jié)浓领,因?yàn)槌四切┻€有更多而不僅僅是用例,如果您對(duì)它的工作原理感到好奇势腮,我邀請(qǐng)您親自做一些探索联贩! (如果您有任何疑問(wèn),也可以隨時(shí)在Twitter上與我聯(lián)系)
同樣捎拯,如果您想進(jìn)一步挖掘泪幌,請(qǐng)?jiān)诖颂庢溄拥皆创a
讓我們繼續(xù)最后一個(gè)也是最有趣的一個(gè)事! ??
__call()
& __callStatic()
當(dāng)調(diào)用的方法在類(lèi)中找不到時(shí)玄渗,__call()
會(huì)被調(diào)用座菠。 在laravel中,該魔術(shù)方法方法使宏在 php 中成為可能藤树。
我不會(huì)深入討論宏的所有細(xì)節(jié)浴滴,但如果您感興趣,這里有一篇很好的文章解釋了如何在 Laravel 應(yīng)用程序中使用它們??
讓我們?cè)囍纯慈绾尉帉?xiě)一個(gè)簡(jiǎn)單的宏示例岁钓。
<?php
class NormalUser
{
public $firstName = 'Alice';
public $lastName = 'Bob';
}
$normalUser = new NormalUser;
$normalUser->fullName(); // 由于沒(méi)有聲明 "fullName" 方法升略,這將會(huì)拋出錯(cuò)誤
使用 __call
,可以定義一個(gè)包含閉包函數(shù)的數(shù)組屡限,在我們開(kāi)發(fā)時(shí)可以程序化地添加到應(yīng)用里品嚣。
<?php
class NormalUser
{
public $firstName = 'Alice';
public $lastName = 'Bob';
/**
* 將我們的宏初始化為一個(gè)空數(shù)組,后面再賦值
*/
public static $macros = [];
/**
* 定義一個(gè)添加新宏的方法
* 第一個(gè)參數(shù)是我們想要定義的宏的名字
* 第二個(gè)參數(shù)是調(diào)用宏時(shí)將會(huì)被執(zhí)行的閉包函數(shù)
*/
public static function addMacro($name, $macro) {
static::$macros[$name] = $macro;
}
/**
* "__call" 接收兩個(gè)參數(shù),
* $name 是被調(diào)用的函數(shù)名稱(chēng)钧大,比如 “fullName”
* $arguments 是傳遞給函數(shù)的所有參數(shù)翰撑,這里我們使用了一個(gè)空數(shù)組,因?yàn)槲覀兊暮瘮?shù)不用傳參
*/
public function __call(string $name, array $arguments) {
/**
* 通過(guò)名稱(chēng)獲取到宏
*/
$macro = static::$macros[$name];
/**
* 然后通過(guò)參數(shù)執(zhí)行宏
* 備注:調(diào)用之前啊央,我們需要將閉包綁定到 “$this”眶诈,從而使宏方法在同樣的上下文中執(zhí)行
*/
return call_user_func_array($macro->bindTo($this, static::class), $arguments);
}
}
$normalUser = new NormalUser;
$normalUser->fullName(); // 這里會(huì)中斷,因?yàn)槲覀兗葲](méi)有定義 “fullName” 宏瓜饥,也沒(méi)有 “fullName” 方法存在逝撬。
/**
* 添加 “fullName” 宏方法
*/
NormalUser::addMacro('fullName', function () {
return $this->firstName.' '.$this->lastName;
});
$normalUser->fullName(); // 現(xiàn)在,返回 “Alice Bob”
宏要比那個(gè)復(fù)雜一些乓土,但是我們?cè)O(shè)法使用 __call
魔術(shù)方法來(lái)創(chuàng)建一個(gè)宏的簡(jiǎn)單工作版本宪潮。
除了對(duì)于靜態(tài)方法, __callStatic
和 __call
是完全一樣的趣苏。
如果你打算自己再深入研究狡相,這里有宏的特性源代碼。
總結(jié)
所以說(shuō)碼農(nóng)們拦键,當(dāng)你第一次用 Laravel 會(huì)感覺(jué)它神奇是對(duì)的谣光,但是通過(guò)深入查看源代碼,你會(huì)理解魔法是如何在場(chǎng)景背后施展的芬为。
就像現(xiàn)實(shí)生活中的魔法萄金,沒(méi)有道理的事情是不會(huì)發(fā)生的蟀悦,在代碼中就更加是了。程序運(yùn)行的背后總是有著一行代碼在執(zhí)行氧敢,只不過(guò)需要你去發(fā)現(xiàn)它日戈。
原文鏈接:https://learnku.com/laravel/t/40926
討論請(qǐng)前往專(zhuān)業(yè)的 Laravel 開(kāi)發(fā)者論壇:https://learnku.com/Laravel