Laravel 執行時類的功能擴充套件的實現

iffor發表於2021-05-25

Laravel 執行時類的功能擴充套件的實現

通常實現類功能擴充套件有以下的方法

  • 使用類繼承的方法,在子類中新增功能
  • 使用類組合的方法,把基礎的類作為擴充套件功能類的一個屬性,來實現擴充套件功能的目的
  • 利用 php 的魔術方法 __call__callStatic 來動態的實現功能擴充套件

通過繼承實現功能擴充套件

Fish 相對於 Animal 擴充套件出了 swim 方法

class Animal
{

 public $name;
 public function __construct($name) { $this->name = $name; }}

class Fish extends Animal
{

 public function swim() { return 'fish swiming'; } }

通過組合的方法實現功能擴充套件

class Animal
{

 public $name;
 public function __construct($name) { $this->name = $name; }}

class Fish
{

 public $animal;
 public function __construct(Animal $animal) { $this->animal = $animal; }
 public function swim() { return 'fish swiming'; }}

$animal = new Animal("suzi");
$fish = new Fish($animal);
echo $fish->animal->name;
echo $fish->swim();

通過類的魔術方法實現類功能擴充套件

class Animal
{
 public $name;
 public function __construct($name) { $this->name = $name; }
 public function __call($method,$params){ if ($method === 'swim'){ return 'swiming'; } trigger_error(sprintf("method %s not found",$method)); }}

$animal = new Animal("suzi");
echo $animal->swim();
//$animal->whatEver(); trigger error

通過繼承組合的方式實現類功能擴充套件的優點是:程式碼清晰,明確的知道類有哪些功能,
缺點是:沒辦法根據條件擴充套件不同的功能。魔術方法的方式的優缺點恰好和前兩者相反。

laravel 動態擴充套件功能

laravel 動態擴充套件功能是通過一個名為 Macroabletrait 實現的。 所有使用了 trait Macroable 都有動態擴充套件類的功能。

主要有3個靜態的API

  • macro($name,$macro) 動態注入方法
  • mixin($mixin, $replace = true) 合併 $mixin 物件的 publicprotected 方法
  • hasMacro($name) 判斷是否擴充套件過指定名稱的功能
class EmptyMacroable
{
 use Illuminate\Support\Traits\Macroable; public  $foo = 'foo';}

EmptyMacroable::macro('hello',function (){
 return 'hello world';});

$foo = new EmptyMacroable();
echo $foo->hello();// output: hello world

class Mixed {
 protected function methodOne(){ return function (){ return $this->foo; }; }}
EmptyMacroable::mixin(new Mixed());
echo "\n";
echo $foo->methodOne();// output: foo

動態擴充套件功能在 laravel 中的應用

laravel framework 的底層類幾乎都使用了 trait Macroable。常用的有 Requestresponse 等。
這些類都具有動態擴充套件功能的能力。下面是一個擴充套件 Request 功能的例子

\Illuminate\Http\Request::macro("isBackend",function (){
 return str_contains($this->pathInfo??'',"/backend");});
$request = new \Illuminate\Http\Request();
var_dump($request->isBackend());// output: false
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章