Skip to content

Commit 985ffb2

Browse files
authored
prepare 3.3.0 release (#108)
1 parent c5f8490 commit 985ffb2

File tree

9 files changed

+448
-29
lines changed

9 files changed

+448
-29
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
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.3.0] - 2018-08-27
6+
### Added:
7+
- 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.
8+
- The `allFlagsState()` method also allows you to select only client-side-enabled flags to pass to the front end, by using the option `clientSideOnly => true`.
9+
10+
### Deprecated:
11+
- `LDClient.allFlags()`
12+
513
## [3.2.1] - 2018-07-16
614
### Fixed:
715
- The `LDClient::VERSION` constant has been fixed to report the current version. In the previous release, it was still set to 3.1.0.

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
3.2.1
1+
3.3.0

src/LaunchDarkly/EvalResult.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public function __construct($variation, $value, array $prerequisiteEvents)
2222
}
2323

2424
/**
25-
* @return int
25+
* @return int | null
2626
*/
2727
public function getVariation()
2828
{

src/LaunchDarkly/FeatureFlag.php

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,16 @@ class FeatureFlag
2727
protected $_variations = array();
2828
/** @var bool */
2929
protected $_deleted = false;
30+
/** @var bool */
31+
protected $_trackEvents = false;
32+
/** @var int | null */
33+
protected $_debugEventsUntilDate = null;
34+
/** @var bool */
35+
protected $_clientSide = false;
36+
37+
// Note, trackEvents and debugEventsUntilDate are not used in EventProcessor, because
38+
// the PHP client doesn't do summary events. However, we need to capture them in case
39+
// they want to pass the flag data to the front end with allFlagsState().
3040

3141
protected function __construct($key,
3242
$version,
@@ -38,7 +48,10 @@ protected function __construct($key,
3848
$fallthrough,
3949
$offVariation,
4050
array $variations,
41-
$deleted)
51+
$deleted,
52+
$trackEvents,
53+
$debugEventsUntilDate,
54+
$clientSide)
4255
{
4356
$this->_key = $key;
4457
$this->_version = $version;
@@ -51,6 +64,9 @@ protected function __construct($key,
5164
$this->_offVariation = $offVariation;
5265
$this->_variations = $variations;
5366
$this->_deleted = $deleted;
67+
$this->_trackEvents = $trackEvents;
68+
$this->_debugEventsUntilDate = $debugEventsUntilDate;
69+
$this->_clientSide = $clientSide;
5470
}
5571

5672
public static function getDecoder()
@@ -67,7 +83,11 @@ public static function getDecoder()
6783
call_user_func(VariationOrRollout::getDecoder(), $v['fallthrough']),
6884
$v['offVariation'],
6985
$v['variations'] ?: [],
70-
$v['deleted']);
86+
$v['deleted'],
87+
isset($v['trackEvents']) && $v['trackEvents'],
88+
isset($v['debugEventsUntilDate']) ? $v['debugEventsUntilDate'] : null,
89+
isset($v['clientSide']) && $v['clientSide']
90+
);
7191
};
7292
}
7393

@@ -222,4 +242,28 @@ public function isDeleted()
222242
{
223243
return $this->_deleted;
224244
}
245+
246+
/**
247+
* @return boolean
248+
*/
249+
public function isTrackEvents()
250+
{
251+
return $this->_trackEvents;
252+
}
253+
254+
/**
255+
* @return int | null
256+
*/
257+
public function getDebugEventsUntilDate()
258+
{
259+
return $this->_debugEventsUntilDate;
260+
}
261+
262+
/**
263+
* @return boolean
264+
*/
265+
public function isClientSide()
266+
{
267+
return $this->_clientSide;
268+
}
225269
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<?php
2+
namespace LaunchDarkly;
3+
4+
/**
5+
* A snapshot of the state of all feature flags with regard to a specific user, generated by
6+
* calling LDClient.allFlagsState(). Serializing this object to JSON using json_encode(), or
7+
* the jsonSerialize() method, will produce the appropriate data structure for bootstrapping
8+
* the LaunchDarkly JavaScript client.
9+
*/
10+
class FeatureFlagsState implements \JsonSerializable
11+
{
12+
/** @var bool */
13+
protected $_valid = false;
14+
15+
/** @var array */
16+
protected $_flagValues;
17+
18+
/** @var array */
19+
protected $_flagMetadata;
20+
21+
public function __construct($valid, $flagValues = array(), $flagMetadata = array())
22+
{
23+
$this->_valid = $valid;
24+
$this->_flagValues = array();
25+
$this->_flagMetadata = array();
26+
}
27+
28+
/**
29+
* Used internally to build the state map.
30+
*/
31+
public function addFlag($flag, $evalResult)
32+
{
33+
$this->_flagValues[$flag->getKey()] = $evalResult->getValue();
34+
$meta = array();
35+
if (!is_null($evalResult->getVariation())) {
36+
$meta['variation'] = $evalResult->getVariation();
37+
}
38+
$meta['version'] = $flag->getVersion();
39+
$meta['trackEvents'] = $flag->isTrackEvents();
40+
if ($flag->getDebugEventsUntilDate()) {
41+
$meta['debugEventsUntilDate'] = $flag->getDebugEventsUntilDate();
42+
}
43+
$this->_flagMetadata[$flag->getKey()] = $meta;
44+
}
45+
46+
/**
47+
* Returns true if this object contains a valid snapshot of feature flag state, or false if the
48+
* state could not be computed (for instance, because the client was offline or there was no user).
49+
* @return bool true if the state is valid
50+
*/
51+
public function isValid()
52+
{
53+
return $this->_valid;
54+
}
55+
56+
/**
57+
* Returns the value of an individual feature flag at the time the state was recorded.
58+
* @param $key string
59+
* @return mixed the flag's value; null if the flag returned the default value, or if there was no such flag
60+
*/
61+
public function getFlagValue($key)
62+
{
63+
return isset($this->_flagValues[$key]) ? $this->_flagValues[$key] : null;
64+
}
65+
66+
/**
67+
* Returns an associative array of flag keys to flag values. If a flag would have evaluated to the default
68+
* value, its value will be null.
69+
* <p>
70+
* Do not use this method if you are passing data to the front end to "bootstrap" the JavaScript client.
71+
* Instead, use jsonSerialize().
72+
* @return array an associative array of flag keys to JSON values
73+
*/
74+
public function toValuesMap()
75+
{
76+
return $this->_flagValues;
77+
}
78+
79+
/**
80+
* Returns a JSON representation of the entire state map (as an associative array), in the format used
81+
* by the LaunchDarkly JavaScript SDK. Use this method if you are passing data to the front end in
82+
* order to "bootstrap" the JavaScript client.
83+
* <p>
84+
* Note that calling json_encode() on a FeatureFlagsState object will automatically use the
85+
* jsonSerialize() method.
86+
* @return array an associative array suitable for passing as a JSON object
87+
*/
88+
public function jsonSerialize()
89+
{
90+
$ret = array_replace([], $this->_flagValues);
91+
$ret['$flagsState'] = $this->_flagMetadata;
92+
$ret['$valid'] = $this->_valid;
93+
return $ret;
94+
}
95+
}

src/LaunchDarkly/LDClient.php

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class LDClient
1212
{
1313
const DEFAULT_BASE_URI = 'https://app.launchdarkly.com';
1414
const DEFAULT_EVENTS_URI = 'https://events.launchdarkly.com';
15-
const VERSION = '3.2.1';
15+
const VERSION = '3.3.0';
1616

1717
/** @var string */
1818
protected $_sdkKey;
@@ -260,38 +260,62 @@ public function identify($user)
260260
* This method will not send analytics events back to LaunchDarkly.
261261
* <p>
262262
* The most common use case for this method is to bootstrap a set of client-side feature flags from a back-end service.
263-
*
263+
* @deprecated Use allFlagsState() instead. Current versions of the client-side SDK will not
264+
* generate analytics events correctly if you pass the result of allFlags().
264265
* @param $user LDUser the end user requesting the feature flags
265266
* @return array()|null Mapping of feature flag keys to their evaluated results for $user
266267
*/
267268
public function allFlags($user)
268269
{
269-
if (is_null($user) || is_null($user->getKey())) {
270-
$this->_logger->warn("allFlags called with null user or null/empty user key! Returning null");
270+
$state = $this->allFlagsState($user);
271+
if (!$state->isValid()) {
271272
return null;
272273
}
274+
return $state->toValuesMap();
275+
}
276+
277+
/**
278+
* Returns an object that encapsulates the state of all feature flags for a given user, including the flag
279+
* values and also metadata that can be used on the front end. This method does not send analytics events
280+
* back to LaunchDarkly.
281+
* <p>
282+
* The most common use case for this method is to bootstrap a set of client-side feature flags from a back-end service.
283+
* To convert the state object into a JSON data structure, call its toJson() method.
284+
* @param $user LDUser the end user requesting the feature flags
285+
* @param $options array optional properties affecting how the state is computed; set
286+
* <code>'clientSideOnly' => true</code> to specify that only flags marked for client-side use
287+
* should be included (by default, all flags are included)
288+
* @return FeatureFlagsState a FeatureFlagsState object (will never be null; see FeatureFlagsState.isValid())
289+
*/
290+
public function allFlagsState($user, $options = array())
291+
{
292+
if (is_null($user) || is_null($user->getKey())) {
293+
$this->_logger->warn("allFlagsState called with null user or null/empty user key! Returning empty state");
294+
return new FeatureFlagsState(false);
295+
}
273296
if ($this->isOffline()) {
274-
return null;
297+
return new FeatureFlagsState(false);
275298
}
276299
try {
277300
$flags = $this->_featureRequester->getAllFeatures();
278301
} catch (UnrecoverableHTTPStatusException $e) {
279302
$this->handleUnrecoverableError();
280-
return null;
303+
return new FeatureFlagsState(false);
281304
}
282305
if ($flags === null) {
283-
return null;
306+
return new FeatureFlagsState(false);
284307
}
285308

286-
/**
287-
* @param $flag FeatureFlag
288-
* @return mixed|null
289-
*/
290-
$eval = function ($flag) use ($user) {
291-
return $flag->evaluate($user, $this->_featureRequester)->getValue();
292-
};
293-
294-
return array_map($eval, $flags);
309+
$state = new FeatureFlagsState(true);
310+
$clientOnly = isset($options['clientSideOnly']) && $options['clientSideOnly'];
311+
foreach ($flags as $key => $flag) {
312+
if ($clientOnly && !$flag->isClientSide()) {
313+
continue;
314+
}
315+
$result = $flag->evaluate($user, $this->_featureRequester);
316+
$state->addFlag($flag, $result);
317+
}
318+
return $state;
295319
}
296320

297321
/** Generates an HMAC sha256 hash for use in Secure mode: https://github.com/launchdarkly/js-client#secure-mode

0 commit comments

Comments
 (0)