From dca6c022271159d5022dd105912b40bddc49ac96 Mon Sep 17 00:00:00 2001 From: tyiuhc Date: Thu, 28 Dec 2023 15:21:09 +0800 Subject: [PATCH] Update to use PHPStan --- .github/workflows/test.yml | 3 + composer.json | 2 +- composer.lock | 44 +++++------ phpstan.neon | 11 +++ src/Amplitude/Amplitude.php | 20 +++-- src/Amplitude/AmplitudeConfig.php | 8 +- src/Amplitude/AmplitudeConfigBuilder.php | 8 +- src/Amplitude/Event.php | 9 +++ src/AmplitudeCookie.php | 20 ++--- src/Assignment/Assignment.php | 9 ++- src/Assignment/AssignmentConfigBuilder.php | 5 +- src/Assignment/LRUCache.php | 83 ++++++++++++++------ src/Experiment.php | 6 ++ src/Flag/FlagConfigFetcher.php | 5 ++ src/Flag/FlagConfigService.php | 14 +++- src/Http/FetchClientInterface.php | 3 +- src/Http/GuzzleFetchClient.php | 8 +- src/Local/LocalEvaluationClient.php | 11 +-- src/Local/LocalEvaluationConfig.php | 10 ++- src/Local/LocalEvaluationConfigBuilder.php | 12 +++ src/Logger/DefaultLogger.php | 3 + src/Logger/InternalLogger.php | 2 +- src/Remote/RemoteEvaluationClient.php | 24 ++++-- src/Remote/RemoteEvaluationConfig.php | 4 + src/Remote/RemoteEvaluationConfigBuilder.php | 7 ++ src/User.php | 20 +++++ src/UserBuilder.php | 18 +++++ src/Variant.php | 9 +++ 28 files changed, 292 insertions(+), 86 deletions(-) create mode 100644 phpstan.neon diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 197f2f6..a0e3dd6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -40,3 +40,6 @@ jobs: - name: Run test suite run: composer run-script test + + - name: Run PHPStan + run: vendor/bin/phpstan analyse --ansi diff --git a/composer.json b/composer.json index e62c57d..e5a7f6f 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,7 @@ }, "require-dev": { "phpunit/phpunit": "9.*", - "phpstan/phpstan": "^1.0" + "phpstan/phpstan": "^1" }, "suggest": { "monolog/monolog": "Allows more advanced logging of the application flow" diff --git a/composer.lock b/composer.lock index 16045f8..88271da 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "48eed85ab7c8388545faf2d591ce9d86", + "content-hash": "a734945f7a1c1cc01371bb797325c3da", "packages": [ { "name": "guzzlehttp/guzzle", @@ -1014,23 +1014,23 @@ }, { "name": "phpunit/php-code-coverage", - "version": "9.2.29", + "version": "9.2.30", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76" + "reference": "ca2bd87d2f9215904682a9cb9bb37dda98e76089" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6a3a87ac2bbe33b25042753df8195ba4aa534c76", - "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ca2bd87d2f9215904682a9cb9bb37dda98e76089", + "reference": "ca2bd87d2f9215904682a9cb9bb37dda98e76089", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.15", + "nikic/php-parser": "^4.18 || ^5.0", "php": ">=7.3", "phpunit/php-file-iterator": "^3.0.3", "phpunit/php-text-template": "^2.0.2", @@ -1080,7 +1080,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.29" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.30" }, "funding": [ { @@ -1088,7 +1088,7 @@ "type": "github" } ], - "time": "2023-09-19T04:57:46+00:00" + "time": "2023-12-22T06:47:57+00:00" }, { "name": "phpunit/php-file-iterator", @@ -1677,20 +1677,20 @@ }, { "name": "sebastian/complexity", - "version": "2.0.2", + "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a", + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a", "shasum": "" }, "require": { - "nikic/php-parser": "^4.7", + "nikic/php-parser": "^4.18 || ^5.0", "php": ">=7.3" }, "require-dev": { @@ -1722,7 +1722,7 @@ "homepage": "https://github.com/sebastianbergmann/complexity", "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", - "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3" }, "funding": [ { @@ -1730,7 +1730,7 @@ "type": "github" } ], - "time": "2020-10-26T15:52:27+00:00" + "time": "2023-12-22T06:19:30+00:00" }, { "name": "sebastian/diff", @@ -2004,20 +2004,20 @@ }, { "name": "sebastian/lines-of-code", - "version": "1.0.3", + "version": "1.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5", "shasum": "" }, "require": { - "nikic/php-parser": "^4.6", + "nikic/php-parser": "^4.18 || ^5.0", "php": ">=7.3" }, "require-dev": { @@ -2049,7 +2049,7 @@ "homepage": "https://github.com/sebastianbergmann/lines-of-code", "support": { "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4" }, "funding": [ { @@ -2057,7 +2057,7 @@ "type": "github" } ], - "time": "2020-11-28T06:42:11+00:00" + "time": "2023-12-22T06:20:34+00:00" }, { "name": "sebastian/object-enumerator", diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..d4b6f20 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,11 @@ +parameters: + level: 8 + paths: + - src + + excludePaths: + - src/Assignment/AssignmentConfigBuilder.php + - src/EvaluationCore + + scanFiles: + - src/EvaluationCore/Util.php diff --git a/src/Amplitude/Amplitude.php b/src/Amplitude/Amplitude.php index 6d97112..b79dd4d 100644 --- a/src/Amplitude/Amplitude.php +++ b/src/Amplitude/Amplitude.php @@ -13,26 +13,29 @@ class Amplitude { private string $apiKey; + /** + * @var array> + */ protected array $queue = []; protected FetchClientInterface $httpClient; private LoggerInterface $logger; - private ?AmplitudeConfig $config; + private AmplitudeConfig $config; public function __construct(string $apiKey, LoggerInterface $logger, AmplitudeConfig $config = null) { $this->apiKey = $apiKey; $this->logger = $logger; $this->config = $config ?? AmplitudeConfig::builder()->build(); - $this->httpClient = $config->fetchClient ?? $this->config->fetchClient ?? new GuzzleFetchClient($this->config->guzzleClientConfig); + $this->httpClient = $this->config->fetchClient ?? $this->config->fetchClient ?? new GuzzleFetchClient($this->config->guzzleClientConfig); } - public function flush() + public function flush(): void { $payload = ["api_key" => $this->apiKey, "events" => $this->queue, "options" => ["min_id_length" => $this->config->minIdLength]]; $this->post($this->config->serverUrl, $payload); } - public function logEvent(Event $event) + public function logEvent(Event $event): void { $this->queue[] = $event->toArray(); if (count($this->queue) >= $this->config->flushQueueSize) { @@ -50,10 +53,17 @@ public function __destruct() } } - private function post(string $url, array $payload) + /** + * @param array $payload + */ + private function post(string $url, array $payload): void { $fetchClient = $this->httpClient->getClient(); $payloadJson = json_encode($payload); + if ($payloadJson === false) { + $this->logger->error('[Amplitude] Failed to encode payload: ' . json_last_error()); + return; + } $request = $this->httpClient->createRequest('POST', $url)->withHeader('json', $payloadJson); try { $response = $fetchClient->sendRequest($request); diff --git a/src/Amplitude/AmplitudeConfig.php b/src/Amplitude/AmplitudeConfig.php index 899204f..3587eae 100644 --- a/src/Amplitude/AmplitudeConfig.php +++ b/src/Amplitude/AmplitudeConfig.php @@ -32,8 +32,11 @@ class AmplitudeConfig /** * True to use batch API endpoint, False to use HTTP V2 API endpoint. */ - public string $useBatch; + public bool $useBatch; public ?FetchClientInterface $fetchClient; + /** + * @var array + */ public array $guzzleClientConfig; const DEFAULTS = [ @@ -56,6 +59,9 @@ class AmplitudeConfig 'guzzleClientConfig' => [] ]; + /** + * @param array $guzzleClientConfig + */ public function __construct( int $flushQueueSize, int $minIdLength, diff --git a/src/Amplitude/AmplitudeConfigBuilder.php b/src/Amplitude/AmplitudeConfigBuilder.php index 8ec55ab..d3cd487 100644 --- a/src/Amplitude/AmplitudeConfigBuilder.php +++ b/src/Amplitude/AmplitudeConfigBuilder.php @@ -12,6 +12,9 @@ class AmplitudeConfigBuilder protected ?string $serverUrl = null; protected bool $useBatch = AmplitudeConfig::DEFAULTS['useBatch']; protected ?FetchClientInterface $fetchClient = AmplitudeConfig::DEFAULTS['fetchClient']; + /** + * @var array + */ protected array $guzzleClientConfig = AmplitudeConfig::DEFAULTS['guzzleClientConfig']; public function __construct() @@ -54,13 +57,16 @@ public function fetchClient(FetchClientInterface $fetchClient): AmplitudeConfigB return $this; } + /** + * @param array $guzzleClientConfig + */ public function guzzleClientConfig(array $guzzleClientConfig): AmplitudeConfigBuilder { $this->guzzleClientConfig = $guzzleClientConfig; return $this; } - public function build() + public function build(): AmplitudeConfig { if (!$this->serverUrl) { if ($this->useBatch) { diff --git a/src/Amplitude/Event.php b/src/Amplitude/Event.php index d2615a6..a2865ac 100644 --- a/src/Amplitude/Event.php +++ b/src/Amplitude/Event.php @@ -5,7 +5,13 @@ class Event { public ?string $eventType = null; + /** + * @var ?array + */ public ?array $eventProperties = null; + /** + * @var ?array + */ public ?array $userProperties = null; public ?string $userId = null; public ?string $deviceId = null; @@ -16,6 +22,9 @@ public function __construct(string $eventType) $this->eventType = $eventType; } + /** + * @return array + */ public function toArray(): array { return array_filter([ diff --git a/src/AmplitudeCookie.php b/src/AmplitudeCookie.php index b237179..b8ed0d4 100644 --- a/src/AmplitudeCookie.php +++ b/src/AmplitudeCookie.php @@ -37,7 +37,7 @@ public static function cookieName(string $amplitudeApiKey, bool $newFormat = fal /** * @param string $amplitudeCookie A string from the amplitude cookie * @param bool $newFormat True if the cookie is in the Browser SDK 2.0 format - * @return array An array containing device_id and user_id (if available) + * @return array An array containing device_id and user_id (if available) */ public static function parse(string $amplitudeCookie, bool $newFormat = false): array { @@ -45,17 +45,16 @@ public static function parse(string $amplitudeCookie, bool $newFormat = false): $decoding = base64_decode($amplitudeCookie); $decoded = urldecode($decoding); - try { - $userSession = json_decode($decoded, true); - return [ - 'deviceId' => $userSession['deviceId'], - 'userId' => $userSession['userId'] ?? null, - ]; - } catch (\Exception $e) { + $userSession = json_decode($decoded, true); + if ($userSession === null) { $logger = new InternalLogger(new DefaultLogger(), LogLevel::INFO); - $logger->error("Error parsing the Amplitude cookie: '{$amplitudeCookie}'. " . $e->getMessage()); + $logger->error("Error parsing the Amplitude cookie: '{$amplitudeCookie}'."); return []; } + return [ + 'deviceId' => $userSession['deviceId'], + 'userId' => $userSession['userId'] ?? null, + ]; } $values = explode('.', $amplitudeCookie); @@ -92,6 +91,9 @@ public static function generate(string $deviceId, bool $newFormat = false): stri ]; $json_data = json_encode($userSessionHash); + if ($json_data === false) { + return ''; + } $encoded_json = urlencode($json_data); return base64_encode($encoded_json); } diff --git a/src/Assignment/Assignment.php b/src/Assignment/Assignment.php index 42b4844..69baed3 100644 --- a/src/Assignment/Assignment.php +++ b/src/Assignment/Assignment.php @@ -3,18 +3,25 @@ namespace AmplitudeExperiment\Assignment; use AmplitudeExperiment\User; +use AmplitudeExperiment\Variant; class Assignment { public User $user; + /** + * @var array + */ public array $variants; public int $timestamp; + /** + * @param array $variants + */ public function __construct(User $user, array $variants) { $this->user = $user; $this->variants = $variants; - $this->timestamp = floor(microtime(true) * 1000); + $this->timestamp = (int) floor(microtime(true) * 1000); } public function canonicalize(): string diff --git a/src/Assignment/AssignmentConfigBuilder.php b/src/Assignment/AssignmentConfigBuilder.php index fe353c6..6084718 100644 --- a/src/Assignment/AssignmentConfigBuilder.php +++ b/src/Assignment/AssignmentConfigBuilder.php @@ -2,16 +2,17 @@ namespace AmplitudeExperiment\Assignment; +use AmplitudeExperiment\Amplitude\AmplitudeConfig; use AmplitudeExperiment\Amplitude\AmplitudeConfigBuilder; /** * Extends AmplitudeConfigBuilder to allow configuration {@link AmplitudeConfig} of underlying {@link Amplitude} client. */ - class AssignmentConfigBuilder extends AmplitudeConfigBuilder { protected string $apiKey; protected int $cacheCapacity = AssignmentConfig::DEFAULTS['cacheCapacity']; + public function __construct(string $apiKey) { parent::__construct(); @@ -24,7 +25,7 @@ public function cacheCapacity(int $cacheCapacity): AssignmentConfigBuilder return $this; } - public function build(): AssignmentConfig + public function build() { return new AssignmentConfig( $this->apiKey, diff --git a/src/Assignment/LRUCache.php b/src/Assignment/LRUCache.php index 87e7e3a..7a6d2e9 100644 --- a/src/Assignment/LRUCache.php +++ b/src/Assignment/LRUCache.php @@ -2,38 +2,59 @@ namespace AmplitudeExperiment\Assignment; -class ListNode { - public $prev; - public $next; +class ListNode +{ + public ?ListNode $prev = null; + public ?ListNode $next = null; + /** + * @var mixed + */ public $data; - public function __construct($data) { + /** + * @param mixed $data + */ + public function __construct($data) + { $this->prev = null; $this->next = null; $this->data = $data; } } -class CacheItem { - public $key; +class CacheItem +{ + public string $key; + /** + * @var mixed + */ public $value; - public $createdAt; + public int $createdAt; - public function __construct($key, $value) { + /** + * @param mixed $value + */ + public function __construct(string $key, $value) + { $this->key = $key; $this->value = $value; - $this->createdAt = floor(microtime(true) * 1000); + $this->createdAt = (int) floor(microtime(true) * 1000); } } -class LRUCache { - private $capacity; - private $ttlMillis; - private $cache; - private $head; - private $tail; - - public function __construct($capacity, $ttlMillis) { +class LRUCache +{ + private int $capacity; + private int $ttlMillis; + /** + * @var array + */ + private array $cache; + private ?ListNode $head = null; + private ?ListNode $tail = null; + + public function __construct(int $capacity, int $ttlMillis) + { $this->capacity = $capacity; $this->ttlMillis = $ttlMillis; $this->cache = []; @@ -41,7 +62,11 @@ public function __construct($capacity, $ttlMillis) { $this->tail = null; } - public function put($key, $value): void { + /** + * @param mixed $value + */ + public function put(string $key, $value): void + { if (isset($this->cache[$key])) { $this->removeFromList($key); } elseif (count($this->cache) >= $this->capacity) { @@ -54,7 +79,11 @@ public function put($key, $value): void { $this->insertToList($node); } - public function get($key) { + /** + * @return mixed + */ + public function get(string $key) + { if (isset($this->cache[$key])) { $node = $this->cache[$key]; $timeElapsed = floor(microtime(true) * 1000) - $node->data->createdAt; @@ -72,24 +101,28 @@ public function get($key) { return null; } - public function remove($key): void { + public function remove(string $key): void + { $this->removeFromList($key); unset($this->cache[$key]); } - public function clear(): void { + public function clear(): void + { $this->cache = []; $this->head = null; $this->tail = null; } - private function evictLRU(): void { + private function evictLRU(): void + { if ($this->head) { $this->remove($this->head->data->key); } } - private function removeFromList($key): void { + private function removeFromList(string $key): void + { $node = $this->cache[$key]; if ($node->prev) { @@ -105,7 +138,8 @@ private function removeFromList($key): void { } } - private function insertToList($node): void { + private function insertToList(ListNode $node): void + { if ($this->tail) { $this->tail->next = $node; $node->prev = $this->tail; @@ -117,4 +151,3 @@ private function insertToList($node): void { } } } - diff --git a/src/Experiment.php b/src/Experiment.php index e9f4347..2fc5424 100644 --- a/src/Experiment.php +++ b/src/Experiment.php @@ -9,7 +9,13 @@ class Experiment { + /** + * @var array + */ private array $remoteInstances = []; + /** + * @var array + */ private array $localInstances = []; /** diff --git a/src/Flag/FlagConfigFetcher.php b/src/Flag/FlagConfigFetcher.php index 03511aa..b724474 100644 --- a/src/Flag/FlagConfigFetcher.php +++ b/src/Flag/FlagConfigFetcher.php @@ -28,6 +28,7 @@ public function __construct(string $apiKey, LoggerInterface $logger, FetchClient * Fetch local evaluation mode flag configs from the Experiment API server. * These flag configs can be used to perform local evaluation. * + * @return array> The flag configs * @throws ClientExceptionInterface */ public function fetch(): array @@ -51,6 +52,10 @@ public function fetch(): array } + /** + * @param array> $flagConfigs + * @return array> + */ private function parse(array $flagConfigs): array { $flagConfigsRecord = []; diff --git a/src/Flag/FlagConfigService.php b/src/Flag/FlagConfigService.php index 8ee4a42..cb087ba 100644 --- a/src/Flag/FlagConfigService.php +++ b/src/Flag/FlagConfigService.php @@ -9,8 +9,15 @@ class FlagConfigService { private LoggerInterface $logger; public FlagConfigFetcher $fetcher; + + /** + * @var array + */ public array $cache; + /** + * @param array $bootstrap + */ public function __construct(FlagConfigFetcher $fetcher, LoggerInterface $logger, array $bootstrap) { $this->fetcher = $fetcher; @@ -18,7 +25,7 @@ public function __construct(FlagConfigFetcher $fetcher, LoggerInterface $logger, $this->cache = $bootstrap; } - public function start() + public function start(): void { $this->logger->debug('[Experiment] Flag service - start'); @@ -26,7 +33,7 @@ public function start() $this->refresh(); } - private function refresh() + private function refresh(): void { $this->logger->debug('[Experiment] Flag config update'); try { @@ -37,6 +44,9 @@ private function refresh() } } + /** + * @return array + */ public function getFlagConfigs(): array { return $this->cache; diff --git a/src/Http/FetchClientInterface.php b/src/Http/FetchClientInterface.php index 908e2e0..4541400 100644 --- a/src/Http/FetchClientInterface.php +++ b/src/Http/FetchClientInterface.php @@ -4,9 +4,10 @@ use Psr\Http\Client\ClientInterface; +use Psr\Http\Message\RequestInterface; interface FetchClientInterface { public function getClient(): ClientInterface; - public function createRequest(string $method, string $uri); + public function createRequest(string $method, string $uri) : RequestInterface; } diff --git a/src/Http/GuzzleFetchClient.php b/src/Http/GuzzleFetchClient.php index 2eaad3a..bc3095c 100644 --- a/src/Http/GuzzleFetchClient.php +++ b/src/Http/GuzzleFetchClient.php @@ -41,8 +41,14 @@ class GuzzleFetchClient implements FetchClientInterface { private Client $client; + /** + * @var array + */ private array $config; + /** + * @param array $config + */ public function __construct(array $config) { $handlerStack = HandlerStack::create(); @@ -75,7 +81,7 @@ public function createRequest(string $method, string $uri): Request } - protected function calculateDelayMillis($iteration): int + protected function calculateDelayMillis(int $iteration): int { $delayMillis = $this->config['fetchRetryBackoffMinMillis']; diff --git a/src/Local/LocalEvaluationClient.php b/src/Local/LocalEvaluationClient.php index ba8d2dc..4c031a5 100644 --- a/src/Local/LocalEvaluationClient.php +++ b/src/Local/LocalEvaluationClient.php @@ -14,6 +14,7 @@ use AmplitudeExperiment\Logger\DefaultLogger; use AmplitudeExperiment\Logger\InternalLogger; use AmplitudeExperiment\User; +use AmplitudeExperiment\Variant; use Psr\Log\LoggerInterface; use function AmplitudeExperiment\EvaluationCore\topologicalSort; @@ -38,14 +39,14 @@ public function __construct(string $apiKey, ?LocalEvaluationConfig $config = nul $httpClient = $config->fetchClient ?? $this->config->fetchClient ?? new GuzzleFetchClient($this->config->guzzleClientConfig); $fetcher = new FlagConfigFetcher($apiKey, $this->logger, $httpClient, $this->config->serverUrl); $this->flagConfigService = new FlagConfigService($fetcher, $this->logger, $this->config->bootstrap); - $this->initializeAssignmentService($config->assignmentConfig); + $this->initializeAssignmentService($this->config->assignmentConfig); $this->evaluation = new EvaluationEngine(); } /** * Fetch initial flag configurations. */ - public function start() + public function start(): void { $this->flagConfigService->start(); } @@ -57,10 +58,10 @@ public function start() * flagKeys argument. If flagKeys is missing or empty, all flags in the * {@link FlagConfigService} will be evaluated. * - * @param $user User The user to evaluate - * @param $flagKeys array The flags to evaluate with the user. If empty, all flags + * @param User $user The user to evaluate + * @param array $flagKeys The flags to evaluate with the user. If empty, all flags * from the flag cache are evaluated. - * @returns array evaluated variants + * @return array evaluated variants */ public function evaluate(User $user, array $flagKeys = []): array { diff --git a/src/Local/LocalEvaluationConfig.php b/src/Local/LocalEvaluationConfig.php index 85a15d7..b23bf07 100644 --- a/src/Local/LocalEvaluationConfig.php +++ b/src/Local/LocalEvaluationConfig.php @@ -22,8 +22,8 @@ class LocalEvaluationConfig */ public string $serverUrl; /** + * @var array * Bootstrap the client with a pre-fetched flag configurations. - * * Useful if you are managing the flag configurations separately. */ public array $bootstrap; @@ -33,6 +33,7 @@ class LocalEvaluationConfig */ public ?FetchClientInterface $fetchClient; /** + * @var array * The configuration for the underlying default Guzzle client. */ public array $guzzleClientConfig; @@ -47,7 +48,12 @@ class LocalEvaluationConfig 'guzzleClientConfig' => [] ]; - public function __construct(?LoggerInterface $logger, int $logLevel, string $serverUrl, array $bootstrap, ?AssignmentConfig $assignmentConfig, ?FetchClientInterface $fetchClient, array $guzzleClientConfig){ + /** + * @param array $guzzleClientConfig + * @param array $bootstrap + */ + public function __construct(?LoggerInterface $logger, int $logLevel, string $serverUrl, array $bootstrap, ?AssignmentConfig $assignmentConfig, ?FetchClientInterface $fetchClient, array $guzzleClientConfig) + { $this->logger = $logger; $this->logLevel = $logLevel; $this->serverUrl = $serverUrl; diff --git a/src/Local/LocalEvaluationConfigBuilder.php b/src/Local/LocalEvaluationConfigBuilder.php index 64656a9..901e616 100644 --- a/src/Local/LocalEvaluationConfigBuilder.php +++ b/src/Local/LocalEvaluationConfigBuilder.php @@ -11,9 +11,15 @@ class LocalEvaluationConfigBuilder protected ?LoggerInterface $logger = LocalEvaluationConfig::DEFAULTS['logger']; protected int $logLevel = LocalEvaluationConfig::DEFAULTS['logLevel']; protected string $serverUrl = LocalEvaluationConfig::DEFAULTS['serverUrl']; + /** + * @var array + */ protected array $bootstrap = LocalEvaluationConfig::DEFAULTS['bootstrap']; protected ?AssignmentConfig $assignmentConfig = LocalEvaluationConfig::DEFAULTS['assignmentConfig']; protected ?FetchClientInterface $fetchClient = LocalEvaluationConfig::DEFAULTS['fetchClient']; + /** + * @var array + */ protected array $guzzleClientConfig = LocalEvaluationConfig::DEFAULTS['guzzleClientConfig']; public function __construct() @@ -38,6 +44,9 @@ public function serverUrl(string $serverUrl): LocalEvaluationConfigBuilder return $this; } + /** + * @param array $bootstrap + */ public function bootstrap(array $bootstrap): LocalEvaluationConfigBuilder { $this->bootstrap = $bootstrap; @@ -56,6 +65,9 @@ public function fetchClient(FetchClientInterface $fetchClient): LocalEvaluationC return $this; } + /** + * @param array $guzzleClientConfig + */ public function guzzleClientConfig(array $guzzleClientConfig): LocalEvaluationConfigBuilder { $this->guzzleClientConfig = $guzzleClientConfig; diff --git a/src/Logger/DefaultLogger.php b/src/Logger/DefaultLogger.php index 4854bee..cf88ce0 100644 --- a/src/Logger/DefaultLogger.php +++ b/src/Logger/DefaultLogger.php @@ -54,6 +54,9 @@ public function log($level, $message, array $context = []): void // Do nothing, only the leveled methods should be used. } + /** + * @param array $context + */ private static function logMessage(int $level, string $message, array $context = []): void { $date = new DateTimeImmutable(); diff --git a/src/Logger/InternalLogger.php b/src/Logger/InternalLogger.php index 17d95b6..407a3c5 100644 --- a/src/Logger/InternalLogger.php +++ b/src/Logger/InternalLogger.php @@ -76,7 +76,7 @@ public function log($level, $message, array $context = array()) // Do nothing } - private function shouldLog($level): bool + private function shouldLog(int $level): bool { return $level >= $this->logLevel; } diff --git a/src/Remote/RemoteEvaluationClient.php b/src/Remote/RemoteEvaluationClient.php index 8e4ee6f..882bfb4 100644 --- a/src/Remote/RemoteEvaluationClient.php +++ b/src/Remote/RemoteEvaluationClient.php @@ -28,8 +28,8 @@ class RemoteEvaluationClient /** * Creates a new RemoteEvaluationClient instance. * - * @param $apiKey string The environment API Key - * @param $config ?RemoteEvaluationConfig See {@link RemoteEvaluationConfig} for config options + * @param string $apiKey The environment API Key + * @param ?RemoteEvaluationConfig $config See {@link RemoteEvaluationConfig} for config options */ public function __construct(string $apiKey, ?RemoteEvaluationConfig $config = null) { @@ -44,9 +44,9 @@ public function __construct(string $apiKey, ?RemoteEvaluationConfig $config = nu * * This method will automatically retry if configured (default). * - * @param $user User The {@link User} context - * @param $flagKeys array The flags to evaluate for this specific fetch request. - * @return array A {@link Variant} array for the user on success, empty array on error. d + * @param User $user The {@link User} context + * @param array $flagKeys The flags to evaluate for this specific fetch request. + * @return array A {@link Variant} array for the user on success, empty array on error. */ public function fetch(User $user, array $flagKeys = []): array { @@ -57,7 +57,12 @@ public function fetch(User $user, array $flagKeys = []): array // Define the request data $libraryUser = $user->copyToBuilder()->library('experiment-php-server/' . VERSION)->build(); - $serializedUser = base64_encode(json_encode($libraryUser->toArray())); + $userJson = json_encode($libraryUser->toArray()); + if ($userJson === false) { + $this->logger->error('[Experiment] Failed to fetch variants: ' . json_last_error_msg()); + return []; + } + $serializedUser = base64_encode($userJson); // Define the request URL $endpoint = $this->config->serverUrl . '/sdk/v2/vardata?v=0'; @@ -67,7 +72,12 @@ public function fetch(User $user, array $flagKeys = []): array ->withHeader('X-Amp-Exp-User', $serializedUser); if (!empty($flagKeys)) { - $request = $request->withHeader('X-Amp-Exp-Flag-Keys', base64_encode(json_encode($flagKeys))); + $flagKeysJson = json_encode($flagKeys); + if ($flagKeysJson === false) { + $this->logger->error('[Experiment] Failed to fetch variants: ' . json_last_error_msg()); + return []; + } + $request = $request->withHeader('X-Amp-Exp-Flag-Keys', base64_encode($flagKeysJson)); } $fetchClient = $this->httpClient->getClient(); diff --git a/src/Remote/RemoteEvaluationConfig.php b/src/Remote/RemoteEvaluationConfig.php index d384dfe..11e74a0 100644 --- a/src/Remote/RemoteEvaluationConfig.php +++ b/src/Remote/RemoteEvaluationConfig.php @@ -32,6 +32,7 @@ class RemoteEvaluationConfig */ public ?FetchClientInterface $fetchClient; /** + * @var array * The configuration for the underlying default Guzzle client. */ public array $guzzleClientConfig; @@ -46,6 +47,9 @@ class RemoteEvaluationConfig ]; + /** + * @param array $guzzleClientConfig + */ public function __construct( ?LoggerInterface $logger, int $logLevel, diff --git a/src/Remote/RemoteEvaluationConfigBuilder.php b/src/Remote/RemoteEvaluationConfigBuilder.php index 5f1ea6e..1b64c5b 100644 --- a/src/Remote/RemoteEvaluationConfigBuilder.php +++ b/src/Remote/RemoteEvaluationConfigBuilder.php @@ -13,6 +13,9 @@ class RemoteEvaluationConfigBuilder protected bool $debug = RemoteEvaluationConfig::DEFAULTS['debug']; protected string $serverUrl = RemoteEvaluationConfig::DEFAULTS['serverUrl']; protected ?FetchClientInterface $fetchClient = RemoteEvaluationConfig::DEFAULTS['fetchClient']; + /** + * @var array + */ protected array $guzzleClientConfig = RemoteEvaluationConfig::DEFAULTS['guzzleClientConfig']; public function __construct() @@ -43,6 +46,10 @@ public function fetchClient(FetchClientInterface $fetchClient): RemoteEvaluation return $this; } + + /** + * @param array $guzzleClientConfig + */ public function guzzleClientConfig(array $guzzleClientConfig): RemoteEvaluationConfigBuilder { $this->guzzleClientConfig = $guzzleClientConfig; diff --git a/src/User.php b/src/User.php index 8266e58..2e67d96 100644 --- a/src/User.php +++ b/src/User.php @@ -38,10 +38,24 @@ class User public ?string $deviceModel; public ?string $carrier; public ?string $library; + /** + * @var ?array + */ public ?array $userProperties; + /** + * @var ?array + */ public ?array $groups; + /** + * @var ?array + */ public ?array $groupProperties; + /** + * @param ?array $userProperties + * @param ?array $groups + * @param ?array $groupProperties + */ public function __construct( ?string $deviceId, ?string $userId, @@ -109,6 +123,9 @@ public function copyToBuilder(): UserBuilder ->groupProperties($this->groupProperties); } + /** + * @return array + */ public function toArray(): array { return array_filter(["device_id" => $this->deviceId, "user_id" => $this->userId, @@ -130,6 +147,9 @@ public function toArray(): array { "group_properties" => $this->groupProperties]); } + /** + * @return array + */ public function toEvaluationContext(): array { diff --git a/src/UserBuilder.php b/src/UserBuilder.php index 3cfd7cc..9f79ee5 100644 --- a/src/UserBuilder.php +++ b/src/UserBuilder.php @@ -19,8 +19,17 @@ class UserBuilder protected ?string $deviceModel = null; protected ?string $carrier = null; protected ?string $library = null; + /** + * @var ?array + */ protected ?array $userProperties = null; + /** + * @var ?array + */ protected ?array $groups = null; + /** + * @var ?array + */ protected ?array $groupProperties = null; public function __construct() @@ -117,18 +126,27 @@ public function library(?string $library): UserBuilder return $this; } + /** + * @param ?array $userProperties + */ public function userProperties(?array $userProperties): UserBuilder { $this->userProperties = $userProperties; return $this; } + /** + * @param ?array $groups + */ public function groups(?array $groups): UserBuilder { $this->groups = $groups; return $this; } + /** + * @param ?array $groupProperties + */ public function groupProperties(?array $groupProperties): UserBuilder { $this->groupProperties = $groupProperties; diff --git a/src/Variant.php b/src/Variant.php index de86ab3..8486ffd 100644 --- a/src/Variant.php +++ b/src/Variant.php @@ -13,6 +13,7 @@ class Variant */ public ?string $value; /** + * @var mixed * The attached payload, if any */ public $payload; @@ -21,11 +22,16 @@ class Variant */ public ?string $expKey; /** + * @var ?array * Flag, segment, and variant metadata produced as a result of * evaluation for the user. Used for system purposes. */ public ?array $metadata; + /** + * @param mixed $payload + * @param ?array $metadata + */ public function __construct( ?string $key = null, ?string $value = null, @@ -41,6 +47,9 @@ public function __construct( $this->metadata = $metadata; } + /** + * @param array $evaluationVariant + */ public static function convertEvaluationVariantToVariant(array $evaluationVariant): Variant {