Open xiaohuilam opened 5 years ago
还记得那个《高速修车》的陈年老梗吗:
公司的业务呢,就像跑在高速路上的车,车不能停,但是新需求和修bug也不能停。
这里要跟大家扒代码的 Macroable 呢,有点像上面这个悖论的出路;但其实也不是,因为他没有实现 修 且不停,只实现了 增 且不停。请原谅我这尴尬的幽默。
Macroable
修
增
网上很多教程都是提及了,Laravel 绝大部分类,都可以扩展方法。比如这个例子:
use Illuminate\Support\Collection; Collection::macro('bcsum', function () { $sum = 0; foreach ($this as $item) { $sum = bcadd($sum, $item, 2); } return $sum; }); dump((new Collection([1,2,3,4]))->bcsum()); // it says "10.00"
我们可以看到,在不改变代码的前提下,我们在使用的过程中给 Illuminate\Support\Collection 扩充了 bcsum 的功能。就如同在其中加入了如下代码:
Illuminate\Support\Collection
bcsum
<?php class Collection ... { public function bcsum() { $sum = 0; foreach ($this as $item) { $sum = bcadd($sum, $item, 2); } return $sum; } }
https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Support/Collection.php#L42-L44
我们看到,Illuminate\Support\Collection 使用了 Illuminate\Support\Traits\Macroable 这个 trait。
Illuminate\Support\Traits\Macroable
macro
作用是将闭包存进static::$macros https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Support/Traits/Macroable.php#L19-L30
static::$macros
在调用我们的方法时,按名字匹配出来,参数传入触发。 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Support/Traits/Macroable.php#L64-L112
这里之所以定义了两个方法,__call 和 __callStatic,是因为 laravel 很多类,部分可以静态调用,部分可以动态调用。macroable 如果要能被这两种类可用,那么就需要定义两个魔术方法。
__call
__callStatic
laravel
macroable
一. 魔术一个魔术方法,不可行
我们现在知道了 Macroable 的神奇,但是其实Macroable 也有失灵的时候。举个例子:
<?php class JoeDoe { use \Illuminate\Support\Traits\Macroable; } JoeDoe::macro('__toString', function () { return '123'; }); echo new JoeDoe();
而运行时,其还是报了这个错:
PHP Recoverable fatal error: Object of class JoeDoe could not be converted to string
但是如果我们在 JoeDoe 这个类中手工硬编码 __toString 这个方法却是可以运行的的; 为什么?
JoeDoe
__toString
其实道理很简单,因为 echo JoeDoe 对象时候,php 内核检查其中有没有 __toString 这个方法时,就已经报错了,根本还来不及走到 __call 这个魔术方法。
还记得那个《高速修车》的陈年老梗吗:
这里要跟大家扒代码的
Macroable
呢,有点像上面这个悖论的出路;但其实也不是,因为他没有实现修
且不停,只实现了增
且不停。请原谅我这尴尬的幽默。从陈年教程入手
网上很多教程都是提及了,Laravel 绝大部分类,都可以扩展方法。比如这个例子:
我们可以看到,在不改变代码的前提下,我们在使用的过程中给
Illuminate\Support\Collection
扩充了bcsum
的功能。就如同在其中加入了如下代码:代码解析
https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Support/Collection.php#L42-L44
我们看到,
Illuminate\Support\Collection
使用了Illuminate\Support\Traits\Macroable
这个 trait。macro
的代码作用是将闭包存进
static::$macros
https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Support/Traits/Macroable.php#L19-L30魔术方法
在调用我们的方法时,按名字匹配出来,参数传入触发。 https://github.com/xiaohuilam/laravel/blob/d081c918b7e582ec5b3f94316f44834466cec37d/vendor/laravel/framework/src/Illuminate/Support/Traits/Macroable.php#L64-L112
这里之所以定义了两个方法,
__call
和__callStatic
,是因为laravel
很多类,部分可以静态调用,部分可以动态调用。macroable
如果要能被这两种类可用,那么就需要定义两个魔术方法。经验分享
失灵的情况
一. 魔术一个魔术方法,不可行
我们现在知道了
Macroable
的神奇,但是其实Macroable
也有失灵的时候。举个例子:而运行时,其还是报了这个错:
但是如果我们在
JoeDoe
这个类中手工硬编码__toString
这个方法却是可以运行的的; 为什么?其实道理很简单,因为 echo
JoeDoe
对象时候,php 内核检查其中有没有__toString
这个方法时,就已经报错了,根本还来不及走到__call
这个魔术方法。