crell / envmapper
一个简单的、快速的从环境变量到类对象的映射器。
Requires
- php: ~8.1
Requires (Dev)
- phpbench/phpbench: ^1.2
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^10.0
Suggests
- crell/serde: A full and robust serialization library. It can do much more advanced manipulation when reading in env vars if EnvMapper is insufficient for your needs.
This package is auto-updated.
Last update: 2024-08-30 01:39:43 UTC
README
读取环境变量是大多数应用程序的常见部分。然而,这通常是通过在代码的任意位置调用 getenv()
或从任意位置读取 $_ENV
来以非正式和不安全的方式进行。这意味着错误处理、缺失值处理、默认值等散布在代码库中。
这个库改变了这一点。它允许您将环境变量快速映射到任意类对象,这使得可以使用类定义本身进行默认处理、类型安全等。然后可以将该类注册到依赖注入容器中,使其对任何服务自动可用。
用法
EnvMapper 几乎没有配置。一切只是类。
// Define the class. class DbSettings { public function __construct( // Loads the DB_USER env var. public readonly string $dbUser, // Loads the DB_PASS env var. public readonly string $dbPass, // Loads the DB_HOST env var. public readonly string $dbHost, // Loads the DB_PORT env var. public readonly int $dbPort, // Loads the DB_NAME env var. public readonly string $dbName, ) {} } $mapper = new Crell\EnvMapper\EnvMapper(); $db = $mapper->map(DbSettings::class);
$db
现在是一个 DbSettings
实例,如果定义了,所有五个属性都将从环境变量中填充。现在可以在任何地方使用该对象,传递给构造函数,等等。因为它的属性都是 readonly
的,所以您可以放心,没有使用此对象的服务能够修改它。
您可以使用任何喜欢的类。唯一重要的是定义的属性(通过构造函数提升定义或不定义)。属性可以是任何可见性,您可以包含任何您想要的函数。
名称映射和默认值
EnvMapper 将属性名称转换为 UPPER_CASE
风格,这是通常用于环境变量的风格。然后它将查找具有该名称的环境变量并将其分配给该属性。这意味着您可以使用 lowerCamel
或 snake_case
为对象属性命名。两者都可以正常工作。
如果找不到环境变量,但属性在类定义中设置了默认值,则将使用该默认值。如果没有默认值,则将其保留为未初始化。
或者,您可以在 map()
调用中设置 requireValues: true
。如果设置 requireValues
,则缺失属性将引发 MissingEnvValue
异常。
class MissingStuff { public function __construct( public readonly string $notSet, ) {} } // This will throw a MissingEnvValue exception unless there is a NOT_SET env var defined. $mapper->map(MissingStuff::class, requireValues: true);
类型强制
环境变量始终是字符串,但您可能知道它们应该是一个 int
或 float
。EnvMapper 将自动将类似于整数(如“5”或“42”)的值转换为整数,将类似于浮点数(如“3.14”)的值转换为浮点数,以便安全地将它们分配给对象的类型属性。
如果属性是 bool
类型,则“1”、“true”、“yes”和“on”(任何大小写)将评估为 true
。其他任何东西都将评估为 false
。
如果值无法分配(例如,如果 DB_PORT
环境变量设置为 "any"
),则将抛出 TypeMismatch
异常。
dot-env 兼容性
EnvMapper 默认从 $_ENV
读取值。如果您使用的是将 .env
文件读取到环境的库,只要它填充了 $_ENV
,则应该与 EnvMapper 一起正常工作。EnvMapper 不使用 getenv()
,因为它要慢得多。
请注意,在PHP中,您的variables_order
变量需要设置为包含E
(环境),以便$_ENV
变量被填充。如果不设置,$_ENV
将为空。如果您无法配置服务器以填充$_ENV
,且无法切换到非损坏的服务器,作为备选方案,可以将getenv()
的返回值传递给source
参数,如下所示:
$mapper->map(Environment::class, source: getenv());
常见模式
注册到DI容器
使用EnvMapper
的推荐方式是将它连接到您的依赖注入容器,最好是支持自动连接的容器。例如,在Laravel Service Provider中,您可以这样做:
namespace App\Providers; use App\Environment; use Crell\EnvMapper\EnvMapper; use Illuminate\Contracts\Foundation\Application; use Illuminate\Support\ServiceProvider; class EnvMapperServiceProvider extends ServiceProvider { // The EnvMapper has no constructor arguments, so registering it is simple. public $singletons = [ EnvMapper::class => EnvMapper::class, ]; public function register(): void { // When the Environment class is requested, it will be loaded lazily out of the env vars by the mapper. // Because it's a singleton, the object will be automatically cached. $this->app->singleton(Environment::class, fn(Application $app) => $app[EnvMapper::class]->map(Environment::class)); } }
在Symfony中,您可以在services.yaml
中实现相同的配置:
services: Crell\EnvMapper\EnvMapper: ~ App\Environment: factory: ['@Crell\EnvMapper\EnvMapper', 'map'] arguments: ['App\Environment']
现在,任何服务都可以简单地声明一个类型为Environment
的构造函数参数,容器将自动实例化和注入对象,以满足需要。
测试
使用集中式环境变量映射器的主要原因是使测试更容易。直接从每个服务读取环境变量是一个全局依赖项,这使得测试更加困难。相反,将专用的环境类作为可注入的服务(如上面的示例所示)意味着任何使用它的服务都可以轻松地传递一个手动创建的版本。
class AService { public function __construct(private readonly AwsSettings $settings) {} // ... } class AServiceTest extends TestCase { public function testSomething(): void $awsSettings = new AwsSettings(awsKey: 'fake', awsSecret: 'fake'); $s = new Something($awsSettings); // ... } }
多个环境对象
任何设置但不在指定类中存在的环境变量将被忽略。这意味着可以轻松地将不同的变量加载到不同的类中。例如:
class DbSettings { public function __construct( public readonly string $dbUser, public readonly string $dbPass, public readonly string $dbHost, public readonly int $dbPort, public readonly string $dbName, ) {} } class AwsSettings { public function __construct( public readonly string $awsKey, public readonly string $awsSecret, ) {} }
// Laravel version. class EnvMapperServiceProvider extends ServiceProvider { public function register(): void { $this->app->singleton(DbSettings::class, fn(Application $app) => $app[EnvMapper::class]->map(DbSettings::class)); $this->app->singleton(AwsSettings::class, fn(Application $app) => $app[EnvMapper::class]->map(AwsSettings::class)); } }
# Symfony version services: Crell\EnvMapper\EnvMapper: ~ App\DbSettings: factory: ['@Crell\EnvMapper\EnvMapper', 'map'] arguments: ['App\DbSettings'] App\AwsSettings: factory: ['@Crell\EnvMapper\EnvMapper', 'map'] arguments: ['App\AwsSettings']
高级用法
EnvMapper
被设计成轻量级和快速。因此,其功能集是故意有限的。
然而,在某些情况下,您可能希望有更复杂的环境设置。例如,您可能希望更自由地重命名属性,在子对象内部嵌套相关属性,或将逗号分隔的环境变量映射到数组中。但是,EnvMapper
没有设计来处理这些。
然而,其兄弟项目Crell/Serde
可以轻松做到。Serde是一个通用序列化库,但您可以将$_ENV
作为数组传入以反序列化为对象。这样,您就可以访问Serde的所有功能,如重命名、收集、嵌套和在其他方面在对象反序列化过程中转换数据。基本工作流程是相同的,在服务容器中的注册也几乎相同。
$serde = new SerdeCommon(); $env = $serde->deserialize($_ENV, from: 'array', to: Environment::class);
使用Serde将比使用EnvMapper慢一些,但两者仍然非常快,适用于几乎任何应用程序。
有关所有各种选项,请参阅Serde文档。
贡献
请参阅CONTRIBUTING和CODE_OF_CONDUCT以获取详细信息。
安全
如果您发现任何安全相关的问题,请通过电子邮件larry at garfieldtech dot com联系,而不是使用问题跟踪器。
致谢
许可
Lesser GPL版本3或更高版本。有关更多信息,请参阅许可文件。