diff --git a/.gitignore b/.gitignore
index 98d8c720..514bed27 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,3 +9,5 @@ flagship
/.phpunit.cache
demo-test.php
docker-compose.demo.yml
+sample.php
+sample.php
diff --git a/.vscode/settings.json b/.vscode/settings.json
index ce6abab7..815d4b97 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -8,4 +8,5 @@
},
"php-cs-fixer.executablePath": "./vendor/bin/php-cs-fixer",
"php-cs-fixer.onsave": true,
+ "intelephense.environment.phpVersion": "8.1.0",
}
diff --git a/composer.json b/composer.json
index a49799a4..00343e14 100644
--- a/composer.json
+++ b/composer.json
@@ -2,7 +2,11 @@
"name": "flagship-io/flagship-php-sdk",
"type": "library",
"description": "Flagship PHP SDK",
- "keywords": ["flagship", "sdk", "flagship-php"],
+ "keywords": [
+ "flagship",
+ "sdk",
+ "flagship-php"
+ ],
"homepage": "https://github.com/flagship-io/flagship-php-sdk",
"authors": [
{
@@ -18,7 +22,12 @@
},
"require-dev": {
"squizlabs/php_codesniffer": "^3.10",
- "phpunit/phpunit": "^9.6"
+ "phpunit/phpunit": "^9.6",
+ "phpstan/phpstan": "^2.1",
+ "phpstan/phpstan-deprecation-rules": "^2.0",
+ "phpstan/phpstan-strict-rules": "^2.0",
+ "phpstan/phpstan-phpunit": "^2.0",
+ "php-mock/php-mock-phpunit": "^2.14"
},
"autoload": {
"psr-4": {
@@ -31,8 +40,19 @@
}
},
"scripts": {
+
"test": "phpunit",
+ "test:all": [
+ "@phpstan:all",
+ "phpunit"
+ ],
"check-style": "phpcs --standard=PSR12 -n src tests",
- "fix-style": "phpcbf --standard=phpcs.xml src tests"
+ "fix-style": "phpcbf --standard=phpcs.xml src tests",
+ "phpstan": "phpstan analyse --configuration=phpstan.neon --memory-limit=512M",
+ "phpstan:future": "phpstan analyse --configuration=phpstan-future.neon --memory-limit=512M",
+ "phpstan:all": [
+ "@phpstan",
+ "@phpstan:future"
+ ]
}
}
diff --git a/composer.lock b/composer.lock
index c0719d09..564e0075 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": "676c01a7b9b8f0b01e28db776080a602",
+ "content-hash": "eae07c148068ea0f65789a193ba0030f",
"packages": [
{
"name": "psr/log",
@@ -354,6 +354,414 @@
},
"time": "2022-02-21T01:04:05+00:00"
},
+ {
+ "name": "php-mock/php-mock",
+ "version": "2.6.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-mock/php-mock.git",
+ "reference": "e134d210e4707c29724ebc7fe50d220123f0fdd9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-mock/php-mock/zipball/e134d210e4707c29724ebc7fe50d220123f0fdd9",
+ "reference": "e134d210e4707c29724ebc7fe50d220123f0fdd9",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.6 || ^7.0 || ^8.0",
+ "phpunit/php-text-template": "^1 || ^2 || ^3 || ^4 || ^5"
+ },
+ "replace": {
+ "malkusch/php-mock": "*"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5 || ^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0",
+ "squizlabs/php_codesniffer": "^3.8"
+ },
+ "suggest": {
+ "php-mock/php-mock-phpunit": "Allows integration into PHPUnit testcase with the trait PHPMock."
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "autoload.php"
+ ],
+ "psr-4": {
+ "phpmock\\": [
+ "classes/",
+ "tests/"
+ ]
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "WTFPL"
+ ],
+ "authors": [
+ {
+ "name": "Markus Malkusch",
+ "email": "markus@malkusch.de",
+ "homepage": "http://markus.malkusch.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "PHP-Mock can mock built-in PHP functions (e.g. time()). PHP-Mock relies on PHP's namespace fallback policy. No further extension is needed.",
+ "homepage": "https://github.com/php-mock/php-mock",
+ "keywords": [
+ "BDD",
+ "TDD",
+ "function",
+ "mock",
+ "stub",
+ "test",
+ "test double",
+ "testing"
+ ],
+ "support": {
+ "issues": "https://github.com/php-mock/php-mock/issues",
+ "source": "https://github.com/php-mock/php-mock/tree/2.6.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/michalbundyra",
+ "type": "github"
+ }
+ ],
+ "time": "2025-08-18T19:59:14+00:00"
+ },
+ {
+ "name": "php-mock/php-mock-integration",
+ "version": "3.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-mock/php-mock-integration.git",
+ "reference": "8ceb860f343a143af604efeb66a7a124381cc52e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-mock/php-mock-integration/zipball/8ceb860f343a143af604efeb66a7a124381cc52e",
+ "reference": "8ceb860f343a143af604efeb66a7a124381cc52e",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.6",
+ "php-mock/php-mock": "^2.5",
+ "phpunit/php-text-template": "^1 || ^2 || ^3 || ^4 || ^5"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^5.7.27 || ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "phpmock\\integration\\": "classes/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "WTFPL"
+ ],
+ "authors": [
+ {
+ "name": "Markus Malkusch",
+ "email": "markus@malkusch.de",
+ "homepage": "http://markus.malkusch.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "Integration package for PHP-Mock",
+ "homepage": "https://github.com/php-mock/php-mock-integration",
+ "keywords": [
+ "BDD",
+ "TDD",
+ "function",
+ "mock",
+ "stub",
+ "test",
+ "test double"
+ ],
+ "support": {
+ "issues": "https://github.com/php-mock/php-mock-integration/issues",
+ "source": "https://github.com/php-mock/php-mock-integration/tree/3.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/michalbundyra",
+ "type": "github"
+ }
+ ],
+ "time": "2025-03-08T19:22:38+00:00"
+ },
+ {
+ "name": "php-mock/php-mock-phpunit",
+ "version": "2.14.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-mock/php-mock-phpunit.git",
+ "reference": "c074f7a260cb80bdc7cf0823dc23174bc49064e1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-mock/php-mock-phpunit/zipball/c074f7a260cb80bdc7cf0823dc23174bc49064e1",
+ "reference": "c074f7a260cb80bdc7cf0823dc23174bc49064e1",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7",
+ "php-mock/php-mock-integration": "^3.0",
+ "phpunit/phpunit": "^6 || ^7 || ^8 || ^9 || ^10.0.17 || ^11 || ^12.0.9"
+ },
+ "require-dev": {
+ "mockery/mockery": "^1.3.6"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "autoload.php"
+ ],
+ "psr-4": {
+ "phpmock\\phpunit\\": "classes/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "WTFPL"
+ ],
+ "authors": [
+ {
+ "name": "Markus Malkusch",
+ "email": "markus@malkusch.de",
+ "homepage": "http://markus.malkusch.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "Mock built-in PHP functions (e.g. time()) with PHPUnit. This package relies on PHP's namespace fallback policy. No further extension is needed.",
+ "homepage": "https://github.com/php-mock/php-mock-phpunit",
+ "keywords": [
+ "BDD",
+ "TDD",
+ "function",
+ "mock",
+ "phpunit",
+ "stub",
+ "test",
+ "test double",
+ "testing"
+ ],
+ "support": {
+ "issues": "https://github.com/php-mock/php-mock-phpunit/issues",
+ "source": "https://github.com/php-mock/php-mock-phpunit/tree/2.14.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/michalbundyra",
+ "type": "github"
+ }
+ ],
+ "time": "2025-11-19T21:07:31+00:00"
+ },
+ {
+ "name": "phpstan/phpstan",
+ "version": "2.1.33",
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/phpstan/zipball/9e800e6bee7d5bd02784d4c6069b48032d16224f",
+ "reference": "9e800e6bee7d5bd02784d4c6069b48032d16224f",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4|^8.0"
+ },
+ "conflict": {
+ "phpstan/phpstan-shim": "*"
+ },
+ "bin": [
+ "phpstan",
+ "phpstan.phar"
+ ],
+ "type": "library",
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "PHPStan - PHP Static Analysis Tool",
+ "keywords": [
+ "dev",
+ "static analysis"
+ ],
+ "support": {
+ "docs": "https://phpstan.org/user-guide/getting-started",
+ "forum": "https://github.com/phpstan/phpstan/discussions",
+ "issues": "https://github.com/phpstan/phpstan/issues",
+ "security": "https://github.com/phpstan/phpstan/security/policy",
+ "source": "https://github.com/phpstan/phpstan-src"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/ondrejmirtes",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/phpstan",
+ "type": "github"
+ }
+ ],
+ "time": "2025-12-05T10:24:31+00:00"
+ },
+ {
+ "name": "phpstan/phpstan-deprecation-rules",
+ "version": "2.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/phpstan-deprecation-rules.git",
+ "reference": "468e02c9176891cc901143da118f09dc9505fc2f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/phpstan-deprecation-rules/zipball/468e02c9176891cc901143da118f09dc9505fc2f",
+ "reference": "468e02c9176891cc901143da118f09dc9505fc2f",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4 || ^8.0",
+ "phpstan/phpstan": "^2.1.15"
+ },
+ "require-dev": {
+ "php-parallel-lint/php-parallel-lint": "^1.2",
+ "phpstan/phpstan-phpunit": "^2.0",
+ "phpunit/phpunit": "^9.6"
+ },
+ "type": "phpstan-extension",
+ "extra": {
+ "phpstan": {
+ "includes": [
+ "rules.neon"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PHPStan\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "PHPStan rules for detecting usage of deprecated classes, methods, properties, constants and traits.",
+ "support": {
+ "issues": "https://github.com/phpstan/phpstan-deprecation-rules/issues",
+ "source": "https://github.com/phpstan/phpstan-deprecation-rules/tree/2.0.3"
+ },
+ "time": "2025-05-14T10:56:57+00:00"
+ },
+ {
+ "name": "phpstan/phpstan-phpunit",
+ "version": "2.0.11",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/phpstan-phpunit.git",
+ "reference": "5e30669bef866eff70db8b58d72a5c185aa82414"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/5e30669bef866eff70db8b58d72a5c185aa82414",
+ "reference": "5e30669bef866eff70db8b58d72a5c185aa82414",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4 || ^8.0",
+ "phpstan/phpstan": "^2.1.32"
+ },
+ "conflict": {
+ "phpunit/phpunit": "<7.0"
+ },
+ "require-dev": {
+ "nikic/php-parser": "^5",
+ "php-parallel-lint/php-parallel-lint": "^1.2",
+ "phpstan/phpstan-deprecation-rules": "^2.0",
+ "phpstan/phpstan-strict-rules": "^2.0",
+ "phpunit/phpunit": "^9.6"
+ },
+ "type": "phpstan-extension",
+ "extra": {
+ "phpstan": {
+ "includes": [
+ "extension.neon",
+ "rules.neon"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PHPStan\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "PHPUnit extensions and rules for PHPStan",
+ "support": {
+ "issues": "https://github.com/phpstan/phpstan-phpunit/issues",
+ "source": "https://github.com/phpstan/phpstan-phpunit/tree/2.0.11"
+ },
+ "time": "2025-12-19T09:05:35+00:00"
+ },
+ {
+ "name": "phpstan/phpstan-strict-rules",
+ "version": "2.0.7",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/phpstan-strict-rules.git",
+ "reference": "d6211c46213d4181054b3d77b10a5c5cb0d59538"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/d6211c46213d4181054b3d77b10a5c5cb0d59538",
+ "reference": "d6211c46213d4181054b3d77b10a5c5cb0d59538",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4 || ^8.0",
+ "phpstan/phpstan": "^2.1.29"
+ },
+ "require-dev": {
+ "php-parallel-lint/php-parallel-lint": "^1.2",
+ "phpstan/phpstan-deprecation-rules": "^2.0",
+ "phpstan/phpstan-phpunit": "^2.0",
+ "phpunit/phpunit": "^9.6"
+ },
+ "type": "phpstan-extension",
+ "extra": {
+ "phpstan": {
+ "includes": [
+ "rules.neon"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PHPStan\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Extra strict and opinionated rules for PHPStan",
+ "support": {
+ "issues": "https://github.com/phpstan/phpstan-strict-rules/issues",
+ "source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.7"
+ },
+ "time": "2025-09-26T11:19:08+00:00"
+ },
{
"name": "phpunit/php-code-coverage",
"version": "9.2.24",
@@ -1872,7 +2280,7 @@
],
"aliases": [],
"minimum-stability": "stable",
- "stability-flags": [],
+ "stability-flags": {},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
@@ -1880,6 +2288,6 @@
"ext-curl": "*",
"ext-json": "*"
},
- "platform-dev": [],
- "plugin-api-version": "2.6.0"
+ "platform-dev": {},
+ "plugin-api-version": "2.9.0"
}
diff --git a/phpcs.xml b/phpcs.xml
index f6d98641..057919ad 100644
--- a/phpcs.xml
+++ b/phpcs.xml
@@ -5,6 +5,8 @@
+
+
src
tests
diff --git a/phpstan-future.neon b/phpstan-future.neon
new file mode 100644
index 00000000..7130b4e9
--- /dev/null
+++ b/phpstan-future.neon
@@ -0,0 +1,5 @@
+includes:
+ - phpstan.neon
+
+parameters:
+ phpVersion: 80500
\ No newline at end of file
diff --git a/phpstan.neon b/phpstan.neon
new file mode 100644
index 00000000..b9462e10
--- /dev/null
+++ b/phpstan.neon
@@ -0,0 +1,18 @@
+includes:
+ - vendor/phpstan/phpstan-deprecation-rules/rules.neon
+ # - vendor/phpstan/phpstan-phpunit/extension.neon
+
+parameters:
+ level: max
+ paths:
+ - src
+ phpVersion: 80100
+
+ # Treat PHPDoc types as certain for better inference
+ treatPhpDocTypesAsCertain: false
+
+ # Ignore vendor and optional files
+ excludePaths:
+ - tests/bootstrap.php (?)
+ - vendor/
+
diff --git a/phpunit.xml b/phpunit.xml
index 7f1bed60..3ac500aa 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -25,9 +25,6 @@
src
-
- src/Utils/FlagshipLogManager.php
-
diff --git a/src/Api/BatchingCachingStrategyAbstract.php b/src/Api/BatchingCachingStrategyAbstract.php
index 47ed18ee..abcef2d6 100644
--- a/src/Api/BatchingCachingStrategyAbstract.php
+++ b/src/Api/BatchingCachingStrategyAbstract.php
@@ -138,33 +138,33 @@ public function getActivatePoolQueue(): array
}
/**
- * @param $key
+ * @param string $key
* @param HitAbstract $hit
* @return void
*/
- public function hydrateHitsPoolQueue($key, HitAbstract $hit): void
+ public function hydrateHitsPoolQueue(string $key, HitAbstract $hit): void
{
$this->hitsPoolQueue[$key] = $hit;
}
/**
- * @param $key
+ * @param string $key
* @param Activate $hit
* @return void
*/
- public function hydrateActivatePoolQueue($key, Activate $hit): void
+ public function hydrateActivatePoolQueue(string $key, Activate $hit): void
{
$this->activatePoolQueue[$key] = $hit;
}
/**
- * @return array
+ * @return array
*/
public function getActivateHeaders(): array
{
return [
- FlagshipConstant::HEADER_X_API_KEY => $this->config->getApiKey(),
+ FlagshipConstant::HEADER_X_API_KEY => $this->config->getApiKey() ?? '',
FlagshipConstant::HEADER_X_SDK_VERSION => FlagshipConstant::SDK_VERSION,
FlagshipConstant::HEADER_CONTENT_TYPE => FlagshipConstant::HEADER_APPLICATION_JSON,
FlagshipConstant::HEADER_X_SDK_CLIENT => FlagshipConstant::SDK_LANGUAGE,
@@ -172,10 +172,10 @@ public function getActivateHeaders(): array
}
/**
- * @param $visitorId
+ * @param string $visitorId
* @return string
*/
- public function generateHitKey($visitorId): string
+ public function generateHitKey(string $visitorId): string
{
return $visitorId . ":" . $this->newGuid();
}
@@ -280,7 +280,7 @@ protected function onVisitorExposed(Activate $activate): void
}
$exposedFlag = new ExposedFlag(
- $activate->getFlagKey(),
+ $activate->getFlagKey() ?? '',
$activate->getFlagValue(),
$activate->getFlagDefaultValue(),
$activate->getFlagMetadata()
@@ -288,7 +288,7 @@ protected function onVisitorExposed(Activate $activate): void
$exposedUser = new ExposedVisitor(
$activate->getVisitorId(),
$activate->getAnonymousId(),
- $activate->getVisitorContext()
+ $activate->getVisitorContext() ?? []
);
try {
@@ -298,7 +298,7 @@ protected function onVisitorExposed(Activate $activate): void
}
}
- protected function sendActivateHitBatch(ActivateBatch $activateBatch)
+ protected function sendActivateHitBatch(ActivateBatch $activateBatch): void
{
$headers = $this->getActivateHeaders();
$requestBody = $activateBatch->toApiKeys();
@@ -346,7 +346,7 @@ protected function sendActivateHitBatch(ActivateBatch $activateBatch)
->setFlagshipInstanceId($this->flagshipInstanceId)
->setLogLevel(LogLevel::ERROR)
->setTraffic(100)->setConfig($this->config)
- ->setVisitorId($this->flagshipInstanceId);
+ ->setVisitorId($this->flagshipInstanceId ?? "");
$this->addTroubleshootingHit($troubleshooting);
$this->sendTroubleshootingQueue();
@@ -386,7 +386,7 @@ protected function sendActivateHit(): void
/**
* @param string $visitorId
- * @return string []
+ * @return string[]
*/
protected function commonNotConsent(string $visitorId): array
{
@@ -497,7 +497,7 @@ public function sendBatch(): void
->setHttpResponseBody($exception->getMessage())
->setHttpResponseTime($this->getNow() - $now)
->setTraffic(100)->setConfig($this->config)
- ->setVisitorId($this->flagshipInstanceId);
+ ->setVisitorId($this->flagshipInstanceId ?? "");
$this->addTroubleshootingHit($troubleshooting);
$this->sendTroubleshootingQueue();
$this->logErrorSprintf(
@@ -532,7 +532,7 @@ public function cacheHit(array $hits): void
HitCacheFields::DATA => [
HitCacheFields::VISITOR_ID => $hit->getVisitorId(),
HitCacheFields::ANONYMOUS_ID => $hit->getAnonymousId(),
- HitCacheFields::TYPE => $hit->getType(),
+ HitCacheFields::TYPE => $hit->getType()->value,
HitCacheFields::CONTENT => $hit->toArray(),
HitCacheFields::TIME => $this->getNow(),
],
@@ -563,7 +563,7 @@ public function cacheHit(array $hits): void
}
/**
- * @param array $hitKeys
+ * @param array $hitKeys
* @return void
*/
public function flushHits(array $hitKeys): void
@@ -649,7 +649,7 @@ public function addTroubleshootingHit(Troubleshooting $hit): void
return;
}
$troubleshootingData = $this->getTroubleshootingData();
- if ($troubleshootingData->getTraffic() < $hit->getTraffic()) {
+ if ($troubleshootingData?->getTraffic() < $hit->getTraffic()) {
return;
}
$hitKey = $this->generateHitKey($hit->getVisitorId());
diff --git a/src/Api/NoBatchingContinuousCachingStrategy.php b/src/Api/NoBatchingContinuousCachingStrategy.php
index bc311fbc..2dbd023b 100644
--- a/src/Api/NoBatchingContinuousCachingStrategy.php
+++ b/src/Api/NoBatchingContinuousCachingStrategy.php
@@ -94,7 +94,7 @@ protected function sendHit(HitAbstract $hit): void
->setHttpResponseBody($exception->getMessage())
->setHttpResponseTime($this->getNow() - $now)
->setTraffic(100)->setConfig($this->config)
- ->setVisitorId($this->flagshipInstanceId)
+ ->setVisitorId($this->flagshipInstanceId ?? "")
;
$this->addTroubleshootingHit($troubleshooting);
$this->sendTroubleshootingQueue();
@@ -155,7 +155,7 @@ public function activateFlag(Activate $hit): void
->setHttpResponseTime($this->getNow() - $now)
->setLogLevel(LogLevel::ERROR)
->setTraffic(100)->setConfig($this->config)
- ->setVisitorId($this->flagshipInstanceId);
+ ->setVisitorId($this->flagshipInstanceId ?? "");
$this->addTroubleshootingHit($troubleshooting);
$this->sendTroubleshootingQueue();
}
@@ -165,7 +165,7 @@ public function activateFlag(Activate $hit): void
protected function notConsent(string $visitorId): void
{
$keysToFlush = $this->commonNotConsent($visitorId);
- $mergedQueue = array_merge($keysToFlush, $this->cacheHitKeys);
+ $mergedQueue = array_merge($keysToFlush, $this->cacheHitKeys);
if (count($mergedQueue) === 0) {
return;
}
diff --git a/src/Api/TrackingManagerAbstract.php b/src/Api/TrackingManagerAbstract.php
index c8385cc3..6aaa6baf 100644
--- a/src/Api/TrackingManagerAbstract.php
+++ b/src/Api/TrackingManagerAbstract.php
@@ -23,6 +23,7 @@
/**
* Class TrackingManagerAbstract
* @package Flagship\Api
+ * @phpstan-import-type HitCacheDataArray from \Flagship\Model\Types
*/
abstract class TrackingManagerAbstract implements TrackingManagerInterface
{
@@ -124,28 +125,41 @@ public function initStrategy(): BatchingCachingStrategyAbstract
};
}
+ /**
+ * @param array $item
+ * @return bool
+ */
protected function checkLookupHitData(array $item): bool
{
- if (
- isset($item[HitCacheFields::DATA][HitCacheFields::CONTENT]) &&
- isset($item[HitCacheFields::DATA][HitCacheFields::TYPE]) &&
- isset($item[HitCacheFields::VERSION]) && $item[HitCacheFields::VERSION] == 1
- ) {
- return true;
+ $data = $item[HitCacheFields::DATA] ?? null;
+ if (is_null($data) || !is_array($data)) {
+ $this->logErrorSprintf(
+ $this->config,
+ FlagshipConstant::PROCESS_CACHE,
+ FlagshipConstant::HIT_CACHE_FORMAT_ERROR,
+ [$item]
+ );
+ return false;
+ }
+ $content = $data[HitCacheFields::CONTENT] ?? null;
+ $type = $data[HitCacheFields::TYPE] ?? null;
+ $version = $item[HitCacheFields::VERSION] ?? null;
+ if (is_null($content) || is_null($type) || is_null($version) || !is_array($content)) {
+ $this->logErrorSprintf(
+ $this->config,
+ FlagshipConstant::PROCESS_CACHE,
+ FlagshipConstant::HIT_CACHE_FORMAT_ERROR,
+ [$item]
+ );
+ return false;
}
- $this->logErrorSprintf(
- $this->config,
- FlagshipConstant::PROCESS_CACHE,
- FlagshipConstant::HIT_CACHE_FORMAT_ERROR,
- [$item]
- );
- return false;
+ return true;
}
- protected function checkHitTime($time): bool
+ protected function checkHitTime(float $time): bool
{
$now = round(microtime(true) * 1000);
- return ($now - $time) >= FlagshipConstant::DEFAULT_HIT_CACHE_TIME_MS;
+ return ($now - $time) <= FlagshipConstant::DEFAULT_HIT_CACHE_TIME_MS;
}
public function lookupHits(): void
{
@@ -163,52 +177,52 @@ public function lookupHits(): void
[$hitsCache]
);
- if (!is_array($hitsCache) || !count($hitsCache)) {
+ if (empty($hitsCache)) {
return;
}
$hitKeysToRemove = [];
+
foreach ($hitsCache as $key => $item) {
- $hitKeysToRemove [] = $key;
- if (
- !$this->checkLookupHitData($item) ||
- $this->checkHitTime($item[HitCacheFields::DATA][HitCacheFields::TIME])
- ) {
+ $hitKeysToRemove[] = $key;
+
+ if (!$this->checkLookupHitData($item)) {
+ continue;
+ }
+
+ $hitCacheData = $item[HitCacheFields::DATA];
+ $hitTime = $hitCacheData[HitCacheFields::TIME] ?? 0;
+
+ if (!$this->checkHitTime($hitTime)) {
continue;
}
$type = $item[HitCacheFields::DATA][HitCacheFields::TYPE];
$content = $item[HitCacheFields::DATA][HitCacheFields::CONTENT];
- switch ($type) {
- case HitType::EVENT->value:
- $hit = HitAbstract::hydrate(Event::getClassName(), $content);
- break;
- case HitType::ITEM->value:
- $hit = HitAbstract::hydrate(Item::getClassName(), $content);
- break;
- case HitType::PAGE_VIEW->value:
- $hit = HitAbstract::hydrate(Page::getClassName(), $content);
- break;
- case HitType::SCREEN_VIEW->value:
- $hit = HitAbstract::hydrate(Screen::getClassName(), $content);
- break;
- case HitType::SEGMENT->value:
- $hit = HitAbstract::hydrate(Segment::getClassName(), $content);
- break;
- case HitType::ACTIVATE->value:
- $hit = HitAbstract::hydrate(Activate::getClassName(), $content);
- $hit->setConfig($this->config);
- $this->getStrategy()->hydrateActivatePoolQueue($hit->getKey(), $hit);
- continue 2;
- case HitType::TRANSACTION->value:
- $hit = HitAbstract::hydrate(Transaction::getClassName(), $content);
- break;
- default:
- continue 2;
+ if (HitType::tryFrom($type) === HitType::ACTIVATE) {
+ $hit = HitAbstract::hydrate(Activate::class, $content);
+ $hit->setConfig($this->config);
+ $this->getStrategy()->hydrateActivatePoolQueue($hit->getKey(), $hit);
+ continue;
}
+ $hit = match (HitType::tryFrom($type)) {
+ HitType::EVENT => HitAbstract::hydrate(Event::class, $content),
+ HitType::ITEM => HitAbstract::hydrate(Item::class, $content),
+ HitType::PAGE_VIEW => HitAbstract::hydrate(Page::class, $content),
+ HitType::SCREEN_VIEW => HitAbstract::hydrate(Screen::class, $content),
+ HitType::SEGMENT => HitAbstract::hydrate(Segment::class, $content),
+ HitType::TRANSACTION => HitAbstract::hydrate(Transaction::class, $content),
+ default => null,
+ };
+
+ if (is_null($hit)) {
+ continue;
+ }
+
+
$hit->setConfig($this->config);
$this->getStrategy()->hydrateHitsPoolQueue($hit->getKey(), $hit);
}
@@ -220,8 +234,8 @@ public function lookupHits(): void
FlagshipConstant::PROCESS_CACHE,
FlagshipConstant::HIT_CACHE_ERROR,
[
- "lookupHits",
- $exception->getMessage(),
+ "lookupHits",
+ $exception->getMessage(),
]
);
}
diff --git a/src/Cache/IHitCacheImplementation.php b/src/Cache/IHitCacheImplementation.php
index 72cee2c3..9957bf09 100644
--- a/src/Cache/IHitCacheImplementation.php
+++ b/src/Cache/IHitCacheImplementation.php
@@ -2,12 +2,15 @@
namespace Flagship\Cache;
+/**
+ * @phpstan-import-type HitCacheArray from \Flagship\Model\Types
+ */
interface IHitCacheImplementation
{
/**
* This method will be called to cache visitor hits when a hit has failed to be sent if there is no internet,
* there has been a timeout or if the request responded with something > 2XX.
- * @param array $hits
+ * @param array $hits
* @return void
*/
public function cacheHit(array $hits): void;
@@ -16,13 +19,13 @@ public function cacheHit(array $hits): void;
* This method will be called to load hits corresponding to visitor ID
* from your database and trying to send them again in the background.
* Note: Hits older than 4H will be ignored
- * @return array
+ * @return array
*/
public function lookupHits(): array;
/**
* This method will be called to erase the visitor hits cache corresponding to visitor ID from your database.
- * @param array $hitKeys
+ * @param string[] $hitKeys
* @return void
*/
public function flushHits(array $hitKeys): void;
diff --git a/src/Cache/IVisitorCacheImplementation.php b/src/Cache/IVisitorCacheImplementation.php
index f9beeb1b..f5b3458e 100644
--- a/src/Cache/IVisitorCacheImplementation.php
+++ b/src/Cache/IVisitorCacheImplementation.php
@@ -2,12 +2,16 @@
namespace Flagship\Cache;
+
+/**
+ * @phpstan-import-type VisitorCacheArray from \Flagship\Model\Types
+ */
interface IVisitorCacheImplementation
{
/**
* This method is called when the SDK needs to cache visitor information in your database.
* @param string $visitorId
- * @param array $data
+ * @param VisitorCacheArray $data
* @return void
*/
public function cacheVisitor(string $visitorId, array $data): void;
@@ -16,9 +20,9 @@ public function cacheVisitor(string $visitorId, array $data): void;
* This method is called when the SDK needs to get the visitor
* information corresponding to visitor ID from your database.
* @param string $visitorId
- * @return array
+ * @return VisitorCacheArray
*/
- public function lookupVisitor(string $visitorId): array;
+ public function lookupVisitor(string $visitorId): array|null;
/**
* This method is called when the SDK needs to erase the visitor
diff --git a/src/Config/BucketingConfig.php b/src/Config/BucketingConfig.php
index 8cbbd059..ac2a2543 100644
--- a/src/Config/BucketingConfig.php
+++ b/src/Config/BucketingConfig.php
@@ -44,7 +44,7 @@ public function getSyncAgentUrl(): string
* @param string $syncAgentUrl
* @return BucketingConfig
*/
- public function setSyncAgentUrl(string $syncAgentUrl): static
+ public function setSyncAgentUrl(string $syncAgentUrl): self
{
$this->syncAgentUrl = $syncAgentUrl;
return $this;
@@ -68,7 +68,7 @@ public function getFetchThirdPartyData(): bool
* @param bool $fetchThirdPartyData
* @return BucketingConfig
*/
- public function setFetchThirdPartyData(bool $fetchThirdPartyData): static
+ public function setFetchThirdPartyData(bool $fetchThirdPartyData): self
{
$this->fetchThirdPartyData = $fetchThirdPartyData;
return $this;
@@ -79,6 +79,9 @@ public function setFetchThirdPartyData(bool $fetchThirdPartyData): static
*/
public function jsonSerialize(): mixed
{
+ /**
+ * @var array $parent
+ */
$parent = parent::jsonSerialize();
$parent[FlagshipField::FIELD_BUCKETING_URL] = $this->syncAgentUrl;
return $parent;
diff --git a/src/Config/FlagshipConfig.php b/src/Config/FlagshipConfig.php
index 1242b85f..3143e6f2 100644
--- a/src/Config/FlagshipConfig.php
+++ b/src/Config/FlagshipConfig.php
@@ -38,9 +38,9 @@ abstract class FlagshipConfig implements JsonSerializable
*/
private DecisionMode $decisionMode = DecisionMode::DECISION_API;
/**
- * @var int
+ * @var float
*/
- private int $timeout = FlagshipConstant::REQUEST_TIME_OUT;
+ private float $timeout = FlagshipConstant::REQUEST_TIME_OUT;
/**
* @var ?LoggerInterface
*/
@@ -108,7 +108,7 @@ public function getEnvId(): ?string
* @param string $envId environment id.
* @return $this
*/
- public function setEnvId(string $envId): static
+ public function setEnvId(string $envId): self
{
$this->envId = $envId;
return $this;
@@ -128,7 +128,7 @@ public function getApiKey(): ?string
* @param string $apiKey secure api key.
* @return $this
*/
- public function setApiKey(string $apiKey): static
+ public function setApiKey(string $apiKey): self
{
$this->apiKey = $apiKey;
return $this;
@@ -148,16 +148,16 @@ public function getDecisionMode(): DecisionMode
* @param DecisionMode $decisionMode decision mode value e.g DecisionMode::DECISION_API
* @return $this
*/
- protected function setDecisionMode(DecisionMode $decisionMode): static
+ protected function setDecisionMode(DecisionMode $decisionMode): self
{
$this->decisionMode = $decisionMode;
return $this;
}
/**
- * @return int
+ * @return float
*/
- public function getTimeout(): int
+ public function getTimeout(): float
{
return $this->timeout * 1000;
}
@@ -168,7 +168,7 @@ public function getTimeout(): int
* @param int $timeout Milliseconds for connect and read timeouts. Default is 2000ms.
* @return $this
*/
- public function setTimeout(int $timeout): static
+ public function setTimeout(int $timeout): self
{
if ($timeout <= 0) {
$this->logError($this, FlagshipConstant::TIMEOUT_TYPE_ERROR);
@@ -192,7 +192,7 @@ public function getLogManager(): ?LoggerInterface
* @param LoggerInterface $logManager custom implementation of LogManager.
* @return $this
*/
- public function setLogManager(LoggerInterface $logManager): static
+ public function setLogManager(LoggerInterface $logManager): self
{
$this->logManager = $logManager;
return $this;
@@ -211,7 +211,7 @@ public function getLogLevel(): LogLevel
* @param LogLevel $logLevel
* @return $this
*/
- public function setLogLevel(LogLevel $logLevel): static
+ public function setLogLevel(LogLevel $logLevel): self
{
$this->logLevel = $logLevel;
return $this;
@@ -231,7 +231,7 @@ public function getCacheStrategy(): CacheStrategy
* @param CacheStrategy $cacheStrategy
* @return FlagshipConfig
*/
- public function setCacheStrategy(CacheStrategy $cacheStrategy): static
+ public function setCacheStrategy(CacheStrategy $cacheStrategy): self
{
$this->cacheStrategy = $cacheStrategy;
return $this;
@@ -250,7 +250,7 @@ public function getOnSdkStatusChanged(): ?callable
* @param callable(FSSdkStatus $status): void $onSdkStatusChanged
* @return $this
*/
- public function setOnSdkStatusChanged(callable $onSdkStatusChanged): static
+ public function setOnSdkStatusChanged(callable $onSdkStatusChanged): self
{
$this->onSdkStatusChanged = $onSdkStatusChanged;
return $this;
@@ -269,7 +269,7 @@ public function getVisitorCacheImplementation(): ?IVisitorCacheImplementation
* @param IVisitorCacheImplementation $visitorCacheImplementation
* @return FlagshipConfig
*/
- public function setVisitorCacheImplementation(IVisitorCacheImplementation $visitorCacheImplementation): static
+ public function setVisitorCacheImplementation(IVisitorCacheImplementation $visitorCacheImplementation): self
{
$this->visitorCacheImplementation = $visitorCacheImplementation;
return $this;
@@ -287,7 +287,7 @@ public function getHitCacheImplementation(): ?IHitCacheImplementation
* @param ?IHitCacheImplementation $hitCacheImplementation
* @return FlagshipConfig
*/
- public function setHitCacheImplementation(?IHitCacheImplementation $hitCacheImplementation): static
+ public function setHitCacheImplementation(?IHitCacheImplementation $hitCacheImplementation): self
{
$this->hitCacheImplementation = $hitCacheImplementation;
return $this;
@@ -305,7 +305,7 @@ public function getOnVisitorExposed(): ?callable
* @param callable(ExposedVisitor $exposedUser, ExposedFlag $exposedFlag): void $onVisitorExposed
* @return FlagshipConfig
*/
- public function setOnVisitorExposed(callable $onVisitorExposed): static
+ public function setOnVisitorExposed(callable $onVisitorExposed): self
{
$this->onVisitorExposed = $onVisitorExposed;
return $this;
@@ -323,7 +323,7 @@ public function disableDeveloperUsageTracking(): bool
* @param bool $disableDeveloperUsageTracking
* @return FlagshipConfig
*/
- public function setDisableDeveloperUsageTracking(bool $disableDeveloperUsageTracking): static
+ public function setDisableDeveloperUsageTracking(bool $disableDeveloperUsageTracking): self
{
$this->disableDeveloperUsageTracking = $disableDeveloperUsageTracking;
return $this;
diff --git a/src/Decision/ApiManager.php b/src/Decision/ApiManager.php
index 50dc6255..5e7d7ab7 100644
--- a/src/Decision/ApiManager.php
+++ b/src/Decision/ApiManager.php
@@ -5,9 +5,10 @@
use DateTime;
use Exception;
use Flagship\Enum\LogLevel;
+use Flagship\Model\CampaignDTO;
use Flagship\Enum\FlagshipField;
-use Flagship\Enum\FSFetchStatus;
use Flagship\Enum\FSFetchReason;
+use Flagship\Enum\FSFetchStatus;
use Flagship\Hit\Troubleshooting;
use Flagship\Enum\FlagshipConstant;
use Flagship\Model\FetchFlagsStatus;
@@ -23,20 +24,47 @@
class ApiManager extends DecisionManagerAbstract
{
/**
+ * @param array|null $body
* @throws Exception
*/
protected function setTroubleshootingData(?array $body): void
{
$this->troubleshootingData = null;
+ if (empty($body) || !isset($body[FlagshipField::EXTRAS])) {
+ return;
+ }
+
+ /** @var mixed[]|null $extras*/
+ $extras = $body[FlagshipField::EXTRAS];
+
+ if (!is_array($extras) || empty($extras)) {
+ return;
+ }
+
+ if (!isset($extras[FlagshipField::ACCOUNT_SETTINGS])) {
+ return;
+ }
+ $accountSettings = $extras[FlagshipField::ACCOUNT_SETTINGS];
+ if (!is_array($accountSettings) || empty($accountSettings)) {
+ return;
+ }
+
+
if (
- $body === null || !isset($body[FlagshipField::EXTRAS]) ||
- !isset($body[FlagshipField::EXTRAS][FlagshipField::ACCOUNT_SETTINGS]) ||
- !isset($body[FlagshipField::EXTRAS][FlagshipField::ACCOUNT_SETTINGS][FlagshipField::TROUBLESHOOTING])
+ !isset($accountSettings[FlagshipField::TROUBLESHOOTING])
) {
return;
}
- $troubleshooting = $body[FlagshipField::EXTRAS][FlagshipField::ACCOUNT_SETTINGS]
- [FlagshipField::TROUBLESHOOTING];
+
+ /**
+ * @var array{startDate: string, endDate: string, timezone: string, traffic: int}|null $troubleshooting
+ */
+ $troubleshooting = $accountSettings[FlagshipField::TROUBLESHOOTING];
+
+ if (!is_array($troubleshooting)) {
+ return;
+ }
+
$startDate = new DateTime($troubleshooting[FlagshipField::START_DATE]);
$endDate = new DateTime($troubleshooting[FlagshipField::END_DATE]);
$troubleshootingData = new TroubleshootingData();
@@ -44,15 +72,18 @@ protected function setTroubleshootingData(?array $body): void
$this->troubleshootingData = $troubleshootingData;
}
+ /**
+ * @inheritDoc
+ */
public function getCampaigns(VisitorAbstract $visitor): array|null
{
$postData = [
- "visitorId" => $visitor->getVisitorId(),
- "anonymousId" => $visitor->getAnonymousId(),
- "trigger_hit" => false,
- "context" => count($visitor->getContext()) > 0 ? $visitor->getContext() : null,
- "visitor_consent" => $visitor->hasConsented(),
- ];
+ "visitorId" => $visitor->getVisitorId(),
+ "anonymousId" => $visitor->getAnonymousId(),
+ "trigger_hit" => false,
+ "context" => count($visitor->getContext()) > 0 ? $visitor->getContext() : null,
+ "visitor_consent" => $visitor->hasConsented(),
+ ];
$headers = $this->buildHeader($this->getConfig()->getApiKey());
$url = $this->buildDecisionApiUrl($this->getConfig()->getEnvId() . '/' .
FlagshipConstant::URL_CAMPAIGNS . '?' .
@@ -63,6 +94,8 @@ public function getCampaigns(VisitorAbstract $visitor): array|null
$this->httpClient->setTimeout($this->getConfig()->getTimeout() / 1000);
$response = $this->httpClient->post($url, [], $postData);
+
+ /** @var array $body*/
$body = $response->getBody();
$hasPanicMode = !empty($body["panic"]);
@@ -70,24 +103,33 @@ public function getCampaigns(VisitorAbstract $visitor): array|null
$this->setTroubleshootingData($body);
- return $body[FlagshipField::FIELD_CAMPAIGNS] ?? null;
+ /**
+ * @var array|null
+ */
+ $campaigns = $body[FlagshipField::FIELD_CAMPAIGNS] ?? null;
+
+ if (!is_array($campaigns)) {
+ return null;
+ }
+
+ return array_map(CampaignDTO::fromArray(...), $campaigns);
} catch (Exception $exception) {
$visitor->setFetchStatus(new FetchFlagsStatus(FSFetchStatus::FETCH_REQUIRED, FSFetchReason::FETCH_ERROR));
$this->logError($this->getConfig(), $exception->getMessage(), [FlagshipConstant::TAG => __FUNCTION__]);
$troubleshooting = new Troubleshooting();
- $troubleshooting->setLabel(TroubleshootingLabel::GET_CAMPAIGNS_ROUTE_RESPONSE_ERROR)->setHttpRequestBody($postData)->setHttpRequestHeaders($headers)->setHttpRequestMethod("POST")->setHttpRequestUrl($url)->setHttpResponseBody($exception->getMessage())->setHttpResponseTime($this->getNow() - $now)->setVisitorContext($visitor->getContext())->setLogLevel(LogLevel::ERROR)->setVisitorSessionId($visitor->getInstanceId())->setFlagshipInstanceId($visitor->getFlagshipInstanceId())->setTraffic(100)->setConfig($this->getConfig())->setVisitorId($visitor->getVisitorId())->setAnonymousId($visitor->getAnonymousId());
+ $troubleshooting->setLabel(TroubleshootingLabel::GET_CAMPAIGNS_ROUTE_RESPONSE_ERROR)
+ ->setHttpRequestBody($postData)
+ ->setHttpRequestHeaders($headers)
+ ->setHttpRequestMethod("POST")
+ ->setHttpRequestUrl($url)
+ ->setHttpResponseBody($exception->getMessage())
+ ->setHttpResponseTime($this->getNow() - $now)
+ ->setVisitorContext($visitor->getContext())
+ ->setLogLevel(LogLevel::ERROR)
+ ->setVisitorSessionId($visitor->getInstanceId())->setFlagshipInstanceId($visitor->getFlagshipInstanceId())->setTraffic(100)->setConfig($this->getConfig())->setVisitorId($visitor->getVisitorId())->setAnonymousId($visitor->getAnonymousId());
$visitor->sendTroubleshootingHit($troubleshooting);
return null;
}
}
-
- /**
- * @inheritDoc
- */
- public function getCampaignFlags(VisitorAbstract $visitor): array
- {
- $campaigns = $this->getCampaigns($visitor);
- return $this->getFlagsData($campaigns);
- }
}
diff --git a/src/Decision/BucketingManager.php b/src/Decision/BucketingManager.php
index c5972b93..e5cd65c8 100644
--- a/src/Decision/BucketingManager.php
+++ b/src/Decision/BucketingManager.php
@@ -2,22 +2,30 @@
namespace Flagship\Decision;
-use DateTime;
use Exception;
-use Flagship\Config\BucketingConfig;
-use Flagship\Config\FlagshipConfig;
-use Flagship\Enum\FlagshipConstant;
-use Flagship\Enum\FlagshipField;
-use Flagship\Enum\LogLevel;
-use Flagship\Enum\TroubleshootingLabel;
use Flagship\Hit\Segment;
-use Flagship\Hit\Troubleshooting;
-use Flagship\Model\TroubleshootingData;
-use Flagship\Utils\HttpClientInterface;
+use Flagship\Enum\LogLevel;
use Flagship\Utils\MurmurHash;
+use Flagship\Model\CampaignDTO;
+use Flagship\Enum\FlagshipField;
+use Flagship\Model\BucketingDTO;
+use Flagship\Model\VariationDTO;
+use Flagship\Hit\Troubleshooting;
+use Flagship\Model\TargetingsDTO;
+use Flagship\Enum\FlagshipConstant;
+use Flagship\Config\BucketingConfig;
+use Flagship\Enum\TargetingOperator;
+use Flagship\Model\TargetingGroupDTO;
+use Flagship\Model\VariationGroupDTO;
use Flagship\Visitor\VisitorAbstract;
-use Flagship\Visitor\StrategyAbstract;
+use Flagship\Enum\TroubleshootingLabel;
+use Flagship\Utils\HttpClientInterface;
+use Flagship\Model\BucketingCampaignDTO;
+use Flagship\Model\BucketingVariationDTO;
+/**
+ * @phpstan-import-type FlagValue from \Flagship\Model\Types
+ */
class BucketingManager extends DecisionManagerAbstract
{
public const NB_MIN_CONTEXT_KEYS = 4;
@@ -32,11 +40,6 @@ class BucketingManager extends DecisionManagerAbstract
*/
private MurmurHash $murmurHash;
- /**
- * @var BucketingConfig
- */
- protected FlagshipConfig $config;
-
/**
* @var Troubleshooting
*/
@@ -45,21 +48,12 @@ class BucketingManager extends DecisionManagerAbstract
/**
* @return BucketingConfig
*/
- public function getConfig(): FlagshipConfig
+ public function getConfig(): BucketingConfig
{
+ /** @var BucketingConfig */
return $this->config;
}
- /**
- * @param FlagshipConfig $config
- * @return BucketingManager
- */
- public function setConfig(FlagshipConfig $config): static
- {
- $this->config = $config;
- return $this;
- }
-
/**
* @param HttpClientInterface $httpClient
* @param BucketingConfig $config
@@ -89,7 +83,7 @@ protected function sendContext(VisitorAbstract $visitor): void
/**
* @param string $visitorId
- * @return array
+ * @return array
*/
protected function getThirdPartySegment(string $visitorId): array
{
@@ -98,11 +92,15 @@ protected function getThirdPartySegment(string $visitorId): array
$context = [];
try {
$response = $this->httpClient->get($url);
+ /**
+ * @var array>
+ */
$content = $response->getBody();
foreach ($content as $item) {
$key = $item[self::PARTNER] . "::" . $item[self::SEGMENT];
$context[$key] = $item[self::VALUE];
}
+
$this->logDebugSprintf(
$this->config,
self::GET_THIRD_PARTY_SEGMENT,
@@ -142,7 +140,7 @@ protected function getThirdPartySegment(string $visitorId): array
}
/**
- * @return mixed|null
+ * @return BucketingDTO|null
*/
protected function getBucketingFile(): mixed
{
@@ -166,10 +164,15 @@ protected function getBucketingFile(): mixed
->setHttpResponseHeaders($response->getHeaders())
->setHttpResponseCode($response->getStatusCode())
->setHttpResponseTime($this->getNow() - $now)
- ->setVisitorId($this->getFlagshipInstanceId())
+ ->setVisitorId($this->getFlagshipInstanceId() ?? '')
->setConfig($this->getConfig());
$this->troubleshootingHit = $troubleshooting;
- return $response->getBody();
+ /** @var array|null */
+ $body = $response->getBody();
+ if (!is_array($body)) {
+ return null;
+ }
+ return BucketingDTO::fromArray($body);
} catch (Exception $exception) {
$this->logError($this->getConfig(), $exception->getMessage(), [FlagshipConstant::TAG => __FUNCTION__]);
$troubleshooting = new Troubleshooting();
@@ -182,7 +185,7 @@ protected function getBucketingFile(): mixed
->setTraffic(0)
->setLogLevel(LogLevel::ERROR)
->setConfig($this->getConfig())
- ->setVisitorId($this->getFlagshipInstanceId());
+ ->setVisitorId($this->getFlagshipInstanceId() ?? "");
$this->troubleshootingHit = $troubleshooting;
}
return null;
@@ -198,45 +201,33 @@ public function getCampaigns(VisitorAbstract $visitor): array|null
if (!$bucketingCampaigns) {
return [];
}
+
$this->troubleshootingData = null;
- if (isset($bucketingCampaigns[FlagshipField::ACCOUNT_SETTINGS][FlagshipField::TROUBLESHOOTING])) {
- $troubleshooting = $bucketingCampaigns[FlagshipField::ACCOUNT_SETTINGS][FlagshipField::TROUBLESHOOTING];
- $troubleshootingData = new TroubleshootingData();
+ $troubleshooting = $bucketingCampaigns->getAccountSettings()?->getTroubleshooting();
- if (isset($troubleshooting[FlagshipField::START_DATE])) {
- $startDate = new DateTime($troubleshooting[FlagshipField::START_DATE]);
- $troubleshootingData->setStartDate($startDate);
- }
- if (isset($troubleshooting[FlagshipField::END_DATE])) {
- $endDate = new DateTime($troubleshooting[FlagshipField::END_DATE]);
- $troubleshootingData->setEndDate($endDate);
- }
- if (isset($troubleshooting[FlagshipField::TRAFFIC])) {
- $troubleshootingData->setTraffic($troubleshooting[FlagshipField::TRAFFIC]);
- }
- if (isset($troubleshooting[FlagshipField::TIMEZONE])) {
- $troubleshootingData->setTimezone($troubleshooting[FlagshipField::TIMEZONE]);
- }
+ if ($troubleshooting) {
+ $troubleshootingData = $troubleshooting->toTroubleshootingData();
$this->troubleshootingData = $troubleshootingData;
- $this->getTrackingManager()->setTroubleshootingData($troubleshootingData);
- $this->getTrackingManager()->addTroubleshootingHit($this->troubleshootingHit);
+ if ($troubleshootingData) {
+ $this->getTrackingManager()->setTroubleshootingData($troubleshootingData);
+ $this->getTrackingManager()->addTroubleshootingHit($this->troubleshootingHit);
+ }
}
- if (isset($bucketingCampaigns[FlagshipField::FIELD_PANIC])) {
- $hasPanicMode = !empty($bucketingCampaigns[FlagshipField::FIELD_PANIC]);
- $this->setIsPanicMode($hasPanicMode);
+ if ($bucketingCampaigns->getPanic() !== null) {
+ $this->setIsPanicMode($bucketingCampaigns->getPanic());
return [];
}
$this->setIsPanicMode(false);
- if (!isset($bucketingCampaigns[FlagshipField::FIELD_CAMPAIGNS])) {
+ $campaigns = $bucketingCampaigns->getCampaigns();
+
+ if (empty($campaigns)) {
return [];
}
- $campaigns = $bucketingCampaigns[FlagshipField::FIELD_CAMPAIGNS];
-
$visitorCampaigns = [];
if ($this->getConfig()->getFetchThirdPartyData()) {
@@ -247,300 +238,276 @@ public function getCampaigns(VisitorAbstract $visitor): array|null
$this->sendContext($visitor);
foreach ($campaigns as $campaign) {
- if (!isset($campaign[FlagshipField::FIELD_VARIATION_GROUPS])) {
- continue;
- }
- $variationGroups = $campaign[FlagshipField::FIELD_VARIATION_GROUPS];
$currentCampaigns = $this->getVisitorCampaigns(
- $variationGroups,
- $campaign[FlagshipField::FIELD_ID],
$visitor,
- $campaign[FlagshipField::FIELD_CAMPAIGN_TYPE],
- $campaign[FlagshipField::FIELD_SLUG] ?? null,
- $campaign[FlagshipField::FIELD_NANE] ?? null
+ $campaign
);
- $visitorCampaigns = array_merge($visitorCampaigns, $currentCampaigns);
+ if ($currentCampaigns !== null) {
+ $visitorCampaigns[] = $currentCampaigns;
+ }
}
return $visitorCampaigns;
}
/**
- * @param array $variationGroups
- * @param string $campaignId
* @param VisitorAbstract $visitor
- * @param string $campaignType
- * @param ?string $slug
- * @param ?string $campaignName
- * @return array
+ * @param BucketingCampaignDTO $campaign
+ * @return CampaignDTO|null
*/
private function getVisitorCampaigns(
- array $variationGroups,
- string $campaignId,
VisitorAbstract $visitor,
- string $campaignType,
- ?string $slug,
- ?string $campaignName
- ): array {
- $visitorCampaigns = [];
+ BucketingCampaignDTO $campaign
+ ): CampaignDTO|null {
+ $variationGroups = $campaign->getVariationGroups();
+ $campaignId = $campaign->getId();
+ $campaignType = $campaign->getType();
foreach ($variationGroups as $variationGroup) {
- if ($this->isMatchTargeting($variationGroup, $visitor)) {
- $variations = $this->getVariation(
+ if ($this->checkVisitorMatchesTargeting($variationGroup, $visitor)) {
+ $variation = $this->getVariation(
$variationGroup,
$visitor
);
- $visitorCampaigns[] = [
- FlagshipField::FIELD_ID => $campaignId,
- FlagshipField::FIELD_NANE => $campaignName,
- FlagshipField::FIELD_SLUG => $slug,
- FlagshipField::FIELD_VARIATION_GROUP_ID => $variationGroup[FlagshipField::FIELD_ID],
- FlagshipField::FIELD_VARIATION_GROUP_NAME => $variationGroup[FlagshipField::FIELD_NANE] ?? null,
- FlagshipField::FIELD_VARIATION => $variations,
- FlagshipField::FIELD_CAMPAIGN_TYPE => $campaignType,
- ];
- break;
+ if (empty($variation)) {
+ return null;
+ }
+ $newCampaign = new CampaignDTO(
+ $campaignId,
+ $variationGroup->getId(),
+ $variation
+ );
+ $newCampaign->setName($campaign->getName());
+ $newCampaign->setSlug($campaign->getSlug());
+ $newCampaign->setType($campaignType);
+ $newCampaign->setVariationGroupName(
+ $variationGroup->getName()
+ );
+ return $newCampaign;
}
}
- return $visitorCampaigns;
+ return null;
}
/**
* @param string $variationGroupId
* @param VisitorAbstract $visitor
- * @return mixed|null
+ * @return string|null
*/
- private function getVisitorAssignmentsHistory(string $variationGroupId, VisitorAbstract $visitor): mixed
+ private function getVisitorAssignmentsHistory(string $variationGroupId, VisitorAbstract $visitor): string|null
{
- return $visitor->visitorCache[StrategyAbstract::DATA][StrategyAbstract::ASSIGNMENTS_HISTORY][$variationGroupId] ?? null;
+ $assignmentsHistory = $visitor->visitorCache?->getData()->getAssignmentsHistory();
+ return $assignmentsHistory[$variationGroupId] ?? null;
}
- private function findVariationById(array $variations, $key)
+ /**
+ *
+ * @param BucketingVariationDTO[] $variations
+ * @param string $assignmentsVariationId
+ */
+ private function findVariationById(array $variations, string $assignmentsVariationId): BucketingVariationDTO|null
{
- foreach ($variations as $item) {
- if ($item[FlagshipField::FIELD_ID] === $key) {
- return $item;
- }
- }
- return null;
+ return $this->array_find(
+ $variations,
+ fn(BucketingVariationDTO $variation) => $variation->getId() === $assignmentsVariationId
+ );
}
/**
*
- * @param array $variationGroup
+ * @param VariationGroupDTO $variationGroup
* @param VisitorAbstract $visitor
- * @return array
+ * @return VariationDTO|null
*/
- private function getVariation(array $variationGroup, VisitorAbstract $visitor): array
+ private function getVariation(VariationGroupDTO $variationGroup, VisitorAbstract $visitor): VariationDTO|null
{
$visitorVariation = [];
- if (!isset($variationGroup[FlagshipField::FIELD_ID])) {
- return $visitorVariation;
+ if (empty($variationGroup->getId())) {
+ return null;
}
- $groupVariationId = $variationGroup[FlagshipField::FIELD_ID];
+
+ $groupVariationId = $variationGroup->getId();
$hash = $this->murmurHash->murmurHash3Int32($groupVariationId . $visitor->getVisitorId());
$hashAllocation = $hash % 100;
- $variations = $variationGroup[FlagshipField::FIELD_VARIATIONS];
+ $variations = $variationGroup->getVariations();
$totalAllocation = 0;
foreach ($variations as $variation) {
- if (!isset($variation[FlagshipField::FIELD_ALLOCATION])) {
+ if ($variation->getAllocation() === null) {
continue;
}
$assignmentsVariationId = $this->getVisitorAssignmentsHistory($groupVariationId, $visitor);
if ($assignmentsVariationId) {
- $newVariation = $this->findVariationById($variations, $assignmentsVariationId);
- if (!$newVariation) {
+ $assignedVariation = $this->findVariationById($variations, $assignmentsVariationId);
+ if (!$assignedVariation) {
continue;
}
- $visitorVariation = [
- FlagshipField::FIELD_ID => $newVariation[FlagshipField::FIELD_ID],
- FlagshipField::FIELD_MODIFICATIONS => $newVariation[FlagshipField::FIELD_MODIFICATIONS],
- FlagshipField::FIELD_REFERENCE => !empty($newVariation[FlagshipField::FIELD_REFERENCE]),
- FlagshipField::FIELD_NANE => $newVariation[FlagshipField::FIELD_NANE] ?? null,
- ];
- break;
+ $visitorVariation = new VariationDTO(
+ $assignedVariation->getId(),
+ $variation->getModifications()
+ );
+ $visitorVariation->setReference($assignedVariation->getReference());
+ $visitorVariation->setName($assignedVariation->getName());
+ return $visitorVariation;
}
- if (!isset($variation[FlagshipField::FIELD_ALLOCATION]) || $variation[FlagshipField::FIELD_ALLOCATION] <= 0) {
+ if ($variation->getAllocation() <= 0) {
continue;
}
- $totalAllocation += $variation[FlagshipField::FIELD_ALLOCATION];
+ $totalAllocation += $variation->getAllocation();
if ($hashAllocation < $totalAllocation) {
- $visitorVariation = [
- FlagshipField::FIELD_ID => $variation[FlagshipField::FIELD_ID],
- FlagshipField::FIELD_MODIFICATIONS => $variation[FlagshipField::FIELD_MODIFICATIONS],
- FlagshipField::FIELD_REFERENCE => !empty($variation[FlagshipField::FIELD_REFERENCE]),
- FlagshipField::FIELD_NANE => $variation[FlagshipField::FIELD_NANE] ?? null,
- ];
- break;
+ $visitorVariation = new VariationDTO(
+ $variation->getId(),
+ $variation->getModifications()
+ );
+ $visitorVariation->setReference($variation->getReference());
+ $visitorVariation->setName($variation->getName());
+
+ return $visitorVariation;
}
}
- return $visitorVariation;
+ return null;
}
/**
- * @param array $variationGroup
+ * @param VariationGroupDTO $variationGroup
* @param VisitorAbstract $visitor
* @return bool
*/
- private function isMatchTargeting(array $variationGroup, VisitorAbstract $visitor): bool
+ private function checkVisitorMatchesTargeting(VariationGroupDTO $variationGroup, VisitorAbstract $visitor): bool
{
- if (!isset($variationGroup[FlagshipField::FIELD_TARGETING])) {
+ $targetingGroups = $variationGroup->getTargeting()->getTargetingGroups();
+ if (empty($targetingGroups)) {
return false;
}
- $targeting = $variationGroup[FlagshipField::FIELD_TARGETING];
+ // OR logic: at least one targeting group must match
+ return $this->array_any(
+ $targetingGroups,
+ fn(TargetingGroupDTO $targetingGroup)
+ => $this->checkAllTargetingRulesMatch(
+ $targetingGroup->getTargetings(),
+ $visitor
+ )
+ );
+ }
- if (!isset($targeting[FlagshipField::FIELD_TARGETING_GROUPS])) {
+ /**
+ *
+ * @param TargetingsDTO[] $targetings
+ * @return bool
+ */
+ private function checkAllTargetingRulesMatch(array $targetings, VisitorAbstract $visitor): bool
+ {
+ if (empty($targetings)) {
return false;
}
- $targetingGroups = $targeting[FlagshipField::FIELD_TARGETING_GROUPS];
-
- foreach ($targetingGroups as $targetingGroup) {
- if (!isset($targetingGroup[FlagshipField::FIELD_TARGETINGS])) {
- continue;
- }
-
- $innerTargetings = $targetingGroup[FlagshipField::FIELD_TARGETINGS];
-
- if ($this->checkAndTargeting($innerTargetings, $visitor)) {
- return true;
- }
- }
-
- return false;
+ // AND logic: ALL targeting rules must match
+ return $this->array_all(
+ $targetings,
+ fn(TargetingsDTO $targeting)
+ => $this->matchesTargetingCriteria(
+ $targeting,
+ $visitor
+ )
+ );
}
- /**
- * @param array $innerTargetings
- * @param VisitorAbstract $visitor
- * @return bool
- */
- private function checkAndTargeting(array $innerTargetings, VisitorAbstract $visitor): bool
+
+ private function matchesArrayTargeting(TargetingsDTO $targeting, VisitorAbstract $visitor): bool
{
- $isMatching = false;
- foreach ($innerTargetings as $innerTargeting) {
- $key = $innerTargeting['key'];
- $operator = $innerTargeting["operator"];
- $targetingValue = $innerTargeting["value"];
- $visitorContext = $visitor->getContext();
-
- if ($operator === "EXISTS") {
- if (array_key_exists($key, $visitorContext)) {
- $isMatching = true;
- continue;
- }
- $isMatching = false;
- break;
- }
- if ($operator === "NOT_EXISTS") {
- if (array_key_exists($key, $visitorContext)) {
- $isMatching = false;
- break;
- }
- $isMatching = true;
- continue;
- }
+ /**
+ * @var array
+ */
+ $targetingValue = $targeting->getValue();
+
+ $notOperator = in_array(
+ $targeting->getOperator()->value,
+ [
+ TargetingOperator::NOT_CONTAINS->value,
+ TargetingOperator::NOT_EQUALS->value,
+ ]
+ );
+
+ $cloneTargeting = clone $targeting;
+
+ /**
+ *
+ * @param FlagValue $value
+ * @return void
+ */
+ $checkOperator = function (mixed $value) use ($cloneTargeting, $visitor): bool {
+ $cloneTargeting->setValue($value);
+ return $this->matchesTargetingCriteria($cloneTargeting, $visitor);
+ };
- switch ($key) {
- case "fs_all_users":
- $isMatching = true;
- continue 2;
- case "fs_users":
- $contextValue = $visitor->getVisitorId();
- break;
- default:
- if (!isset($visitorContext[$key])) {
- $isMatching = false;
- break 2;
- }
- $contextValue = $visitorContext[$key];
- break;
- }
- $isMatching = $this->testOperator($operator, $contextValue, $targetingValue);
- if (!$isMatching) {
- break;
- }
+ if ($notOperator) {
+ return $this->array_all(
+ $targetingValue,
+ $checkOperator(...)
+ );
}
- return $isMatching;
+ return $this->array_any(
+ $targetingValue,
+ $checkOperator(...)
+ );
}
- /**
- * @param $operator
- * @return bool
- */
- private function isANDListOperator($operator): bool
+ private function matchesTargetingCriteria(TargetingsDTO $targeting, VisitorAbstract $visitor): bool
{
- return in_array($operator, ['NOT_EQUALS', 'NOT_CONTAINS']);
- }
+ if ($targeting->getKey() === FlagshipField::FS_ALL_USERS) {
+ return true;
+ }
- /**
- * @param string $operator
- * @param mixed $contextValue
- * @param array $targetingValue
- * @param bool $initialCheck
- * @return bool|mixed
- */
- private function testListOperatorLoop(
- string $operator,
- mixed $contextValue,
- array $targetingValue,
- bool $initialCheck
- ): mixed {
- $check = $initialCheck;
- foreach ($targetingValue as $value) {
- $check = $this->testOperator($operator, $contextValue, $value);
- if ($check !== $initialCheck) {
- break;
- }
+ if (is_array($targeting->getValue())) {
+ return $this->matchesArrayTargeting($targeting, $visitor);
}
- return $check;
- }
- /**
- * @param string $operator
- * @param mixed $contextValue
- * @param array $targetingValue
- * @return bool
- */
- private function testListOperator(string $operator, mixed $contextValue, array $targetingValue): bool
- {
- $andOperator = $this->isANDListOperator($operator);
- if ($andOperator) {
- $check = $this->testListOperatorLoop($operator, $contextValue, $targetingValue, true);
- } else {
- $check = $this->testListOperatorLoop($operator, $contextValue, $targetingValue, false);
+ $visitorValue = $targeting->getKey() === FlagshipField::FS_USERS
+ ? $visitor->getVisitorId()
+ : $visitor->getContext()[$targeting->getKey()] ?? null;
+
+ if ($visitorValue === null) {
+ return $targeting->getOperator() === TargetingOperator::NOT_EXISTS;
}
- return $check;
+
+ return $this->evaluateOperator(
+ $targeting->getOperator(),
+ $visitorValue,
+ $targeting->getValue()
+ );
}
/**
- * @param string $operator
- * @param mixed $contextValue
+ *
+ * @param TargetingOperator $operator
+ * @param scalar $visitorValue
* @param mixed $targetingValue
* @return bool
*/
- private function testOperator(string $operator, mixed $contextValue, mixed $targetingValue): bool
- {
+ private function evaluateOperator(
+ TargetingOperator $operator,
+ float|int|string|bool|null $visitorValue,
+ mixed $targetingValue
+ ): bool {
+
+ $targetingValueStr = is_scalar($targetingValue) ? strval($targetingValue) : '';
- if (is_array($targetingValue)) {
- return $this->testListOperator($operator, $contextValue, $targetingValue);
- }
return match ($operator) {
- "EQUALS" => $contextValue === $targetingValue,
- "NOT_EQUALS" => $contextValue !== $targetingValue,
- "CONTAINS" => str_contains(strval($contextValue), strval($targetingValue)),
- "NOT_CONTAINS" => !str_contains(strval($contextValue), strval($targetingValue)),
- "GREATER_THAN" => $contextValue > $targetingValue,
- "LOWER_THAN" => $contextValue < $targetingValue,
- "GREATER_THAN_OR_EQUALS" => $contextValue >= $targetingValue,
- "LOWER_THAN_OR_EQUALS" => $contextValue <= $targetingValue,
- "STARTS_WITH" => (bool)preg_match("/^$targetingValue/i", $contextValue),
- "ENDS_WITH" => (bool)preg_match("/$targetingValue$/i", $contextValue),
+ TargetingOperator::EQUALS => $visitorValue === $targetingValue,
+ TargetingOperator::NOT_EQUALS => $visitorValue !== $targetingValue,
+ TargetingOperator::EXISTS => $visitorValue !== null,
+ TargetingOperator::CONTAINS => str_contains(strval($visitorValue), $targetingValueStr),
+ TargetingOperator::NOT_CONTAINS => !str_contains(strval($visitorValue), $targetingValueStr),
+ TargetingOperator::GREATER_THAN => $visitorValue > $targetingValue,
+ TargetingOperator::LOWER_THAN => $visitorValue < $targetingValue,
+ TargetingOperator::GREATER_THAN_OR_EQUALS => $visitorValue >= $targetingValue,
+ TargetingOperator::LOWER_THAN_OR_EQUALS => $visitorValue <= $targetingValue,
+ TargetingOperator::STARTS_WITH => (bool)preg_match("/^" . preg_quote($targetingValueStr, '/') . "/i", strval($visitorValue)),
+ TargetingOperator::ENDS_WITH => (bool)preg_match("/" . preg_quote($targetingValueStr, '/') . "$/i", strval($visitorValue)),
default => false,
};
}
diff --git a/src/Decision/DecisionManagerAbstract.php b/src/Decision/DecisionManagerAbstract.php
index 4fcc82d6..697e41d5 100644
--- a/src/Decision/DecisionManagerAbstract.php
+++ b/src/Decision/DecisionManagerAbstract.php
@@ -6,6 +6,7 @@
use Flagship\Config\FlagshipConfig;
use Flagship\Enum\FlagshipField;
use Flagship\Enum\FSSdkStatus;
+use Flagship\Model\CampaignDTO;
use Flagship\Model\FlagDTO;
use Flagship\Model\TroubleshootingData;
use Flagship\Traits\BuildApiTrait;
@@ -29,9 +30,9 @@ abstract class DecisionManagerAbstract implements DecisionManagerInterface
*/
protected HttpClientInterface $httpClient;
/**
- * @var callable
+ * @var ?callable
*/
- private $statusChangedCallback;
+ private $statusChangedCallback = null;
/**
* @var FlagshipConfig
@@ -76,7 +77,7 @@ public function getTrackingManager(): TrackingManagerInterface
* @param TrackingManagerInterface $trackingManager
* @return DecisionManagerAbstract
*/
- public function setTrackingManager(TrackingManagerInterface $trackingManager): static
+ public function setTrackingManager(TrackingManagerInterface $trackingManager): self
{
$this->trackingManager = $trackingManager;
return $this;
@@ -94,7 +95,7 @@ public function getFlagshipInstanceId(): ?string
* @param ?string $flagshipInstanceId
* @return DecisionManagerAbstract
*/
- public function setFlagshipInstanceId(?string $flagshipInstanceId): static
+ public function setFlagshipInstanceId(?string $flagshipInstanceId): self
{
$this->flagshipInstanceId = $flagshipInstanceId;
return $this;
@@ -119,7 +120,7 @@ public function getIsPanicMode(): bool
* @param bool $isPanicMode
* @return DecisionManagerAbstract
*/
- public function setIsPanicMode(bool $isPanicMode): static
+ public function setIsPanicMode(bool $isPanicMode): self
{
$status = $isPanicMode ? FSSdkStatus::SDK_PANIC : FSSdkStatus::SDK_INITIALIZED;
$this->updateFlagshipStatus($status);
@@ -133,11 +134,9 @@ public function setIsPanicMode(bool $isPanicMode): static
* @param callable $statusChangedCallback callback
* @return DecisionManagerAbstract
*/
- public function setStatusChangedCallback(callable $statusChangedCallback): static
+ public function setStatusChangedCallback(callable $statusChangedCallback): self
{
- if (is_callable($statusChangedCallback)) {
- $this->statusChangedCallback = $statusChangedCallback;
- }
+ $this->statusChangedCallback = $statusChangedCallback;
return $this;
}
@@ -153,7 +152,7 @@ public function getConfig(): FlagshipConfig
* @param FlagshipConfig $config
* @return DecisionManagerAbstract
*/
- public function setConfig(FlagshipConfig $config): static
+ public function setConfig(FlagshipConfig $config): self
{
$this->config = $config;
return $this;
@@ -171,129 +170,94 @@ protected function updateFlagshipStatus(FSSdkStatus $newStatus): void
}
}
- /**
- * @param FlagDTO[] $flags
- * @param string $key
- * @return FlagDTO|null
- */
- protected function checkFlagKeyExist(array $flags, string $key): ?FlagDTO
- {
- foreach ($flags as $flag) {
- if ($flag->getKey() === $key) {
- return $flag;
- }
- }
- return null;
- }
+
/**
* Return an array of flags from all campaigns
*
- * @param $campaigns
+ * @param CampaignDTO[] $campaigns
* @return FlagDTO[] Return an array of flags
*/
- public function getFlagsData($campaigns): array
+ public function getFlagsData(array $campaigns): array
{
+ /** @var array $existingFlags*/
+ $existingFlags = [];
- $flags = [];
foreach ($campaigns as $campaign) {
- if (
- !isset($campaign[FlagshipField::FIELD_VARIATION])
- || !isset($campaign[FlagshipField::FIELD_VARIATION][FlagshipField::FIELD_MODIFICATIONS])
- || !isset($campaign[FlagshipField::FIELD_VARIATION][FlagshipField::FIELD_MODIFICATIONS]
- [FlagshipField::FIELD_VALUE])
- ) {
+
+ if (empty($campaign->getVariationGroupId())) {
continue;
}
- $flagsValue = $campaign[FlagshipField::FIELD_VARIATION]
- [FlagshipField::FIELD_MODIFICATIONS][FlagshipField::FIELD_VALUE];
-
- $flags = $this->getFlagsValue($flagsValue, $campaign, $flags);
+ $existingFlags = $this->extractFlagsFromCampaign($campaign, $existingFlags);
}
- return $flags;
+ return array_values($existingFlags);
}
/**
* Return modification of a campaign
*
- * @param array $flagsValue
- * @param array $campaign
- * @param array $flagsDTO
- * @return array
+ * @param CampaignDTO $campaign
+ * @param array $existingFlags
+ * @return array
*/
- protected function getFlagsValue(array $flagsValue, array $campaign, array $flagsDTO): array
+ protected function extractFlagsFromCampaign(CampaignDTO $campaign, array $existingFlags): array
{
- $localFlags = [];
+ $flagsValue = $campaign->getVariation()->getModifications()->getValue();
+
foreach ($flagsValue as $key => $flagValue) {
- if (!$this->isKeyValid($key)) {
+ if (!is_string($key) || empty($key)) {
continue;
}
- //check if the key is already used
- $flagDTO = $this->checkFlagKeyExist($flagsDTO, $key);
- $isKeyUsed = true;
-
- if (is_null($flagDTO)) {
- $flagDTO = new FlagDTO();
- $isKeyUsed = false;
- }
+ $flagDTO = $existingFlags[$key] ?? new FlagDTO();
$flagDTO->setKey($key);
$flagDTO->setValue($flagValue);
- if (isset($campaign[FlagshipField::FIELD_ID])) {
- $flagDTO->setCampaignId($campaign[FlagshipField::FIELD_ID]);
- }
+ $flagDTO->setCampaignId($campaign->getId());
- if (isset($campaign[FlagshipField::FIELD_CAMPAIGNS_NAME])) {
- $flagDTO->setCampaignName($campaign[FlagshipField::FIELD_CAMPAIGNS_NAME]);
+ if (!empty($campaign->getName())) {
+ $flagDTO->setCampaignName($campaign->getName());
}
- if (isset($campaign[FlagshipField::FIELD_CAMPAIGN_TYPE])) {
- $flagDTO->setCampaignType($campaign[FlagshipField::FIELD_CAMPAIGN_TYPE]);
+ if (!empty($campaign->getType())) {
+ $flagDTO->setCampaignType($campaign->getType());
}
- if (isset($campaign[FlagshipField::FIELD_VARIATION_GROUP_ID])) {
- $flagDTO->setVariationGroupId($campaign[FlagshipField::FIELD_VARIATION_GROUP_ID]);
+ if (!empty($campaign->getVariationGroupId())) {
+ $flagDTO->setVariationGroupId($campaign->getVariationGroupId());
}
- if (isset($campaign[FlagshipField::FIELD_VARIATION_GROUP_NAME])) {
- $flagDTO->setVariationGroupName($campaign[FlagshipField::FIELD_VARIATION_GROUP_NAME]);
+
+ if (!empty($campaign->getVariationGroupName())) {
+ $flagDTO->setVariationGroupName($campaign->getVariationGroupName());
}
- if (isset($campaign[FlagshipField::FIELD_VARIATION][FlagshipField::FIELD_ID])) {
+ if (!empty($campaign->getVariation()->getId())) {
$flagDTO->setVariationId(
- $campaign[FlagshipField::FIELD_VARIATION]
- [FlagshipField::FIELD_ID]
+ $campaign->getVariation()->getId()
);
}
- if (isset($campaign[FlagshipField::FIELD_VARIATION][FlagshipField::FIELD_NANE])) {
- $flagDTO->setVariationName($campaign[FlagshipField::FIELD_VARIATION][FlagshipField::FIELD_NANE]);
- }
-
- if (isset($campaign[FlagshipField::FIELD_VARIATION][FlagshipField::FIELD_REFERENCE])) {
- $flagDTO->setIsReference(
- $campaign[FlagshipField::FIELD_VARIATION]
- [FlagshipField::FIELD_REFERENCE]
- );
+ if (!empty($campaign->getVariation()->getName())) {
+ $flagDTO->setVariationName($campaign->getVariation()->getName());
}
- if (isset($campaign[FlagshipField::FIELD_SLUG])) {
- $flagDTO->setSlug($campaign[FlagshipField::FIELD_SLUG]);
- }
+ $flagDTO->setIsReference(
+ $campaign->getVariation()->getReference() ?? false
+ );
- if (!$isKeyUsed) {
- $localFlags[] = $flagDTO;
+ if (!empty($campaign->getSlug())) {
+ $flagDTO->setSlug($campaign->getSlug());
}
+ $existingFlags[$key] = $flagDTO;
}
- return array_merge($flagsDTO, $localFlags);
+ return $existingFlags;
}
/**
- * @param VisitorAbstract $visitor
- * @return array|null
+ * @inheritDoc
*/
abstract public function getCampaigns(VisitorAbstract $visitor): array|null;
@@ -303,6 +267,9 @@ abstract public function getCampaigns(VisitorAbstract $visitor): array|null;
public function getCampaignFlags(VisitorAbstract $visitor): array
{
$campaigns = $this->getCampaigns($visitor);
+ if (is_null($campaigns) || empty($campaigns)) {
+ return [];
+ }
return $this->getFlagsData($campaigns);
}
diff --git a/src/Decision/DecisionManagerInterface.php b/src/Decision/DecisionManagerInterface.php
index e280d505..c376375a 100644
--- a/src/Decision/DecisionManagerInterface.php
+++ b/src/Decision/DecisionManagerInterface.php
@@ -3,8 +3,9 @@
namespace Flagship\Decision;
use Flagship\Model\FlagDTO;
-use Flagship\Model\TroubleshootingData;
+use Flagship\Model\CampaignDTO;
use Flagship\Visitor\VisitorAbstract;
+use Flagship\Model\TroubleshootingData;
interface DecisionManagerInterface
{
@@ -18,15 +19,15 @@ public function getCampaignFlags(VisitorAbstract $visitor): array;
/**
* @param VisitorAbstract $visitor
- * @return array|null
+ * @return CampaignDTO[]|null
*/
public function getCampaigns(VisitorAbstract $visitor): array|null;
/**
- * @param $campaigns
+ * @param CampaignDTO[] $campaigns
* @return FlagDTO[]
*/
- public function getFlagsData($campaigns): array;
+ public function getFlagsData(array $campaigns): array;
public function getTroubleshootingData(): ?TroubleshootingData;
}
diff --git a/src/Decision/Types.php b/src/Decision/Types.php
new file mode 100644
index 00000000..05a31624
--- /dev/null
+++ b/src/Decision/Types.php
@@ -0,0 +1,15 @@
+ */
private static array $predefinedContext = [
- self::DEVICE_LOCALE => "string",
- self::DEVICE_TYPE => "string",
- self::DEVICE_MODEL => "string",
- self::LOCATION_CITY => "string",
- self::LOCATION_REGION => "string",
- self::LOCATION_COUNTRY => "string",
- self::LOCATION_LAT => "float",
- self::LOCATION_LONG => "float",
- self::IP => "string",
- self::OS_NAME => "string",
- self::OS_VERSION_NAME => "string",
- self::OS_VERSION_CODE => "float",
- self::CARRIER_NAME => "string",
- self::INTERNET_CONNECTION => "string",
- self::APP_VERSION_NAME => "string",
- self::APP_VERSION_CODE => "float",
- self::INTERFACE_NAME => "string",
- self::FLAGSHIP_CLIENT => "string",
- self::FLAGSHIP_VERSION => "string",
- self::FLAGSHIP_VISITOR => "string",
- ];
+ self::DEVICE_LOCALE => "string",
+ self::DEVICE_TYPE => "string",
+ self::DEVICE_MODEL => "string",
+ self::LOCATION_CITY => "string",
+ self::LOCATION_REGION => "string",
+ self::LOCATION_COUNTRY => "string",
+ self::LOCATION_LAT => "float",
+ self::LOCATION_LONG => "float",
+ self::IP => "string",
+ self::OS_NAME => "string",
+ self::OS_VERSION_NAME => "string",
+ self::OS_VERSION_CODE => "float",
+ self::CARRIER_NAME => "string",
+ self::INTERNET_CONNECTION => "string",
+ self::APP_VERSION_NAME => "string",
+ self::APP_VERSION_CODE => "float",
+ self::INTERFACE_NAME => "string",
+ self::FLAGSHIP_CLIENT => "string",
+ self::FLAGSHIP_VERSION => "string",
+ self::FLAGSHIP_VISITOR => "string",
+ ];
/**
* @param $context string
diff --git a/src/Enum/FlagshipField.php b/src/Enum/FlagshipField.php
index 426a4a0e..5a80f120 100644
--- a/src/Enum/FlagshipField.php
+++ b/src/Enum/FlagshipField.php
@@ -48,4 +48,17 @@ class FlagshipField
public const TRAFFIC = "traffic";
public const FIELD_CAMPAIGN_NAME = "campaignName";
public const FIELD_VARIATION_NAME = "variationName";
+
+ public const FIELD_ACTIVATED = "activated";
+
+ public const FIELD_FLAGS = "flags";
+
+ public const FIELD_OPERATOR = 'operator';
+
+ public const FS_ALL_USERS = 'fs_all_users';
+ public const FS_USERS = 'fs_users';
+
+ public const ENABLED_XPC = 'enabledXPC';
+ public const EAI_COLLECT_ENABLED = 'eaiCollectEnabled';
+ public const EAI_ACTIVATION_ENABLED = 'eaiActivationEnabled';
}
diff --git a/src/Enum/TargetingOperator.php b/src/Enum/TargetingOperator.php
new file mode 100644
index 00000000..ef6e9702
--- /dev/null
+++ b/src/Enum/TargetingOperator.php
@@ -0,0 +1,22 @@
+|bool|float|int|string|null $defaultValue
*/
private string|array|bool|int|null|float $defaultValue;
@@ -36,7 +42,7 @@ class FSFlag implements FSFlagInterface
*/
public function __construct(
string $key,
- VisitorAbstract $visitorDelegate = null
+ ?VisitorAbstract $visitorDelegate = null
) {
$this->key = $key;
$this->visitorDelegate = $visitorDelegate;
@@ -65,6 +71,9 @@ protected function findFlagDTO(string $key): ?FlagDTO
/**
* @inheritDoc
+ * @phpstan-template T of scalar|array|null
+ * @param T $defaultValue
+ * @return T
*/
public function getValue(
float|array|bool|int|string|null $defaultValue,
diff --git a/src/Flag/FSFlagCollection.php b/src/Flag/FSFlagCollection.php
index eda75305..3ccb77bb 100644
--- a/src/Flag/FSFlagCollection.php
+++ b/src/Flag/FSFlagCollection.php
@@ -27,13 +27,13 @@ class FSFlagCollection implements FSFlagCollectionInterface
*/
private array $flags;
- private int $index = 0;
+ private int $index = 0;
/**
* @param VisitorAbstract|null $visitor
* @param array $flags
*/
- public function __construct(VisitorAbstract $visitor = null, array $flags = [])
+ public function __construct(?VisitorAbstract $visitor = null, array $flags = [])
{
$this->visitor = $visitor;
$this->flags = $flags;
@@ -56,17 +56,20 @@ public function getSize(): int
return count($this->keys);
}
+ /**
+ * @inheritDoc
+ */
public function get(string $key): FSFlagInterface
{
if (!isset($this->flags[$key])) {
$this->logWarningSprintf(
- $this->visitor->getConfig(),
+ $this->visitor?->getConfig(),
FlagshipConstant::GET_FLAG,
FlagshipConstant::GET_FLAG_NOT_FOUND,
[
- $this->visitor->getVisitorId(),
- $key,
- ]
+ $this->visitor?->getVisitorId(),
+ $key,
+ ]
);
return new FSFlag($key);
}
@@ -124,20 +127,21 @@ public function toJSON(): string
foreach ($this->flags as $key => $flag) {
$metadata = $flag->getMetadata();
$serializedData[] = [
- 'key' => $key,
- 'campaignId' => $metadata->getCampaignId(),
- 'campaignName' => $metadata->getCampaignName(),
- 'variationGroupId' => $metadata->getVariationGroupId(),
- 'variationGroupName' => $metadata->getVariationGroupName(),
- 'variationId' => $metadata->getVariationId(),
- 'variationName' => $metadata->getVariationName(),
- 'isReference' => $metadata->isReference(),
- 'campaignType' => $metadata->getCampaignType(),
- 'slug' => $metadata->getSlug(),
- 'hex' => $this->valueToHex(['v' => $flag->getValue(null, false)]),
- ];
+ 'key' => $key,
+ 'campaignId' => $metadata->getCampaignId(),
+ 'campaignName' => $metadata->getCampaignName(),
+ 'variationGroupId' => $metadata->getVariationGroupId(),
+ 'variationGroupName' => $metadata->getVariationGroupName(),
+ 'variationId' => $metadata->getVariationId(),
+ 'variationName' => $metadata->getVariationName(),
+ 'isReference' => $metadata->isReference(),
+ 'campaignType' => $metadata->getCampaignType(),
+ 'slug' => $metadata->getSlug(),
+ 'hex' => $this->valueToHex(['v' => $flag->getValue(null, false)]),
+ ];
}
- return json_encode($serializedData);
+ $json = json_encode($serializedData);
+ return $json !== false ? $json : '[]';
}
/**
diff --git a/src/Flag/FSFlagCollectionInterface.php b/src/Flag/FSFlagCollectionInterface.php
index 635c3b92..5f47288b 100644
--- a/src/Flag/FSFlagCollectionInterface.php
+++ b/src/Flag/FSFlagCollectionInterface.php
@@ -4,6 +4,9 @@
use Iterator;
+/**
+ * @extends Iterator
+ */
interface FSFlagCollectionInterface extends Iterator
{
/**
@@ -74,5 +77,5 @@ public function toJSON(): string;
* 2. string $key - The key of the current flag.
* 3. FSFlagCollectionInterface $collection - The collection the flag belongs to.
*/
- public function each(callable $callbackFn);
+ public function each(callable $callbackFn):void;
}
diff --git a/src/Flag/FSFlagInterface.php b/src/Flag/FSFlagInterface.php
index 0453e81c..c0102896 100644
--- a/src/Flag/FSFlagInterface.php
+++ b/src/Flag/FSFlagInterface.php
@@ -4,15 +4,17 @@
use Flagship\Enum\FSFlagStatus;
+
interface FSFlagInterface
{
- /**
- * Returns the value from the assigned campaign variation or the Flag default value if the Flag does not exist,
- * or if types are different.
- * @param array|bool|string|numeric|null $defaultValue
- * @param boolean $visitorExposed
- * @return bool|numeric|string|array|null
- */
+ /**
+ * Returns the value from the assigned campaign variation or the Flag default value if the Flag does not exist,
+ * or if types are different.
+ * @phpstan-template T of scalar|array|null
+ * @param T $defaultValue
+ * @param boolean $visitorExposed
+ * @return T
+ */
public function getValue(
float|array|bool|int|string|null $defaultValue,
bool $visitorExposed = true
diff --git a/src/Flagship.php b/src/Flagship.php
index 8502875d..fbddf482 100644
--- a/src/Flagship.php
+++ b/src/Flagship.php
@@ -4,7 +4,7 @@
use Exception;
use Flagship\Traits\Guid;
-use Flagship\Utils\FlagshipLogManager8;
+use Flagship\Utils\FlagshipLogManager;
use Flagship\Utils\HttpClient;
use Psr\Log\LoggerInterface;
use Flagship\Traits\LogTrait;
@@ -44,10 +44,11 @@ class Flagship
* @var Container
*/
private Container $container;
+
/**
* @var FlagshipConfig|null
*/
- private ?FlagshipConfig $config;
+ private ?FlagshipConfig $config = null;
/**
* @var ?ConfigManager
@@ -128,9 +129,9 @@ public static function start(string $envId, string $apiKey, ?FlagshipConfig $con
$decisionManager = $container->get(
BucketingManager::class,
[
- $httpClient,
- $config,
- $murmurHash,
+ $httpClient,
+ $config,
+ $murmurHash,
]
);
} else {
@@ -144,9 +145,9 @@ public static function start(string $envId, string $apiKey, ?FlagshipConfig $con
$trackingManager = $container->get(
TrackingManager::class,
[
- $config,
- $httpClient,
- $flagship->flagshipInstanceId,
+ $config,
+ $httpClient,
+ $flagship->flagshipInstanceId,
]
);
@@ -200,7 +201,7 @@ private function containerInitialization(): Container
);
$newContainer->bind(
LoggerInterface::class,
- FlagshipLogManager8::class
+ FlagshipLogManager::class
);
return $newContainer;
@@ -218,7 +219,7 @@ protected function getConfigManager(): ?ConfigManager
* @param ConfigManager $configManager
* @return Flagship
*/
- protected function setConfigManager(ConfigManager $configManager): static
+ protected function setConfigManager(ConfigManager $configManager): self
{
$this->configManager = $configManager;
return $this;
@@ -228,9 +229,9 @@ protected function setConfigManager(ConfigManager $configManager): static
/**
* Return the current config set by the customer and used by the SDK.
*
- * @return FlagshipConfig
+ * @return ?FlagshipConfig
*/
- public static function getConfig(): FlagshipConfig
+ public static function getConfig(): ?FlagshipConfig
{
return self::getInstance()->config;
}
@@ -239,7 +240,7 @@ public static function getConfig(): FlagshipConfig
* @param FlagshipConfig $config
* @return Flagship
*/
- protected function setConfig(FlagshipConfig $config): static
+ protected function setConfig(FlagshipConfig $config): self
{
$this->config = $config;
return $this;
@@ -260,7 +261,7 @@ public static function getStatus(): FSSdkStatus
* @param FSSdkStatus $status FSSdkStatus
* @return Flagship
*/
- public function setStatus(FSSdkStatus $status): static
+ public function setStatus(FSSdkStatus $status): self
{
$onSdkStatusChanged = $this->config?->getOnSdkStatusChanged();
if ($onSdkStatusChanged && $this->status !== $status) {
@@ -291,6 +292,48 @@ public static function close(): void
$instance->getConfigManager()?->getTrackingManager()?->sendBatch();
}
+ private function defaultConfigManagerInitialization(?ConfigManager $configManager): ConfigManager
+ {
+ if ($configManager) {
+ return $configManager;
+ }
+ $instance = self::getInstance();
+ $container = $instance->getContainer();
+
+ $instance->flagshipInstanceId = $instance->newGuid();
+
+ $config = $container->get(DecisionApiConfig::class, ['', '']);
+ $logManager = $container->get(LoggerInterface::class);
+ $config->setLogManager($logManager);
+
+ $config = $container->get(DecisionApiConfig::class, ['', '']);
+ $httpClient = $container->get(HttpClientInterface::class);
+ $decisionManager = $container->get(
+ ApiManager::class,
+ [$httpClient, $config]
+ );
+ $trackingManager = $container->get(
+ TrackingManager::class,
+ [
+ $config,
+ $httpClient,
+ $instance->flagshipInstanceId,
+ ]
+ );
+ $configManager = $container->get(
+ ConfigManager::class,
+ [$config, $decisionManager, $trackingManager],
+ true
+ );
+
+ $instance->logWarning(
+ $config,
+ FlagshipConstant::NEW_VISITOR_WARNING,
+ [FlagshipConstant::TAG => FlagshipConstant::TAG_NEW_VISITOR]
+ );
+ return $configManager;
+ }
+
/**
* Initialize the builder and return a \Flagship\Visitor\VisitorBuilder.
*
@@ -301,10 +344,12 @@ public static function close(): void
public static function newVisitor(?string $visitorId, bool $hasConsented): VisitorBuilder
{
$instance = self::getInstance();
+ $configManager = $instance->defaultConfigManagerInitialization($instance->getConfigManager());
+
return VisitorBuilder::builder(
$visitorId,
$hasConsented,
- $instance->getConfigManager(),
+ $configManager,
$instance->getContainer(),
$instance->flagshipInstanceId
);
diff --git a/src/Hit/Activate.php b/src/Hit/Activate.php
index 47cff6de..8a1df329 100644
--- a/src/Hit/Activate.php
+++ b/src/Hit/Activate.php
@@ -21,34 +21,31 @@ class Activate extends HitAbstract
private string $variationId;
/**
- * @var ?string
+ * @var string
*/
- private ?string $flagKey = null;
+ private string $flagKey;
/**
- * @var bool|numeric|string|array|null
+ * @var array|scalar|null
*/
private string|array|bool|int|float|null $flagValue = null;
/**
- * @var ?array
+ * @var ?array
*/
private ?array $visitorContext = null;
/**
- * @var ?FSFlagMetadataInterface
+ * @var FSFlagMetadataInterface
*/
- private ?FSFlagMetadataInterface $flagMetadata = null;
+ private FSFlagMetadataInterface $flagMetadata;
/**
- * @var bool|numeric|string|array|null
+ * @var scalar|array|null
*/
private string|array|bool|int|float|null $flagDefaultValue = null;
- public static function getClassName(): string
- {
- return __CLASS__;
- }
+
@@ -56,11 +53,17 @@ public static function getClassName(): string
* @param string $variationGroupId
* @param string $variationId
*/
- public function __construct(string $variationGroupId, string $variationId)
- {
+ public function __construct(
+ string $variationGroupId,
+ string $variationId,
+ string $flagKey,
+ FSFlagMetadataInterface $flagMetadata
+ ) {
parent::__construct(HitType::ACTIVATE);
$this->variationGroupId = $variationGroupId;
$this->variationId = $variationId;
+ $this->flagKey = $flagKey;
+ $this->flagMetadata = $flagMetadata;
}
/**
@@ -75,7 +78,7 @@ public function getVariationGroupId(): string
* @param string $variationGroupId
* @return Activate
*/
- public function setVariationGroupId(string $variationGroupId): static
+ public function setVariationGroupId(string $variationGroupId): self
{
$this->variationGroupId = $variationGroupId;
return $this;
@@ -93,32 +96,32 @@ public function getVariationId(): string
* @param string $variationId
* @return Activate
*/
- public function setVariationId(string $variationId): static
+ public function setVariationId(string $variationId): self
{
$this->variationId = $variationId;
return $this;
}
/**
- * @return string
+ * @return ?string
*/
- public function getFlagKey(): string
+ public function getFlagKey(): ?string
{
return $this->flagKey;
}
/**
- * @param ?string $flagKey
+ * @param string $flagKey
* @return Activate
*/
- public function setFlagKey(?string $flagKey): static
+ public function setFlagKey(string $flagKey): self
{
$this->flagKey = $flagKey;
return $this;
}
/**
- * @return bool|numeric|string|array|null
+ * @return array|scalar|null
*/
public function getFlagValue(): float|array|bool|int|string|null
{
@@ -126,28 +129,28 @@ public function getFlagValue(): float|array|bool|int|string|null
}
/**
- * @param array|bool|string|numeric|null $flagValue
+ * @param array|scalar|null $flagValue
* @return Activate
*/
- public function setFlagValue(float|array|bool|int|string|null $flagValue): static
+ public function setFlagValue(float|array|bool|int|string|null $flagValue): self
{
$this->flagValue = $flagValue;
return $this;
}
/**
- * @return array
+ * @return ?array
*/
- public function getVisitorContext(): array
+ public function getVisitorContext(): ?array
{
return $this->visitorContext;
}
/**
- * @param ?array $visitorContext
+ * @param ?array $visitorContext
* @return Activate
*/
- public function setVisitorContext(?array $visitorContext): static
+ public function setVisitorContext(?array $visitorContext): self
{
$this->visitorContext = $visitorContext;
return $this;
@@ -162,17 +165,17 @@ public function getFlagMetadata(): FSFlagMetadataInterface
}
/**
- * @param ?FSFlagMetadataInterface $flagMetadata
+ * @param FSFlagMetadataInterface $flagMetadata
* @return Activate
*/
- public function setFlagMetadata(?FSFlagMetadataInterface $flagMetadata): static
+ public function setFlagMetadata(FSFlagMetadataInterface $flagMetadata): self
{
$this->flagMetadata = $flagMetadata;
return $this;
}
/**
- * @return bool|numeric|string|array|null
+ * @return array|scalar|null
*/
public function getFlagDefaultValue(): float|array|bool|int|string|null
{
@@ -180,10 +183,10 @@ public function getFlagDefaultValue(): float|array|bool|int|string|null
}
/**
- * @param array|bool|string|numeric|null $flagDefaultValue
+ * @param array|scalar|null $flagDefaultValue
* @return Activate
*/
- public function setFlagDefaultValue(float|array|bool|int|string|null $flagDefaultValue): static
+ public function setFlagDefaultValue(float|array|bool|int|string|null $flagDefaultValue): self
{
$this->flagDefaultValue = $flagDefaultValue;
return $this;
@@ -198,13 +201,13 @@ public function setFlagDefaultValue(float|array|bool|int|string|null $flagDefaul
public function toApiKeys(): array
{
$apiKeys = [
- FlagshipConstant::VISITOR_ID_API_ITEM => $this->getVisitorId(),
- FlagshipConstant::VARIATION_ID_API_ITEM => $this->getVariationId(),
- FlagshipConstant::VARIATION_GROUP_ID_API_ITEM => $this->getVariationGroupId(),
- FlagshipConstant::CUSTOMER_ENV_ID_API_ITEM => $this->config->getEnvId(),
- FlagshipConstant::ANONYMOUS_ID => null,
- FlagshipConstant::QT_API_ITEM => round(microtime(true) * 1000) - $this->createdAt,
- ];
+ FlagshipConstant::VISITOR_ID_API_ITEM => $this->getVisitorId(),
+ FlagshipConstant::VARIATION_ID_API_ITEM => $this->getVariationId(),
+ FlagshipConstant::VARIATION_GROUP_ID_API_ITEM => $this->getVariationGroupId(),
+ FlagshipConstant::CUSTOMER_ENV_ID_API_ITEM => $this->config?->getEnvId() ?? '',
+ FlagshipConstant::ANONYMOUS_ID => null,
+ FlagshipConstant::QT_API_ITEM => $this->getNow() - $this->createdAt,
+ ];
if ($this->getVisitorId() && $this->getAnonymousId()) {
$apiKeys[FlagshipConstant::VISITOR_ID_API_ITEM] = $this->getVisitorId();
diff --git a/src/Hit/ActivateBatch.php b/src/Hit/ActivateBatch.php
index d60bec1d..db3a83b4 100644
--- a/src/Hit/ActivateBatch.php
+++ b/src/Hit/ActivateBatch.php
@@ -7,8 +7,8 @@
class ActivateBatch
{
- /***
- * @var Activate[]
+ /**
+ * @var Activate[] $hits
*/
protected array $hits;
@@ -19,7 +19,7 @@ class ActivateBatch
/**
* @param FlagshipConfig $config
- * @param array $hits
+ * @param Activate[] $hits
*/
public function __construct(FlagshipConfig $config, array $hits)
{
@@ -28,7 +28,7 @@ public function __construct(FlagshipConfig $config, array $hits)
}
/**
- * @return array
+ * @return array
*/
public function toApiKeys(): array
{
@@ -39,8 +39,8 @@ public function toApiKeys(): array
$activates[] = $apiKeys;
}
return [
- FlagshipConstant::CUSTOMER_ENV_ID_API_ITEM => $this->config->getEnvId(),
- FlagshipConstant::BATCH => $activates,
- ];
+ FlagshipConstant::CUSTOMER_ENV_ID_API_ITEM => $this->config->getEnvId(),
+ FlagshipConstant::BATCH => $activates,
+ ];
}
}
diff --git a/src/Hit/Diagnostic.php b/src/Hit/Diagnostic.php
index e83f0b70..dc236bdd 100644
--- a/src/Hit/Diagnostic.php
+++ b/src/Hit/Diagnostic.php
@@ -93,9 +93,9 @@ class Diagnostic extends HitAbstract
private ?bool $sdkConfigStatusListener = null;
/**
- * @var int|float|null
+ * @var int|float|null|string
*/
- private int|float|null $sdkConfigTimeout = null;
+ private int|float|null|string $sdkConfigTimeout = null;
/**
@@ -139,7 +139,7 @@ class Diagnostic extends HitAbstract
private ?string $httpRequestMethod = null;
/**
- * @var ?array
+ * @var ?array
*/
private ?array $httpRequestHeaders = null;
@@ -159,14 +159,14 @@ class Diagnostic extends HitAbstract
private ?string $httpResponseMethod = null;
/**
- * @var ?array
+ * @var ?array
*/
private ?array $httpResponseHeaders = null;
/**
- * @var ?int
+ * @var int|string|null
*/
- private ?int $httpResponseCode = null;
+ private string|int|null $httpResponseCode = null;
/**
* @var mixed
@@ -176,10 +176,10 @@ class Diagnostic extends HitAbstract
/**
* @var ?int
*/
- private ?int $httpResponseTime = null;
+ private int|float|null $httpResponseTime = null;
/**
- * @var ?array
+ * @var ?array
*/
private ?array $visitorContext = null;
@@ -189,7 +189,7 @@ class Diagnostic extends HitAbstract
private ?bool $visitorConsent = null;
/**
- * @var ?array
+ * @var ?array
*/
private ?array $visitorAssignmentHistory = null;
@@ -199,7 +199,7 @@ class Diagnostic extends HitAbstract
private ?array $visitorFlags = null;
/**
- * @var ?array
+ * @var ?array
*/
private ?array $visitorCampaigns = null;
@@ -275,9 +275,9 @@ class Diagnostic extends HitAbstract
/**
- * @var mixed
+ * @var array
*/
- private mixed $hitContent = null;
+ private ?array $hitContent = null;
/**
* @var ?string
@@ -312,7 +312,7 @@ public function getFlagshipInstanceId(): ?string
* @param ?string $flagshipInstanceId
* @return Diagnostic
*/
- public function setFlagshipInstanceId(?string $flagshipInstanceId): static
+ public function setFlagshipInstanceId(?string $flagshipInstanceId): self
{
$this->flagshipInstanceId = $flagshipInstanceId;
return $this;
@@ -330,7 +330,7 @@ public function getVisitorSessionId(): ?string
* @param string $visitorSessionId
* @return Diagnostic
*/
- public function setVisitorSessionId(string $visitorSessionId): static
+ public function setVisitorSessionId(string $visitorSessionId): self
{
$this->visitorSessionId = $visitorSessionId;
return $this;
@@ -348,7 +348,7 @@ public function getTraffic(): float|int|null
* @param float|int|null $traffic
* @return Diagnostic
*/
- public function setTraffic(float|int|null $traffic): static
+ public function setTraffic(float|int|null $traffic): self
{
$this->traffic = $traffic;
return $this;
@@ -366,7 +366,7 @@ public function getVersion(): string
* @param string $version
* @return Diagnostic
*/
- public function setVersion(string $version): static
+ public function setVersion(string $version): self
{
$this->version = $version;
return $this;
@@ -384,7 +384,7 @@ public function getLogLevel(): LogLevel
* @param LogLevel $logLevel
* @return Diagnostic
*/
- public function setLogLevel(LogLevel $logLevel): static
+ public function setLogLevel(LogLevel $logLevel): self
{
$this->logLevel = $logLevel;
return $this;
@@ -402,7 +402,7 @@ public function getTimestamp(): string
* @param string $timestamp
* @return Diagnostic
*/
- public function setTimestamp(string $timestamp): static
+ public function setTimestamp(string $timestamp): self
{
$this->timestamp = $timestamp;
return $this;
@@ -420,7 +420,7 @@ public function getTimeZone(): string
* @param string $timeZone
* @return Diagnostic
*/
- public function setTimeZone(string $timeZone): static
+ public function setTimeZone(string $timeZone): self
{
$this->timeZone = $timeZone;
return $this;
@@ -438,7 +438,7 @@ public function getLabel(): TroubleshootingLabel
* @param TroubleshootingLabel $label
* @return Diagnostic
*/
- public function setLabel(TroubleshootingLabel $label): static
+ public function setLabel(TroubleshootingLabel $label): self
{
$this->label = $label;
return $this;
@@ -456,7 +456,7 @@ public function getStackType(): string
* @param string $stackType
* @return Diagnostic
*/
- public function setStackType(string $stackType): static
+ public function setStackType(string $stackType): self
{
$this->stackType = $stackType;
return $this;
@@ -474,7 +474,7 @@ public function getStackName(): string
* @param string $stackName
* @return Diagnostic
*/
- public function setStackName(string $stackName): static
+ public function setStackName(string $stackName): self
{
$this->stackName = $stackName;
return $this;
@@ -492,7 +492,7 @@ public function getStackVersion(): string
* @param string $stackVersion
* @return Diagnostic
*/
- public function setStackVersion(string $stackVersion): static
+ public function setStackVersion(string $stackVersion): self
{
$this->stackVersion = $stackVersion;
return $this;
@@ -510,7 +510,7 @@ public function getStackOriginName(): ?string
* @param string $stackOriginName
* @return Diagnostic
*/
- public function setStackOriginName(string $stackOriginName): static
+ public function setStackOriginName(string $stackOriginName): self
{
$this->stackOriginName = $stackOriginName;
return $this;
@@ -528,7 +528,7 @@ public function getStackOriginVersion(): ?string
* @param string $stackOriginVersion
* @return Diagnostic
*/
- public function setStackOriginVersion(string $stackOriginVersion): static
+ public function setStackOriginVersion(string $stackOriginVersion): self
{
$this->stackOriginVersion = $stackOriginVersion;
return $this;
@@ -546,7 +546,7 @@ public function getSdkStatus(): ?FSSdkStatus
* @param ?FSSdkStatus $sdkStatus
* @return Diagnostic
*/
- public function setSdkStatus(?FSSdkStatus $sdkStatus): static
+ public function setSdkStatus(?FSSdkStatus $sdkStatus): self
{
$this->sdkStatus = $sdkStatus;
return $this;
@@ -564,7 +564,7 @@ public function getSdkConfigMode(): ?DecisionMode
* @param DecisionMode $sdkConfigMode
* @return Diagnostic
*/
- public function setSdkConfigMode(DecisionMode $sdkConfigMode): static
+ public function setSdkConfigMode(DecisionMode $sdkConfigMode): self
{
$this->sdkConfigMode = $sdkConfigMode;
return $this;
@@ -582,7 +582,7 @@ public function getSdkConfigCustomLogManager(): ?bool
* @param bool $sdkConfigCustomLogManager
* @return Diagnostic
*/
- public function setSdkConfigCustomLogManager(bool $sdkConfigCustomLogManager): static
+ public function setSdkConfigCustomLogManager(bool $sdkConfigCustomLogManager): self
{
$this->sdkConfigCustomLogManager = $sdkConfigCustomLogManager;
return $this;
@@ -600,7 +600,7 @@ public function getSdkConfigCustomCacheManager(): ?bool
* @param bool $sdkConfigCustomCacheManager
* @return Diagnostic
*/
- public function setSdkConfigCustomCacheManager(bool $sdkConfigCustomCacheManager): static
+ public function setSdkConfigCustomCacheManager(bool $sdkConfigCustomCacheManager): self
{
$this->sdkConfigCustomCacheManager = $sdkConfigCustomCacheManager;
return $this;
@@ -618,7 +618,7 @@ public function getSdkConfigStatusListener(): ?bool
* @param bool $sdkConfigStatusListener
* @return Diagnostic
*/
- public function setSdkConfigStatusListener(bool $sdkConfigStatusListener): static
+ public function setSdkConfigStatusListener(bool $sdkConfigStatusListener): self
{
$this->sdkConfigStatusListener = $sdkConfigStatusListener;
return $this;
@@ -636,7 +636,7 @@ public function getSdkConfigBucketingUrl(): ?string
* @param ?string $sdkConfigBucketingUrl
* @return Diagnostic
*/
- public function setSdkConfigBucketingUrl(?string $sdkConfigBucketingUrl): static
+ public function setSdkConfigBucketingUrl(?string $sdkConfigBucketingUrl): self
{
$this->sdkConfigBucketingUrl = $sdkConfigBucketingUrl;
return $this;
@@ -651,10 +651,10 @@ public function getSdkConfigFetchThirdPartyData(): ?bool
}
/**
- * @param ?string $sdkConfigFetchThirdPartyData
+ * @param ?bool $sdkConfigFetchThirdPartyData
* @return Diagnostic
*/
- public function setSdkConfigFetchThirdPartyData(?string $sdkConfigFetchThirdPartyData): static
+ public function setSdkConfigFetchThirdPartyData(?bool $sdkConfigFetchThirdPartyData): self
{
$this->sdkConfigFetchThirdPartyData = $sdkConfigFetchThirdPartyData;
return $this;
@@ -672,7 +672,7 @@ public function getSdkConfigTimeout(): float|int|string|null
* @param numeric $sdkConfigTimeout
* @return Diagnostic
*/
- public function setSdkConfigTimeout(float|int|string $sdkConfigTimeout): static
+ public function setSdkConfigTimeout(float|int|string $sdkConfigTimeout): self
{
$this->sdkConfigTimeout = $sdkConfigTimeout;
return $this;
@@ -693,7 +693,7 @@ public function getSdkConfigTrackingManagerConfigStrategy(): ?CacheStrategy
*/
public function setSdkConfigTrackingManagerConfigStrategy(
CacheStrategy $sdkConfigTrackingManagerConfigStrategy
- ): static {
+ ): self {
$this->sdkConfigTrackingManagerConfigStrategy = $sdkConfigTrackingManagerConfigStrategy;
return $this;
}
@@ -710,7 +710,7 @@ public function isSdkConfigUsingCustomHitCache(): ?bool
* @param bool $sdkConfigUsingCustomHitCache
* @return Diagnostic
*/
- public function setSdkConfigUsingCustomHitCache(bool $sdkConfigUsingCustomHitCache): static
+ public function setSdkConfigUsingCustomHitCache(bool $sdkConfigUsingCustomHitCache): self
{
$this->sdkConfigUsingCustomHitCache = $sdkConfigUsingCustomHitCache;
return $this;
@@ -728,7 +728,7 @@ public function isSdkConfigUsingCustomVisitorCache(): ?bool
* @param bool $sdkConfigUsingCustomVisitorCache
* @return Diagnostic
*/
- public function setSdkConfigUsingCustomVisitorCache(bool $sdkConfigUsingCustomVisitorCache): static
+ public function setSdkConfigUsingCustomVisitorCache(bool $sdkConfigUsingCustomVisitorCache): self
{
$this->sdkConfigUsingCustomVisitorCache = $sdkConfigUsingCustomVisitorCache;
return $this;
@@ -746,7 +746,7 @@ public function isSdkConfigUsingOnVisitorExposed(): ?bool
* @param bool $sdkConfigUsingOnVisitorExposed
* @return Diagnostic
*/
- public function setSdkConfigUsingOnVisitorExposed(bool $sdkConfigUsingOnVisitorExposed): static
+ public function setSdkConfigUsingOnVisitorExposed(bool $sdkConfigUsingOnVisitorExposed): self
{
$this->sdkConfigUsingOnVisitorExposed = $sdkConfigUsingOnVisitorExposed;
return $this;
@@ -764,7 +764,7 @@ public function getHttpRequestUrl(): ?string
* @param string $httpRequestUrl
* @return Diagnostic
*/
- public function setHttpRequestUrl(string $httpRequestUrl): static
+ public function setHttpRequestUrl(string $httpRequestUrl): self
{
$this->httpRequestUrl = $httpRequestUrl;
return $this;
@@ -782,14 +782,14 @@ public function getHttpRequestMethod(): ?string
* @param string $httpRequestMethod
* @return Diagnostic
*/
- public function setHttpRequestMethod(string $httpRequestMethod): static
+ public function setHttpRequestMethod(string $httpRequestMethod): self
{
$this->httpRequestMethod = $httpRequestMethod;
return $this;
}
/**
- * @return array|null
+ * @return array|null
*/
public function getHttpRequestHeaders(): ?array
{
@@ -797,10 +797,10 @@ public function getHttpRequestHeaders(): ?array
}
/**
- * @param array $httpRequestHeaders
+ * @param array $httpRequestHeaders
* @return Diagnostic
*/
- public function setHttpRequestHeaders(array $httpRequestHeaders): static
+ public function setHttpRequestHeaders(array $httpRequestHeaders): self
{
$this->httpRequestHeaders = $httpRequestHeaders;
return $this;
@@ -818,7 +818,7 @@ public function getHttpRequestBody(): mixed
* @param mixed $httpRequestBody
* @return Diagnostic
*/
- public function setHttpRequestBody(mixed $httpRequestBody): static
+ public function setHttpRequestBody(mixed $httpRequestBody): self
{
$this->httpRequestBody = $httpRequestBody;
return $this;
@@ -836,7 +836,7 @@ public function getHttpResponseUrl(): ?string
* @param string $httpResponseUrl
* @return Diagnostic
*/
- public function setHttpResponseUrl(string $httpResponseUrl): static
+ public function setHttpResponseUrl(string $httpResponseUrl): self
{
$this->httpResponseUrl = $httpResponseUrl;
return $this;
@@ -854,14 +854,14 @@ public function getHttpResponseMethod(): ?string
* @param string $httpResponseMethod
* @return Diagnostic
*/
- public function setHttpResponseMethod(string $httpResponseMethod): static
+ public function setHttpResponseMethod(string $httpResponseMethod): self
{
$this->httpResponseMethod = $httpResponseMethod;
return $this;
}
/**
- * @return array|null
+ * @return array|null
*/
public function getHttpResponseHeaders(): ?array
{
@@ -869,28 +869,28 @@ public function getHttpResponseHeaders(): ?array
}
/**
- * @param array $httpResponseHeaders
+ * @param array $httpResponseHeaders
* @return Diagnostic
*/
- public function setHttpResponseHeaders(array $httpResponseHeaders): static
+ public function setHttpResponseHeaders(array $httpResponseHeaders): self
{
$this->httpResponseHeaders = $httpResponseHeaders;
return $this;
}
/**
- * @return int|null
+ * @return int|null|string
*/
- public function getHttpResponseCode(): ?int
+ public function getHttpResponseCode(): int|null|string
{
return $this->httpResponseCode;
}
/**
- * @param int $httpResponseCode
+ * @param int|null|string $httpResponseCode
* @return Diagnostic
*/
- public function setHttpResponseCode(int $httpResponseCode): static
+ public function setHttpResponseCode(int|null|string $httpResponseCode): self
{
$this->httpResponseCode = $httpResponseCode;
return $this;
@@ -908,32 +908,32 @@ public function getHttpResponseBody(): mixed
* @param mixed $httpResponseBody
* @return Diagnostic
*/
- public function setHttpResponseBody(mixed $httpResponseBody): static
+ public function setHttpResponseBody(mixed $httpResponseBody): self
{
$this->httpResponseBody = $httpResponseBody;
return $this;
}
/**
- * @return int|null
+ * @return int|float|string|null
*/
- public function getHttpResponseTime(): ?int
+ public function getHttpResponseTime(): int|float|null|string
{
return $this->httpResponseTime;
}
/**
- * @param int $httpResponseTime
+ * @param int|float $httpResponseTime
* @return Diagnostic
*/
- public function setHttpResponseTime(int $httpResponseTime): static
+ public function setHttpResponseTime(int|float $httpResponseTime): self
{
$this->httpResponseTime = $httpResponseTime;
return $this;
}
/**
- * @return array|null
+ * @return array|null
*/
public function getVisitorContext(): ?array
{
@@ -941,10 +941,10 @@ public function getVisitorContext(): ?array
}
/**
- * @param array $visitorContext
+ * @param array $visitorContext
* @return Diagnostic
*/
- public function setVisitorContext(array $visitorContext): static
+ public function setVisitorContext(array $visitorContext): self
{
$this->visitorContext = $visitorContext;
return $this;
@@ -962,14 +962,14 @@ public function isVisitorConsent(): ?bool
* @param bool $visitorConsent
* @return Diagnostic
*/
- public function setVisitorConsent(bool $visitorConsent): static
+ public function setVisitorConsent(bool $visitorConsent): self
{
$this->visitorConsent = $visitorConsent;
return $this;
}
/**
- * @return array|null
+ * @return array|null
*/
public function getVisitorAssignmentHistory(): ?array
{
@@ -977,17 +977,17 @@ public function getVisitorAssignmentHistory(): ?array
}
/**
- * @param array $visitorAssignmentHistory
+ * @param array $visitorAssignmentHistory
* @return Diagnostic
*/
- public function setVisitorAssignmentHistory(array $visitorAssignmentHistory): static
+ public function setVisitorAssignmentHistory(array $visitorAssignmentHistory): self
{
$this->visitorAssignmentHistory = $visitorAssignmentHistory;
return $this;
}
/**
- * @return array|null
+ * @return FlagDTO[]|null
*/
public function getVisitorFlags(): ?array
{
@@ -998,14 +998,14 @@ public function getVisitorFlags(): ?array
* @param FlagDTO[] $visitorFlags
* @return Diagnostic
*/
- public function setVisitorFlags(array $visitorFlags): static
+ public function setVisitorFlags(array $visitorFlags): self
{
$this->visitorFlags = $visitorFlags;
return $this;
}
/**
- * @return array|null
+ * @return array|null
*/
public function getVisitorCampaigns(): ?array
{
@@ -1013,10 +1013,10 @@ public function getVisitorCampaigns(): ?array
}
/**
- * @param array $visitorCampaigns
+ * @param array $visitorCampaigns
* @return Diagnostic
*/
- public function setVisitorCampaigns(array $visitorCampaigns): static
+ public function setVisitorCampaigns(array $visitorCampaigns): self
{
$this->visitorCampaigns = $visitorCampaigns;
return $this;
@@ -1034,7 +1034,7 @@ public function isVisitorIsAuthenticated(): ?bool
* @param bool $visitorIsAuthenticated
* @return Diagnostic
*/
- public function setVisitorIsAuthenticated(bool $visitorIsAuthenticated): static
+ public function setVisitorIsAuthenticated(bool $visitorIsAuthenticated): self
{
$this->visitorIsAuthenticated = $visitorIsAuthenticated;
return $this;
@@ -1052,7 +1052,7 @@ public function getFlagKey(): ?string
* @param string $flagKey
* @return Diagnostic
*/
- public function setFlagKey(string $flagKey): static
+ public function setFlagKey(string $flagKey): self
{
$this->flagKey = $flagKey;
return $this;
@@ -1070,7 +1070,7 @@ public function getFlagValue(): mixed
* @param mixed $flagValue
* @return Diagnostic
*/
- public function setFlagValue(mixed $flagValue): static
+ public function setFlagValue(mixed $flagValue): self
{
$this->flagValue = $flagValue;
return $this;
@@ -1088,7 +1088,7 @@ public function getFlagDefault(): mixed
* @param mixed $flagDefault
* @return Diagnostic
*/
- public function setFlagDefault(mixed $flagDefault): static
+ public function setFlagDefault(mixed $flagDefault): self
{
$this->flagDefault = $flagDefault;
return $this;
@@ -1106,7 +1106,7 @@ public function isVisitorExposed(): ?bool
* @param bool $visitorExposed
* @return Diagnostic
*/
- public function setVisitorExposed(bool $visitorExposed): static
+ public function setVisitorExposed(bool $visitorExposed): self
{
$this->visitorExposed = $visitorExposed;
return $this;
@@ -1124,7 +1124,7 @@ public function getFlagMetadataCampaignId(): ?string
* @param string $flagMetadataCampaignId
* @return Diagnostic
*/
- public function setFlagMetadataCampaignId(string $flagMetadataCampaignId): static
+ public function setFlagMetadataCampaignId(string $flagMetadataCampaignId): self
{
$this->flagMetadataCampaignId = $flagMetadataCampaignId;
return $this;
@@ -1142,7 +1142,7 @@ public function getFlagMetadataVariationGroupId(): ?string
* @param string $flagMetadataVariationGroupId
* @return Diagnostic
*/
- public function setFlagMetadataVariationGroupId(string $flagMetadataVariationGroupId): static
+ public function setFlagMetadataVariationGroupId(string $flagMetadataVariationGroupId): self
{
$this->flagMetadataVariationGroupId = $flagMetadataVariationGroupId;
return $this;
@@ -1160,7 +1160,7 @@ public function getFlagMetadataVariationId(): ?string
* @param string $flagMetadataVariationId
* @return Diagnostic
*/
- public function setFlagMetadataVariationId(string $flagMetadataVariationId): static
+ public function setFlagMetadataVariationId(string $flagMetadataVariationId): self
{
$this->flagMetadataVariationId = $flagMetadataVariationId;
return $this;
@@ -1178,7 +1178,7 @@ public function getFlagMetadataCampaignSlug(): ?string
* @param string $flagMetadataCampaignSlug
* @return Diagnostic
*/
- public function setFlagMetadataCampaignSlug(string $flagMetadataCampaignSlug): static
+ public function setFlagMetadataCampaignSlug(string $flagMetadataCampaignSlug): self
{
$this->flagMetadataCampaignSlug = $flagMetadataCampaignSlug;
return $this;
@@ -1196,7 +1196,7 @@ public function getFlagMetadataCampaignType(): ?string
* @param string $flagMetadataCampaignType
* @return Diagnostic
*/
- public function setFlagMetadataCampaignType(string $flagMetadataCampaignType): static
+ public function setFlagMetadataCampaignType(string $flagMetadataCampaignType): self
{
$this->flagMetadataCampaignType = $flagMetadataCampaignType;
return $this;
@@ -1214,7 +1214,7 @@ public function isFlagMetadataCampaignIsReference(): ?bool
* @param bool $flagMetadataCampaignIsReference
* @return Diagnostic
*/
- public function setFlagMetadataCampaignIsReference(bool $flagMetadataCampaignIsReference): static
+ public function setFlagMetadataCampaignIsReference(bool $flagMetadataCampaignIsReference): self
{
$this->flagMetadataCampaignIsReference = $flagMetadataCampaignIsReference;
return $this;
@@ -1232,7 +1232,7 @@ public function getFlagMetadataCampaignName(): ?string
* @param string $flagMetadataCampaignName
* @return Diagnostic
*/
- public function setFlagMetadataCampaignName(string $flagMetadataCampaignName): static
+ public function setFlagMetadataCampaignName(string $flagMetadataCampaignName): self
{
$this->flagMetadataCampaignName = $flagMetadataCampaignName;
return $this;
@@ -1250,7 +1250,7 @@ public function getFlagMetadataVariationGroupName(): ?string
* @param string $flagMetadataVariationGroupName
* @return Diagnostic
*/
- public function setFlagMetadataVariationGroupName(string $flagMetadataVariationGroupName): static
+ public function setFlagMetadataVariationGroupName(string $flagMetadataVariationGroupName): self
{
$this->flagMetadataVariationGroupName = $flagMetadataVariationGroupName;
return $this;
@@ -1268,7 +1268,7 @@ public function getFlagMetadataVariationName(): ?string
* @param string $flagMetadataVariationName
* @return Diagnostic
*/
- public function setFlagMetadataVariationName(string $flagMetadataVariationName): static
+ public function setFlagMetadataVariationName(string $flagMetadataVariationName): self
{
$this->flagMetadataVariationName = $flagMetadataVariationName;
return $this;
@@ -1276,18 +1276,18 @@ public function setFlagMetadataVariationName(string $flagMetadataVariationName):
/**
- * @return mixed
+ * @return array|null
*/
- public function getHitContent(): mixed
+ public function getHitContent(): ?array
{
return $this->hitContent;
}
/**
- * @param mixed $hitContent
+ * @param array $hitContent
* @return Diagnostic
*/
- public function setHitContent(mixed $hitContent): static
+ public function setHitContent(array $hitContent): self
{
$this->hitContent = $hitContent;
return $this;
@@ -1305,7 +1305,7 @@ public function getSdkConfigLogLeve(): ?LogLevel
* @param LogLevel $sdkConfigLogLeve
* @return Diagnostic
*/
- public function setSdkConfigLogLevel(LogLevel $sdkConfigLogLeve): static
+ public function setSdkConfigLogLevel(LogLevel $sdkConfigLogLeve): self
{
$this->sdkConfigLogLeve = $sdkConfigLogLeve;
return $this;
@@ -1313,20 +1313,24 @@ public function setSdkConfigLogLevel(LogLevel $sdkConfigLogLeve): static
+ /**
+ *
+ * @return array
+ */
public function toApiKeys(): array
{
$customVariable = [
- 'version' => $this->getVersion(),
- 'logLevel' => $this->getLogLevel()->name,
- 'envId' => $this->getConfig()->getEnvId(),
- "timestamp" => $this->getTimestamp(),
- 'timeZone' => $this->getTimeZone(),
- 'label' => $this->getLabel()->value,
- 'stack.type' => $this->getStackType(),
- 'stack.name' => $this->getStackName(),
- 'stack.version' => $this->getStackVersion(),
- ];
- if ($this->getVisitorId() !== null) {
+ 'version' => $this->getVersion(),
+ 'logLevel' => $this->getLogLevel()->name,
+ 'envId' => $this->getConfig()?->getEnvId(),
+ "timestamp" => $this->getTimestamp(),
+ 'timeZone' => $this->getTimeZone(),
+ 'label' => $this->getLabel()->value,
+ 'stack.type' => $this->getStackType(),
+ 'stack.name' => $this->getStackName(),
+ 'stack.version' => $this->getStackVersion(),
+ ];
+ if (!empty($this->getVisitorId())) {
$customVariable["visitor.visitorId"] = $this->getVisitorId();
}
@@ -1522,12 +1526,12 @@ public function toApiKeys(): array
}
return [
- FlagshipConstant::VISITOR_ID_API_ITEM => $this->visitorId,
- FlagshipConstant::DS_API_ITEM => $this->getDs(),
- FlagshipConstant::CUSTOMER_ENV_ID_API_ITEM => $this->getConfig()->getEnvId(),
- FlagshipConstant::T_API_ITEM => $this->getType()->value,
- 'cv' => $customVariable,
- ];
+ FlagshipConstant::VISITOR_ID_API_ITEM => $this->visitorId,
+ FlagshipConstant::DS_API_ITEM => $this->getDs(),
+ FlagshipConstant::CUSTOMER_ENV_ID_API_ITEM => $this->getConfig()?->getEnvId(),
+ FlagshipConstant::T_API_ITEM => $this->getType()->value,
+ 'cv' => $customVariable,
+ ];
}
/**
diff --git a/src/Hit/Event.php b/src/Hit/Event.php
index 55e0617d..a4a74a3d 100644
--- a/src/Hit/Event.php
+++ b/src/Hit/Event.php
@@ -17,10 +17,7 @@ class Event extends HitAbstract
public const CATEGORY_ERROR = "The category value must be either EventCategory::ACTION_TRACKING or EventCategory::ACTION_TRACKING";
public const VALUE_FIELD_ERROR = 'value must be an integer and be >= 0';
- public static function getClassName(): string
- {
- return __CLASS__;
- }
+
/**
* @var EventCategory
@@ -71,7 +68,7 @@ public function getCategory(): EventCategory
* @param EventCategory $category
* @return Event
*/
- public function setCategory(EventCategory $category): static
+ public function setCategory(EventCategory $category): self
{
$this->category = $category;
return $this;
@@ -94,7 +91,7 @@ public function getAction(): string
* @param string $action : Event name.
* @return Event
*/
- public function setAction(string $action): static
+ public function setAction(string $action): self
{
$this->action = $action;
return $this;
@@ -116,7 +113,7 @@ public function getLabel(): ?string
* @param ?string $label : event label.
* @return Event
*/
- public function setLabel(?string $label): static
+ public function setLabel(?string $label): self
{
$this->label = $label;
return $this;
@@ -140,7 +137,7 @@ public function getValue(): ?float
* @param float|null $value : event value
* @return Event
*/
- public function setValue(?float $value): static
+ public function setValue(?float $value): self
{
if (is_numeric($value) && $value < 0) {
$this->logError(
@@ -179,7 +176,8 @@ public function toApiKeys(): array
*/
public function isReady(): bool
{
- return parent::isReady() && $this->getCategory() && $this->getAction();
+ return parent::isReady() &&
+ $this->getAction();
}
/**
diff --git a/src/Hit/HitAbstract.php b/src/Hit/HitAbstract.php
index f1766a39..8b18325d 100644
--- a/src/Hit/HitAbstract.php
+++ b/src/Hit/HitAbstract.php
@@ -76,9 +76,9 @@ abstract class HitAbstract
protected string $key;
/**
- * @var int
+ * @var float
*/
- protected int $createdAt;
+ protected float $createdAt;
/**
* @var bool
@@ -115,9 +115,9 @@ public function getVisitorId(): string
* Specifies visitor unique identifier provided by developer at visitor creation
*
* @param string $visitorId
- * @return HitAbstract
+ * @return self
*/
- public function setVisitorId(string $visitorId): static
+ public function setVisitorId(string $visitorId): self
{
$this->visitorId = $visitorId;
return $this;
@@ -133,9 +133,9 @@ public function getDs(): string
/**
* @param string $ds
- * @return HitAbstract
+ * @return self
*/
- public function setDs(string $ds): static
+ public function setDs(string $ds): self
{
$this->ds = $ds;
return $this;
@@ -151,10 +151,10 @@ public function getType(): HitType
return $this->type;
}
- protected function setType(HitType $type): static
+ protected function setType(HitType $type): self
{
- $this->type = $type;
- return $this;
+ $this->type = $type;
+ return $this;
}
/**
@@ -167,9 +167,9 @@ public function getConfig(): ?FlagshipConfig
/**
* @param FlagshipConfig $config
- * @return HitAbstract
+ * @return self
*/
- public function setConfig(FlagshipConfig $config): static
+ public function setConfig(FlagshipConfig $config): self
{
$this->config = $config;
return $this;
@@ -185,9 +185,9 @@ public function getAnonymousId(): ?string
/**
* @param string|null $anonymousId
- * @return HitAbstract
+ * @return self
*/
- public function setAnonymousId(?string $anonymousId): static
+ public function setAnonymousId(?string $anonymousId): self
{
$this->anonymousId = $anonymousId;
return $this;
@@ -205,9 +205,9 @@ public function getUserIP(): ?string
/**
* Define the User IP address
* @param string|null $userIP
- * @return HitAbstract
+ * @return self
*/
- public function setUserIP(?string $userIP): static
+ public function setUserIP(?string $userIP): self
{
$this->userIP = $userIP;
return $this;
@@ -225,9 +225,9 @@ public function getScreenResolution(): ?string
/**
* Screen Resolution
* @param string|null $screenResolution
- * @return HitAbstract
+ * @return self
*/
- public function setScreenResolution(?string $screenResolution): static
+ public function setScreenResolution(?string $screenResolution): self
{
$this->screenResolution = $screenResolution;
return $this;
@@ -245,9 +245,9 @@ public function getLocale(): ?string
/**
* Define User language
* @param string|null $locale
- * @return HitAbstract
+ * @return self
*/
- public function setLocale(?string $locale): static
+ public function setLocale(?string $locale): self
{
$this->locale = $locale;
return $this;
@@ -265,9 +265,9 @@ public function getSessionNumber(): float|int|string|null
/**
* Define Session number. Number of sessions the current visitor has logged, including the current session
* @param float|int|string|null $sessionNumber
- * @return HitAbstract
+ * @return self
*/
- public function setSessionNumber(float|int|string|null $sessionNumber): static
+ public function setSessionNumber(float|int|string|null $sessionNumber): self
{
$this->sessionNumber = $sessionNumber;
return $this;
@@ -283,27 +283,27 @@ public function getKey(): string
/**
* @param string $key
- * @return HitAbstract
+ * @return self
*/
- public function setKey(string $key): static
+ public function setKey(string $key): self
{
$this->key = $key;
return $this;
}
/**
- * @return int
+ * @return float
*/
- public function getCreatedAt(): int
+ public function getCreatedAt(): float
{
return $this->createdAt;
}
/**
- * @param int $createdAt
- * @return HitAbstract
+ * @param float $createdAt
+ * @return self
*/
- public function setCreatedAt(int $createdAt): static
+ public function setCreatedAt(float $createdAt): self
{
$this->createdAt = $createdAt;
return $this;
@@ -319,9 +319,9 @@ public function getIsFromCache(): bool
/**
* @param bool $isFromCache
- * @return HitAbstract
+ * @return self
*/
- protected function setIsFromCache(bool $isFromCache): static
+ protected function setIsFromCache(bool $isFromCache): self
{
$this->isFromCache = $isFromCache;
return $this;
@@ -330,18 +330,18 @@ protected function setIsFromCache(bool $isFromCache): static
/**
* Return an associative array of the class with Api parameters as keys
*
- * @return array
+ * @return array
*/
public function toApiKeys(): array
{
$data = [
- FlagshipConstant::VISITOR_ID_API_ITEM => $this->visitorId ?: $this->anonymousId,
- FlagshipConstant::DS_API_ITEM => $this->getDs(),
- FlagshipConstant::CUSTOMER_ENV_ID_API_ITEM => $this->getConfig()->getEnvId(),
- FlagshipConstant::T_API_ITEM => $this->getType()->value,
- FlagshipConstant::CUSTOMER_UID => null,
- FlagshipConstant::QT_API_ITEM => $this->getNow() - $this->createdAt,
- ];
+ FlagshipConstant::VISITOR_ID_API_ITEM => $this->visitorId ?: $this->anonymousId,
+ FlagshipConstant::DS_API_ITEM => $this->getDs(),
+ FlagshipConstant::CUSTOMER_ENV_ID_API_ITEM => $this->getConfig()?->getEnvId(),
+ FlagshipConstant::T_API_ITEM => $this->getType()->value,
+ FlagshipConstant::CUSTOMER_UID => null,
+ FlagshipConstant::QT_API_ITEM => $this->getNow() - $this->createdAt,
+ ];
if ($this->getUserIP() !== null) {
$data[FlagshipConstant::USER_IP_API_ITEM] = $this->getUserIP();
@@ -367,30 +367,34 @@ public function toApiKeys(): array
}
/**
- * @param $class
- * @param $data
- * @return object
+ * @template T of object
+ * @param class-string $class
+ * @param array $data
+ * @return T
* @throws ReflectionException
*/
- public static function hydrate($class, $data): object
+ public static function hydrate(string $class, array $data): object
{
+
$reflector = new ReflectionClass($class);
$objet = $reflector->newInstanceWithoutConstructor();
foreach ($data as $key => $value) {
$method = 'set' . ucwords($key);
if (is_callable(array($objet, $method))) {
- if ($key === "type") {
+ if ($key === "type" && is_string($value)) {
$value = HitType::from($value);
}
$objet->$method($value);
}
}
- $objet->setIsFromCache(true);
+ if ($objet instanceof HitAbstract) {
+ $objet->setIsFromCache(true);
+ }
return $objet;
}
/**
- * @return array
+ * @return array
*/
public function toArray(): array
{
@@ -401,7 +405,6 @@ public function toArray(): array
if ($property->getName() === 'config' || $property->getName() === 'isFromCache') {
continue;
}
- $property->setAccessible(true);
$value = $property->getValue($this);
if ($value instanceof HitType) {
$value = $value->value;
@@ -420,7 +423,7 @@ public function toArray(): array
public function isReady(): bool
{
return $this->getVisitorId() && $this->getDs() && $this->getConfig() &&
- $this->getConfig()->getEnvId() && $this->getType();
+ $this->getConfig()->getEnvId();
}
/**
diff --git a/src/Hit/HitBatch.php b/src/Hit/HitBatch.php
index 7bfac945..337e92ab 100644
--- a/src/Hit/HitBatch.php
+++ b/src/Hit/HitBatch.php
@@ -32,13 +32,13 @@ public function __construct(FlagshipConfig $config, array $hits)
}
/**
- * @return array
+ * @return array
*/
public function toApiKeys(): array
{
$data = [
FlagshipConstant::DS_API_ITEM => $this->getDs(),
- FlagshipConstant::CUSTOMER_ENV_ID_API_ITEM => $this->getConfig()->getEnvId(),
+ FlagshipConstant::CUSTOMER_ENV_ID_API_ITEM => $this->getConfig()?->getEnvId(),
FlagshipConstant::T_API_ITEM => $this->getType()->value,
FlagshipConstant::QT_API_ITEM => $this->getNow() - $this->createdAt,
FlagshipConstant::H_API_ITEM => [],
diff --git a/src/Hit/Item.php b/src/Hit/Item.php
index 727c9833..2d2cdb15 100644
--- a/src/Hit/Item.php
+++ b/src/Hit/Item.php
@@ -14,10 +14,7 @@ class Item extends HitAbstract
{
public const ERROR_MESSAGE = 'Transaction Id, Item name and item code are required';
- public static function getClassName(): string
- {
- return __CLASS__;
- }
+
/**
* @var string
@@ -81,7 +78,7 @@ public function getTransactionId(): string
* @param string $transactionId : Transaction unique identifier.
* @return Item
*/
- public function setTransactionId(string $transactionId): static
+ public function setTransactionId(string $transactionId): self
{
$this->transactionId = $transactionId;
return $this;
@@ -103,7 +100,7 @@ public function getProductName(): string
* @param string $productName : Name of the item product.
* @return Item
*/
- public function setProductName(string $productName): static
+ public function setProductName(string $productName): self
{
$this->productName = $productName;
return $this;
@@ -125,7 +122,7 @@ public function getProductSku(): string
* @param string $productSku
* @return Item
*/
- public function setProductSku(string $productSku): static
+ public function setProductSku(string $productSku): self
{
$this->productSku = $productSku;
return $this;
@@ -147,7 +144,7 @@ public function getItemPrice(): ?float
* @param ?float $itemPrice
* @return Item
*/
- public function setItemPrice(?float $itemPrice): static
+ public function setItemPrice(?float $itemPrice): self
{
$this->itemPrice = $itemPrice;
return $this;
@@ -169,7 +166,7 @@ public function getItemQuantity(): ?int
* @param ?int $itemQuantity
* @return Item
*/
- public function setItemQuantity(?int $itemQuantity): static
+ public function setItemQuantity(?int $itemQuantity): self
{
$this->itemQuantity = $itemQuantity;
return $this;
@@ -191,7 +188,7 @@ public function getItemCategory(): ?string
* @param ?string $itemCategory
* @return Item
*/
- public function setItemCategory(?string $itemCategory): static
+ public function setItemCategory(?string $itemCategory): self
{
$this->itemCategory = $itemCategory;
return $this;
diff --git a/src/Hit/Page.php b/src/Hit/Page.php
index 96c3af6a..ecd751ff 100644
--- a/src/Hit/Page.php
+++ b/src/Hit/Page.php
@@ -14,10 +14,7 @@ class Page extends HitAbstract
{
public const ERROR_MESSAGE = 'Page url is required';
- public static function getClassName(): string
- {
- return __CLASS__;
- }
+
/**
* @var string
@@ -49,7 +46,7 @@ public function getPageUrl(): string
* @param string $pageUrl
* @return Page
*/
- public function setPageUrl(string $pageUrl): static
+ public function setPageUrl(string $pageUrl): self
{
$this->pageUrl = $pageUrl;
return $this;
diff --git a/src/Hit/Screen.php b/src/Hit/Screen.php
index c551358c..b7bcdf76 100644
--- a/src/Hit/Screen.php
+++ b/src/Hit/Screen.php
@@ -14,13 +14,7 @@ class Screen extends HitAbstract
{
public const ERROR_MESSAGE = 'Screen name is required';
- /**
- * @return string
- */
- public static function getClassName(): string
- {
- return __CLASS__;
- }
+
/**
* @var string
@@ -55,7 +49,7 @@ public function getScreenName(): string
* @param string $screenName : Interface seen.
* @return Screen
*/
- public function setScreenName(string $screenName): static
+ public function setScreenName(string $screenName): self
{
$this->screenName = $screenName;
return $this;
diff --git a/src/Hit/Segment.php b/src/Hit/Segment.php
index a6748279..ba17d0a5 100644
--- a/src/Hit/Segment.php
+++ b/src/Hit/Segment.php
@@ -11,11 +11,6 @@ class Segment extends HitAbstract
public const SL_MESSAGE_ERROR = "Sl value must be an associative array";
public const ERROR_MESSAGE = 'sl is required';
- public static function getClassName(): string
- {
- return __CLASS__;
- }
-
/**
* @var array
*/
@@ -30,10 +25,10 @@ public function getSl(): array
}
/**
- * @param array $sl
+ * @param array $sl
* @return Segment
*/
- public function setSl(array $sl): static
+ public function setSl(array $sl): self
{
if (!$this->isAssoc($sl)) {
$this->logError($this->getConfig(), self::SL_MESSAGE_ERROR, [FlagshipConstant::TAG => __FUNCTION__]);
@@ -44,7 +39,7 @@ public function setSl(array $sl): static
}
/**
- * @param array $sl
+ * @param array $sl
*/
public function __construct(array $sl, FlagshipConfig $config)
{
@@ -54,7 +49,7 @@ public function __construct(array $sl, FlagshipConfig $config)
}
/**
- * @param array $array
+ * @param array $array
* @return bool
*/
protected function isAssoc(array $array): bool
@@ -75,7 +70,10 @@ public function toApiKeys(): array
if (is_bool($value)) {
return $value ? 'true' : 'false';
}
- return strval($value);
+ if (is_scalar($value)) {
+ return strval($value);
+ }
+ return '';
}, $this->getSl());
$arrayParent[FlagshipConstant::SL_API_ITEM] = $apiContext;
@@ -87,7 +85,7 @@ public function toApiKeys(): array
*/
public function isReady(): bool
{
- return parent::isReady() && $this->getSl() && count($this->getSl()) > 0;
+ return parent::isReady() && !empty($this->getSl());
}
/**
diff --git a/src/Hit/Transaction.php b/src/Hit/Transaction.php
index b7e69331..b8c9a517 100644
--- a/src/Hit/Transaction.php
+++ b/src/Hit/Transaction.php
@@ -15,13 +15,7 @@ class Transaction extends HitAbstract
public const CURRENCY_ERROR = "'%s' must be a string and have exactly 3 letters";
public const ERROR_MESSAGE = 'Transaction Id and Transaction affiliation are required';
- /**
- * @return string
- */
- public static function getClassName(): string
- {
- return __CLASS__;
- }
+
/**
* @var string
@@ -93,7 +87,7 @@ public function getTransactionId(): string
* @param string $transactionId
* @return Transaction
*/
- public function setTransactionId(string $transactionId): static
+ public function setTransactionId(string $transactionId): self
{
$this->transactionId = $transactionId;
return $this;
@@ -115,7 +109,7 @@ public function getAffiliation(): string
* @param string $affiliation
* @return Transaction
*/
- public function setAffiliation(string $affiliation): static
+ public function setAffiliation(string $affiliation): self
{
$this->affiliation = $affiliation;
return $this;
@@ -137,7 +131,7 @@ public function getTaxes(): ?float
* @param ?float $taxes
* @return Transaction
*/
- public function setTaxes(?float $taxes): static
+ public function setTaxes(?float $taxes): self
{
$this->taxes = $taxes;
return $this;
@@ -158,7 +152,7 @@ public function getCurrency(): ?string
* @param ?string $currency
* @return Transaction
*/
- public function setCurrency(?string $currency): static
+ public function setCurrency(?string $currency): self
{
$this->currency = $currency;
return $this;
@@ -180,7 +174,7 @@ public function getCouponCode(): ?string
* @param ?string $couponCode
* @return Transaction
*/
- public function setCouponCode(?string $couponCode): static
+ public function setCouponCode(?string $couponCode): self
{
$this->couponCode = $couponCode;
return $this;
@@ -202,7 +196,7 @@ public function getItemCount(): ?int
* @param ?integer $itemsCount
* @return Transaction
*/
- public function setItemCount(?int $itemsCount): static
+ public function setItemCount(?int $itemsCount): self
{
$this->itemCount = $itemsCount;
return $this;
@@ -224,7 +218,7 @@ public function getShippingMethod(): ?string
* @param ?string $shippingMethod
* @return Transaction
*/
- public function setShippingMethod(?string $shippingMethod): static
+ public function setShippingMethod(?string $shippingMethod): self
{
$this->shippingMethod = $shippingMethod;
return $this;
@@ -246,7 +240,7 @@ public function getPaymentMethod(): ?string
* @param ?string $paymentMethod
* @return Transaction
*/
- public function setPaymentMethod(?string $paymentMethod): static
+ public function setPaymentMethod(?string $paymentMethod): self
{
$this->paymentMethod = $paymentMethod;
return $this;
@@ -269,7 +263,7 @@ public function getTotalRevenue(): ?float
* @param ?float $totalRevenue
* @return Transaction
*/
- public function setTotalRevenue(?float $totalRevenue): static
+ public function setTotalRevenue(?float $totalRevenue): self
{
$this->totalRevenue = $totalRevenue;
return $this;
@@ -291,7 +285,7 @@ public function getShippingCosts(): ?float
* @param ?float $shippingCosts
* @return Transaction
*/
- public function setShippingCosts(?float $shippingCosts): static
+ public function setShippingCosts(?float $shippingCosts): self
{
$this->shippingCosts = $shippingCosts;
return $this;
diff --git a/src/Model/AccountSettingsDTO.php b/src/Model/AccountSettingsDTO.php
new file mode 100644
index 00000000..17ba30c2
--- /dev/null
+++ b/src/Model/AccountSettingsDTO.php
@@ -0,0 +1,130 @@
+enabledXPC;
+ }
+
+ public function setEnabledXPC(?bool $enabledXPC): self
+ {
+ $this->enabledXPC = $enabledXPC;
+ return $this;
+ }
+
+ public function getTroubleshooting(): ?TroubleshootingDTO
+ {
+ return $this->troubleshooting;
+ }
+
+ public function setTroubleshooting(?TroubleshootingDTO $troubleshooting): self
+ {
+ $this->troubleshooting = $troubleshooting;
+ return $this;
+ }
+
+ public function getEaiCollectEnabled(): ?bool
+ {
+ return $this->eaiCollectEnabled;
+ }
+
+ public function setEaiCollectEnabled(?bool $eaiCollectEnabled): self
+ {
+ $this->eaiCollectEnabled = $eaiCollectEnabled;
+ return $this;
+ }
+
+ public function getEaiActivationEnabled(): ?bool
+ {
+ return $this->eaiActivationEnabled;
+ }
+
+ public function setEaiActivationEnabled(?bool $eaiActivationEnabled): self
+ {
+ $this->eaiActivationEnabled = $eaiActivationEnabled;
+ return $this;
+ }
+
+ /**
+ * Check if troubleshooting is enabled and valid
+ *
+ * @return bool
+ */
+ public function isTroubleshootingEnabled(): bool
+ {
+ return $this->troubleshooting !== null;
+ }
+
+ /**
+ * @param array $data
+ * @return self
+ */
+ public static function fromArray(array $data): self
+ {
+ $instance = new self();
+
+ if (isset($data[FlagshipField::ENABLED_XPC]) && is_bool($data[FlagshipField::ENABLED_XPC])) {
+ $instance->setEnabledXPC($data[FlagshipField::ENABLED_XPC]);
+ }
+
+ if (isset($data[FlagshipField::TROUBLESHOOTING]) && is_array($data[FlagshipField::TROUBLESHOOTING])) {
+ /** @var array $troubleshootingData */
+ $troubleshootingData = $data[FlagshipField::TROUBLESHOOTING];
+ $instance->setTroubleshooting(
+ TroubleshootingDTO::fromArray($troubleshootingData)
+ );
+ }
+
+ if (isset($data[FlagshipField::EAI_COLLECT_ENABLED]) && is_bool($data[FlagshipField::EAI_COLLECT_ENABLED])) {
+ $instance->setEaiCollectEnabled($data[FlagshipField::EAI_COLLECT_ENABLED]);
+ }
+
+ if (isset($data[FlagshipField::EAI_ACTIVATION_ENABLED]) && is_bool($data[FlagshipField::EAI_ACTIVATION_ENABLED])) {
+ $instance->setEaiActivationEnabled($data[FlagshipField::EAI_ACTIVATION_ENABLED]);
+ }
+
+ return $instance;
+ }
+
+ /**
+ * @return AccountSettingsArray
+ */
+ public function toArray(): array
+ {
+ $result = [];
+
+ if ($this->enabledXPC !== null) {
+ $result[FlagshipField::ENABLED_XPC] = $this->enabledXPC;
+ }
+
+ if ($this->troubleshooting !== null) {
+ $result[FlagshipField::TROUBLESHOOTING] = $this->troubleshooting->toArray();
+ }
+
+ if ($this->eaiCollectEnabled !== null) {
+ $result[FlagshipField::EAI_COLLECT_ENABLED] = $this->eaiCollectEnabled;
+ }
+
+ if ($this->eaiActivationEnabled !== null) {
+ $result[FlagshipField::EAI_ACTIVATION_ENABLED] = $this->eaiActivationEnabled;
+ }
+
+ return $result;
+ }
+}
diff --git a/src/Model/BucketingCampaignDTO.php b/src/Model/BucketingCampaignDTO.php
new file mode 100644
index 00000000..8d7f0374
--- /dev/null
+++ b/src/Model/BucketingCampaignDTO.php
@@ -0,0 +1,160 @@
+ */
+ private array $variationGroups;
+
+ /**
+ * @param string $id
+ * @param string $type
+ * @param array $variationGroups
+ */
+ public function __construct(string $id, string $type, array $variationGroups)
+ {
+ $this->id = $id;
+ $this->type = $type;
+ $this->variationGroups = $variationGroups;
+ }
+
+ public function getId(): string
+ {
+ return $this->id;
+ }
+
+ public function setId(string $id): self
+ {
+ $this->id = $id;
+ return $this;
+ }
+
+ public function getName(): ?string
+ {
+ return $this->name;
+ }
+
+ public function setName(?string $name): self
+ {
+ $this->name = $name;
+ return $this;
+ }
+
+ public function getType(): string
+ {
+ return $this->type;
+ }
+
+ public function setType(string $type): self
+ {
+ $this->type = $type;
+ return $this;
+ }
+
+ public function getSlug(): ?string
+ {
+ return $this->slug;
+ }
+
+ public function setSlug(?string $slug): self
+ {
+ $this->slug = $slug;
+ return $this;
+ }
+
+ /**
+ * @return array
+ */
+ public function getVariationGroups(): array
+ {
+ return $this->variationGroups;
+ }
+
+ /**
+ * @param array $variationGroups
+ */
+ public function setVariationGroups(array $variationGroups): self
+ {
+ $this->variationGroups = $variationGroups;
+ return $this;
+ }
+
+ /**
+ * @param array $data
+ * @return self
+ */
+ public static function fromArray(array $data): self
+ {
+ /**
+ * @var array>|null $variationGroupsData
+ */
+ $variationGroupsData = $data[FlagshipField::FIELD_VARIATION_GROUPS] ?? null;
+ $variationGroups = [];
+
+ if (is_array($variationGroupsData)) {
+ $variationGroups = array_map(
+ VariationGroupDTO::fromArray(...),
+ $variationGroupsData
+ );
+ }
+
+ $id = $data[FlagshipField::FIELD_ID] ?? '';
+ $type = $data[FlagshipField::FIELD_CAMPAIGN_TYPE] ?? '';
+
+ $instance = new self(
+ is_string($id) ? $id : '',
+ is_string($type) ? $type : '',
+ $variationGroups
+ );
+
+ if (isset($data[FlagshipField::FIELD_NANE]) && is_string($data[FlagshipField::FIELD_NANE])) {
+ $instance->setName($data[FlagshipField::FIELD_NANE]);
+ }
+
+ if (array_key_exists(FlagshipField::FIELD_SLUG, $data)) {
+ $slug = $data[FlagshipField::FIELD_SLUG];
+ $instance->setSlug(is_string($slug) ? $slug : null);
+ }
+
+ return $instance;
+ }
+
+ /**
+ * @return BucketingCampaignArray
+ */
+ public function toArray(): array
+ {
+ $result = [
+ FlagshipField::FIELD_ID => $this->id,
+ FlagshipField::FIELD_CAMPAIGN_TYPE => $this->type,
+ FlagshipField::FIELD_VARIATION_GROUPS => array_map(
+ fn(VariationGroupDTO $group) => $group->toArray(),
+ $this->variationGroups
+ ),
+ ];
+
+ if ($this->name !== null) {
+ $result[FlagshipField::FIELD_NANE] = $this->name;
+ }
+
+ if ($this->slug !== null) {
+ $result[FlagshipField::FIELD_SLUG] = $this->slug;
+ }
+
+ return $result;
+ }
+}
diff --git a/src/Model/BucketingDTO.php b/src/Model/BucketingDTO.php
new file mode 100644
index 00000000..06333f87
--- /dev/null
+++ b/src/Model/BucketingDTO.php
@@ -0,0 +1,115 @@
+|null */
+ private ?array $campaigns = null;
+
+ private ?AccountSettingsDTO $accountSettings = null;
+
+ public function getPanic(): ?bool
+ {
+ return $this->panic;
+ }
+
+ public function setPanic(?bool $panic): self
+ {
+ $this->panic = $panic;
+ return $this;
+ }
+
+ /**
+ * @return array|null
+ */
+ public function getCampaigns(): ?array
+ {
+ return $this->campaigns;
+ }
+
+ /**
+ * @param array|null $campaigns
+ */
+ public function setCampaigns(?array $campaigns): self
+ {
+ $this->campaigns = $campaigns;
+ return $this;
+ }
+
+ public function getAccountSettings(): ?AccountSettingsDTO
+ {
+ return $this->accountSettings;
+ }
+
+ public function setAccountSettings(?AccountSettingsDTO $accountSettings): self
+ {
+ $this->accountSettings = $accountSettings;
+ return $this;
+ }
+
+ /**
+ * @param array $data
+ * @return self
+ */
+ public static function fromArray(array $data): self
+ {
+ $instance = new self();
+
+ if (isset($data[FlagshipField::FIELD_PANIC]) && is_bool($data[FlagshipField::FIELD_PANIC])) {
+ $instance->setPanic($data[FlagshipField::FIELD_PANIC]);
+ }
+
+ if (isset($data[FlagshipField::FIELD_CAMPAIGNS]) && is_array($data[FlagshipField::FIELD_CAMPAIGNS])) {
+ /** @var array> $campaignsData */
+ $campaignsData = $data[FlagshipField::FIELD_CAMPAIGNS];
+ $campaigns = array_map(
+ BucketingCampaignDTO::fromArray(...),
+ $campaignsData
+ );
+ $instance->setCampaigns($campaigns);
+ }
+
+ if (isset($data[FlagshipField::ACCOUNT_SETTINGS]) && is_array($data[FlagshipField::ACCOUNT_SETTINGS])) {
+ /** @var array $accountSettingsData */
+ $accountSettingsData = $data[FlagshipField::ACCOUNT_SETTINGS];
+ $instance->setAccountSettings(
+ AccountSettingsDTO::fromArray($accountSettingsData)
+ );
+ }
+
+ return $instance;
+ }
+
+ /**
+ * @return BucketingArray
+ */
+ public function toArray(): array
+ {
+ $result = [];
+
+ if ($this->panic !== null) {
+ $result[FlagshipField::FIELD_PANIC] = $this->panic;
+ }
+
+ if ($this->campaigns !== null) {
+ $result[FlagshipField::FIELD_CAMPAIGNS] = array_map(
+ fn(BucketingCampaignDTO $campaign) => $campaign->toArray(),
+ $this->campaigns
+ );
+ }
+
+ if ($this->accountSettings !== null) {
+ $result[FlagshipField::ACCOUNT_SETTINGS] = $this->accountSettings->toArray();
+ }
+
+ return $result;
+ }
+}
diff --git a/src/Model/BucketingVariationDTO.php b/src/Model/BucketingVariationDTO.php
new file mode 100644
index 00000000..10883acb
--- /dev/null
+++ b/src/Model/BucketingVariationDTO.php
@@ -0,0 +1,68 @@
+allocation;
+ }
+
+ public function setAllocation(?float $allocation): self
+ {
+ $this->allocation = $allocation;
+ return $this;
+ }
+
+ /**
+ * @param array $data
+ * @return self
+ */
+ public static function fromArray(array $data): self
+ {
+
+ $modificationsData = $data[FlagshipField::FIELD_MODIFICATIONS] ?? null;
+ $modifications = is_array($modificationsData)
+ ? ModificationsDTO::fromArray($modificationsData)
+ : new ModificationsDTO('', []);
+
+ $id = $data[FlagshipField::FIELD_ID] ?? '';
+
+ $instance = new self(is_string($id) ? $id : '', $modifications);
+
+ if (isset($data[FlagshipField::FIELD_NANE]) && is_string($data[FlagshipField::FIELD_NANE])) {
+ $instance->setName($data[FlagshipField::FIELD_NANE]);
+ }
+
+ if (isset($data[FlagshipField::FIELD_REFERENCE]) && is_bool($data[FlagshipField::FIELD_REFERENCE])) {
+ $instance->setReference($data[FlagshipField::FIELD_REFERENCE]);
+ }
+
+ if (isset($data[FlagshipField::FIELD_ALLOCATION])) {
+ $allocation = $data[FlagshipField::FIELD_ALLOCATION];
+ $instance->setAllocation(is_numeric($allocation) ? (float)$allocation : null);
+ }
+
+ return $instance;
+ }
+
+ /**
+ * @return BucketingVariationArray
+ */
+ public function toArray(): array
+ {
+ $result = parent::toArray();
+
+ $result[FlagshipField::FIELD_ALLOCATION] = $this->allocation;
+
+ return $result;
+ }
+}
diff --git a/src/Model/CampaignCacheDTO.php b/src/Model/CampaignCacheDTO.php
new file mode 100644
index 00000000..666fab01
--- /dev/null
+++ b/src/Model/CampaignCacheDTO.php
@@ -0,0 +1,228 @@
+campaignId = $campaignId;
+ $this->variationGroupId = $variationGroupId;
+ $this->variationId = $variationId;
+ $this->flags = $flags;
+ }
+
+ public function getCampaignId(): string
+ {
+ return $this->campaignId;
+ }
+
+ public function setCampaignId(string $campaignId): self
+ {
+ $this->campaignId = $campaignId;
+ return $this;
+ }
+
+ public function getVariationGroupId(): string
+ {
+ return $this->variationGroupId;
+ }
+
+ public function setVariationGroupId(string $variationGroupId): self
+ {
+ $this->variationGroupId = $variationGroupId;
+ return $this;
+ }
+
+ public function getVariationId(): string
+ {
+ return $this->variationId;
+ }
+
+ public function setVariationId(string $variationId): self
+ {
+ $this->variationId = $variationId;
+ return $this;
+ }
+
+ public function getType(): ?string
+ {
+ return $this->type;
+ }
+
+ public function setType(?string $type): self
+ {
+ $this->type = $type;
+ return $this;
+ }
+
+ public function getSlug(): ?string
+ {
+ return $this->slug;
+ }
+
+ public function setSlug(?string $slug): self
+ {
+ $this->slug = $slug;
+ return $this;
+ }
+
+ public function getName(): ?string
+ {
+ return $this->name;
+ }
+
+ public function setName(?string $name): self
+ {
+ $this->name = $name;
+ return $this;
+ }
+
+ public function getIsReference(): ?bool
+ {
+ return $this->isReference;
+ }
+
+ public function setIsReference(?bool $isReference): self
+ {
+ $this->isReference = $isReference;
+ return $this;
+ }
+
+ public function getActivated(): ?bool
+ {
+ return $this->activated;
+ }
+
+ public function setActivated(?bool $activated): self
+ {
+ $this->activated = $activated;
+ return $this;
+ }
+
+ /**
+ * @return ModificationsDTO
+ */
+ public function getFlags(): ModificationsDTO
+ {
+ return $this->flags;
+ }
+
+ /**
+ * @param ModificationsDTO $flags
+ */
+ public function setFlags(ModificationsDTO $flags): self
+ {
+ $this->flags = $flags;
+ return $this;
+ }
+
+ /**
+ * @param array $data
+ * @return self
+ */
+ public static function fromArray(array $data): self
+ {
+ $campaignId = $data[StrategyAbstract::CAMPAIGN_ID] ?? '';
+ $variationGroupId = $data[StrategyAbstract::VARIATION_GROUP_ID] ?? '';
+ $variationId = $data[StrategyAbstract::VARIATION_ID] ?? '';
+
+ $flags = null;
+
+ if (isset($data[FlagshipField::FIELD_FLAGS]) && is_array($data[FlagshipField::FIELD_FLAGS])) {
+ $flags = ModificationsDTO::fromArray($data[FlagshipField::FIELD_FLAGS]);
+ }
+
+ $instance = new self(
+ is_string($campaignId) ? $campaignId : '',
+ is_string($variationGroupId) ? $variationGroupId : '',
+ is_string($variationId) ? $variationId : '',
+ $flags ?? new ModificationsDTO('', [])
+ );
+
+ if (isset($data[FlagshipField::FIELD_CAMPAIGN_TYPE]) && is_string($data[FlagshipField::FIELD_CAMPAIGN_TYPE])) {
+ $instance->setType($data[FlagshipField::FIELD_CAMPAIGN_TYPE]);
+ }
+
+ if (isset($data[FlagshipField::FIELD_SLUG]) && is_string($data[FlagshipField::FIELD_SLUG])) {
+ $instance->setSlug(FlagshipField::FIELD_SLUG);
+ }
+
+ if (isset($data[FlagshipField::FIELD_CAMPAIGN_NAME]) && is_string($data[FlagshipField::FIELD_CAMPAIGN_NAME])) {
+ $instance->setName($data[FlagshipField::FIELD_CAMPAIGN_NAME]);
+ }
+
+ if (isset($data[FlagshipField::FIELD_IS_REFERENCE]) && is_bool($data[FlagshipField::FIELD_IS_REFERENCE])) {
+ $instance->setIsReference($data[FlagshipField::FIELD_IS_REFERENCE]);
+ }
+
+ if (isset($data[FlagshipField::FIELD_ACTIVATED]) && is_bool($data[FlagshipField::FIELD_ACTIVATED])) {
+ $instance->setActivated($data[FlagshipField::FIELD_ACTIVATED]);
+ }
+
+
+
+ return $instance;
+ }
+
+ /**
+ * @return CampaignCacheArray
+ */
+ public function toArray(): array
+ {
+ $result = [
+ StrategyAbstract::CAMPAIGN_ID => $this->campaignId,
+ StrategyAbstract::VARIATION_GROUP_ID => $this->variationGroupId,
+ StrategyAbstract::VARIATION_ID => $this->variationId,
+ FlagshipField::FIELD_CAMPAIGN_TYPE => $this->type,
+ FlagshipField::FIELD_FLAGS => $this->flags->toArray(),
+ ];
+
+ if ($this->slug !== null) {
+ $result[FlagshipField::FIELD_SLUG] = $this->slug;
+ }
+
+ if ($this->name !== null) {
+ $result[FlagshipField::FIELD_CAMPAIGN_NAME] = $this->name;
+ }
+
+ if ($this->isReference !== null) {
+ $result[FlagshipField::FIELD_IS_REFERENCE] = $this->isReference;
+ }
+
+ if ($this->activated !== null) {
+ $result[FlagshipField::FIELD_ACTIVATED] = $this->activated;
+ }
+
+ return $result;
+ }
+}
diff --git a/src/Model/CampaignDTO.php b/src/Model/CampaignDTO.php
new file mode 100644
index 00000000..1e0b6974
--- /dev/null
+++ b/src/Model/CampaignDTO.php
@@ -0,0 +1,174 @@
+id = $id;
+ $this->variationGroupId = $variationGroupId;
+ $this->variation = $variation;
+ }
+
+ public function getId(): string
+ {
+ return $this->id;
+ }
+
+ public function setId(string $id): self
+ {
+ $this->id = $id;
+ return $this;
+ }
+
+ public function getName(): ?string
+ {
+ return $this->name;
+ }
+
+ public function setName(?string $name): self
+ {
+ $this->name = $name;
+ return $this;
+ }
+
+ public function getSlug(): ?string
+ {
+ return $this->slug;
+ }
+
+ public function setSlug(?string $slug): self
+ {
+ $this->slug = $slug;
+ return $this;
+ }
+
+ public function getVariationGroupId(): string
+ {
+ return $this->variationGroupId;
+ }
+
+ public function setVariationGroupId(string $variationGroupId): self
+ {
+ $this->variationGroupId = $variationGroupId;
+ return $this;
+ }
+
+ public function getVariationGroupName(): ?string
+ {
+ return $this->variationGroupName;
+ }
+
+ public function setVariationGroupName(?string $variationGroupName): self
+ {
+ $this->variationGroupName = $variationGroupName;
+ return $this;
+ }
+
+ public function getVariation(): VariationDTO
+ {
+ return $this->variation;
+ }
+
+ public function setVariation(VariationDTO $variation): self
+ {
+ $this->variation = $variation;
+ return $this;
+ }
+
+ public function getType(): ?string
+ {
+ return $this->type;
+ }
+
+ public function setType(?string $type): self
+ {
+ $this->type = $type;
+ return $this;
+ }
+
+ /**
+ * @param array $data
+ */
+ public static function fromArray(array $data): self
+ {
+ $variationData = $data[FlagshipField::FIELD_VARIATION] ?? [];
+ $variation = is_array($variationData)
+ ? VariationDTO::fromArray($variationData)
+ : new VariationDTO('',
+ new ModificationsDTO('', []));
+
+ $id = $data[FlagshipField::FIELD_ID] ?? '';
+ $variationGroupId = $data[FlagshipField::FIELD_VARIATION_GROUP_ID] ?? '';
+
+ $instance = new self(
+ is_string($id) ? $id : '',
+ is_string($variationGroupId) ? $variationGroupId : '',
+ $variation
+ );
+
+ if (isset($data[FlagshipField::FIELD_NANE]) && is_string($data[FlagshipField::FIELD_NANE])) {
+ $instance->setName($data[FlagshipField::FIELD_NANE]);
+ }
+
+ if (isset($data[FlagshipField::FIELD_SLUG]) && is_string($data[FlagshipField::FIELD_SLUG])) {
+ $instance->setSlug($data[FlagshipField::FIELD_SLUG]);
+ }
+
+ if (isset($data[FlagshipField::FIELD_VARIATION_GROUP_NAME]) && is_string($data[FlagshipField::FIELD_VARIATION_GROUP_NAME])) {
+ $instance->setVariationGroupName($data[FlagshipField::FIELD_VARIATION_GROUP_NAME]);
+ }
+
+ if (isset($data[FlagshipField::FIELD_CAMPAIGN_TYPE]) && is_string($data[FlagshipField::FIELD_CAMPAIGN_TYPE])) {
+ $instance->setType($data[FlagshipField::FIELD_CAMPAIGN_TYPE]);
+ }
+
+ return $instance;
+ }
+
+ /**
+ * @return CampaignArray
+ */
+ public function toArray(): array
+ {
+ return [
+ FlagshipField::FIELD_ID => $this->id,
+ FlagshipField::FIELD_NANE => $this->name,
+ FlagshipField::FIELD_SLUG => $this->slug,
+ FlagshipField::FIELD_VARIATION_GROUP_ID => $this->variationGroupId,
+ FlagshipField::FIELD_VARIATION_GROUP_NAME => $this->variationGroupName,
+ FlagshipField::FIELD_VARIATION => $this->variation->toArray(),
+ FlagshipField::FIELD_CAMPAIGN_TYPE => $this->type,
+ ];
+ }
+
+ public function jsonSerialize(): mixed
+ {
+ return $this->toArray();
+ }
+}
diff --git a/src/Model/ExposedFlag.php b/src/Model/ExposedFlag.php
index 239c7b10..3eb0f098 100644
--- a/src/Model/ExposedFlag.php
+++ b/src/Model/ExposedFlag.php
@@ -12,7 +12,7 @@ class ExposedFlag implements ExposedFlagInterface
private string $key;
/**
- * @var bool|numeric|string|array
+ * @var scalar|array|null
*/
private string|array|bool|int|float|null $value;
@@ -22,14 +22,14 @@ class ExposedFlag implements ExposedFlagInterface
private FSFlagMetadataInterface $metadata;
/**
- * @var bool|numeric|string|array
+ * @var scalar|array|null
*/
private string|array|bool|int|float|null $defaultValue;
/**
* @param string $key
- * @param array|bool|string|numeric $value
- * @param array|bool|string|numeric $defaultValue
+ * @param scalar|array|null $value
+ * @param scalar|array|null $defaultValue
* @param FSFlagMetadataInterface $metadata
*/
public function __construct(
@@ -53,9 +53,9 @@ public function getKey(): string
}
/**
- * @return bool|numeric|string|array
+ * @inheritDoc
*/
- public function getValue(): float|int|bool|array|string
+ public function getValue(): float|int|bool|array|string|null
{
return $this->value;
}
@@ -71,7 +71,7 @@ public function getMetadata(): FSFlagMetadataInterface
/**
* @inheritDoc
*/
- public function getDefaultValue(): float|array|bool|int|string
+ public function getDefaultValue(): float|array|bool|int|string|null
{
return $this->defaultValue;
}
diff --git a/src/Model/ExposedFlagInterface.php b/src/Model/ExposedFlagInterface.php
index 7ac4b83b..065c525d 100644
--- a/src/Model/ExposedFlagInterface.php
+++ b/src/Model/ExposedFlagInterface.php
@@ -14,7 +14,7 @@ public function getKey(): string;
/**
* Return the value of flag
- * @return float|array|bool|int|string|null
+ * @return scalar|array|null
*/
public function getValue(): float|array|bool|int|string|null;
@@ -26,7 +26,7 @@ public function getMetadata(): FSFlagMetadataInterface;
/**
* Return the default value of flag
- * @return float|array|bool|int|string|null
+ * @return scalar|array|null
*/
public function getDefaultValue(): float|array|bool|int|string|null;
}
diff --git a/src/Model/ExposedVisitor.php b/src/Model/ExposedVisitor.php
index 65e7e340..7ded2244 100644
--- a/src/Model/ExposedVisitor.php
+++ b/src/Model/ExposedVisitor.php
@@ -18,14 +18,14 @@ class ExposedVisitor implements ExposedVisitorInterface
private ?string $anonymousId;
/**
- * @var array
+ * @var array
*/
private array $context;
/**
* @param string $id
* @param ?string $anonymousId
- * @param array $context
+ * @param array $context
*/
public function __construct(string $id, ?string $anonymousId, array $context)
{
@@ -51,7 +51,7 @@ public function getAnonymousId(): ?string
}
/**
- * @return array
+ * @return array
*/
public function getContext(): array
{
diff --git a/src/Model/FlagDTO.php b/src/Model/FlagDTO.php
index 6b833d4d..ac01f17c 100644
--- a/src/Model/FlagDTO.php
+++ b/src/Model/FlagDTO.php
@@ -48,8 +48,9 @@ class FlagDTO implements JsonSerializable
* @var bool
*/
private bool $isReference;
+
/**
- * @var string|bool|numeric
+ * @var scalar|array|null
*/
private string|int|bool|float|null|array $value;
@@ -75,7 +76,7 @@ public function getKey(): string
* @param string $key
* @return FlagDTO
*/
- public function setKey(string $key): static
+ public function setKey(string $key): self
{
$this->key = $key;
return $this;
@@ -93,7 +94,7 @@ public function getCampaignId(): string
* @param string $campaignId
* @return FlagDTO
*/
- public function setCampaignId(string $campaignId): static
+ public function setCampaignId(string $campaignId): self
{
$this->campaignId = $campaignId;
return $this;
@@ -111,7 +112,7 @@ public function getVariationGroupId(): string
* @param string $variationGroupId
* @return FlagDTO
*/
- public function setVariationGroupId(string $variationGroupId): static
+ public function setVariationGroupId(string $variationGroupId): self
{
$this->variationGroupId = $variationGroupId;
return $this;
@@ -129,7 +130,7 @@ public function getVariationId(): string
* @param string $variationId
* @return FlagDTO
*/
- public function setVariationId(string $variationId): static
+ public function setVariationId(string $variationId): self
{
$this->variationId = $variationId;
return $this;
@@ -147,14 +148,14 @@ public function getIsReference(): bool
* @param bool $isReference
* @return FlagDTO
*/
- public function setIsReference(bool $isReference): static
+ public function setIsReference(bool $isReference): self
{
$this->isReference = $isReference;
return $this;
}
/**
- * @return float|bool|int|string|array|null
+ * @return scalar|array|null $value
*/
public function getValue(): float|bool|int|string|null|array
{
@@ -162,10 +163,10 @@ public function getValue(): float|bool|int|string|null|array
}
/**
- * @param bool|string|numeric $value
+ * @param scalar|array|null $value
* @return FlagDTO
*/
- public function setValue(float|bool|int|string|null|array $value): static
+ public function setValue(float|bool|int|string|null|array $value): self
{
$this->value = $value;
return $this;
@@ -183,7 +184,7 @@ public function getCampaignType(): string
* @param string $campaignType
* @return FlagDTO
*/
- public function setCampaignType(string $campaignType): static
+ public function setCampaignType(string $campaignType): self
{
$this->campaignType = $campaignType;
return $this;
@@ -201,7 +202,7 @@ public function getSlug(): ?string
* @param string $slug
* @return FlagDTO
*/
- public function setSlug(string $slug): static
+ public function setSlug(string $slug): self
{
$this->slug = $slug;
return $this;
@@ -219,7 +220,7 @@ public function getCampaignName(): ?string
* @param string $campaignName
* @return FlagDTO
*/
- public function setCampaignName(string $campaignName): static
+ public function setCampaignName(string $campaignName): self
{
$this->campaignName = $campaignName;
return $this;
@@ -237,7 +238,7 @@ public function getVariationGroupName(): ?string
* @param string $variationGroupName
* @return FlagDTO
*/
- public function setVariationGroupName(string $variationGroupName): static
+ public function setVariationGroupName(string $variationGroupName): self
{
$this->variationGroupName = $variationGroupName;
return $this;
@@ -255,7 +256,7 @@ public function getVariationName(): ?string
* @param string $variationName
* @return FlagDTO
*/
- public function setVariationName(string $variationName): static
+ public function setVariationName(string $variationName): self
{
$this->variationName = $variationName;
return $this;
@@ -264,16 +265,16 @@ public function setVariationName(string $variationName): static
public function jsonSerialize(): mixed
{
return [
- FlagshipField::FIELD_KEY => $this->getKey(),
- FlagshipField::FIELD_CAMPAIGN_ID => $this->getCampaignId(),
- FlagshipField::FIELD_CAMPAIGN_NAME => $this->getCampaignName(),
- FlagshipField::FIELD_VARIATION_GROUP_ID => $this->getVariationGroupId(),
- FlagshipField::FIELD_VARIATION_GROUP_NAME => $this->getVariationGroupName(),
- FlagshipField::FIELD_VARIATION_ID => $this->getVariationId(),
- FlagshipField::FIELD_VARIATION_NAME => $this->getVariationName(),
- FlagshipField::FIELD_IS_REFERENCE => $this->getIsReference(),
- FlagshipField::FIELD_VALUE => $this->getValue(),
- FlagshipField::FIELD_SLUG => $this->getSlug(),
- ];
+ FlagshipField::FIELD_KEY => $this->getKey(),
+ FlagshipField::FIELD_CAMPAIGN_ID => $this->getCampaignId(),
+ FlagshipField::FIELD_CAMPAIGN_NAME => $this->getCampaignName(),
+ FlagshipField::FIELD_VARIATION_GROUP_ID => $this->getVariationGroupId(),
+ FlagshipField::FIELD_VARIATION_GROUP_NAME => $this->getVariationGroupName(),
+ FlagshipField::FIELD_VARIATION_ID => $this->getVariationId(),
+ FlagshipField::FIELD_VARIATION_NAME => $this->getVariationName(),
+ FlagshipField::FIELD_IS_REFERENCE => $this->getIsReference(),
+ FlagshipField::FIELD_VALUE => $this->getValue(),
+ FlagshipField::FIELD_SLUG => $this->getSlug(),
+ ];
}
}
diff --git a/src/Model/HttpResponse.php b/src/Model/HttpResponse.php
index 0471dd76..ce6d1e50 100644
--- a/src/Model/HttpResponse.php
+++ b/src/Model/HttpResponse.php
@@ -11,7 +11,7 @@ class HttpResponse
private string|int $statusCode;
private mixed $body;
/**
- * @var array
+ * @var array $headers
*/
private array $headers;
@@ -19,7 +19,7 @@ class HttpResponse
* HttpResponse constructor.
* @param string|int $statusCode
* @param mixed $body
- * @param array $headers
+ * @param array $headers
*/
public function __construct(
string|int $statusCode,
@@ -40,10 +40,10 @@ public function getStatusCode(): string|int
}
/**
- * @param mixed $statusCode
+ * @param string|int $statusCode
* @return HttpResponse
*/
- public function setStatusCode(mixed $statusCode): static
+ public function setStatusCode(string|int $statusCode): self
{
$this->statusCode = $statusCode;
return $this;
@@ -61,14 +61,14 @@ public function getBody(): mixed
* @param mixed $body
* @return HttpResponse
*/
- public function setBody(mixed $body): static
+ public function setBody(mixed $body): self
{
$this->body = $body;
return $this;
}
/**
- * @return array
+ * @return array
*/
public function getHeaders(): array
{
diff --git a/src/Model/ModificationsDTO.php b/src/Model/ModificationsDTO.php
new file mode 100644
index 00000000..b5a982ca
--- /dev/null
+++ b/src/Model/ModificationsDTO.php
@@ -0,0 +1,90 @@
+type = $type;
+ $this->value = $value;
+ }
+
+ public function getType(): string
+ {
+ return $this->type;
+ }
+
+ public function setType(string $type): self
+ {
+ $this->type = $type;
+ return $this;
+ }
+
+ /**
+ *
+ * @return FlagValue
+ */
+ public function getValue(): array
+ {
+ return $this->value;
+ }
+
+ /**
+ *
+ * @param FlagValue $value
+ * @return self
+ */
+ public function setValue(array $value): self
+ {
+ $this->value = $value;
+ return $this;
+ }
+
+ /**
+ * @param array $data
+ * @return self
+ */
+ public static function fromArray(array $data): self
+ {
+ $type = $data[FlagshipField::FIELD_CAMPAIGN_TYPE] ?? '';
+
+ /** @var FlagValue|null $value */
+ $value = $data[FlagshipField::FIELD_VALUE] ?? null;
+
+ return new self(
+ is_string($type) ? $type : '',
+ is_array($value) ? $value : []
+ );
+ }
+
+ /**
+ * @return ModificationsArray
+ */
+ public function toArray(): array
+ {
+ return [
+ FlagshipField::FIELD_CAMPAIGN_TYPE => $this->type,
+ FlagshipField::FIELD_VALUE => $this->value,
+ ];
+ }
+}
diff --git a/src/Model/TargetingDTO.php b/src/Model/TargetingDTO.php
new file mode 100644
index 00000000..fcf80bf8
--- /dev/null
+++ b/src/Model/TargetingDTO.php
@@ -0,0 +1,75 @@
+ */
+ private array $targetingGroups;
+
+ /**
+ * @param array $targetingGroups
+ */
+ public function __construct(array $targetingGroups)
+ {
+ $this->targetingGroups = $targetingGroups;
+ }
+
+ /**
+ * @return array
+ */
+ public function getTargetingGroups(): array
+ {
+ return $this->targetingGroups;
+ }
+
+ /**
+ * @param array $targetingGroups
+ */
+ public function setTargetingGroups(array $targetingGroups): self
+ {
+ $this->targetingGroups = $targetingGroups;
+ return $this;
+ }
+
+ /**
+ * @param array $data
+ * @return self
+ */
+ public static function fromArray(array $data): self
+ {
+ /**
+ * @var array> | null $targetingGroupsData
+ */
+ $targetingGroupsData = $data[FlagshipField::FIELD_TARGETING_GROUPS] ?? [];
+
+ $targetingGroups = [];
+
+ if (is_array($targetingGroupsData)) {
+ $targetingGroups = array_map(
+ TargetingGroupDTO::fromArray(...),
+ $targetingGroupsData
+ );
+ }
+
+ return new self(array_values($targetingGroups));
+ }
+
+ /**
+ * @return TargetingArray
+ */
+ public function toArray(): array
+ {
+ return [
+ FlagshipField::FIELD_TARGETING_GROUPS => array_map(
+ fn(TargetingGroupDTO $group) => $group->toArray(),
+ $this->targetingGroups
+ ),
+ ];
+ }
+}
diff --git a/src/Model/TargetingGroupDTO.php b/src/Model/TargetingGroupDTO.php
new file mode 100644
index 00000000..db3522dc
--- /dev/null
+++ b/src/Model/TargetingGroupDTO.php
@@ -0,0 +1,74 @@
+ */
+ private array $targetings;
+
+ /**
+ * @param array $targetings
+ */
+ public function __construct(array $targetings)
+ {
+ $this->targetings = $targetings;
+ }
+
+ /**
+ * @return array
+ */
+ public function getTargetings(): array
+ {
+ return $this->targetings;
+ }
+
+ /**
+ * @param array $targetings
+ */
+ public function setTargetings(array $targetings): self
+ {
+ $this->targetings = $targetings;
+ return $this;
+ }
+
+ /**
+ * @param array $data
+ * @return self
+ */
+ public static function fromArray(array $data): self
+ {
+ /**
+ * @var array> | null $targetingsData
+ */
+ $targetingsData = $data[FlagshipField::FIELD_TARGETINGS] ?? [];
+ $targetings = [];
+
+ if (is_array($targetingsData)) {
+ $targetings = array_map(
+ fn($targeting) => TargetingsDTO::fromArray($targeting),
+ $targetingsData
+ );
+ }
+
+ return new self($targetings);
+ }
+
+ /**
+ * @return TargetingGroupArray
+ */
+ public function toArray(): array
+ {
+ return [
+ FlagshipField::FIELD_TARGETINGS => array_map(
+ fn(TargetingsDTO $targeting) => $targeting->toArray(),
+ $this->targetings
+ ),
+ ];
+ }
+}
diff --git a/src/Model/TargetingsDTO.php b/src/Model/TargetingsDTO.php
new file mode 100644
index 00000000..50425e67
--- /dev/null
+++ b/src/Model/TargetingsDTO.php
@@ -0,0 +1,110 @@
+operator;
+ }
+
+ public function setOperator(TargetingOperator $operator): self
+ {
+ $this->operator = $operator;
+ return $this;
+ }
+
+ public function getKey(): string
+ {
+ return $this->key;
+ }
+
+ public function setKey(string $key): self
+ {
+ $this->key = $key;
+ return $this;
+ }
+
+ /**
+ *
+ * @return mixed
+ */
+ public function getValue(): mixed
+ {
+ return $this->value;
+ }
+
+ /**
+ *
+ * @param mixed $value
+ * @return self
+ */
+ public function setValue(mixed $value): self
+ {
+ $this->value = $value;
+ return $this;
+ }
+
+ /**
+ *
+ * @param TargetingOperator $operator
+ * @param string $key
+ * @param mixed $value
+ */
+ public function __construct(TargetingOperator $operator, string $key, mixed $value)
+ {
+ $this->operator = $operator;
+ $this->key = $key;
+ $this->value = $value;
+ }
+
+ /**
+ * @param array $data
+ * @return self
+ */
+ public static function fromArray(array $data): self
+ {
+ $operatorValue = $data[FlagshipField::FIELD_OPERATOR] ?? '';
+ $operator = is_string($operatorValue)
+ ? (TargetingOperator::tryFrom($operatorValue) ?? TargetingOperator::EQUALS)
+ : TargetingOperator::EQUALS;
+
+ $key = $data[FlagshipField::FIELD_KEY] ?? '';
+
+ /** @var mixed|null $value */
+ $value = $data[FlagshipField::FIELD_VALUE] ?? null;
+
+ return new self(
+ $operator,
+ is_string($key) ? $key : '',
+ $value,
+ );
+ }
+
+ /**
+ * @return TargetingsArray
+ */
+ public function toArray(): array
+ {
+ return [
+ FlagshipField::FIELD_OPERATOR => $this->operator->value,
+ FlagshipField::FIELD_KEY => $this->key,
+ FlagshipField::FIELD_VALUE => $this->value,
+ ];
+ }
+}
diff --git a/src/Model/TroubleshootingDTO.php b/src/Model/TroubleshootingDTO.php
new file mode 100644
index 00000000..5995f133
--- /dev/null
+++ b/src/Model/TroubleshootingDTO.php
@@ -0,0 +1,160 @@
+startDate = $startDate;
+ $this->endDate = $endDate;
+ $this->traffic = $traffic;
+ $this->timezone = $timezone;
+ }
+
+ public function getStartDate(): string
+ {
+ return $this->startDate;
+ }
+
+ public function setStartDate(string $startDate): self
+ {
+ $this->startDate = $startDate;
+ return $this;
+ }
+
+ /**
+ * Get start date as DateTime object
+ *
+ * @return DateTime|null
+ */
+ public function getStartDateAsDateTime(): ?DateTime
+ {
+ try {
+ return new DateTime($this->startDate);
+ } catch (Exception $e) {
+ return null;
+ }
+ }
+
+ public function getEndDate(): string
+ {
+ return $this->endDate;
+ }
+
+ public function setEndDate(string $endDate): self
+ {
+ $this->endDate = $endDate;
+ return $this;
+ }
+
+ /**
+ * Get end date as DateTime object
+ *
+ * @return DateTime|null
+ */
+ public function getEndDateAsDateTime(): ?DateTime
+ {
+ try {
+ return new DateTime($this->endDate);
+ } catch (Exception $e) {
+ return null;
+ }
+ }
+
+ public function getTraffic(): float
+ {
+ return $this->traffic;
+ }
+
+ public function setTraffic(float $traffic): self
+ {
+ $this->traffic = $traffic;
+ return $this;
+ }
+
+ public function getTimezone(): string
+ {
+ return $this->timezone;
+ }
+
+ public function setTimezone(string $timezone): self
+ {
+ $this->timezone = $timezone;
+ return $this;
+ }
+
+ /**
+ * @param array $data
+ * @return self
+ */
+ public static function fromArray(array $data): self
+ {
+ $startDate = $data[FlagshipField::START_DATE] ?? '';
+ $endDate = $data[FlagshipField::END_DATE] ?? '';
+ $traffic = $data[FlagshipField::TRAFFIC] ?? 0;
+ $timezone = $data[FlagshipField::TIMEZONE] ?? '';
+
+ return new self(
+ is_string($startDate) ? $startDate : '',
+ is_string($endDate) ? $endDate : '',
+ is_numeric($traffic) ? (float)$traffic : 0.0,
+ is_string($timezone) ? $timezone : ''
+ );
+ }
+
+ /**
+ * @return TroubleshootingArray
+ */
+ public function toArray(): array
+ {
+ return [
+ FlagshipField::START_DATE => $this->startDate,
+ FlagshipField::END_DATE => $this->endDate,
+ FlagshipField::TRAFFIC => $this->traffic,
+ FlagshipField::TIMEZONE => $this->timezone,
+ ];
+ }
+
+ /**
+ * Convert to legacy TroubleshootingData format
+ *
+ * @return TroubleshootingData|null
+ */
+ public function toTroubleshootingData(): ?TroubleshootingData
+ {
+ $startDateTime = $this->getStartDateAsDateTime();
+ $endDateTime = $this->getEndDateAsDateTime();
+
+ if (!$startDateTime || !$endDateTime) {
+ return null;
+ }
+
+ $data = new TroubleshootingData();
+ $data->setStartDate($startDateTime)
+ ->setEndDate($endDateTime)
+ ->setTraffic($this->traffic)
+ ->setTimezone($this->timezone);
+
+ return $data;
+ }
+}
diff --git a/src/Model/TroubleshootingData.php b/src/Model/TroubleshootingData.php
index 46e6f4d6..b4668006 100644
--- a/src/Model/TroubleshootingData.php
+++ b/src/Model/TroubleshootingData.php
@@ -17,9 +17,9 @@ class TroubleshootingData
private DateTime $endDate;
/**
- * @var numeric
+ * @var int|float $traffic
*/
- private string|int|float $traffic;
+ private int|float $traffic;
/**
* @var string
@@ -38,7 +38,7 @@ public function getStartDate(): DateTime
* @param DateTime $startDate
* @return TroubleshootingData
*/
- public function setStartDate(DateTime $startDate): static
+ public function setStartDate(DateTime $startDate): self
{
$this->startDate = $startDate;
return $this;
@@ -56,7 +56,7 @@ public function getEndDate(): DateTime
* @param DateTime $endDate
* @return TroubleshootingData
*/
- public function setEndDate(DateTime $endDate): static
+ public function setEndDate(DateTime $endDate): self
{
$this->endDate = $endDate;
return $this;
@@ -74,7 +74,7 @@ public function getTraffic(): float|int
* @param float|int $traffic
* @return TroubleshootingData
*/
- public function setTraffic(float|int $traffic): static
+ public function setTraffic(float|int $traffic): self
{
$this->traffic = $traffic;
return $this;
@@ -92,7 +92,7 @@ public function getTimezone(): string
* @param string $timezone
* @return TroubleshootingData
*/
- public function setTimezone(string $timezone): static
+ public function setTimezone(string $timezone): self
{
$this->timezone = $timezone;
return $this;
diff --git a/src/Model/Types.php b/src/Model/Types.php
new file mode 100644
index 00000000..b06e7d8a
--- /dev/null
+++ b/src/Model/Types.php
@@ -0,0 +1,33 @@
+, assignmentsHistory?: array, campaigns?: array}
+ * @phpstan-type VisitorCacheArray array{version: int, data: VisitorCacheDataArray}
+ * @phpstan-type FlagValue array|bool|float|int|string|null>
+ * @phpstan-type TargetingsArray array{operator: string, key: string, value: mixed}
+ * @phpstan-type TargetingGroupArray array{targetings: array}
+ * @phpstan-type TargetingArray array{targetingGroups: array}
+ * @phpstan-type BucketingVariationArray array{id: string, name: string|null, modifications: ModificationsArray, allocation: float|null, reference: bool|null}
+ * @phpstan-type VariationGroupArray array{id: string, name?: string, targeting: TargetingArray, variations: array}
+ * @phpstan-type TroubleshootingArray array{startDate: string, endDate: string, traffic: float, timezone: string}
+ * @phpstan-type AccountSettingsArray array{enabledXPC?: bool, troubleshooting?: TroubleshootingArray, eaiCollectEnabled?: bool, eaiActivationEnabled?: bool}
+ * @phpstan-type BucketingCampaignArray array{id: string, name?: string, type: string, slug?: string|null, variationGroups: array}
+ * @phpstan-type BucketingArray array{panic?: bool, campaigns?: array, accountSettings?: AccountSettingsArray}
+ * @phpstan-type HitCacheDataArray array{visitorId: string, anonymousId?: string|null, type: string, time: float|null, content: array}
+ * @phpstan-type HitCacheArray array{version: int, data: HitCacheDataArray}
+ */
+final class Types
+{
+ private function __construct() {}
+}
diff --git a/src/Model/VariationDTO.php b/src/Model/VariationDTO.php
new file mode 100644
index 00000000..583addef
--- /dev/null
+++ b/src/Model/VariationDTO.php
@@ -0,0 +1,107 @@
+id = $id;
+ $this->modifications = $modifications;
+ }
+
+ public function getId(): string
+ {
+ return $this->id;
+ }
+
+ public function setId(string $id): self
+ {
+ $this->id = $id;
+ return $this;
+ }
+
+ public function getName(): ?string
+ {
+ return $this->name;
+ }
+
+ public function setName(?string $name): self
+ {
+ $this->name = $name;
+ return $this;
+ }
+
+ public function getReference(): ?bool
+ {
+ return $this->reference;
+ }
+
+ public function setReference(?bool $reference): self
+ {
+ $this->reference = $reference;
+ return $this;
+ }
+
+ public function getModifications(): ModificationsDTO
+ {
+ return $this->modifications;
+ }
+
+ public function setModifications(ModificationsDTO $modifications): self
+ {
+ $this->modifications = $modifications;
+ return $this;
+ }
+
+ /**
+ * @param array $data
+ * @return self
+ */
+ public static function fromArray(array $data): self
+ {
+ $modificationsData = $data[FlagshipField::FIELD_MODIFICATIONS]?? null;
+ $modifications = is_array($modificationsData)
+ ? ModificationsDTO::fromArray($modificationsData)
+ : new ModificationsDTO('', []);
+
+ $id = $data[FlagshipField::FIELD_ID] ?? '';
+ $instance = new self(is_string($id) ? $id : '', $modifications);
+
+ if (isset($data[FlagshipField::FIELD_NANE]) && is_string($data[FlagshipField::FIELD_NANE])) {
+ $instance->setName($data[FlagshipField::FIELD_NANE]);
+ }
+
+ if (isset($data[FlagshipField::FIELD_REFERENCE]) && is_bool($data[FlagshipField::FIELD_REFERENCE])) {
+ $instance->setReference($data[FlagshipField::FIELD_REFERENCE]);
+ }
+
+ return $instance;
+ }
+
+ /**
+ * @return VariationArray
+ */
+ public function toArray(): array
+ {
+ return [
+ FlagshipField::FIELD_ID => $this->id,
+ FlagshipField::FIELD_NANE => $this->name,
+ FlagshipField::FIELD_REFERENCE => $this->reference,
+ FlagshipField::FIELD_MODIFICATIONS => $this->modifications->toArray(),
+ ];
+ }
+}
diff --git a/src/Model/VariationGroupDTO.php b/src/Model/VariationGroupDTO.php
new file mode 100644
index 00000000..115af712
--- /dev/null
+++ b/src/Model/VariationGroupDTO.php
@@ -0,0 +1,144 @@
+ */
+ private array $variations;
+
+ /**
+ * @param string $id
+ * @param TargetingDTO $targeting
+ * @param array $variations
+ */
+ public function __construct(string $id, TargetingDTO $targeting, array $variations)
+ {
+ $this->id = $id;
+ $this->targeting = $targeting;
+ $this->variations = $variations;
+ }
+
+ public function getId(): string
+ {
+ return $this->id;
+ }
+
+ public function setId(string $id): self
+ {
+ $this->id = $id;
+ return $this;
+ }
+
+ public function getName(): ?string
+ {
+ return $this->name;
+ }
+
+ public function setName(?string $name): self
+ {
+ $this->name = $name;
+ return $this;
+ }
+
+ public function getTargeting(): TargetingDTO
+ {
+ return $this->targeting;
+ }
+
+ public function setTargeting(TargetingDTO $targeting): self
+ {
+ $this->targeting = $targeting;
+ return $this;
+ }
+
+ /**
+ * @return array
+ */
+ public function getVariations(): array
+ {
+ return $this->variations;
+ }
+
+ /**
+ * @param array $variations
+ */
+ public function setVariations(array $variations): self
+ {
+ $this->variations = $variations;
+ return $this;
+ }
+
+ /**
+ * @param array $data
+ * @return self
+ */
+ public static function fromArray(array $data): self
+ {
+ /**
+ * @var array| null $targetingData
+ */
+ $targetingData = $data[FlagshipField::FIELD_TARGETING] ?? null;
+
+ $targeting = is_array($targetingData)
+ ? TargetingDTO::fromArray($targetingData)
+ : new TargetingDTO([]);
+
+ /**
+ * @var array>|null $variationsData
+ */
+ $variationsData = $data[FlagshipField::FIELD_VARIATIONS] ?? null;
+ $variations = [];
+ if (is_array($variationsData)) {
+ $variations = array_map(
+ BucketingVariationDTO::fromArray(...),
+ $variationsData
+ );
+ }
+
+ $id = $data[FlagshipField::FIELD_ID] ?? '';
+ $instance = new self(
+ is_string($id) ? $id : '',
+ $targeting,
+ $variations
+ );
+
+ if (isset($data[FlagshipField::FIELD_NANE]) && is_string($data[FlagshipField::FIELD_NANE])) {
+ $instance->setName($data[FlagshipField::FIELD_NANE]);
+ }
+
+ return $instance;
+ }
+
+ /**
+ * @return VariationGroupArray
+ */
+ public function toArray(): array
+ {
+ $result = [
+ FlagshipField::FIELD_ID => $this->id,
+ FlagshipField::FIELD_TARGETING => $this->targeting->toArray(),
+ FlagshipField::FIELD_VARIATIONS => array_map(
+ fn(BucketingVariationDTO $variation) => $variation->toArray(),
+ $this->variations
+ ),
+ ];
+
+ if ($this->name !== null) {
+ $result[FlagshipField::FIELD_NANE] = $this->name;
+ }
+
+ return $result;
+ }
+}
diff --git a/src/Model/VisitorCacheDTO.php b/src/Model/VisitorCacheDTO.php
new file mode 100644
index 00000000..c24fe3d1
--- /dev/null
+++ b/src/Model/VisitorCacheDTO.php
@@ -0,0 +1,71 @@
+version = $version;
+ $this->data = $data;
+ }
+
+ public function getVersion(): int
+ {
+ return $this->version;
+ }
+
+ public function setVersion(int $version): self
+ {
+ $this->version = $version;
+ return $this;
+ }
+
+ public function getData(): VisitorCacheDataDTO
+ {
+ return $this->data;
+ }
+
+ public function setData(VisitorCacheDataDTO $data): self
+ {
+ $this->data = $data;
+ return $this;
+ }
+
+ /**
+ * @param array $data
+ * @return self
+ */
+ public static function fromArray(array $data): self
+ {
+ $version = $data[StrategyAbstract::VERSION] ?? StrategyAbstract::CURRENT_VERSION;
+ $dataArray = $data[StrategyAbstract::DATA] ?? [];
+
+ return new self(
+ is_int($version) ? $version : StrategyAbstract::CURRENT_VERSION,
+ is_array($dataArray) ? VisitorCacheDataDTO::fromArray($dataArray) : new VisitorCacheDataDTO('', null)
+ );
+ }
+
+ /**
+ * @return VisitorCacheArray
+ */
+ public function toArray(): array
+ {
+ return [
+ StrategyAbstract::VERSION => $this->version,
+ StrategyAbstract::DATA => $this->data->toArray(),
+ ];
+ }
+}
diff --git a/src/Model/VisitorCacheDataDTO.php b/src/Model/VisitorCacheDataDTO.php
new file mode 100644
index 00000000..5b9a2dbb
--- /dev/null
+++ b/src/Model/VisitorCacheDataDTO.php
@@ -0,0 +1,199 @@
+|null */
+ private ?array $context = null;
+
+ /** @var array|null */
+ private ?array $assignmentsHistory = null;
+
+ /** @var array|null */
+ private ?array $campaigns = null;
+
+ public function __construct(string $visitorId, ?string $anonymousId = null)
+ {
+ $this->visitorId = $visitorId;
+ $this->anonymousId = $anonymousId;
+ }
+
+ public function getVisitorId(): string
+ {
+ return $this->visitorId;
+ }
+
+ public function setVisitorId(string $visitorId): self
+ {
+ $this->visitorId = $visitorId;
+ return $this;
+ }
+
+ public function getAnonymousId(): ?string
+ {
+ return $this->anonymousId;
+ }
+
+ public function setAnonymousId(?string $anonymousId): self
+ {
+ $this->anonymousId = $anonymousId;
+ return $this;
+ }
+
+ public function getConsent(): ?bool
+ {
+ return $this->consent;
+ }
+
+ public function setConsent(?bool $consent): self
+ {
+ $this->consent = $consent;
+ return $this;
+ }
+
+ /**
+ * @return array|null
+ */
+ public function getContext(): ?array
+ {
+ return $this->context;
+ }
+
+ /**
+ * @param array|null $context
+ */
+ public function setContext(?array $context): self
+ {
+ $this->context = $context;
+ return $this;
+ }
+
+ /**
+ * @return array|null
+ */
+ public function getAssignmentsHistory(): ?array
+ {
+ return $this->assignmentsHistory;
+ }
+
+ /**
+ * @param array|null $assignmentsHistory
+ */
+ public function setAssignmentsHistory(?array $assignmentsHistory): self
+ {
+ $this->assignmentsHistory = $assignmentsHistory;
+ return $this;
+ }
+
+ /**
+ * @return array|null
+ */
+ public function getCampaigns(): ?array
+ {
+ return $this->campaigns;
+ }
+
+ /**
+ * @param array|null $campaigns
+ */
+ public function setCampaigns(?array $campaigns): self
+ {
+ $this->campaigns = $campaigns;
+ return $this;
+ }
+
+ /**
+ * @param array $data
+ * @return self
+ */
+ public static function fromArray(array $data): self
+ {
+ $visitorId = $data[StrategyAbstract::VISITOR_ID] ?? '';
+ $anonymousId = $data[StrategyAbstract::ANONYMOUS_ID] ?? null;
+
+ $instance = new self(
+ is_string($visitorId) ? $visitorId : '',
+ is_string($anonymousId) ? $anonymousId : null
+ );
+
+ if (isset($data[StrategyAbstract::CONSENT]) && is_bool($data[StrategyAbstract::CONSENT])) {
+ $instance->setConsent($data[StrategyAbstract::CONSENT]);
+ }
+
+ if (isset($data[StrategyAbstract::CONTEXT]) && is_array($data[StrategyAbstract::CONTEXT])) {
+ $context = array_filter(
+ $data[StrategyAbstract::CONTEXT],
+ fn($value, $key) => is_string($key) && is_scalar($value),
+ ARRAY_FILTER_USE_BOTH
+ );
+ $instance->setContext($context ?: null);
+ }
+
+ $assignmentsHistory = [];
+
+ if (isset($data[StrategyAbstract::ASSIGNMENTS_HISTORY]) && is_array($data[StrategyAbstract::ASSIGNMENTS_HISTORY])) {
+ $assignmentsHistory = array_filter(
+ $data[StrategyAbstract::ASSIGNMENTS_HISTORY],
+ fn($value, $key) => is_string($key) && is_string($value),
+ ARRAY_FILTER_USE_BOTH
+ );
+ }
+ $instance->setAssignmentsHistory($assignmentsHistory);
+
+ $campaigns = [];
+ if (isset($data[StrategyAbstract::CAMPAIGNS]) && is_array($data[StrategyAbstract::CAMPAIGNS])) {
+ $campaigns = array_map(
+ fn($campaign) => is_array($campaign) ? CampaignCacheDTO::fromArray($campaign) : null,
+ $data[StrategyAbstract::CAMPAIGNS]
+ );
+ $campaigns = array_filter($campaigns);
+ }
+ $instance->setCampaigns($campaigns );
+
+ return $instance;
+ }
+
+ /**
+ * @return VisitorCacheDataArray
+ */
+ public function toArray(): array
+ {
+ $result = [
+ StrategyAbstract::VISITOR_ID => $this->visitorId,
+ StrategyAbstract::ANONYMOUS_ID => $this->anonymousId,
+ ];
+
+ if ($this->consent !== null) {
+ $result[StrategyAbstract::CONSENT] = $this->consent;
+ }
+
+ if ($this->context !== null) {
+ $result[StrategyAbstract::CONTEXT] = $this->context;
+ }
+
+ if ($this->assignmentsHistory !== null) {
+ $result[StrategyAbstract::ASSIGNMENTS_HISTORY] = $this->assignmentsHistory;
+ }
+
+ if ($this->campaigns !== null) {
+ $result[StrategyAbstract::CAMPAIGNS] = array_map(
+ fn(CampaignCacheDTO $campaign) => $campaign->toArray(),
+ $this->campaigns
+ );
+ }
+
+ return $result;
+ }
+}
diff --git a/src/Traits/BuildApiTrait.php b/src/Traits/BuildApiTrait.php
index 7b64c679..02bd11e0 100644
--- a/src/Traits/BuildApiTrait.php
+++ b/src/Traits/BuildApiTrait.php
@@ -4,22 +4,25 @@
use Flagship\Enum\FlagshipConstant;
+/**
+ * @phpstan-import-type BuildHeaderArray from Types
+ */
trait BuildApiTrait
{
/**
* Build http request header
*
* @param string $apiKey
- * @return array
+ * @return BuildHeaderArray
*/
- protected function buildHeader(string $apiKey): array
+ protected function buildHeader(?string $apiKey): array
{
return [
- FlagshipConstant::HEADER_X_API_KEY => $apiKey,
- FlagshipConstant::HEADER_X_SDK_VERSION => FlagshipConstant::SDK_VERSION,
- FlagshipConstant::HEADER_CONTENT_TYPE => FlagshipConstant::HEADER_APPLICATION_JSON,
- FlagshipConstant::HEADER_X_SDK_CLIENT => FlagshipConstant::SDK_LANGUAGE,
- ];
+ FlagshipConstant::HEADER_X_API_KEY => $apiKey??'',
+ FlagshipConstant::HEADER_X_SDK_VERSION => FlagshipConstant::SDK_VERSION,
+ FlagshipConstant::HEADER_CONTENT_TYPE => FlagshipConstant::HEADER_APPLICATION_JSON,
+ FlagshipConstant::HEADER_X_SDK_CLIENT => FlagshipConstant::SDK_LANGUAGE,
+ ];
}
/**
diff --git a/src/Traits/CommonLogManagerTrait.php b/src/Traits/CommonLogManagerTrait.php
index 80263181..90bb2579 100644
--- a/src/Traits/CommonLogManagerTrait.php
+++ b/src/Traits/CommonLogManagerTrait.php
@@ -14,22 +14,25 @@ public function getDateTime(): string
}
/**
- * @param $level
+ * @param mixed $level
* @param string $message
- * @param array $context
+ * @param array $context
* @return void
*/
- public function customLog($level, string $message, array $context = []): void
+ public function customLog(mixed $level, string $message, array $context = []): void
{
$flagshipSdk = FlagshipConstant::FLAGSHIP_SDK;
$formatDate = $this->getDateTime();
- $customMessage = "[$formatDate] [$flagshipSdk] [$level] ";
+ $levelStr = is_scalar($level) || (is_object($level) && method_exists($level, '__toString'))
+ ? (string) $level
+ : gettype($level);
+ $customMessage = "[$formatDate] [$flagshipSdk] [$levelStr] ";
$contextString = $this->parseContextToString($context);
error_log($customMessage . $contextString . " " . $message);
}
/**
- * @param array $context
+ * @param array $context
* @return string
*/
private function parseContextToString(array $context): string
@@ -40,7 +43,10 @@ private function parseContextToString(array $context): string
$contextToString .= '[';
}
foreach ($context as $key => $item) {
- $contextToString .= "$key => $item, ";
+ $itemStr = is_scalar($item) || (is_object($item) && method_exists($item, '__toString'))
+ ? (string) $item
+ : json_encode($item);
+ $contextToString .= "$key => $itemStr, ";
}
$contextToString = substr($contextToString, 0, -2);
diff --git a/src/Traits/Guid.php b/src/Traits/Guid.php
index 284dbb06..b3a50381 100644
--- a/src/Traits/Guid.php
+++ b/src/Traits/Guid.php
@@ -9,7 +9,7 @@ trait Guid
*/
protected function newGuid(): string
{
- $rand = function ($min, $max) {
+ $rand = function (int $min, int $max): int {
return rand($min, $max);
};
diff --git a/src/Traits/Helper.php b/src/Traits/Helper.php
index 7ccbe3c0..8926fdab 100644
--- a/src/Traits/Helper.php
+++ b/src/Traits/Helper.php
@@ -27,6 +27,9 @@ public function getCurrentDateTime(): DateTime
public function valueToHex(mixed $value): string
{
$jsonString = json_encode($value);
+ if ($jsonString === false) {
+ return '';
+ }
$hex = '';
for ($i = 0; $i < strlen($jsonString); $i++) {
$hex .= dechex(ord($jsonString[$i]));
@@ -34,6 +37,13 @@ public function valueToHex(mixed $value): string
return $hex;
}
+ /**
+ * Compare two arrays for equality, including nested arrays.
+ *
+ * @param array $array1
+ * @param array $array2
+ * @return bool
+ */
function arraysAreEqual(array $array1, array $array2): bool
{
if (count($array1) !== count($array2)) {
@@ -58,4 +68,52 @@ function arraysAreEqual(array $array1, array $array2): bool
return true;
}
+
+ /**
+ *
+ * @param array $array
+ * @param callable $callback
+ * @return bool
+ */
+ protected function array_all(array $array, callable $callback): bool
+ {
+ foreach ($array as $key => $value) {
+ if (!$callback($value, $key)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ *
+ * @param array $array
+ * @param callable $callback
+ * @return bool
+ */
+ protected function array_any(array $array, callable $callback): bool
+ {
+ foreach ($array as $key => $value) {
+ if ($callback($value, $key)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @phpstan-template T of mixed
+ * @param array $array
+ * @param callable $callback
+ * @return T|null
+ */
+ protected function array_find(array $array, callable $callback): mixed
+ {
+ foreach ($array as $key => $value) {
+ if ($callback($value, $key)) {
+ return $value;
+ }
+ }
+ return null;
+ }
}
diff --git a/src/Traits/LogTrait.php b/src/Traits/LogTrait.php
index 92080430..70be4086 100644
--- a/src/Traits/LogTrait.php
+++ b/src/Traits/LogTrait.php
@@ -9,31 +9,122 @@
trait LogTrait
{
/**
- * @param array $args
- * @return array
+ * Format arguments for logging by converting them to scalar values
+ *
+ * @param array $args Arguments to format
+ * @return array Formatted arguments safe for logging
*/
- protected function formatArgs($args = []): array
+ protected function formatArgs(array $args = []): array
{
- $formatArgs = [];
+ $formatted = [];
+
foreach ($args as $arg) {
- if (is_array($arg)) {
- $arg = json_encode($arg);
- }
- $formatArgs[] = $arg;
+ $formatted[] = $this->convertToScalar($arg);
}
- return $formatArgs;
+
+ return $formatted;
}
/**
- * @param FlagshipConfig $config
+ * Convert any value to a scalar or null for safe logging
+ *
+ * @param mixed $value The value to convert
+ * @return scalar|null Scalar representation of the value
+ */
+ private function convertToScalar(mixed $value): string|int|float|bool|null
+ {
+
+ return match (true) {
+ $value === null => null,
+ is_bool($value) => $value,
+ is_int($value) => $value,
+ is_float($value) => $value,
+ is_string($value) => $value,
+ is_array($value) => $this->serializeToJson($value),
+ is_object($value) => $this->convertObjectToString($value),
+ is_resource($value) => $this->formatResource($value),
+ default => $this->serializeToJson($value),
+ };
+ }
+
+ /**
+ * Convert an object to its string representation
+ *
+ * @param object $object The object to convert
+ * @return string String representation
+ */
+ private function convertObjectToString(object $object): string
+ {
+
+
+ if (method_exists($object, '__toString')) {
+ return (string) $object;
+ }
+
+ if ($object instanceof \JsonSerializable) {
+ return $this->serializeToJson($object);
+ }
+
+
+ return get_class($object);
+ }
+
+ /**
+ * Serialize a value to JSON with error handling
+ *
+ * @param mixed $value Value to serialize
+ * @return string JSON string or error message
+ */
+ private function serializeToJson(mixed $value): string
+ {
+ try {
+ $json = json_encode(
+ $value
+ );
+ return $json ?: '{}';
+ // @phpstan-ignore catch.neverThrown
+ } catch (\JsonException $e) {
+ return sprintf('[JSON Error: %s]', $e->getMessage());
+ }
+ }
+
+ /**
+ * Format a resource for logging
+ *
+ * @param resource $resource The resource to format
+ * @return string Resource description
+ */
+ private function formatResource($resource): string
+ {
+ $type = get_resource_type($resource);
+ return sprintf('[Resource: %s]', $type);
+ }
+
+ /**
+ * Format error when __toString fails
+ *
+ * @param object $object The object that failed
+ * @return string Error description
+ */
+ private function formatToStringError(object $object, \Throwable $error): string
+ {
+ $class = get_class($object);
+ return sprintf('[%s __toString error: %s]', $class, $error->getMessage());
+ }
+
+ /**
+ * @param ?FlagshipConfig $config
* @param string $tag
* @param string $message
- * @param array $args
+ * @param array $args
* @return void
*/
- protected function logDebugSprintf(FlagshipConfig $config, string $tag, string $message, array $args = []): void
+ protected function logDebugSprintf(?FlagshipConfig $config, string $tag, string $message, array $args = []): void
{
- if ($config->getLogLevel()->value < LogLevel::DEBUG->value || is_null($config->getLogManager())) {
+ if (
+ $config?->getLogLevel()->value < LogLevel::DEBUG->value ||
+ is_null($config->getLogManager())
+ ) {
return;
}
$customMessage = vsprintf($message, $this->formatArgs($args));
@@ -43,7 +134,7 @@ protected function logDebugSprintf(FlagshipConfig $config, string $tag, string $
/**
* @param FlagshipConfig $config
* @param string $message
- * @param array $context
+ * @param array $context
*@return void
*/
protected function logDebug(FlagshipConfig $config, string $message, array $context = []): void
@@ -58,7 +149,7 @@ protected function logDebug(FlagshipConfig $config, string $message, array $cont
* @param FlagshipConfig $config
* @param string $tag
* @param string $message
- * @param array ...$args
+ * @param array $args
* @return void
*/
protected function logErrorSprintf(FlagshipConfig $config, string $tag, string $message, array $args = []): void
@@ -70,14 +161,14 @@ protected function logErrorSprintf(FlagshipConfig $config, string $tag, string $
$this->logError($config, $customMessage, [FlagshipConstant::TAG => $tag]);
}
/**
- * @param FlagshipConfig $config
+ * @param ?FlagshipConfig $config
* @param string $message
- * @param array $context
+ * @param array $context
* @return void
*/
- protected function logError(FlagshipConfig $config, string $message, array $context = []): void
+ protected function logError(?FlagshipConfig $config, string $message, array $context = []): void
{
- if ($config->getLogLevel()->value < LogLevel::ERROR->value || is_null($config->getLogManager())) {
+ if ($config?->getLogLevel()->value < LogLevel::ERROR->value || is_null($config->getLogManager())) {
return;
}
$config->getLogManager()->error($message, $context);
@@ -87,7 +178,7 @@ protected function logError(FlagshipConfig $config, string $message, array $cont
* @param FlagshipConfig $config
* @param string $tag
* @param string $message
- * @param array $args
+ * @param array $args
* @return void
*/
protected function logInfoSprintf(FlagshipConfig $config, string $tag, string $message, array $args = []): void
@@ -101,7 +192,7 @@ protected function logInfoSprintf(FlagshipConfig $config, string $tag, string $m
/**
* @param FlagshipConfig $config
* @param string $message
- * @param array $context
+ * @param array $context
* @return void
*/
protected function logInfo(FlagshipConfig $config, string $message, array $context = []): void
@@ -113,15 +204,15 @@ protected function logInfo(FlagshipConfig $config, string $message, array $conte
}
/**
- * @param FlagshipConfig $config
+ * @param FlagshipConfig|null $config
* @param string $tag
* @param string $message
- * @param array $args
+ * @param array< mixed> $args
* @return void
*/
- protected function logWarningSprintf(FlagshipConfig $config, string $tag, string $message, array $args = []): void
+ protected function logWarningSprintf(FlagshipConfig|null $config, string $tag, string $message, array $args = []): void
{
- if ($config->getLogLevel()->value < LogLevel::WARNING->value || is_null($config->getLogManager())) {
+ if ($config?->getLogLevel()->value < LogLevel::WARNING->value || is_null($config->getLogManager())) {
return;
}
$customMessage = vsprintf($message, $this->formatArgs($args));
@@ -131,7 +222,7 @@ protected function logWarningSprintf(FlagshipConfig $config, string $tag, string
/**
* @param FlagshipConfig $config
* @param string $message
- * @param array $context
+ * @param array $context
* @return void
*/
protected function logWarning(FlagshipConfig $config, string $message, array $context = []): void
@@ -143,22 +234,22 @@ protected function logWarning(FlagshipConfig $config, string $message, array $co
}
/**
- * @param string $message
+ * @param string|null $message
* @param string $url
- * @param array $requestBody
- * @param array $headers
- * @param string $duration
+ * @param ?array $requestBody
+ * @param ?array $headers
+ * @param float|int|null $duration
* @param mixed $responseHeader
* @param mixed $responseBody
* @param mixed $responseStatus
- * @return array
+ * @return array
*/
protected function getLogFormat(
- $message,
- $url,
- $requestBody,
- $headers,
- $duration,
+ string|null $message,
+ string $url,
+ ?array $requestBody,
+ ?array $headers,
+ float|int|null $duration,
$responseHeader = null,
$responseBody = null,
$responseStatus = null
diff --git a/src/Traits/Types.php b/src/Traits/Types.php
new file mode 100644
index 00000000..e1e27f7f
--- /dev/null
+++ b/src/Traits/Types.php
@@ -0,0 +1,15 @@
+logError(
- $config,
- sprintf(FlagshipConstant::TYPE_ERROR, $itemName, 'numeric')
- );
- return false;
- }
- return true;
- }
+ }
}
diff --git a/src/Utils/ConfigManager.php b/src/Utils/ConfigManager.php
index bde0e3cc..9df7d864 100644
--- a/src/Utils/ConfigManager.php
+++ b/src/Utils/ConfigManager.php
@@ -44,7 +44,7 @@ public function getConfig(): FlagshipConfig
* @param FlagshipConfig $config
* @return ConfigManager
*/
- public function setConfig(FlagshipConfig $config): static
+ public function setConfig(FlagshipConfig $config): self
{
$this->config = $config;
return $this;
@@ -62,7 +62,7 @@ public function getDecisionManager(): DecisionManagerAbstract
* @param DecisionManagerAbstract $decisionManager
* @return ConfigManager
*/
- public function setDecisionManager(DecisionManagerAbstract $decisionManager): static
+ public function setDecisionManager(DecisionManagerAbstract $decisionManager): self
{
$this->decisionManager = $decisionManager;
return $this;
@@ -80,7 +80,7 @@ public function getTrackingManager(): TrackingManagerAbstract
* @param TrackingManagerAbstract $trackerManager
* @return ConfigManager
*/
- public function setTrackingManager(TrackingManagerAbstract $trackerManager): static
+ public function setTrackingManager(TrackingManagerAbstract $trackerManager): self
{
$this->trackingManager = $trackerManager;
return $this;
diff --git a/src/Utils/Container.php b/src/Utils/Container.php
index 30c0f14a..55b1345f 100644
--- a/src/Utils/Container.php
+++ b/src/Utils/Container.php
@@ -7,129 +7,514 @@
use ReflectionException;
use ReflectionNamedType;
use ReflectionParameter;
+use ReflectionUnionType;
+/**
+ * Dependency Injection Container
+ *
+ * Provides automatic dependency resolution and singleton management
+ * for class instantiation throughout the SDK.
+ */
class Container implements ContainerInterface
{
+ /**
+ * Singleton instances cache
+ * @var array
+ */
private array $instances = [];
+
+ /**
+ * Interface to implementation bindings
+ * @var array
+ */
private array $bindings = [];
/**
- * @param string $alias
- * @param string $className
- * @return $this
- * @throws Exception
+ * Factory callbacks for custom instantiation
+ * @var array
*/
- public function bind(string $alias, string $className): static
+ private array $factories = [];
+
+ /**
+ * Bind an alias to a concrete class implementation
+ *
+ * @template T of object
+ * @param class-string $alias The interface or abstract class
+ * @param class-string $className The concrete implementation
+ * @return self
+ * @throws ContainerException If alias already exists
+ */
+ public function bind(string $alias, string $className): self
{
if (isset($this->bindings[$alias])) {
- throw new Exception('alias ' . $alias . ' already exist');
+ throw new ContainerException(
+ sprintf('Alias "%s" is already bound to "%s"', $alias, $this->bindings[$alias])
+ );
+ }
+
+ if (!class_exists($className) && !interface_exists($className)) {
+ throw new ContainerException(
+ sprintf('Class "%s" does not exist', $className)
+ );
}
+
$this->bindings[$alias] = $className;
return $this;
}
/**
- * @param string $id
- * @param null $args
- * @param bool $isFactory
- * @return mixed|object|null
+ * Register a factory callback for custom instantiation
+ *
+ * @template T of object
+ * @param class-string $id The class identifier
+ * @param callable(self, array|null): T $factory Factory function that returns the instance
+ * @return self
+ */
+ public function factory(string $id, callable $factory): self
+ {
+ $this->factories[$id] = $factory;
+ return $this;
+ }
+
+ /**
+ * Bind a singleton instance directly
+ *
+ * @template T of object
+ * @param class-string $id The class identifier
+ * @param T $instance The instance to register
+ * @return self
+ */
+ public function instance(string $id, object $instance): self
+ {
+ $this->instances[$id] = $instance;
+ return $this;
+ }
+
+ /**
+ * Check if a binding exists
+ *
+ * @param class-string $id
+ * @return bool
+ */
+ public function has(string $id): bool
+ {
+ return isset($this->instances[$id])
+ || isset($this->bindings[$id])
+ || isset($this->factories[$id])
+ || class_exists($id);
+ }
+
+ /**
+ * Resolve and return an instance
+ *
+ * @template T of object
+ * @param class-string $id The class identifier
+ * @param array|null $args Constructor arguments (optional)
+ * @param bool $isFactory Whether to create a new instance each time
+ * @return T
+ * @throws ContainerException
* @throws ReflectionException
*/
- public function get(string $id, $args = null, bool $isFactory = false): mixed
+ public function get(string $id, ?array $args = null, bool $isFactory = false): object
{
+ // Always create new instance if factory mode
if ($isFactory) {
return $this->resolve($id, $args);
}
+
+ // Return existing singleton if available
if (isset($this->instances[$id])) {
+ /** @var T */
return $this->instances[$id];
}
- return $this->instances[$id] = $this->resolve($id, $args);
+
+ // Create and cache new singleton
+ $instance = $this->resolve($id, $args);
+ $this->instances[$id] = $instance;
+
+ /** @var T */
+ return $instance;
}
/**
- * @param string $id
- * @param array|null $args
- * @return object|null
+ * Create a new instance (alias for factory mode)
+ *
+ * @template T of object
+ * @param class-string $id
+ * @param array|null $args
+ * @return T
+ * @throws ContainerException
* @throws ReflectionException
- * @throws Exception
*/
- private function resolve(string $id, array $args = null): ?object
+ public function make(string $id, ?array $args = null): object
{
- $className = $id;
- if (isset($this->bindings[$id])) {
- $className = $this->bindings[$id];
+ return $this->resolve($id, $args);
+ }
+
+ /**
+ * Resolve a class to an instance
+ *
+ * @template T of object
+ * @param class-string $id The class identifier
+ * @param array|null $args Constructor arguments
+ * @return T
+ * @throws ContainerException
+ * @throws ReflectionException
+ */
+ private function resolve(string $id, ?array $args = null): object
+ {
+ // Use factory if registered
+ if (isset($this->factories[$id])) {
+ return $this->invokeFactory($id, $args);
}
- $reflectedClass = new ReflectionClass($className);
- if (!$reflectedClass->isInstantiable()) {
- throw new Exception($className . "not an instantiable Class");
+ // Resolve bound implementation
+ $className = $this->getConcreteClassName($id);
+
+ // Create reflection
+ $reflectionClass = $this->createReflection($className);
+
+ // Validate instantiability
+ $this->validateInstantiable($reflectionClass);
+
+ // Get constructor
+ $constructor = $reflectionClass->getConstructor();
+
+ // No constructor - simple instantiation
+ if ($constructor === null) {
+ /** @var T */
+ return $reflectionClass->newInstance();
}
- $constructor = $reflectedClass->getConstructor();
+ // Resolve constructor parameters
+ $parameters = $this->resolveConstructorParameters($constructor, $args);
+
+ /** @var T */
+ return $reflectionClass->newInstanceArgs($parameters);
+ }
+
+ /**
+ * Invoke a registered factory
+ *
+ * @template T of object
+ * @param class-string $id
+ * @param array|null $args
+ * @return T
+ * @throws ContainerException
+ */
+ private function invokeFactory(string $id, ?array $args = null): object
+ {
+ $factory = $this->factories[$id];
+ $instance = $factory($this, $args);
- if (!$constructor) {
- return $reflectedClass->newInstance();
+ if (!is_object($instance)) {
+ throw new ContainerException(
+ sprintf('Factory for "%s" must return an object', $id)
+ );
}
- if (is_array($args)) {
- $constructorParameters = $args;
- } else {
- $parameters = $constructor->getParameters();
- $constructorParameters = $this->extractConstructorParam($parameters);
+ /** @var T */
+ return $instance;
+ }
+
+ /**
+ * Get the concrete class name from binding or use the id
+ *
+ * @template T of object
+ * @param class-string $id
+ * @return class-string
+ */
+ private function getConcreteClassName(string $id): string
+ {
+ /** @var class-string */
+ return $this->bindings[$id] ?? $id;
+ }
+
+ /**
+ * Create a reflection class with error handling
+ *
+ * @template T of object
+ * @param class-string $className
+ * @return ReflectionClass
+ * @throws ContainerException
+ * @phpstan-ignore throws.unusedType
+ */
+ private function createReflection(string $className): ReflectionClass
+ {
+ try {
+ return new ReflectionClass($className);
+ // @phpstan-ignore catch.neverThrown
+ } catch (ReflectionException $e) {
+ throw new ContainerException(
+ sprintf('Class "%s" does not exist', $className),
+ 0,
+ $e
+ );
}
- return $reflectedClass->newInstanceArgs($constructorParameters);
}
- private function getDefaultForType(string $typeName): float|bool|array|int|string|null
+ /**
+ * Validate that a class is instantiable
+ *
+ * @param ReflectionClass