Skip to content

Commit 30f7590

Browse files
authored
prepare 3.0.0 release (#94)
1 parent a86eb6c commit 30f7590

16 files changed

+579
-41
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

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

5+
## [3.0.0] - 2018-02-21
6+
### Added
7+
- Support for a new LaunchDarkly feature: reusable user segments.
8+
59
## [2.5.0] - 2018-02-13
610
### Added
711
- Adds support for a future LaunchDarkly feature, coming soon: semantic version user attributes.

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2.5.0
1+
3.0.0

src/LaunchDarkly/ApcLDDFeatureRequester.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ protected function fetch($key, &$success = null)
3232
return \apc_fetch($key, $success);
3333
}
3434

35-
protected function get_from_cache($key)
35+
protected function get_from_cache($namespace, $key)
3636
{
37-
$key = self::make_cache_key($key);
37+
$key = self::make_cache_key($namespace, $key);
3838
$enabled = $this->fetch($key);
3939
if ($enabled === false) {
4040
return null;
@@ -54,13 +54,13 @@ protected function add($key, $var, $ttl = 0)
5454
return \apc_add($key, $var, $ttl);
5555
}
5656

57-
protected function store_in_cache($key, $val)
57+
protected function store_in_cache($namespace, $key, $val)
5858
{
59-
$this->add($this->make_cache_key($key), $val, $this->_expiration);
59+
$this->add($this->make_cache_key($namespace, $key), $val, $this->_expiration);
6060
}
6161

62-
private function make_cache_key($name)
62+
private function make_cache_key($namespace, $name)
6363
{
64-
return $this->_features_key.'.'.$name;
64+
return $namespace.'.'.$name;
6565
}
6666
}

src/LaunchDarkly/Clause.php

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,28 @@ public static function getDecoder()
3232
* @param $user LDUser
3333
* @return bool
3434
*/
35-
public function matchesUser($user)
35+
public function matchesUser($user, $featureRequester)
36+
{
37+
if ($this->_op === 'segmentMatch') {
38+
foreach ($this->_values as $value) {
39+
$segment = $featureRequester->getSegment($value);
40+
if ($segment) {
41+
if ($segment->matchesUser($user)) {
42+
return $this->_maybeNegate(true);
43+
}
44+
}
45+
}
46+
return $this->_maybeNegate(false);
47+
} else {
48+
return $this->matchesUserNoSegments($user);
49+
}
50+
}
51+
52+
/**
53+
* @param $user LDUser
54+
* @return bool
55+
*/
56+
public function matchesUserNoSegments($user)
3657
{
3758
$userValue = $user->getValueForEvaluation($this->_attribute);
3859
if ($userValue === null) {

src/LaunchDarkly/FeatureFlag.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ private function _evaluate($user, $featureRequester, &$events)
115115
foreach ($this->_prerequisites as $prereq) {
116116
try {
117117
$prereqEvalResult = null;
118-
$prereqFeatureFlag = $featureRequester->get($prereq->getKey());
118+
$prereqFeatureFlag = $featureRequester->getFeature($prereq->getKey());
119119
if ($prereqFeatureFlag == null) {
120120
return null;
121121
} elseif ($prereqFeatureFlag->isOn()) {
@@ -134,7 +134,7 @@ private function _evaluate($user, $featureRequester, &$events)
134134
}
135135
}
136136
if ($prereqOk) {
137-
return $this->getVariation($this->evaluateIndex($user));
137+
return $this->getVariation($this->evaluateIndex($user, $featureRequester));
138138
}
139139
return null;
140140
}
@@ -143,7 +143,7 @@ private function _evaluate($user, $featureRequester, &$events)
143143
* @param $user LDUser
144144
* @return int|null
145145
*/
146-
private function evaluateIndex($user)
146+
private function evaluateIndex($user, $featureRequester)
147147
{
148148
// Check to see if targets match
149149
if ($this->_targets != null) {
@@ -158,7 +158,7 @@ private function evaluateIndex($user)
158158
// Now walk through the rules and see if any match
159159
if ($this->_rules != null) {
160160
foreach ($this->_rules as $rule) {
161-
if ($rule->matchesUser($user)) {
161+
if ($rule->matchesUser($user, $featureRequester)) {
162162
return $rule->variationIndexForUser($user, $this->_key, $this->_salt);
163163
}
164164
}

src/LaunchDarkly/FeatureRequester.php

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,20 @@ interface FeatureRequester
1010
* @param $key string feature key
1111
* @return FeatureFlag|null The decoded FeatureFlag, or null if missing
1212
*/
13-
public function get($key);
13+
public function getFeature($key);
14+
15+
/**
16+
* Gets segment data from a likely cached store
17+
*
18+
* @param $key string segment key
19+
* @return Segment|null The decoded Segment, or null if missing
20+
*/
21+
public function getSegment($key);
1422

1523
/**
1624
* Gets all features.
1725
*
1826
* @return array()|null The decoded FeatureFlags, or null if missing
1927
*/
20-
public function getAll();
28+
public function getAllFeatures();
2129
}

src/LaunchDarkly/GuzzleFeatureRequester.php

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
class GuzzleFeatureRequester implements FeatureRequester
1212
{
1313
const SDK_FLAGS = "/sdk/flags";
14+
const SDK_SEGMENTS = "/sdk/segments";
1415
/** @var Client */
1516
private $_client;
1617
/** @var string */
@@ -46,14 +47,13 @@ public function __construct($baseUri, $sdkKey, $options)
4647
$this->_client = new Client(['handler' => $stack, 'debug' => false]);
4748
}
4849

49-
5050
/**
5151
* Gets feature data from a likely cached store
5252
*
5353
* @param $key string feature key
5454
* @return FeatureFlag|null The decoded FeatureFlag, or null if missing
5555
*/
56-
public function get($key)
56+
public function getFeature($key)
5757
{
5858
try {
5959
$uri = $this->_baseUri . self::SDK_FLAGS . "/" . $key;
@@ -71,12 +71,36 @@ public function get($key)
7171
}
7272
}
7373

74+
/**
75+
* Gets segment data from a likely cached store
76+
*
77+
* @param $key string segment key
78+
* @return Segment|null The decoded Segment, or null if missing
79+
*/
80+
public function getSegment($key)
81+
{
82+
try {
83+
$uri = $this->_baseUri . self::SDK_SEGMENTS . "/" . $key;
84+
$response = $this->_client->get($uri, $this->_defaults);
85+
$body = $response->getBody();
86+
return Segment::decode(json_decode($body, true));
87+
} catch (BadResponseException $e) {
88+
$code = $e->getResponse()->getStatusCode();
89+
if ($code == 404) {
90+
$this->_logger->warning("GuzzleFeatureRequester::get returned 404. Segment does not exist for key: " . $key);
91+
} else {
92+
$this->handleUnexpectedStatus($code, "GuzzleFeatureRequester::get");
93+
}
94+
return null;
95+
}
96+
}
97+
7498
/**
7599
* Gets all features from a likely cached store
76100
*
77101
* @return array()|null The decoded FeatureFlags, or null if missing
78102
*/
79-
public function getAll()
103+
public function getAllFeatures()
80104
{
81105
try {
82106
$uri = $this->_baseUri . self::SDK_FLAGS;

src/LaunchDarkly/LDClient.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class LDClient
1919
{
2020
const DEFAULT_BASE_URI = 'https://app.launchdarkly.com';
2121
const DEFAULT_EVENTS_URI = 'https://events.launchdarkly.com';
22-
const VERSION = '2.5.0';
22+
const VERSION = '3.0.0';
2323

2424
/** @var string */
2525
protected $_sdkKey;
@@ -157,7 +157,7 @@ public function variation($key, $user, $default = false)
157157
$this->_logger->warning("User key is blank. Flag evaluation will proceed, but the user will not be stored in LaunchDarkly.");
158158
}
159159
try {
160-
$flag = $this->_featureRequester->get($key);
160+
$flag = $this->_featureRequester->getFeature($key);
161161
} catch (InvalidSDKKeyException $e) {
162162
$this->handleInvalidSDKKey();
163163
return $default;
@@ -278,7 +278,7 @@ public function allFlags($user)
278278
return null;
279279
}
280280
try {
281-
$flags = $this->_featureRequester->getAll();
281+
$flags = $this->_featureRequester->getAllFeatures();
282282
} catch (InvalidSDKKeyException $e) {
283283
$this->handleInvalidSDKKey();
284284
return null;

src/LaunchDarkly/LDDFeatureRequester.php

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class LDDFeatureRequester implements FeatureRequester
1010
protected $_sdkKey;
1111
protected $_options;
1212
protected $_features_key;
13+
protected $_segments_key;
1314
/** @var LoggerInterface */
1415
private $_logger;
1516
/** @var ClientInterface */
@@ -33,6 +34,7 @@ public function __construct($baseUri, $sdkKey, $options)
3334
$prefix = $options['redis_prefix'];
3435
}
3536
$this->_features_key = "$prefix:features";
37+
$this->_segments_key = "$prefix:segments";
3638
$this->_logger = $options['logger'];
3739

3840
if (isset($this->_options['predis_client']) && $this->_options['predis_client'] instanceof ClientInterface) {
@@ -56,21 +58,20 @@ protected function get_connection()
5658
"port" => $this->_options['redis_port']));
5759
}
5860

59-
6061
/**
6162
* Gets feature data from a likely cached store
6263
*
6364
* @param $key string feature key
6465
* @return FeatureFlag|null The decoded JSON feature data, or null if missing
6566
*/
66-
public function get($key)
67+
public function getFeature($key)
6768
{
68-
$raw = $this->get_from_cache($key);
69+
$raw = $this->get_from_cache($this->_features_key, $key);
6970
if ($raw === null) {
7071
$redis = $this->get_connection();
7172
$raw = $redis->hget($this->_features_key, $key);
7273
if ($raw) {
73-
$this->store_in_cache($key, $raw);
74+
$this->store_in_cache($this->_features_key, $key, $raw);
7475
}
7576
}
7677
if ($raw) {
@@ -86,22 +87,53 @@ public function get($key)
8687
}
8788
}
8889

90+
/**
91+
* Gets segment data from a likely cached store
92+
*
93+
* @param $key string segment key
94+
* @return Segment|null The decoded JSON segment data, or null if missing
95+
*/
96+
public function getSegment($key)
97+
{
98+
$raw = $this->get_from_cache($this->_segments_key, $key);
99+
if ($raw === null) {
100+
$redis = $this->get_connection();
101+
$raw = $redis->hget($this->_features_key, $key);
102+
if ($raw) {
103+
$this->store_in_cache($this->_segments_key, $key, $raw);
104+
}
105+
}
106+
if ($raw) {
107+
$segment = Segment::decode(json_decode($raw, true));
108+
if ($segment->isDeleted()) {
109+
$this->_logger->warning("LDDFeatureRequester: Attempted to get deleted segment with key: " . $key);
110+
return null;
111+
}
112+
return $segment;
113+
} else {
114+
$this->_logger->warning("LDDFeatureRequester: Attempted to get missing segment with key: " . $key);
115+
return null;
116+
}
117+
}
118+
89119
/**
90120
* Gets the value from local cache. No-op by default.
91-
* @param $key string The feature key
92-
* @return null|array The feature data or null if missing
121+
* @param $namespace string that denotes features or segments
122+
* @param $key string The feature or segment key
123+
* @return null|array The feature or segment data or null if missing
93124
*/
94-
protected function get_from_cache($key)
125+
protected function get_from_cache($namespace, $key)
95126
{
96127
return null;
97128
}
98129

99130
/**
100-
* Stores the feature data into the local cache. No-op by default.
101-
* @param $key string The feature key
102-
* @param $val array The feature data
131+
* Stores the feature or segment data into the local cache. No-op by default.
132+
* @param $namespace string that denotes features or segments
133+
* @param $key string The feature or segment key
134+
* @param $val array The feature or segment data
103135
*/
104-
protected function store_in_cache($key, $val)
136+
protected function store_in_cache($namespace, $key, $val)
105137
{
106138
}
107139

@@ -110,7 +142,7 @@ protected function store_in_cache($key, $val)
110142
*
111143
* @return array()|null The decoded FeatureFlags, or null if missing
112144
*/
113-
public function getAll()
145+
public function getAllFeatures()
114146
{
115147
$redis = $this->get_connection();
116148
$raw = $redis->hgetall($this->_features_key);

src/LaunchDarkly/Rule.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ public static function getDecoder()
2727
* @param $user LDUser
2828
* @return bool
2929
*/
30-
public function matchesUser($user)
30+
public function matchesUser($user, $featureRequester)
3131
{
3232
foreach ($this->_clauses as $clause) {
33-
if (!$clause->matchesUser($user)) {
33+
if (!$clause->matchesUser($user, $featureRequester)) {
3434
return false;
3535
}
3636
}

0 commit comments

Comments
 (0)