Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support for manager methods #12

Merged
merged 12 commits into from
Sep 19, 2023
7 changes: 6 additions & 1 deletion CHANGES
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
1.2.0 (Sep 19, 2023):
- Add support for Client/GetTreatment(s)WithConfig operations.
- Add support for Manager operations.
- Add default arguments for value & properties in .track()
- Enforce status code validation on rpc responses.

1.1.0 (Sep 6, 2023):
====================
- Fix issue with datetime attributes on php7
- Remove unit tests from autoload
- Add support for .track()
Expand Down
21 changes: 21 additions & 0 deletions examples/manager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

require_once '../vendor/autoload.php';

use \SplitIO\ThinSdk\Factory;

$factory = Factory::withConfig([
'transfer' => [
'address' => '../../splitd/splitd.sock',
'type' => 'unix-stream',
],
'logging' => [
'level' => \Psr\Log\LogLevel::DEBUG,
],
]);

$manager = $factory->manager();
$names = $manager->splitNames();
print_r($names);
var_dump($manager->split($names[0]));
var_dump($manager->splits());
File renamed without changes.
37 changes: 37 additions & 0 deletions examples/treatmentWithConfig.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

require_once '../vendor/autoload.php';

use \SplitIO\ThinSdk\Factory;
use \SplitIO\ThinSdk\Utils\ImpressionListener;
use \SplitIO\ThinSdk\Models\Impression;

class CustomListener implements ImpressionListener
{
public function accept(Impression $i, ?array $a)
{
echo "got an impression for: key=" . $i->getKey()
. " feat=" . $i->getFeature()
. " treatment=" . $i->getTreatment()
. " label=" . $i->getLabel()
. " cn=" . $i->getChangeNumber()
. " #attrs=" . (($a == null) ? 0 : count($a)) . "\n";
}
}

$factory = Factory::withConfig([
'transfer' => [
'address' => '../../splitd/splitd.sock',
'type' => 'unix-stream',
],
'logging' => [
'level' => \Psr\Log\LogLevel::INFO,
],
'utils' => [
'impressionListener' => new CustomListener(),
],
]);

$client = $factory->client();
print_r($client->getTreatmentWithConfig("key", null, "feature1", ['age' => 22]));
print_r($client->getTreatmentsWithConfig("key", null, ["feature1", "feature2"], ['age' => 22]));
43 changes: 39 additions & 4 deletions src/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public function __construct(Manager $manager, LoggerInterface $logger, ?Impressi
$this->inputValidator = new InputValidator($logger);
}

public function getTreatment(string $key, ?string $bucketingKey, string $feature, ?array $attributes): string
public function getTreatment(string $key, ?string $bucketingKey, string $feature, ?array $attributes = null): string
{
try {
list($treatment, $ilData) = $this->lm->getTreatment($key, $bucketingKey, $feature, $attributes);
Expand All @@ -38,7 +38,7 @@ public function getTreatment(string $key, ?string $bucketingKey, string $feature
}
}

public function getTreatments(string $key, ?string $bucketingKey, array $features, ?array $attributes): array
public function getTreatments(string $key, ?string $bucketingKey, array $features, ?array $attributes = null): array
{
try {
$results = $this->lm->getTreatments($key, $bucketingKey, $features, $attributes);
Expand All @@ -51,11 +51,46 @@ public function getTreatments(string $key, ?string $bucketingKey, array $feature
return $toReturn;
} catch (\Exception $exc) {
$this->logger->error($exc);
return array_reduce($features, function ($r, $k) { $r[$k] = "control"; return $r; }, []);
return array_reduce($features, function ($r, $k) {
$r[$k] = "control";
return $r;
}, []);
}
}

public function track(string $key, string $trafficType, string $eventType, ?float $value, ?array $properties): bool
public function getTreatmentWithConfig(string $key, ?string $bucketingKey, string $feature, ?array $attributes = null): array
{
try {
list($treatment, $ilData, $config) = $this->lm->getTreatmentWithConfig($key, $bucketingKey, $feature, $attributes);
$this->handleListener($key, $bucketingKey, $feature, $attributes, $treatment, $ilData);
return ['treatment' => $treatment, 'config' => $config];
} catch (\Exception $exc) {
$this->logger->error($exc);
return "control";
}
}

public function getTreatmentsWithConfig(string $key, ?string $bucketingKey, array $features, ?array $attributes = null): array
{
try {
$results = $this->lm->getTreatmentsWithConfig($key, $bucketingKey, $features, $attributes);
$toReturn = [];
foreach ($results as $feature => $result) {
list($treatment, $ilData, $config) = $result;
$toReturn[$feature] = ['treatment' => $treatment, 'config' => $config];
$this->handleListener($key, $bucketingKey, $feature, $attributes, $treatment, $ilData);
}
return $toReturn;
} catch (\Exception $exc) {
$this->logger->error($exc);
return array_reduce($features, function ($r, $k) {
$r[$k] = ['treatment' => 'control', 'config' => null];
return $r;
}, []);
}
}

public function track(string $key, string $trafficType, string $eventType, ?float $value = null, ?array $properties = null): bool
{
try {
$properties = $this->inputValidator->validProperties($properties);
Expand Down
4 changes: 3 additions & 1 deletion src/ClientInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
interface ClientInterface
{
function getTreatment(string $key, ?string $bucketingKey, string $feature, ?array $attributes): string;
function getTreatmentWithConfig(string $key, ?string $bucketingKey, string $feature, ?array $attributes): array;
function getTreatments(string $key, ?string $bucketingKey, array $features, ?array $attributes): array;
function track(string $key, string $trafficType, string $eventType, ?float $value, ?array $properties): bool;
function getTreatmentsWithConfig(string $key, ?string $bucketingKey, array $features, ?array $attributes): array;
function track(string $key, string $trafficType, string $eventType, ?float $value = null, ?array $properties = null): bool;
}
53 changes: 53 additions & 0 deletions src/Config/Fallback.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

namespace SplitIO\ThinSdk\Config;

use SplitIO\ThinSdk\ClientInterface;
use SplitIO\ThinSdk\ManagerInterface;
use SplitIO\ThinSdk\Fallback\AlwaysControlClient;
use SplitIO\ThinSdk\Fallback\AlwaysEmptyManager;


class Fallback
{
private /*bool*/ $disable;
private /*ClientInterface*/ $customUserClient;
private /*ManagerInterface*/ $customUserManager;

private function __construct(bool $disable, ?ClientInterface $client, ?ManagerInterface $manager)
{
$this->disable = $disable;
$this->customUserClient = $client;
$this->customUserManager = $manager;
}

public function disable(): bool
{
return $this->disable;
}

public function client(): ?ClientInterface
{
return $this->customUserClient;
}

public function manager(): ?ManagerInterface
{
return $this->customUserManager;
}

public static function fromArray(array $config): Fallback
{
$d = self::default();
return new Fallback(
$config['disable'] ?? $d->disable(),
$config['client'] ?? $d->client(),
$config['manager'] ?? $d->manager()
);
}

public static function default(): Fallback
{
return new Fallback(false, new AlwaysControlClient(), new AlwaysEmptyManager());
}
}
11 changes: 10 additions & 1 deletion src/Config/Main.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ class Main
private /*Transfer*/ $transfer;
private /*Serialization*/ $serialization;
private /*Logging*/ $logging;
private /*Fallback*/ $fallback;
private /*Utils*/ $utils;

private function __construct(Transfer $transfer, Serialization $serialization, Logging $logging, Utils $utils)
private function __construct(Transfer $transfer, Serialization $serialization, Logging $logging, Fallback $fallback, Utils $utils)
{
$this->transfer = $transfer;
$this->serialization = $serialization;
$this->logging = $logging;
$this->fallback = $fallback;
$this->utils = $utils;
}

Expand All @@ -32,6 +34,11 @@ public function logging(): Logging
return $this->logging;
}

public function fallback(): Fallback
{
return $this->fallback;
}

public function utils(): Utils
{
return $this->utils;
Expand All @@ -43,6 +50,7 @@ public static function fromArray(array $config): Main
Transfer::fromArray($config['transfer'] ?? []),
Serialization::fromArray($config['serialization'] ?? []),
Logging::fromArray($config['logging'] ?? []),
Fallback::fromArray($config['fallback'] ?? []),
Utils::fromArray($config['utils'] ?? []),
);
}
Expand All @@ -53,6 +61,7 @@ public static function default(): Main
Transfer::default(),
Serialization::default(),
Logging::default(),
Fallback::default(),
Utils::default(),
);
}
Expand Down
33 changes: 31 additions & 2 deletions src/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,42 @@ public static function default(): Factory
return new Factory(Config\Main::default());
}

public static function withConfig(array $config): Factory
public static function withConfig(array $config): FactoryInterface
{
return new Factory(Config\Main::fromArray($config));
try {
return new Factory(Config\Main::fromArray($config));
} catch (\Exception $e) {

try {
$parsedConfig = Config\Main::fromArray($config);
if ($parsedConfig->fallback()->disable()) { // fallback disabled, re-throw
throw new Fallback\FallbackDisabledException($e);
}

$logger = Helpers::getLogger($parsedConfig->logging());
$logger->error(sprintf("error instantiating a factory with supplied config (%s). will return a fallback.", $e->getMessage()));
$logger->debug($e);
return new Fallback\GenericFallbackFactory($parsedConfig->fallback()->client(), $parsedConfig->fallback()->manager());
} catch (Fallback\FallbackDisabledException $e) {
// client wants to handle exception himself. re-throw it;
throw $e->wrapped();
} catch (\Exception $e) {
// This branch is virtually unreachable (hence untestable) unless we introduce a bug.
// it's basically a safeguard to prevent the customer app from crashing if we do.
$logger = Helpers::getLogger(Config\Logging::default());
$logger->error(sprintf("error parsing supplied factory config config (%s). will return a fallback.", $e->getMessage()));
return new Fallback\GenericFallbackFactory(new Fallback\AlwaysControlClient(), new Fallback\AlwaysEmptyManager());
}
}
}

public function client(): ClientInterface
{
return new Client($this->linkManager, $this->logger, $this->config->utils()->impressionListener());
}

public function manager(): ManagerInterface
{
return new Manager($this->linkManager, $this->logger);
}
};
1 change: 1 addition & 0 deletions src/FactoryInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@
interface FactoryInterface
{
public function client(): ClientInterface;
public function manager(): ManagerInterface;
};
39 changes: 39 additions & 0 deletions src/Fallback/AlwaysControlClient.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

namespace SplitIO\ThinSdk\Fallback;

use SplitIO\ThinSdk\ClientInterface;

class AlwaysControlClient implements ClientInterface
{
public function getTreatment(string $key, ?string $bucketingKey, string $feature, ?array $attributes): string
{
return "control";
}

public function getTreatmentWithConfig(string $key, ?string $bucketingKey, string $feature, ?array $attributes): array
{
return ['treatment' => 'control', 'config' => null];
}

public function getTreatments(string $key, ?string $bucketingKey, array $features, ?array $attributes): array
{
return array_reduce($features, function ($carry, $item) {
$carry[$item] = "control";
return $carry;
}, []);
}

public function getTreatmentsWithConfig(string $key, ?string $bucketingKey, array $features, ?array $attributes): array
{
return array_reduce($features, function ($carry, $item) {
$carry[$item] = ['treatment' => 'control', 'config' => null];
return $carry;
}, []);
}

public function track(string $key, string $trafficType, string $eventType, ?float $value = null, ?array $properties = null): bool
{
return false;
}
}
24 changes: 24 additions & 0 deletions src/Fallback/AlwaysEmptyManager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace SplitIO\ThinSdk\Fallback;

use SplitIO\ThinSdk\ManagerInterface;
use SplitIO\ThinSdk\SplitView;

class AlwaysEmptyManager implements ManagerInterface
{
public function splitNames(): array
{
return [];
}

public function split(string $name): ?SplitView
{
return null;
}

public function splits(): array
{
return [];
}
}
19 changes: 19 additions & 0 deletions src/Fallback/FallbackDisabledException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

namespace SplitIO\ThinSdk\Fallback;

class FallbackDisabledException extends \Exception
{

private /*\Exception*/ $wrapped;

public function __construct(\Exception $wrapped)
{
$this->wrapped = $wrapped;
}

public function wrapped(): \Exception
{
return $this->wrapped;
}
}
Loading