Laravel的Facade,實際上是對service container中的service provider的一層包裝,使用魔術方法__callStatic調用service provider類的方法。拿Log Facade來探究一下。
<?php
namespace Illuminate\Support\Facades;
/**
* @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裏看個究竟
<?php
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
開始創建一個Facade
第一步,實現一個service provider
代碼大致如下
<?php
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();
});
}
}
第二步,註冊service provider
在bootstrap/app.php文件添加代碼
$app->register(App\Providers\LogServiceProvider::class);
第三步,創建Facade類
<?php
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