diff --git a/.circleci/config.yml b/.circleci/config.yml
index a9737ccdb..8fc9d310e 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -7,19 +7,27 @@ workflows:
name: PHP 5.5
- test-with-preinstalled-php:
name: PHP 5.6
- docker-image: circleci/php:5.6.34-cli-jessie
+ docker-image: circleci/php:5.6
use-phpredis: false
- test-with-preinstalled-php:
name: PHP 7.0
- docker-image: circleci/php:7.0.28-cli-jessie
+ docker-image: circleci/php:7.0
- test-with-preinstalled-php:
name: PHP 7.1
- docker-image: circleci/php:7.1.15-cli-jessie
+ docker-image: circleci/php:7.1
- test-with-preinstalled-php:
name: PHP 7.2
- docker-image: circleci/php:7.2.3-cli-stretch
+ docker-image: circleci/php:7.2
include-lowest-compatible-versions-test: false
# lowest "compatible" version of phpunit in 7.2 has a bug - https://github.com/sebastianbergmann/comparator/pull/30
+ - test-with-preinstalled-php:
+ name: PHP 7.3
+ docker-image: circleci/php:7.3
+ include-lowest-compatible-versions-test: false
+ - test-with-preinstalled-php:
+ name: PHP 7.4
+ docker-image: circleci/php:7.4
+ include-lowest-compatible-versions-test: false
- integration-test
jobs:
diff --git a/.gitignore b/.gitignore
index 9d330805f..d0d7fdb94 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@ composer.phar
.vagrant
integration-tests/vendor
composer.lock
+docs/build
diff --git a/.ldrelease/build-docs.sh b/.ldrelease/build-docs.sh
new file mode 100755
index 000000000..d39cf4ea4
--- /dev/null
+++ b/.ldrelease/build-docs.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+set -e
+
+cd docs
+make PHPDOC_ARCHIVE=/home/circleci/ldtools/phpDocumentor.phar # provided in ldcircleci/ld-php-sdk-release image
diff --git a/.ldrelease/config.yml b/.ldrelease/config.yml
index 7b4b52e5d..6d102c880 100644
--- a/.ldrelease/config.yml
+++ b/.ldrelease/config.yml
@@ -6,5 +6,12 @@ publications:
- url: https://packagist.org/packages/launchdarkly/server-sdk
description: Packagist
+circleCI:
+ linux:
+ image: ldcircleci/ld-php-sdk-release:1 # built in sdks-ci-docker project, contains PHP 7.2 + phpDocumentor
+
+documentation:
+ githubPages: true
+
sdk:
displayName: "PHP"
diff --git a/.ldrelease/publish-docs.sh b/.ldrelease/publish-docs.sh
new file mode 100755
index 000000000..29fe6a382
--- /dev/null
+++ b/.ldrelease/publish-docs.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+set -e
+
+# Releaser will publish the docs to GitHub pages for us if we put a "docs.zip" artifact in ./artifacts
+
+mkdir -p artifacts
+pushd docs/build/html
+rm -f docs.zip
+zip -r docs.zip *
+popd
+mv docs/build/html/docs.zip artifacts
diff --git a/.ldrelease/update-version.sh b/.ldrelease/update-version.sh
index 3fbea18d4..b017b80df 100755
--- a/.ldrelease/update-version.sh
+++ b/.ldrelease/update-version.sh
@@ -1,5 +1,7 @@
#!/bin/bash
+set -e
+
# Update version in LDClient class
LDCLIENT_PHP=src/LaunchDarkly/LDClient.php
LDCLIENT_PHP_TEMP=${LDCLIENT_PHP}.tmp
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b9d39c907..4c57d02d0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,7 +9,7 @@ All notable changes to the LaunchDarkly PHP SDK will be documented in this file.
## [3.7.0] - 2019-12-13
### Added:
-- Added integration with the [`phpredis`](https://github.com/phpredis/phpredis) extension, which supports persistent Redis connections unlike the existing `predis` integration. See `LaunchDarkly::Integrations::PHPRedis`. (Thanks, [nicofff](https://github.com/launchdarkly/php-server-sdk/pull/128)!)
+- Added integration with the [`phpredis`](https://github.com/phpredis/phpredis) extension, which has similar functionality to the already-supported `predis` but may have better performance (since `predis` is written in pure PHP, whereas `phpredis` uses a C extension). See `LaunchDarkly::Integrations::PHPRedis`. (Thanks, [nicofff](https://github.com/launchdarkly/php-server-sdk/pull/128)!)
## [3.6.0] - 2019-10-01
### Added:
diff --git a/composer.json b/composer.json
index 560e0407e..2a90f4613 100644
--- a/composer.json
+++ b/composer.json
@@ -20,10 +20,9 @@
},
"require-dev": {
"aws/aws-sdk-php": "^3.86",
- "friendsofphp/php-cs-fixer": "~2.2.19",
+ "friendsofphp/php-cs-fixer": ">=2.2.19 <3.0",
"guzzlehttp/guzzle": "^6.2.1",
"kevinrob/guzzle-cache-middleware": "^1.4.1",
- "phpdocumentor/phpdocumentor": "^2.0",
"phpunit/phpunit": ">=4.8.26 <5.4",
"predis/predis": "^1.0",
"sensiolabs/consul-php-sdk": ">=2.1 <3.0",
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 000000000..4423ca925
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,17 @@
+
+# PHPDOC_ARCHIVE is overridden in the CircleCI release
+PHPDOC_ARCHIVE := $(shell pwd)/build/phpDocumentor.phar
+OUTPUT_DIR := $(shell pwd)/build
+PHPDOC_URL=https://www.phpdoc.org/phpDocumentor.phar
+
+.PHONY: install html
+
+html: install
+ rm -rf build/temp build/html
+ cd .. && php $(PHPDOC_ARCHIVE) --visibility public --title "LaunchDarkly PHP SDK ${LD_RELEASE_VERSION}"
+
+install: $(PHPDOC_ARCHIVE)
+
+$(PHPDOC_ARCHIVE):
+ mkdir -p build
+ cd build && unzip ../lib/phpDocumentor.phar.zip
diff --git a/phpdoc.dist.xml b/phpdoc.dist.xml
new file mode 100644
index 000000000..9f467505a
--- /dev/null
+++ b/phpdoc.dist.xml
@@ -0,0 +1,40 @@
+
+
- * The most common use case for this method is to bootstrap a set of client-side feature flags from a back-end service. + * + * The most common use case for this method is to bootstrap a set of client-side feature flags from a back-end + * service. Current versions of the JavaScript SDK require somewhat different data; for best compatibility, + * use {@link allFlagsState()} instead. + * * @deprecated Use allFlagsState() instead. Current versions of the client-side SDK will not * generate analytics events correctly if you pass the result of allFlags(). - * @param $user LDUser the end user requesting the feature flags - * @return array()|null Mapping of feature flag keys to their evaluated results for $user + * @param LDUser $user The end user requesting the feature flags + * @return array|null Mapping of feature flag keys to their evaluated results for $user */ public function allFlags($user) { @@ -325,18 +357,20 @@ public function allFlags($user) } /** - * Returns an object that encapsulates the state of all feature flags for a given user, including the flag - * values and also metadata that can be used on the front end. This method does not send analytics events - * back to LaunchDarkly. - *
- * The most common use case for this method is to bootstrap a set of client-side feature flags from a back-end service.
- * To convert the state object into a JSON data structure, call its toJson() method.
- * @param $user LDUser the end user requesting the feature flags
- * @param $options array optional properties affecting how the state is computed; set
- * 'clientSideOnly' => true to specify that only flags marked for client-side use
- * should be included (by default, all flags are included); set 'withReasons' => true
- * to include evaluation reasons (see variationDetail)
- * @return FeatureFlagsState a FeatureFlagsState object (will never be null; see FeatureFlagsState.isValid())
+ * Returns an object that encapsulates the state of all feature flags for a given user.
+ *
+ * This includes the flag values as well as other flag metadata that may be needed by front-end code,
+ * since the most common use case for this method is [bootstrapping](https://docs.launchdarkly.com/docs/js-sdk-reference#section-bootstrapping)
+ * in conjunction with the JavaScript browser SDK.
+ *
+ * This method does not send analytics events back to LaunchDarkly.
+ *
+ * @param LDUser $user The end user requesting the feature flags
+ * @param array $options Optional properties affecting how the state is computed:
+ * - `clientSideOnly`: Set this to true to specify that only flags marked for client-side use
+ * should be included; by default, all flags are included
+ * - `withReasons`: Set this to true to include evaluation reasons (see {@link variationDetail()})
+ * @return FeatureFlagsState a FeatureFlagsState object (will never be null)
*/
public function allFlagsState($user, $options = array())
{
@@ -374,8 +408,12 @@ public function allFlagsState($user, $options = array())
return $state;
}
- /** Generates an HMAC sha256 hash for use in Secure mode: https://github.com/launchdarkly/js-client#secure-mode
- * @param $user LDUser
+ /**
+ * Generates an HMAC sha256 hash for use in Secure mode.
+ *
+ * See: https://docs.launchdarkly.com/docs/js-sdk-reference#section-secure-mode
+ *
+ * @param LDUser $user
* @return string
*/
public function secureModeHash($user)
@@ -387,7 +425,9 @@ public function secureModeHash($user)
}
/**
- * Publish events to LaunchDarkly
+ * Publishes any pending analytics events to LaunchDarkly.
+ *
+ * This is normally done automatically by the SDK.
* @return bool Whether the events were successfully published
*/
public function flush()
diff --git a/src/LaunchDarkly/LDDFeatureRequester.php b/src/LaunchDarkly/LDDFeatureRequester.php
index 9e2727db6..b48f8a74a 100644
--- a/src/LaunchDarkly/LDDFeatureRequester.php
+++ b/src/LaunchDarkly/LDDFeatureRequester.php
@@ -4,7 +4,8 @@
use LaunchDarkly\Impl\Integrations\RedisFeatureRequester;
/**
- * Deprecated implementation class for Redis integration.
+ * Deprecated integration class that reads feature flags from Redis.
+ *
* Replaced by {@link \LaunchDarkly\Integrations\Redis::featureRequester()}.
*
* @deprecated Use {@link \LaunchDarkly\Integrations\Redis::featureRequester()}
diff --git a/src/LaunchDarkly/LDUser.php b/src/LaunchDarkly/LDUser.php
index 9fc67f790..46e2a47df 100644
--- a/src/LaunchDarkly/LDUser.php
+++ b/src/LaunchDarkly/LDUser.php
@@ -2,9 +2,12 @@
namespace LaunchDarkly;
/**
- * Contains specific attributes of a user browsing your site. The only mandatory property property is the key,
- * which must uniquely identify each user. For authenticated users, this may be a username or e-mail address. For anonymous users,
- * this could be an IP address or session ID.
+ * Contains specific attributes of a user browsing your site.
+ *
+ * The only mandatory property property is the key, which must uniquely identify each user. For authenticated users,
+ * this may be a username or e-mail address. For anonymous users, it could be an IP address or session ID.
+ *
+ * Use {@link \LaunchDarkly\LDUserBuilder} to construct instances of this class.
*/
class LDUser
{
@@ -22,6 +25,8 @@ class LDUser
protected $_privateAttributeNames = array();
/**
+ * Constructor for directly creating an instance; it is preferable to use LDUserBuilder.
+ *
* @param string $key Unique key for the user. For authenticated users, this may be a username or e-mail address. For anonymous users, this could be an IP address or session ID.
* @param string|null $secondary An optional secondary identifier
* @param string|null $ip The user's IP address (optional)
@@ -33,6 +38,7 @@ class LDUser
* @param string|null $lastName The user's last name (optional)
* @param boolean|null $anonymous Whether this is an anonymous user
* @param array|null $custom Other custom attributes that can be used to create custom rules
+ * @return LDUser
*/
public function __construct($key, $secondary = null, $ip = null, $country = null, $email = null, $name = null, $avatar = null, $firstName = null, $lastName = null, $anonymous = null, $custom = array(), $privateAttributeNames = array())
{
@@ -52,6 +58,10 @@ public function __construct($key, $secondary = null, $ip = null, $country = null
$this->_privateAttributeNames = $privateAttributeNames;
}
+ /**
+ * Used internally in flag evaluation.
+ * @ignore
+ */
public function getValueForEvaluation($attr)
{
switch ($attr) {
@@ -87,69 +97,81 @@ public function getValueForEvaluation($attr)
}
}
+ /** @return string|null */
public function getCountry()
{
return $this->_country;
}
+ /** @return array|null */
public function getCustom()
{
return $this->_custom;
}
+ /** @return string|null */
public function getIP()
{
return $this->_ip;
}
/**
- * @return null|string
+ * @return string
*/
public function getKey()
{
return $this->_key;
}
+ /** @return string|null */
public function getSecondary()
{
return $this->_secondary;
}
+ /** @return string|null */
public function getEmail()
{
return $this->_email;
}
+ /** @return string|null */
public function getName()
{
return $this->_name;
}
+ /** @return string|null */
public function getAvatar()
{
return $this->_avatar;
}
+ /** @return string|null */
public function getFirstName()
{
return $this->_firstName;
}
+ /** @return string|null */
public function getLastName()
{
return $this->_lastName;
}
+ /** @return bool|null */
public function getAnonymous()
{
return $this->_anonymous;
}
+ /** @return array|null */
public function getPrivateAttributeNames()
{
return $this->_privateAttributeNames;
}
+ /** @return bool */
public function isKeyBlank()
{
return isset($this->_key) && empty($this->_key);
diff --git a/src/LaunchDarkly/LDUserBuilder.php b/src/LaunchDarkly/LDUserBuilder.php
index ddd673874..d394a9fbf 100644
--- a/src/LaunchDarkly/LDUserBuilder.php
+++ b/src/LaunchDarkly/LDUserBuilder.php
@@ -2,12 +2,12 @@
namespace LaunchDarkly;
/**
- * A builder that helps construct LDUser objects.
+ * A builder for constructing LDUser objects.
*
- * Note that all user attributes, except for key and anonymous, can be designated as
- * private so that they will not be sent back to LaunchDarkly. You can do this either on a per-user basis in
- * LDUserBuilder, or globally via the private_attribute_names and all_attributes_private
- * options in the client configuration.
+ * Note that all user attributes, except for `key` and `anonymous`, can be designated as private so that
+ * they will not be sent back to LaunchDarkly. You can do this either on a per-user basis in LDUserBuilder,
+ * or globally via the `private_attribute_names` and `all_attributes_private` options in the client
+ * configuration.
*/
class LDUserBuilder
{
@@ -26,18 +26,30 @@ class LDUserBuilder
/**
* Creates a builder with the specified key.
+ * @param string $key The user key
+ * @return LDUserBuilder
*/
public function __construct($key)
{
$this->_key = $key;
}
+ /**
+ * Sets the user's secondary key attribute.
+ * @param string $secondary The secondary key
+ * @return LDUserBuilder the same builder
+ */
public function secondary($secondary)
{
$this->_secondary = $secondary;
return $this;
}
+ /**
+ * Sets the user's secondary key attribute, and marks it as private.
+ * @param string $secondary The secondary key
+ * @return LDUserBuilder the same builder
+ */
public function privateSecondary($secondary)
{
array_push($this->_privateAttributeNames, 'secondary');
@@ -45,7 +57,9 @@ public function privateSecondary($secondary)
}
/**
- * Sets the IP for a user.
+ * Sets the user's IP address attribute.
+ * @param string $ip The IP address
+ * @return LDUserBuilder the same builder
*/
public function ip($ip)
{
@@ -54,7 +68,9 @@ public function ip($ip)
}
/**
- * Sets the IP for a user, and ensures that the IP attribute will not be sent back to LaunchDarkly.
+ * Sets the user's IP address attribute, and marks it as private.
+ * @param string $ip The IP address
+ * @return LDUserBuilder the same builder
*/
public function privateIp($ip)
{
@@ -63,9 +79,11 @@ public function privateIp($ip)
}
/**
- * Sets the country for a user. The country should be a valid ISO 3166-1
- * alpha-2 or alpha-3 code. If it is not a valid ISO-3166-1 code, an attempt will be made to look up the country by its name.
- * If that fails, a warning will be logged, and the country will not be set.
+ * Sets the user's country attribute.
+ *
+ * This may be an ISO 3166-1 country code, or any other value you wish; it is not validated.
+ * @param string $country The country
+ * @return LDUserBuilder the same builder
*/
public function country($country)
{
@@ -74,10 +92,11 @@ public function country($country)
}
/**
- * Sets the country for a user, and ensures that the country attribute will not be sent back to LaunchDarkly.
- * The country should be a valid ISO 3166-1
- * alpha-2 or alpha-3 code. If it is not a valid ISO-3166-1 code, an attempt will be made to look up the country by its name.
- * If that fails, a warning will be logged, and the country will not be set.
+ * Sets the user's country attribute, and marks it as private.
+ *
+ * This may be an ISO 3166-1 country code, or any other value you wish; it is not validated.
+ * @param string $country The country
+ * @return LDUserBuilder the same builder
*/
public function privateCountry($country)
{
@@ -86,7 +105,9 @@ public function privateCountry($country)
}
/**
- * Sets the user's email address.
+ * Sets the user's email address attribute.
+ * @param string $email The email address
+ * @return LDUserBuilder the same builder
*/
public function email($email)
{
@@ -95,7 +116,9 @@ public function email($email)
}
/**
- * Sets the user's email address, and ensures that the email attribute will not be sent back to LaunchDarkly.
+ * Sets the user's email address attribute, and marks it as private.
+ * @param string $email The email address
+ * @return LDUserBuilder the same builder
*/
public function privateEmail($email)
{
@@ -104,7 +127,9 @@ public function privateEmail($email)
}
/**
- * Sets the user's full name.
+ * Sets the user's full name attribute.
+ * @param string $name The full name
+ * @return LDUserBuilder the same builder
*/
public function name($name)
{
@@ -113,7 +138,9 @@ public function name($name)
}
/**
- * Sets the user's full name, and ensures that the name attribute will not be sent back to LaunchDarkly.
+ * Sets the user's full name attribute, and marks it as private.
+ * @param string $name The full name
+ * @return LDUserBuilder the same builder
*/
public function privateName($name)
{
@@ -122,7 +149,9 @@ public function privateName($name)
}
/**
- * Sets the user's avatar.
+ * Sets the user's avatar URL attribute.
+ * @param string $avatar The avatar URL
+ * @return LDUserBuilder the same builder
*/
public function avatar($avatar)
{
@@ -131,7 +160,9 @@ public function avatar($avatar)
}
/**
- * Sets the user's avatar, and ensures that the avatar attribute will not be sent back to LaunchDarkly.
+ * Sets the user's avatar URL attribute, and marks it as private.
+ * @param string $avatar The avatar URL
+ * @return LDUserBuilder the same builder
*/
public function privateAvatar($avatar)
{
@@ -140,7 +171,9 @@ public function privateAvatar($avatar)
}
/**
- * Sets the user's first name.
+ * Sets the user's first name attribute.
+ * @param string $firstName The first name
+ * @return LDUserBuilder the same builder
*/
public function firstName($firstName)
{
@@ -149,7 +182,9 @@ public function firstName($firstName)
}
/**
- * Sets the user's first name, and ensures that the first name attribute will not be sent back to LaunchDarkly.
+ * Sets the user's first name attribute, and marks it as private.
+ * @param string $firstName The first name
+ * @return LDUserBuilder the same builder
*/
public function privateFirstName($firstName)
{
@@ -158,7 +193,9 @@ public function privateFirstName($firstName)
}
/**
- * Sets the user's last name.
+ * Sets the user's last name attribute.
+ * @param string $lastName The last name
+ * @return LDUserBuilder the same builder
*/
public function lastName($lastName)
{
@@ -167,7 +204,9 @@ public function lastName($lastName)
}
/**
- * Sets the user's last name, and ensures that the last name attribute will not be sent back to LaunchDarkly.
+ * Sets the user's last name attribute, and marks it as private.
+ * @param string $lastName The last name
+ * @return LDUserBuilder the same builder
*/
public function privateLastName($lastName)
{
@@ -176,7 +215,11 @@ public function privateLastName($lastName)
}
/**
- * Sets whether this user is anonymous. The default is false.
+ * Sets whether this user is anonymous.
+ *
+ * The default is false.
+ * @param bool $anonymous True if the user should not appear on the LaunchDarkly dashboard
+ * @return LDUserBuilder the same builder
*/
public function anonymous($anonymous)
{
@@ -186,7 +229,9 @@ public function anonymous($anonymous)
/**
* Sets any number of custom attributes for the user.
+ *
* @param array $custom An associative array of custom attribute names and values.
+ * @return LDUserBuilder the same builder
*/
public function custom($custom)
{
@@ -196,6 +241,10 @@ public function custom($custom)
/**
* Sets a single custom attribute for the user.
+ *
+ * @param string $customKey The attribute name
+ * @param mixed $customValue The attribute value
+ * @return LDUserBuilder the same builder
*/
public function customAttribute($customKey, $customValue)
{
@@ -204,7 +253,11 @@ public function customAttribute($customKey, $customValue)
}
/**
- * Sets a single custom attribute for the user, and ensures that the attribute will not be sent back to LaunchDarkly.
+ * Sets a single custom attribute for the user, and marks it as private.
+ *
+ * @param string $customKey The attribute name
+ * @param mixed $customValue The attribute value
+ * @return LDUserBuilder the same builder
*/
public function privateCustomAttribute($customKey, $customValue)
{
@@ -212,6 +265,11 @@ public function privateCustomAttribute($customKey, $customValue)
return $this->customAttribute($customKey, $customValue);
}
+ /**
+ * Creates the LDUser instance based on the builder's current properties.
+ *
+ * @return LDUser
+ */
public function build()
{
return new LDUser($this->_key, $this->_secondary, $this->_ip, $this->_country, $this->_email, $this->_name, $this->_avatar, $this->_firstName, $this->_lastName, $this->_anonymous, $this->_custom, $this->_privateAttributeNames);
diff --git a/src/LaunchDarkly/Operators.php b/src/LaunchDarkly/Operators.php
index a02995218..5a34be065 100644
--- a/src/LaunchDarkly/Operators.php
+++ b/src/LaunchDarkly/Operators.php
@@ -5,6 +5,12 @@
use DateTimeZone;
use Exception;
+/**
+ * Internal class used in feature flag evaluations.
+ *
+ * @ignore
+ * @internal
+ */
class Operators
{
const RFC3339 = 'Y-m-d\TH:i:s.uP';
diff --git a/src/LaunchDarkly/PreloadedFeatureRequester.php b/src/LaunchDarkly/PreloadedFeatureRequester.php
index ba307f6c0..3869a3a3d 100644
--- a/src/LaunchDarkly/PreloadedFeatureRequester.php
+++ b/src/LaunchDarkly/PreloadedFeatureRequester.php
@@ -1,6 +1,12 @@
assertEquals($metricValue, $event['metricValue']);
}
+ public function testEventsAreNotPublishedIfSendEventsIsFalse()
+ {
+ // In order to do this test, we cannot provide a mock object for Event_Processor_,
+ // because if we do that, it won't bother even looking at the send_events flag.
+ // Instead, we need to just put in a mock Event_Publisher_, so that the default
+ // EventProcessor would forward events to it if send_events were not disabled.
+ $mockPublisher = new MockEventPublisher("", array());
+ $options = array(
+ 'feature_requester_class' => MockFeatureRequester::class,
+ 'event_publisher' => $mockPublisher
+ );
+ $client = new LDClient("someKey", $options);
+ $client->track('eventkey', new LDUser('userkey'));
+ $this->assertEquals(array(), $mockPublisher->payloads);
+ }
+
public function testOnlyValidFeatureRequester()
{
$this->setExpectedException(InvalidArgumentException::class);
diff --git a/tests/MockEventPublisher.php b/tests/MockEventPublisher.php
new file mode 100644
index 000000000..f1603f622
--- /dev/null
+++ b/tests/MockEventPublisher.php
@@ -0,0 +1,16 @@
+payloads[] = $payload;
+ }
+}