PHPackages                             open-feature/sdk - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. open-feature/sdk

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

open-feature/sdk
================

PHP implementation of the OpenFeature SDK

2.2.0(3w ago)42781.1k—1.4%11[12 issues](https://github.com/open-feature/php-sdk/issues)[14 PRs](https://github.com/open-feature/php-sdk/pulls)17Apache-2.0PHPPHP ^8CI passing

Since Oct 21Pushed yesterday3 watchersCompare

[ Source](https://github.com/open-feature/php-sdk)[ Packagist](https://packagist.org/packages/open-feature/sdk)[ RSS](/packages/open-feature-sdk/feed)WikiDiscussions main Synced yesterday

READMEChangelog (10)Dependencies (72)Versions (77)Used By (17)

   ![OpenFeature Logo](https://raw.githubusercontent.com/open-feature/community/0e23508c163a6a1ac8c0ced3e4bd78faafe627c7/assets/logo/horizontal/black/openfeature-horizontal-black.svg)

OpenFeature PHP SDK
-------------------

[](#openfeature-php-sdk)

 [ ![Specification](https://camo.githubusercontent.com/a8f266aecc029c6623d9e63278e9668d809105a06e074d74a889823c09803edc/68747470733a2f2f696d672e736869656c64732e696f2f7374617469632f76313f6c6162656c3d73706563696669636174696f6e266d6573736167653d76302e352e3126636f6c6f723d79656c6c6f77267374796c653d666f722d7468652d6261646765) ](https://github.com/open-feature/spec/releases/tag/v0.5.1) [ ![Release](https://camo.githubusercontent.com/05d9a6237b28bdad022b81fa9aee2ebe79eedf5590fde5a0c349f785d64a1824/68747470733a2f2f696d672e736869656c64732e696f2f7374617469632f76313f6c6162656c3d72656c65617365266d6573736167653d76322e322e3026636f6c6f723d626c7565267374796c653d666f722d7468652d6261646765) ](https://github.com/open-feature/php-sdk/releases/tag/2.2.0)
 [ ![Total Downloads](https://camo.githubusercontent.com/43c7a86330c7ee2f7004e317a9b8c216f44ecdca46facf94dc2cb768c9acc556/68747470733a2f2f706f7365722e707567782e6f72672f6f70656e2d666561747572652f73646b2f646f776e6c6f616473) ](https://packagist.org/packages/open-feature/sdk) [![PHP 8.0+](https://camo.githubusercontent.com/c5fdb85fe7b1043ce306c68d5babfde6e15cf801dc06aa15754878b2fde1cf97/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d3e3d382e302d626c75652e737667)](https://camo.githubusercontent.com/c5fdb85fe7b1043ce306c68d5babfde6e15cf801dc06aa15754878b2fde1cf97/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d3e3d382e302d626c75652e737667) [ ![License](https://camo.githubusercontent.com/191e6098a2ab362608abc5be08582e3515a51e103917185e3ca3a4bccc703e34/68747470733a2f2f706f7365722e707567782e6f72672f6f70656e2d666561747572652f73646b2f6c6963656e7365) ](https://packagist.org/packages/open-feature/sdk) [ ![OpenSSF Best Practices](https://camo.githubusercontent.com/1dfcceb2812f8e12bb41b87f2b1f34431fcd9697b9f45d3b1e22eb7fab856955/68747470733a2f2f626573747072616374696365732e636f7265696e6672617374727563747572652e6f72672f70726f6a656374732f363835332f6261646765) ](https://bestpractices.coreinfrastructure.org/projects/6853)

[OpenFeature](https://openfeature.dev) is an open specification that provides a vendor-agnostic, community-driven API for feature flagging that works with your favorite feature flag management tool.

🚀 Quick start
-------------

[](#-quick-start)

### Requirements

[](#requirements)

This library targets PHP version 8.0 and newer. As long as you have any compatible version of PHP on your system you should be able to utilize the OpenFeature SDK.

This package also has a `.tool-versions` file for use with PHP version managers like `asdf`.

### Install

[](#install)

```
composer require open-feature/sdk
```

### Usage

[](#usage)

```
use OpenFeature\OpenFeatureAPI;
use OpenFeature\Providers\Flagd\FlagdProvider;

function example()
{
    $api = OpenFeatureAPI::getInstance();

    // configure a provider
    $api->setProvider(new FlagdProvider());

    // create a client
    $client = $api->getClient();

    // get a bool flag value
    $client->getBooleanValue('v2_enabled', false);
}
```

#### Extended Example

[](#extended-example)

```
use OpenFeature\OpenFeatureAPI;
use OpenFeature\OpenFeatureClient;

class MyClass
{
  private OpenFeatureClient $client;

  public function __construct()
  {
    $this->client = OpenFeatureAPI::getInstance()->getClient('MyClass');
  }

  public function booleanExample(): UI
  {
      // Should we render the redesign? Or the default webpage?
      if ($this->client->getBooleanValue('redesign_enabled', false)) {
          return render_redesign();
      }
      return render_normal();
  }

  public function stringExample(): Template
  {
      // Get the template to load for the custom new homepage
      $template = $this->client->getStringValue('homepage_template', 'default-homepage.html');

      return render_template($template);
  }

  public function numberExample(): array
  {
      // How many modules should we be fetching?
      $count = $this->client->getIntegerValue('module-fetch-count', 4);

      return fetch_modules($count);
  }

  public function structureExample(): HomepageModule
  {
      $obj = $this->client->getObjectValue('hero-module', $previouslyDefinedDefaultStructure);

      return HomepageModuleBuilder::new()
              ->title($obj->getValue('title'))
              ->body($obj->getValue('description'))
              ->build();
  }
}
```

🌟 Features
----------

[](#-features)

StatusFeaturesDescription✅[Providers](#providers)Integrate with a commercial, open source, or in-house feature management tool.✅[Targeting](#targeting)Contextually-aware flag evaluation using [evaluation context](https://openfeature.dev/docs/reference/concepts/evaluation-context).✅[Hooks](#hooks)Add functionality to various stages of the flag evaluation life-cycle.✅[Logging](#logging)Integrate with popular logging packages.✅[MultiProvider](#multiprovider)Combine multiple providers with configurable evaluation strategies for fallback and aggregation.❌[Named clients](#named-clients)Utilize multiple providers in a single application.⚠️[Eventing](#eventing)React to state changes in the provider or flag management system.❌[Shutdown](#shutdown)Gracefully clean up a provider during application shutdown.✅[Extending](#extending)Extend OpenFeature with custom providers and hooks.Implemented: ✅ | In-progress: ⚠️ | Not implemented yet: ❌

### Providers

[](#providers)

[Providers](https://openfeature.dev/docs/reference/concepts/provider) are an abstraction between a flag management system and the OpenFeature SDK. Look [here](https://openfeature.dev/ecosystem?instant_search%5BrefinementList%5D%5Btype%5D%5B0%5D=Provider&instant_search%5BrefinementList%5D%5Btechnology%5D%5B0%5D=PHP) for a complete list of available providers. If the provider you're looking for hasn't been created yet, see the [develop a provider](#develop-a-provider) section to learn how to build it yourself.

Once you've added a provider as a dependency, it can be registered with OpenFeature like this:

```
$api = OpenFeatureAPI::getInstance();
$api->setProvider(new MyProvider());
```

#### MultiProvider

[](#multiprovider)

The Multi-Provider allows you to use multiple underlying providers as sources of flag data for the OpenFeature SDK. When a flag is being evaluated, the Multi-Provider will consult each underlying provider it is managing in order to determine the final result. Different evaluation strategies can be defined to control which providers get evaluated and which result is used.

The Multi-Provider is a powerful tool for performing migrations between flag providers, or combining multiple providers into a single feature flagging interface. For example:

- **Migration**: When migrating between two providers, you can run both in parallel under a unified flagging interface. As flags are added to the new provider, the Multi-Provider will automatically find and return them, falling back to the old provider if the new provider does not have the flag.
- **Multiple Data Sources**: The Multi-Provider allows you to seamlessly combine many sources of flagging data, such as environment variables, local files, database values, and SaaS-hosted feature management systems.
- **High Availability**: Use multiple providers as redundant sources, automatically failing over when one provider is unavailable.

**Basic Usage**

The Multi-Provider is initialized with an array of providers it should evaluate:

```
use OpenFeature\OpenFeatureAPI;
use OpenFeature\implementation\multiprovider\MultiProvider;

$multiProvider = new MultiProvider([
    ['provider' => new ProviderA()],
    ['provider' => new ProviderB()],
]);

$api = OpenFeatureAPI::getInstance();
$api->setProvider($multiProvider);

$client = $api->getClient();
echo $client->getBooleanValue('my-flag', false);
```

By default, the Multi-Provider will evaluate all underlying providers in order and return the **first successful result**. If a provider indicates it does not have a flag (`FLAG_NOT_FOUND` error code), then it will be skipped and the next provider will be evaluated. If any provider throws or returns an error result, the operation will fail and the error will be returned. If no provider returns a successful result, the operation will fail with a `FLAG_NOT_FOUND` error code.

To change this behaviour, a different "strategy" can be provided:

```
use OpenFeature\implementation\multiprovider\strategy\FirstSuccessfulStrategy;

$multiProvider = new MultiProvider([
    ['provider' => new ProviderA()],
    ['provider' => new ProviderB()],
], new FirstSuccessfulStrategy());
```

The Multi-Provider comes with three strategies out of the box:

1. **FirstMatchStrategy** (default): Evaluates all providers in order and returns the first successful result. Providers that indicate `FLAG_NOT_FOUND` error will be skipped and the next provider will be evaluated. Any other error will cause the operation to fail and the error to be returned.
2. **FirstSuccessfulStrategy**: Evaluates all providers in order and returns the first successful result. Any error will cause that provider to be skipped. If no successful result is returned, the set of errors will be returned.
3. **ComparisonStrategy**: Evaluates all providers at one time. If every provider returns a successful result with the same value, then that result is returned. Otherwise, an error is returned immediately if any provider errors. When values do not agree, an optional callback will be executed to notify you of the mismatch, and the configured "fallback provider" value will be used. This can be useful when migrating between providers that are expected to contain identical configuration. You can easily spot mismatches in configuration without affecting flag behaviour.

**Provider Naming**

Providers can be named explicitly or have names auto-generated:

```
// Explicit naming (recommended for clarity)
new MultiProvider([
    ['name' => 'LaunchDarkly', 'provider' => $ldProvider],
    ['name' => 'FlagSmith', 'provider' => $fsProvider],
]);

// Auto-generated naming (provider class name is used)
new MultiProvider([
    ['provider' => $ldProvider],  // Named "launchdarkly" (lowercase)
    ['provider' => $fsProvider],  // Named "flagsmith" (lowercase)
    ['provider' => $fsProvider],  // Named "flagsmith_2" (auto-incremented, lowercase)
]);
```

> **Note:** Provider names are **case-insensitive** and stored in lowercase. "MyProvider", "myprovider", and "MYPROVIDER" are all treated as the same provider name. Auto-generated names from provider metadata are also normalized to lowercase.

**Strategies**

##### FirstMatchStrategy (Default)

[](#firstmatchstrategy-default)

Evaluates providers sequentially and returns the **first successful result**. Continues to the next provider only if the current one throws a `FLAG_NOT_FOUND` error.

**Use cases:**

- Primary/fallback provider setup
- Gradual migration between providers
- Provider priority ordering

```
use OpenFeature\implementation\multiprovider\strategy\FirstMatchStrategy;

$multiProvider = new MultiProvider([
    ['name' => 'Remote', 'provider' => new RemoteProvider()],   // Try remote first
    ['name' => 'Cache', 'provider' => new CacheProvider()],     // Fall back to cache
    ['name' => 'Default', 'provider' => new DefaultProvider()], // Final fallback
], new FirstMatchStrategy());
```

**Behavior:**

- **Remote** has the flag → returns Remote's value ✅
- **Remote** throws `FLAG_NOT_FOUND` → continues to **Cache**
- **Remote** throws other error (e.g., network timeout) → stops and returns error ❌
- All providers throw `FLAG_NOT_FOUND` → returns default value with aggregated errors

##### FirstSuccessfulStrategy

[](#firstsuccessfulstrategy)

Evaluates providers sequentially and returns the **first successful result**, skipping providers that throw **any error** (not just `FLAG_NOT_FOUND`).

**Use cases:**

- High availability setups
- Tolerating provider failures
- Failover scenarios

```
use OpenFeature\implementation\multiprovider\strategy\FirstSuccessfulStrategy;

$multiProvider = new MultiProvider([
    ['name' => 'Primary', 'provider' => new UnstableProvider()],
    ['name' => 'Secondary', 'provider' => new StableProvider()],
    ['name' => 'Tertiary', 'provider' => new LocalProvider()],
], new FirstSuccessfulStrategy());
```

**Behavior:**

- Evaluates providers in order until one succeeds
- Ignores **all types of errors** from failing providers
- Returns the first successful result
- If all providers fail → returns default value with aggregated errors

##### ComparisonStrategy

[](#comparisonstrategy)

Evaluates **all providers** at one time. If every provider returns a successful result with the same value, then that result is returned. Otherwise, an error is returned immediately if any provider errors. When values do not agree, the configured "fallback provider" value will be used.

This strategy accepts several arguments during initialization:

```
use OpenFeature\implementation\multiprovider\strategy\ComparisonStrategy;

// Set up providers
$providerA = new ProviderA();
$multiProvider = new MultiProvider([
    ['provider' => $providerA],
    ['provider' => new ProviderB()],
], new ComparisonStrategy(
    $providerA,  // First argument: "fallback provider" whose value to use when providers don't agree
    function($resolutions) {  // Second argument: callback when values don't match
        error_log('Mismatch detected: ' . json_encode($resolutions));
    }
));
```

The first argument is the "fallback provider" whose value to use in the event that providers do not agree. It should be the same object reference as one of the providers in the list. The second argument is a callback function that will be executed when a mismatch is detected. The callback will be passed an array containing the details of each provider's resolution, including the flag key, the value returned, and any errors that were thrown.

**Use cases:**

- A/B testing during provider migrations
- Validating new provider implementations against trusted baselines
- Detecting configuration drift across multiple sources
- Ensuring provider consistency before committing to a new provider

**Behavior:**

- Evaluates **ALL providers** sequentially
- **Fail-fast on errors:** If ANY provider returns an error, immediately returns all errors (no partial results)
- **Compares values:** Uses strict equality (`===`) to check if all providers returned the same value
- **On agreement:** If all providers succeed and all values match, returns the common value
- **On mismatch:** If all succeed but values don't match, calls optional `onMismatch` callback, then returns the fallback provider's value
- **Throws exception** if fallback provider not found in results

**Important Notes:**

- The fallback provider parameter is **required** (not optional)
- Fallback provider must be included in the provider list
- This is **not** a "highest value wins" strategy - it checks for exact equality
- Fallback provider is only used for value mismatches, NOT for error recovery
- Useful for ensuring consistency during migrations, not for aggregating different values

**Strategy Comparison**

ScenarioFirstMatchStrategyFirstSuccessfulStrategyComparisonStrategyProvider order matters✅ Yes (stops at first match)✅ Yes (stops at first success)❌ No (evaluates all)Handles FLAG\_NOT\_FOUNDContinues to nextContinues to nextReturns all errorsHandles other errorsStops evaluationContinues to nextReturns all errors immediatelyEvaluates all providers❌ No❌ No✅ YesBest for fallback✅✅❌Best for high availability❌✅❌Best for consistency validation❌❌✅Requires fallback provider❌ Optional❌ Optional✅ Required**Error Handling**

Error handling varies by strategy:

**FirstMatchStrategy:**

- Continues evaluation when providers throw `FLAG_NOT_FOUND`
- Stops and returns error if provider throws other errors
- Aggregates `FLAG_NOT_FOUND` errors if all providers fail

**FirstSuccessfulStrategy:**

- Continues evaluation when providers throw any error
- Returns first successful result, ignoring all previous errors
- Aggregates all errors if all providers fail

**ComparisonStrategy:**

- **Fail-fast:** Returns all errors immediately if ANY provider errors
- Fallback provider only used for value mismatches (not error recovery)
- All providers are evaluated (no short-circuiting on mismatches)
- On value mismatch: Invokes `onMismatch` callback, then returns fallback provider's value (not an error)

**Error Aggregation:**

When all providers fail, MultiProvider aggregates individual provider errors into a single detailed error message:

```
// Example: FirstMatchStrategy with all FLAG_NOT_FOUND
$result = $client->getBooleanDetails('non-existent-flag', false);
// Returns: value=false, reason=ERROR,
// error="Multi-provider evaluation failed with 3 provider error(s): [ProviderA]: Flag not found; [ProviderB]: Flag not found; [ProviderC]: Flag not found"

// Example: ComparisonStrategy with provider errors
$result = $client->getBooleanDetails('some-flag', false);
// Returns: value=false, reason=ERROR,
// error="Multi-provider evaluation failed with 2 provider error(s): [ProviderA]: Connection timeout; [ProviderB]: Invalid configuration"
```

This detailed error aggregation helps with debugging by showing exactly which provider failed and why, similar to JavaScript's `AggregateError`.

**Complete Example**

```
use OpenFeature\OpenFeatureAPI;
use OpenFeature\implementation\multiprovider\MultiProvider;
use OpenFeature\implementation\multiprovider\strategy\FirstMatchStrategy;

// Set up providers
$remoteProvider = new LaunchDarklyProvider($config);
$cacheProvider = new RedisCacheProvider($redis);
$defaultProvider = new InMemoryProvider([
    'feature-enabled' => true,
    'api-timeout' => 30,
]);

// Configure MultiProvider with priority order
$multiProvider = new MultiProvider([
    ['name' => 'LaunchDarkly', 'provider' => $remoteProvider],
    ['name' => 'Redis', 'provider' => $cacheProvider],
    ['name' => 'Defaults', 'provider' => $defaultProvider],
], new FirstMatchStrategy());

// Register with OpenFeature
$api = OpenFeatureAPI::getInstance();
$api->setProvider($multiProvider);

// Use as normal
$client = $api->getClient();
$enabled = $client->getBooleanValue('feature-enabled', false);

// Evaluation flow:
// 1. Try LaunchDarkly → if FLAG_NOT_FOUND, continue
// 2. Try Redis → if FLAG_NOT_FOUND, continue
// 3. Try Defaults → returns value
// 4. If all fail → returns default with aggregated errors
```

**Custom Strategies**

It is also possible to implement your own strategy if the above options do not fit your use case. To do so, create a class which extends `BaseEvaluationStrategy`:

```
use OpenFeature\implementation\multiprovider\strategy\BaseEvaluationStrategy;
use OpenFeature\implementation\multiprovider\strategy\ProviderContext;
use OpenFeature\implementation\multiprovider\strategy\StrategyContext;
use OpenFeature\implementation\multiprovider\ProviderResolutionResult;
use OpenFeature\implementation\multiprovider\FinalResult;
use OpenFeature\interfaces\provider\RunMode;

class CustomStrategy extends BaseEvaluationStrategy
{
    // Set to RunMode::EVALUATE_ALL to evaluate all providers at one time
    // or leave as default for sequential evaluation
    public string $runMode = RunMode::SEQUENTIAL;

    /**
     * Called before each provider is evaluated.
     * Return false to skip evaluating this provider.
     */
    public function shouldEvaluateThisProvider(ProviderContext $context): bool
    {
        // Custom logic to determine if this provider should be evaluated
        return true;
    }

    /**
     * Called after a provider is evaluated (sequential mode only).
     * Return false to stop evaluating remaining providers.
     */
    public function shouldEvaluateNextProvider(
        ProviderContext $context,
        ProviderResolutionResult $result
    ): bool {
        // Custom logic to determine if evaluation should continue
        return true;
    }

    /**
     * Called after all providers have been evaluated.
     * Determines the final result to return based on all provider results.
     */
    public function determineFinalResult(
        StrategyContext $context,
        array $resolutions
    ): FinalResult {
        // Custom logic to determine which result to return
        // Can analyze all results and choose one, aggregate them, etc.

        // Example: Return first successful result
        foreach ($resolutions as $resolution) {
            if (!$resolution->hasError()) {
                return new FinalResult(
                    $resolution->getDetails(),
                    $resolution->getProviderName(),
                    null
                );
            }
        }

        // All failed - return errors
        return new FinalResult(null, null, $this->aggregateErrors($resolutions));
    }
}
```

The `$runMode` property determines whether the list of providers will be evaluated sequentially or at once (using `RunMode::EVALUATE_ALL`).

The `shouldEvaluateThisProvider()` method is called just before a provider is evaluated by the Multi-Provider. If the function returns false, then the provider will be skipped instead of being evaluated.

The `shouldEvaluateNextProvider()` method is called after a provider is evaluated in sequential mode. If it returns true, the next provider in the sequence will be called, otherwise no more providers will be evaluated. This method is not called when `$runMode` is `RunMode::EVALUATE_ALL`.

The `determineFinalResult()` method is called after all providers have been evaluated, or the `shouldEvaluateNextProvider()` method returned false. It is called with an array of results from all the individual providers' evaluations. It returns the final result, or can throw an error if needed.

#### Known Limitations

[](#known-limitations)

**Sub-Provider Hooks Not Executed:**

Currently, when using MultiProvider, hooks registered on individual sub-providers via `provider->getHooks()` are **not executed** during flag evaluation. Only hooks registered at the API, Client, and Invocation levels (plus MultiProvider's own hooks) are executed.

In the JS-SDK reference implementation, each sub-provider's hooks are executed around each provider call via a dedicated `HookExecutor`. The PHP `Provider` interface extends `HooksGetter` (per OpenFeature Requirement 2.10), so the mechanism exists—it just isn't wired up in the current implementation.

**Workaround:** Register hooks at the API or Client level instead of on individual providers:

```
// Instead of this (won't execute):
$providerA->addHooks($myHook);

// Do this (will execute):
$client = $api->getClient();
$client->addHooks($myHook);
```

This limitation will be addressed in a future release where per-provider hook execution will be implemented to match the JS-SDK behavior.

### Targeting

[](#targeting)

Sometimes, the value of a flag must consider some dynamic criteria about the application or user, such as the user's location, IP, email address, or the server's location. In OpenFeature, we refer to this as [targeting](https://openfeature.dev/specification/glossary#targeting). If the flag management system you're using supports targeting, you can provide the input data using the [evaluation context](https://openfeature.dev/docs/reference/concepts/evaluation-context).

```
// add a value to the global context
$api = OpenFeatureAPI::getInstance();
$api->setEvaluationContext(new EvaluationContext('targetingKey', new Attributes(['myGlobalKey' => 'myGlobalValue'])));

// add a value to the client context
$client = $api->getClient();
$client->setEvaluationContext(new EvaluationContext('targetingKey', new Attributes(['myClientKey' => 'myClientValue'])));

// add a value to the invocation context
$context = new EvaluationContext('targetingKey', new Attributes(['myInvocationKey' => 'myInvocationValue']));

$boolValue = $client->getBooleanValue('boolFlag', false, $context);
```

### Hooks

[](#hooks)

[Hooks](https://openfeature.dev/docs/reference/concepts/hooks) allow for custom logic to be added at well-defined points of the flag evaluation life-cycle. Look [here](https://openfeature.dev/ecosystem/?instant_search%5BrefinementList%5D%5Btype%5D%5B0%5D=Hook&instant_search%5BrefinementList%5D%5Btechnology%5D%5B0%5D=php) for a complete list of available hooks. If the hook you're looking for hasn't been created yet, see the [develop a hook](#develop-a-hook) section to learn how to build it yourself.

Once you've added a hook as a dependency, it can be registered at the global, client, or flag invocation level.

```
// add a hook globally, to run on all evaluations
$api = OpenFeatureAPI::getInstance();
$api->addHooks(new ExampleGlobalHook());

// add a hook on this client, to run on all evaluations made by this client
$client = $api->getClient();
$client->addHooks(new ExampleClientHook());

// add a hook for this evaluation only
$value = $client->getBooleanValue("boolFlag", false, $context, new EvaluationOptions([new ExampleInvocationHook()]));
```

### Logging

[](#logging)

The PHP SDK utilizes several of the PHP Standards Recommendation, one of those being [PSR-3](https://www.php-fig.org/psr/psr-3/) which provides a standard `LoggerInterface`. The SDK makes use of a `LoggerAwareTrait` on several components, including the client for flag evaluation, the hook executor, and the global `OpenFeatureAPI` instance. When an OpenFeature client is created by the API, it will automatically utilize the configured logger in the API for it. The logger set in the client is also automatically used for the hook execution.

> ⚠️ Once the client is instantiated, updates to the API's logger will not synchronize. This is done to support the separation of named clients. If you must update an existing client's logger, do so directly!

```
$api = OpenFeatureAPI::getInstance();

$logger = new FancyLogger();

$defaultLoggerClient = $api->getClient('default-logger');

$api->setLogger(new CustomLogger());

$customLoggerClient = $api->getClient('custom-logger');

$overrideLoggerClient = $api->getClient('override-logger');
$overrideLoggerClient->setLogger($logger);

// now let's do some evaluations with these!

$defaultLoggerClient->getBooleanValue('A', false);
// uses default logger in the SDK

$customLoggerClient->getBooleanValue('B', false);
// uses the CustomLogger set in the API before the client was made

$overrideLoggerClient->getBooleanValue('C', false);
// uses the FancyLogger set directly on the client
```

### Named clients

[](#named-clients)

Named clients are not yet available in the PHP SDK. Progress on this feature can be tracked [here](https://github.com/open-feature/php-sdk/issues/93).

### Eventing

[](#eventing)

Events are not yet available in the PHP SDK. Progress on this feature can be tracked [here](https://github.com/open-feature/php-sdk/issues/93).

### Shutdown

[](#shutdown)

A shutdown method is not yet available in the PHP SDK. Progress on this feature can be tracked [here](https://github.com/open-feature/php-sdk/issues/93).

Extending
---------

[](#extending)

### Develop a provider

[](#develop-a-provider)

To develop a provider, you need to create a new project and include the OpenFeature SDK as a dependency. This can be a new repository or included in [the existing contrib repository](https://github.com/open-feature/php-sdk-contrib) available under the OpenFeature organization. You’ll then need to write the provider by implementing the `Provider` interface exported by the OpenFeature SDK.

```
declare(strict_types=1);

namespace OpenFeature\Example\Providers;

use OpenFeature\implementation\common\Metadata;
use OpenFeature\interfaces\common\Metadata as IMetadata;
use OpenFeature\interfaces\flags\EvaluationContext;
use OpenFeature\interfaces\hooks\Hook;
use OpenFeature\interfaces\provider\Provider;
use OpenFeature\interfaces\provider\ResolutionDetails;

class ExampleProviderImplementation implements Provider
{
    public function setLogger(LoggerInterface $logger): void
    {
        $this->logger = $logger;

        // or, utilize the OpenFeature\interfaces\common\LoggerAwareTrait
    }

    /**
     * @return Hook[]
     */
    public function getHooks(): array
    {
        return $this->hooks; // implement according to the OpenFeature specification
    }

    /**
     * Returns the metadata for the current resource
     */
    public function getMetadata(): IMetadata
    {
        return new Metadata(self::class);
    }

    public function resolveBooleanValue(string $flagKey, bool $defaultValue, ?EvaluationContext $context = null): ResolutionDetails
    {
        // resolve some ResolutionDetails
    }

    public function resolveStringValue(string $flagKey, string $defaultValue, ?EvaluationContext $context = null): ResolutionDetails
    {
        // resolve some ResolutionDetails
    }

    public function resolveIntegerValue(string $flagKey, int $defaultValue, ?EvaluationContext $context = null): ResolutionDetails
    {
        // resolve some ResolutionDetails
    }

    public function resolveFloatValue(string $flagKey, float $defaultValue, ?EvaluationContext $context = null): ResolutionDetails
    {
        // resolve some ResolutionDetails
    }

    /**
     * @inheritdoc
     */
    public function resolveObjectValue(string $flagKey, array $defaultValue, ?EvaluationContext $context = null): ResolutionDetails
    {
        // resolve some ResolutionDetails
    }
}
```

As you can see, this ends up requiring some boilerplate to fulfill all of the functionality that a Provider expects. Another option for implementing a provider is to utilize the AbstractProvider base class. This provides some internally wiring and simple scaffolding so you can skip some of it and focus on what's most important: resolving feature flags!

```
declare(strict_types=1);

namespace OpenFeature\Example\Providers;

use OpenFeature\implementation\provider\AbstractProvider;
use OpenFeature\interfaces\flags\EvaluationContext;
use OpenFeature\interfaces\provider\ResolutionDetails as ResolutionDetailsInterface;

class ExampleProviderExtension extends AbstractProvider
{
    protected static string $NAME = self::class;

    public function resolveBooleanValue(string $flagKey, bool $defaultValue, ?EvaluationContext $context = null): ResolutionDetailsInterface
    {
        // resolve some ResolutionDetails
    }

    public function resolveStringValue(string $flagKey, string $defaultValue, ?EvaluationContext $context = null): ResolutionDetailsInterface
    {
        // resolve some ResolutionDetails
    }

    public function resolveIntegerValue(string $flagKey, int $defaultValue, ?EvaluationContext $context = null): ResolutionDetailsInterface
    {
        // resolve some ResolutionDetails
    }

    public function resolveFloatValue(string $flagKey, float $defaultValue, ?EvaluationContext $context = null): ResolutionDetailsInterface
    {
        // resolve some ResolutionDetails
    }

    /**
     * @inheritdoc
     */
    public function resolveObjectValue(string $flagKey, array $defaultValue, ?EvaluationContext $context = null): ResolutionDetailsInterface
    {
        // resolve some ResolutionDetails
    }
}
```

> Built a new provider? [Let us know](https://github.com/open-feature/openfeature.dev/issues/new?assignees=&labels=provider&projects=&template=document-provider.yaml&title=%5BProvider%5D%3A+) so we can add it to the docs!

### Develop a hook

[](#develop-a-hook)

To develop a hook, you need to create a new project and include the OpenFeature SDK as a dependency. This can be a new repository or included in [the existing contrib repository](https://github.com/open-feature/php-sdk-contrib) available under the OpenFeature organization. Implement your own hook by conforming to the `Hook` interface. To satisfy the interface, all methods (`before`/`after`/`finally`/`error`) need to be defined. You can also extend one of the typed abstract base classes (`BooleanHook`, `StringHook`, `IntegerHook`, `FloatHook`, `ObjectHook`) which automatically implement `supportsFlagValueType()` for the corresponding flag type.

```
declare(strict_types=1);

namespace OpenFeature\Example\Hooks;

use OpenFeature\interfaces\flags\EvaluationContext;
use OpenFeature\interfaces\flags\FlagValueType;
use OpenFeature\interfaces\hooks\Hook;
use OpenFeature\interfaces\hooks\HookContext;
use OpenFeature\interfaces\hooks\HookHints;
use OpenFeature\interfaces\provider\ResolutionDetails;
use Throwable;

class ExampleStringHookImplementation implements Hook
{
    public function before(HookContext $context, HookHints $hints): ?EvaluationContext
    {
    }

    public function after(HookContext $context, ResolutionDetails $details, HookHints $hints): void
    {
    }

    public function error(HookContext $context, Throwable $error, HookHints $hints): void
    {
    }

    public function finally(HookContext $context, HookHints $hints): void
    {
    }

    public function supportsFlagValueType(string $flagValueType): bool
    {
        return $flagValueType === FlagValueType::STRING;
    }
}
```

You can also make use of existing base classes for various types and behaviors. Suppose you want to make this same hook, and have no limitation around extending a base class, you could do the following:

```
declare(strict_types=1);

namespace OpenFeature\Example\Hooks;

use OpenFeature\implementation\hooks\StringHook;
use OpenFeature\interfaces\flags\EvaluationContext;
use OpenFeature\interfaces\flags\FlagValueType;
use OpenFeature\interfaces\hooks\HookContext;
use OpenFeature\interfaces\hooks\HookHints;
use OpenFeature\interfaces\provider\ResolutionDetails;
use Throwable;

class ExampleStringHookExtension extends StringHook
{
    public function before(HookContext $context, HookHints $hints): ?EvaluationContext
    {
    }

    public function after(HookContext $context, ResolutionDetails $details, HookHints $hints): void
    {
    }

    public function error(HookContext $context, Throwable $error, HookHints $hints): void
    {
    }

    public function finally(HookContext $context, HookHints $hints): void
    {
    }
}
```

> Built a new hook? [Let us know](https://github.com/open-feature/openfeature.dev/issues/new?assignees=&labels=hook&projects=&template=document-hook.yaml&title=%5BHook%5D%3A+) so we can add it to the docs!

⭐️ Support the project
----------------------

[](#️-support-the-project)

- Give this repo a ⭐️!
- Follow us on social media:
    - Twitter: [@openfeature](https://twitter.com/openfeature)
    - LinkedIn: [OpenFeature](https://www.linkedin.com/company/openfeature/)
- Join us on [Slack](https://cloud-native.slack.com/archives/C0344AANLA1)
- For more, check out our [community page](https://openfeature.dev/community/)

🤝 Contributing
--------------

[](#-contributing)

Interested in contributing? Great, we'd love your help! To get started, take a look at the [CONTRIBUTING](CONTRIBUTING.md) guide.

### Thanks to everyone who has already contributed

[](#thanks-to-everyone-who-has-already-contributed)

[ ![Pictures of the folks who have contributed to the project](https://camo.githubusercontent.com/efcc870284e02b597bc6ba88f9fa79c48403c93f1798b7f145184a7f266a6864/68747470733a2f2f636f6e747269622e726f636b732f696d6167653f7265706f3d6f70656e2d666561747572652f7068702d73646b)](https://github.com/open-feature/php-sdk/graphs/contributors)Made with [contrib.rocks](https://contrib.rocks).

###  Health Score

62

—

FairBetter than 99% of packages

Maintenance92

Actively maintained with recent releases

Popularity52

Moderate usage in the ecosystem

Community34

Small or concentrated contributor base

Maturity62

Established project with proven stability

 Bus Factor2

2 contributors hold 50%+ of commits

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~36 days

Recently: every ~69 days

Total

37

Last Release

25d ago

Major Versions

0.1.1 → 1.0.02022-11-27

0.0.10 → 1.1.02022-12-19

1.4.0 → 2.0.02023-02-12

PHP version history (2 changes)0.0.2PHP ^7.4 || ^8

1.4.0PHP ^8

### Community

Maintainers

![](https://www.gravatar.com/avatar/37b811aae08f6063ce4a6c9111849a2c8c5400b58718072c224c9eb8c46e5f17?d=identicon)[0xc](/maintainers/0xc)

![](https://www.gravatar.com/avatar/6e56ad788a4b12e749a62432cbd228ff9a0799341cf6bc6f060531d0bd49be7a?d=identicon)[beeme1mr](/maintainers/beeme1mr)

---

Top Contributors

[![tcarrio](https://avatars.githubusercontent.com/u/8659099?v=4)](https://github.com/tcarrio "tcarrio (43 commits)")[![renovate[bot]](https://avatars.githubusercontent.com/in/2740?v=4)](https://github.com/renovate[bot] "renovate[bot] (38 commits)")[![github-actions[bot]](https://avatars.githubusercontent.com/in/15368?v=4)](https://github.com/github-actions[bot] "github-actions[bot] (32 commits)")[![beeme1mr](https://avatars.githubusercontent.com/u/682996?v=4)](https://github.com/beeme1mr "beeme1mr (7 commits)")[![Tmakinde](https://avatars.githubusercontent.com/u/57964881?v=4)](https://github.com/Tmakinde "Tmakinde (3 commits)")[![schodemeiss](https://avatars.githubusercontent.com/u/619116?v=4)](https://github.com/schodemeiss "schodemeiss (2 commits)")[![jonathannorris](https://avatars.githubusercontent.com/u/1219069?v=4)](https://github.com/jonathannorris "jonathannorris (2 commits)")[![toddbaert](https://avatars.githubusercontent.com/u/25272906?v=4)](https://github.com/toddbaert "toddbaert (1 commits)")[![xepozz](https://avatars.githubusercontent.com/u/6815714?v=4)](https://github.com/xepozz "xepozz (1 commits)")[![ChrisLightfootWild](https://avatars.githubusercontent.com/u/106102472?v=4)](https://github.com/ChrisLightfootWild "ChrisLightfootWild (1 commits)")[![jaugustin](https://avatars.githubusercontent.com/u/564420?v=4)](https://github.com/jaugustin "jaugustin (1 commits)")[![kidager](https://avatars.githubusercontent.com/u/3314980?v=4)](https://github.com/kidager "kidager (1 commits)")[![marcozabel](https://avatars.githubusercontent.com/u/49674991?v=4)](https://github.com/marcozabel "marcozabel (1 commits)")[![MilesChou](https://avatars.githubusercontent.com/u/1258752?v=4)](https://github.com/MilesChou "MilesChou (1 commits)")[![mmito](https://avatars.githubusercontent.com/u/45098081?v=4)](https://github.com/mmito "mmito (1 commits)")

---

Tags

feature-flagsopenfeaturephpfeatureflagsfeatureflaggingopenfeature

###  Code Quality

TestsBehat

Static AnalysisPHPStan, Psalm

Type Coverage Yes

### Embed Badge

![Health badge](/badges/open-feature-sdk/health.svg)

```
[![Health](https://phpackages.com/badges/open-feature-sdk/health.svg)](https://phpackages.com/packages/open-feature-sdk)
```

###  Alternatives

[symfony/lock

Creates and manages locks, a mechanism to provide exclusive access to a shared resource

514139.2M689](/packages/symfony-lock)[matomo/matomo

Matomo is the leading Free/Libre open analytics platform

21.7k38.9k](/packages/matomo-matomo)[ecotone/ecotone

Enterprise architecture layer for Laravel and Symfony — CQRS, Event Sourcing, Durable Workflows (Sagas, Orchestrators), Projections, and Outbox messaging via PHP attributes.

564576.7k51](/packages/ecotone-ecotone)[civicrm/civicrm-core

Open source constituent relationship management for non-profits, NGOs and advocacy organizations.

751291.4k43](/packages/civicrm-civicrm-core)[illuminate/broadcasting

The Illuminate Broadcasting package.

7127.2M208](/packages/illuminate-broadcasting)[logiscape/mcp-sdk-php

Model Context Protocol SDK for PHP

368116.8k12](/packages/logiscape-mcp-sdk-php)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
