unleash/symfony-client-bundle

安装数: 164 370

依赖项: 0

建议者: 2

安全性: 0

星标: 32

关注者: 4

分支: 8

开放性问题: 3

类型:symfony-bundle

v0.11.382 2024-06-03 14:48 UTC

README

Download

这是一个用于PHP实现的Unleash协议(也称为GitLab中的功能标志)的Symfony包。您可以在Unleash官网GitLab文档中查看更多信息。

PackagistGitHub上查看独立的PHP版本。

Unleash允许您在基于多种策略(如仅向特定用户或向用户基数的百分比发布)进行完整发布之前,逐步发布您的应用功能。有关更多信息,请参阅上述链接文档。

需要php 7.3或更高版本。

要了解方法的通用描述,请阅读独立包的文档,本README将重点介绍Symfony特定内容

安装

composer require unleash/symfony-client-bundle

如果您使用flex,则捆绑包将自动启用,否则请将Unleash\Client\Bundle\UnleashSymfonyClientBundle添加到您的config/bundles.php

基本用法

首先配置基本参数,可以使用DSN或作为单独的参数

unleash_client:
  dsn: http://localhost:4242/api?instance_id=myCoolApp-Server1&app_name=myCoolApp

unleash_client:
  app_url: http://localhost:4242/api
  instance_id: myCoolApp-Server1
  app_name: myCoolApp

提示:通过运行php bin/console config:dump unleash_client > config/packages/unleash_client.yaml来生成默认配置,这将创建默认配置文件,然后您可以对其进行修改

<?php

use Unleash\Client\Unleash;

class MyService
{
    public function __construct(Unleash $unleash)
    {
        if ($unleash->isEnabled('someFeatureName')) {
            // todo
        }
    }
}

控制器属性

您还可以使用#[IsEnabled]#[IsNotEnabled]属性在控制器上检查功能标志。您可以在整个控制器类以及具体方法上使用它。

<?php

use Unleash\Client\Bundle\Attribute\IsEnabled;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

#[IsEnabled('my_awesome_feature')]
final class MyController
{
    #[IsEnabled('another_awesome_feature', Response::HTTP_BAD_REQUEST)]
    #[Route('/my-route')]
    public function myRoute(): Response
    {
        // todo
    }
    
    #[Route('/other-route')]
    public function otherRoute(): Response
    {
        // todo
    }
}

在上面的示例中,在/my-route上的用户需要同时启用my_awesome_featureanother_awesome_feature(因为类和方法的属性),而/other-route只需要启用my_awesome_feature(因为类属性)。

<?php

use Unleash\Client\Bundle\Attribute\IsNotEnabled;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

#[IsNotEnabled('kill_switch')]
final class MyHeavyController
{
    #[Route('/my-route')]
    public function myRoute(): Response
    {
        // todo
    }
}

在第二个示例中,只有当kill_switch未启用时,/my-route路由才启用。

您还可以注意到,其中一个属性指定了第二个可选的状态码参数。支持的状态码有:

  • 404 - NotFoundHttpException
  • 403 - AccessDeniedHttpException
  • 400 - BadRequestHttpException
  • 401 - UnauthorizedHttpException带有消息“未授权”。
  • 503 - ServiceUnavailableHttpException

默认状态码是404。如果您使用不受支持的状态码,将抛出InvalidValueException

为属性设置自定义异常

如果您想在基于属性拒绝用户访问的情况下使用自定义异常,可以监听事件

<?php

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Unleash\Client\Bundle\Event\UnleashEvents;
use Unleash\Client\Bundle\Event\BeforeExceptionThrownForAttributeEvent;
use Symfony\Component\HttpFoundation\Response;

final class MySubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents(): array
    {
        return [
            UnleashEvents::BEFORE_EXCEPTION_THROWN_FOR_ATTRIBUTE => 'handleException',
        ];
    }
    
    public function handleException(BeforeExceptionThrownForAttributeEvent $event): void
    {
        $statusCode = $event->getErrorCode();
        switch ($statusCode) {
            case Response::HTTP_NOT_FOUND:
                $exception = new CustomException('Custom message');
                break;
            default:
                $exception = null;
        }
        
        // the exception can be a Throwable or null, null means that this bundle reverts 
        // to its own default exceptions
        $event->setException($exception);
    }
}

上下文

上下文对象为Unleash提供额外的参数,并支持Symfony的功能。此上下文还注入到Unleash服务中,而不是通用服务。

<?php

use Unleash\Client\Configuration\Context;
use Unleash\Client\Enum\ContextField;

class MyService
{
    public function __construct(Context $context)
    {
        $context->getCurrentUserId();
        $context->getSessionId();
        $context->getIpAddress();
        $context->hasCustomProperty('someProperty');
        $context->getCustomProperty('someProperty');
        $context->hasMatchingFieldValue('someProperty', ['someValue1', 'someValue2']);
        $context->findContextValue(ContextField::USER_ID);
    }
}

如果已安装Symfony Security组件,将自动分配当前用户ID。您可以通过配置来指定用于用户ID的字段,默认情况下,它使用Symfony\Component\Security\Core\User\UserInterface::getUserIdentifier()Symfony\Component\Security\Core\User\UserInterface::getUsername()

unleash_client:
  context:
    user_id_field: id 

使用此配置,此包将使用id属性来分配用户ID。此属性不必为公共属性。

此包还会自动集成到Symfony的请求堆栈中,从中获取IP地址和会话ID,如果您位于代理后面并且它在您的信任代理列表中,这可能特别有用。

上下文环境默认值为kernel.environment参数的值。

自定义属性

您还可以定义自己的属性,这些属性将存在于上下文中。如果您使用Symfony表达式语言,您还可以在这些属性中使用表达式。如果值是表达式,则必须以>字符开头。如果想要您的值以>开头但不作为表达式,请使用\进行转义。所有表达式都可以访问user变量,该变量是用户对象或null。

unleash_client:
  context:
    custom_properties:
      myCustomProperty: someValue # just a good old string
      myOtherProperty: '> 1+1' # starts with >, it will be turned into expression, meaning the value will be 2
      myEscapedProperty: '\> someValue' # will be turned into '> someValue'
      someUserField: '> user.getCustomField()' # will be the result of the getCustomField() method call
      safeUserField: '> user ? user.getCustomField() : null'

如果您不想在配置中嵌入逻辑,您也可以监听事件

<?php

use Unleash\Client\Bundle\Event\UnleashEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Unleash\Client\Bundle\Event\ContextValueNotFoundEvent;

class MyListener implements EventSubscriberInterface
{
    public static function getSubscribedEvents(): array
    {
        return [
            UnleashEvents::CONTEXT_VALUE_NOT_FOUND => 'handleNotFoundContextValue',
        ];
    }
    
    public function handleNotFoundContextValue(ContextValueNotFoundEvent $event)
    {
        switch ($event->getContextName()) {
            case 'myProperty':
                $value = '...'; // dynamically create the value
                $event->setValue($value);
                break;
        }
    }
}

事件

有两种类型的事件,来自基本Unleash PHP SDK和来自此Symfony包的事件。

基础SDK事件

  • \Unleash\Client\Event\UnleashEvents::FEATURE_TOGGLE_NOT_FOUND
  • \Unleash\Client\Event\UnleashEvents::FEATURE_TOGGLE_DISABLED
  • \Unleash\Client\Event\UnleashEvents::FEATURE_TOGGLE_MISSING_STRATEGY_HANDLER

有关更多信息,请参阅基础SDK中的文档

Symfony包事件

  • \Unleash\Client\Bundle\Event\UnleashEvents::CONTEXT_VALUE_NOT_FOUND
  • \Unleash\Client\Bundle\Event\UnleashEvents::BEFORE_EXCEPTION_THROWN_FOR_ATTRIBUTE

事件在README的相关部分中进行了说明。

Twig

如果您使用twig,可以利用函数、过滤器、测试和自定义标签。名称是通用的,因此您可以在它们与您自己的函数/过滤器/测试/标签冲突时禁用它们中的任何一个。

默认情况下,如果安装了twig,则所有内容都启用。

unleash_client:
  twig:
    functions: true
    filters: true
    tests: true
    tags: true

Twig函数

有两个函数:feature_is_enabled()feature_variant()

第一个返回一个布尔值,第二个返回一个Unleash\Client\DTO\Variant实例。

{% if feature_is_enabled('featureName') %}
    {% set variant = feature_variant('featureName') %}
    {% if variant.enabled %}
        {{ variant.name }}
    {% endif %}
{% endif %}

Twig过滤器

您可以使用具有相同名称的过滤器来代替函数。

{% if 'featureName'|feature_is_enabled %}
    {% set variant = 'featureName' | feature_variant %}
    {# Do something #}
{% endif %}

Twig测试

您还可以使用名为enabled的测试。

{% if 'featureName' is enabled %}
    {# Do something #}
{% endif %}

Twig标签

此标签是实验性的,可能在未来的版本中删除

您可以使用自定义的feature标签。如果特征被启用,则将处理标签体内的任何内容。您还可以访问隐式variant变量。

{% feature 'featureName' %}
    {{ variant.name }} {# Implicit variant variable that only exists in the scope of feature block #}
{% endfeature %}

自定义策略

定义自定义策略非常简单,因为它们会自动注入,您只需要创建一个实现Unleash\Client\Strategy\StrategyHandler(或扩展Unleash\Client\Strategy\AbstractStrategyHandler)的类。

<?php

use Unleash\Client\Strategy\AbstractStrategyHandler;
use Unleash\Client\DTO\Strategy;
use Unleash\Client\Configuration\Context;

class MyCustomStrategy extends AbstractStrategyHandler
{
    public function getStrategyName() : string
    {
        return 'my_custom_strategy';
    }

    public function isEnabled(Strategy $strategy, Context $context) : bool
    {
        $someCustomProperty = $this->findParameter('customProperty', $strategy);
        if ($someCustomProperty === false) {
            return false;
        }
        
        // assume it's a list
        $someCustomProperty = array_map('trim', explode(',', $someCustomProperty));
        
        $enabled = $context->hasMatchingFieldValue('customProperty', $someCustomProperty);
        
        // check if the constraints are valid using the abstract class' method
        if (!$enabled || !$this->validateConstraints($strategy, $context)) {
            return false;
        }
        
        return true;
    }
}

就是这样,由于实现了接口(通过扩展抽象类),您的类自动注册为策略处理器,并且Unleash服务可以处理它。

如果您想使用其中一个默认策略,您可以这样做,所有这些策略都支持自动装配。

禁用内置策略

如果您出于某种原因想要禁用任何内置策略,您可以在配置中这样做。

unleash_client:
  disabled_strategies:
    - default
    - remoteAddress

缓存和http

默认情况下,服务设置为使用symfony/http-clientnyholm/psr7symfony/cache

您可以在配置中覆盖默认值

unleash_client:
  http_client_service: my_custom_http_client_service
  request_factory_service: my_custom_request_factory_service
  cache_service: my_custom_cache_service

HTTP客户端服务必须实现Psr\Http\Client\ClientInterfaceSymfony\Contracts\HttpClient\HttpClientInterface

请求工厂服务必须实现Psr\Http\Message\RequestFactoryInterface

缓存服务必须实现Psr\SimpleCache\CacheInterfacePsr\Cache\CacheItemPoolInterface(这通过扩展意味着它可以实现标准Symfony\Component\Cache\Adapter\AdapterInterface,它扩展了它)。

引导

您可以为SDK设置默认响应,以防由于某种原因无法联系Unleash服务器。

您可以使用文件或实现\Unleash\Client\Bootstrap\BootstrapProvider的服务进行引导。

服务

<?php

use Unleash\Client\Bootstrap\BootstrapProvider;

final class MyBootstrap implements BootstrapProvider
{
    public function getBootstrap() : array|JsonSerializable|Traversable|null{
        // TODO: Implement getBootstrap() method.
    }
}
unleash_client:
  bootstrap: '@MyBootstrap'

提示:如果您只创建一个实现 BootstrapProvider 的服务,它将被自动注入。如果您创建多个,您需要像上面示例中那样手动选择一个引导。

文件

假设您在配置目录中创建了一个名为 bootstrap.json 的文件,这样您就可以将其作为 Unleash 引导注入。

unleash_client:
  bootstrap: 'file://%kernel.project_dir%/config/bootstrap.json'

注意:所有文件都必须以 file:// 前缀开始。

禁用与 Unleash 服务器通信

对于本地开发和使用引导,禁用与 Unleash 服务器的通信可能很有用。

请注意,当您禁用与 Unleash 的通信并且没有提供引导时,将抛出一个异常。

提示:将缓存间隔设置为 0 以始终使用最新的引导内容。

当禁用通信时,通常所需的参数(应用名称、实例 ID、应用 URL)是不需要的。

unleash_client:
  bootstrap: '@MyBootstrap'
  cache_ttl: 0
  fetching_enabled: false

测试命令

如果您需要快速测试您的标志将评估为什么,您可以使用内置命令 unleash:test-flag

该命令有文档说明,以下是 ./bin/console unleash:test-flag --help 的输出。

Description:
  Check the status of an Unleash feature

Usage:
  unleash:test-flag [options] [--] <flag>

Arguments:
  flag                                 The name of the feature flag to check the result for

Options:
  -f, --force                          When this flag is present, fresh results without cache will be forced
      --user-id=USER-ID                [Context] Provide the current user's ID
      --ip-address=IP-ADDRESS          [Context] Provide the current IP address
      --session-id=SESSION-ID          [Context] Provide the current session ID
      --hostname=HOSTNAME              [Context] Provide the current hostname
      --environment=ENVIRONMENT        [Context] Provide the current environment
      --current-time=CURRENT-TIME      [Context] Provide the current date and time
      --custom-context=CUSTOM-CONTEXT  [Context] Custom context values in the format [contextName]=[contextValue], for example: myCustomContextField=someValue (multiple values allowed)
      --expected=EXPECTED              For use in testing, if this option is present, the exit code will be either 0 or 1 depending on whether the expectation matches the result
  -h, --help                           Display help for the given command. When no command is given display help for the list command
  -q, --quiet                          Do not output any message
  -V, --version                        Display this application version
      --ansi|--no-ansi                 Force (or disable --no-ansi) ANSI output
  -n, --no-interaction                 Do not ask any interactive question
  -e, --env=ENV                        The Environment name. [default: "dev"]
      --no-debug                       Switch off debug mode.
      --profile                        Enables profiling (requires debug).
  -v|vv|vvv, --verbose                 Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

配置参考

这是自动生成的配置转储(通过运行 php bin/console config:dump unleash_client)。

# Default configuration for extension with alias: "unleash_client"
# Default configuration for extension with alias: "unleash_client"
unleash_client:

  # You can provide the connection details as a DSN instead of app_url, instance_id and app_name. DSN takes precedence over individual parameters.
  dsn:                  null # Example: 'https://localhost:4242/api?instance_id=myCoolApp-Server1&app_name=myCoolApp'

  # The application api URL
  app_url:              null

  # The instance ID, for Unleash it can be anything, for GitLab it must be the generated value
  instance_id:          null

  # Application name, for Unleash it can be anything, for GitLab it should be a GitLab environment name
  app_name:             null
  context:

    # The field name to use as user id, if set to null getUserIdentifier() or getUsername() method will be called
    user_id_field:        null

    # Any additional context properties
    custom_properties:    []

  # Whether to enable communication with Unleash server or not. If you set it to false you must also provide a bootstrap.
  fetching_enabled:     true

  # The http client service, must implement the Psr\Http\Client\ClientInterface or Symfony\Contracts\HttpClient\HttpClientInterface interface
  http_client_service:  psr18.http_client

  # The request factory service, must implement the Psr\Http\Message\RequestFactoryInterface interface. Providing null means autodetect between supported default services.
  request_factory_service: null

  # The cache service, must implement the Psr\SimpleCache\CacheInterface or Psr\Cache\CacheItemPoolInterface interface
  cache_service:        cache.app

  # Default bootstrap in case contacting Unleash servers fails. Can be a path to file (prefixed with file://) or a service implementing Unleash\Client\Bootstrap\BootstrapProvider (prefixed with @)
  bootstrap:            null

  # Disabled default strategies, must be one of: default, flexibleRollout, gradualRolloutRandom, gradualRolloutSessionId, gradualRolloutUserId, remoteAddress, userWithId, applicationHostname
  disabled_strategies:  []

  # The interval at which to send metrics to the server in milliseconds
  metrics_send_interval: 30000

  # Whether to allow sending feature usage metrics to your instance of Unleash, set this to false for GitLab
  metrics_enabled:      true

  # Whether to allow automatic client registration on client initialization, set this to false for GitLab
  auto_registration:    true

  # The time in seconds the features will stay valid in cache
  cache_ttl:            30

  # The maximum age (in seconds) old features will be served from cache if http request fails for some reason
  stale_ttl:            1800

  # Additional headers to use in http client, for Unleash "Authorization" is required
  custom_headers:       []

  # Enable or disable twig function/filter/tests
  twig:

    # Enables the "feature_is_enabled" and "feature_variant" twig functions
    functions:            false

    # Enables the "feature_is_enabled" and "feature_variant" filters
    filters:              false

    # Enables the "enabled" test, allowing you to write {% if "featureName" is enabled %}
    tests:                false

    # Enables the "feature" twig tag
    tags:                 false