antfroger / progressive
该库可以逐步、快速和简单地启用新功能
Requires
- php: ^7.3 || ^8.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.0
- phpunit/phpunit: ^9
README
Progressive 是一个功能标志库 (也称为开关、切换等)。
借助 Progressive,您可以 逐步、快速 和 简单 地启用新功能。(在紧急情况下可以快速禁用)
安装
$ composer require antfroger/progressive
使用
// From a file $config = \Symfony\Component\Yaml\Yaml::parseFile('/your-own-config-file.yaml'); // Content of /your-own-config-file.yaml // features: // dark-theme: true // call-center: false // homepage-v2: // enabled: true // Or from a PHP array $config = [ 'features' => [ 'dark-theme' => true, 'call-center' => false, 'homepage-v2' => [ 'enabled' => true ], ] ]; $progressive = new Progressive($config); $progressive->isEnabled('dark-theme'); // true $progressive->isEnabled('call-center'); // false $progressive->isEnabled('homepage-v2'); // true
规则
内置
enabled: true|false
enabled
为所有人、所有地方、始终启用(或禁用)功能。
该值应为布尔值,true|false
。
// Short $config = [ 'features' => [ 'dark-theme' => true ] ]; // Verbose $config = [ 'features' => [ 'dark-theme' => [ 'enabled' => true ] ] ];
自定义
您可能需要更多规则来满足您的需求和堆栈。
假设您想重新设计主页,并逐步显示以测试是否一切正常。
您首先在 dev 中启用它,然后是 preprod,然后是 prod,但只为开发者启用,然后是管理员,然后是 1% 的用户...
您如何实现这一点?
使用自定义规则!
$config = [ 'features' => [ 'homepage-v123' => [ 'env' => ['DEV', 'PREPROD'] ] ] ]; $progressive = new Progressive($config); $progressive->addCustomRule('env', function (Context $context, array $envs) { return in_array(getenv('ENV'), $envs); }); $progressive->isEnabled('homepage-v123'); // Returns true if ENV is DEV or PREPROD, otherwise returns false
(此 lambda 可以通过 上下文对象
进行改进 - 更多信息请参阅 此处)
策略
规则很棒,但有时一个规则不足以决定是否启用功能。
您可能只想在 PROD
中为管理员启用功能,但在 DEV
、TEST
和 PREPROD
中为所有人启用。
或者,您可能想为管理员和一定比例的用户启用功能。
这就是策略发挥作用的地方!
(策略只是嵌套规则的另一种名称。
就像规则一样,您还可以创建自己的策略!)
Progressive 随附两个内置策略
unanimous: []
unanimous
如果所有条件都满足,则启用功能。
该值应为 规则 数组。
$config = [ 'features' => [ 'translate-interface' => [ 'unanimous' => [ 'env' => ['DEV', 'PREPROD'], 'roles' => ['ROLE_ADMIN', 'ROLE_TRANSLATOR'] ] ] ] ];
在此示例中,翻译应用程序的界面仅在 DEV
和 PREPROD
环境中向具有 ROLE_ADMIN
或 ROLE_TRANSLATOR
角色的用户显示。
此策略可以定义为 AND。
partial: []
partial
如果仅满足 一个 条件,则启用功能。
规则按顺序评估。功能将在某个规则为真时立即启用。
该值应为 规则 数组。
$config = [ 'features' => [ 'translate-interface' => [ 'partial' => [ 'env' => ['DEV', 'PREPROD'], 'roles' => ['ROLE_ADMIN', 'ROLE_TRANSLATOR'] ] ] ] ];
在此示例中,翻译应用程序的界面将在
DEV
和PREPROD
环境中显示(对所有用户)- 对于具有
ROLE_ADMIN
或ROLE_TRANSLATOR
角色的用户,无论环境如何。
此策略可以定义为 OR。
上下文对象
Progressive 对您的应用程序逻辑代码一无所知(也不想知道)。
但您可能想在自定义规则中使用自己的逻辑。
在这种情况下,上下文对象是您的朋友!
它只是一个用户定义的数据包。
您可以使用它来改进我们之前的自定义规则
$context = new Context([ 'env' => getenv('ENV') ?: 'PROD' ]); $config = [ 'features' => [ 'homepage-v123' => [ 'env' => ['DEV', 'PREPROD'] ] ] ]; $progressive = new Progressive($config, $context); $progressive->addCustomRule('env', function (Context $context, array $envs) { return in_array($context->get('env'), $envs); });
另一个常见示例是将当前用户存储在上下文中
$context = new Context(['user' => $user]); $config = [ 'features' => [ 'homepage-v123' => [ 'roles' => ['ROLE_ADMIN'] ] ] ]; $progressive = new Progressive($config, $context); $progressive->addCustomRule('roles', function (Context $context, array $roles) { $userRoles = $context->get('user')->getRoles(); foreach ($roles as $role) { if (in_array($role, $userRoles) { return true; } } return false; });
请求不存在的标志
无论何时您请求不存在的标志,isEnabled
都将返回 false。
该标志被视为禁用。
$config = [ 'features' => [] ]; $progressive = new Progressive($config); $progressive->isEnabled('dark-theme'); // false
将功能发布与代码部署分开
Progressive 接收一组配置数组,这使得将发布日历与代码部署解耦成为可能。
在这个例子中,我们使用 Symfony Yaml 组件从 YAML 文件中读取配置。
use Symfony\Component\Yaml\Yaml; $config = Yaml::parseFile('/your-own-config-file.yaml'); // Content of /your-own-config-file.yaml // features: // new-feature: false $progressive = new Progressive($config); $progressive->isEnabled('new-feature'); // false
想象一下,如果您能够独立于代码库部署配置文件,那么您就可以通过仅重新部署 /your-own-config-file.yaml
来发布新功能,而无需部署整个代码库。
// Content of /your-own-config-file.yaml // features: // new-feature: true $progressive->isEnabled('new-feature'); // true
您还可能希望将配置存储在数据库中,并将其作为数组传递给 Progressive。
在您的项目中使用 Progressive
- Progressive 还可以作为 Symfony 扩展包 使用。
受 Laurent Callarec 的 JavaScript 特性标志库 Banderole 启发。