Skip to content

chore: Various cleanup #26

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jan 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 0 additions & 8 deletions .github/actions/ci/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,6 @@ inputs:
type: boolean
required: false
default: false
shared-test-version:
description: 'Which version of the shared test package should we required'
required: false
default: 4.x-dev
token:
description: 'Token used to prevent composer rate limiting'
required: true
Expand All @@ -33,10 +29,6 @@ runs:
shell: bash
run: composer install --no-progress

- name: Require appropriate shared tests package
shell: bash
run: composer require --dev 'launchdarkly/server-sdk-shared-tests:${{ inputs.shared-test-version }}'

- name: Downgrade to lowest versions
if: ${{ inputs.use-lowest-dependencies }}
shell: bash
Expand Down
20 changes: 2 additions & 18 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
name: Run CI
on:
push:
branches: [ main ]
branches: [ main, 'feat/**' ]
paths-ignore:
- '**.md' # Do not need to run CI for markdown changes.
pull_request:
branches: [ main ]
branches: [ main, 'feat/**' ]
paths-ignore:
- '**.md'

Expand All @@ -23,29 +23,14 @@ jobs:
fail-fast: false
matrix:
include:
# 8.1 configurations
- php-version: 8.1
use-lowest-dependencies: true
shared-test-version: 5.x-dev
- php-version: 8.1
use-lowest-dependencies: true
shared-test-version: dev-main

# 8.2 configurations
- php-version: 8.2
use-lowest-dependencies: false
shared-test-version: 5.x-dev
- php-version: 8.2
use-lowest-dependencies: false
shared-test-version: dev-main

# 8.3 configurations
- php-version: 8.3
use-lowest-dependencies: false
shared-test-version: 5.x-dev
- php-version: 8.3
use-lowest-dependencies: false
shared-test-version: dev-main

steps:
- uses: actions/checkout@v4
Expand All @@ -56,5 +41,4 @@ jobs:
with:
php-version: ${{ matrix.php-version }}
use-lowest-dependencies: ${{ matrix.use-lowest-dependencies }}
shared-test-version: ${{ matrix.shared-test-version }}
token: ${{ secrets.GITHUB_TOKEN }}
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ help: #! Show this help message

.PHONY: test
test: #! Run unit tests
php -d xdebug.mode=coverage vendor/bin/phpunit
php vendor/bin/phpunit

.PHONY: coverage
coverage: #! Run unit tests with test coverage
Expand Down
14 changes: 6 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@

This library provides a Redis-backed data source for the [LaunchDarkly PHP SDK](https://github.com/launchdarkly/php-server-sdk), replacing the default behavior of querying the LaunchDarkly service endpoints. The underlying Redis client implementation is the [`phpredis`](https://github.com/phpredis/phpredis) extension. If you want to use the Predis package instead, see https://github.com/launchdarkly/php-server-sdk-redis-predis.

The minimum version of the LaunchDarkly PHP SDK for use with this library is 4.0.0. In earlier versions of the SDK, the Redis integrations were bundled in the main SDK package.
The minimum version of the LaunchDarkly PHP SDK for use with this library is 6.4.0.

The minimum PHP version is 7.3.
The minimum PHP version is 8.1.

For more information, see [our SDK documentation](https://docs.launchdarkly.com/sdk/features/storing-data).

Expand All @@ -27,15 +27,13 @@ php composer.phar install launchdarkly/server-sdk-redis-phpredis --save
3. In your SDK configuration code, configure the Redis integration:

```php
$fr = LaunchDarkly\Integrations\PHPRedis::featureRequester([
"prefix" => "my-key-prefix"
]);
$config = [ "feature_requester" => $fr ];
$fr = LaunchDarkly\Integrations\PHPRedis::featureRequester(
$redisClient, ["prefix" => "my-key-prefix"]
);
$config = ["feature_requester" => $fr];
$client = new LDClient("sdk_key", $config);
```

By default, the store will try to connect to a local Redis instance on port 6379. You may specify an alternate configuration as described in the API documentation for `PHPRedis::featureRequester`. Make sure the `prefix` option corresponds to the key prefix that is being used by the Relay Proxy.

## About LaunchDarkly

* LaunchDarkly is a continuous delivery platform that provides feature flags as a service and allows developers to iterate quickly and safely. We allow you to easily flag your features and manage them from the LaunchDarkly dashboard. With LaunchDarkly, you can:
Expand Down
6 changes: 0 additions & 6 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
{
"repositories": [
{
"type": "vcs",
"url": "https://github.com/launchdarkly/php-server-sdk-shared-tests"
}
],
"name": "launchdarkly/server-sdk-redis-phpredis",
"description": "LaunchDarkly PHP SDK Redis integration using the phpredis extension",
"keywords": [
Expand Down
12 changes: 11 additions & 1 deletion phpunit.xml
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
<?xml version="1.0"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd" bootstrap="vendor/autoload.php" colors="true" beStrictAboutChangesToGlobalState="true" beStrictAboutOutputDuringTests="true" beStrictAboutResourceUsageDuringSmallTests="true" beStrictAboutTestsThatDoNotTestAnything="true" beStrictAboutTodoAnnotatedTests="true" verbose="true">

<logging>
<junit outputFile="phpunit/junit.xml"/>
</logging>

<coverage>
<include>
<directory suffix=".php">src</directory>
</include>
<report>
<html outputDirectory="build/phpunit/html-coverage"/>
<xml outputDirectory="build/phpunit/xml-coverage"/>
</report>
</coverage>

<testsuites>
<testsuite name="Unit Tests">
<directory>tests</directory>
Expand Down
73 changes: 27 additions & 46 deletions src/LaunchDarkly/Impl/Integrations/PHPRedisFeatureRequester.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace LaunchDarkly\Impl\Integrations;

use LaunchDarkly\Integrations;
Expand All @@ -10,65 +12,44 @@
*/
class PHPRedisFeatureRequester extends FeatureRequesterBase
{
private ?array $redisOptions = null;
private ?Redis $redisInstance = null;
private ?string $prefix;

public function __construct(string $baseUri, string $sdkKey, array $options)
{
private readonly string $prefix;

public function __construct(
private readonly Redis $client,
string $baseUri,
string $sdkKey,
array $options
) {
parent::__construct($baseUri, $sdkKey, $options);

/** @var ?string **/
$this->prefix = $options['redis_prefix'] ?? null;
if ($this->prefix === null || $this->prefix === '') {
$this->prefix = Integrations\PHPRedis::DEFAULT_PREFIX;
}

/** @var ?Redis */
$client = $this->_options['phpredis_client'] ?? null;
if ($client instanceof Redis) {
$this->redisInstance = $client;
} else {
$this->redisOptions = [
"timeout" => $options['redis_timeout'] ?? 5,
"host" => $options['redis_host'] ?? 'localhost',
"port" => $options['redis_port'] ?? 6379,
"password" => $options['redis_password'] ?? null
];
/** @var ?string */
$prefix = $options['prefix'] ?? null;
if ($prefix === null || $prefix === '') {
$prefix = Integrations\PHPRedis::DEFAULT_PREFIX;
}
$this->prefix = $prefix;
}

protected function readItemString(string $namespace, string $key): ?string
{
$redis = $this->getConnection();
return $redis->hget("$this->prefix:$namespace", $key);
}
/** @var string|false */
$result = $this->client->hget("$this->prefix:$namespace", $key);
if ($result === false) {
return null;
}

protected function readItemStringList(string $namespace): ?array
{
$redis = $this->getConnection();
$raw = $redis->hgetall("$this->prefix:$namespace");
return $raw ? array_values($raw) : null;
return $result;
}

protected function getConnection(): Redis
protected function readItemStringList(string $namespace): ?array
{
if ($this->redisInstance instanceof Redis) {
return $this->redisInstance;
}

$redis = new Redis();
$redis->pconnect(
$this->redisOptions["host"],
$this->redisOptions["port"],
$this->redisOptions["timeout"],
'launchdarkly/php-server-sdk-redis-phpredis'
);
/** @var ?array<string, string>|false */
$raw = $this->client->hgetall("$this->prefix:$namespace");

if ($this->redisOptions['password']) {
$redis->auth($this->redisOptions['password']);
if ($raw === null || $raw === false) {
return null;
}

return $this->redisInstance = $redis;
return array_values($raw);
}
}
49 changes: 29 additions & 20 deletions src/LaunchDarkly/Integrations/PHPRedis.php
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
<?php

declare(strict_types=1);

namespace LaunchDarkly\Integrations;

use LaunchDarkly\Impl\Integrations;
use LaunchDarkly\Subsystems;
use LaunchDarkly\Subsystems\FeatureRequester;
use Psr\Log\LoggerInterface;
use Redis;

Expand All @@ -20,48 +23,54 @@ class PHPRedis
* To use this method, you must have installed the `phpredis` extension. After calling this
* method, store its return value in the `feature_requester` property of your client configuration:
*
* $fr = LaunchDarkly\Integrations\PHPRedis::featureRequester([ "redis_prefix" => "env1" ]);
* $config = [ "feature_requester" => $fr ];
* $client = new LDClient("sdk_key", $config);
* ```php
* $fr = LaunchDarkly\Integrations\PHPRedis::featureRequester(["prefix" => "env1"]);
* $config = ["feature_requester" => $fr];
* $client = new LDClient("sdk_key", $config);
* ```
*
* For more about using LaunchDarkly with databases, see the
* [SDK reference guide](https://docs.launchdarkly.com/sdk/features/storing-data).
*
* @param array $options Configuration settings (can also be passed in the main client configuration):
* - `redis_host`: hostname of the Redis server; defaults to `localhost`
* - `redis_port`: port of the Redis server; defaults to 6379
* - `redis_password`: password to auth against the Redis server; optional
* - `redis_timeout`: connection timeout in seconds; defaults to 5
* - `redis_prefix`: a string to be prepended to all database keys; corresponds to the prefix
* setting in ld-relay
* - `phpredis_client`: an already-configured Redis client instance if you wish to reuse one
* - `prefix`: a string to be prepended to all database keys; corresponds
* to the prefix setting in ld-relay
* - `apc_expiration`: expiration time in seconds for local caching, if `APCu` is installed
* @return mixed an object to be stored in the `feature_requester` configuration property
* @return callable(string, string, array): FeatureRequester an object to be stored in the `feature_requester` configuration property
*/
public static function featureRequester($options = [])
public static function featureRequester(Redis $client, $options = []): callable
{
if (!extension_loaded('redis')) {
throw new \RuntimeException("phpredis extension is required to use Integrations\\PHPRedis");
}

return function (string $baseUri, string $sdkKey, array $baseOptions) use ($options) {
return new Integrations\PHPRedisFeatureRequester($baseUri, $sdkKey, array_merge($baseOptions, $options));
return function (string $baseUri, string $sdkKey, array $baseOptions) use ($client, $options): FeatureRequester {
return new Integrations\PHPRedisFeatureRequester($client, $baseUri, $sdkKey, array_merge($baseOptions, $options));
};
}

/**
* Configures a big segments store instance backed by Redis.
*
* After calling this method, store its return value in the `store` property of your Big Segment configuration:
*
* ```php
* $store = LaunchDarkly\Integrations\PHPRedis::bigSegmentsStore(["prefix" => "env1"]);
* $bigSegmentsConfig = new LaunchDarkly\BigSegmentConfig(store: $store);
* $config = ["big_segments" => $bigSegmentsConfig];
* $client = new LDClient("sdk_key", $config);
* ```
*
* @param array<string,mixed> $options
* - `prefix`: namespace prefix to add to all hash keys
* @return callable(LoggerInterface, array): Subsystems\BigSegmentsStore
* - `prefix`: a string to be prepended to all database keys; corresponds
* to the prefix setting in ld-relay
*/
public static function bigSegmentsStore(Redis $client, array $options = []): callable
public static function bigSegmentsStore(Redis $client, LoggerInterface $logger, array $options = []): Subsystems\BigSegmentsStore
{
if (!extension_loaded('redis')) {
throw new \RuntimeException("phpredis extension is required to use Integrations\\PHPRedis");
}

return function (LoggerInterface $logger, array $baseOptions) use ($client, $options): Subsystems\BigSegmentsStore {
return new Integrations\PHPRedisBigSegmentsStore($client, $logger, array_merge($baseOptions, $options));
};
return new Integrations\PHPRedisBigSegmentsStore($client, $logger, $options);
}
}
2 changes: 2 additions & 0 deletions tests/Impl/Integrations/PHPRedisBigSegmentsStoreTest.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace LaunchDarkly\Impl\Integrations\Tests\Impl\Integrations;

use LaunchDarkly\Impl\Integrations\PHPRedisBigSegmentsStore;
Expand Down
Loading
Loading