用过laravel的人,必定都用过其集合,各种对数据库查询出来的数据,进行随意变换,来满足复杂的业务需求。那每个集合的方法,又是如何实现的? 很好奇,里面一定有各种奇思妙想,所以我想一探究竟。
这里直接按照 laravel-china 的官网文档开搞~
具体的代码,就不一一copy了。可以参照着集合 collection.php 对着看。
1. Macroable
这是集合类里 use 的 trait Macroable,主要用来自定义扩展集合类的方法的,下面仔细分析一下。
// 这里得命名空间和依赖的类就省略了
trait Macroable
{
/**
* 定义数组用来存储注册的扩展。
*
* @var array
*/
protected static $macros = [];
/**
* 注册自定制的方法macro。
*
* @param string $name
* @param object|callable $macro
* @return void
*/
public static function macro($name, $macro)
{
// 把方法存到定义的$macros里。
static::$macros[$name] = $macro;
}
/**
* 将其他的类混合mix到这个Collection集合里。
*
* @param object $mixin
* @return void
*/
public static function mixin($mixin)
{
// 通过反射 获取所有传过来的类 $mixin 里的方法,并保留public 和 protected 方法
$methods = (new ReflectionClass($mixin))->getMthods(
ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED
);
// 将方法进行循环, 将所有都方法都设置为可以访问执行
foreach($methods as $method) {
$method->setAccessible(true);
// 利用invoke方法 来调用$mixin里的 $method,并且执行静态方法
// macro 来储存在$macros数组里。
static::macro($method->name, $method->invoke($mixin));
}
}
/**
* 根据某个方法名判断方法是否已注册
*
* @param string $name
* @return bool
*/
public static function hasMacro($name)
{
return isset(static::$macros[$name]);
}
/**
* 处理Collection类里不存在的静态方法调用
* 也就是这里自定义扩充的方法
*
* @param string $method
* @param array $parameters
* @return mixed
*
* @throws \BadMethodCallException
*/
public static function __callStatic($method, $parameters)
{
// 如果要调用的方法并不存在,就抛出错误
if (! static::hasMacro($method)) {
// 这个错误类 其实就是继承了Exception类,没别的啥
throw new BadMethodCallException("Method {$method} does not exist.");
}
// 如果存在,并且是匿名函数
if (static::$macros[$method] instanceof Closure) {
// 首先用匿名函数类Closure::bind 这静态方法来复制这个匿名函数,并且重新定义作用域和上下文$this
// 因为是静态方法,所以 $this 传 null
// 然后再用call_user_func_array 来调用执行匿名函数。
return call_user_func_array(Closure::bind(static::$macros[$method], null, static::class), $parameters);
}
// 如果不属于匿名函数,那么就直接调用。
return call_user_func_array(static::$macros[$method], $parameters);
}
/**
* 处理Collection里不存在的成员方法
* 也就是扩充的方法
*
* @param string $method
* @param array $parameters
* @return mixed
*
* @throws \BadMethodCallException
*/
public function __call($method, $parameters)
{
// 如果要调用的方法并不存在,就抛出错误
if (! static::hasMacro($method)) {
throw new BadMethodCallException("Method {$method} does not exist.");
}
// 若存在,将其赋值给$macro
$macro = static::$macros[$method];
// 如果是属于匿名函数
if ($macro instanceof Closure) {
// 复制该闭包,并为其重新定义上下文$this,和作用域。
return call_user_func_array($macro->bindTo($this, static::class), $parameters);
}
// 如果不是匿名函数,直接调用
return call_user_func_array($macro, $parameters);
}
}
小结:
1.定义一个静态变量来存储方法。
2.mixin静态方法,传入类的实例,利用RelationClass类来获取想要的类型方法,并且利用RelationMethod类里的setAccessible方法使其可以执行。再用invoke方法进行执行,并存入到静态变量里。
3.利用魔术方法__call和__callStatic来执行匿名函数