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 池对象和一个LayeredLoader
实例(或任何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 而不是使用问题跟踪器。
鸣谢
许可
较宽松的通用公共许可证第3版或更高版本。请参阅许可证文件获取更多信息。