13. Macroable 解析
xiaohuilam opened this issue · 0 comments
还记得那个《高速修车》的陈年老梗吗:
公司的业务呢,就像跑在高速路上的车,车不能停,但是新需求和修bug也不能停。
这里要跟大家扒代码的 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
的功能。就如同在其中加入了如下代码:
<?php
class Collection ...
{
public function bcsum()
{
$sum = 0;
foreach ($this as $item) {
$sum = bcadd($sum, $item, 2);
}
return $sum;
}
}
代码解析
我们看到,Illuminate\Support\Collection
使用了 Illuminate\Support\Traits\Macroable
这个 trait。
macro
的代码
作用是将闭包存进static::$macros
魔术方法
在调用我们的方法时,按名字匹配出来,参数传入触发。
这里之所以定义了两个方法,__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
这个方法却是可以运行的的;
为什么?
其实道理很简单,因为 echo JoeDoe
对象时候,php 内核检查其中有没有 __toString
这个方法时,就已经报错了,根本还来不及走到 __call
这个魔术方法。