Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
aa34e8b
fix version string
eli-darkly Jul 16, 2018
6a08f47
Merge pull request #26 from launchdarkly/3.2.1
eli-darkly Jul 16, 2018
0d4fc33
add new version of allFlags() that captures more metadata
eli-darkly Aug 20, 2018
0f76969
linter
eli-darkly Aug 20, 2018
9023ea6
missing array key guard
eli-darkly Aug 21, 2018
e215364
missing array key guards
eli-darkly Aug 21, 2018
30e9e3c
use the standard method for specifying custom JSON serialization
eli-darkly Aug 21, 2018
775f0a1
indents
eli-darkly Aug 21, 2018
c1ac079
Merge pull request #27 from launchdarkly/eb/ch22308/all-flags-state
eli-darkly Aug 21, 2018
2f80b4c
add ability to filter for client-side flags only
eli-darkly Aug 21, 2018
fd08375
fix test to fill in all required flag fields
eli-darkly Aug 21, 2018
42c1ff2
Merge pull request #28 from launchdarkly/eb/ch12124/client-side-filter
eli-darkly Aug 22, 2018
2e9829c
implement evaluation with explanations
eli-darkly Aug 24, 2018
5d8e2b0
add another evaluation test
eli-darkly Aug 24, 2018
1977097
linter
eli-darkly Aug 24, 2018
54759db
fix test method
eli-darkly Aug 25, 2018
5038b0d
Merge branch 'master' of github.com:launchdarkly/php-client
eli-darkly Aug 25, 2018
87417f1
Merge branch 'master' of github.com:launchdarkly/php-client
eli-darkly Aug 27, 2018
07eb1a7
Merge branch 'explanation' into eb/ch19976/explanations
eli-darkly Aug 29, 2018
39d5105
fix for ch22995 - include prereq value in event even if prereq is off
eli-darkly Aug 29, 2018
8391618
Merge pull request #29 from launchdarkly/eb/ch19976/explanations
eli-darkly Sep 4, 2018
4fcf1ea
version 3.4.0
eli-darkly Sep 4, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

All notable changes to the LaunchDarkly PHP SDK will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org).

## [3.4.0] - 2018-09-04
### Added:
- The new `LDClient` method `variationDetail` allows you to evaluate a feature flag (using the same parameters as you would for `variation`) and receive more information about how the value was calculated. This information is returned in an object that contains both the result value and a "reason" object which will tell you, for instance, if the user was individually targeted for the flag or was matched by one of the flag's rules, or if the flag returned the default value due to an error.

### Fixed:
- When evaluating a prerequisite feature flag, the analytics event for the evaluation did not include the result value if the prerequisite flag was off.

## [3.3.0] - 2018-08-27
### Added:
- The new `LDClient` method `allFlagsState()` should be used instead of `allFlags()` if you are passing flag data to the front end for use with the JavaScript SDK. It preserves some flag metadata that the front end requires in order to send analytics events correctly. Versions 2.5.0 and above of the JavaScript SDK are able to use this metadata, but the output of `allFlagsState()` will still work with older versions.
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.3.0
3.4.0
25 changes: 8 additions & 17 deletions src/LaunchDarkly/EvalResult.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,28 @@

class EvalResult
{
private $_variation = null;
private $_value = null;
/** @var EvaluationDetail */
private $_detail = null;
/** @var array */
private $_prerequisiteEvents = [];

/**
* EvalResult constructor.
* @param null $value
* @param EvaluationDetail $detail
* @param array $prerequisiteEvents
*/
public function __construct($variation, $value, array $prerequisiteEvents)
public function __construct($detail, array $prerequisiteEvents)
{
$this->_variation = $variation;
$this->_value = $value;
$this->_detail = $detail;
$this->_prerequisiteEvents = $prerequisiteEvents;
}

/**
* @return int | null
* @return EvaluationDetail
*/
public function getVariation()
public function getDetail()
{
return $this->_variation;
}

/**
* @return null
*/
public function getValue()
{
return $this->_value;
return $this->_detail;
}

/**
Expand Down
68 changes: 68 additions & 0 deletions src/LaunchDarkly/EvaluationDetail.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

namespace LaunchDarkly;

/**
* An object returned by LDClient.variationDetail(), combining the result of a flag evaluation with
* an explanation of how it was calculated.
*/
class EvaluationDetail
{
private $_variationIndex = null;
private $_value = null;
private $_reason = null;

/**
* EvaluationDetail constructor.
* @param mixed $value the value of the flag variation
* @param int|null $variationIndex the index of the flag variation, or null if it was the default value
* @param EvaluationReason $reason evaluation reason properties
*/
public function __construct($value, $variationIndex, $reason = null)
{
$this->_value = $value;
$this->_variationIndex = $variationIndex;
$this->_reason = $reason;
}

/**
* Returns the value of the flag variation for the user.
*
* @return mixed
*/
public function getValue()
{
return $this->_value;
}

/**
* Returns the index of the flag variation for the user, e.g. 0 for the first variation -
* or null if it was the default value.
*
* @return int | null
*/
public function getVariationIndex()
{
return $this->_variationIndex;
}

/**
* Returns information about how the flag value was calculated.
*
* @return EvaluationReason
*/
public function getReason()
{
return $this->_reason;
}

/**
* Returns true if the flag evaluated to the default value, rather than one of its variations.
*
* @return bool
*/
public function isDefaultValue()
{
return ($this->_variationIndex === null);
}
}
222 changes: 222 additions & 0 deletions src/LaunchDarkly/EvaluationReason.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
<?php

namespace LaunchDarkly;

/**
* Describes the reason that a flag evaluation produced a particular value. This is part of
* the EvaluationDetail object returned by LDClient.variationDetail().
*/
class EvaluationReason implements \JsonSerializable
{
/**
* A possible value for getKind(): indicates that the flag was off and therefore returned
* its configured off value.
*/
const OFF = 'OFF';
/**
* A possible value for getKind(): indicates that the flag was on but the user did not
* match any targets or rules.
*/
const FALLTHROUGH = 'FALLTHROUGH';
/**
* A possible value for getKind(): indicates that the user key was specifically targeted
* for this flag.
*/
const TARGET_MATCH = 'TARGET_MATCH';
/**
* A possible value for getKind(): indicates that the user matched one of the flag's rules.
*/
const RULE_MATCH = 'RULE_MATCH';
/**
* A possible value for getKind(): indicates that the flag was considered off because it
* had at least one prerequisite flag that either was off or did not return the desired variation.
*/
const PREREQUISITE_FAILED = 'PREREQUISITE_FAILED';
/**
* A possible value for getKind(): indicates that the flag could not be evaluated, e.g.
* because it does not exist or due to an unexpected error.
*/
const ERROR = 'ERROR';

/**
* A possible value for getErrorKind(): indicates that the caller tried to evaluate a flag
* before the client had successfully initialized.
*/
const CLIENT_NOT_READY_ERROR = 'CLIENT_NOT_READY';

/**
* A possible value for getErrorKind(): indicates that the caller provided a flag key that
* did not match any known flag.
*/
const FLAG_NOT_FOUND_ERROR = 'FLAG_NOT_FOUND';

/**
* A possible value for getErrorKind(): indicates that there was an internal inconsistency
* in the flag data, e.g. a rule specified a nonexistent variation. An error message will
* always be logged in this case.
*/
const MALFORMED_FLAG_ERROR = 'MALFORMED_FLAG';

/**
* A possible value for getErrorKind(): indicates that the caller passed null for the user
* parameter, or the user lacked a key.
*/
const USER_NOT_SPECIFIED_ERROR = 'USER_NOT_SPECIFIED';

/**
* A possible value for getErrorKind(): indicates that an unexpected exception stopped flag
* evaluation.
*/
const EXCEPTION_ERROR = 'EXCEPTION';

private $_kind;
private $_errorKind;
private $_ruleIndex;
private $_ruleId;
private $_prerequisiteKey;

/**
* Creates a new instance of the OFF reason.
*/
public static function off()
{
return new EvaluationReason(self::OFF);
}

/**
* Creates a new instance of the FALLTHROUGH reason.
*/
public static function fallthrough()
{
return new EvaluationReason(self::FALLTHROUGH);
}

/**
* Creates a new instance of the TARGET_MATCH reason.
*/
public static function targetMatch()
{
return new EvaluationReason(self::TARGET_MATCH);
}

/**
* Creates a new instance of the RULE_MATCH reason.
*/
public static function ruleMatch($ruleIndex, $ruleId)
{
return new EvaluationReason(self::RULE_MATCH, null, $ruleIndex, $ruleId);
}

/**
* Creates a new instance of the PREREQUISITE_FAILED reason.
*/
public static function prerequisiteFailed($prerequisiteKey)
{
return new EvaluationReason(self::PREREQUISITE_FAILED, null, null, null, $prerequisiteKey);
}

/**
* Creates a new instance of the ERROR reason.
*/
public static function error($errorKind)
{
return new EvaluationReason(self::ERROR, $errorKind);
}

private function __construct($kind, $errorKind = null, $ruleIndex = null, $ruleId = null, $prerequisiteKey = null)
{
$this->_kind = $kind;
$this->_errorKind = $errorKind;
$this->_ruleIndex = $ruleIndex;
$this->_ruleId = $ruleId;
$this->_prerequisiteKey = $prerequisiteKey;
}

/**
* Returns a constant indicating the general category of the reason, such as OFF.
* @return string
*/
public function getKind()
{
return $this->_kind;
}

/**
* Returns a constant indicating the nature of the error, if getKind() is OFF. Otherwise
* returns null.
* @return string|null
*/
public function getErrorKind()
{
return $this->_errorKind;
}

/**
* Returns the positional index of the rule that was matched (0 for the first), if getKind()
* is RULE_MATCH. Otherwise returns null.
* @return int|null
*/
public function getRuleIndex()
{
return $this->_ruleIndex;
}

/**
* Returns the unique identifier of the rule that was matched, if getKind() is RULE_MATCH.
* Otherwise returns null.
* @return string|null
*/
public function getRuleId()
{
return $this->_ruleId;
}

/**
* Returns the key of the prerequisite feature flag that failed, if getKind() is
* PREREQUISITE_FAILED. Otherwise returns null.
* @return string|null
*/
public function getPrerequisiteKey()
{
return $this->_prerequisiteKey;
}

/**
* Returns a simple string representation of this object.
*/
public function __toString()
{
switch ($this->_kind) {
case self::RULE_MATCH:
return $this->_kind . '(' . $this->_ruleIndex . ',' . $this->_ruleId . ')';
case self::PREREQUISITE_FAILED:
return $this->_kind . '(' . $this->_prerequisiteKey . ')';
case self::ERROR:
return $this->_kind . '(' . $this->_errorKind . ')';
default:
return $this->_kind;
}
}

/**
* Returns a JSON representation of this object. This method is used automatically
* if you call json_encode().
*/
public function jsonSerialize()
{
$ret = array('kind' => $this->_kind);
if ($this->_errorKind !== null) {
$ret['errorKind'] = $this->_errorKind;
}
if ($this->_ruleIndex !== null) {
$ret['ruleIndex'] = $this->_ruleIndex;
}
if ($this->_ruleId !== null) {
$ret['ruleId'] = $this->_ruleId;
}
if ($this->_prerequisiteKey !== null) {
$ret['prerequisiteKey'] = $this->_prerequisiteKey;
}
return $ret;
}
}
Loading