1、Laravel 好比如是装了药的 药箱,专门处理人们的问题、治病。
2、人们喜欢把我的药箱叫做 service container 服务容器。
3、把我的药箱里面的一块一块的小格子叫 service provider 服务提供者。在这些小格子里可以放置不同的药。
4、有的人用到我的时候,会在我的小格子或者是他们自制的格子里面放置自己制作的药。
有些药有副作用,比如可以治疗肚子疼又能治疗头痛,这样肚子疼、头不疼的患者吃了可能对头产生不良影响。
所以我定了一份合同契约让放进来的药有个规范。并且我内置的药也是有契约来规范约束我自己。他们把我的这个合同称为 contract 契约。
5、有些药片很难看,可以把它用糖衣包装起来,这样小孩子更容易吞食使用。
我里面很多药都用了这种包装,药效没增加,但是更容易使用。
人们后期添加的药也可以自制包装。这种包装称之为 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) { });
二、药箱里的小格子(服务提供者)
制作一个服务提供者
php artisan make:provider RiakServiceProvider
- 服务提供者主要由两个方法:
register
和boot
。register
只负责绑定一些东西到容器(放药)。boot
可以使用类型提示解析等来完成任意你想做的事情,这些都归功于容器调用所有服务提供者的register
方法之后才去调用boot
方法。 - 在
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 列表
四、Contracts
Facades
和 Contracts
没有什么值得注意的区别,但是当你开发第三方包的时候,最好使用 Contracts
,这样有利于你编写测试,否则如果使用 Facades
,因为是第三方包,将不能访问 facade
测试函数。
使用方法
在构造函数中类型提示注入就行了。
Contracts 列表
五、请求生命周期
本节主要概括了框架运行的生命周期。
- 所有请求必定首先通过
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 令牌等)。- 处理请求,返回响应内容。