前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Laravel底层学习笔记04 加载并启动ServiceProvider,事件(观察者模式)

Laravel底层学习笔记04 加载并启动ServiceProvider,事件(观察者模式)

作者头像
用户7353560
修改2021-11-08 09:21:24
5500
修改2021-11-08 09:21:24
举报
文章被收录于专栏:天天向上吧天天向上吧

加载并启动ServiceProvider

源码

public/index.php

代码语言:javascript
复制
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
//1. Illuminate\Contracts\Http\Kernel::class 是别名
//2. $kernel是App\Http\Kernel的实例化对象
//3. App\Http\Kernel::class继承src/Illuminate/Foundation/Http/Kernel

vim src/Illuminate/Foundation/Http/Kernel.php

代码语言:javascript
复制
//处理HTTP请求
public function handle($request) ...

vim vendor/symfony/http-foundation/Request.php

代码语言:javascript
复制
//发送请求穿过中间件和路由
protected function sendRequestThroughRouter($request) ...
//为HTTP请求引导应用程序
public function bootstrap()
{
    if (! $this->app->hasBeenBootstrapped()) {
        $this->app->bootstrapWith($this->bootstrappers());
    }
}

vim vendor/laravel/framework/src/Illuminate/Foundation/Application.php

代码语言:javascript
复制
/**
 * 运行给定的引导类数组 Kernel::class $this->bootstrappers
 * 
 * \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class, 
 * 加载环境变量
 * \Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
 * 加载配置文件
 * \Illuminate\Foundation\Bootstrap\HandleExceptions::class,
 * 加载异常处理
 * \Illuminate\Foundation\Bootstrap\RegisterFacades::class,
 * 注册门面模式
 * \Illuminate\Foundation\Bootstrap\SetRequestForConsole::class,
 * 设置Console请求
 * \Illuminate\Foundation\Bootstrap\RegisterProviders::class,
 * 注册Providers
 * \Illuminate\Foundation\Bootstrap\BootProviders::class,
 * 启动Providers
 */
public function bootstrapWith(array $bootstrappers)
{
    $this->hasBeenBootstrapped = true;
    foreach ($bootstrappers as $bootstrapper) {
        $this['events']->fire('bootstrapping: '.$bootstrapper, [$this]);
        //调用$bootstrapper实例中的bootstrap方法
        $this->make($bootstrapper)->bootstrap($this);
        //Container引用了ArrayAccess
        //$this['events']相当于$this->make('events')
        $this['events']->fire('bootstrapped: '.$bootstrapper, [$this]);
    }
}

vim vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php

代码语言:javascript
复制
//启动事件并调用监听者
public function fire($event, $payload = [], $halt = false)
{
    return $this->dispatch($event, $payload, $halt);
}

注册Providers

vim Illuminate/Foundation/Bootstrap/RegisterProviders.php

代码语言:javascript
复制
public function bootstrap(Application $app)
{
    $app->registerConfiguredProviders();
}

vim Illuminate/Foundation/Application.php

代码语言:javascript
复制
// 将所有配置的providers载入框架
public function registerConfiguredProviders(){
    //$this->config['app.providers']是app/config的providers
    //将是否以Illuminate开头的providers分割到两个Collection数组中
    $providers = Collection::make($this->config['app.providers'])//return static 延时加载
                        ->partition(function ($provider) {
                            return Str::startsWith($provider, 'Illuminate\\');
                        });
    //引入第三方的Service Providers加入Collection数组中
    $providers->splice(1, 0, [$this->make(PackageManifest::class)->providers()]);
    // 
    (new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath()))
                    ->load($providers->collapse()->toArray());
}

vim src/Illuminate/Foundation/ProviderRepository.php

代码语言:javascript
复制
//注册Service Providers
public function load(array $providers)
{
    //加载bootstrap/cache/services.php
    $manifest = $this->loadManifest();
    //判断是否需要重新编译$manifest
    if ($this->shouldRecompile($manifest, $providers)) {
        $manifest = $this->compileManifest($providers);
    }
    //注册需要延迟加载的Serivice Provider($defer=true)
    foreach ($manifest['when'] as $provider => $events) {
        $this->registerLoadEvents($provider, $events);
    }
    //注册非延迟加载的Serivice Provider(通过调用register方法)
    foreach ($manifest['eager'] as $provider) {
        $this->app->register($provider);
    }
    //将延迟加载的Serivice Provider加入容器的deferredServices数组
    $this->app->addDeferredServices($manifest['deferred']);
}

启动Providers

代码语言:javascript
复制
public function boot()
{
    if ($this->booted) {
        return;
    }
    $this->fireAppCallbacks($this->bootingCallbacks);
    array_walk($this->serviceProviders, function ($p) {
            $this->bootProvider($p);
    });
    $this->booted = true;
    $this->fireAppCallbacks($this->bootedCallbacks);
}
//调用serviceProviders中Provider实例的boot方法
protected function bootProvider(ServiceProvider $provider)
{
    if (method_exists($provider, 'boot')) {
        return $this->call([$provider, 'boot']);
    }
}

new self 和new static

new self() 返回的是self所在类 new static() 返回调用者所在类

代码语言:javascript
复制
class Father{
    public function getSelf(){
        return new self();
    }
    public function getStatic(){
        return new static();
    }
}
$f = new Father();
echo get_class($f->getSelf());//Father
echo get_class($f->getStatic());//Father
class Son extends Father{
}
$s = new Son();
echo get_class($s->getSelf());//Father
echo get_class($s->getStatic());//Son

boot和register方法的区别

register方法是必须的,boot方法不是

代码语言:javascript
复制
register 方法用于绑定服务到容器,框架会先调用所有 provider 的 register 方法,等所有服务都注册完毕再去调用每一个服务的 boot 方法。

所以不能在 register 方法里面调用其他 provider 提供的服务,因为我们无法保证其他服务已经注册完毕。

而在 boot 方法里面你可以干任何事情!

延迟加载

开启延迟加载

vim FamilyServiceProvider.php

代码语言:javascript
复制
class FamilyServiceProvider extends DeferrableProvider{
    $defer = true;
    public function register(){
    }
    public function providers(){
        return ['Family'];
    }
}

除编译生成的文件(optimize的反向操作)

代码语言:javascript
复制
php artisan clear-compiled

调用

代码语言:javascript
复制
//Application的make会调用deferServices数组中Family对应的实例
app('Family')->test();

//没有实现providers()时的调用方法
app('App\Service\Family\FamilyService')->test();
//providers()方法等价于
app('app')->bind('Family','App\Service\Family\FamilyService');
源码

vim Illuminate/Foundation/Application.php

代码语言:javascript
复制
public function make($abstract, array $parameters = [])
{
    $abstract = $this->getAlias($abstract);
    if (isset($this->deferredServices[$abstract]) && ! isset($this->instances[$abstract])) {
        $this->loadDeferredProvider($abstract);
    }
}

事件和监听者

观察者模式

观察者模式能够便利的创建查看目标对象状态的对象,并且提供与核心对象非耦合的指定性功能。 为软件添加由某个动作或状态变化激活的,但是松散耦合的新功能时,应当创建基于观察者模式的对象。

模拟创建订单时发送短信邮件
非观察者模式
代码语言:javascript
复制
class order{
    public function add(){
        $a = new Message();$a->send();
        $b = new Email();$b->send();
    }
}
class Message{
    public function send(){
        echo '发送短信';
    }
}
class Email{
    public function send(){
        echo '发送邮件';
    }
}
$order = new Order();
$order->add();//发送短信发送邮件
观察者模式
代码语言:javascript
复制
<?php
//被观察者接口
interface Observable{
    function add(Observe $observe);//新增观察者
    function del(Observe $observe);//删除观察者
}
class order implements Observable{
    private $instance = array();
    public function add(Observe $observe){
        if(array_search($observe, $this->instance) === false){
            $this->instance[] = $observe;
        }
    }
    public function del(Observe $observe){
        if($key = array_search($observe, $this->instance) !== false){
            unset($this->instance[$key]);
        }
    }
    public function notify(){
        foreach ($this->instance as $observe){
            $observe->send();
        }
    }
}
//观察者
interface Observe{
    function send();
}
class Message implements Observe{
    public function send(){
        echo '发送短信';
    }
}
class Email implements Observe{
    public function send(){
        echo '发送邮件';
    }
}
$order = new Order();
$order->add(new Message());
$order->add(new Email());
$order->notify();//发送短信发送邮件

如果再想发送钉钉提醒,则只需要实现Observe接口类即可

代码语言:javascript
复制
class DingTalk implements Observe{
    public function send(){
        echo '发送钉钉提醒';
    }
}
$order = new Order();
$order->add(new DingTalk());
$order->notify();//发送钉钉提醒
创建事件和监听者

创建事件和监听者

代码语言:javascript
复制
php artisan make:event EventTest
php artisan make:listener EventTestListener
php artisan make:listener OtherListener

注册事件和监听者

vim app/Providers/EventServiceProvider.php

代码语言:javascript
复制
'App\Events\EventTest' => [
    'App\Listeners\EventListener',
    'App\Listeners\OtherListener',
],

调用事件

代码语言:javascript
复制
event(new EventTest());

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 加载并启动ServiceProvider
    • 源码
      • 注册Providers
        • 启动Providers
          • new self 和new static
            • boot和register方法的区别
              • 延迟加载
                • 开启延迟加载
                • 源码
              • 事件和监听者
                • 观察者模式
                • 模拟创建订单时发送短信邮件
                • 创建事件和监听者
            相关产品与服务
            短信
            腾讯云短信(Short Message Service,SMS)可为广大企业级用户提供稳定可靠,安全合规的短信触达服务。用户可快速接入,调用 API / SDK 或者通过控制台即可发送,支持发送验证码、通知类短信和营销短信。国内验证短信秒级触达,99%到达率;国际/港澳台短信覆盖全球200+国家/地区,全球多服务站点,稳定可靠。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档