Spiral 在引导过程中使用 bootloader 类来处理应用程序配置。其中一项关键特性是 bootloader 仅在应用程序引导期间执行一次,确保向其中添加额外代码不会对运行时性能产生负面影响。
Bootloader 类在配置应用程序的各个方面发挥着至关重要的作用。
以下是一些示例:
要轻松创建一个简单的 bootloader,请使用 scaffolding 命令:
php app.php create:bootloader GithubClient
注意 更多关于 scaffolding 的内容请阅读 基础知识 — Scaffolding 章节。
执行此命令后,将通过以下输出确认创建成功:
Declaration of ' [32mGithubClientBootloader [39m' has been successfully written into ' [33mapp/src/Application/Bootloader/GithubClientBootloader.php [39m'.
现在您可以在 app/src/Application/Bootloader
目录中找到 GithubClientBootloader
类。
目前,新的 Bootloader 不会执行任何操作。稍后,我们将为其添加一些功能。
每个 bootloader 都必须在您的应用程序内核 app/src/Application/Kernel.php
中激活。
在 Spiral 中,您可以使用两种不同的方法在内核中注册 bootloader:常量和方法。
Kernel::SYSTEM
、Kernel::LOAD
和 Kernel::APP
之类的常量。这些常量允许您指定应在应用程序初始化过程的不同阶段执行哪些 bootloader。通过使用这些常量,您可以实现清晰的关注点分离,并轻松理解哪些 bootloader 处理特定的任务。defineBootloaders
、defineAppBootloaders
和 defineSystemBootloaders
之类的方法。这些方法允许更复杂的逻辑和对象、匿名类或其他高级用例的注册。使用这些方法,您可以更好地控制和灵活地控制应用程序的初始化过程。警告 您不能同时使用方法和常量。如果您选择使用方法,则通过常量进行的注册将不会生效。
将类引用添加到您的 App\Application\Kernel
类的 defineBootloaders
或 defineAppBootloaders
方法中:
namespace App\Application;
use App\Application\Bootloader\RoutesBootloader;
use App\Application\Bootloader\LoggingBootloader;
use App\Application\Bootloader\MyBootloader;
class Kernel extends \Spiral\Framework\Kernel
{
public function defineBootloaders(): array
{
return [
// ...
RoutesBootloader::class,
];
}
public function defineAppBootloaders(): array
{
return [
LoggingBootloader::class,
MyBootloader::class,
// anonymous bootloader via object instance
new class extends Bootloader {
// ...
},
];
}
}
注意
defineAppBootloaders
方法中的 Bootloader 始终在defineBootloaders
方法中的 bootloader 之后加载。将特定于域的 bootloader 保留在其中。
还有一个特性可以让开发人员管理 bootloader 的设置和使用方式。此功能对于使应用程序适应各种环境(例如 HTTP、命令行界面或任何其他上下文)特别有利。它允许根据环境的特定需求选择性地激活或停用 bootloader,从而提高效率和性能。
有一个新的 DTO 类 Spiral\Boot\Attribute\BootloadConfig
,它支持包含或排除 bootloader,传递将转发到 bootloader 的 init 和 boot 方法的参数,并根据环境变量动态调整 bootloader 加载。
要在 Kernel 中使用它,您应该使用 bootloader 的完整类名作为 bootloader 数组中的键,相应的值是 BootloadConfig
对象。
namespace App\Application;
use Spiral\Boot\Attribute\BootloadConfig;
use Spiral\Prototype\Bootloader\PrototypeBootloader;
class Kernel extends \Spiral\Framework\Kernel
{
public function defineBootloaders(): array
{
return [
// ...
PrototypeBootloader::class => new BootloadConfig(allowEnv: ['APP_ENV' => ['local', 'dev']]),
// ...
];
}
}
在此示例中,我们指定仅当定义了环境变量 APP_ENV
且其值为 local
或 dev
时,才应加载 PrototypeBootloader
。
您无需直接创建 BootloadConfig
对象,而是可以定义一个返回 BootloadConfig
对象的函数。此函数可以接受参数,这些参数可能从容器中获取。
namespace App\Application;
use Spiral\Boot\Attribute\BootloadConfig;
use Spiral\Boot\Environment\AppEnvironment;
use Spiral\Prototype\Bootloader\PrototypeBootloader;
class Kernel extends \Spiral\Framework\Kernel
{
public function defineBootloaders(): array
{
return [
// ...
PrototypeBootloader::class => static fn (AppEnvironment $env) => new BootloadConfig(enabled: $env->isLocal()),
// ...
];
}
}
您还可以使用 BootloadConfig
类作为属性来控制 bootloader 的行为。此方法特别有用,因为它允许您直接在 bootloader 的类中设置配置,使其更简单、更容易理解。
以下是您如何使用属性配置 bootloader 的简单示例:
use Spiral\Boot\Attribute\BootloadConfig;
use Spiral\Boot\Bootloader\Bootloader;
#[BootloadConfig(allowEnv: ['APP_ENV' => 'local'])]
final class SomeBootloader extends Bootloader
{
}
当您希望将配置与 bootloader 的代码保持紧密联系时,属性是一个绝佳的选择。这是一种更直观的设置 bootloader 的方式,尤其是在配置简单且不需要复杂逻辑的情况下。
通过扩展 BootloadConfig
,您可以创建自定义类,这些类封装了 bootloader 应该运行的特定条件。这种方法通过将配置详细信息抽象到这些自定义类中来简化 bootloader 的使用。
namespace App\Application\Bootloader;
use Spiral\Boot\Attribute\BootloadConfig;
use Spiral\Boot\Bootloader\Bootloader;
class TargetRRWorker extends BootloadConfig
{
public function __construct(array $modes)
{
parent::__construct(
env: ['RR_MODE' => $modes],
);
}
}
现在您可以在您的 bootloader 中使用它
use Spiral\Boot\Attribute\BootloadConfig;
use Spiral\Boot\Bootloader\Bootloader;
#[TargetRRWorker(modes: ['http', 'grpc'])]
final class SomeBootloader extends Bootloader
{
}
或者在 Kernel 中使用:
namespace App\Application;
use Spiral\Framework\Kernel;
class Kernel extends Kernel
{
public function defineBootloaders(): array
{
return [
HttpBootloader::class => new TargetRRWorker(['http']),
RoutesBootloader::class => new TargetRRWorker(['http']),
GrpcBootloader::class => new TargetRRWorker(['grpc']),
TemporalBootloader::class => new TargetRRWorker(['temporal']),
// Other bootloaders...
];
}
}
扩展 BootloadConfig
的能力为自定义 bootloader 的行为开辟了无限的可能性。
Bootloader 提供了两个方法 init
和 boot
,它们在应用程序初始化时执行。
此方法将首先运行。
在继续之前为配置文件设置默认值是个好主意。您可以使用特殊的 bootloader 方法根据需要修改配置文件。之后,您可以继续运行任何不需要读取配置文件并且不依赖于其他 bootloader 的 init
和 boot
方法中的代码执行的逻辑。
这也是添加初始化回调和配置容器绑定的好时机,只要它不需要访问应用程序配置即可。
namespace App\Application\Bootloader;
use Spiral\Boot\Bootloader\Bootloader;
use Spiral\Boot\EnvironmentInterface;
use Spiral\Config\ConfiguratorInterface;
use App\Service\Github\GithubConfig;
final class GithubClientBootloader extends Bootloader
{
public function __construct(
private readonly ConfiguratorInterface $config
) {
}
public function init(EnvironmentInterface $env): void
{
$this->config->setDefaults(
GithubConfig::CONFIG,
[
'access_token' => $env->get('GITHUB_ACCESS_TOKEN'),
'secret' => $env->get('GITHUB_SECRET'),
]
);
}
}
注意
此方法将在所有 bootloader 中的 init
方法执行后运行。这样做的原因是您可能需要 bootloader 初始化结果才能继续进行。例如,已编译的配置文件。
请记住,它应该在所有这些 init
方法完成后运行。
namespace App\Application\Bootloader;
// ...
use App\Service\Github\GithubConfig;
final class GithubClientBootloader extends Bootloader
{
// See code above ...
public function boot(GithubConfig $config): void
{
$token = $config->getAccessToken();
// ...
}
}
Bootloader 通常用于设置容器,例如,如果我们想将多个实现链接到它们的接口或创建一些服务。我们可以使用 init
或 boot
方法来做到这一点,这使我们能够使用方法注入来请求我们需要的任何服务。
namespace App\Application\Bootloader;
// ...
use Spiral\Core\BinderInterface;
use App\Service\Github\GithubConfig;
use App\Service\Github\ClinetInterface;
use App\Service\Github\Clinet;
final class GithubClientBootloader extends Bootloader
{
// See code above ...
public function boot(BinderInterface $binder): void
{
$binder->bindSingleton(
ClinetInterface::class,
static fn (GithubConfig $config) => new Clinet(
$config->getAccessToken(),
$config->getSecret(),
)
);
}
}
注意 依赖注入 (DI) 容器将在需要创建
MyService
实例时调用作为参数提供给bindSingleton
方法的闭包。当调用闭包时,DI 容器将自动解析并注入闭包所需的任何依赖项。如果您想了解更多关于 DI 的信息,您可以查看文档的 容器和工厂 部分。它应该包含您需要的所有信息。
Bootloader 还提供了简化容器绑定定义的功能
您可以使用 defineBindings
和 defineSingletons
方法以声明方式定义容器绑定。
namespace App\Application\Bootloader;
// ...
use Spiral\Core\BinderInterface;
use App\Service\Github\GithubConfig;
use App\Service\Github\ClientInterface;
use App\Service\Github\Client;
final class GithubClientBootloader extends Bootloader
{
public function defineSingletons(): array
{
return [
MyInterface::class => MyClass::class
];
}
public function defineSingletons(): array
{
return [
ClientInterface::class => [self::class, 'createClient'],
// or
ClientInterface::class => static fn(GithubConfig $config) => new Client(
$config->getAccessToken(),
$config->getSecret(),
);
];
}
// See code above ...
public function createClient(GithubConfig $config): ClinetInterface
{
return new Clinet(
$config->getAccessToken(),
$config->getSecret(),
);
}
}
Bootloader 的另一个常见用例是在应用程序启动之前配置框架。例如,我们可以为我们的应用程序或模块声明一个新的路由:
namespace App\Application\Bootloader;
use Spiral\Router\RouterInterface;
use Spiral\Router\Target\Controller;
use Spiral\Router\Route;
final class RoutesBootloader extends Bootloader
{
public function boot(RouterInterface $router): void
{
$router->setRoute(
'my-route',
new Route('/<action>', new Controller(MyController::class))
);
}
}
注意 您只能在引导阶段(又名通过另一个 bootloader)使用 bootloader 来配置您的组件。框架不允许您在组件初始化后更改任何配置值。
依赖于其他 bootloader 在某些情况下可能非常有用。例如,如果您想确保在您的 bootloader 之前初始化某个 bootloader,您可以使用两种主要方法之一:将 bootloader 类注入到 init 或 boot 方法中作为参数,或者在您的 bootloader 类中使用 Bootloader::DEPENDENCIES
常量。
这可以成为管理应用程序初始化并确保在您需要时所有必要的资源和依赖项都可用的一种好方法。请记住,即使依赖 bootloader 被多个其他 bootloader 依赖,也只会初始化一次。
一些框架 bootloader 可以用作配置应用程序设置的简单方法。例如,我们可以使用 Spiral\Bootloader\Http\HttpBootloader
添加全局 PSR-15 中间件:
定义依赖 bootloader 有两种方法:
init
或 boot
方法中作为参数例如:
namespace App\Application\Bootloader;
use Spiral\Bootloader\Http\HttpBootloader;
use App\Middleware\MyMiddleware;
class MyBootloader extends Bootloader
{
public function boot(HttpBootloader $http): void
{
$http->addMiddleware(MyMiddleware::class);
}
}
Bootloader::DEPENDENCIES
常量。当您不需要直接在 bootloader 类中访问依赖 bootloader 时,这可以成为定义依赖 bootloader 的一种便捷方式。例如:
namespace App\Application\Bootloader;
class MyBootloader extends Bootloader
{
protected const DEPENDENCIES = [
\Spiral\Bootloader\Http\HttpBootloader::class
];
public function boot(): void
{
// ...
}
}
在初始化依赖的 bootloader 之后,Spiral 将自动解析并初始化依赖的 bootloader。
这两种方法都允许您以声明方式定义依赖 bootloader,这可以使您更容易管理应用程序的初始化,并确保所有必要的资源和依赖项在需要时可用。
注意 依赖 bootloader 仅初始化一次,即使有多个其他 bootloader 依赖于它们也是如此。
您可以使用 Bootloader 本身控制 bootload 过程,只需请求 Spiral\Boot\BootloadManager
:
namespace App\Application\Bootloader;
use Spiral\Boot\Bootloader\Bootloader;
use Spiral\Boot\BootloadManager;
use Spiral\Bootloader\DebugBootloader;
use Spiral\Boot\EnvironmentInterface;
class AppBootloader extends Bootloader
{
public function boot(BootloadManager $bootloadManager, EnvironmentInterface $env): void
{
if ($env->get('DEBUG')) {
$bootloadManager->bootload([
DebugBootloader::class
]);
}
}
}
现在,通过阅读一些文章深入了解基础知识: