在閱讀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 協議》,轉載必須註明作者和本文連結