Test Failed
Pull Request — master (#7)
by Thomas
05:50
created

Variations::get()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 6
cts 6
cp 1
rs 9.9332
c 0
b 0
f 0
cc 3
nc 3
nop 1
crap 3
1
<?php
2
/**
3
 * InnoCraft Ltd - We are the makers of Piwik Analytics, the leading open source analytics platform.
4
 *
5
 * @link https://www.innocraft.com
6
 * @license https://www.gnu.org/licenses/lgpl-3.0.en.html LGPL v3.0
7
 */
8
9
namespace InnoCraft\Experiments;
10
11
use InvalidArgumentException;
12
use InnoCraft\Experiments\Variations\CallableVariation;
13
use InnoCraft\Experiments\Variations\UrlRedirectVariation;
14
use InnoCraft\Experiments\Variations\StandardVariation;
15
use InnoCraft\Experiments\Variations\VariationInterface;
16
17
class Variations {
18
19
    /**
20
     * @var VariationInterface[]
21
     */
22
    private $variations;
23
24
    /**
25
     * @var string
26
     */
27
    private $experimentName;
28
29
    /**
30
     * Variations constructor.
31
     *
32
     * @param string $experimentName
33
     * @param array|VariationInterface[] $variations
34
     */
35 20
    public function __construct($experimentName, $variations)
36
    {
37 20
        $this->experimentName = $experimentName;
38
39 20
        $this->setVariations($variations);
40 20
    }
41
42
    /**
43
     * Adds a new variation to the set of existing variations.
44
     *
45
     * @param array|VariationInterface $variation
46
     */
47 19
    public function addVariation($variation)
48
    {
49 19
        if (is_array($variation)) {
50
            // could be moved to a factory or so
51 11
            if (isset($variation['url'])) {
52
                $this->variations[] = new UrlRedirectVariation($this->experimentName, $variation);
53 11
            } elseif (isset($variation['callable'])) {
54
                $this->variations[] = new CallableVariation($variation);
55
            } else {
56 11
                $this->variations[] = new StandardVariation($variation);
57
            }
58 19
        } elseif ($variation instanceof VariationInterface) {
59 16
            $this->variations[] = $variation;
60 16
        } else {
61
            throw new InvalidArgumentException('A variation needs to be either an array of an instance of VariationInterface');
62
        }
63 19
    }
64
65
    /**
66
     * Set (overwrite) all existing variations by the given variations.
67
     *
68
     * @param array|VariationInterface[] $variations
69
     */
70 20
    public function setVariations($variations)
71
    {
72 20
        $this->variations = [];
73
74 20
        $variations = (array) $variations;
75
76 20
        foreach ($variations as $variation) {
77 11
            $this->addVariation($variation);
78 20
        }
79 20
    }
80
81
    /**
82
     * Get all set variations.
83
     *
84
     * @return VariationInterface[]
85
     */
86 5
    public function getVariations()
87
    {
88 5
        return $this->variations;
89
    }
90
91 2
    protected function getNumVariations()
92
    {
93 2
        return count($this->variations);
94
    }
95
96 2
    protected function getVariationDefaultPercentage()
97
    {
98 2
        $percentageUsed = 100;
99
100 2
        $numVariations = $this->getNumVariations();
101
102 2
        foreach ($this->variations as $variation) {
103 2
            $percentage = $variation->getPercentage();
104 2
            if (isset($percentage)) {
105
                // a fixed percentage was specified, we respect this percentage
106
                $numVariations--;
107
                $percentageUsed = $percentageUsed - $percentage;
108
            }
109 2
        }
110
111 2
        if ($percentageUsed < 0 || !$numVariations) {
112
            $result = 0;
113
        } else {
114
            // and then we share the remaining percentage equally across other remaining variations
115
            // where no percentage was specified
116 2
            $result = (int) ($percentageUsed / $numVariations);
117
        }
118
119 2
        return $result;
120
    }
121
122
    /**
123
     * Chooses randomly a variation from the set of existing variations. Each variation may set a percentage to
124
     * allocate more or less traffic to each variation. By default all variation share the traffic equally.
125
     *
126
     * @return VariationInterface|null   null if no variations are set
127
     */
128 3
    public function selectRandomVariation()
129
    {
130 3
        if (!empty($this->variations)) {
131 2
            $defaultPercentage = $this->getVariationDefaultPercentage();
132 2
            $indexes = [];
133
134 2
            foreach ($this->variations as $index => $variation) {
135 2
                $percentage = $variation->getPercentage();
136 2
                if (!isset($percentage)) {
137 2
                    $percentage = $defaultPercentage;
138 2
                }
139
140 2
                for ($j = 0; $j < $percentage; $j++) {
141 2
                    $indexes[] = $index;
142 2
                }
143 2
            }
144
145 2
            $index = Experiment::getRandomInt(0, count($indexes) - 1);
146 2
            $variationIndex = $indexes[$index];
147
148 2
            return $this->variations[$variationIndex];
149
        }
150 1
    }
151
152
    /**
153
     * Detects whether a variation with the given name exists in the pool of set variations.
154
     *
155
     * @param string $variationName
156
     * @return bool
157
     */
158 18
    public function exists($variationName)
159
    {
160 18
        $variation = $this->get($variationName);
161
162 18
        return !empty($variation);
163
    }
164
165
    /**
166
     * Get the instance of a set variation by its variation name. If no variation matches the given name, null will be
167
     * returned.
168
     *
169
     * @param string $variationName
170
     * @return VariationInterface|null
171
     */
172 18
    public function get($variationName)
173
    {
174 18
        foreach ($this->variations as $variation) {
175
            // we do not use == as we might deal with integers vs string and still want to match eg
176
            // variationId 2 with '2'
177 10
            if ($variation->getName() == $variationName) {
178 7
                return $variation;
179
            }
180 18
        }
181 17
    }
182
183
}