特征控制系统 / fcs
特征控制系统
README
这是什么以及为什么我需要它?
FCS(特征控制系统)是一个简单的功能开关库,可以用来定义具有简单和复杂条件的特征列表,并允许定义环境设置和标签以解决每个功能的状况。
例如,您可以定义特定用户、特定活动Cookie、开发环境、标签或它们的组合逻辑模式的特征。
您可以从一个地方管理所有特征,并轻松地将它们更新到更广泛的受众,回滚到先前环境或完全删除。
FCS在持续集成工作流程中特别有用,在短时间内将多个更改合并到主线中,并非所有更改都准备好部署到生产环境中。
它还可以用于限制某些功能,例如由于市场法规或某些促销的条件。
对于A/B测试以及只针对用户的一部分启用新功能以确认新代码在真实环境中运行良好,而不会对所有用户造成风险,这也很有用。
如何配置特征列表?
对于小型项目,您可以将配置简单地保存在PHP或JSON文件中的数组中。
对于大型项目,最好从redis或其他缓存提供程序加载它,以在不同代码仓库之间分配特征列表及其标志。
在redis集成的情况下,最好在后台办公室准备一些简单的部署脚本,以轻松更新每个特征的标志,理想情况下具有基本用户界面。这样,任何具有权限的团队成员都可以在紧急情况下使用它,包括项目经理或质量保证或甚至市场营销团队来处理特定的临时促销(为不同用户提供仅有的某些标志)。
FCS
配置方法
配置某些设置的代码示例
use \FeatureControlSystem\FCS;
FCS::Instance()->setTag('env', 'preproduction');
FCS::Instance()->setTag('username', 'john123');
FCS::Instance()->setTag('lang', 'en');
// set flags manually
FCS::Instance()->setFlags([
'my-feature' => ['flag' => FCS::FLAG_ACTIVE],
'my-feature2' => ['flag' => FCS::FLAG_TAG, 'name' => 'username', 'value' => 'john123']
]);
// or load flags from redis cache
$redis = new \Redis();
// ...
FCS::Instance()->loadFlags($redis, 'flags');
- setTag
设置带有名称和值的标签
FCS::Instance()->setTag(string $name, string $value);
// e.g. FCS::Instance()->setTag('lang', 'en')
- setFlags
注册具有标志的特征列表,这些标志可以用来确定功能是否激活
FCS::Instance()->setFlags(array $flags);
// e.g. FCS::Instance()->setFlags([
'my-feature' => ['flag' => FCS::FLAG_ACTIVE],
'my-feature2' => ['flag' => FCS::FLAG_TAG, 'name' => 'username', 'value' => 'john123'],
'my-feature3' => ['flag' => FCS::FLAG_TAG, 'name' => 'lang', 'value' => ['pl', 'de']]
])
- loadFlags
注册从redis缓存加载的特征列表及其标志;redis中的数据需要用JSON编码并存储在简单的键下
第一个参数可以是Predis客户端的实例或Redis PHP扩展
FCS::Instance()->loadFlags(object $redis, string $key);
// e.g. FCS::Instance()->loadFlags($redis, 'flags')
获取方法
- feature
检查功能是否激活,并使用返回的布尔值启用某些代码
if (<bool> = FCS::Instance()->feature(string $name)) {
// code is enabled
}
// e.g. FCS::Instance()->feature('my-feature') === true
- getBodyCssClasses
获取可以附加到前端body元素的CSS类字符串
返回的CSS类有"fcs-"前缀
<string> = FCS::Instance()->getBodyCssClasses();
// e.g. FCS::Instance()->getBodyCssClasses() === 'fcs-my-feature fcs-my-feature2 fcs-my-feature100'
- getActiveFeatures
获取特征名称的数组,例如可以将其注入到JS中,以在前端启用某些动态代码
<array:string> = FCS::Instance()->getActiveFeatures();
// e.g. FCS::Instance()->getActiveFeatures() === ['my-feature', 'my-feature2', 'my-feature100']
可用标志
use \FeatureControlSystem\FCS;
FCS::FLAG_INACTIVE = 0
FCS::FLAG_ACTIVE = 1
FCS::FLAG_COOKIE = 2
FCS::FLAG_TAG = 3
FCS::FLAG_MULTI_AND = 4
FCS::FLAG_MULTI_OR = 5
- FLAG_INACTIVE
非活动标志表示功能对所有人禁用
FCS::Instance()->setFlags([
'my-feature' => ['flag' => FCS::FLAG_INACTIVE] // FCS::FLAG_INACTIVE = 0
]);
if (FCS::Instance()->feature('my-feature')) { // false
// no one can see it
}
- FLAG_ACTIVE
活动标志表示功能对所有人启用
FCS::Instance()->setFlags([
'my-feature' => ['flag' => FCS::FLAG_ACTIVE] // FCS::FLAG_ACTIVE = 1
]);
if (FCS::Instance()->feature('my-feature')) { // true
// always working
}
- FLAG_COOKIE
Cookie标志表示对具有匹配其名称的活动的cookie请求服务器的所有人启用功能
Cookie需要具有"fcs-"前缀
注意:特征名称中的斜杠(/)将自动转换为双横线(--)
FCS::Instance()->setFlags([
'my-feature' => ['flag' => FCS::FLAG_COOKIE] // FCS::FLAG_COOKIE = 2
]);
$_COOKIE['fcs-my-feature'] = true;
if (FCS::Instance()->feature('my-feature')) { // true, if fcs-my-feature cookie exists
// working for anyone with enabled cookie "fcs-my-feature" in this case
}
- FLAG_TAG
标签标志表示该功能已启用,对于通过名称定义的标签以及单个值(字符串)或值列表(字符串数组)
FCS::Instance()->setFlags([
'my-feature' => ['flag' => FCS::FLAG_TAG, 'name' => 'lang', 'value' => 'en'], // FCS::FLAG_TAG = 3
'my-feature2' => ['flag' => FCS::FLAG_TAG, 'name' => 'lang', 'value' => ['pl', 'de']] // FCS::FLAG_TAG = 3
]);
FCS::Instance()->setTag('lang', 'en');
if (FCS::Instance()->feature('my-feature')) { // true
// working for "lang" tag if its value is "en"
}
if (FCS::Instance()->feature('my-feature2')) { // false
// working for "lang" tag if its value is "pl" or "de"
}
- FLAG_MULTI_AND
多与标志允许定义多个标志的组,表示如果所有子标志都满足其条件,则功能被启用
FCS::Instance()->setFlags([
'my-feature' => ['flag' => FCS::FLAG_MULTI_AND, 'flags' => [ // FCS::FLAG_MULTI_AND = 4
['flag' => FCS::FLAG_TAG, 'name' => 'domain', 'value' => 'domain.com'], // FCS::FLAG_TAG = 3
['flag' => FCS::FLAG_TAG, 'name' => 'lang', 'value' => 'en'] // FCS::FLAG_TAG = 3
]],
'my-feature2' => ['flag' => FCS::FLAG_MULTI_AND, 'flags' => [ // FCS::FLAG_MULTI_AND = 4
['flag' => FCS::FLAG_TAG, 'name' => 'domain', 'value' => 'domain.com'], // FCS::FLAG_TAG = 3
['flag' => FCS::FLAG_TAG, 'name' => 'lang', 'value' => 'pl'] // FCS::FLAG_TAG = 3
]]
]);
FCS::Instance()->setTag('domain', 'domain.com');
FCS::Instance()->setTag('lang', 'en');
if (FCS::Instance()->feature('my-feature')) { // true
// available only if domain tag is equal "domain.com" AND lang tag is "en"
}
if (FCS::Instance()->feature('my-feature2')) { // false
// available only if domain tag is equal "domain.com" AND lang tag is "pl"
}
- FLAG_MULTI_OR
多或标志允许定义多个标志的组,表示如果有任何子标志满足其条件,则功能被启用
FCS::Instance()->setFlags([
'my-feature' => ['flag' => FCS::FLAG_MULTI_OR, 'flags' => [ // FCS::FLAG_MULTI_OR = 5
['flag' => FCS::FLAG_TAG, 'name' => 'domain', 'value' => 'domain.com'], // FCS::FLAG_TAG = 3
['flag' => FCS::FLAG_TAG, 'name' => 'domain', 'value' => 'domain2.com'] // FCS::FLAG_TAG = 3
]],
'my-feature2' => ['flag' => FCS::FLAG_MULTI_OR, 'flags' => [ // FCS::FLAG_MULTI_OR = 5
['flag' => FCS::FLAG_TAG, 'name' => 'domain', 'value' => 'domain3.com'], // FCS::FLAG_TAG = 3
['flag' => FCS::FLAG_TAG, 'name' => 'domain', 'value' => 'domain4.com] // FCS::FLAG_TAG = 3
]]
]);
FCS::Instance()->setTag('domain', 'domain.com');
if (FCS::Instance()->feature('my-feature')) { // true
// available only if domain tag is equal "domain.com" OR "domain2.com"
}
if (FCS::Instance()->feature('my-feature2')) { // false
// available only if domain tag is equal "domain3.com" OR "domain4.com"
}
示例
use \FeatureControlSystem\FCS;
// Define initial settings for your environment + project + user, usually set just once at the beginning
FCS::Instance()->setTag('env', 'preproduction');
FCS::Instance()->setTag('username', 'test123');
FCS::Instance()->setTag('IP', $_SERVER['REMOTE_ADDR']);
FCS::Instance()->setTag('role', 'user');
FCS::Instance()->setTag('country', 'UK');
FCS::Instance()->setTag('product', 'product1');
FCS::Instance()->setTag('domain', 'domain.com');
FCS::Instance()->setTag('lang', 'en');
// Sometimes you can define also extra tags later
FCS::Instance()->setTag('section', 'promotions');
FCS::Instance()->setTag('promotion', '1');
// Define list of features from array (loaded from configuration file or cache provider e.g. redis)
FCS::Instance()->setFlags([
'experiment' => ['flag' => FCS::FLAG_INACTIVE],
'blue-background' => ['flag' => FCS::FLAG_TAG, 'name' => 'country', 'value' => ['UK', 'DE']],
'xmas-promo' => ['flag' => FCS::FLAG_MULTI_AND, 'flags' => [
['flag' => FCS::FLAG_TAG, 'name' => 'domain', 'value' => 'domain.com'],
['flag' => FCS::FLAG_TAG, 'name' => 'lang', 'value' => 'pl']
]],
'chat' => ['flag' => FCS::FLAG_MULTI_OR, 'flags' => [
['flag' => FCS::FLAG_COOKIE],
['flag' => FCS::FLAG_TAG, 'name' => 'env', 'value' => ['test123', 'test456']]
]],
'auth' => ['flag' => FCS::FLAG_ACTIVE],
'new-registration' => ['flag' => FCS::FLAG_TAG, 'name' => 'env', 'value' => 'preproduction'],
'my-carousel' => ['flag' => FCS::FLAG_COOKIE],
'new-bonus' => ['flag' => FCS::FLAG_TAG, 'name' => 'env', 'value' => ['test123', 'test456']]
]);
// Get list of features to inject to your frontend to JS variable
$activeFeatures = FCS::Instance()->getActiveFeatures(); // ['blue-background', 'chat', 'auth', 'new-registration', 'new-bonus']
// Get CSS classes to append them to <body> element
$cssClasses = FCS::Instance()->getBodyCssClasses(); // 'fcs-blue-background fcs-chat fcs-auth fcs-new-registration fcs-new-bonus'
// Activate features in PHP
if (FCS::Instance()->feature('auth')) { // true
// active feature
}
if (FCS::Instance()->feature('experiment')) { // false
// inactive feature
}
$_COOKIE['fcs-my-carousel'] = 1;
if (FCS::Instance()->feature('my-carousel')) { // true
// feature active if $_COOKIE['fcs-my-carousel'] cookie is enabled
}
if (FCS::Instance()->feature('new-bonus')) { // true
// feature active for this user when logged in (test123)
} else {
// old feature for everyone else
}
if (FCS::Instance()->feature('new-registration')) { // true
// feature active in preproduction environment
}
if (FCS::Instance()->feature('xmas-promo')) { // false
// feature active if both tags domain AND lang meet the current settings
}
if (FCS::Instance()->feature('chat')) { // true
// feature active if cookie is enabled ($_COOKIE['fcs-chat']) OR if one of listed users is logged in
}
许可证
MIT许可证