该软件包已被弃用,不再维护。未建议替代软件包。

特征控制系统

1.4.2 2022-12-16 00:01 UTC

This package is auto-updated.

Last update: 2022-12-16 00:13:52 UTC


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许可证