countxvat / launchdarkly-bundle
为 Symfony 集成的 LaunchDarkly
Requires
- launchdarkly/launchdarkly-php: ^4.3 | ^5.1
- symfony/dependency-injection: ^4.2 | ^5.2 | ^6.0
- symfony/expression-language: ^4.2 | ^5.2 | ^6.0
Requires (Dev)
README
简介
这是一个用于集成 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_uri
、timeout
、connect_timeout
、events
和 defaults
键都是 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 提供者服务
- 与路由集成以处理具有不同控制器的路由,这些控制器的控制器取决于标志值
- 与事件监听器/订阅者集成,允许根据标志值进行注册
- 与事件监听器/订阅者集成,允许根据标志值进行注册
- 与安全集成,允许具有角色的东西取决于标志值,如果这样做有实际意义的话