• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

wingify / vwo-php-sdk / 5398400589

pending completion
5398400589

push

github

rohitesh-wingify
refactor(bucketing): update variation bucketing based on feature-flag

33 of 33 new or added lines in 3 files covered. (100.0%)

1462 of 1629 relevant lines covered (89.75%)

93.74 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.66
/src/Core/VariationDecider.php
1
<?php
2

3
/**
4
 * Copyright 2019-2022 Wingify Software Pvt. Ltd.
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 *    http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18

19
namespace vwo\Core;
20

21
use Exception as Exception;
22
use Monolog\Logger as Logger;
23
use vwo\Constants\CampaignTypes;
24
use vwo\Constants\Hooks;
25
use vwo\Utils\Common as CommonUtil;
26
use vwo\Utils\Campaign as CampaignUtil;
27
use vwo\Services\LoggerService;
28
use vwo\Services\HooksManager;
29
use vwo\Core\Bucketer as Bucketer;
30
use vwo\Utils\ImpressionBuilder;
31
use vwo\Utils\UuidUtil;
32
use vwo\Utils\Validations as ValidationsUtil;
33

34
class VariationDecider
35
{
36
    public $hasStoredVariation;
37
    private $accountId;
38
    private $hooksManager;
39
    private $settings;
40

41
    const CLASSNAME = 'vwo\Core\VariationDecider';
42
    const RandomAlgo = 1;
43

44
    function __construct($settings = null)
45
    {
46
        $this->settings = $settings;
432✔
47
    }
432✔
48

49
    public function getAccountId()
50
    {
51
        return $this->accountId;
×
52
    }
53

54
    public function setAccountId($accountId)
55
    {
56
        $this->accountId = $accountId;
378✔
57
    }
378✔
58

59
    public function setHooksManager($hooksManager)
60
    {
61
        $this->hooksManager = $hooksManager;
402✔
62
    }
402✔
63

64
    public function getHooksManager()
65
    {
66
        return $this->hooksManager;
×
67
    }
68

69
    /**
70
     * Returns variation for the user for given campaign
71
     * This method achieves the variation assignment in the following way:
72
     * If campaign is part of any group, the winner is found in the following way:
73
     * 1. Check whitelisting for called campaign, if passed return targeted variation.
74
     * 2. Check user storage for called campaign, if passed return stored variation.
75
     * 3. Check presegmentation and traffic allocation for called campaign, if passed then
76
     * check whitelisting and user storage for other campaigns of same group if any
77
     * campaign passes return None else find eligible campaigns
78
     * 4. Find winner campaign from eligible campaigns and if winner campaign is same as
79
     * called campaign return bucketed variation and store variation in user storage,
80
     * however if winner campaign is not called campaign return None
81
     *
82
     * However if campaign is not part of any group, then this method achieves the variation
83
     * assignment in the following way:
84
     * 1. First get variation from UserStorage, if variation is found in user_storage_data,
85
     * return from there
86
     * 2. Evaluates white listing users for each variation, and find a targeted variation.
87
     * 3. If no targeted variation is found, evaluate pre-segmentation result
88
     * 4. Evaluate percent traffic
89
     * 5. If user becomes part of campaign assign a variation.
90
     * 6. Store the variation found in the user_storage
91
     *
92
     * @param  $userStorageObj
93
     * @param  $campaign
94
     * @param  $userId
95
     * @param  array $options
96
     * @param  $apiName
97
     * @param  $goalIdentifier
98
     * @return array|mixed|null
99
     */
100
    public function fetchVariationData($userStorageObj, $campaign, $userId, $options = [], $apiName = '', $goalIdentifier = '')
101
    {
102
        LoggerService::setApiName($apiName);
300✔
103
        $bucketInfo = null;
300✔
104
        $this->hasStoredVariation = false;
300✔
105

106
        if ($campaign == null) {
300✔
107
            return $bucketInfo;
6✔
108
        }
109

110
        $isCampaignPartOfGroup = $this->settings && CampaignUtil::isPartOfGroup($this->settings, $campaign["id"]);
300✔
111
        $campaignKey = $campaign['key'];
300✔
112
        $decision['isUserWhitelisted'] = false;
300✔
113
        $decision['fromUserStorageService'] = false;
300✔
114

115
        # get new bucketing enabled flag from settings
116
        if ($this->settings!=null && isset($this->settings["isNB"]) && $this->settings["isNB"]) {
300✔
117
            $is_new_bucketing_enabled = true;
30✔
118
        } else {
5✔
119
            $is_new_bucketing_enabled = false;
270✔
120
        }
121

122
        // VWO generated UUID based on passed UserId and Account ID
123
        if (isset($this->accountId)) {
300✔
124
            $decision['vwoUserId'] = UuidUtil::get($userId, $this->accountId);
288✔
125
        }
48✔
126

127
        if ($isCampaignPartOfGroup) {
300✔
128
            $groupId = $this->settings["campaignGroups"][$campaign["id"]];
114✔
129
            $decision["groupId"] = $groupId;
114✔
130
            $groupName = $this->settings["groups"][$groupId]["name"];
114✔
131
            $decision["groupName"] = $groupName;
114✔
132
        }
19✔
133

134
        //check for whitelisting if applied and get Variation Info
135
        $bucketInfo = CampaignUtil::findVariationFromWhiteListing($campaign, $userId, $options, $is_new_bucketing_enabled);
300✔
136
        // do murmur operations and get Variation for the userId
137
        if ($bucketInfo == null) {
300✔
138
            if (isset($campaign['isAlwaysCheckSegment'])) {
276✔
139
                $isPreSegmentation = ValidationsUtil::checkPreSegmentation($campaign, $userId, $options);
6✔
140
                $bucketInfo = $this->getVariationIfPreSegmentationApplied($isPreSegmentation, $campaign, $userId, $userStorageObj, $goalIdentifier);
6✔
141
            } else {
1✔
142
                $bucketInfo = $this->userStorageGet($userStorageObj, $userId, $campaign);
270✔
143
                if ($bucketInfo == null) {
270✔
144
                    if (self::checkCampaignNotActivated($apiName, $userStorageObj, $userId, $campaignKey)) {
240✔
145
                        return $bucketInfo;
12✔
146
                    }
147

148
                    $isPresegmentation = ValidationsUtil::checkPreSegmentation($campaign, $userId, $options);
234✔
149
                    $isPresegmentationAndTrafficPassed = $isPresegmentation && self::isUserPartOfCampaign($userId, $campaign['percentTraffic'], $campaign, $is_new_bucketing_enabled);
234✔
150
                    if ($isPresegmentationAndTrafficPassed && $isCampaignPartOfGroup) {
234✔
151
                        $groupCampaigns = CampaignUtil::getGroupCampaigns($this->settings, $groupId);
72✔
152

153
                        if ($groupCampaigns) {
72✔
154
                            $isAnyCampaignWhitelistedOrStored = $this->checkWhitelistingOrStorageForGroupedCampaigns($userStorageObj, $userId, $campaign, $groupCampaigns, $groupName, $options);
72✔
155

156
                            // Return None as other campaign(s) is/are whitelisted or stored
157
                            if ($isAnyCampaignWhitelistedOrStored) {
72✔
158
                                LoggerService::log(
12✔
159
                                    Logger::INFO,
12✔
160
                                    'MEG_CALLED_CAMPAIGN_NOT_WINNER',
12✔
161
                                    [
162
                                        '{userId}' => $userId,
12✔
163
                                        '{campaignKey}' => $campaign["key"],
12✔
164
                                        '{groupName}' => $groupName
10✔
165
                                    ],
2✔
166
                                    self::CLASSNAME
10✔
167
                                );
2✔
168
                                return null;
12✔
169
                            }
170

171
                            $eligibleCampaigns = self::getEligibleCampaigns($userId, $groupCampaigns, $campaign, $options, $is_new_bucketing_enabled);
60✔
172
                            $megAlgoNumber = isset($this->settings["groups"][$groupId]["et"]) ? $this->settings["groups"][$groupId]["et"] : self::RandomAlgo ;
60✔
173

174
                            $nonEligibleCampaignsKey = self::getNonEligibleCampaignsKey($eligibleCampaigns, $groupCampaigns);
60✔
175
                            LoggerService::log(
60✔
176
                                Logger::DEBUG,
60✔
177
                                'MEG_ELIGIBLE_CAMPAIGNS',
60✔
178
                                [
179
                                    '{userId}' => $userId,
60✔
180
                                    '{eligibleCampaignKeys}' => implode(",", self::getEligibleCampaignsKey($eligibleCampaigns)),
60✔
181
                                    '{inEligibleText}' => "campaigns:" . ($nonEligibleCampaignsKey ? implode(",", $nonEligibleCampaignsKey) : "no campaigns"),
60✔
182
                                    '{groupName}' => $groupName
50✔
183
                                ],
10✔
184
                                self::CLASSNAME
50✔
185
                            );
10✔
186

187
                            LoggerService::log(
60✔
188
                                Logger::INFO,
60✔
189
                                'MEG_ELIGIBLE_CAMPAIGNS',
60✔
190
                                [
191
                                    '{userId}' => $userId,
60✔
192
                                    '{noOfEligibleCampaigns}' => count($eligibleCampaigns),
60✔
193
                                    '{noOfGroupCampaigns}' => count($groupCampaigns),
60✔
194
                                    '{groupName}' => $groupName
50✔
195
                                ],
10✔
196
                                self::CLASSNAME
50✔
197
                            );
10✔
198
                            $winnerCampaign = $this->findWinnerCampaign($userId, $eligibleCampaigns, $megAlgoNumber, $groupId, $this->settings);
60✔
199

200
                            LoggerService::log(
60✔
201
                                Logger::INFO,
60✔
202
                                'MEG_GOT_WINNER_CAMPAIGN',
60✔
203
                                [
204
                                    '{userId}' => $userId,
60✔
205
                                    '{campaignKey}' => $winnerCampaign["key"],
60✔
206
                                    '{groupName}' => $groupName
50✔
207
                                ],
10✔
208
                                self::CLASSNAME
50✔
209
                            );
10✔
210
                            if ($winnerCampaign && $winnerCampaign["id"] == $campaign["id"]) {
60✔
211
                                $bucketInfo = Bucketer::getBucket($userId, $campaign, $is_new_bucketing_enabled);
42✔
212
                                if ($bucketInfo == null) {
42✔
213
                                    return $bucketInfo;
×
214
                                } else {
215
                                    $this->userStorageSet($userStorageObj, $userId, $campaign['key'], $bucketInfo, $goalIdentifier);
42✔
216
                                }
217
                            } else {
7✔
218
                                // No winner/variation
219
                                LoggerService::log(
36✔
220
                                    Logger::INFO,
36✔
221
                                    'MEG_CALLED_CAMPAIGN_NOT_WINNER',
36✔
222
                                    [
223
                                        '{userId}' => $userId,
36✔
224
                                        '{campaignKey}' => $campaign["key"],
36✔
225
                                        '{groupName}' => $groupName
30✔
226
                                    ],
6✔
227
                                    self::CLASSNAME
30✔
228
                                );
6✔
229
                                return $bucketInfo;
36✔
230
                            }
231
                        }
7✔
232
                    }
7✔
233

234
                    if ($bucketInfo == null) {
204✔
235
                        $bucketInfo = $this->getVariationIfPreSegmentationApplied($isPresegmentation, $campaign, $userId, $userStorageObj, $goalIdentifier);
197✔
236
                    }
27✔
237
                } else {
34✔
238
                    $this->hasStoredVariation = true;
42✔
239
                    $decision['fromUserStorageService'] = !!$bucketInfo['name'];
42✔
240
                    LoggerService::log(
42✔
241
                        Logger::INFO,
42✔
242
                        'GOT_STORED_VARIATION',
42✔
243
                        [
244
                            '{userId}' => $userId,
42✔
245
                            '{variationName}' => $bucketInfo['name'],
42✔
246
                            '{campaignKey}' => $campaign['key']
42✔
247
                        ],
7✔
248
                        self::CLASSNAME
205✔
249
                    );
7✔
250
                }
251
            }
252
        } else {
41✔
253
            $decision['isUserWhitelisted'] = true;
30✔
254
        }
255

256
        if ($bucketInfo != null) {
270✔
257
            $decision['campaignId'] = $campaign['id'];
246✔
258
            $decision['campaignKey'] = $campaignKey;
246✔
259
            $decision['campaignType'] = $campaign['type'];
246✔
260
            // campaign segmentation conditions
261
            $decision['customVariables'] = isset($options['customVariables']) ? $options['customVariables'] : [];
246✔
262
            // event name
263
            $decision['event'] = Hooks::DECISION_TYPES['CAMPAIGN_DECISION'];
246✔
264
            // goal tracked in case of track API
265
            $decision['goalIdentifier'] = $goalIdentifier;
246✔
266
            // campaign whitelisting flag
267
            $decision['isForcedVariationEnabled'] = isset($campaign['isForcedVariationEnabled']) ? $campaign['isForcedVariationEnabled'] : false;
246✔
268
            $decision['sdkVersion'] = ImpressionBuilder::SDK_VERSION;
246✔
269
            // API name which triggered the event
270
            $decision['source'] = $apiName;
246✔
271
            // Passed in API
272
            $decision['userId'] = $userId;
246✔
273
            // Campaign Whitelisting conditions
274
            $decision['variationTargetingVariables'] = isset($options['variationTargetingVariables']) ? $options['variationTargetingVariables'] : [];
246✔
275

276
            if (isset($campaign['name'])) {
246✔
277
                $decision["campaignName"] = $campaign["name"];
216✔
278
            }
36✔
279

280
            $variationName = $bucketInfo['name'];
246✔
281
            if ($campaign['type'] === CampaignTypes::FEATURE_ROLLOUT) {
246✔
282
                $decision['isFeatureEnabled'] = true;
36✔
283
            } else {
6✔
284
                if ($campaign['type'] === CampaignTypes::FEATURE_TEST) {
210✔
285
                    $decision['isFeatureEnabled'] = $bucketInfo['isFeatureEnabled'];
42✔
286
                }
7✔
287
                $decision['variationName'] = $variationName;
210✔
288
                $decision['variationId'] = $bucketInfo['id'];
210✔
289
            }
290
            $this->hooksManager->execute($decision);
246✔
291
        }
41✔
292

293
        return $bucketInfo;
270✔
294
    }
295

296
    /***
297
     * @param  $userStorageObj
298
     * @param  $userId
299
     * @param  $campaign
300
     * @param  bool $disableLogs    optional: disable logs if True
301
     * @return array|null
302
     */
303
    private function userStorageGet($userStorageObj, $userId, $campaign, $disableLogs = false)
304
    {
305

306
        if (!empty($userStorageObj)) {
276✔
307
            $campaignKey = $campaign['key'];
54✔
308
            try {
309
                $variationInfo = $userStorageObj->get($userId, $campaignKey);
54✔
310
            } catch (Exception $e) {
9✔
311
                $variationInfo = null;
×
312
                LoggerService::log(
×
313
                    Logger::ERROR,
×
314
                    'USER_STORAGE_SERVICE_GET_FAILED',
×
315
                    ['{userId}' => $userId, '{error}' => $e->getMessage()],
×
316
                    self::CLASSNAME
317
                );
318
            }
319

320
            if (
321
                isset($variationInfo['variationName']) && is_string(
54✔
322
                    $variationInfo['variationName']
54✔
323
                ) && !empty($variationInfo['variationName']) && array_key_exists('campaignKey', $variationInfo) && $variationInfo['campaignKey'] == $campaignKey
54✔
324
            ) {
9✔
325
                LoggerService::log(
54✔
326
                    Logger::INFO,
54✔
327
                    'GETTING_DATA_USER_STORAGE_SERVICE',
54✔
328
                    ['{userId}' => $userId, '{campaignKey}' => $campaignKey],
54✔
329
                    self::CLASSNAME,
54✔
330
                    $disableLogs
27✔
331
                );
9✔
332
                if ($campaign !== null) {
54✔
333
                    $bucketInfo = Bucketer::getBucketVariationId(
54✔
334
                        $campaign,
54✔
335
                        $variationInfo['variationName']
54✔
336
                    );
9✔
337
                    if (isset($variationInfo['goalIdentifier'])) {
54✔
338
                        $bucketInfo['goalIdentifier'] = $variationInfo['goalIdentifier'];
6✔
339
                    }
1✔
340
                    return $bucketInfo;
54✔
341
                }
342
            } else {
343
                LoggerService::log(
18✔
344
                    Logger::DEBUG,
18✔
345
                    'USER_STORAGE_SERVICE_NO_STORED_DATA',
18✔
346
                    ['{userId}' => $userId, '{campaignKey}' => $campaignKey],
18✔
347
                    self::CLASSNAME,
18✔
348
                    $disableLogs
9✔
349
                );
3✔
350
            }
351
        } else {
3✔
352
            LoggerService::log(
228✔
353
                Logger::DEBUG,
228✔
354
                'USER_STORAGE_SERVICE_NOT_CONFIGURED',
228✔
355
                [],
228✔
356
                self::CLASSNAME,
228✔
357
                $disableLogs
114✔
358
            );
38✔
359
        }
360

361
        return null;
240✔
362
    }
363

364
    /**
365
     * this function will save the data to user-storage
366
     *
367
     * @param string $userId
368
     * @param string $campaignKey
369
     * @param array  $variation
370
     * @param string $goalIdentifier
371
     */
372
    public function userStorageSet($userStorageObj, $userId, $campaignKey, $variation, $goalIdentifier = '')
373
    {
374
        if (!empty($userStorageObj)) {
198✔
375
            $campaignInfo = CommonUtil::getUserCampaignVariationMapping($campaignKey, $variation, $userId, $goalIdentifier);
18✔
376
            try {
377
                $userStorageObj->set($campaignInfo);
18✔
378
            } catch (Exception $e) {
3✔
379
                LoggerService::log(
×
380
                    Logger::ERROR,
×
381
                    'USER_STORAGE_SERVICE_GET_FAILED',
×
382
                    ['{userId}' => $userId, '{error}' => $e->getMessage()],
×
383
                    self::CLASSNAME
384
                );
385
            }
386
            LoggerService::log(
18✔
387
                Logger::INFO,
18✔
388
                'SETTING_DATA_USER_STORAGE_SERVICE',
18✔
389
                ['{userId}' => $userId, '{campaignKey}' => $campaignKey],
18✔
390
                self::CLASSNAME
15✔
391
            );
3✔
392
        } else {
3✔
393
            LoggerService::log(Logger::DEBUG, 'USER_STORAGE_SERVICE_NOT_CONFIGURED', [], self::CLASSNAME);
186✔
394
        }
395
    }
198✔
396

397
    /**
398
     * Finds and returns eligible campaigns from $groupCampaigns.
399
     *
400
     * @param  string $userId         the unique ID assigned to User
401
     * @param  array  $groupCampaigns campaigns part of group
402
     * @param  array  $calledCampaign campaign for which api is called
403
     * @param  array  $options        contains variables for segmentation
404
     * @return array  eligible campaigns from which winner campaign is to be selected
405
     */
406
    private static function getEligibleCampaigns($userId, $groupCampaigns, $calledCampaign, $options, $is_new_bucketing_enabled)
407
    {
408
        $eligibleCampaigns = [];
60✔
409
        foreach ($groupCampaigns as $campaign) {
60✔
410
            if ($calledCampaign["id"] == $campaign["id"] || ValidationsUtil::checkPreSegmentation($campaign, $userId, $options, true) && self::isUserPartOfCampaign($userId, $campaign['percentTraffic'], $campaign, $is_new_bucketing_enabled)) {
60✔
411
                $eligibleCampaigns[] = $campaign;
60✔
412
            }
10✔
413
        }
10✔
414
        return $eligibleCampaigns;
60✔
415
    }
416

417
    /**
418
     * Evaluates whether the user should become part of campaign or not
419
     *
420
     * @param  string    $userId         the unique ID assigned to User
421
     * @param  int|float $percentTraffic traffic for a campaign in which user is participating
422
     * @return bool
423
     */
424
    private static function isUserPartOfCampaign($userId, $percentTraffic, $campaign, $is_new_bucketing_enabled)
425
    {
426
        list($bucketVal, $hashValue) = Bucketer::getBucketVal($userId, $campaign, $is_new_bucketing_enabled, true);
216✔
427
        return Bucketer::isUserPartofCampaign($bucketVal, $percentTraffic);
216✔
428
    }
429

430
    /**
431
     * Finds and returns the winner campaign from $eligibleCampaigns list.
432
     *
433
     * @param  string $userId            the unique ID assigned to User
434
     * @param  array  $eligibleCampaigns campaigns part of group which were eligible to be winner
435
     * @return array  winner campaign from eligible_campaigns
436
     */
437
    private static function findWinnerCampaign($userId, $eligibleCampaigns, $megAlgoNumber, $groupId, $settingsFile)
438
    {
439
        # get new bucketing enabled flag from settings
440
        if ($settingsFile!=null && isset($settingsFile["isNB"]) && $settingsFile["isNB"]) {
60✔
441
            $is_new_bucketing_enabled = true;
×
442
        } else {
443
            $is_new_bucketing_enabled = false;
60✔
444
        }
445

446
        if (count($eligibleCampaigns) == 1) {
60✔
447
            return  $eligibleCampaigns[0];
×
448
        } else {
449
            if ($megAlgoNumber == self::RandomAlgo) {
60✔
450
            //Scale the traffic percent of each campaign
451
                $eligibleCampaigns = CampaignUtil::scaleCampaigns($eligibleCampaigns);
30✔
452
            //Allocate new range for campaigns
453
                $eligibleCampaigns = Bucketer::addRangesToCampaigns($eligibleCampaigns);
30✔
454
            //Now retrieve the campaign from the modified_campaign_for_whitelisting
455
                list($bucketVal, $hashValue) = Bucketer::getBucketVal($userId, [], false, true);
30✔
456
                return Bucketer::getCampaignUsingRange($bucketVal, $eligibleCampaigns);
30✔
457
            } else {
458
                $winnerCampaign = null;
30✔
459

460
                $found = false; // flag to check whether winnerCampaign has been found or not and helps to break from the outer loop
30✔
461
                $priorityOrder = isset($settingsFile['groups'][$groupId]['p']) ? $settingsFile['groups'][$groupId]['p'] : [];
30✔
462
                $wt = isset($settingsFile['groups'][$groupId]['wt']) ? $settingsFile['groups'][$groupId]['wt'] : [];
30✔
463

464
                for ($i = 0; $i < count($priorityOrder); $i++) {
30✔
465
                    for ($j = 0; $j < count($eligibleCampaigns); $j++) {
12✔
466
                        if ($eligibleCampaigns[$j]['id'] == $priorityOrder[$i]) {
12✔
467
                            $winnerCampaign = $eligibleCampaigns[$j];
12✔
468
                            $found = true;
12✔
469
                            break;
12✔
470
                        }
471
                    }
2✔
472
                    if ($found == true) {
12✔
473
                        break;
12✔
474
                    }
475
                }
476

477
                // If winnerCampaign not found through Priority, then go for weighted Random distribution and for that,
478
                // Store the list of campaigns (participatingCampaigns) out of eligibleCampaigns and their corresponding weights which are present in weightage distribution array (wt) in 2 different lists
479
                if ($winnerCampaign == null) {
30✔
480
                    $weights = array();
18✔
481
                    $partipatingCampaignList = array();
18✔
482

483
                    for ($i = 0; $i < count($eligibleCampaigns); $i++) {
18✔
484
                        $campaignId = $eligibleCampaigns[$i]['id'];
18✔
485
                        if (isset($wt[$campaignId])) {
18✔
486
                            $weights[] = $wt[$campaignId];
18✔
487
                            $partipatingCampaignList[] = $eligibleCampaigns[$i];
18✔
488
                        }
3✔
489
                    }
3✔
490

491
                    /*
492
                    * Finding winner campaign using weighted random distribution :
493
                    1. Calculate the sum of all weights
494
                    2. Generate a random number between 0 and the weight sum:
495
                    3. Iterate over the weights array and subtract each weight from the random number until the random number becomes negative. The corresponding ith value is the required value
496
                    4. Set the ith campaign as WinnerCampaign
497
                    */
498
                    $weightSum = array_sum($weights);
18✔
499
                    $randomNumber = rand(1, $weightSum);
18✔
500

501
                    $sum = 0;
18✔
502
                    for ($i = 0; $i < count($weights); $i++) {
18✔
503
                        $sum += $weights[$i];
18✔
504
                        if ($randomNumber < $sum) {
18✔
505
                            $winnerCampaign = $partipatingCampaignList[$i];
18✔
506
                            break;
18✔
507
                        }
508
                    }
2✔
509
                }
3✔
510

511
                return $winnerCampaign;
30✔
512
            }
513
        }
514
    }
515

516
    /**
517
     * Get campaign keys of all eligibleCampaigns.
518
     *
519
     * @param  array $eligibleCampaigns contains eligibleCampaigns
520
     * @return array campaign keys of all eligibleCampaigns
521
     */
522
    private static function getEligibleCampaignsKey($eligibleCampaigns)
523
    {
524
        $eligibleCampaignsName = [];
60✔
525
        foreach ($eligibleCampaigns as $campaign) {
60✔
526
            $eligibleCampaignsName[] = $campaign["key"];
60✔
527
        }
10✔
528
        return $eligibleCampaignsName;
60✔
529
    }
530

531
    /**
532
     * get campaign keys of all non eligibleCampaigns.
533
     *
534
     * @param  array $eligibleCampaigns contains eligibleCampaigns
535
     * @param  array $groupCampaigns    contains groupCampaigns
536
     * @return array campaign keys of all non eligibleCampaigns
537
     */
538
    private static function getNonEligibleCampaignsKey($eligibleCampaigns, $groupCampaigns)
539
    {
540
        $NonEligibleCampaignsName = [];
60✔
541
        foreach ($groupCampaigns as $groupCampaign) {
60✔
542
            if (!in_array($groupCampaign, $eligibleCampaigns)) {
60✔
543
                $NonEligibleCampaignsName[] = $groupCampaign["key"];
20✔
544
            }
545
        }
10✔
546
        return $NonEligibleCampaignsName;
60✔
547
    }
548

549
    /**
550
     * Checks if any other campaign in groupCampaigns satisfies whitelisting or is in user storage.
551
     *
552
     * @param  object $userStorageObj userStorage object
553
     * @param  string $userId         the unique ID assigned to User
554
     * @param  array  $calledCampaign
555
     * @param  array  $groupCampaigns campaigns part of group
556
     * @param  string $groupName      group name
557
     * @param  array  $options        contains variationTargetingVariables
558
     * @return bool
559
     */
560
    private function checkWhitelistingOrStorageForGroupedCampaigns($userStorageObj, $userId, $calledCampaign, $groupCampaigns, $groupName, $options)
561
    {
562
        # get new bucketing enabled flag from settings
563
        if ($this->settings!=null && isset($this->settings["isNB"]) && $this->settings["isNB"]) {
72✔
564
            $is_new_bucketing_enabled = true;
×
565
        } else {
566
            $is_new_bucketing_enabled = false;
72✔
567
        }
568

569
        foreach ($groupCampaigns as $campaign) {
72✔
570
            if ($calledCampaign["id"] != $campaign["id"]) {
72✔
571
                $targetedVariation = CampaignUtil::findVariationFromWhiteListing($campaign, $userId, $options, $is_new_bucketing_enabled, true);
72✔
572
                if ($targetedVariation) {
72✔
573
                    LoggerService::log(
6✔
574
                        Logger::INFO,
6✔
575
                        'OTHER_CAMPAIGN_SATISFIES_WHITELISTING_STORAGE',
6✔
576
                        [
577
                            '{userId}' => $userId,
6✔
578
                            '{campaignKey}' => $campaign["key"],
6✔
579
                            '{groupName}' => $groupName,
6✔
580
                            '{type}' => "whitelisting"
5✔
581
                        ],
1✔
582
                        self::CLASSNAME
5✔
583
                    );
1✔
584
                    return true;
28✔
585
                }
586
            }
11✔
587
        }
11✔
588

589
        foreach ($groupCampaigns as $campaign) {
66✔
590
            if ($calledCampaign["id"] != $campaign["id"]) {
66✔
591
                $userStorageData = $this->userStorageGet($userStorageObj, $userId, $campaign, true);
66✔
592
                if ($userStorageData) {
66✔
593
                    LoggerService::log(
6✔
594
                        Logger::INFO,
6✔
595
                        'OTHER_CAMPAIGN_SATISFIES_WHITELISTING_STORAGE',
6✔
596
                        [
597
                            '{userId}' => $userId,
6✔
598
                            '{campaignKey}' => $campaign["key"],
6✔
599
                            '{groupName}' => $groupName,
6✔
600
                            '{type}' => "user storage"
5✔
601
                        ],
1✔
602
                        self::CLASSNAME
5✔
603
                    );
1✔
604
                    return true;
26✔
605
                }
606
            }
10✔
607
        }
10✔
608
        return false;
60✔
609
    }
610

611
    /**
612
     * Checks if campaign is activated for track, getVariationName, getFeatureVariableValue API when userStorage is used
613
     *
614
     * @param  string $apiName        api name
615
     * @param  object $userStorageObj userStorage object
616
     * @param  string $userId         the unique ID assigned to User
617
     * @param  string $campaignKey    Campaign Key
618
     * @return bool
619
     */
620
    private static function checkCampaignNotActivated($apiName, $userStorageObj, $userId, $campaignKey)
621
    {
622
        if (
623
            in_array($apiName, ['track', 'getVariationName', 'getFeatureVariableValue'])
240✔
624
            && !empty($userStorageObj)
240✔
625
        ) {
40✔
626
            LoggerService::log(
12✔
627
                Logger::WARNING,
12✔
628
                'CAMPAIGN_NOT_ACTIVATED',
12✔
629
                ['{userId}' => $userId, '{campaignKey}' => $campaignKey, '{api}' => $apiName],
12✔
630
                self::CLASSNAME
10✔
631
            );
2✔
632
            LoggerService::log(
12✔
633
                Logger::INFO,
12✔
634
                'CAMPAIGN_NOT_ACTIVATED',
12✔
635
                [
636
                    '{userId}' => $userId,
12✔
637
                    '{campaignKey}' => $campaignKey,
12✔
638
                    '{reason}' => $apiName === 'track' ? 'track it' : 'get the decision/value'
12✔
639
                ],
2✔
640
                self::CLASSNAME
10✔
641
            );
2✔
642
            return true;
12✔
643
        }
644
        return false;
234✔
645
    }
646

647
    /**
648
     * Get variation by murmur logic if pre segmentation pass
649
     *
650
     * @param  bool   $isPreSegmentation pre-segmentation flag
651
     * @param  string $userId         the unique ID assigned to User
652
     * @param  object $userStorageObj userStorage object
653
     * @param  array  $campaign       campaign data
654
     * @param  string $goalIdentifier goal Identifier used in track API
655
     * @return array|null
656
     */
657
    private function getVariationIfPreSegmentationApplied($isPreSegmentation, $campaign, $userId, $userStorageObj = null, $goalIdentifier = '')
658
    {
659
        $bucketInfo = null;
168✔
660
        //check for pre-segmentation if applied
661
        if ($isPreSegmentation == false) {
168✔
662
            LoggerService::log(
18✔
663
                Logger::INFO,
18✔
664
                'DECISION_NO_VARIATION_ALLOTED',
18✔
665
                [
666
                    '{userId}' => $userId,
18✔
667
                    '{campaignKey}' => $campaign['key']
18✔
668
                ],
3✔
669
                self::CLASSNAME
15✔
670
            );
3✔
671

672
            return $bucketInfo;
18✔
673
        }
674

675
        # get new bucketing enabled flag from settings
676
        if ($this->settings!=null && isset($this->settings["isNB"]) && $this->settings["isNB"]) {
150✔
677
            $is_new_bucketing_enabled = true;
30✔
678
        } else {
5✔
679
            $is_new_bucketing_enabled = false;
120✔
680
        }
681

682
        $bucketInfo = Bucketer::getBucket($userId, $campaign, $is_new_bucketing_enabled);
150✔
683
        LoggerService::log(
150✔
684
            Logger::INFO,
150✔
685
            'USER_VARIATION_ALLOCATION_STATUS',
150✔
686
            [
687
                '{userId}' => $userId,
150✔
688
                '{status}' => $bucketInfo ? 'got variation:' . $bucketInfo['name'] : 'did not get any variation',
150✔
689
                '{campaignKey}' => $campaign['key']
150✔
690
            ],
25✔
691
            self::CLASSNAME
125✔
692
        );
25✔
693
        if ($bucketInfo == null) {
150✔
694
            return $bucketInfo;
72✔
695
        }
696

697
        $this->userStorageSet($userStorageObj, $userId, $campaign['key'], $bucketInfo, $goalIdentifier);
138✔
698
        return $bucketInfo;
138✔
699
    }
700
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc