Laravel 核心概念
核心概念
简介、服务容器、服务提供者、facade、contract、生命周期。
简介
我叫 Laravel。我是一个装了药的 药箱,专门处理人们的问题、治病。
人们喜欢把我的药箱叫做 service container 服务容器。
把我的药箱里面的一块一块的小格子叫 service provider 服务提供者。在这些小格子里可以放置不同的药。
有的人用到我的时候,会在我的小格子或者是他们自制的格子里面放置自己制作的药。有些药有副作用,比如可以治疗肚子疼又能治疗头痛,这样肚子疼、头不疼的患者吃了可能对头产生不良影响。所以我定了一份合同契约让放进来的药有个规范。并且我内置的药也是有契约来规范约束我自己。他们把我的这个合同称为 contract 契约。
有些药片很难看,可以把它用糖衣包装起来,这样小孩子更容易吞食使用。我里面很多药都用了这种包装,药效没增加,但是更容易使用。人们后期添加的药也可以自制包装。这种包装称之为 facade 门面。
一、药箱(服务容器)
绑定,解析,解析事件(类似于在药瓶中放药,取药,取药事项)。
放药(绑定)
基础绑定
$this->app->bind('HelpSpot\API', function ($app) {
return new HelpSpot\API($app->make('HttpClient'));
});
绑定单例
$this->app->singleton('HelpSpot\API', function ($app) {
return new HelpSpot\API($app->make('HttpClient'));
});
绑定实例
$api = new HelpSpot\API(new HttpClient);
$this->app->instance('HelpSpot\API', $api);
绑定实例时给定初始化数据
$this->app->when('App\Http\Controllers\UserController')
->needs('$variableName')
->give($value); // 利用上下文给绑定设置初始数据
举个栗子
interface Fruit
{
public function color();
}
class Apple implements Fruit
{
public $color;
public function __construct($color){
$this->color = $color;
}
public function color(){
return $this->color;
}
}
app()->bind('Fruit', 'Apple');
app()->when('Apple')->needs('$color')->give('red');
echo app('Fruit')->color();
绑定接口到实例
$this->app->bind(
'App\Contracts\EventPusher',
'App\Services\RedisEventPusher'
);
根据上下文提供不同的绑定
$this->app->when(PhotoController::class)
->needs(Filesystem::class)
->give(function () {
return Storage::disk('local');
});
$this->app->when([VideoController::class, UploadController::class])
->needs(Filesystem::class)
->give(function () {
return Storage::disk('s3');
});
给绑定设置标签
$this->app->bind('SpeedReport', function () {
//
});
$this->app->bind('MemoryReport', function () {
//
});
$this->app->tag(['SpeedReport', 'MemoryReport'], 'reports');
$this->app->bind('ReportAggregator', function ($app) {
return new ReportAggregator($app->tagged('reports'));
});
当 Service 已经被解析,extend 方法可以用来修改解析出来的实例 $service:
$this->app->extend(Service::class, function ($service, $app) {
return new DecoratedService($service);
});
拿药(解析)
$this->app->make('HelpSpot\API');
app()->make('HelpSpot\API');
resolve('HelpSpot\API');
app('HelpSpot\API');
app()['HelpSpot\API']
app()->get('HelpSpot\API')
public function xxx(HelpSpot\API $users)
// 注入依赖
$api = $this->app->makeWith('HelpSpot\API', ['id' => 1]);
$api = app('HelpSpot\API', ['id' => 1]);
$api = resolve('HelpSpot\API', ['id' => 1]);
# laravel 实现了 PSR-11 接口,所以就可以用该接口的类型提示解析
use Psr\Container\ContainerInterface;
Route::get('/', function (ContainerInterface $container) {
$service = $container->get('Service');
//
});
容器事件
容器解析任何对象时调用
$this->app->resolving(function ($object, $app) {
});
容器解析 HelpSpot\API 时调用
$this->app->resolving(HelpSpot\API::class, function ($api, $app) {
});
二、药箱里的小格子(服务提供者)
制作一个服务提供者
1. php artisan make:provider RiakServiceProvider
2. 服务提供者主要由两个方法:register 和 boot 。register 只负责绑定一些东西到容器(放药)。boot 可以使用类型提示解析等来完成任意你想做的事情,这些都归功于容器调用所有服务提供者的register方法之后才去调用boot方法。
3. 在config/app.php的providers数组中注册服务提供者。
制作一个延迟服务提供者
如果只是绑定服务到服务容器,可以设置为延迟(实现 DeferrableProvider 接口),在项目真正需要用到之前不会注册。
class RiakServiceProvider extends ServiceProvider implements DeferrableProvider
{
public function register()
{
$this->app->singleton(Connection::class, function ($app) {
return new Connection(config('riak'));
});
}
public function provides()
{
return [Connection::class];
}
}
三、Facades
不要在一个类中,用太多的facades。过于臃肿的情况下应该将大类分解成几个小类。
优点
方便测试(辅助函数和 facades 没什么区别,测试方法也是一样的)。
Route::get('/cache', function () {
return Cache::get('key'); // === return cache('key');
});
public function testBasicExample()
{
Cache::shouldReceive('get')
->with('key')
->andReturn('value');
$this->visit('/cache')
->see('value');
}
实时的 facades
原生用法 vs 实时用法
# 原生用法 class Podcast
use App\Contracts\Publisher;
public function publish(Publisher $publisher)
{
$this->update(['publishing' => now()]);
$publisher->publish($this);
}
# 实时用法
use Facades\App\Contracts\Publisher;
Publisher::publish($this);
测试实时的 facades
use Facades\App\Contracts\Publisher;
Publisher::shouldReceive('publish')->once()->with($podcast);
facades 列表
Facade | Class | Service Container Binding |
---|---|---|
App | Illuminate\Foundation\Application | app |
Artisan | Illuminate\Contracts\Console\Kernel | artisan |
Auth | Illuminate\Auth\AuthManager | auth |
Auth (Instance) | Illuminate\Contracts\Auth\Guard | auth.driver |
Blade | Illuminate\View\Compilers\BladeCompiler blade.compiler | |
Broadcast | Illuminate\Contracts\Broadcasting\Factory | |
Broadcast (Instance) | Illuminate\Contracts\Broadcasting\Broadcaster | |
Bus | Illuminate\Contracts\Bus\Dispatcher | |
Cache | Illuminate\Cache\CacheManager | cache |
Cache (Instance) | Illuminate\Cache\Repository | cache.store |
Config | Illuminate\Config\Repository | config |
Cookie | Illuminate\Cookie\CookieJar | cookie |
Crypt | Illuminate\Encryption\Encrypter | encrypter |
DB | Illuminate\Database\DatabaseManager | db |
DB (Instance) | Illuminate\Database\Connection | db.connection |
Event | Illuminate\Events\Dispatcher | events |
File | Illuminate\Filesystem\Filesystem | files |
Gate | Illuminate\Contracts\Auth\Access\Gate | |
Hash | Illuminate\Contracts\Hashing\Hasher | hash |
Lang | Illuminate\Translation\Translator | translator |
Log | Illuminate\Log\LogManager | log |
Illuminate\Mail\Mailer | mailer | |
Notification | Illuminate\Notifications\ChannelManager | |
Password | Illuminate\Auth\Passwords\PasswordBrokerManager | auth.password |
Password (Instance) | Illuminate\Auth\Passwords\PasswordBroker | auth.password.broker |
Queue | Illuminate\Queue\QueueManager | queue |
Queue (Instance) | Illuminate\Contracts\Queue\Queue | queue.connection |
Queue (Base Class) | Illuminate\Queue\Queue | |
Redirect | Illuminate\Routing\Redirector | redirect |
Redis | Illuminate\Redis\RedisManager | redis |
Redis (Instance) | Illuminate\Redis\Connections\Connection | redis.connection |
Request | Illuminate\Http\Request | request |
Response | Illuminate\Contracts\Routing\ResponseFactory | |
Response (Instance) | Illuminate\Http\Response | |
Route | Illuminate\Routing\Router | router |
Schema | Illuminate\Database\Schema\Builder | |
Session | Illuminate\Session\SessionManager | session |
Session (Instance) | Illuminate\Session\Store | session.store |
Storage | Illuminate\Filesystem\FilesystemManager | filesystem |
Storage (Instance) | Illuminate\Contracts\Filesystem\Filesystem | filesystem.disk |
URL | Illuminate\Routing\UrlGenerator | url |
Validator | Illuminate\Validation\Factory | validator |
Validator (Instance) | Illuminate\Validation\Validator | |
View | Illuminate\View\Factory | view |
View (Instance) | Illuminate\View\View |
四、Contracts
Facades 和 Contracts 没有什么值得注意的区别,但是当你开发第三方包的时候,最好使用 Contracts,这样有利于你编写测试,否则如果使用 Facades,因为是第三方包,将不能访问 facade 测试函数。
使用方法
在构造函数中类型提示注入就行了。
Contracts 列表
Contract | References Facade |
---|---|
Illuminate\Contracts\Auth\Access\Authorizable | |
Illuminate\Contracts\Auth\Access\Gate | Gate |
Illuminate\Contracts\Auth\Authenticatable | |
Illuminate\Contracts\Auth\CanResetPassword | |
Illuminate\Contracts\Auth\Factory | Auth |
Illuminate\Contracts\Auth\Guard | Auth::guard() |
Illuminate\Contracts\Auth\PasswordBroker | Password::broker() |
Illuminate\Contracts\Auth\PasswordBrokerFactory | Password |
Illuminate\Contracts\Auth\StatefulGuard | |
Illuminate\Contracts\Auth\SupportsBasicAuth | |
Illuminate\Contracts\Auth\UserProvider | |
Illuminate\Contracts\Bus\Dispatcher | Bus |
Illuminate\Contracts\Bus\QueueingDispatcher | Bus::dispatchToQueue() |
Illuminate\Contracts\Broadcasting\Factory | Broadcast |
Illuminate\Contracts\Broadcasting\Broadcaster | Broadcast::connection() |
Illuminate\Contracts\Broadcasting\ShouldBroadcast | |
Illuminate\Contracts\Broadcasting\ShouldBroadcastNow | |
Illuminate\Contracts\Cache\Factory | Cache |
Illuminate\Contracts\Cache\Lock | |
Illuminate\Contracts\Cache\LockProvider | |
Illuminate\Contracts\Cache\Repository | Cache::driver() |
Illuminate\Contracts\Cache\Store | |
Illuminate\Contracts\Config\Repository | Config |
Illuminate\Contracts\Console\Application | |
Illuminate\Contracts\Console\Kernel | Artisan |
Illuminate\Contracts\Container\Container | App |
Illuminate\Contracts\Cookie\Factory | Cookie |
Illuminate\Contracts\Cookie\QueueingFactory | Cookie::queue() |
Illuminate\Contracts\Database\ModelIdentifier | |
Illuminate\Contracts\Debug\ExceptionHandler | |
Illuminate\Contracts\Encryption\Encrypter | Crypt |
Illuminate\Contracts\Events\Dispatcher | Event |
Illuminate\Contracts\Filesystem\Cloud | Storage::cloud() |
Illuminate\Contracts\Filesystem\Factory | Storage |
Illuminate\Contracts\Filesystem\Filesystem | Storage::disk() |
Illuminate\Contracts\Foundation\Application | App |
Illuminate\Contracts\Hashing\Hasher | Hash |
Illuminate\Contracts\Http\Kernel | |
Illuminate\Contracts\Mail\MailQueue | Mail::queue() |
Illuminate\Contracts\Mail\Mailable | |
Illuminate\Contracts\Mail\Mailer | |
Illuminate\Contracts\Notifications\Dispatcher | Notification |
Illuminate\Contracts\Notifications\Factory | Notification |
Illuminate\Contracts\Pagination\LengthAwarePaginator | |
Illuminate\Contracts\Pagination\Paginator | |
Illuminate\Contracts\Pipeline\Hub | |
Illuminate\Contracts\Pipeline\Pipeline | |
Illuminate\Contracts\Queue\EntityResolver | |
Illuminate\Contracts\Queue\Factory | Queue |
Illuminate\Contracts\Queue\Job | |
Illuminate\Contracts\Queue\Monitor | Queue |
Illuminate\Contracts\Queue\Queue | Queue::connection() |
Illuminate\Contracts\Queue\QueueableCollection | |
Illuminate\Contracts\Queue\QueueableEntity | |
Illuminate\Contracts\Queue\ShouldQueue | |
Illuminate\Contracts\Redis\Factory | Redis |
Illuminate\Contracts\Routing\BindingRegistrar | Route |
Illuminate\Contracts\Routing\Registrar | Route |
Illuminate\Contracts\Routing\ResponseFactory | Response |
Illuminate\Contracts\Routing\UrlGenerator | URL |
Illuminate\Contracts\Routing\UrlRoutable | |
Illuminate\Contracts\Session\Session | Session::driver() |
Illuminate\Contracts\Support\Arrayable | |
Illuminate\Contracts\Support\Htmlable | |
Illuminate\Contracts\Support\Jsonable | |
Illuminate\Contracts\Support\MessageBag | |
Illuminate\Contracts\Support\MessageProvider | |
Illuminate\Contracts\Support\Renderable | |
Illuminate\Contracts\Support\Responsable | |
Illuminate\Contracts\Translation\Loader | |
Illuminate\Contracts\Translation\Translator | Lang |
Illuminate\Contracts\Validation\Factory | Validator |
Illuminate\Contracts\Validation\ImplicitRule | |
Illuminate\Contracts\Validation\Rule | |
Illuminate\Contracts\Validation\ValidatesWhenResolved | |
Illuminate\Contracts\Validation\Validator | Validator::make() |
Illuminate\Contracts\View\Engine | |
Illuminate\Contracts\View\Factory | View |
Illuminate\Contracts\View\View | View::make() |
五、请求生命周期
本节主要概括了框架运行的生命周期。
- 所有请求必定首先通过 public/index.php。
- 在上述这个文件中首先加载 composer 自动加载文件,然后从 bootstrap/app.php 实例化一个服务容器。
- 接下来,框架会根据请求类型传送请求至 app/Http/Kernel.php 或者 app/Console/Kernel.php。
- app/Http/Kernel.php 扩展了Illuminate\Foundation\Http\Kernel 类,父类强制在处理请求前应该做哪些操作,操作内容都放到了 bootstrappers 数组里面(配置错误处理、配置记录日志、检测应用环境、注册和启动服务提供者等)。子类在数组 middleware 中规定了请求在被处理前必须经过的一些处理(读写 session、判断是否处于维护模式、验证 csrf 令牌等)。
- 处理请求,返回响应内容。