Decider::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 5
c 1
b 0
f 0
nc 1
nop 5
dl 0
loc 7
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * Copyright (C) 2020-2025 Iain Cambridge
7
 *
8
 * This program is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE as published by
10
 * the Free Software Foundation, either version 2.1 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20
 */
21
22
namespace Parthenon\AbTesting\Experiment;
23
24
use Parthenon\AbTesting\Decider\ChoiceDeciderInterface;
25
use Parthenon\AbTesting\Decider\EnabledDecider\DecidedManagerInterface;
26
use Parthenon\AbTesting\Events\SessionCreator;
27
use Parthenon\AbTesting\Repository\ExperimentLogRepositoryInterface;
28
use Parthenon\AbTesting\Repository\ExperimentRepositoryInterface;
29
use Parthenon\Common\Exception\NoEntityFoundException;
30
use Parthenon\User\Entity\UserInterface;
31
use Ramsey\Uuid\Uuid;
32
use Symfony\Component\HttpFoundation\Exception\SessionNotFoundException;
33
use Symfony\Component\HttpFoundation\RequestStack;
34
35
final class Decider implements DeciderInterface
36
{
37
    private ExperimentLogRepositoryInterface $experimentLogRepository;
38
    private RequestStack $requestStack;
39
    private DecidedManagerInterface $enabledDecider;
40
    private ChoiceDeciderInterface $choiceDecider;
41
    private ExperimentRepositoryInterface $experimentRepository;
42
43
    public function __construct(ExperimentRepositoryInterface $experimentRepository, ExperimentLogRepositoryInterface $experimentLogRepository, RequestStack $requestStack, DecidedManagerInterface $enabledDecider, ChoiceDeciderInterface $deciderManager)
44
    {
45
        $this->experimentLogRepository = $experimentLogRepository;
46
        $this->requestStack = $requestStack;
47
        $this->enabledDecider = $enabledDecider;
48
        $this->choiceDecider = $deciderManager;
49
        $this->experimentRepository = $experimentRepository;
50
    }
51
52
    public function doExperiment(string $experimentName, ?UserInterface $user = null, array $options = []): string
53
    {
54
        try {
55
            $session = $this->requestStack->getSession();
56
        } catch (SessionNotFoundException $e) {
57
            return 'control';
58
        }
59
60
        if (!$this->enabledDecider->isTestable()) {
61
            return 'control';
62
        }
63
64
        $decision = $this->choiceDecider->getChoice($experimentName);
65
66
        if (!is_null($decision)) {
67
            return $decision;
68
        }
69
70
        $decision = $session->get('ab_testing_'.$experimentName);
71
72
        if (null === $decision) {
73
            $randomNumber = mt_rand(0, 100);
74
            $lowerBound = 0;
75
            try {
76
                $experiment = $this->experimentRepository->findByName($experimentName);
77
                $decision = 'control';
78
                foreach ($experiment->getVariants() as $variant) {
79
                    $upperBound = $lowerBound + $variant->getPercentage();
80
                    if (100 === $upperBound) {
81
                        $decision = $variant->getName();
82
                        break;
83
                    }
84
                    if ($randomNumber > $lowerBound && $randomNumber <= $upperBound) {
85
                        $decision = $variant->getName();
86
                        break;
87
                    }
88
                    $lowerBound = $upperBound;
89
                }
90
            } catch (NoEntityFoundException $e) {
91
                $decision = 'control';
92
            }
93
94
            $idAsString = $session->get(SessionCreator::SESSION_ID);
95
            $uuid = Uuid::fromString($idAsString);
96
97
            $this->experimentLogRepository->saveDecision($uuid, $experimentName, $decision);
98
            $session->set('ab_testing_'.$experimentName, $decision);
99
        }
100
101
        return $decision;
102
    }
103
}
104