强化版Macroable
在阅读Laravel源码的过程中,发现了Macroable这个trait,觉得如果能将带有依赖注入的方法也”揉杂”到指定类中就好了。于是经过尝试,有了下面的强化版Macroable。
<?php
use Closure;
use Illuminate\Support\Traits\Macroable;
use ReflectionClass;
use ReflectionMethod;
use BadMethodCallException;
trait InjectMacroable
{
use Macroable;
/**
* Register a custom macro.
*
* @param object $mixin
* @param array $methods
*
* @return void
*/
public static function enhanceMacro($mixin, $methods)
{
if(! is_array($methods)) $methods = [$methods];
if(! empty($methods)){
$reflection_class = new ReflectionClass($mixin);
foreach($methods as $method){
if(!self::hasMacro($method) && $reflection_method = $reflection_class->getMethod($method)){
static::$macros[$reflection_method->name] = $reflection_method->getClosure($mixin);
}
}
}
}
/**
* Mix another object into the class.
*
* @param object $mixin
* @return void
*
* @throws \ReflectionException
*/
public static function mixin($mixin)
{
$methods = (new ReflectionClass($mixin))->getMethods(
ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED
);
foreach ($methods as $method) {
if($method->isConstructor() || $method->isDestructor()) continue;
$method->setAccessible(true);
if(!self::hasMacro($method->name) && $closure=$method->getClosure($mixin)){
static::macro($method->name, $closure);
}
}
}
/**
* Dynamically handle calls to the class.
*
* @param string $method
* @param array $parameters
* @return mixed
*
* @throws \BadMethodCallException
*/
public function __call($method, $parameters)
{
if (! static::hasMacro($method)) {
throw new BadMethodCallException(sprintf(
'Method %s::%s does not exist.', static::class, $method
));
}
$macro = static::$macros[$method];
if ($macro instanceof Closure) {
return app()->call($macro, $parameters);
}
return call_user_func_array($macro, $parameters);
}
}
如果需要在AController中引入BController的Test方法:
class BController{
public function Test(Request $request, Cache $cache)
{
//todo
}
}
class AController{
use InjectMacroable ;
public function __construct()
{
self::enhanceMacro(new BController(), 'Test');
}
}
如果想将BController里的所有方法都引入,则:
self::mixin(new BController());
在路由中定义AController@Test即可访问。
引入的普通方法也能正常访问。
因为多数场景都是调用非静态方法,所以这里只实现了__call方法,有需要的同学可以自行尝试实现__callStatic。
另外,想引入其他类的方法也可以通过继承之类的方式实现,这里只是做一些不同的尝试。
最后,希望和大家一起享受代码的快乐。
本作品采用《CC 协议》,转载必须注明作者和本文链接
没看明白,区别是什么 :sweat_smile: