eppo/php-sdk


README

Eppo 是一个模块化的标记和实验分析工具。Eppo 的 PHP SDK 用于在多用户服务器端环境中进行分配,兼容 PHP 7.3 及以上版本。在继续之前,您需要一个 Eppo 账户。

功能

  • 功能门
  • 开关
  • 渐进式发布
  • A/B/n 实验
  • 互斥实验(层)
  • 动态配置
  • 多臂上下文随机游走

安装

composer require eppo/php-sdk

快速开始

首先初始化 Eppo 客户端的单例实例。初始化后,客户端可以在您的应用程序的任何位置使用。

初始化一次

<?php

use Eppo\EppoClient;

require __DIR__ . '/vendor/autoload.php';

$eppoClient = EppoClient::init(
   '<your_api_key>',
   '<base_url>', // optional, default https://fscdn.eppo.cloud/api
   $assignmentLogger, // optional, must be an instance of Eppo\Logger\LoggerInterface
   $cache // optional, must be an instance of PSR-16 SimpleCache\CacheInterface. If not passed, FileSystem cache will be used
   $httpClient // optional, must be an instance of PSR-18 ClientInterface. If not passed, Discovery will be used to find a suitable implementation
   $requestFactory // optional, must be an instance of PSR-17 Factory. If not passed, Discovery will be used to find a suitable implementation
);

在任何地方分配

$subjectAttributes = [ 'tier' => 2 ];
$assignment = $eppoClient->getStringAssignment('experimentalBackground', 'user123', $subjectAttributes, 'defaultValue');

if ($assignment !== 'defaultValue') {
    // do something
}

选择 Bandit 行动

此 SDK 支持 多臂上下文随机游走

$subjectContext = [
    'age' => 30, // Gets interpreted as a Numeric Attribute
    'country' => 'uk', // Categorical Attribute
    'pricingTier' => '1'  // NOTE: Deliberately setting to string causes this to be treated as a Categorical Attribute
];

$actionContexts = [
    'nike' => [
        'brandLoyalty' => 0.4,
        'from' => 'usa'
    ],
    'adidas' => [
        'brandLoyalty' => 2,
        'from' => 'germany'
    ]
];

$result = $client->getBanditAction(
    'flagKey',
    'subjectKey',
    $subjectContext,
    $actionContexts,
    'defaultValue'
);

if ($result->action != null) {
    // Follow the Bandit action
    doAction($result->action);
} else {
    doSomething($result->variation);
}

分配函数

每个 Eppo 标记在仪表板上创建时都会设置一次返回类型。创建标记后,应使用相应的类型函数在代码中进行分配

getBooleanAssignment(...)
getNumericAssignment(...)
getIntegerAssignment(...)
getStringAssignment(...)
getJSONAssignment(...)

每个函数都有相同的签名,但函数名中返回类型不同。对于布尔值使用 getBooleanAssignment,其签名如下

function getBooleanAssignment(
    string $flagKey,
    string $subjectKey,
    array $subjectAttributes,
    bool $defaultValue
): bool

初始化选项

init 函数接受以下可选配置参数。

分配记录器

要使用 Eppo SDK 进行需要分析的实验,请在 SDK 初始化时将 LoggerInterface 的实现传递给 init 函数。SDK 会调用回调以捕获分配数据。分配数据需要在仓库中进行分析。

下面的代码演示了使用 Segment 的日志回调示例实现,但您可以使用任何系统。唯一要求是 SDK 收到 logAssignment 回调函数。我们定义了一个 Eppo AssignmentLogger 接口的实现,其中包含一个名为 logAssignment 的单个函数

<?php

use Eppo\Logger\LoggerInterface;


use Eppo\Logger\AssignmentEvent;
use Eppo\Logger\LoggerInterface;

class SegmentLogger implements LoggerInterface
{
    public function logAssignment(AssignmentEvent $assignmentEvent): void
    {
        Segment::track([
            'event' => 'Flag Assignment for ' . $assignmentEvent->featureFlag,
            'userId' => $assignmentEvent->subject,
            'properties' => $assignmentEvent->toArray()
        ]);
    }
}

Bandit 行动日志记录

当使用 Bandits 时,会调用不同的日志记录方法。您的日志类必须实现 IBanditLogger 而不是 LoggerInterface

<?php

use Eppo\Logger\AssignmentEvent;
use Eppo\Logger\BanditActionEvent;
use Eppo\Logger\IBanditLogger;

class SegmentLogger implements IBanditLogger
{
    public function logAssignment(AssignmentEvent $assignmentEvent): void
    {
        Segment::track([
            'event' => 'Flag Assignment for ' . $assignmentEvent->featureFlag,
            'userId' => $assignmentEvent->subject,
            'properties' => $assignmentEvent->toArray()
        ]);
    }

    public function logBanditAction(BanditActionEvent $banditActionEvent): void
    {
        Segment::track([
            'event' => 'Bandit Action Selected',
            'userId' => $banditActionEvent->subjectKey,
            'properties' => $banditActionEvent->toArray()
        ]);
    }
}

后台轮询

为了使使用库的体验更快,有一个选项可以启动后台轮询以获取随机化参数。此后台作业将开始调用 Eppo API,更新缓存中的配置。

为此,创建一个文件,例如 eppo-poller.php,其内容如下

$eppoClient = EppoClient::init(
   '<your_api_key>',
   '<base_url>', // optional, default https://fscdn.eppo.cloud/api
   $assignmentLogger, // optional, must be an instance of Eppo\LoggerInterface
   $cache // optional, must be an instance of PSR-16 SimpleInterface. If not passed, FileSystem cache will be used
   $httpClient // optional, must be an instance of PSR-18 ClientInterface. If not passed, Discovery will be used to find a suitable implementation
   $requestFactory // optional, must be an instance of PSR-17 Factory. If not passed, Discovery will be used to find a suitable implementation
);

$eppoClient->startPolling();

然后,通过以下方式运行此脚本

php eppo-poller.php

这将开始无限期轮询 Eppo-api 的过程。

故障排除

HTTP

此软件包使用 php-http/discovery 软件包来自动定位各种 HTTP 相关 PSR 接口的实现(例如:ClientInterfaceRequstFactory 等)。如果您的项目不依赖于任何可以满足此需求的库,您可能会看到以下异常。

致命错误:未捕获的 Http\Discovery\Exception\DiscoveryFailedException:无法使用任何发现策略找到资源。

要解决这个问题,只需引入一个合适的包,例如 guzzle

composer require guzzlehttp/guzzle:^7.0

哲学

易博的SDKs是为了简洁、速度和可靠性而构建的。标志配置被压缩并分布在全球CDN(Fastly)上,通常在15毫秒内到达您的服务器。服务器SDKs继续以30秒间隔轮询易博的API。配置随后在本地缓存,确保每次分配都是瞬间的。每个SDK内的评估逻辑由几行简单的数值和字符串比较组成。上面列出的类型化函数是开发者需要理解的全部,它抽象掉了易博底层(并且不断扩展)的功能集的复杂性。