crell / config
任何 PHP 项目的快速、类型驱动的配置加载系统。
Requires
- php: ~8.2
- crell/attributeutils: ^1.2
- crell/fp: ^1.0
- crell/serde: ^1.2
Requires (Dev)
- fig/cache-util: ^2.0
- mikey179/vfsstream: ^1.6
- phpbench/phpbench: ^1.1
- phpstan/phpstan: ^1.11
- phpunit/phpunit: ^10.3
- psr/cache: ^3.0
- symfony/yaml: ^6.2
- vishnubob/wait-for-it: dev-master
Suggests
- symfony/yaml: Required to use the YAML source type
This package is auto-updated.
Last update: 2024-08-27 17:11:47 UTC
README
Config Loader 就是它所说的那样:一个简单、快速但强大的配置加载系统,适用于任何框架。
它的工作原理
Config Loader 基于 "配置对象"。配置对象只是一个普通的 PHP 类。每个配置对象完全由一个 PHP 类定义,每个属性都是一个配置值。这意味着每个配置值的名称、类型和默认值都由普通的 PHP 代码定义和认可。如果某个值是必需的,则没有设置默认值。如果是可选的,默认值直接在代码中指定。虽然系统中没有强制要求,但强烈建议将配置类设置为 readonly
。
配置对象可以从不同的来源以 "层" 的形式进行填充。通常,这可以是磁盘上的文件,格式随意(YAML、PHP 等)。多层允许覆盖单个配置属性,例如,有一个基础配置然后为 dev
和 prod
环境进行修改。
由于每个配置对象都是自己的类,因此它可以无缝地与依赖注入容器和测试集成。(请参阅下面的 DI 部分。)
让我们看看一个例子。
YAML 配置示例
注意:要使用 YAML 配置文件,您需要安装 symfony/yaml
。
use Crell\Config\LayeredLoader; use Crell\Config\YamlFileSource; class EditorSettings { public function __construct( public readonly string $color, public readonly string $bgcolor, public readonly int $fontSize = 14, ) {} } $loader = new LayeredLoader([ new YamlFileSource('./config/common'), new YamlFileSource('./config/' . APP_ENV), ]); $editorConfig = $loader->load(EditorSettings::class);
给定磁盘上的这些文件
# config/common/editorsettings.yaml color: "#ccddee" bgcolor: "#ffffff"
# config/dev/editorsettings.yaml bgcolor: '#eeff00'
JSON 配置示例
use Crell\Config\LayeredLoader; use Crell\Config\JsonFileSource; class EditorSettings { public function __construct( public readonly string $color, public readonly string $bgcolor, public readonly int $fontSize = 14, ) {} } $loader = new LayeredLoader([ new JsonFileSource('./config/common'), new JsonFileSource('./config/' . APP_ENV), ]); $editorConfig = $loader->load(EditorSettings::class);
给定磁盘上的这些文件
config/common/editorsettings.json
:
{ "color": "#ccddee", "bgcolor": "#ffffff" }
config/dev/editorsettings.json
:
{ "bgcolor": "#eeff00" }
现在,当运行此代码时,$editorConfig
将具有颜色 #ccddee
、背景颜色 #ffffff
和字体大小 14
(因为提供了默认值)。然而,如果在 dev
环境中运行(APP_ENV
是 dev
),则背景颜色将是 #eeff00
。
结果是快速轻松地加载应用程序的配置,并完全支持按环境覆盖。当然,您也可以以任何其他方式使用层。(例如,根据系统语言变化。)
您可以定义尽可能多的不同配置对象。它们都单独加载。有关如何使用依赖注入来帮助的说明,请参阅下面的部分。
源类型
默认情况下支持几种文件格式,包括 JsonFileSource
、YamlFileSource
、PhpFileSource
,甚至 IniFileSource
(为什么不呢?)。
注意:要使用 YamlFileSource
,您需要安装 symfony/yaml
包。
编写其他源很简单,因为 ConfigSource
接口只有一个方法。
如果您想支持多种文件类型,甚至可以将同一目录中的多个文件类型堆叠到单个列表中读取。
自定义文件键
默认情况下,每个配置对象的标识符和文件名是其类的全名,小写,并将 \
替换为 _
。因此,My\App\Config\EditorSettings
将成为 my_app_config_editorsettings.yaml
(或任何文件格式)。
这通常不是一个好的文件名。但是,您可以通过属性来自定义键,如下所示
use Crell\Config\Config; #[Config(key: 'editor_settings')] class EditorSettings { public function __construct( public readonly string $color, public readonly string $bgcolor, public readonly int $fontSize = 14, ) {} }
现在,这个类的文件名将是 editor_settings.yaml
(以及其他类似文件)。
复杂对象
配置加载器使用功能强大且灵活的 Crell/Serde
库来填充配置对象。这意味着 Serde 的所有灵活性和功能都可以通过属性来访问。请参阅 Serde 的文档以获取所有可能选项,但尤其关注其将属性收集到子对象中、添加或删除前缀、折叠 camelCase 和 snake_case 之间的不同属性,以及更多功能。您还可以使用后加载回调方法进行验证,以实施类型系统无法处理的规则。
如果您没有将 Serde 实例传递给 LayeredLoader,则会自动创建一个。对于简单使用来说,这很好,但如果要将配置加载器集成到依赖注入容器中,则最好注入一个托管的 Serde 实例。
缓存
虽然 Serde 速度相当快,但仍然存在成本。如果您正在加载许多配置对象,那么时间可能会累积。
配置加载器附带两个缓存包装器,可以轻松地包装在 LayeredLoader
旁边。
Psr6CacheConfigLoader
- 向其提供配置好的 PSR-6 池对象和一个LayerdLoader
实例(或任何ConfigLoader
对象),并且它将在加载每个配置对象时自动透明地缓存。SerializedFiesystemCache
- PSR-6 包装器的限制是您需要已经启动了您的缓存后端,而配置通常在请求的早期加载。相反,您可以使用文件系统缓存,将每个对象作为 PHP 序列化值保存到磁盘。它只需要一个路径。这是可能的最快选项,并且对于大多数配置都推荐使用。
注意:请确保存储文件缓存的目录不是公开可访问且安全性很高。反序列化 PHP 对象速度快,但也可能是一个潜在的安全漏洞。绝对不要允许除了缓存包装器之外的其他任何内容写入该目录。
示例
YAML 配置示例
注意:要使用 YAML 配置文件,您需要安装 symfony/yaml
。
use Crell\Config\LayeredLoader; use Crell\Config\YamlFileSource; use Crell\Config\SerializedFilesystemCache; $loader = new LayeredLoader([ new YamlFileSource('./config/common'), new YamlFileSource('./config/' . APP_ENV), ]); $cachedLoader = new SerializedFilesytemCache($loader, '/path/to/cache/dir'); $cachedLoader->load(EditorSettings::class);
JSON 配置示例
use Crell\Config\LayeredLoader; use Crell\Config\JsonFileSource; use Crell\Config\SerializedFilesystemCache; $loader = new LayeredLoader([ new JsonFileSource('./config/common'), new JsonFileSource('./config/' . APP_ENV), ]); $cachedLoader = new SerializedFilesytemCache($loader, '/path/to/cache/dir'); $cachedLoader->load(EditorSettings::class);
依赖注入
配置加载器优化的目标是集成到依赖注入容器中。具体来说,它可以作为一个工厂来生产每种配置类型的对象,然后可以将这些对象暴露给容器的自动装配功能。
考虑以下服务类
class EditorForm { public function __construct( private EditorSettings $settings, ) {} public function renderForm(): string { // Do stuff here. $this->settings->color; ... } }
它依赖于一个 EditorSettings
实例。它如何获取它,没有人关心。但它可以依赖于 PHP 提供的所有关于类型安全、值已定义、IDE 中的自动完成等的保证。
您现在可以在测试中轻松测试该服务,通过创建自己的 EditorSettings
实例并将其传递进去
class EditorFormTest extends TestCase { #[Test] public function some_test(): void { $settings = new EditorSettings(color: '#fff', bgcolor: '#000'); $subject = new EditorForm($settings); // Make various assertions. } }
对于运行中的应用程序,您可以将每个配置对象注册为服务,按其类名键入,并定义它为对配置加载器服务的工厂调用进行加载。现在,容器将自动创建、缓存每个配置对象,并在需要时将其注入到需要它的服务中,就像任何其他服务一样。
例如,在 Laravel 中,您可以这样做
namespace App\Providers; use Crell\Config\ConfigLoader; use Crell\Config\LayeredLoader; use Crell\Config\PhpFileSource; use Crell\Config\SerializedFilesystemCache; use Crell\Serde\Serde; use Crell\Serde\SerdeCommon; use Illuminate\Contracts\Foundation\Application; use Illuminate\Support\ServiceProvider; class ConfigServiceProvider extends ServiceProvider { public $singletons = [ // Wire up Serde first. See its documentation for more // robust ways to configure it. Serde::class => SerdeCommon::class, ]; public function register(): void { // Set up some sources. $this->app->singleton('base_config', fn(Application $app) => new PhpFileSource('config/base') ); $this->app->singleton('env_config', fn(Application $app) => new PhpFileSource('config/'. APP_ENV) ); // Register the loader, and wrap it in a cache. $this->app->singleton(LayeredLoader::class, fn(Application $app) => new LayeredLoader( [$app['base_config'], $app['env_config']], $app[Serde::class], ) ); $this->app->singleton(ConfigLoader::class, fn(Application $app) => new SerializedFilesystemCache($app[LayeredLoader::class], 'cache/config') ); // Now register the config objects. // You could also use a compiler pass to discover these from disk and // auto-register them, if your framework has that ability. $this->app->singleton(EditorSettings::class, fn(Application $app) => $app[ConfigLoader::class]->load(EditorSettings::class); } }
现在,当第一次加载需要 EditorSettings
的服务(如 EditorForm
)时,将创建、填充、缓存在磁盘上,并在容器中作为单例缓存 EditorSettings
配置对象服务。一切都是透明的!任何需要 EditorSettings
的服务都可以简单地声明一个构造函数依赖项,然后完成。在后续加载中,将从磁盘加载缓存的版本,以获得更快的速度。
贡献
请参阅 CONTRIBUTING 和 CODE_OF_CONDUCT 了解详细信息。
安全
如果您发现任何与安全相关的问题,请通过电子邮件larry at garfieldtech dot com联系,而不是使用问题跟踪器。
致谢
许可证
较宽松的GPL版本3或更高版本。请参阅许可证文件获取更多信息。