{"id":20607744,"url":"https://github.com/open-feature/php-sdk","last_synced_at":"2025-04-15T04:10:37.407Z","repository":{"id":58965929,"uuid":"534805192","full_name":"open-feature/php-sdk","owner":"open-feature","description":"PHP implementation of the OpenFeature SDK","archived":false,"fork":false,"pushed_at":"2024-05-07T20:06:48.000Z","size":324,"stargazers_count":18,"open_issues_count":15,"forks_count":6,"subscribers_count":4,"default_branch":"main","last_synced_at":"2024-05-08T06:28:47.170Z","etag":null,"topics":["feature-flags","openfeature","php"],"latest_commit_sha":null,"homepage":"https://openfeature.dev","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/open-feature.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-09-09T21:03:59.000Z","updated_at":"2024-06-19T05:32:35.683Z","dependencies_parsed_at":"2023-11-07T04:37:00.035Z","dependency_job_id":"f18fa6ef-4ff1-40d5-8324-d62c2154bc80","html_url":"https://github.com/open-feature/php-sdk","commit_stats":{"total_commits":80,"total_committers":5,"mean_commits":16.0,"dds":0.4625,"last_synced_commit":"2adecfb0b626c35b906d3a507e235b39317e922c"},"previous_names":[],"tags_count":29,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/open-feature%2Fphp-sdk","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/open-feature%2Fphp-sdk/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/open-feature%2Fphp-sdk/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/open-feature%2Fphp-sdk/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/open-feature","download_url":"https://codeload.github.com/open-feature/php-sdk/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224901311,"owners_count":17388950,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["feature-flags","openfeature","php"],"created_at":"2024-11-16T10:08:29.600Z","updated_at":"2024-11-16T10:08:30.522Z","avatar_url":"https://github.com/open-feature.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!-- markdownlint-disable MD033 --\u003e\n\u003c!-- x-hide-in-docs-start --\u003e\n\u003cp align=\"center\"\u003e\n  \u003cpicture\u003e\n    \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/open-feature/community/0e23508c163a6a1ac8c0ced3e4bd78faafe627c7/assets/logo/horizontal/white/openfeature-horizontal-white.svg\" /\u003e\n    \u003cimg align=\"center\" alt=\"OpenFeature Logo\" src=\"https://raw.githubusercontent.com/open-feature/community/0e23508c163a6a1ac8c0ced3e4bd78faafe627c7/assets/logo/horizontal/black/openfeature-horizontal-black.svg\" /\u003e\n  \u003c/picture\u003e\n\u003c/p\u003e\n\n\u003ch2 align=\"center\"\u003eOpenFeature PHP SDK\u003c/h2\u003e\n\n\u003c!-- x-hide-in-docs-end --\u003e\n\u003c!-- The 'github-badges' class is used in the docs --\u003e\n\u003cp align=\"center\" class=\"github-badges\"\u003e\n\n  \u003ca href=\"https://github.com/open-feature/spec/releases/tag/v0.5.1\"\u003e\n    \u003cimg alt=\"Specification\" src=\"https://img.shields.io/static/v1?label=specification\u0026message=v0.5.1\u0026color=yellow\u0026style=for-the-badge\" /\u003e\n  \u003c/a\u003e\n  \u003c!-- x-release-please-start-version --\u003e\n\n  \u003ca href=\"https://github.com/open-feature/php-sdk/releases/tag/2.0.10\"\u003e\n    \u003cimg alt=\"Release\" src=\"https://img.shields.io/static/v1?label=release\u0026message=v2.0.10\u0026color=blue\u0026style=for-the-badge\" /\u003e\n  \u003c/a\u003e  \n\n  \u003c!-- x-release-please-end --\u003e\n  \u003cbr/\u003e\n\n  \u003ca href=\"https://packagist.org/packages/open-feature/sdk\"\u003e\n    \u003cimg alt=\"Total Downloads\" src=\"https://poser.pugx.org/open-feature/sdk/downloads\" /\u003e\n  \u003c/a\u003e\n\n  \u003ca\u003e\n    \u003cimg alt=\"PHP 8.0+\" src=\"https://img.shields.io/badge/php-\u003e=8.0-blue.svg\" /\u003e\n  \u003c/a\u003e\n\n  \u003ca href=\"https://packagist.org/packages/open-feature/sdk\"\u003e\n    \u003cimg alt=\"License\" src=\"https://poser.pugx.org/open-feature/sdk/license\" /\u003e\n  \u003c/a\u003e\n  \n  \u003ca href=\"https://bestpractices.coreinfrastructure.org/projects/6853\"\u003e\n    \u003cimg alt=\"OpenSSF Best Practices\" src=\"https://bestpractices.coreinfrastructure.org/projects/6853/badge\" /\u003e\n  \u003c/a\u003e\n\n\u003c/p\u003e\n\u003c!-- x-hide-in-docs-start --\u003e\n\n[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.\n\n\u003c!-- x-hide-in-docs-end --\u003e\n## 🚀 Quick start\n\n### Requirements\n\nThis 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.\n\nThis package also has a `.tool-versions` file for use with PHP version managers like `asdf`.\n\n### Install\n\n```shell\ncomposer require open-feature/sdk\n```\n\n### Usage\n\n```php\nuse OpenFeature\\OpenFeatureAPI;\nuse OpenFeature\\Providers\\Flagd\\FlagdProvider;\n\nfunction example()\n{\n    $api = OpenFeatureAPI::getInstance();\n    \n    // configure a provider\n    $api-\u003esetProvider(new FlagdProvider());\n\n    // create a client\n    $client = $api-\u003egetClient();\n    \n    // get a bool flag value\n    $client-\u003egetBooleanValue('v2_enabled', false);\n}\n```\n\n#### Extended Example\n\n```php\nuse OpenFeature\\OpenFeatureClient;\n\nclass MyClass \n{\n  private OpenFeatureClient $client;\n\n  public function __construct() \n  {\n    $this-\u003eclient = OpenFeatureAPI::getInstance()-\u003egetClient('MyClass');\n  }\n\n  public function booleanExample(): UI\n  {\n      // Should we render the redesign? Or the default webpage? \n      if ($this-\u003eclient-\u003egetBooleanValue('redesign_enabled', false)) {\n          return render_redesign();\n      }\n      return render_normal();\n  }\n\n  public function stringExample(): Template\n  {\n      // Get the template to load for the custom new homepage\n      $template = $this-\u003eclient-\u003egetStringValue('homepage_template', 'default-homepage.html');\n\n      return render_template($template);\n  }\n\n  public function numberExample(): array\n  {\n      // How many modules should we be fetching?\n      $count = $this-\u003eclient-\u003egetIntegerValue('module-fetch-count', 4);\n\n      return fetch_modules($count);\n  }\n\n  public function structureExample(): HomepageModule\n  {\n      $obj = $this-\u003eclient-\u003egetObjectValue('hero-module', $previouslyDefinedDefaultStructure);\n\n      return HomepageModuleBuilder::new()\n              -\u003etitle($obj-\u003egetValue('title'))\n              -\u003ebody($obj-\u003egetValue('description'))\n              -\u003ebuild();\n  }\n}\n```\n\n## 🌟 Features\n\n| Status | Features                        | Description                                                                                                                        |\n| ------ | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |\n| ✅      | [Providers](#providers)         | Integrate with a commercial, open source, or in-house feature management tool.                                                     |\n| ✅      | [Targeting](#targeting)         | Contextually-aware flag evaluation using [evaluation context](https://openfeature.dev/docs/reference/concepts/evaluation-context). |\n| ✅      | [Hooks](#hooks)                 | Add functionality to various stages of the flag evaluation life-cycle.                                                             |\n| ✅      | [Logging](#logging)             | Integrate with popular logging packages.                                                                                           |\n| ❌      | [Named clients](#named-clients) | Utilize multiple providers in a single application.                                                                                |\n| ⚠️      | [Eventing](#eventing)           | React to state changes in the provider or flag management system.                                                                  |\n| ❌      | [Shutdown](#shutdown)           | Gracefully clean up a provider during application shutdown.                                                                        |\n| ✅      | [Extending](#extending)         | Extend OpenFeature with custom providers and hooks.                                                                                |\n\n\u003csub\u003eImplemented: ✅ | In-progress: ⚠️ | Not implemented yet: ❌\u003c/sub\u003e\n\n### Providers\n\n[Providers](https://openfeature.dev/docs/reference/concepts/provider) are an abstraction between a flag management system and the OpenFeature SDK.\nLook [here](https://openfeature.dev/ecosystem?instant_search%5BrefinementList%5D%5Btype%5D%5B0%5D=Provider\u0026instant_search%5BrefinementList%5D%5Btechnology%5D%5B0%5D=PHP) for a complete list of available providers.\nIf 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.\n\nOnce you've added a provider as a dependency, it can be registered with OpenFeature like this:\n\n```php\n$api = OpenFeatureAPI::getInstance();\n$api-\u003esetProvider(new MyProvider());\n```\n\n\u003c!-- In some situations, it may be beneficial to register multiple providers in the same application.\nThis is possible using [named clients](#named-clients), which is covered in more detail below. --\u003e\n\n### Targeting\n\nSometimes, 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.\nIn OpenFeature, we refer to this as [targeting](https://openfeature.dev/specification/glossary#targeting).\nIf 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).\n\n```php\n// add a value to the global context\n$api = OpenFeatureAPI::getInstance();\n$api-\u003esetEvaluationContext(new EvaluationContext('targetingKey', ['myGlobalKey' =\u003e 'myGlobalValue']));\n\n// add a value to the client context\n$client = $api-\u003egetClient();\n$client-\u003esetEvaluationContext(new EvaluationContext('targetingKey', ['myClientKey' =\u003e 'myClientValue']));\n\n// add a value to the invocation context\n$context = new EvaluationContext('targetingKey', ['myInvocationKey' =\u003e 'myInvocationValue']);\n\n$boolValue = $client-\u003egetBooleanValue('boolFlag', false, $context);\n```\n\n### Hooks\n\n[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.\nLook [here](https://openfeature.dev/ecosystem/?instant_search%5BrefinementList%5D%5Btype%5D%5B0%5D=Hook\u0026instant_search%5BrefinementList%5D%5Btechnology%5D%5B0%5D=php) for a complete list of available hooks.\nIf 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.\n\nOnce you've added a hook as a dependency, it can be registered at the global, client, or flag invocation level.\n\n```php\n// add a hook globally, to run on all evaluations\n$api = OpenFeatureAPI::getInstance();\n$api-\u003eaddHook(new ExampleGlobalHook());\n\n// add a hook on this client, to run on all evaluations made by this client\n$client = $api-\u003egetClient();\n$client-\u003eaddHook(new ExampleClientHook());\n\n// add a hook for this evaluation only\n$value = $client-\u003egetBooleanValue(\"boolFlag\", false, $context, new EvaluationOptions([new ExampleInvocationHook()]));\n```\n\n### Logging\n\nThe 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`.\nThe SDK makes use of a `LoggerAwareTrait` on several components, including the client for flag evaluation, the hook executor, and the global `OpenFeatureAPI` instance.\nWhen 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.\n\n\u003e ⚠️ 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!\n\n```php\n$api = OpenFeatureAPI::getInstance();\n\n$logger = new FancyLogger();\n\n$defaultLoggerClient = $api-\u003egetClient('default-logger');\n\n$api-\u003esetLogger(new CustomLogger());\n\n$customLoggerClient = $api-\u003egetClient('custom-logger');\n\n$overrideLoggerClient = $api-\u003egetClient('override-logger');\n$overrideLoggerClient-\u003esetLogger($logger);\n\n// now let's do some evaluations with these!\n\n$defaultLoggerClient-\u003egetBooleanValue('A', false);\n// uses default logger in the SDK\n\n$customLoggerClient-\u003egetBooleanValue('B', false);\n// uses the CustomLogger set in the API before the client was made\n\n$overrideLoggerClient-\u003egetBooleanValue('C', false);\n// uses the FancyLogger set directly on the client\n```\n\n### Named clients\n\nNamed 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).\n\n### Eventing\n\nEvents 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).\n\n### Shutdown\n\nA 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).\n\n## Extending\n\n### Develop a provider\n\nTo develop a provider, you need to create a new project and include the OpenFeature SDK as a dependency.\nThis 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.\nYou’ll then need to write the provider by implementing the `Provider` interface exported by the OpenFeature SDK.\n\n```php\ndeclare(strict_types=1);\n\nnamespace OpenFeature\\Example\\Providers;\n\nuse OpenFeature\\implementation\\common\\Metadata;\nuse OpenFeature\\interfaces\\common\\Metadata as IMetadata;\nuse OpenFeature\\interfaces\\flags\\EvaluationContext;\nuse OpenFeature\\interfaces\\hooks\\Hook;\nuse OpenFeature\\interfaces\\provider\\Provider;\nuse OpenFeature\\interfaces\\provider\\ResolutionDetails;\n\nclass ExampleProviderImplementation implements Provider\n{\n    public function setLogger(LoggerInterface $logger): void\n    {\n        $this-\u003elogger = $logger;\n\n        // or, utilize the OpenFeature\\interfaces\\common\\LoggerAwareTrait\n    }\n\n    /**\n     * @return Hook[]\n     */\n    public function getHooks(): array\n    {\n        return $this-\u003ehooks; // implement according to the OpenFeature specification\n    }\n\n    /**\n     * Returns the metadata for the current resource\n     */\n    public function getMetadata(): IMetadata\n    {\n        return new Metadata(self::class);\n    }\n\n    public function resolveBooleanValue(string $flagKey, bool $defaultValue, ?EvaluationContext $context = null): ResolutionDetails\n    {\n        // resolve some ResolutionDetails\n    }\n\n    public function resolveStringValue(string $flagKey, string $defaultValue, ?EvaluationContext $context = null): ResolutionDetails\n    {\n        // resolve some ResolutionDetails\n    }\n\n    public function resolveIntegerValue(string $flagKey, int $defaultValue, ?EvaluationContext $context = null): ResolutionDetails\n    {\n        // resolve some ResolutionDetails\n    }\n\n    public function resolveFloatValue(string $flagKey, float $defaultValue, ?EvaluationContext $context = null): ResolutionDetails\n    {\n        // resolve some ResolutionDetails\n    }\n\n    /**\n     * @inheritdoc\n     */\n    public function resolveObjectValue(string $flagKey, array $defaultValue, ?EvaluationContext $context = null): ResolutionDetails\n    {\n        // resolve some ResolutionDetails\n    }\n}\n```\n\nAs you can see, this ends up requiring some boilerplate to fulfill all of the functionality that a Provider expects.\nAnother option for implementing a provider is to utilize the AbstractProvider base class.\nThis provides some internally wiring and simple scaffolding so you can skip some of it and focus on what's most important: resolving feature flags!\n\n```php\ndeclare(strict_types=1);\n\nnamespace OpenFeature\\Example\\Providers;\n\nuse OpenFeature\\interfaces\\flags\\EvaluationContext;\nuse OpenFeature\\interfaces\\provider\\ResolutionDetails;\n\nclass ExampleProviderExtension extends AbstractProvider\n{\n    protected static string $NAME = self::class;\n\n    public function resolveBooleanValue(string $flagKey, bool $defaultValue, ?EvaluationContext $context = null): ResolutionDetailsInterface\n    {\n        // resolve some ResolutionDetails\n    }\n\n    public function resolveStringValue(string $flagKey, string $defaultValue, ?EvaluationContext $context = null): ResolutionDetailsInterface\n    {\n        // resolve some ResolutionDetails\n    }\n\n    public function resolveIntegerValue(string $flagKey, int $defaultValue, ?EvaluationContext $context = null): ResolutionDetailsInterface\n    {\n        // resolve some ResolutionDetails\n    }\n\n    public function resolveFloatValue(string $flagKey, float $defaultValue, ?EvaluationContext $context = null): ResolutionDetailsInterface\n    {\n        // resolve some ResolutionDetails\n    }\n\n    /**\n     * @inheritdoc\n     */\n    public function resolveObjectValue(string $flagKey, array $defaultValue, ?EvaluationContext $context = null): ResolutionDetailsInterface\n    {\n        // resolve some ResolutionDetails\n    }\n}\n```\n\n\u003e Built a new provider? [Let us know](https://github.com/open-feature/openfeature.dev/issues/new?assignees=\u0026labels=provider\u0026projects=\u0026template=document-provider.yaml\u0026title=%5BProvider%5D%3A+) so we can add it to the docs!\n\n### Develop a hook\n\nTo develop a hook, you need to create a new project and include the OpenFeature SDK as a dependency.\nThis 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.\nImplement your own hook by conforming to the `Hook interface`.\nTo satisfy the interface, all methods (`Before`/`After`/`Finally`/`Error`) need to be defined.\nTo avoid defining empty functions, make use of the `UnimplementedHook` struct (which already implements all the empty functions).\n\n```php\ndeclare(strict_types=1);\n\nnamespace OpenFeature\\Example\\Hooks;\n\nuse OpenFeature\\interfaces\\flags\\EvaluationContext;\nuse OpenFeature\\interfaces\\flags\\FlagValueType;\nuse OpenFeature\\interfaces\\hooks\\Hook;\nuse OpenFeature\\interfaces\\hooks\\HookContext;\nuse OpenFeature\\interfaces\\hooks\\HookHints;\nuse OpenFeature\\interfaces\\provider\\ResolutionDetails;\n\n\nclass ExampleStringHookImplementation implements Hook\n{\n    public function before(HookContext $context, HookHints $hints): ?EvaluationContext\n    {\n    }\n\n    public function after(HookContext $context, ResolutionDetails $details, HookHints $hints): void\n    {\n    }\n\n    public function error(HookContext $context, Throwable $error, HookHints $hints): void\n    {\n    }\n\n    public function finally(HookContext $context, HookHints $hints): void\n    {\n    }\n\n\n    public function supportsFlagValueType(string $flagValueType): bool\n    {\n        return $flagValueType === FlagValueType::STRING;\n    }\n}\n```\n\nYou can also make use of existing base classes for various types and behaviors.\nSuppose you want to make this same hook, and have no limitation around extending a base class, you could do the following:\n\n```php\ndeclare(strict_types=1);\n\nnamespace OpenFeature\\Example\\Hooks;\n\nuse OpenFeature\\implementation\\hooks\\StringHook;\nuse OpenFeature\\interfaces\\flags\\EvaluationContext;\nuse OpenFeature\\interfaces\\flags\\FlagValueType;\nuse OpenFeature\\interfaces\\hooks\\HookContext;\nuse OpenFeature\\interfaces\\hooks\\HookHints;\nuse OpenFeature\\interfaces\\provider\\ResolutionDetails;\n\n\nclass ExampleStringHookExtension extends StringHook\n{\n    public function before(HookContext $context, HookHints $hints): ?EvaluationContext\n    {\n    }\n\n    public function after(HookContext $context, ResolutionDetails $details, HookHints $hints): void\n    {\n    }\n\n    public function error(HookContext $context, Throwable $error, HookHints $hints): void\n    {\n    }\n\n    public function finally(HookContext $context, HookHints $hints): void\n    {\n    }\n}\n```\n\n\u003e Built a new hook? [Let us know](https://github.com/open-feature/openfeature.dev/issues/new?assignees=\u0026labels=hook\u0026projects=\u0026template=document-hook.yaml\u0026title=%5BHook%5D%3A+) so we can add it to the docs!\n\n\u003c!-- x-hide-in-docs-start --\u003e\n## ⭐️ Support the project\n\n- Give this repo a ⭐️!\n- Follow us on social media:\n  - Twitter: [@openfeature](https://twitter.com/openfeature)\n  - LinkedIn: [OpenFeature](https://www.linkedin.com/company/openfeature/)\n- Join us on [Slack](https://cloud-native.slack.com/archives/C0344AANLA1)\n- For more, check out our [community page](https://openfeature.dev/community/)\n\n## 🤝 Contributing\n\nInterested in contributing? Great, we'd love your help! To get started, take a look at the [CONTRIBUTING](CONTRIBUTING.md) guide.\n\n### Thanks to everyone who has already contributed\n\n\u003ca href=\"https://github.com/open-feature/php-sdk/graphs/contributors\"\u003e\n  \u003cimg src=\"https://contrib.rocks/image?repo=open-feature/php-sdk\" alt=\"Pictures of the folks who have contributed to the project\" /\u003e\n\u003c/a\u003e\n\n\n\nMade with [contrib.rocks](https://contrib.rocks).\n\u003c!-- x-hide-in-docs-end --\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopen-feature%2Fphp-sdk","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fopen-feature%2Fphp-sdk","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopen-feature%2Fphp-sdk/lists"}