Laravel的Facade,实际上是对service container中的service provider的一层包装,使用魔术方法__callStatic调用service provider类的方法。拿Log Facade来探究一下。
/**
* @see \Illuminate\Log\Writer
*/
class Log extends Facade
{
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor()
{
return 'log';
}
}
Facade类只需要重写父类中的getFacadeAccessor方法,返回一个字符串即可。
返回的log代表什么,有什么用?
到框架的service container里看个究竟
namespace Laravel\Lumen;
use Monolog\Logger;
use Illuminate\Support\Str;
use Illuminate\Http\Request;
use Illuminate\Support\Composer;
use Monolog\Handler\StreamHandler;
use Illuminate\Container\Container;
use Monolog\Formatter\LineFormatter;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Facades\Facade;
use Illuminate\Support\ServiceProvider;
use Zend\Diactoros\Response as PsrResponse;
use Illuminate\Config\Repository as ConfigRepository;
use Symfony\Bridge\PsrHttpMessage\Factory\DiactorosFactory;
class Application extends Container
{
/**
* Register the core container aliases.
*
* @return void
*/
protected function registerContainerAliases()
{
$this->aliases = [
'Illuminate\Contracts\Foundation\Application' => 'app',
'Illuminate\Contracts\Auth\Factory' => 'auth',
'Illuminate\Contracts\Auth\Guard' => 'auth.driver',
'Illuminate\Contracts\Cache\Factory' => 'cache',
'Illuminate\Contracts\Cache\Repository' => 'cache.store',
'Illuminate\Contracts\Config\Repository' => 'config',
'Illuminate\Container\Container' => 'app',
'Illuminate\Contracts\Container\Container' => 'app',
'Illuminate\Database\ConnectionResolverInterface' => 'db',
'Illuminate\Database\DatabaseManager' => 'db',
'Illuminate\Contracts\Encryption\Encrypter' => 'encrypter',
'Illuminate\Contracts\Events\Dispatcher' => 'events',
'Illuminate\Contracts\Hashing\Hasher' => 'hash',
'log' => 'Psr\Log\LoggerInterface', // 重点在这里
'Illuminate\Contracts\Queue\Factory' => 'queue',
'Illuminate\Contracts\Queue\Queue' => 'queue.connection',
'request' => 'Illuminate\Http\Request',
'Laravel\Lumen\Routing\UrlGenerator' => 'url',
'Illuminate\Contracts\View\Factory' => 'view',
];
}
/**
* Register container bindings for the application.
*
* @return void
*/
protected function registerLogBindings()
{
$this->singleton('Psr\Log\LoggerInterface', function () {
if ($this->monologConfigurator) {
return call_user_func($this->monologConfigurator, new Logger('lumen'));
} else {
return new Logger('lumen', [$this->getMonologHandler()]);
}
});
}
}
当我们调用Log::info时,实际上调用的是Psr\Log\LoggerInterface的info方法,Psr\Log\LoggerInterface可以看做Log的代理,Log有需要时,转发给代理,由代理决定具体的执行。有了Facade,代码可读性更高,不需要解具体的内部实现。
进一步说
Log::info()
// 等同于
app('log')->info() // app('log')创建了什么?请看Application::registerLogBindings
代码大致如下
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Http\Request;
use App\Logger;
/**
* Class LogServiceProvider
* @package App\Providers
*/
class LogServiceProvider extends ServiceProvider
{
public function register()
{
// TODO: Implement register() method.
$this->app->singleton('mylog', function($app) {
return new Logger();
});
}
}
在bootstrap/app.php文件添加代码
$app->register(App\Providers\LogServiceProvider::class);
namespace App\Library\Facade;
use Illuminate\Support\Facades\Facade;
class Mylog extends Facade
{
public static function getFacadeAccessor()
{
return 'mylog';
}
}
就是这么简单。
容易看出,Facade类带来了好处,也带来了坏处,比如:PHPStorm的代码跟踪就拿Facade无能为力了。
关于Laravel 5 Service container与service provider,可以参考
Laravel5 container & service provider