gmazzap / gea
PHP 环境变量管理
Requires
- php: >=5.5
Requires (Dev)
- gmazzap/andrew: 1.0.*
- mockery/mockery: 0.9.3
- phpunit/phpunit: 4.8.*
This package is auto-updated.
Last update: 2024-08-26 14:08:16 UTC
README
PHP 环境变量管理
来自 PHP Dotenv 的分支,由 Vance Lucas 开发。
什么是 Gea
Gea 是一个通过环境变量管理应用程序配置的 PHP 库。
什么是环境变量
环境变量是可能根据应用环境(生产、开发、预发布...)变化的配置。
很多时候,这些都是 敏感 的配置,例如数据库凭证、API 密钥等。
这类配置 绝不能 放置在代码中,出于安全原因,同时也为了能够根据环境轻松更改它们。
如何存储环境变量
环境变量可以设置在运行应用程序的服务器上。这可以通过命令行、通过自动化部署工具(如 Capistrano)或从云托管服务提供的界面(如 Heroku)进行配置。
这很好,但在开发过程中这并不是特别方便。还有另一种方法:.env 文件。
.env
文件
.env
文件是一个文本文件,包含一系列环境变量,每行一个。其写法类似于 bash 语法,例如:
FOO=Bar BAR=Baz
嵌套变量
您甚至可以使用变量来定义其他变量
BASE_PATH=/var/www/app
PUBLIC_PATH=${BASE_PATH}/public
包含空格的字符串
包含空格的字符串需要用引号括起来
TITLE="Hello World"
注释
任何以 #
开头的字符串都是注释
# Following line is super secret PASSWORD=mypassword # Cool, isn't it?
导出
还可以在变量前添加 bash 命令 export
export PASSWORD=mypassword
环境变量与 PHP
PHP 有两个函数用于读取和写入环境变量:getenv()
和 putenv()
。
然而,使用这些函数一次无法设置更多变量,因此您需要解析 .env
文件,逐行读取,处理空格、引号、注释等...
...别担心,这就是 Gea 的作用。
使用 Gea 加载 .env
文件
使用 Gea 从文件加载环境变量非常简单。
首先,我们需要使用静态 instance()
方法获取 Gea\Gea
类的实例。
$gea = Gea\Gea::instance(__DIR__);
唯一必需的参数是保存文件的文件夹路径。文件名默认为 .env
,但可以通过传递名称作为第二个参数进行自定义。
然后,我们可以在获取的实例上调用 load()
$gea->load();
就是这样。文件中的所有环境变量现在都已加载。
读取环境变量
当然,可以使用 PHP 函数 getenv()
读取环境变量,但 Gea 还提供了一种 read()
方法,以及 ArrayAccess
支持。
以下行执行相同操作
$apiKey = $gea->read('APY_KEY'); $apiKey = $gea['APY_KEY']; $apiKey = getenv('APY_KEY');
写入环境变量
如你所料,Gea 还提供了一种 write()
方法,你可以使用 ArrayAccess
语法来写入变量
$gea->write('APY_KEY', 'mysupersecretkey'); $gea['APY_KEY'] = 'mysupersecretkey'; putenv('APY_KEY=mysupersecretkey');
不可变性
环境变量本身不是不可变的。您可以随时更改它们。这对许多人来说可能看起来很正常,但实际上它会增加应用程序的复杂性。
因此,Gea致力于实现不可变性。
例如,无法多次调用load()
。
$gea->load(); # ok $gea->load(); # throw an exception
这也意味着已经写入(或从.env
文件加载)的环境变量不能被覆盖。
$gea->write('A_VAR', 'A value'); # ok $gea->write('A_VAR', 'Another value'); # throw an exception
请注意,Gea不控制putenv()
,因此调用该函数实际上可以覆盖变量,所以不可变行为仅在通过Gea方法访问变量时适用。
但是,如果您真的需要,有两种方法可以更改已设置的变量的值
- 丢弃它
- 强制刷新它
丢弃变量
可以使用Gea的discard()
方法或简单地使用ArrayAccess
语法中的unset()
来丢弃变量。
$gea->discard('FOO'); unset($gea['FOO']);
变量被刷新后,可以设置不同的值
$gea['FOO'] = 'First Value'; echo $gea['FOO']; # print 'First Value' unset($gea['FOO']); # Without this an exception had been thrown on next line $gea['FOO'] = 'Second Value'; echo $gea['FOO']; # Print 'Second Value'
刷新变量
Gea有两种不同的变量刷新方式:硬刷新和软刷新。
软刷新
软刷新允许执行更多的连续load()
调用。然而,如果在第二次调用中加载的变量与第一次加载的相同,则会抛出异常,因为不可变行为。
$gea->flush(Gea\Gea::FLUSH_SOFT);
因此,软刷新仅在所有加载的变量都已丢弃或.env
文件在应用程序执行期间已更改的情况下才有用...这种情况很少见。
硬刷新
硬刷新可以看作是一种批量丢弃,实际上,它可以一次性丢弃更多变量。
$gea->flush(Gea\Gea::FLUSH_HARD, ['FOO', 'BAR', 'BAZ']);
如你所猜,flush()
的第二个参数是需要丢弃的变量的数组。
这意味着我们可能希望知道哪些变量已被设置。
获取变量的名称
当变量从.env
文件加载时,load()
方法返回一个包含所有已加载变量名称的数组(不是值)。
默认情况下,Gea不会将此数组存储在任何地方。但可以通过将Gea\Gea::VAR_NAMES_HOLD
标志作为第三个参数传递给instance()
方法来指示Gea这样做。
$gea = Gea\Gea::instance(__DIR__, '.env', Gea\Gea::VAR_NAMES_HOLD); $gea->load();
Gea被指示保存变量名称后,varNames()
方法会返回它。
var_export( $gea->varNames() ); // array( 'FOO', 'BAR', 'BAZ' )
数组会保持更新,这意味着任何写入的变量都会被添加,任何丢弃的变量都会被删除。
这也意味着此方法与硬刷新结合使用可以丢弃所有已加载的变量。
$gea = Gea\Gea::instance(__DIR__, '.env', Gea\Gea::VAR_NAMES_HOLD); $gea->load(); $gea->flush(Gea\Gea::FLUSH_HARD, $gea->varNames());
实际上,第二个参数可以省略:如果未提供内容且使用Gea\Gea::VAR_NAMES_HOLD
标志实例化Gea,则硬刷新默认为丢弃所有变量。
只读行为
Gea提供了一种禁用所有写入、刷新和丢弃操作的方法。变量加载后,它们不能进行任何更改。
可以通过将Gea\Gea::READ_ONLY
标志作为第三个参数传递给instance()
方法来实现。
$gea = Gea\Gea::instance(__DIR__, '.env', Gea\Gea::READ_ONLY); $gea->load();
之后,任何对write()
、discard()
或flush()
的调用都会抛出异常。
请注意,instance()
的第三个参数接受一个标志位掩码,因此可以组合它们。
$flags = Gea\Gea::READ_ONLY|Gea\Gea::VAR_NAMES_HOLD; $gea = Gea\Gea::instance(__DIR__, '.env', $flags);
过滤和验证变量
Gea的一个强大功能是过滤和验证变量。
环境变量是字符串。然而,配置值不总是字符串...您可能想要整数、布尔值等。
此外,一些配置值是必需的,如果这些必需的配置不存在,则“尽早失败”是正常的。
Gea变量过滤器可以做到所有这些。
使用Gea实例的addFilter()
方法添加过滤器。Gea附带以下过滤器:
- required
- enum
- choices
- int
- float
- bool
- array
- object
- callback
还可以编写自定义过滤器。
过滤器的作用
使用Gea方法访问变量时,返回值可以被过滤器更改。
大多数过滤器是懒加载的,这意味着它们在第一次访问值时(如果需要)才进行评估(仅评估一次)。
执行“required”或“enum”之类的验证过滤器的操作是在变量加载后立即评估的,以确保如果缺少必需的值,则尽早失败。
必需的变量
要使环境变量成为必需的,可以使用“required”过滤器,如下所示
$gea = Gea\Gea::instance(__DIR__); $gea->addFilter('API_KEY', 'required');
现在,当加载变量时,如果API_KEY
变量未设置,则会抛出异常。
并非所有过滤器都具有相同的行为:事实上,只有'required'
、enum
和choices
在加载时进行评估,所有其他过滤器都是懒加载的,它们在第一次访问变量时(如果需要)进行评估。
将变量约束为某些值
'enum'
和'choices'
过滤器几乎做同样的事情:强制变量为可能值集。唯一的区别是enum
执行严格的比较(===
),而'choices'
执行弱比较(==
)。
$gea->addFilter('MY_SWITCH', ['enum' => ['on', 'off']]);
这次我传递了一个包含单个项目的数组作为过滤器,其中键是过滤器名称,值是一个用于配置过滤器的数组。这是实际需要配置的过滤器的使用语法。
值得注意的是,此过滤器可以用来自定义变量,例如上面的代码将在'MY_SWITCH'
变量未设置时触发异常。通过将null
添加到值列表中,变量变为可选。
转换为数值
'int'
和'float'
两个过滤器用于确保应用它们的变量分别转换为整数和浮点数。这些过滤器,就像以下所有过滤器一样,是懒加载的,它们在关联的变量第一次访问时(如果需要)进行评估。
$gea->addFilter('MY_NUMBER', 'int');
如果值不是数值,则过滤器会触发异常。
转换为布尔值
'bool'
过滤器用于将变量转换为布尔值。请注意,此过滤器结合使用filter_var()
和FILTER_VALIDATE_BOOLEAN
常量,这意味着字符串'1'
、'true'
、'yes'
、'on'
都被转换为true
。
$gea->addFilter('AWESOMENESS_ALLOWED', 'bool');
从变量创建数组
'array'
过滤器非常灵活,用于将变量转换为数组。在其最简单形式中,只需用逗号拆分字符串即可
$gea->addFilter('MY_LIST', 'array');
此过滤器的配置接受三个参数:第一个参数允许设置不同的分隔符,第二个参数可用于打开或关闭trim拆分后的项目,最后第三个参数可以设置为回调,该回调将用于映射所有项目。
例如,假设变量USER
在.env
文件中设置如下
USER=" john | doe"
使用过滤器
$gea->addFilter('USER', ['array' => ['|', ArrayFilter::DO_TRIM, 'ucfirst']]); $gea->load();
那么
var_export($gea['USER']); // array( 'John', 'Doe' )
从变量实例化对象
'object'
过滤器可用于使用在添加过滤器时给出的类来实例化对象,并将变量的当前值传递给类构造函数。
$gea->addFilter('USER_EMAIL', ['object' => [My\App\EmailObject::class]);
这对于直接从环境变量实例化值对象非常有用。
使用回调转换变量
Gea附带的最灵活的过滤器可能是callback
,它允许设置一个回调,该回调将传递当前变量的值作为参数。无论回调返回什么,都将用作变量值。
$gea->addFilter('USER_ID', ['callback' => [function($userId) { return My\App\UserFactory::fromID($userId); }]);
组合过滤器
在Gea中,可以组合变量过滤器。当设置了相同变量的多个过滤器时,它们将按照管道模式依次调用:下一个过滤器将使用前一个过滤器的结果作为参数。
要向变量添加更多过滤器,您可以
- 多次使用相同变量名调用
addFilter()
- 将过滤器数组作为
addFilter()
的第二个参数传递 - 以上两种方法都可以
一些示例
$gea->addFilter('USER_ID', ['required', 'int']);
$gea->addFilter('MY_LIST', ['array', ['object' => [\ArrayIterator::class]]);
$gea->addFilter('USER_EMAIL', ['required', ['callback' => function($email) { return filter_var($email, FILTER_SANITIZE_EMAIL); }]);
Gea适用于生产环境
从.env
文件加载变量对于开发来说非常好用,但是在生产环境中应避免使用,以避免加载和解析.env
文件的额外开销。
Gea的不错之处在于您不需要用它来加载变量就可以使用它:您可以使用Gea访问(可选地验证和过滤)环境变量,而无需调用load()
。
Gea将查找变量,无论它们是如何设置的。
这种行为,加上Gea实现的ArrayAccess
接口,允许将其用作配置桶,这在测试中很容易模拟或替换,从而将应用程序代码与任何环境相关操作解耦。
无加载器实例
在不加载任何内容的情况下使用Gea作为配置“容器”的最简单方法是使用名为noLoaderInstance()
的命名构造函数,就像这样简单
$gea = Gea\Gea::noLoaderInstance();
由于不需要加载任何文件,因此不需要传递文件夹路径或文件名。如果需要,可以将Gea标志作为第一个参数传递。
以这种方式获得的实例可以执行任何“正常”Gea实例可以执行的操作,您甚至可以在它上面调用load()
,它将加载没有任何内容,但是任何非懒加载过滤器都将立即评估。
无加载器且只读实例
Gea中另一个可用的命名构造函数是readOnlyInstance()
。使用此方法获得的实例不仅不加载任何内容,而且还处于只读模式。
$gea = Gea\Gea::readOnlyInstance();
等价于
$gea = Gea\Gea::noLoaderInstance(Gea\Gea::READ_ONLY);
集成示例
让我们想象一个类似这样的伪类
namespace MyApp; class App { public function run(\ArrayAccess $configs) { // bootstrap the application here } }
以及一个索引文件,如下所示
$gea = getenv('APP_ENV') === 'production' ? Gea\Gea::noLoaderInstance() # in production we load nothing : Gea\Gea::instance(__DIR__); # in development we load .env file $gea->addFilter(['DB_NAME', 'DB_USER', 'DB_PASS'] 'required'); // maybe more filters here... // This will load nothing on production, but ensures filter are validated $gea->load(); $myApp = new MyApp\App(); $myApp->run($gea);
类App
的代码与环境函数、全局变量甚至Gea特定方法完全解耦。这意味着在测试中模拟配置将非常容易,或者可能切换到另一种类型的配置,例如JSON、Yaml或仅仅是PHP文件。
为什么选择Gea?
Gea是由Vance Lucas的PHP dotenv分支而来。
该库被广泛使用,并被PHP社区视为非常经济的代码。
那么,为什么是这个?
一切始于我为一个应用程序工作,我无法允许环境变量存储在$_SERVER
数组中(这是PHP Dotenv的默认行为),这是暴露的。
我意识到定制此行为并不像我之前想象的那么简单,于是决定分叉该库并添加一个接口,该接口允许更容易地替换PHP Dotenv的Loader
类。
原始想法是向PHP Dotenv做出贡献,但在分叉并添加了接口之后,我无法停止自己重构……最终以完全不同的架构进行了完全重写。
这肯定不能合并到一个pull请求中。
Gea的架构比PHP Dotenv更复杂,这是不好的,但它也更加灵活,并且具有更多PHP Dotenv中没有的“糖”。
当然,人们可以使用Gea像Dotenv那样只加载一个.env
文件,而不使用其他功能或自定义;在这种情况下,与PHP Dotenv的差异几乎为零。
根据您的使用情况,Gea可能适合或可能不适合。
PHP Dotenv和Gea之间的一些差异
- Gea具有高级变量类型转换和过滤的过滤器(PHP Dotenv有“required”和“allowedValues”用于验证)
- Gea实现了
ArrayAccess
、read()
、write()
、discard()
和flush()
方法,使其可以用作配置"桶"。这使得Gea更像是一个配置"桶",而不仅仅是加载环境变量。这个特性与可能根本不使用的加载器完全解耦。 - Gea默认确保了不可变性,当使用其方法访问值时。
- 在Gea中,很容易了解哪些变量已被设置/加载。
- Gea是完全面向对象的,这意味着实现其接口很容易自定义其行为。
- 最低要求的PHP版本:PHP Dotenv为5.3.9,Gea为5.5。
- 许可证为PHP Dotenv的BSD-3-clause,Gea的MIT。
包含PHP Dotenv的部分。
此包包含来自PHP Dotenv的代码部分。更具体地说,解析"引擎"基本上是从那里直接采用的。
测试固定值和一些测试来源来自PHP Dotenv,以确保Gea加载文件的方式与Dotenv一样。
包含来自PHP Dotenv代码的所有文件都在顶部包含许可证声明。
最低要求
- PHP 5.5+
安装
使用Composer require gmazzap/gea
。
许可证
Gea在MIT许可证下发布 https://open-source.org.cn/licenses/MIT
贡献
请参阅CONTRIBUTING.md
。
不要使用问题跟踪器(也不发送任何pull请求)如果您发现一个安全问题。它们是公开的,所以请将电子邮件发送到我的Github个人资料上的地址。谢谢。