Skip to content

Commit 1469760

Browse files
sakshamg1304rohitesh-wingify
authored andcommitted
feat: multiple instance support
1 parent 3b700b8 commit 1469760

34 files changed

+881
-466
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
9+
## [1.16.0] - 2025-12-12
10+
11+
### Fixed
12+
13+
- Fixed singleton class issue where single service instance was being used across multiple sdk instances.
14+
815
## [1.15.0] - 2025-12-10
916

1017
### Added

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "vwo/vwo-fme-php-sdk",
33

4-
"version": "1.15.0",
4+
"version": "1.16.0",
55
"keywords": ["vwo", "fme", "sdk"],
66
"license": "Apache-2.0",
77
"authors": [{

src/Api/GetFlag.php

Lines changed: 67 additions & 59 deletions
Large diffs are not rendered by default.

src/Api/SetAttribute.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
use vwo\Models\SettingsModel;
2323
use vwo\Enums\EventEnum;
2424
use vwo\Utils\NetworkUtil;
25+
use vwo\Services\ServiceContainer;
2526

2627
interface ISetAttribute
2728
{
@@ -42,10 +43,10 @@ class SetAttribute implements ISetAttribute
4243
* @param array $attributes Key-value map of attributes.
4344
* @param ContextModel $context Context containing user information.
4445
*/
45-
public function setAttribute(SettingsModel $settings, array $attributes, ContextModel $context, bool $isDebuggerUsed = false)
46+
public function setAttribute(SettingsModel $settings, array $attributes, ContextModel $context, bool $isDebuggerUsed = false, ServiceContainer $serviceContainer = null)
4647
{
4748
if (!$isDebuggerUsed) {
48-
$this->createImpressionForAttributes($settings, $attributes, $context);
49+
$this->createImpressionForAttributes($settings, $attributes, $context, $serviceContainer);
4950
}
5051
}
5152

@@ -54,10 +55,11 @@ public function setAttribute(SettingsModel $settings, array $attributes, Context
5455
* @param SettingsModel $settings Configuration settings.
5556
* @param array $attributes Key-value map of attributes.
5657
* @param ContextModel $context Context containing user information.
58+
* @param ServiceContainer $serviceContainer The service container (optional).
5759
*/
58-
private function createImpressionForAttributes(SettingsModel $settings, array $attributes, ContextModel $context)
60+
private function createImpressionForAttributes(SettingsModel $settings, array $attributes, ContextModel $context, ServiceContainer $serviceContainer = null)
5961
{
60-
$networkUtil = new NetworkUtil();
62+
$networkUtil = new NetworkUtil($serviceContainer);
6163

6264
// Retrieve base properties for the event
6365
$properties = $networkUtil->getEventsBaseProperties(

src/Api/TrackEvent.php

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@
2222
use vwo\Enums\ErrorLogMessagesEnum;
2323
use vwo\Models\SettingsModel;
2424
use vwo\Models\User\ContextModel;
25-
use vwo\Packages\Logger\Core\LogManager;
2625
use vwo\Services\HooksService;
2726
use vwo\Utils\FunctionUtil as FunctionUtil;
2827
use vwo\Utils\NetworkUtil as NetworkUtil;
2928
use vwo\Utils\LogMessageUtil as LogMessageUtil;
29+
use vwo\Services\ServiceContainer;
3030

3131
// Interface for tracking functionality
3232
interface ITrack
@@ -55,13 +55,13 @@ class TrackEvent implements ITrack
5555
* @param HooksService $hooksService Manager for handling hooks and callbacks.
5656
* @return array Returns an array indicating the success or failure of the event tracking.
5757
*/
58-
public function track(SettingsModel $settings, string $eventName, ContextModel $context, array $eventProperties, HooksService $hooksService, bool $isDebuggerUsed = false): array
58+
public function track(SettingsModel $settings, string $eventName, ContextModel $context, array $eventProperties, HooksService $hooksService, bool $isDebuggerUsed = false, ServiceContainer $serviceContainer = null): array
5959
{
6060
if (FunctionUtil::doesEventBelongToAnyFeature($eventName, $settings)) {
6161
// Create an impression for the track event
6262
// if settings passed in init options is true, then we don't need to send an impression
6363
if (!$isDebuggerUsed) {
64-
$this->createImpressionForTrack($settings, $eventName, $context, $eventProperties);
64+
$this->createImpressionForTrack($settings, $eventName, $context, $eventProperties, $serviceContainer);
6565
}
6666

6767
// Set and execute integration callback for the track event
@@ -72,7 +72,8 @@ public function track(SettingsModel $settings, string $eventName, ContextModel $
7272
}
7373

7474
// Log an error if the event does not exist
75-
LogManager::instance()->error("Event '$eventName' not found in any of the features");
75+
$logManager = $serviceContainer->getLogManager();
76+
$logManager->error("Event '$eventName' not found in any of the features");
7677

7778

7879
return [$eventName => false];
@@ -84,10 +85,11 @@ public function track(SettingsModel $settings, string $eventName, ContextModel $
8485
* @param string $eventName Name of the event to track.
8586
* @param ContextModel $context Contextual information like user details.
8687
* @param array $eventProperties Properties associated with the event.
88+
* @param ServiceContainer $serviceContainer The service container (optional).
8789
*/
88-
private function createImpressionForTrack(SettingsModel $settings, string $eventName, ContextModel $context, array $eventProperties)
90+
private function createImpressionForTrack(SettingsModel $settings, string $eventName, ContextModel $context, array $eventProperties, ServiceContainer $serviceContainer = null)
8991
{
90-
$networkUtil = new NetworkUtil();
92+
$networkUtil = new NetworkUtil($serviceContainer);
9193

9294
// Get base properties for the event
9395
$properties = $networkUtil->getEventsBaseProperties($eventName, $context->getUserAgent(), $context->getIpAddress());

src/Constants/Constants.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class Constants {
4040
const DEFAULT_EVENTS_PER_REQUEST = 100;
4141
const SDK_NAME = 'vwo-fme-php-sdk';
4242

43-
const SDK_VERSION = '1.15.0';
43+
const SDK_VERSION = '1.16.0';
4444
const AP = 'server';
4545

4646
const SETTINGS = 'settings';

src/Decorators/StorageDecorator.php

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,24 +18,24 @@
1818

1919
namespace vwo\Decorators;
2020

21-
use vwo\Packages\Logger\Core\LogManager;
2221
use vwo\Services\StorageService;
2322
use vwo\Enums\StorageEnum;
2423
use vwo\Models\FeatureModel;
2524
use vwo\Models\VariationModel;
2625
use vwo\Models\User\ContextModel;
26+
use vwo\Services\ServiceContainer;
2727

2828
interface IStorageDecorator
2929
{
30-
public function getFeatureFromStorage($featureKey, $context, $storageService);
31-
public function setDataInStorage($data, $storageService);
30+
public function getFeatureFromStorage($featureKey, $context, $storageService, ServiceContainer $serviceContainer = null);
31+
public function setDataInStorage($data, $storageService, ServiceContainer $serviceContainer = null);
3232
}
3333

3434
class StorageDecorator implements IStorageDecorator
3535
{
36-
public function getFeatureFromStorage($featureKey, $context, $storageService)
36+
public function getFeatureFromStorage($featureKey, $context, $storageService, ServiceContainer $serviceContainer = null)
3737
{
38-
$campaignMap = $storageService->getDataInStorage($featureKey, $context);
38+
$campaignMap = $storageService->getDataInStorage($featureKey, $context, $serviceContainer);
3939

4040
switch ($campaignMap) {
4141
case StorageEnum::STORAGE_UNDEFINED:
@@ -55,9 +55,10 @@ public function getFeatureFromStorage($featureKey, $context, $storageService)
5555
}
5656
}
5757

58-
public function setDataInStorage($data, $storageService)
58+
public function setDataInStorage($data, $storageService, ServiceContainer $serviceContainer = null)
5959
{
6060
$featureKey = $data['featureKey'] ?? null;
61+
$featureId = $data['featureId'] ?? null;
6162
$context = $data['context'] ?? null;
6263
$rolloutId = $data['rolloutId'] ?? null;
6364
$rolloutKey = $data['rolloutKey'] ?? null;
@@ -66,27 +67,29 @@ public function setDataInStorage($data, $storageService)
6667
$experimentKey = $data['experimentKey'] ?? null;
6768
$experimentVariationId = $data['experimentVariationId'] ?? null;
6869

70+
$logManager = $serviceContainer->getLogManager();
71+
6972
if (!$featureKey) {
70-
LogManager::instance()->error("Error storing data: featureKey is invalid.");
73+
$logManager->error("Error storing data: featureKey is invalid.");
7174
return false;
7275
}
7376

7477
if ($context->getId() == null) {
75-
LogManager::instance()->error("Error storing data: Context or Context.id is invalid.");
78+
$logManager->error("Error storing data: Context or Context.id is invalid.");
7679
return false;
7780
}
7881

7982
if ($rolloutKey && !$experimentKey && !$rolloutVariationId) {
80-
LogManager::instance()->error("Error storing data: Variation (rolloutKey, experimentKey or rolloutVariationId) is invalid.");
83+
$logManager->error("Error storing data: Variation (rolloutKey, experimentKey or rolloutVariationId) is invalid.");
8184
return false;
8285
}
8386

8487
if ($experimentKey && !$experimentVariationId) {
85-
LogManager::instance()->error("Error storing data: Variation (experimentKey or experimentVariationId) is invalid.");
88+
$logManager->error("Error storing data: Variation (experimentKey or experimentVariationId) is invalid.");
8689
return false;
8790
}
8891

89-
$storageService->setDataInStorage([
92+
$storageData = [
9093
'featureKey' => $featureKey,
9194
'userId' => $context->getId(),
9295
'rolloutId' => isset($rolloutId) ? $rolloutId : null,
@@ -95,7 +98,14 @@ public function setDataInStorage($data, $storageService)
9598
'experimentId' => isset($experimentId) ? $experimentId : null,
9699
'experimentKey' => isset($experimentKey) ? $experimentKey : null,
97100
'experimentVariationId' => isset($experimentVariationId) ? $experimentVariationId : null,
98-
]);
101+
];
102+
103+
// Add featureId if provided
104+
if ($featureId !== null) {
105+
$storageData['featureId'] = $featureId;
106+
}
107+
108+
$storageService->setDataInStorage($storageData, $serviceContainer);
99109

100110
return true;
101111
}

src/Packages/Logger/Core/LogManager.php

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -56,19 +56,18 @@ public function __construct($config = []) {
5656
return (new \DateTime())->format(\DateTime::ISO8601);
5757
};
5858

59-
if (!isset(self::$instance)) {
60-
self::$instance = $this;
61-
62-
$this->config['name'] = $config['name'] ?? $this->name;
63-
$this->config['requestId'] = $config['requestId'] ?? Uuid::uuid4()->toString();
64-
$this->config['level'] = $config['level'] ?? $this->level;
65-
$this->config['prefix'] = $config['prefix'] ?? $this->prefix;
66-
$this->config['dateTimeFormat'] = $config['dateTimeFormat'] ?? $this->dateTimeFormat;
67-
68-
$this->transportManager = new LogTransportManager($this->config);
69-
70-
$this->handleTransports();
71-
}
59+
// Always initialize config and transportManager for this instance
60+
$this->config['name'] = $config['name'] ?? $this->name;
61+
$this->config['requestId'] = $config['requestId'] ?? Uuid::uuid4()->toString();
62+
$this->config['level'] = $config['level'] ?? $this->level;
63+
$this->config['prefix'] = $config['prefix'] ?? $this->prefix;
64+
$this->config['dateTimeFormat'] = $config['dateTimeFormat'] ?? $this->dateTimeFormat;
65+
66+
$this->transportManager = new LogTransportManager($this->config);
67+
$this->handleTransports();
68+
69+
// Always update singleton to the latest instance to support multiple SDK instances
70+
self::$instance = $this;
7271
}
7372

7473
public static function instance() {

src/Packages/NetworkLayer/Client/NetworkClient.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
use vwo\Services\LoggerService;
2424
use vwo\Enums\LogLevelEnum;
2525
use vwo\Constants\Constants;
26+
use vwo\Packages\NetworkLayer\Manager\NetworkManager;
27+
use vwo\Packages\Logger\Core\LogManager;
2628

2729
class NetworkClient implements NetworkClientInterface
2830
{
@@ -31,6 +33,7 @@ class NetworkClient implements NetworkClientInterface
3133
private $isGatewayUrlNotSecure = false; // Flag to store the value
3234
private $shouldWaitForTrackingCalls = false;
3335
private $retryConfig = Constants::DEFAULT_RETRY_CONFIG;
36+
private $logManager;
3437

3538
// Constructor to accept options and store the flag
3639
public function __construct($options = []) {
@@ -43,6 +46,9 @@ public function __construct($options = []) {
4346
if(isset($options['retryConfig'])) {
4447
$this->retryConfig = $options['retryConfig'];
4548
}
49+
if(isset($options['logManager'])) {
50+
$this->logManager = $options['logManager'];
51+
}
4652
}
4753

4854
private function shouldUseCurl($networkOptions)
@@ -268,7 +274,8 @@ public function GET($request)
268274
'GET',
269275
$networkOptions['headers'],
270276
null,
271-
$networkOptions['timeout'] / 1000
277+
$networkOptions['timeout'] / 1000,
278+
$this->retryConfig
272279
);
273280
$rawResponse = $curlResponse['body'];
274281
$responseModel->setStatusCode($curlResponse['status_code']);
@@ -304,7 +311,7 @@ public function POST($request)
304311
$networkOptions['headers'],
305312
$networkOptions['body'],
306313
$networkOptions['timeout'] / 1000,
307-
$retryConfig
314+
$this->retryConfig
308315
);
309316

310317
$rawResponse = $curlResponse['body'];

src/Packages/NetworkLayer/Manager/NetworkManager.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ class NetworkManager {
3333
private $isGatewayUrlNotSecure = false; // Store the flag here
3434
private $shouldWaitForTrackingCalls = false;
3535
private $retryConfig = Constants::DEFAULT_RETRY_CONFIG;
36+
private $logManager;
37+
38+
// public function __construct() {
39+
// $this->config = new GlobalRequestModel(null, null, null, null);
40+
// }
3641

3742
public function attachClient($client = null, $options = []) {
3843
$this->config = new GlobalRequestModel(null, null, null, null);
@@ -42,6 +47,12 @@ public function attachClient($client = null, $options = []) {
4247
if(isset($options['shouldWaitForTrackingCalls'])) {
4348
$this->shouldWaitForTrackingCalls = $options['shouldWaitForTrackingCalls'];
4449
}
50+
if(isset($options['retryConfig'])) {
51+
$this->retryConfig = $options['retryConfig'];
52+
}
53+
if(isset($options['logManager'])) {
54+
$this->logManager = $options['logManager'];
55+
}
4556
// Normalize retry config from options (if any)
4657
$providedRetry = isset($options['retryConfig']) && is_array($options['retryConfig']) ? $options['retryConfig'] : [];
4758
$this->retryConfig = $this->validateRetryConfig($providedRetry);
@@ -51,6 +62,7 @@ public function attachClient($client = null, $options = []) {
5162
'isGatewayUrlNotSecure' => $this->isGatewayUrlNotSecure,
5263
'shouldWaitForTrackingCalls' => $this->shouldWaitForTrackingCalls,
5364
'retryConfig' => $this->retryConfig,
65+
'logManager' => $this->logManager,
5466
];
5567
$this->client = $client ?: new NetworkClient($clientOptions);
5668
}
@@ -88,6 +100,10 @@ public function createRequest($request): RequestModel {
88100
}
89101

90102
public function get($request) {
103+
if ($this->client === null) {
104+
throw new \Exception('NetworkManager client is not initialized. Please call attachClient() first.');
105+
}
106+
91107
$networkOptions = $this->createRequest($request);
92108

93109
if ($networkOptions === null) {
@@ -103,6 +119,10 @@ public function get($request) {
103119
}
104120

105121
public function post($request) {
122+
if ($this->client === null) {
123+
throw new \Exception('NetworkManager client is not initialized. Please call attachClient() first.');
124+
}
125+
106126
$networkOptions = $this->createRequest($request);
107127

108128
if ($networkOptions === null) {

0 commit comments

Comments
 (0)