countxvat/launchdarkly-bundle

为 Symfony 集成的 LaunchDarkly

2.0.0 2023-03-10 22:22 UTC

This package is auto-updated.

Last update: 2024-09-11 13:02:48 UTC


README

tests

简介

这是一个用于集成 LaunchDarkly PHP 客户端的 Symfony 扩展包,LaunchDarkly 是一个功能标志管理服务。

PHP 客户端库的文档可以在 https://launchdarkly.github.io/php-server-sdk 找到

安装

$ composer require countxvat/launchdarkly-bundle

需要以某种方式获取标志。最简单的方法是使用 Guzzle 发送 HTTP 请求。一个更复杂但性能更好的选项是使用 ld-daemon
将标志存储在 Redis 中,然后从那里访问它们。这些选项各有进一步依赖

Guzzle

$ composer require "guzzlehttp/guzzle:^7"
$ composer require "kevinrob/guzzle-cache-middleware": "1.4.1"

Redis 和 ld-bundle

$ composer require "predis/predis": "1.0.*"

还需要设置 ld-daemon

最简单的配置和示例

需要在 AppKernel 中注册该扩展包

// app/AppKernel.php

public function registerBundles()
{
    $bundles = [
        //...
        new new countX\LaunchDarklyBundle\countXLaunchDarklyBundle(),
    ];
}

所需的最小配置是提供您的 API 密钥

#app/config/config.yml

countx_launch_darkly:
    api_key: APIKEY

注意:在实际应用中,不同环境的 API 密钥可能不同,因此将其作为参数将是一个好方法。

然后可以使用标志服务检查一个标志

if ($this->get('countx_launchdarkly.client')->variation('my-flag')) {
    //new feature
}

代码中的标志

在条件语句中检查标志是最简单的入门方法。但这可能不是维护性的最佳方法,以下提供其他选项。

传递默认值

可以传递一个默认值,当遇到错误时(例如,如果功能标志键不存在或用户未指定密钥)将使用它。默认为 false。

if ($this->get('countx_launchdarkly.client')->variation('my-flag', true)) {
    //new feature
}

服务

您还可以将服务注入其他类,以及使用常规的 Symfony 服务配置从容器中检索它

<service id="my-service" class="MyClass">
	<argument type="service" id="countx_launchdarkly.client"/>
</service>

静态访问

还可以静态访问检查标志,虽然这通常不被推荐,但与注入服务和相关配置的样板代码相比,这可能是更可取的,因为这是应该一旦标志值不再改变就删除的临时代码

if (\countX\LaunchDarklyBundle\Client\StaticClient::variation('my-flag', true)) {
    //new feature
}

Twig 扩展

有一个twig函数,允许您从模板中检查标志

{% if isFlagOn("new-template-content") %}
    <p>the new template content</p>
{% else %}
    <p>the old template content</p>
{% endif %}

或传递默认值

{% if isFlagOn("new-template-content", true) %}
    <p>the new template content</p>
{% else %}
    <p>the old template content</p>
{% endif %}

服务配置

您可能更喜欢将替代版本的服务(然后一旦标志始终开启,就删除旧服务)注入代码中的条件语句。

这可以通过定义服务参数的表达式语言函数在定义服务的级别上完成

 countx_launchdarkly.test_service:
        class: OuterService
        arguments: ["@=toggle('new-service-content', 'countx_launchdarkly.new_test_service', 'countx_launchdarkly.old_test_service')"]

其中第一个参数(new-service-content)是标志,第二个(countx_launchdarkly.new_test_service)是如果标志开启则注入的服务,第三个(countx_launchdarkly.old_test_service)是如果标志关闭则使用的服务。

或者,您可以更改服务 ID 指向的服务,类似于别名的工作方式,但由标志值确定。这可以通过使用标签来完成

countx_launchdarkly.test_service:
    class: TestService
    tags:
        - name: countx_launchdarkly.toggle
          flag: new-service-content
          active-id: countx_launchdarkly.new_test_service
          inactive-id: countx_launchdarkly.old_test_service

在这里,countx_launchdarkly.test_service 服务 ID 将是 countx_launchdarkly.new_test_service 的别名如果 new-service-content 标志开启,如果是关闭则是指向 countx_launchdarkly.old_test_service。这允许您在单个地方定义更改,并影响所有使用 countx_launchdarkly.test_service 服务的地方。

一种更短但更巧妙(以捆绑方式处理的方式更巧妙)的替代方法是使用

countx_launchdarkly.test_service:
        alias: "new-aliased-service-content?countx_launchdarkly.new_test_service:countx_launchdarkly.old_test_service"

用户 ID 提供者服务

在请求标志值时使用的用户 ID/密钥由一个需要实现 \countX\LaunchDarklyBundle\User\KeyProvider 接口的服务提供

interface KeyProvider
{
    public function userKey();
}

这应该返回一个表示用户唯一性的字符串值,例如他们的用户名,如果匿名则为其会话 ID。默认实现使用会话 ID。要使用替代服务,请按正常方式创建服务定义,使用您想要的任何 ID,然后将其设置为捆绑配置中 user_key_provider_service 键的值

countx_launch_darkly:
    api_key: MYAPIKEY
    user_key_provider_service: my_user_key_provider_sevice

用户工厂服务

默认情况下,只会将用户 ID 提供者服务返回的 ID/密钥发送到 LaunchDarkly。您可以发送更多允许您微调标志针对哪个用户的信息。这可以通过提供一个替代的用户工厂服务的实现来完成,该实现需要实现 \countX\LaunchDarklyBundle\User\UserFactory 接口

interface UserFactory
{
    public function create($key);
}

这应该返回一个 \LaunchDarkly\LDUser 对象。有一个用于 LDUser 的构建器(\LaunchDarkly\LDUserBuilder),可以用来简化这个过程。然后,该服务需要创建一个定义,通常提供,并将提供的 ID 作为配置中 user_factory_service 键的值

countx_launch_darkly:
    api_key: MYAPIKEY
    user_factory_service: my_user_factory_sevice

例如,如果我们想发送用户的 IP 地址,我们可以创建一个像这样的实现

use countX\LaunchDarklyBundle\User\UserFactory;use LaunchDarkly\LDUserBuilder;

class IPUserFactory implements UserFactory
{
    public function create($key)
    {
        $ip = $_SERVER['REMOTE_ADDR'];
        return (new LDUserBuilder($key))->ip($ip)->build();
    }
    
}

显式传递用户密钥

如果用户不能隐式地从会话(或其他方式)中获取,则可以直接使用

countx_launchdarkly.user_client

服务传递。例如

if ($this->get('countx_launchdarkly.user_client')->variation('my-flag', new LDClient('the-user-id'))) {
    //new feature
}

或者使用替代静态客户端

if (\countX\LaunchDarklyBundle\Client\ExplicitUser\StaticClient::variation('my-flag', new LDClient('the-user-id'))) {
    //new feature
}

使用这些客户端版本时,将不会使用用户 ID 提供者服务和用户工厂服务。

调试工具栏

请求的标志以及它们是在模板中、作为服务还是在代码中请求的都会被捕获并显示在调试工具栏中。

完整配置

完整的配置如下。 base_uritimeoutconnect_timeouteventsdefaults 键都是 LaunchDarkly 客户端的设置。有关详细信息,请参阅 http://docs.launchdarkly.com/docs/php-sdk-reference

# Default configuration for extension with alias: "countx_launch_darkly"
countx_launch_darkly:  # Required
    api_key:              ~ # Required
    base_uri:             ~
    user_factory_service:  countx_launchdarkly.simple_user_factory
    user_key_provider_service:  countx_launchdarkly.session_key_provider
    feature_requester_class:  ~
    timeout:              ~
    connect_timeout:      ~
    capacity:             ~
    events:               ~
    defaults:

        # Prototype
        flag:                 ~

完整客户端

您还可以使用完整客户端服务来访问 LDClient 的其他方法(请参阅 http://docs.launchdarkly.com/docs/php-sdk-reference)。此服务的 ID 为 countx_launchdarkly.inner_client

待办事项

  • 一个与安全用户集成的用户 ID 提供者服务
  • 与路由集成以处理具有不同控制器的路由,这些控制器的控制器取决于标志值
  • 与事件监听器/订阅者集成,允许根据标志值进行注册
  • 与事件监听器/订阅者集成,允许根据标志值进行注册
  • 与安全集成,允许具有角色的东西取决于标志值,如果这样做有实际意义的话