Skip to content

Commit 8391618

Browse files
authored
Merge pull request #29 from launchdarkly/eb/ch19976/explanations
implement evaluation with explanations
2 parents 87417f1 + 39d5105 commit 8391618

File tree

12 files changed

+1036
-203
lines changed

12 files changed

+1036
-203
lines changed

src/LaunchDarkly/EvalResult.php

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,37 +4,28 @@
44

55
class EvalResult
66
{
7-
private $_variation = null;
8-
private $_value = null;
7+
/** @var EvaluationDetail */
8+
private $_detail = null;
99
/** @var array */
1010
private $_prerequisiteEvents = [];
1111

1212
/**
1313
* EvalResult constructor.
14-
* @param null $value
14+
* @param EvaluationDetail $detail
1515
* @param array $prerequisiteEvents
1616
*/
17-
public function __construct($variation, $value, array $prerequisiteEvents)
17+
public function __construct($detail, array $prerequisiteEvents)
1818
{
19-
$this->_variation = $variation;
20-
$this->_value = $value;
19+
$this->_detail = $detail;
2120
$this->_prerequisiteEvents = $prerequisiteEvents;
2221
}
2322

2423
/**
25-
* @return int | null
24+
* @return EvaluationDetail
2625
*/
27-
public function getVariation()
26+
public function getDetail()
2827
{
29-
return $this->_variation;
30-
}
31-
32-
/**
33-
* @return null
34-
*/
35-
public function getValue()
36-
{
37-
return $this->_value;
28+
return $this->_detail;
3829
}
3930

4031
/**
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
3+
namespace LaunchDarkly;
4+
5+
/**
6+
* An object returned by LDClient.variationDetail(), combining the result of a flag evaluation with
7+
* an explanation of how it was calculated.
8+
*/
9+
class EvaluationDetail
10+
{
11+
private $_variationIndex = null;
12+
private $_value = null;
13+
private $_reason = null;
14+
15+
/**
16+
* EvaluationDetail constructor.
17+
* @param mixed $value the value of the flag variation
18+
* @param int|null $variationIndex the index of the flag variation, or null if it was the default value
19+
* @param EvaluationReason $reason evaluation reason properties
20+
*/
21+
public function __construct($value, $variationIndex, $reason = null)
22+
{
23+
$this->_value = $value;
24+
$this->_variationIndex = $variationIndex;
25+
$this->_reason = $reason;
26+
}
27+
28+
/**
29+
* Returns the value of the flag variation for the user.
30+
*
31+
* @return mixed
32+
*/
33+
public function getValue()
34+
{
35+
return $this->_value;
36+
}
37+
38+
/**
39+
* Returns the index of the flag variation for the user, e.g. 0 for the first variation -
40+
* or null if it was the default value.
41+
*
42+
* @return int | null
43+
*/
44+
public function getVariationIndex()
45+
{
46+
return $this->_variationIndex;
47+
}
48+
49+
/**
50+
* Returns information about how the flag value was calculated.
51+
*
52+
* @return EvaluationReason
53+
*/
54+
public function getReason()
55+
{
56+
return $this->_reason;
57+
}
58+
59+
/**
60+
* Returns true if the flag evaluated to the default value, rather than one of its variations.
61+
*
62+
* @return bool
63+
*/
64+
public function isDefaultValue()
65+
{
66+
return ($this->_variationIndex === null);
67+
}
68+
}
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
<?php
2+
3+
namespace LaunchDarkly;
4+
5+
/**
6+
* Describes the reason that a flag evaluation produced a particular value. This is part of
7+
* the EvaluationDetail object returned by LDClient.variationDetail().
8+
*/
9+
class EvaluationReason implements \JsonSerializable
10+
{
11+
/**
12+
* A possible value for getKind(): indicates that the flag was off and therefore returned
13+
* its configured off value.
14+
*/
15+
const OFF = 'OFF';
16+
/**
17+
* A possible value for getKind(): indicates that the flag was on but the user did not
18+
* match any targets or rules.
19+
*/
20+
const FALLTHROUGH = 'FALLTHROUGH';
21+
/**
22+
* A possible value for getKind(): indicates that the user key was specifically targeted
23+
* for this flag.
24+
*/
25+
const TARGET_MATCH = 'TARGET_MATCH';
26+
/**
27+
* A possible value for getKind(): indicates that the user matched one of the flag's rules.
28+
*/
29+
const RULE_MATCH = 'RULE_MATCH';
30+
/**
31+
* A possible value for getKind(): indicates that the flag was considered off because it
32+
* had at least one prerequisite flag that either was off or did not return the desired variation.
33+
*/
34+
const PREREQUISITE_FAILED = 'PREREQUISITE_FAILED';
35+
/**
36+
* A possible value for getKind(): indicates that the flag could not be evaluated, e.g.
37+
* because it does not exist or due to an unexpected error.
38+
*/
39+
const ERROR = 'ERROR';
40+
41+
/**
42+
* A possible value for getErrorKind(): indicates that the caller tried to evaluate a flag
43+
* before the client had successfully initialized.
44+
*/
45+
const CLIENT_NOT_READY_ERROR = 'CLIENT_NOT_READY';
46+
47+
/**
48+
* A possible value for getErrorKind(): indicates that the caller provided a flag key that
49+
* did not match any known flag.
50+
*/
51+
const FLAG_NOT_FOUND_ERROR = 'FLAG_NOT_FOUND';
52+
53+
/**
54+
* A possible value for getErrorKind(): indicates that there was an internal inconsistency
55+
* in the flag data, e.g. a rule specified a nonexistent variation. An error message will
56+
* always be logged in this case.
57+
*/
58+
const MALFORMED_FLAG_ERROR = 'MALFORMED_FLAG';
59+
60+
/**
61+
* A possible value for getErrorKind(): indicates that the caller passed null for the user
62+
* parameter, or the user lacked a key.
63+
*/
64+
const USER_NOT_SPECIFIED_ERROR = 'USER_NOT_SPECIFIED';
65+
66+
/**
67+
* A possible value for getErrorKind(): indicates that an unexpected exception stopped flag
68+
* evaluation.
69+
*/
70+
const EXCEPTION_ERROR = 'EXCEPTION';
71+
72+
private $_kind;
73+
private $_errorKind;
74+
private $_ruleIndex;
75+
private $_ruleId;
76+
private $_prerequisiteKey;
77+
78+
/**
79+
* Creates a new instance of the OFF reason.
80+
*/
81+
public static function off()
82+
{
83+
return new EvaluationReason(self::OFF);
84+
}
85+
86+
/**
87+
* Creates a new instance of the FALLTHROUGH reason.
88+
*/
89+
public static function fallthrough()
90+
{
91+
return new EvaluationReason(self::FALLTHROUGH);
92+
}
93+
94+
/**
95+
* Creates a new instance of the TARGET_MATCH reason.
96+
*/
97+
public static function targetMatch()
98+
{
99+
return new EvaluationReason(self::TARGET_MATCH);
100+
}
101+
102+
/**
103+
* Creates a new instance of the RULE_MATCH reason.
104+
*/
105+
public static function ruleMatch($ruleIndex, $ruleId)
106+
{
107+
return new EvaluationReason(self::RULE_MATCH, null, $ruleIndex, $ruleId);
108+
}
109+
110+
/**
111+
* Creates a new instance of the PREREQUISITE_FAILED reason.
112+
*/
113+
public static function prerequisiteFailed($prerequisiteKey)
114+
{
115+
return new EvaluationReason(self::PREREQUISITE_FAILED, null, null, null, $prerequisiteKey);
116+
}
117+
118+
/**
119+
* Creates a new instance of the ERROR reason.
120+
*/
121+
public static function error($errorKind)
122+
{
123+
return new EvaluationReason(self::ERROR, $errorKind);
124+
}
125+
126+
private function __construct($kind, $errorKind = null, $ruleIndex = null, $ruleId = null, $prerequisiteKey = null)
127+
{
128+
$this->_kind = $kind;
129+
$this->_errorKind = $errorKind;
130+
$this->_ruleIndex = $ruleIndex;
131+
$this->_ruleId = $ruleId;
132+
$this->_prerequisiteKey = $prerequisiteKey;
133+
}
134+
135+
/**
136+
* Returns a constant indicating the general category of the reason, such as OFF.
137+
* @return string
138+
*/
139+
public function getKind()
140+
{
141+
return $this->_kind;
142+
}
143+
144+
/**
145+
* Returns a constant indicating the nature of the error, if getKind() is OFF. Otherwise
146+
* returns null.
147+
* @return string|null
148+
*/
149+
public function getErrorKind()
150+
{
151+
return $this->_errorKind;
152+
}
153+
154+
/**
155+
* Returns the positional index of the rule that was matched (0 for the first), if getKind()
156+
* is RULE_MATCH. Otherwise returns null.
157+
* @return int|null
158+
*/
159+
public function getRuleIndex()
160+
{
161+
return $this->_ruleIndex;
162+
}
163+
164+
/**
165+
* Returns the unique identifier of the rule that was matched, if getKind() is RULE_MATCH.
166+
* Otherwise returns null.
167+
* @return string|null
168+
*/
169+
public function getRuleId()
170+
{
171+
return $this->_ruleId;
172+
}
173+
174+
/**
175+
* Returns the key of the prerequisite feature flag that failed, if getKind() is
176+
* PREREQUISITE_FAILED. Otherwise returns null.
177+
* @return string|null
178+
*/
179+
public function getPrerequisiteKey()
180+
{
181+
return $this->_prerequisiteKey;
182+
}
183+
184+
/**
185+
* Returns a simple string representation of this object.
186+
*/
187+
public function __toString()
188+
{
189+
switch ($this->_kind) {
190+
case self::RULE_MATCH:
191+
return $this->_kind . '(' . $this->_ruleIndex . ',' . $this->_ruleId . ')';
192+
case self::PREREQUISITE_FAILED:
193+
return $this->_kind . '(' . $this->_prerequisiteKey . ')';
194+
case self::ERROR:
195+
return $this->_kind . '(' . $this->_errorKind . ')';
196+
default:
197+
return $this->_kind;
198+
}
199+
}
200+
201+
/**
202+
* Returns a JSON representation of this object. This method is used automatically
203+
* if you call json_encode().
204+
*/
205+
public function jsonSerialize()
206+
{
207+
$ret = array('kind' => $this->_kind);
208+
if ($this->_errorKind !== null) {
209+
$ret['errorKind'] = $this->_errorKind;
210+
}
211+
if ($this->_ruleIndex !== null) {
212+
$ret['ruleIndex'] = $this->_ruleIndex;
213+
}
214+
if ($this->_ruleId !== null) {
215+
$ret['ruleId'] = $this->_ruleId;
216+
}
217+
if ($this->_prerequisiteKey !== null) {
218+
$ret['prerequisiteKey'] = $this->_prerequisiteKey;
219+
}
220+
return $ret;
221+
}
222+
}

0 commit comments

Comments
 (0)