Passed
Branch main (b6a268)
by Iain
04:11
created

Decider::doExperiment()   B

Complexity

Conditions 10
Paths 12

Size

Total Lines 50
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 10
eloc 32
c 1
b 0
f 0
nc 12
nop 3
dl 0
loc 50
rs 7.6666

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * Copyright Humbly Arrogant Ltd 2020-2022.
7
 *
8
 * Use of this software is governed by the Business Source License included in the LICENSE file and at https://getparthenon.com/docs/next/license.
9
 *
10
 * Change Date: TBD ( 3 years after 2.0.0 release )
11
 *
12
 * On the date above, in accordance with the Business Source License, use of this software will be governed by the open source license specified in the LICENSE file.
13
 */
14
15
namespace Parthenon\AbTesting\Experiment;
16
17
use Parthenon\AbTesting\Decider\ChoiceDeciderInterface;
18
use Parthenon\AbTesting\Decider\EnabledDecider\DecidedManagerInterface;
19
use Parthenon\AbTesting\Events\SessionCreator;
20
use Parthenon\AbTesting\Repository\ExperimentLogRepositoryInterface;
21
use Parthenon\AbTesting\Repository\ExperimentRepositoryInterface;
22
use Parthenon\Common\Exception\NoEntityFoundException;
23
use Parthenon\User\Entity\UserInterface;
24
use Ramsey\Uuid\Uuid;
25
use Symfony\Component\HttpFoundation\Exception\SessionNotFoundException;
26
use Symfony\Component\HttpFoundation\RequestStack;
27
28
final class Decider implements DeciderInterface
29
{
30
    private ExperimentLogRepositoryInterface $experimentLogRepository;
31
    private RequestStack $requestStack;
32
    private DecidedManagerInterface $enabledDecider;
33
    private ChoiceDeciderInterface $choiceDecider;
34
    private ExperimentRepositoryInterface $experimentRepository;
35
36
    public function __construct(ExperimentRepositoryInterface $experimentRepository, ExperimentLogRepositoryInterface $experimentLogRepository, RequestStack $requestStack, DecidedManagerInterface $enabledDecider, ChoiceDeciderInterface $deciderManager)
37
    {
38
        $this->experimentLogRepository = $experimentLogRepository;
39
        $this->requestStack = $requestStack;
40
        $this->enabledDecider = $enabledDecider;
41
        $this->choiceDecider = $deciderManager;
42
        $this->experimentRepository = $experimentRepository;
43
    }
44
45
    public function doExperiment(string $experimentName, ?UserInterface $user = null, array $options = []): string
46
    {
47
        try {
48
            $session = $this->requestStack->getSession();
49
        } catch (SessionNotFoundException $e) {
50
            return 'control';
51
        }
52
53
        if (!$this->enabledDecider->isTestable()) {
54
            return 'control';
55
        }
56
57
        $decision = $this->choiceDecider->getChoice($experimentName);
58
59
        if (!is_null($decision)) {
60
            return $decision;
61
        }
62
63
        $decision = $session->get('ab_testing_'.$experimentName);
64
65
        if (null === $decision) {
66
            $randomNumber = mt_rand(0, 100);
67
            $lowerBound = 0;
68
            try {
69
                $experiment = $this->experimentRepository->findByName($experimentName);
70
                $decision = 'control';
71
                foreach ($experiment->getVariants() as $variant) {
72
                    $upperBound = $lowerBound + $variant->getPercentage();
73
                    if (100 === $upperBound) {
74
                        $decision = $variant->getName();
75
                        break;
76
                    }
77
                    if ($randomNumber > $lowerBound && $randomNumber <= $upperBound) {
78
                        $decision = $variant->getName();
79
                        break;
80
                    }
81
                    $lowerBound = $upperBound;
82
                }
83
            } catch (NoEntityFoundException $e) {
84
                $decision = 'control';
85
            }
86
87
            $idAsString = $session->get(SessionCreator::SESSION_ID);
88
            $uuid = Uuid::fromString($idAsString);
89
90
            $this->experimentLogRepository->saveDecision($uuid, $experimentName, $decision);
91
            $session->set('ab_testing_'.$experimentName, $decision);
92
        }
93
94
        return $decision;
95
    }
96
}
97