惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

大猫的无限游戏
大猫的无限游戏
Webroot Blog
Webroot Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
T
Threat Research - Cisco Blogs
V2EX - 技术
V2EX - 技术
L
LINUX DO - 热门话题
Google DeepMind News
Google DeepMind News
Recorded Future
Recorded Future
S
Schneier on Security
I
InfoQ
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
The GitHub Blog
The GitHub Blog
S
Security @ Cisco Blogs
O
OpenAI News
W
WeLiveSecurity
Vercel News
Vercel News
阮一峰的网络日志
阮一峰的网络日志
Simon Willison's Weblog
Simon Willison's Weblog
人人都是产品经理
人人都是产品经理
Cloudbric
Cloudbric
The Last Watchdog
The Last Watchdog
The Hacker News
The Hacker News
Google Online Security Blog
Google Online Security Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
GbyAI
GbyAI
NISL@THU
NISL@THU
T
Tailwind CSS Blog
V
Visual Studio Blog
PCI Perspectives
PCI Perspectives
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Jina AI
Jina AI
D
DataBreaches.Net
B
Blog RSS Feed
N
News and Events Feed by Topic
N
News and Events Feed by Topic
H
Heimdal Security Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
腾讯CDC
Latest news
Latest news
V
Vulnerabilities – Threatpost
Hacker News: Ask HN
Hacker News: Ask HN
WordPress大学
WordPress大学
V
V2EX
aimingoo的专栏
aimingoo的专栏
博客园 - 司徒正美
Apple Machine Learning Research
Apple Machine Learning Research
D
Darknet – Hacking Tools, Hacker News & Cyber Security
The Register - Security
The Register - Security
Help Net Security
Help Net Security

青空之蓝

[青空之蓝-2023] - 色彩 | 青空之蓝 [青空之蓝-2022] - 平静 | 青空之蓝 [青空之蓝-2021] - 远望 | 青空之蓝 浅谈垃圾回收 | 青空之蓝 浅谈泛型擦除 | 青空之蓝 浅谈单点登录 | 青空之蓝 使用 Kotlin 编写 Spring 测试 | 青空之蓝 设计模式系列文章 | 青空之蓝 从零实现一个 Java 微框架 - IoC | 青空之蓝 从零实现一个 Java 微框架 - 前言 | 青空之蓝 浅谈 JVM:类加载 | 青空之蓝 浅谈 IO | 青空之蓝 浅谈并发:synchronized & ReentrantLock | 青空之蓝 浅谈并发:CAS & AQS | 青空之蓝 浅谈并发:ThreadLocal | 青空之蓝 浅谈并发:三大特性 | 青空之蓝 浅谈组合注解 & 注解别名 | 青空之蓝 [青空之蓝-2020]-迷茫 | 青空之蓝 Java 系列文章 | 青空之蓝 HTTP 系列文章 | 青空之蓝 浅谈 EatWhatYouKill | 青空之蓝 浅谈可扩展线程池 | 青空之蓝 聊聊写框架 | 青空之蓝 聊聊现状-[2020-09] | 青空之蓝 浅谈并发:锁 | 青空之蓝 浅谈并发:基础 | 青空之蓝 浅谈缓存 | 青空之蓝 无须定义类,Spring 快速注入 Json 参数 | 青空之蓝 浅谈 Proxy 和 Aop | 青空之蓝 从零实现一个 PHP 微框架 - 初始化请求 | 青空之蓝 为 Vue3 添加一个简单的 Store | 青空之蓝 WSL2 踩坑记录 | 青空之蓝 浅谈浏览器Event Loop [更新] | 青空之蓝 从零实现一个 PHP 微框架 - Bootstrap 启动加载 | 青空之蓝 从零实现一个 PHP 微框架 - IoC 容器 | 青空之蓝 从零实现一个 PHP 微框架 - PSR & Composer | 青空之蓝 从零实现一个 PHP 微框架 - 前言 | 青空之蓝 MVVM 简单实现 | 青空之蓝 浅谈 DI 和 IoC | 青空之蓝 中间件实现 [PHP] | 青空之蓝 告别 Windows 终端的难看难用,打造好用的 PowerShell | 青空之蓝 VSCode Java输出中文乱码问题解决[更新] | 青空之蓝 浅谈浏览器渲染 | 青空之蓝 Vue-Cli@2 项目迁移日志 | 青空之蓝 Laragon & Scoop 集成踩坑记录 | 青空之蓝 「一行代码」优雅管理 Windows 软件 | 青空之蓝 [青空之蓝-2019]-年度总结 | 青空之蓝 为Vue添加简单的Store | 青空之蓝 为React添加简单的Store | 青空之蓝 为Vuex添加同步Action | 青空之蓝 浅谈B+树 | 青空之蓝 浅谈跳表 | 青空之蓝 浅谈数据库索引 | 青空之蓝 MySQL事务隔离 | 青空之蓝 算法复杂度分析(1) | 青空之蓝 一年来的经验总结 | 青空之蓝 Acrylic - VSCode Extension | 青空之蓝 ace编辑器设置惯性滚动 | 青空之蓝 Java二叉树实现 | 青空之蓝 Java图实现 | 青空之蓝 为apt方式安装的nginx重新编译增加WebDAV | 青空之蓝 XK-Editor - 一个支持富文本和Markdown的编辑器 | 青空之蓝 JS生成列表树 | 青空之蓝 Laravel生成目录树 | 青空之蓝 XK-Note - 集各种神奇功能的云笔记 | 青空之蓝 PHP GD生成验证码 | 青空之蓝 PHP GD图片处理[转换格式-水印-缩略图] | 青空之蓝 Origami - 简洁轻快的WordPress主题 | 青空之蓝 为WordPress启用WorkBox | 青空之蓝 [青空之蓝-2018]-年度总结 | 青空之蓝 VSCode Java手动导入jar和源码包 | 青空之蓝 Windows IP变化自动发送邮件 | 青空之蓝 C链表实现重制版 | 青空之蓝 C 结构体的定义和使用 | 青空之蓝 图的搜索(遍历) - BFS & DFS | 青空之蓝 Java链表实现 | 青空之蓝 C 快速排序 | 青空之蓝 C 插入排序 | 青空之蓝 C 归并排序 | 青空之蓝 C语言链表实现 | 青空之蓝 VSCode配置Java调试环境[Windows] | 青空之蓝 C 选择排序 | 青空之蓝 C 冒泡排序 | 青空之蓝 VSCode配置PHP调试环境[Windows] | 青空之蓝 VSCode配置C/C++ GDB调试环境[Windows] | 青空之蓝 WordPress友情链接模板 | 青空之蓝 Intel Optane 傲腾内存体验 | 青空之蓝 Mysql双机热备实战 | 青空之蓝 博客一年记录 | 青空之蓝 为WordPress启用Service Worker | 青空之蓝 Bing每日一图API | 青空之蓝 iframe延迟加载 | 青空之蓝 写在2018年高考前 | 青空之蓝 The Fox主题汉化分享 | 青空之蓝 [青空之蓝-2017]-崭新 | 青空之蓝 本博客评论规则 | 青空之蓝 世界,您好! | 青空之蓝
从零实现一个 PHP 微框架 - 服务提供者 | 青空之蓝
Otstar Lin · 2020-07-10 · via 青空之蓝

前言

考试在两周前就结束了,因为一直在填坑 XK-Java,所以一直没更新 PHP 微框架系列文章 2333,最近 XK-Java 也填的差不多了,后续打算把 XK-PHP 也适配到 Swoole,还有 XK-Blog 的坑还没填 ?。

什么是服务提供者(Provider)?

如果你写过 Laravel,那么你一定对 Provider 不陌生,服务提供者实现了将服务绑定到服务容器(IoC Container),并按需启动的功能。

在 Laravel 中,服务提供者是在 config/app.php 的配置文件中配置的:

<?php
return [
    'providers' => [
        /*
         * Laravel Framework Service Providers...
         */
        Illuminate\Auth\AuthServiceProvider::class,
        Illuminate\Broadcasting\BroadcastServiceProvider::class,
        Illuminate\Bus\BusServiceProvider::class,
        Illuminate\Cache\CacheServiceProvider::class,
        Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,
        Illuminate\Cookie\CookieServiceProvider::class,
        Illuminate\Database\DatabaseServiceProvider::class,
        Illuminate\Encryption\EncryptionServiceProvider::class,
        Illuminate\Filesystem\FilesystemServiceProvider::class,
        Illuminate\Foundation\Providers\FoundationServiceProvider::class,
        Illuminate\Hashing\HashServiceProvider::class,
        Illuminate\Mail\MailServiceProvider::class,
        Illuminate\Notifications\NotificationServiceProvider::class,
        Illuminate\Pagination\PaginationServiceProvider::class,
        Illuminate\Pipeline\PipelineServiceProvider::class,
        Illuminate\Queue\QueueServiceProvider::class,
        Illuminate\Redis\RedisServiceProvider::class,
        Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
        Illuminate\Session\SessionServiceProvider::class,
        Illuminate\Translation\TranslationServiceProvider::class,
        Illuminate\Validation\ValidationServiceProvider::class,
        Illuminate\View\ViewServiceProvider::class,

        /*
         * Package Service Providers...
         */

        /*
         * Application Service Providers...
         */
        App\Providers\AppServiceProvider::class,
        App\Providers\AuthServiceProvider::class,
        // App\Providers\BroadcastServiceProvider::class,
        App\Providers\EventServiceProvider::class,
        App\Providers\RouteServiceProvider::class
    ]
];
<?php
return [
    'providers' => [
        /*
         * Laravel Framework Service Providers...
         */
        Illuminate\Auth\AuthServiceProvider::class,
        Illuminate\Broadcasting\BroadcastServiceProvider::class,
        Illuminate\Bus\BusServiceProvider::class,
        Illuminate\Cache\CacheServiceProvider::class,
        Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,
        Illuminate\Cookie\CookieServiceProvider::class,
        Illuminate\Database\DatabaseServiceProvider::class,
        Illuminate\Encryption\EncryptionServiceProvider::class,
        Illuminate\Filesystem\FilesystemServiceProvider::class,
        Illuminate\Foundation\Providers\FoundationServiceProvider::class,
        Illuminate\Hashing\HashServiceProvider::class,
        Illuminate\Mail\MailServiceProvider::class,
        Illuminate\Notifications\NotificationServiceProvider::class,
        Illuminate\Pagination\PaginationServiceProvider::class,
        Illuminate\Pipeline\PipelineServiceProvider::class,
        Illuminate\Queue\QueueServiceProvider::class,
        Illuminate\Redis\RedisServiceProvider::class,
        Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
        Illuminate\Session\SessionServiceProvider::class,
        Illuminate\Translation\TranslationServiceProvider::class,
        Illuminate\Validation\ValidationServiceProvider::class,
        Illuminate\View\ViewServiceProvider::class,

        /*
         * Package Service Providers...
         */

        /*
         * Application Service Providers...
         */
        App\Providers\AppServiceProvider::class,
        App\Providers\AuthServiceProvider::class,
        // App\Providers\BroadcastServiceProvider::class,
        App\Providers\EventServiceProvider::class,
        App\Providers\RouteServiceProvider::class
    ]
];

这些服务提供者会在框架启动后将服务注册到服务容器上,如果你学过 Spring,那么就很好理解了,这些服务提供者就是用编码的方式来注册 BeanComponent 等等,因为 PHP8 才开始支持注解,所以目前跑在 PHP7 的 Laravel 无法像 Spring 一样很方便的绑定服务实例。

光说肯定没法很好的理解,那么我们就来看一个简单的服务提供者吧:

<?php

namespace Illuminate\Cookie;

use Illuminate\Support\ServiceProvider;

class CookieServiceProvider extends ServiceProvider
{
    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        $this->app->singleton('cookie', function ($app) {
            $config = $app->make('config')->get('session');

            return (new CookieJar)->setDefaultPathAndDomain(
                $config['path'], $config['domain'], $config['secure'], $config['same_site'] ?? null
            );
        });
    }
}
<?php

namespace Illuminate\Cookie;

use Illuminate\Support\ServiceProvider;

class CookieServiceProvider extends ServiceProvider
{
    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        $this->app->singleton('cookie', function ($app) {
            $config = $app->make('config')->get('session');

            return (new CookieJar)->setDefaultPathAndDomain(
                $config['path'], $config['domain'], $config['secure'], $config['same_site'] ?? null
            );
        });
    }
}

这是 Laravel 用来注册 CookieJar 实例的服务提供者,可以看到在 register 方法中使用了 singleton 方法将一个的回调注册到了服务容器中,这样,当我们使用 $app->make('cookie') 的时候就可以取得 CookieJar 实例了。

当然服务提供者不止可以注册服务,还能在服务注册后启动实例,只要重写 boot 方法即可。

定义 Provider

了解了服务提供者,那么就可以开始编码了。

首先,我们需要定义 Provider 的接口和抽象类来规范 Provider,如下:

<?php

namespace App\Contracts;

interface Provider
{
    // 之所以只定义 register 方法是因为并不是每一个服务提供者都需要预加载(boot),当然你也可以在接口定义 boot 方法,然后在抽象类中提供一个无影响的实现
    public function register(): void;
}
<?php

namespace App\Contracts;

interface Provider
{
    // 之所以只定义 register 方法是因为并不是每一个服务提供者都需要预加载(boot),当然你也可以在接口定义 boot 方法,然后在抽象类中提供一个无影响的实现
    public function register(): void;
}
<?php

namespace App\Providers;

use App\Application;

abstract class Provider implements \App\Contracts\Provider
{
    /**
     * @var Application
     */
    protected $app;

    /**
     * @var bool
     */
    public $booted = false;

    public function __construct(Application $app)
    {
        $this->app = $app;
    }
}
<?php

namespace App\Providers;

use App\Application;

abstract class Provider implements \App\Contracts\Provider
{
    /**
     * @var Application
     */
    protected $app;

    /**
     * @var bool
     */
    public $booted = false;

    public function __construct(Application $app)
    {
        $this->app = $app;
    }
}

可以看到 ProviderBootstrap 其实差不多,所以这里就不对抽象类做说明了,具体请看 Bootstrap 篇

编写 Provider

由于 Provider 数量众多,所以这里就不全部写了,只列举几个比较典型的。

CookieProvider

CookieProvider 是一个简单纯注册服务的 Provider

<?php

namespace App\Providers;

use App\Http\CookieManager;

class CookieProvider extends Provider
{
    public function register(): void
    {
        // 将 CookieManager 注册到服务容器
        $this->app->singleton(CookieManager::class, null, 'cookie');
    }
}
<?php

namespace App\Providers;

use App\Http\CookieManager;

class CookieProvider extends Provider
{
    public function register(): void
    {
        // 将 CookieManager 注册到服务容器
        $this->app->singleton(CookieManager::class, null, 'cookie');
    }
}

RouteProvider

RouteProvider 在注册后还会提供预加载,即 boot

<?php

namespace App\Providers;

use App\Kernel\RouteManager;

class RouteProvider extends Provider
{
    public function register(): void
    {
        $this->app->singleton(RouteManager::class, null, 'route');
    }

    // 在所有服务容器注册完毕后,经 BootProvider 这个 Bootstrap 调用该方法来预加载服务
    public function boot(): void
    {
        // 预加载就是在框架启动后立即实例化服务的功能
        $this->app->make(RouteManager::class);
    }
}
<?php

namespace App\Providers;

use App\Kernel\RouteManager;

class RouteProvider extends Provider
{
    public function register(): void
    {
        $this->app->singleton(RouteManager::class, null, 'route');
    }

    // 在所有服务容器注册完毕后,经 BootProvider 这个 Bootstrap 调用该方法来预加载服务
    public function boot(): void
    {
        // 预加载就是在框架启动后立即实例化服务的功能
        $this->app->make(RouteManager::class);
    }
}

AspectProvider

除了简单 Provider,还有一些比较复杂的 Provider,如 AspectProvider

<?php

namespace App\Providers;

use App\Kernel\AspectManager;
use function array_keys;
use function config;

class AspectProvider extends Provider
{
    /**
     * @var string[]
     */
    protected $aspects;

    public function register(): void
    {
        $this->app->singleton(
            AspectManager::class,
            function () {
                return new AspectManager($this->app);
            },
            'aspect.manager'
        );
        $this->aspects = config('aspect');
        foreach (array_keys($this->aspects) as $aspect) {
            $this->app->singleton($aspect, null);
        }
    }

    public function boot(): void
    {
        /* @var AspectManager $manager */
        $manager = $this->app->make(AspectManager::class);
        foreach ($this->aspects as $aspect => $points) {
            $manager->putPoint($points, $this->app->make($aspect));
        }
    }
}
<?php

namespace App\Providers;

use App\Kernel\AspectManager;
use function array_keys;
use function config;

class AspectProvider extends Provider
{
    /**
     * @var string[]
     */
    protected $aspects;

    public function register(): void
    {
        $this->app->singleton(
            AspectManager::class,
            function () {
                return new AspectManager($this->app);
            },
            'aspect.manager'
        );
        $this->aspects = config('aspect');
        foreach (array_keys($this->aspects) as $aspect) {
            $this->app->singleton($aspect, null);
        }
    }

    public function boot(): void
    {
        /* @var AspectManager $manager */
        $manager = $this->app->make(AspectManager::class);
        foreach ($this->aspects as $aspect => $points) {
            $manager->putPoint($points, $this->app->make($aspect));
        }
    }
}

AspectProvider 中不止注册了 AspectManager,还会读取配置文件将切面也加载进服务容器,同时在启动时把切面依次放入 AspectManager

加载 Provider

有了 Provider,那么 Provider 是如何被创建和执行的呢?

Bootstrap 有两个 Provider 相关的 Bootstrap,分别是 RegisterProvidersBootProviders,上一篇文章只是简单介绍了这两个 Bootstrap 的功能,这里就详细的讲解一下。

<?php

namespace App\Bootstrap;

use App\Kernel\ProviderManager;
use function config;

class RegisterProviders extends Bootstrap
{
    public function boot(): void
    {
        $this->app->setProviderManager(new ProviderManager($this->app));
        $this->app->getProviderManager()->registers(config('app.providers'));
        $this->app->instance(
            ProviderManager::class,
            $this->app->getProviderManager(),
            'provider_manager'
        );
    }
}
<?php

namespace App\Bootstrap;

use App\Kernel\ProviderManager;
use function config;

class RegisterProviders extends Bootstrap
{
    public function boot(): void
    {
        $this->app->setProviderManager(new ProviderManager($this->app));
        $this->app->getProviderManager()->registers(config('app.providers'));
        $this->app->instance(
            ProviderManager::class,
            $this->app->getProviderManager(),
            'provider_manager'
        );
    }
}

然后我们看看 ProviderManager

<?php

namespace App\Kernel;

use App\Application;
use App\Providers\Provider;
use function array_map;
use function array_walk;
use function is_string;
use function method_exists;

class ProviderManager
{
    /**
     * @var Application
     */
    protected $app;

    /**
     * @var Provider[]
     */
    protected $providers;

    public function __construct(Application $app)
    {
        $this->app = $app;
    }

    /**
     * @param string|Provider $provider
     * @return Provider|null
     */
    public function getProvider($provider): ?Provider
    {
        if (is_string($provider)) {
            return $this->providers[$provider] ?? null;
        }
        return $provider;
    }

    public function setProvider($name, $provider): void
    {
        $this->providers[$name] = $provider;
    }

    public function register($provider, $force = false): Provider
    {
        if (!$force && ($reg = $this->getProvider($provider)) !== null) {
            return $reg;
        }
        if (is_string($provider)) {
            $name = $provider;
            $provider = new $provider($this->app);
            $this->setProvider($name, $provider);
        }
        if (method_exists($provider, 'register')) {
            $provider->register();
        }
        return $provider;
    }

    public function registers(array $providers): array
    {
        return array_map(function ($provider) {
            return $this->register($provider);
        }, $providers);
    }

    public function boot(): void
    {
        array_walk($this->providers, function (Provider $provider) {
            if (!$provider->booted && method_exists($provider, 'boot')) {
                $this->app->call('boot', [], $provider);
                $provider->booted = true;
            }
        });
    }
}
<?php

namespace App\Kernel;

use App\Application;
use App\Providers\Provider;
use function array_map;
use function array_walk;
use function is_string;
use function method_exists;

class ProviderManager
{
    /**
     * @var Application
     */
    protected $app;

    /**
     * @var Provider[]
     */
    protected $providers;

    public function __construct(Application $app)
    {
        $this->app = $app;
    }

    /**
     * @param string|Provider $provider
     * @return Provider|null
     */
    public function getProvider($provider): ?Provider
    {
        if (is_string($provider)) {
            return $this->providers[$provider] ?? null;
        }
        return $provider;
    }

    public function setProvider($name, $provider): void
    {
        $this->providers[$name] = $provider;
    }

    public function register($provider, $force = false): Provider
    {
        if (!$force && ($reg = $this->getProvider($provider)) !== null) {
            return $reg;
        }
        if (is_string($provider)) {
            $name = $provider;
            $provider = new $provider($this->app);
            $this->setProvider($name, $provider);
        }
        if (method_exists($provider, 'register')) {
            $provider->register();
        }
        return $provider;
    }

    public function registers(array $providers): array
    {
        return array_map(function ($provider) {
            return $this->register($provider);
        }, $providers);
    }

    public function boot(): void
    {
        array_walk($this->providers, function (Provider $provider) {
            if (!$provider->booted && method_exists($provider, 'boot')) {
                $this->app->call('boot', [], $provider);
                $provider->booted = true;
            }
        });
    }
}

其实看起来复杂,不过 ProviderManager 做的事情也很简单。

首先利用 Class 创建对应 Provider 实例,然后将 Provider 添加到 ProviderManagerProvider 数组中,最后判断有没有 register 方法,如果有则执行该方法。

有了注册,那么还需要一个启动:

<?php

namespace App\Bootstrap;

class BootProviders extends Bootstrap
{
    public function boot(): void
    {
        $this->app->getProviderManager()->boot();
    }
}
<?php

namespace App\Bootstrap;

class BootProviders extends Bootstrap
{
    public function boot(): void
    {
        $this->app->getProviderManager()->boot();
    }
}

BootProvidersboot 方法会从服务容器中取得 ProviderManager,然后调用其 boot 方法,依次执行 Providerboot 方法。

结语

到这里 Provider 的部分就说完了。现在也放假了,PHP 微框架的文章会逐步更新。不过最近还是比较忙的,更新速度会慢一点。?

从零实现一个 PHP 微框架 - 服务提供者

https://blog.ixk.me/post/implement-a-php-microframework-from-zero-5
  • 许可协议

    BY-NC-SA

  • 本文作者

    Otstar Lin

  • 发布于

    2020/07/10

转载或引用本文时请遵守许可协议,注明出处、不得用于商业用途!

为 Vue3 添加一个简单的 StoreWSL2 踩坑记录