From 9e63abd6977fff9149fd07fe0391d18b338482d6 Mon Sep 17 00:00:00 2001 From: Tom Carrio Date: Wed, 29 Nov 2023 23:13:43 -0500 Subject: [PATCH 01/10] feat: nix support with direnv and updated README Signed-off-by: Tom Carrio --- .envrc | 1 + README.md | 14 ++++++-- flake.lock | 26 +++++++++++++++ flake.nix | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 .envrc create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..8392d15 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake \ No newline at end of file diff --git a/README.md b/README.md index bfdf31e..a29335d 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![Specification](https://img.shields.io/static/v1?label=Specification&message=v0.5.1&color=yellow)](https://github.com/open-feature/spec/tree/v0.5.1) [![Latest Stable Version](http://poser.pugx.org/open-feature/sdk/v)](https://packagist.org/packages/open-feature/sdk) [![Total Downloads](http://poser.pugx.org/open-feature/sdk/downloads)](https://packagist.org/packages/open-feature/sdk) -![PHP 8.0+](https://img.shields.io/badge/php->=8.0-blue.svg) +![PHP 8.1+](https://img.shields.io/badge/php->=8.0-blue.svg) [![License](http://poser.pugx.org/open-feature/sdk/license)](https://packagist.org/packages/open-feature/sdk) [![OpenSSF Best Practices](https://bestpractices.coreinfrastructure.org/projects/6853/badge)](https://bestpractices.coreinfrastructure.org/projects/6853) @@ -108,7 +108,17 @@ The OpenFeature project maintains the [open-feature/php-sdk-contrib](https://git 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`. +#### asdf + +This package has a `.tool-versions` file for use with PHP version managers like `asdf`. + +#### Nix + +This package includes a `flake.nix` file which defines reproducible development shells powered by [Nix](https://nixos.org/). You can manually drop into a shell with `nix develop`, or provide a specific PHP minor version target with `nix develop .#php82`. + +#### direnv + +This package includes a `.envrc` file which automatically infers the usage of the default shell for the project, which is set to the minimum supported version of PHP. ### Installation and Dependencies diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..52633e0 --- /dev/null +++ b/flake.lock @@ -0,0 +1,26 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1701315746, + "narHash": "sha256-4HUEjLTe/DV2nT8Kj47v8aRNxSnQHwxXGZj5iZMTZCY=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e94cea6132d50ff4bb73845da902b8950d8620f5", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..fadcf88 --- /dev/null +++ b/flake.nix @@ -0,0 +1,94 @@ +{ + description = "OpenFeature PHP SDK Nix flake dev shells"; + + # Flake inputs + inputs.nixpkgs.url = "github:NixOS/nixpkgs"; + + # Flake outputs + outputs = { self, nixpkgs }: + let + # Systems supported + allSystems = [ + "x86_64-linux" # 64-bit Intel/ARM Linux + "aarch64-linux" # 64-bit AMD Linux + "x86_64-darwin" # 64-bit Intel/ARM macOS + "aarch64-darwin" # 64-bit Apple Silicon + ]; + + # Helper to provide system-specific attributes + nameValuePair = name: value: { inherit name value; }; + genAttrs = names: f: builtins.listToAttrs (map (n: nameValuePair n (f n)) names); + forAllSystems = f: genAttrs allSystems (system: f { + pkgs = import nixpkgs { + inherit system; + }; + }); + in + { + # Development environment output + devShells = forAllSystems ({ pkgs }: + let + coreShellPackages = [ + pkgs.zsh + ]; + coreDevPackages = [ + pkgs.git + ]; + corePhpPackages = [ + pkgs.libpng + ]; + php81Packages = [ + pkgs.php81 + pkgs.php81.packages.composer + ]; + php82Packages = [ + pkgs.php82 + pkgs.php82.packages.composer + ]; + php83Packages = [ + pkgs.php83 + pkgs.php83.packages.composer + ]; + emptyStr = ""; + shellHookCommandFactory = { git ? true, php ? false, node ? false, yarn ? false, pnpm ? false, python ? false, bun ? false }: '' + echo $ Started devenv shell for OpenFeature PHP-SDK with $PHP_VERSION + echo + ${if git then ''git --version'' else emptyStr} + ${if php then ''php --version'' else emptyStr} + echo + ''; + phpShellHookCommand = shellHookCommandFactory { php = true; }; + in rec + { + php81 = pkgs.mkShell { + packages = coreShellPackages ++ coreDevPackages ++ corePhpPackages ++ php81Packages; + + PHP_VERSION = "PHP81"; + + shellHook = phpShellHookCommand; + }; + + php82 = pkgs.mkShell { + packages = coreShellPackages ++ coreDevPackages ++ corePhpPackages ++ php82Packages; + + PHP_VERSION = "PHP82"; + + shellHook = phpShellHookCommand; + }; + + php83 = pkgs.mkShell { + packages = coreShellPackages ++ coreDevPackages ++ corePhpPackages ++ php83Packages; + + PHP_VERSION = "PHP83"; + + shellHook = phpShellHookCommand; + }; + + # Default aliases, uses minimum supported PHP version + php = php81; + + default = php; + } + ); + }; +} \ No newline at end of file From 9ebc58cbd3067d111cda9279031e32b9dd0b8f8f Mon Sep 17 00:00:00 2001 From: Tom Carrio Date: Wed, 29 Nov 2023 23:13:58 -0500 Subject: [PATCH 02/10] chore: bump PHP version in .tool-versions Signed-off-by: Tom Carrio --- .tool-versions | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.tool-versions b/.tool-versions index 89eae35..19a822a 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,3 +1,3 @@ -php 8.2.12 +php 8.3.0 +# php 8.2.12 # php 8.1.11 -# php 8.0.24 \ No newline at end of file From e72513978caf79d0349931179e3399967f3d5c72 Mon Sep 17 00:00:00 2001 From: Tom Carrio Date: Wed, 29 Nov 2023 23:14:33 -0500 Subject: [PATCH 03/10] chore: FIXUP for direnv and nix commit Signed-off-by: Tom Carrio --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 0199447..bea6a4d 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,4 @@ /features/*.feature -/.devenv* \ No newline at end of file +/.direnv \ No newline at end of file From ab59e384ec706364f079628cdb6cefe3add8f0be Mon Sep 17 00:00:00 2001 From: Tom Carrio Date: Wed, 29 Nov 2023 23:16:20 -0500 Subject: [PATCH 04/10] chore: upgrade min version supported to PHP 8.1 Signed-off-by: Tom Carrio --- CONTRIBUTING.md | 4 ++-- composer.json | 6 +++--- integration/composer.json | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 91dccbe..af5e9b6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,11 +4,11 @@ ### System Requirements -PHP 8+ is required. +PHP 8.1+ is required. ### Compilation target(s) -We target compatibility with PHP versions 8.0, 8.1, and 8.2. +We target compatibility with supported versions of PHP. Currently, this includes PHP versions 8.1, 8.2, and 8.3. ### Installation and Dependencies diff --git a/composer.json b/composer.json index 2ef5a88..f6f1a4c 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ } ], "require": { - "php": "^8", + "php": "^8.1", "myclabs/php-enum": "^1.8", "psr/log": "^2.0 || ^3.0" }, @@ -56,10 +56,10 @@ }, "config": { "allow-plugins": { - "phpstan/extension-installer": true, + "captainhook/plugin-composer": true, "dealerdirect/phpcodesniffer-composer-installer": true, "ergebnis/composer-normalize": true, - "captainhook/plugin-composer": true, + "phpstan/extension-installer": true, "ramsey/composer-repl": true }, "sort-packages": true diff --git a/integration/composer.json b/integration/composer.json index 906f60d..b39c551 100644 --- a/integration/composer.json +++ b/integration/composer.json @@ -15,7 +15,7 @@ } ], "require": { - "php": "^8", + "php": "^8.1", "open-feature/sdk": "^2.0.0", "open-feature/flagd-provider": "^0.7.0", "guzzlehttp/guzzle": "^7.5", From 5cb8e4722f827e91ea90713cf2c334a8708d7823 Mon Sep 17 00:00:00 2001 From: Tom Carrio Date: Wed, 29 Nov 2023 23:53:07 -0500 Subject: [PATCH 05/10] refactor: array unpacking native enums and constructor promotion and readonly properties Signed-off-by: Tom Carrio --- src/OpenFeatureAPI.php | 3 +- src/OpenFeatureClient.php | 32 ++++++++----------- src/implementation/common/Metadata.php | 5 +-- src/implementation/flags/Attributes.php | 4 +-- .../flags/EvaluationContext.php | 11 +++---- .../flags/EvaluationDetails.php | 1 - .../flags/EvaluationOptions.php | 4 +-- src/implementation/hooks/HookHints.php | 17 ++++------ .../provider/AbstractProvider.php | 25 +++------------ src/implementation/provider/Reason.php | 13 ++++---- src/interfaces/flags/AttributeType.php | 29 +++++++++++++---- src/interfaces/flags/FlagValueType.php | 29 +++++++++++++---- 12 files changed, 83 insertions(+), 90 deletions(-) diff --git a/src/OpenFeatureAPI.php b/src/OpenFeatureAPI.php index 8971509..cca9c34 100644 --- a/src/OpenFeatureAPI.php +++ b/src/OpenFeatureAPI.php @@ -16,7 +16,6 @@ use Psr\Log\LoggerAwareInterface; use Throwable; -use function array_merge; use function is_null; final class OpenFeatureAPI implements API, LoggerAwareInterface @@ -130,7 +129,7 @@ public function getHooks(): array */ public function addHooks(Hook ...$hooks): void { - $this->hooks = array_merge($this->hooks, $hooks); + $this->hooks = [...$this->hooks, ...$hooks]; } public function clearHooks(): void diff --git a/src/OpenFeatureClient.php b/src/OpenFeatureClient.php index 4181a60..018e8e8 100644 --- a/src/OpenFeatureClient.php +++ b/src/OpenFeatureClient.php @@ -36,7 +36,6 @@ use Psr\Log\LoggerAwareInterface; use Throwable; -use function array_merge; use function array_reverse; use function sprintf; @@ -45,9 +44,6 @@ class OpenFeatureClient implements Client, LoggerAwareInterface use HooksAwareTrait; use LoggerAwareTrait; - private API $api; - private string $name; - private string $version; private ?EvaluationContextInterface $evaluationContext = null; /** @@ -57,11 +53,11 @@ class OpenFeatureClient implements Client, LoggerAwareInterface * @param string $name Name of the client (used by observability tools). * @param string $version Version of the client (used by observability tools). */ - public function __construct(API $api, string $name, string $version) - { - $this->api = $api; - $this->name = $name; - $this->version = $version; + public function __construct( + private readonly API $api, + private readonly string $name, + private readonly string $version, + ) { $this->hooks = []; } @@ -102,7 +98,7 @@ public function setEvaluationContext(EvaluationContextInterface $context): void */ public function addHooks(Hook ...$hooks): void { - $this->hooks = array_merge($this->hooks, $hooks); + $this->hooks = [...$this->hooks, ...$hooks]; } /** @@ -326,14 +322,14 @@ private function evaluateFlag( // after: Provider, Invocation, Client, API // error (if applicable): Provider, Invocation, Client, API // finally: Provider, Invocation, Client, API - $mergedBeforeHooks = array_merge( - $api->getHooks(), - $this->getHooks(), - $options->getHooks(), - $provider->getHooks(), - ); - - $mergedRemainingHooks = array_reverse(array_merge([], $mergedBeforeHooks)); + $mergedBeforeHooks = [ + ...$api->getHooks(), + ...$this->getHooks(), + ...$options->getHooks(), + ...$provider->getHooks(), + ]; + + $mergedRemainingHooks = array_reverse([...$mergedBeforeHooks]); try { $contextFromBeforeHook = $hookExecutor->beforeHooks($flagValueType, $hookContext, $mergedBeforeHooks, $hookHints); diff --git a/src/implementation/common/Metadata.php b/src/implementation/common/Metadata.php index 6759b3a..139ee1d 100644 --- a/src/implementation/common/Metadata.php +++ b/src/implementation/common/Metadata.php @@ -8,11 +8,8 @@ class Metadata implements MetadataInterface { - private string $name; - - public function __construct(string $name) + public function __construct(private string $name) { - $this->name = $name; } public function getName(): string diff --git a/src/implementation/flags/Attributes.php b/src/implementation/flags/Attributes.php index 9676c4e..886bfd7 100644 --- a/src/implementation/flags/Attributes.php +++ b/src/implementation/flags/Attributes.php @@ -8,8 +8,6 @@ use OpenFeature\implementation\common\ArrayHelper; use OpenFeature\interfaces\flags\Attributes as AttributesInterface; -use function array_merge; - class Attributes implements AttributesInterface { /** @var Array $attributesMap */ @@ -50,6 +48,6 @@ public function get(string $key): bool | string | int | float | DateTime | array */ public function toArray(): array { - return array_merge([], $this->attributesMap); + return [...$this->attributesMap]; } } diff --git a/src/implementation/flags/EvaluationContext.php b/src/implementation/flags/EvaluationContext.php index 5af458f..ec9a956 100644 --- a/src/implementation/flags/EvaluationContext.php +++ b/src/implementation/flags/EvaluationContext.php @@ -11,13 +11,10 @@ class EvaluationContext implements EvaluationContextInterface { use EvaluationContextMerger; - private ?string $targetingKey; - protected AttributesInterface $attributes; - - public function __construct(?string $targetingKey = null, ?AttributesInterface $attributes = null) - { - $this->targetingKey = $targetingKey; - $this->attributes = $attributes ?? new Attributes(); + public function __construct( + private ?string $targetingKey = null, + protected ?AttributesInterface $attributes = new Attributes(), + ) { } public function getTargetingKey(): ?string diff --git a/src/implementation/flags/EvaluationDetails.php b/src/implementation/flags/EvaluationDetails.php index d96c46b..a0f81ca 100644 --- a/src/implementation/flags/EvaluationDetails.php +++ b/src/implementation/flags/EvaluationDetails.php @@ -20,7 +20,6 @@ class EvaluationDetails implements EvaluationDetailsInterface public function __construct() { - $this->value = null; } public function getFlagKey(): string diff --git a/src/implementation/flags/EvaluationOptions.php b/src/implementation/flags/EvaluationOptions.php index 72a8c89..965b345 100644 --- a/src/implementation/flags/EvaluationOptions.php +++ b/src/implementation/flags/EvaluationOptions.php @@ -13,12 +13,10 @@ class EvaluationOptions implements EvaluationOptionsInterface { use HooksAwareTrait; - private ?HookHints $hookHints; - /** * @param Hook[] $hooks */ - public function __construct(array $hooks = [], ?HookHints $hookHints = null) + public function __construct(array $hooks = [], private ?HookHints $hookHints = null) { $this->setHooks($hooks); $this->hookHints = $hookHints; diff --git a/src/implementation/hooks/HookHints.php b/src/implementation/hooks/HookHints.php index 2385d57..ee8bdff 100644 --- a/src/implementation/hooks/HookHints.php +++ b/src/implementation/hooks/HookHints.php @@ -12,8 +12,13 @@ class HookHints implements HookHintsInterface { - /** @var Array $hints */ - private array $hints = []; + /** + * @param Array $hints + */ + public function __construct(private readonly array $hints = []) + { + $this->hints = $hints; + } /** * @return bool|string|int|float|DateTime|mixed[]|null @@ -34,12 +39,4 @@ public function keys(): array { return array_keys($this->hints); } - - /** - * @param Array $hints - */ - public function __construct(array $hints = []) - { - $this->hints = $hints; - } } diff --git a/src/implementation/provider/AbstractProvider.php b/src/implementation/provider/AbstractProvider.php index 3598d3f..ec8cc2b 100644 --- a/src/implementation/provider/AbstractProvider.php +++ b/src/implementation/provider/AbstractProvider.php @@ -7,20 +7,19 @@ use OpenFeature\implementation\common\Metadata; use OpenFeature\interfaces\common\Metadata as MetadataInterface; use OpenFeature\interfaces\flags\EvaluationContext; -use OpenFeature\interfaces\hooks\Hook; +use OpenFeature\interfaces\hooks\HooksAware; +use OpenFeature\interfaces\hooks\HooksAwareTrait; use OpenFeature\interfaces\provider\Provider; use OpenFeature\interfaces\provider\ResolutionDetails as ResolutionDetailsInterface; use Psr\Log\LoggerAwareTrait; -abstract class AbstractProvider implements Provider +abstract class AbstractProvider implements HooksAware, Provider { + use HooksAwareTrait; use LoggerAwareTrait; protected static string $NAME = 'AbstractProvider'; - /** @var Hook[] $hooks */ - private array $hooks = []; - public function getMetadata(): MetadataInterface { return new Metadata(self::$NAME); @@ -38,20 +37,4 @@ abstract public function resolveFloatValue(string $flagKey, float $defaultValue, * @param mixed[] $defaultValue */ abstract public function resolveObjectValue(string $flagKey, array $defaultValue, ?EvaluationContext $context = null): ResolutionDetailsInterface; - - /** - * @return Hook[] - */ - public function getHooks(): array - { - return $this->hooks; - } - - /** - * @param Hook[] $hooks - */ - public function setHooks(array $hooks): void - { - $this->hooks = $hooks; - } } diff --git a/src/implementation/provider/Reason.php b/src/implementation/provider/Reason.php index a867e7b..2905b78 100644 --- a/src/implementation/provider/Reason.php +++ b/src/implementation/provider/Reason.php @@ -4,13 +4,12 @@ namespace OpenFeature\implementation\provider; -/** - * A pseudo-enumerator to support PHP 7.x - * - * TODO: Bump to PHP 8.x + support after EOL with - * native enum implementation - */ -class Reason +enum Reason: string { + case Error = 'Error'; + + /** + * @deprecated prefer enum value over const + */ public const ERROR = 'Error'; } diff --git a/src/interfaces/flags/AttributeType.php b/src/interfaces/flags/AttributeType.php index e0f08f1..f954b32 100644 --- a/src/interfaces/flags/AttributeType.php +++ b/src/interfaces/flags/AttributeType.php @@ -4,17 +4,32 @@ namespace OpenFeature\interfaces\flags; -/** - * A pseudo-enumerator to support PHP 7.x - * - * TODO: Bump to PHP 8.x + support after EOL with - * native enum implementation - */ -class AttributeType +enum AttributeType: string { + case String = 'STRING'; + case Integer = 'INTEGER'; + case Float = 'FLOAT'; + case Structure = 'STRUCTURE'; + case Boolean = 'BOOLEAN'; + + /** + * @deprecated prefer enum value over const + */ public const STRING = 'STRING'; + /** + * @deprecated prefer enum value over const + */ public const INTEGER = 'INTEGER'; + /** + * @deprecated prefer enum value over const + */ public const FLOAT = 'FLOAT'; + /** + * @deprecated prefer enum value over const + */ public const STRUCTURE = 'STRUCTURE'; + /** + * @deprecated prefer enum value over const + */ public const BOOLEAN = 'BOOLEAN'; } diff --git a/src/interfaces/flags/FlagValueType.php b/src/interfaces/flags/FlagValueType.php index a5eca98..9108d77 100644 --- a/src/interfaces/flags/FlagValueType.php +++ b/src/interfaces/flags/FlagValueType.php @@ -4,17 +4,32 @@ namespace OpenFeature\interfaces\flags; -/** - * A pseudo-enumerator to support PHP 7.x - * - * TODO: Bump to PHP 8.x + support after EOL with - * native enum implementation - */ -class FlagValueType +enum FlagValueType: string { + case String = 'STRING'; + case Integer = 'INTEGER'; + case Float = 'FLOAT'; + case Object = 'OBJECT'; + case Boolean = 'BOOLEAN'; + + /** + * @deprecated prefer enum value over const + */ public const STRING = 'STRING'; + /** + * @deprecated prefer enum value over const + */ public const INTEGER = 'INTEGER'; + /** + * @deprecated prefer enum value over const + */ public const FLOAT = 'FLOAT'; + /** + * @deprecated prefer enum value over const + */ public const OBJECT = 'OBJECT'; + /** + * @deprecated prefer enum value over const + */ public const BOOLEAN = 'BOOLEAN'; } From a80fdb18a976c791e3ff84c035deb70dac70be6a Mon Sep 17 00:00:00 2001 From: Tom Carrio Date: Wed, 29 Nov 2023 23:55:30 -0500 Subject: [PATCH 06/10] fix: phpstan issues Signed-off-by: Tom Carrio --- src/implementation/flags/EvaluationContext.php | 2 +- src/implementation/hooks/HookHints.php | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/implementation/flags/EvaluationContext.php b/src/implementation/flags/EvaluationContext.php index ec9a956..20eaaee 100644 --- a/src/implementation/flags/EvaluationContext.php +++ b/src/implementation/flags/EvaluationContext.php @@ -13,7 +13,7 @@ class EvaluationContext implements EvaluationContextInterface public function __construct( private ?string $targetingKey = null, - protected ?AttributesInterface $attributes = new Attributes(), + protected readonly AttributesInterface $attributes = new Attributes(), ) { } diff --git a/src/implementation/hooks/HookHints.php b/src/implementation/hooks/HookHints.php index ee8bdff..a237777 100644 --- a/src/implementation/hooks/HookHints.php +++ b/src/implementation/hooks/HookHints.php @@ -17,7 +17,6 @@ class HookHints implements HookHintsInterface */ public function __construct(private readonly array $hints = []) { - $this->hints = $hints; } /** From 731393ba7a0a44b6d61962e1f75610b0ee9c6004 Mon Sep 17 00:00:00 2001 From: Tom Carrio Date: Thu, 30 Nov 2023 00:40:33 -0500 Subject: [PATCH 07/10] refactor: static analysis updates and plenty of enum changes Signed-off-by: Tom Carrio --- .../features/bootstrap/FeatureContext.php | 52 ++++++++-------- src/OpenFeatureClient.php | 60 ++++++++++--------- .../common/ValueTypeValidator.php | 24 +++----- src/implementation/hooks/AbstractHook.php | 3 +- .../hooks/AbstractHookContext.php | 15 ++++- src/implementation/hooks/BooleanHook.php | 4 +- src/implementation/hooks/FloatHook.php | 4 +- .../hooks/HookContextBuilder.php | 3 +- .../hooks/HookContextFactory.php | 3 +- src/implementation/hooks/HookExecutor.php | 9 +-- .../hooks/ImmutableHookContext.php | 3 +- src/implementation/hooks/IntegerHook.php | 4 +- .../hooks/MutableHookContext.php | 3 +- src/implementation/hooks/ObjectHook.php | 4 +- src/implementation/hooks/StringHook.php | 4 +- src/interfaces/hooks/Hook.php | 5 +- src/interfaces/hooks/HookContext.php | 3 +- src/interfaces/hooks/MutableHookContext.php | 3 +- tests/TestHook.php | 37 +----------- tests/unit/BaseHooksTest.php | 60 +++++++++---------- tests/unit/HookContextBuilderTest.php | 5 +- tests/unit/HooksTest.php | 30 +++++----- tests/unit/NoOpClientTest.php | 3 +- 23 files changed, 164 insertions(+), 177 deletions(-) diff --git a/integration/features/bootstrap/FeatureContext.php b/integration/features/bootstrap/FeatureContext.php index 78040bf..570f9a2 100644 --- a/integration/features/bootstrap/FeatureContext.php +++ b/integration/features/bootstrap/FeatureContext.php @@ -82,7 +82,7 @@ public function aProviderIsRegisteredWithCacheDisabled() */ public function aBooleanFlagWithKeyIsEvaluatedWithDefaultValue(string $flagKey, bool $defaultValue) { - $this->flagType = FlagValueType::BOOLEAN; + $this->flagType = FlagValueType::Boolean; $this->inputFlagKey = $flagKey; $this->inputFlagDefaultValue = $defaultValue; } @@ -103,7 +103,7 @@ public function theResolvedBooleanValueShouldBe(bool $resolvedValue) */ public function aStringFlagWithKeyIsEvaluatedWithDefaultValue(string $flagKey, string $defaultValue) { - $this->flagType = FlagValueType::STRING; + $this->flagType = FlagValueType::String; $this->inputFlagKey = $flagKey; $this->inputFlagDefaultValue = $defaultValue; } @@ -124,7 +124,7 @@ public function theResolvedStringValueShouldBe(string $resolvedValue) */ public function anIntegerFlagWithKeyIsEvaluatedWithDefaultValue(string $flagKey, int $defaultValue) { - $this->flagType = FlagValueType::INTEGER; + $this->flagType = FlagValueType::Integer; $this->inputFlagKey = $flagKey; $this->inputFlagDefaultValue = $defaultValue; print_r("Setting integer...\n"); @@ -146,7 +146,7 @@ public function theResolvedIntegerValueShouldBe(int $resolvedValue) */ public function aFloatFlagWithKeyIsEvaluatedWithDefaultValue(string $flagKey, float $defaultValue) { - $this->flagType = FlagValueType::FLOAT; + $this->flagType = FlagValueType::Float; $this->inputFlagKey = $flagKey; $this->inputFlagDefaultValue = $defaultValue; } @@ -167,7 +167,7 @@ public function theResolvedFloatValueShouldBe(float $resolvedValue) */ public function anObjectFlagWithKeyIsEvaluatedWithANullDefaultValue(string $flagKey, mixed $defaultValue) { - $this->flagType = FlagValueType::OBJECT; + $this->flagType = FlagValueType::Object; $this->inputFlagKey = $flagKey; $this->inputFlagDefaultValue = $defaultValue; } @@ -192,7 +192,7 @@ public function theResolvedObjectValueShouldBeContainFieldsAndWithValuesAndRespe */ public function aBooleanFlagWithKeyIsEvaluatedWithDetailsAndDefaultValue(string $flagKey, bool $defaultValue) { - $this->flagType = FlagValueType::BOOLEAN; + $this->flagType = FlagValueType::Boolean; $this->inputFlagKey = $flagKey; $this->inputFlagDefaultValue = $defaultValue; } @@ -214,7 +214,7 @@ public function theResolvedBooleanDetailsValueShouldBeTheVariantShouldBeAndTheRe */ public function aStringFlagWithKeyIsEvaluatedWithDetailsAndDefaultValue(string $flagKey, string $defaultValue) { - $this->flagType = FlagValueType::STRING; + $this->flagType = FlagValueType::String; $this->inputFlagKey = $flagKey; $this->inputFlagDefaultValue = $defaultValue; } @@ -236,7 +236,7 @@ public function theResolvedStringDetailsValueShouldBeTheVariantShouldBeAndTheRea */ public function anIntegerFlagWithKeyIsEvaluatedWithDetailsAndDefaultValue(string $flagKey, int $defaultValue) { - $this->flagType = FlagValueType::INTEGER; + $this->flagType = FlagValueType::Integer; $this->inputFlagKey = $flagKey; $this->inputFlagDefaultValue = $defaultValue; } @@ -258,7 +258,7 @@ public function theResolvedIntegerDetailsValueShouldBeTheVariantShouldBeAndTheRe */ public function aFloatFlagWithKeyIsEvaluatedWithDetailsAndDefaultValue(string $flagKey, float $defaultValue) { - $this->flagType = FlagValueType::FLOAT; + $this->flagType = FlagValueType::Float; $this->inputFlagKey = $flagKey; $this->inputFlagDefaultValue = $defaultValue; } @@ -280,7 +280,7 @@ public function theResolvedFloatDetailsValueShouldBeTheVariantShouldBeAndTheReas */ public function anObjectFlagWithKeyIsEvaluatedWithDetailsAndANullDefaultValue(string $flagKey, mixed $defaultValue) { - $this->flagType = FlagValueType::OBJECT; + $this->flagType = FlagValueType::Object; $this->inputFlagKey = $flagKey; $this->inputFlagDefaultValue = $defaultValue; } @@ -409,7 +409,7 @@ public function theReasonShouldIndicateAnErrorAndTheErrorCodeShouldIndicateAMiss */ public function aStringFlagWithKeyIsEvaluatedAsAnIntegerWithDetailsAndADefaultValue(string $flagKey, int $defaultValue) { - $this->flagType = FlagValueType::INTEGER; + $this->flagType = FlagValueType::Integer; $this->inputFlagKey = $flagKey; $this->inputFlagDefaultValue = $defaultValue; } @@ -447,23 +447,23 @@ private function calculateValue() { $value = null; switch ($this->flagType) { - case FlagValueType::BOOLEAN: + case FlagValueType::Boolean: $value = $this->client->getBooleanValue($this->inputFlagKey, $this->inputFlagDefaultValue, $this->inputContext, $this->inputOptions); break; - case FlagValueType::FLOAT: + case FlagValueType::Float: $value = $this->client->getFloatValue($this->inputFlagKey, $this->inputFlagDefaultValue, $this->inputContext, $this->inputOptions); break; - case FlagValueType::INTEGER: + case FlagValueType::Integer: $value = $this->client->getIntegerValue($this->inputFlagKey, $this->inputFlagDefaultValue, $this->inputContext, $this->inputOptions); break; - case FlagValueType::OBJECT: + case FlagValueType::Object: $value = $this->client->getObjectValue($this->inputFlagKey, $this->inputFlagDefaultValue, $this->inputContext, $this->inputOptions); break; - case FlagValueType::STRING: + case FlagValueType::String: $value = $this->client->getStringValue($this->inputFlagKey, $this->inputFlagDefaultValue, $this->inputContext, $this->inputOptions); break; @@ -479,23 +479,23 @@ private function calculateDetails(): EvaluationDetails { $details = null; switch ($this->flagType) { - case FlagValueType::BOOLEAN: + case FlagValueType::Boolean: $details = $this->client->getBooleanDetails($this->inputFlagKey, $this->inputFlagDefaultValue, $this->inputContext, $this->inputOptions); break; - case FlagValueType::FLOAT: + case FlagValueType::Float: $details = $this->client->getFloatDetails($this->inputFlagKey, $this->inputFlagDefaultValue, $this->inputContext, $this->inputOptions); break; - case FlagValueType::INTEGER: + case FlagValueType::Integer: $details = $this->client->getIntegerDetails($this->inputFlagKey, $this->inputFlagDefaultValue, $this->inputContext, $this->inputOptions); break; - case FlagValueType::OBJECT: + case FlagValueType::Object: $details = $this->client->getObjectDetails($this->inputFlagKey, $this->inputFlagDefaultValue, $this->inputContext, $this->inputOptions); break; - case FlagValueType::STRING: + case FlagValueType::String: $details = $this->client->getStringDetails($this->inputFlagKey, $this->inputFlagDefaultValue, $this->inputContext, $this->inputOptions); break; @@ -518,23 +518,23 @@ private function setFlagTypeIfNullByValue(mixed $value): void private function getFlagTypeOf(mixed $value): ?string { if (is_string($value)) { - return FlagValueType::STRING; + return FlagValueType::String; } if (is_array($value)) { - return FlagValueType::OBJECT; + return FlagValueType::Object; } if (is_float($value)) { - return FlagValueType::FLOAT; + return FlagValueType::Float; } if (is_int($value)) { - return FlagValueType::INTEGER; + return FlagValueType::Integer; } if (is_bool($value)) { - return FlagValueType::BOOLEAN; + return FlagValueType::Boolean; } } diff --git a/src/OpenFeatureClient.php b/src/OpenFeatureClient.php index 018e8e8..4ddefbf 100644 --- a/src/OpenFeatureClient.php +++ b/src/OpenFeatureClient.php @@ -7,7 +7,6 @@ use DateTime; use OpenFeature\implementation\common\Metadata; use OpenFeature\implementation\common\ValueTypeValidator; -use OpenFeature\implementation\errors\FlagValueTypeError; use OpenFeature\implementation\errors\InvalidResolutionValueError; use OpenFeature\implementation\flags\EvaluationContext; use OpenFeature\implementation\flags\EvaluationDetailsBuilder; @@ -142,7 +141,7 @@ public function getBooleanValue(string $flagKey, bool $defaultValue, ?Evaluation */ public function getBooleanDetails(string $flagKey, bool $defaultValue, ?EvaluationContextInterface $context = null, ?EvaluationOptionsInterface $options = null): EvaluationDetailsInterface { - return $this->evaluateFlag(FlagValueType::BOOLEAN, $flagKey, $defaultValue, $context, $options); + return $this->evaluateFlag(FlagValueType::Boolean, $flagKey, $defaultValue, $context, $options); } /** @@ -171,7 +170,7 @@ public function getStringValue(string $flagKey, string $defaultValue, ?Evaluatio */ public function getStringDetails(string $flagKey, string $defaultValue, ?EvaluationContextInterface $context = null, ?EvaluationOptionsInterface $options = null): EvaluationDetailsInterface { - return $this->evaluateFlag(FlagValueType::STRING, $flagKey, $defaultValue, $context, $options); + return $this->evaluateFlag(FlagValueType::String, $flagKey, $defaultValue, $context, $options); } /** @@ -205,7 +204,7 @@ public function getIntegerValue(string $flagKey, int $defaultValue, ?EvaluationC */ public function getIntegerDetails(string $flagKey, int $defaultValue, ?EvaluationContextInterface $context = null, ?EvaluationOptionsInterface $options = null): EvaluationDetailsInterface { - return $this->evaluateFlag(FlagValueType::INTEGER, $flagKey, $defaultValue, $context, $options); + return $this->evaluateFlag(FlagValueType::Integer, $flagKey, $defaultValue, $context, $options); } /** @@ -239,7 +238,7 @@ public function getFloatValue(string $flagKey, float $defaultValue, ?EvaluationC */ public function getFloatDetails(string $flagKey, float $defaultValue, ?EvaluationContextInterface $context = null, ?EvaluationOptionsInterface $options = null): EvaluationDetailsInterface { - return $this->evaluateFlag(FlagValueType::FLOAT, $flagKey, $defaultValue, $context, $options); + return $this->evaluateFlag(FlagValueType::Float, $flagKey, $defaultValue, $context, $options); } /** @@ -274,7 +273,7 @@ public function getObjectValue(string $flagKey, $defaultValue, ?EvaluationContex */ public function getObjectDetails(string $flagKey, $defaultValue, ?EvaluationContextInterface $context = null, ?EvaluationOptionsInterface $options = null): EvaluationDetailsInterface { - return $this->evaluateFlag(FlagValueType::OBJECT, $flagKey, $defaultValue, $context, $options); + return $this->evaluateFlag(FlagValueType::Object, $flagKey, $defaultValue, $context, $options); } /** @@ -286,7 +285,7 @@ public function getObjectDetails(string $flagKey, $defaultValue, ?EvaluationCont * @param bool|string|int|float|DateTime|mixed[]|null $defaultValue */ private function evaluateFlag( - string $flagValueType, + FlagValueType $flagValueType, string $flagKey, bool | string | int | float | DateTime | array | null $defaultValue, ?EvaluationContextInterface $invocationContext = null, @@ -353,7 +352,7 @@ private function evaluateFlag( ); if (!$resolutionDetails->getError() && !ValueTypeValidator::is($flagValueType, $resolutionDetails->getValue())) { - throw new InvalidResolutionValueError($flagValueType); + throw new InvalidResolutionValueError($flagValueType->value); } $details = EvaluationDetailsFactory::fromResolution($flagKey, $resolutionDetails); @@ -385,40 +384,45 @@ private function evaluateFlag( } private function createProviderEvaluation( - string $type, + FlagValueType $type, string $key, - mixed $defaultValue, + bool | string | int | float | array | null $defaultValue, Provider $provider, EvaluationContextInterface $context, ): ResolutionDetails { - switch ($type) { - case FlagValueType::BOOLEAN: - /** @var bool $defaultValue */ + switch ($type->value) { + case FlagValueType::Boolean->value: + /** @var bool $defaultValue */; $defaultValue = $defaultValue; + $resolver = $provider->resolveBooleanValue(...); - return $provider->resolveBooleanValue($key, $defaultValue, $context); - case FlagValueType::STRING: - /** @var string $defaultValue */ + break; + case FlagValueType::String->value: + /** @var string $defaultValue */; $defaultValue = $defaultValue; + $resolver = $provider->resolveStringValue(...); - return $provider->resolveStringValue($key, $defaultValue, $context); - case FlagValueType::INTEGER: - /** @var int $defaultValue */ + break; + case FlagValueType::Integer->value: + /** @var int $defaultValue */; $defaultValue = $defaultValue; + $resolver = $provider->resolveIntegerValue(...); - return $provider->resolveIntegerValue($key, $defaultValue, $context); - case FlagValueType::FLOAT: - /** @var float $defaultValue */ + break; + case FlagValueType::Float->value: + /** @var float $defaultValue */; $defaultValue = $defaultValue; + $resolver = $provider->resolveFloatValue(...); - return $provider->resolveFloatValue($key, $defaultValue, $context); - case FlagValueType::OBJECT: - /** @var mixed[] $defaultValue */ + break; + case FlagValueType::Object->value: + /** @var object $defaultValue */; $defaultValue = $defaultValue; + $resolver = $provider->resolveObjectValue(...); - return $provider->resolveObjectValue($key, $defaultValue, $context); - default: - throw new FlagValueTypeError($type); + break; } + + return $resolver($key, $defaultValue, $context); } } diff --git a/src/implementation/common/ValueTypeValidator.php b/src/implementation/common/ValueTypeValidator.php index 955f468..f4f1353 100644 --- a/src/implementation/common/ValueTypeValidator.php +++ b/src/implementation/common/ValueTypeValidator.php @@ -74,21 +74,15 @@ public static function isDateTime(mixed $value): bool /** * Validates whether the value is valid for the given type */ - public static function is(string $type, mixed $value): bool + public static function is(FlagValueType $type, mixed $value): bool { - switch ($type) { - case FlagValueType::BOOLEAN: - return self::isBoolean($value); - case FlagValueType::FLOAT: - return self::isFloat($value); - case FlagValueType::INTEGER: - return self::isInteger($value); - case FlagValueType::STRING: - return self::isString($value); - case FlagValueType::OBJECT: - return self::isStructure($value) || self::isArray($value); - default: - return false; - } + return match ($type) { + FlagValueType::Boolean => self::isBoolean($value), + FlagValueType::Float => self::isFloat($value), + FlagValueType::Integer => self::isInteger($value), + FlagValueType::String => self::isString($value), + FlagValueType::Object => self::isStructure($value) || self::isArray($value), + default => false, + }; } } diff --git a/src/implementation/hooks/AbstractHook.php b/src/implementation/hooks/AbstractHook.php index 4303d9c..ead670d 100644 --- a/src/implementation/hooks/AbstractHook.php +++ b/src/implementation/hooks/AbstractHook.php @@ -5,6 +5,7 @@ namespace OpenFeature\implementation\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; @@ -21,5 +22,5 @@ abstract public function error(HookContext $context, Throwable $error, HookHints abstract public function finally(HookContext $context, HookHints $hints): void; - abstract public function supportsFlagValueType(string $flagValueType): bool; + abstract public function supportsFlagValueType(FlagValueType $flagValueType): bool; } diff --git a/src/implementation/hooks/AbstractHookContext.php b/src/implementation/hooks/AbstractHookContext.php index 39fbeba..0a1cefc 100644 --- a/src/implementation/hooks/AbstractHookContext.php +++ b/src/implementation/hooks/AbstractHookContext.php @@ -5,24 +5,29 @@ namespace OpenFeature\implementation\hooks; use DateTime; +use Exception; use OpenFeature\implementation\common\Metadata; use OpenFeature\implementation\flags\EvaluationContext; use OpenFeature\interfaces\common\Metadata as MetadataInterface; use OpenFeature\interfaces\flags\EvaluationContext as EvaluationContextInterface; +use OpenFeature\interfaces\flags\FlagValueType; use OpenFeature\interfaces\hooks\HookContext; +use function array_values; use function is_array; abstract class AbstractHookContext { - protected string $flagKey = ''; - protected string $type = ''; + protected string $flagKey; + protected FlagValueType $type; /** @var bool|string|int|float|DateTime|mixed[]|null $defaultValue */ protected bool | string | int | float | DateTime | array | null $defaultValue = null; protected EvaluationContextInterface $evaluationContext; protected MetadataInterface $clientMetadata; protected MetadataInterface $providerMetadata; + private const REQUIRED_PROPERTIES = ['flagKey', 'type']; + /** * @param HookContext|mixed[]|null $hookContext */ @@ -43,6 +48,12 @@ public function __construct(HookContext | array | null $hookContext = null) $this->clientMetadata = $hookContext->getClientMetadata(); $this->providerMetadata = $hookContext->getProviderMetadata(); } elseif (is_array($hookContext)) { + foreach (array_values(self::REQUIRED_PROPERTIES) as $requiredProperty) { + if (!isset($hookContext[$requiredProperty])) { + throw new Exception('Required property missing from hook context'); + } + } + /** @var string $property */ /** @var mixed $value */ foreach ($hookContext as $property => $value) { diff --git a/src/implementation/hooks/BooleanHook.php b/src/implementation/hooks/BooleanHook.php index 500c176..45c931e 100644 --- a/src/implementation/hooks/BooleanHook.php +++ b/src/implementation/hooks/BooleanHook.php @@ -8,8 +8,8 @@ abstract class BooleanHook extends AbstractHook { - public function supportsFlagValueType(string $flagValueType): bool + public function supportsFlagValueType(FlagValueType $flagValueType): bool { - return $flagValueType === FlagValueType::BOOLEAN; + return $flagValueType === FlagValueType::Boolean; } } diff --git a/src/implementation/hooks/FloatHook.php b/src/implementation/hooks/FloatHook.php index 5e80e5b..f7e49e4 100644 --- a/src/implementation/hooks/FloatHook.php +++ b/src/implementation/hooks/FloatHook.php @@ -8,8 +8,8 @@ abstract class FloatHook extends AbstractHook { - public function supportsFlagValueType(string $flagValueType): bool + public function supportsFlagValueType(FlagValueType $flagValueType): bool { - return $flagValueType === FlagValueType::FLOAT; + return $flagValueType === FlagValueType::Float; } } diff --git a/src/implementation/hooks/HookContextBuilder.php b/src/implementation/hooks/HookContextBuilder.php index 5e94f37..110d98a 100644 --- a/src/implementation/hooks/HookContextBuilder.php +++ b/src/implementation/hooks/HookContextBuilder.php @@ -7,6 +7,7 @@ use DateTime; use OpenFeature\interfaces\common\Metadata; use OpenFeature\interfaces\flags\EvaluationContext; +use OpenFeature\interfaces\flags\FlagValueType; use OpenFeature\interfaces\hooks\HookContext as HookContextInterface; class HookContextBuilder @@ -26,7 +27,7 @@ public function withFlagKey(string $flagKey): self return $this; } - public function withType(string $type): self + public function withType(FlagValueType $type): self { $this->hookContext->setType($type); diff --git a/src/implementation/hooks/HookContextFactory.php b/src/implementation/hooks/HookContextFactory.php index 1d22e69..ce4be9a 100644 --- a/src/implementation/hooks/HookContextFactory.php +++ b/src/implementation/hooks/HookContextFactory.php @@ -8,6 +8,7 @@ use OpenFeature\implementation\flags\EvaluationContext; use OpenFeature\interfaces\common\Metadata; use OpenFeature\interfaces\flags\EvaluationContext as EvaluationContextInterface; +use OpenFeature\interfaces\flags\FlagValueType; use OpenFeature\interfaces\hooks\HookContext; class HookContextFactory @@ -17,7 +18,7 @@ class HookContextFactory */ public static function from( string $flagKey, - string $type, + FlagValueType $type, bool | string | int | float | DateTime | array | null $defaultValue, ?EvaluationContextInterface $evaluationContext, Metadata $clientMetadata, diff --git a/src/implementation/hooks/HookExecutor.php b/src/implementation/hooks/HookExecutor.php index b92374c..b218340 100644 --- a/src/implementation/hooks/HookExecutor.php +++ b/src/implementation/hooks/HookExecutor.php @@ -8,6 +8,7 @@ use OpenFeature\implementation\flags\MutableEvaluationContext; use OpenFeature\interfaces\common\LoggerAwareTrait; 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 as HookHintsInterface; @@ -35,7 +36,7 @@ public function __construct(?LoggerInterface $logger = null) * * @param Hook[] $mergedHooks */ - public function beforeHooks(string $type, HookContext $hookContext, array $mergedHooks, HookHintsInterface $hints): ?EvaluationContext + public function beforeHooks(FlagValueType $type, HookContext $hookContext, array $mergedHooks, HookHintsInterface $hints): ?EvaluationContext { $additionalContext = new MutableEvaluationContext(); @@ -54,7 +55,7 @@ public function beforeHooks(string $type, HookContext $hookContext, array $merge /** * @param Hook[] $mergedHooks */ - public function afterHooks(string $type, HookContext $hookContext, ResolutionDetails $details, array $mergedHooks, HookHintsInterface $hints): void + public function afterHooks(FlagValueType $type, HookContext $hookContext, ResolutionDetails $details, array $mergedHooks, HookHintsInterface $hints): void { foreach ($mergedHooks as $hook) { if ($hook->supportsFlagValueType($type)) { @@ -66,7 +67,7 @@ public function afterHooks(string $type, HookContext $hookContext, ResolutionDet /** * @param Hook[] $mergedHooks */ - public function errorHooks(string $type, HookContext $hookContext, Throwable $err, array $mergedHooks, HookHintsInterface $hints): void + public function errorHooks(FlagValueType $type, HookContext $hookContext, Throwable $err, array $mergedHooks, HookHintsInterface $hints): void { foreach ($mergedHooks as $hook) { if ($hook->supportsFlagValueType($type)) { @@ -78,7 +79,7 @@ public function errorHooks(string $type, HookContext $hookContext, Throwable $er /** * @param Hook[] $mergedHooks */ - public function finallyHooks(string $type, HookContext $hookContext, array $mergedHooks, HookHintsInterface $hints): void + public function finallyHooks(FlagValueType $type, HookContext $hookContext, array $mergedHooks, HookHintsInterface $hints): void { foreach ($mergedHooks as $hook) { if ($hook->supportsFlagValueType($type)) { diff --git a/src/implementation/hooks/ImmutableHookContext.php b/src/implementation/hooks/ImmutableHookContext.php index e0ed360..b7f1baa 100644 --- a/src/implementation/hooks/ImmutableHookContext.php +++ b/src/implementation/hooks/ImmutableHookContext.php @@ -7,6 +7,7 @@ use DateTime; use OpenFeature\interfaces\common\Metadata; use OpenFeature\interfaces\flags\EvaluationContext; +use OpenFeature\interfaces\flags\FlagValueType; use OpenFeature\interfaces\hooks\HookContext as HookContextInterface; class ImmutableHookContext extends AbstractHookContext implements HookContextInterface @@ -16,7 +17,7 @@ public function getFlagKey(): string return $this->flagKey; } - public function getType(): string + public function getType(): FlagValueType { return $this->type; } diff --git a/src/implementation/hooks/IntegerHook.php b/src/implementation/hooks/IntegerHook.php index 86eebf3..4c9d730 100644 --- a/src/implementation/hooks/IntegerHook.php +++ b/src/implementation/hooks/IntegerHook.php @@ -8,8 +8,8 @@ abstract class IntegerHook extends AbstractHook { - public function supportsFlagValueType(string $flagValueType): bool + public function supportsFlagValueType(FlagValueType $flagValueType): bool { - return $flagValueType === FlagValueType::INTEGER; + return $flagValueType === FlagValueType::Integer; } } diff --git a/src/implementation/hooks/MutableHookContext.php b/src/implementation/hooks/MutableHookContext.php index a8a1b77..d7226f5 100644 --- a/src/implementation/hooks/MutableHookContext.php +++ b/src/implementation/hooks/MutableHookContext.php @@ -7,6 +7,7 @@ use DateTime; use OpenFeature\interfaces\common\Metadata; use OpenFeature\interfaces\flags\EvaluationContext; +use OpenFeature\interfaces\flags\FlagValueType; use OpenFeature\interfaces\hooks\HookContext as HookContextInterface; use OpenFeature\interfaces\hooks\MutableHookContext as MutableHookContextInterface; @@ -17,7 +18,7 @@ public function setFlagKey(string $flagKey): void $this->flagKey = $flagKey; } - public function setType(string $type): void + public function setType(FlagValueType $type): void { $this->type = $type; } diff --git a/src/implementation/hooks/ObjectHook.php b/src/implementation/hooks/ObjectHook.php index 45dcfc7..7fd0b5d 100644 --- a/src/implementation/hooks/ObjectHook.php +++ b/src/implementation/hooks/ObjectHook.php @@ -8,8 +8,8 @@ abstract class ObjectHook extends AbstractHook { - public function supportsFlagValueType(string $flagValueType): bool + public function supportsFlagValueType(FlagValueType $flagValueType): bool { - return $flagValueType === FlagValueType::OBJECT; + return $flagValueType === FlagValueType::Object; } } diff --git a/src/implementation/hooks/StringHook.php b/src/implementation/hooks/StringHook.php index 3f8e2cd..04d8974 100644 --- a/src/implementation/hooks/StringHook.php +++ b/src/implementation/hooks/StringHook.php @@ -8,8 +8,8 @@ abstract class StringHook extends AbstractHook { - public function supportsFlagValueType(string $flagValueType): bool + public function supportsFlagValueType(FlagValueType $flagValueType): bool { - return $flagValueType === FlagValueType::STRING; + return $flagValueType === FlagValueType::String; } } diff --git a/src/interfaces/hooks/Hook.php b/src/interfaces/hooks/Hook.php index 25bc807..5f7db60 100644 --- a/src/interfaces/hooks/Hook.php +++ b/src/interfaces/hooks/Hook.php @@ -5,6 +5,7 @@ namespace OpenFeature\interfaces\hooks; use OpenFeature\interfaces\flags\EvaluationContext; +use OpenFeature\interfaces\flags\FlagValueType; use OpenFeature\interfaces\provider\ResolutionDetails; use Throwable; @@ -54,7 +55,7 @@ public function finally(HookContext $context, HookHints $hints): void; /** * Determines whether the hook should be run for the flag value type returned. * - * @param string $flagValueType The type of flag value + * @param FlagValueType $flagValueType The type of flag value */ - public function supportsFlagValueType(string $flagValueType): bool; + public function supportsFlagValueType(FlagValueType $flagValueType): bool; } diff --git a/src/interfaces/hooks/HookContext.php b/src/interfaces/hooks/HookContext.php index 6ae0599..c76e3c5 100644 --- a/src/interfaces/hooks/HookContext.php +++ b/src/interfaces/hooks/HookContext.php @@ -7,6 +7,7 @@ use DateTime; use OpenFeature\interfaces\common\Metadata; use OpenFeature\interfaces\flags\EvaluationContext; +use OpenFeature\interfaces\flags\FlagValueType; interface HookContext { @@ -36,7 +37,7 @@ public function getFlagKey(): string; * The flag key, flag type, and default value properties MUST be immutable. If * the language does not support immutability, the hook MUST NOT modify these properties. */ - public function getType(): string; + public function getType(): FlagValueType; /** * ----------------- diff --git a/src/interfaces/hooks/MutableHookContext.php b/src/interfaces/hooks/MutableHookContext.php index b6c144f..33f4796 100644 --- a/src/interfaces/hooks/MutableHookContext.php +++ b/src/interfaces/hooks/MutableHookContext.php @@ -7,12 +7,13 @@ use DateTime; use OpenFeature\interfaces\common\Metadata; use OpenFeature\interfaces\flags\EvaluationContext; +use OpenFeature\interfaces\flags\FlagValueType; interface MutableHookContext { public function setFlagKey(string $flagKey): void; - public function setType(string $type): void; + public function setType(FlagValueType $type): void; /** * @param bool|string|int|float|DateTime|mixed[]|null $defaultValue diff --git a/tests/TestHook.php b/tests/TestHook.php index fdf0ad2..a09f187 100644 --- a/tests/TestHook.php +++ b/tests/TestHook.php @@ -5,6 +5,7 @@ namespace OpenFeature\Test; 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; @@ -13,58 +14,24 @@ class TestHook implements Hook { - /** - * Runs before flag is resolved. - * - * @param HookContext $context Information about the particular flag evaluation - * @param HookHints $hints An immutable mapping of data for users to communicate to the hooks. - * - * @return ?EvaluationContext An optional EvaluationContext. If returned, it will be merged with the EvaluationContext - * instances from other hooks, the client and API. - */ public function before(HookContext $context, HookHints $hints): ?EvaluationContext { return null; } - /** - * Runs after a flag is resolved. - * - * @param HookContext $context Information about the particular flag evaluation - * @param ResolutionDetails $details Information about how the flag was resolved, including any resolved values. - * @param HookHints $hints An immutable mapping of data for users to communicate to the hooks. - */ public function after(HookContext $context, ResolutionDetails $details, HookHints $hints): void { } - /** - * Run when evaluation encounters an error. This will always run. Errors thrown will be swallowed. - * - * @param HookContext $context Information about the particular flag evaluation - * @param Throwable $error The exception that was thrown. - * @param HookHints $hints An immutable mapping of data for users to communicate to the hooks. - */ public function error(HookContext $context, Throwable $error, HookHints $hints): void { } - /** - * Run after flag evaluation, including any error processing. This will always run. Errors will be swallowed. - * - * @param HookContext $context Information about the particular flag evaluation - * @param HookHints $hints An immutable mapping of data for users to communicate to the hooks. - */ public function finally(HookContext $context, HookHints $hints): void { } - /** - * Determines whether the hook should be run for the flag value type returned. - * - * @param string $flagValueType The type of flag value - */ - public function supportsFlagValueType(string $flagValueType): bool + public function supportsFlagValueType(FlagValueType $flagValueType): bool { return true; } diff --git a/tests/unit/BaseHooksTest.php b/tests/unit/BaseHooksTest.php index 83903e9..ac76629 100644 --- a/tests/unit/BaseHooksTest.php +++ b/tests/unit/BaseHooksTest.php @@ -22,7 +22,7 @@ class BaseHooksTest extends TestCase /** * @dataProvider dataBooleanHook */ - public function testBooleanHook(string $flagValueType, bool $supportsFlagValueType): void + public function testBooleanHook(FlagValueType $flagValueType, bool $supportsFlagValueType): void { $testBooleanHook = new class extends BooleanHook { public function before(HookContext $context, HookHints $hints): ?EvaluationContext @@ -55,18 +55,18 @@ public function finally(HookContext $context, HookHints $hints): void public function dataBooleanHook(): array { return [ - [FlagValueType::BOOLEAN, true], - [FlagValueType::FLOAT, false], - [FlagValueType::INTEGER, false], - [FlagValueType::OBJECT, false], - [FlagValueType::STRING, false], + [FlagValueType::Boolean, true], + [FlagValueType::Float, false], + [FlagValueType::Integer, false], + [FlagValueType::Object, false], + [FlagValueType::String, false], ]; } /** * @dataProvider dataFloatHook */ - public function testFloatHook(string $flagValueType, bool $supportsFlagValueType): void + public function testFloatHook(FlagValueType $flagValueType, bool $supportsFlagValueType): void { $testFloatHook = new class extends FloatHook { public function before(HookContext $context, HookHints $hints): ?EvaluationContext @@ -99,18 +99,18 @@ public function finally(HookContext $context, HookHints $hints): void public function dataFloatHook(): array { return [ - [FlagValueType::BOOLEAN, false], - [FlagValueType::FLOAT, true], - [FlagValueType::INTEGER, false], - [FlagValueType::OBJECT, false], - [FlagValueType::STRING, false], + [FlagValueType::Boolean, false], + [FlagValueType::Float, true], + [FlagValueType::Integer, false], + [FlagValueType::Object, false], + [FlagValueType::String, false], ]; } /** * @dataProvider dataIntegerHook */ - public function testIntegerHook(string $flagValueType, bool $supportsFlagValueType): void + public function testIntegerHook(FlagValueType $flagValueType, bool $supportsFlagValueType): void { $testIntegerHook = new class extends IntegerHook { public function before(HookContext $context, HookHints $hints): ?EvaluationContext @@ -143,18 +143,18 @@ public function finally(HookContext $context, HookHints $hints): void public function dataIntegerHook(): array { return [ - [FlagValueType::BOOLEAN, false], - [FlagValueType::FLOAT, false], - [FlagValueType::INTEGER, true], - [FlagValueType::OBJECT, false], - [FlagValueType::STRING, false], + [FlagValueType::Boolean, false], + [FlagValueType::Float, false], + [FlagValueType::Integer, true], + [FlagValueType::Object, false], + [FlagValueType::String, false], ]; } /** * @dataProvider dataObjectHook */ - public function testObjectHook(string $flagValueType, bool $supportsFlagValueType): void + public function testObjectHook(FlagValueType $flagValueType, bool $supportsFlagValueType): void { $testObjectHook = new class extends ObjectHook { public function before(HookContext $context, HookHints $hints): ?EvaluationContext @@ -187,18 +187,18 @@ public function finally(HookContext $context, HookHints $hints): void public function dataObjectHook(): array { return [ - [FlagValueType::BOOLEAN, false], - [FlagValueType::FLOAT, false], - [FlagValueType::INTEGER, false], - [FlagValueType::OBJECT, true], - [FlagValueType::STRING, false], + [FlagValueType::Boolean, false], + [FlagValueType::Float, false], + [FlagValueType::Integer, false], + [FlagValueType::Object, true], + [FlagValueType::String, false], ]; } /** * @dataProvider dataStringHook */ - public function testStringHook(string $flagValueType, bool $supportsFlagValueType): void + public function testStringHook(FlagValueType $flagValueType, bool $supportsFlagValueType): void { $testStringHook = new class extends StringHook { public function before(HookContext $context, HookHints $hints): ?EvaluationContext @@ -231,11 +231,11 @@ public function finally(HookContext $context, HookHints $hints): void public function dataStringHook(): array { return [ - [FlagValueType::BOOLEAN, false], - [FlagValueType::FLOAT, false], - [FlagValueType::INTEGER, false], - [FlagValueType::OBJECT, false], - [FlagValueType::STRING, true], + [FlagValueType::Boolean, false], + [FlagValueType::Float, false], + [FlagValueType::Integer, false], + [FlagValueType::Object, false], + [FlagValueType::String, true], ]; } } diff --git a/tests/unit/HookContextBuilderTest.php b/tests/unit/HookContextBuilderTest.php index c24a4e5..46a6c93 100644 --- a/tests/unit/HookContextBuilderTest.php +++ b/tests/unit/HookContextBuilderTest.php @@ -8,6 +8,7 @@ use OpenFeature\implementation\hooks\HookContextBuilder; use OpenFeature\implementation\hooks\ImmutableHookContext; use OpenFeature\implementation\hooks\MutableHookContext; +use OpenFeature\interfaces\flags\FlagValueType; use OpenFeature\interfaces\hooks\HookContext; class HookContextBuilderTest extends TestCase @@ -24,9 +25,9 @@ public function testAsMutable(): void public function testAsImmutable(): void { - $expectedValue = new ImmutableHookContext(['flagKey' => 'test-key']); + $expectedValue = new ImmutableHookContext(['flagKey' => 'test-key', 'type' => FlagValueType::Boolean]); - $actualValue = (new HookContextBuilder())->withFlagKey('test-key')->asImmutable()->build(); + $actualValue = (new HookContextBuilder())->withFlagKey('test-key')->withType(FlagValueType::Boolean)->asImmutable()->build(); $this->assertInstanceOf(HookContext::class, $actualValue); $this->assertEqualsCanonicalizing($expectedValue, $actualValue); diff --git a/tests/unit/HooksTest.php b/tests/unit/HooksTest.php index 51c7370..56e3e86 100644 --- a/tests/unit/HooksTest.php +++ b/tests/unit/HooksTest.php @@ -38,7 +38,7 @@ class HooksTest extends TestCase public function testHookContextMustProvideArguments(): void { $flagKey = 'test-key'; - $flagValueType = FlagValueType::BOOLEAN; + $flagValueType = FlagValueType::Boolean; $evaluationContext = new EvaluationContext(); $defaultValue = false; @@ -60,7 +60,7 @@ public function testHookContextShouldProvideAccessToMetadataFields(): void $clientMetadata = new Metadata('client'); $providerMetadata = new Metadata('provider'); - $hookContext = HookContextFactory::from('key', FlagValueType::BOOLEAN, false, new EvaluationContext(), $clientMetadata, $providerMetadata); + $hookContext = HookContextFactory::from('key', FlagValueType::Boolean, false, new EvaluationContext(), $clientMetadata, $providerMetadata); $this->assertEquals($clientMetadata, $hookContext->getClientMetadata()); $this->assertEquals($providerMetadata, $hookContext->getProviderMetadata()); @@ -73,7 +73,7 @@ public function testHookContextShouldProvideAccessToMetadataFields(): void */ public function testValuePropertiesMustBeImmutable(): void { - $expectedHookContext = HookContextFactory::from('key', FlagValueType::BOOLEAN, false, new EvaluationContext(), new Metadata('client'), new Metadata('provider')); + $expectedHookContext = HookContextFactory::from('key', FlagValueType::Boolean, false, new EvaluationContext(), new Metadata('client'), new Metadata('provider')); $testRunner = $this; $hook = $this->mockery(TestHook::class)->makePartial(); @@ -83,7 +83,7 @@ public function testValuePropertiesMustBeImmutable(): void $testRunner->assertEquals($expectedHookContext, $context); }); - (new HookExecutor(null))->beforeHooks(FlagValueType::BOOLEAN, $expectedHookContext, [$hook], new HookHints()); + (new HookExecutor(null))->beforeHooks(FlagValueType::Boolean, $expectedHookContext, [$hook], new HookHints()); } /** @@ -106,8 +106,8 @@ public function testEvaluationContextIsMutableWithinBeforeHook(): void $additionalEvaluationContext = (new HookExecutor()) ->beforeHooks( - FlagValueType::BOOLEAN, - HookContextFactory::from('flagKey', FlagValueType::BOOLEAN, false, new EvaluationContext(), new Metadata('client'), new Metadata('provider')), + FlagValueType::Boolean, + HookContextFactory::from('flagKey', FlagValueType::Boolean, false, new EvaluationContext(), new Metadata('client'), new Metadata('provider')), [$mutationHook], new HookHints(), ); @@ -137,8 +137,8 @@ public function testEvaluationContextIsImmutableInAfterHooks(): void // @phpstan-ignore-next-line $additionalEvaluationContext = (new HookExecutor()) ->afterHooks( - FlagValueType::BOOLEAN, - HookContextFactory::from('flagKey', FlagValueType::BOOLEAN, false, new EvaluationContext(), new Metadata('client'), new Metadata('provider')), + FlagValueType::Boolean, + HookContextFactory::from('flagKey', FlagValueType::Boolean, false, new EvaluationContext(), new Metadata('client'), new Metadata('provider')), ResolutionDetailsFactory::fromSuccess(true), [$mutationHook], new HookHints(), @@ -168,8 +168,8 @@ public function testEvaluationContextIsImmutableInErrorHooks(): void //@phpstan-ignore-next-line $additionalEvaluationContext = (new HookExecutor()) ->errorHooks( - FlagValueType::BOOLEAN, - HookContextFactory::from('flagKey', FlagValueType::BOOLEAN, false, new EvaluationContext(), new Metadata('client'), new Metadata('provider')), + FlagValueType::Boolean, + HookContextFactory::from('flagKey', FlagValueType::Boolean, false, new EvaluationContext(), new Metadata('client'), new Metadata('provider')), new Exception('Error'), [$mutationHook], new HookHints(), @@ -200,8 +200,8 @@ public function testEvaluationContextIsImmutableInFinallyHooks(): void // @phpstan-ignore-next-line $additionalEvaluationContext = (new HookExecutor()) ->finallyHooks( - FlagValueType::BOOLEAN, - HookContextFactory::from('flagKey', FlagValueType::BOOLEAN, false, new EvaluationContext(), new Metadata('client'), new Metadata('provider')), + FlagValueType::Boolean, + HookContextFactory::from('flagKey', FlagValueType::Boolean, false, new EvaluationContext(), new Metadata('client'), new Metadata('provider')), [$mutationHook], new HookHints(), ); @@ -289,7 +289,7 @@ public function testClientMetadataInHookContextIsImmutable(): void $hookContext = (new HookContextBuilder())->withClientMetadata($clientMetadata)->build(); - (new HookExecutor())->beforeHooks(FlagValueType::BOOLEAN, $hookContext, [], new HookHints()); + (new HookExecutor())->beforeHooks(FlagValueType::Boolean, $hookContext, [], new HookHints()); $this->assertEquals($originalName, $hookContext->getClientMetadata()->getName()); } @@ -326,7 +326,7 @@ public function testProviderMetadataInHookContextIsImmutable(): void $hookContext = (new HookContextBuilder())->withProviderMetadata($providerMetadata)->build(); - (new HookExecutor())->beforeHooks(FlagValueType::BOOLEAN, $hookContext, [], new HookHints()); + (new HookExecutor())->beforeHooks(FlagValueType::Boolean, $hookContext, [], new HookHints()); $this->assertEquals($originalName, $hookContext->getProviderMetadata()->getName()); } @@ -436,7 +436,7 @@ public function testHooksMustNotAlterHookHints(): void $hookContext = new ImmutableHookContext(); - (new HookExecutor())->beforeHooks(FlagValueType::BOOLEAN, $hookContext, [], $expectedHookHints); + (new HookExecutor())->beforeHooks(FlagValueType::Boolean, $hookContext, [], $expectedHookHints); $this->assertEquals('value', $expectedHookHints->get('key')); } diff --git a/tests/unit/NoOpClientTest.php b/tests/unit/NoOpClientTest.php index 093bb77..82b0bce 100644 --- a/tests/unit/NoOpClientTest.php +++ b/tests/unit/NoOpClientTest.php @@ -9,6 +9,7 @@ use OpenFeature\implementation\flags\EvaluationContext; use OpenFeature\implementation\flags\EvaluationDetailsBuilder; use OpenFeature\implementation\flags\NoOpClient; +use OpenFeature\interfaces\flags\FlagValueType; use OpenFeature\interfaces\hooks\Hook; use OpenFeature\interfaces\hooks\HookContext; use OpenFeature\interfaces\hooks\HookHints; @@ -198,7 +199,7 @@ public function finally(HookContext $context, HookHints $hints): void // no-op } - public function supportsFlagValueType(string $flagValueType): bool + public function supportsFlagValueType(FlagValueType $flagValueType): bool { return true; } From ff047aaed1f9ee010bc9a4db8a1680c93749d243 Mon Sep 17 00:00:00 2001 From: Tom Carrio Date: Sun, 3 Dec 2023 22:37:51 -0500 Subject: [PATCH 08/10] ci: update min PHP version to 8.1 Signed-off-by: Tom Carrio --- .github/workflows/pullrequest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pullrequest.yml b/.github/workflows/pullrequest.yml index 3dae50e..d83bcf6 100644 --- a/.github/workflows/pullrequest.yml +++ b/.github/workflows/pullrequest.yml @@ -20,7 +20,7 @@ jobs: strategy: matrix: operating-system: [ubuntu-latest] - php-version: ['8.0', '8.1', '8.2'] + php-version: ['8.1', '8.2', '8.3'] steps: - uses: actions/checkout@v4 From 09dbb024b66403bf5e2c4677376b4edd7151f3e0 Mon Sep 17 00:00:00 2001 From: Tom Carrio Date: Sun, 3 Dec 2023 23:22:24 -0500 Subject: [PATCH 09/10] fix: updates to support 8.1 and errors Signed-off-by: Tom Carrio --- src/OpenFeatureClient.php | 29 ++++++++----------- .../common/ValueTypeValidator.php | 1 - .../flags/EvaluationDetails.php | 13 ++++----- .../flags/EvaluationDetailsBuilder.php | 5 ++-- .../flags/EvaluationDetailsFactory.php | 5 ++-- src/implementation/flags/NoOpClient.php | 3 +- .../hooks/AbstractHookContext.php | 12 ++++---- .../hooks/HookContextBuilder.php | 5 ++-- .../hooks/HookContextFactory.php | 5 ++-- .../hooks/ImmutableHookContext.php | 5 ++-- .../hooks/MutableHookContext.php | 3 +- src/implementation/provider/Reason.php | 15 ---------- .../provider/ResolutionDetails.php | 13 ++++----- .../provider/ResolutionDetailsBuilder.php | 5 ++-- .../provider/ResolutionDetailsFactory.php | 5 ++-- src/interfaces/common/TypeValuePair.php | 17 ----------- src/interfaces/flags/EvaluationDetails.php | 5 ++-- src/interfaces/hooks/HookContext.php | 5 ++-- src/interfaces/hooks/MutableHookContext.php | 5 ++-- src/interfaces/provider/ResolutionDetails.php | 6 ++-- tests/unit/HookContextBuilderTest.php | 2 +- 21 files changed, 54 insertions(+), 110 deletions(-) delete mode 100644 src/implementation/provider/Reason.php delete mode 100644 src/interfaces/common/TypeValuePair.php diff --git a/src/OpenFeatureClient.php b/src/OpenFeatureClient.php index 4ddefbf..929eb5b 100644 --- a/src/OpenFeatureClient.php +++ b/src/OpenFeatureClient.php @@ -4,7 +4,6 @@ namespace OpenFeature; -use DateTime; use OpenFeature\implementation\common\Metadata; use OpenFeature\implementation\common\ValueTypeValidator; use OpenFeature\implementation\errors\InvalidResolutionValueError; @@ -16,7 +15,6 @@ use OpenFeature\implementation\hooks\HookContextFactory; use OpenFeature\implementation\hooks\HookExecutor; use OpenFeature\implementation\hooks\HookHints; -use OpenFeature\implementation\provider\Reason; use OpenFeature\implementation\provider\ResolutionError; use OpenFeature\interfaces\common\LoggerAwareTrait; use OpenFeature\interfaces\common\Metadata as MetadataInterface; @@ -30,6 +28,7 @@ use OpenFeature\interfaces\hooks\HooksAwareTrait; use OpenFeature\interfaces\provider\ErrorCode; use OpenFeature\interfaces\provider\Provider; +use OpenFeature\interfaces\provider\Reason; use OpenFeature\interfaces\provider\ResolutionDetails; use OpenFeature\interfaces\provider\ThrowableWithResolutionError; use Psr\Log\LoggerAwareInterface; @@ -282,12 +281,12 @@ public function getObjectDetails(string $flagKey, $defaultValue, ?EvaluationCont * ----------------- * Methods, functions, or operations on the client MUST NOT throw exceptions, or otherwise abnormally terminate. Flag evaluation calls must always return the default value in the event of abnormal execution. Exceptions include functions or methods for the purposes for configuration or setup. * - * @param bool|string|int|float|DateTime|mixed[]|null $defaultValue + * @param bool|string|int|float|mixed[]|null $defaultValue */ private function evaluateFlag( FlagValueType $flagValueType, string $flagKey, - bool | string | int | float | DateTime | array | null $defaultValue, + bool | string | int | float | array | null $defaultValue, ?EvaluationContextInterface $invocationContext = null, ?EvaluationOptionsInterface $options = null, ): EvaluationDetailsInterface { @@ -383,6 +382,9 @@ private function evaluateFlag( return $details; } + /** + * @param bool|string|int|float|mixed[]|null $defaultValue + */ private function createProviderEvaluation( FlagValueType $type, string $key, @@ -394,35 +396,28 @@ private function createProviderEvaluation( case FlagValueType::Boolean->value: /** @var bool $defaultValue */; $defaultValue = $defaultValue; - $resolver = $provider->resolveBooleanValue(...); - break; + return $provider->resolveBooleanValue($key, $defaultValue, $context); case FlagValueType::String->value: /** @var string $defaultValue */; $defaultValue = $defaultValue; - $resolver = $provider->resolveStringValue(...); - break; + return $provider->resolveStringValue($key, $defaultValue, $context); case FlagValueType::Integer->value: /** @var int $defaultValue */; $defaultValue = $defaultValue; - $resolver = $provider->resolveIntegerValue(...); - break; + return $provider->resolveIntegerValue($key, $defaultValue, $context); case FlagValueType::Float->value: /** @var float $defaultValue */; $defaultValue = $defaultValue; - $resolver = $provider->resolveFloatValue(...); - break; + return $provider->resolveFloatValue($key, $defaultValue, $context); case FlagValueType::Object->value: - /** @var object $defaultValue */; + /** @var mixed[] $defaultValue */; $defaultValue = $defaultValue; - $resolver = $provider->resolveObjectValue(...); - break; + return $provider->resolveObjectValue($key, $defaultValue, $context); } - - return $resolver($key, $defaultValue, $context); } } diff --git a/src/implementation/common/ValueTypeValidator.php b/src/implementation/common/ValueTypeValidator.php index f4f1353..3fd12e7 100644 --- a/src/implementation/common/ValueTypeValidator.php +++ b/src/implementation/common/ValueTypeValidator.php @@ -82,7 +82,6 @@ public static function is(FlagValueType $type, mixed $value): bool FlagValueType::Integer => self::isInteger($value), FlagValueType::String => self::isString($value), FlagValueType::Object => self::isStructure($value) || self::isArray($value), - default => false, }; } } diff --git a/src/implementation/flags/EvaluationDetails.php b/src/implementation/flags/EvaluationDetails.php index a0f81ca..31e66eb 100644 --- a/src/implementation/flags/EvaluationDetails.php +++ b/src/implementation/flags/EvaluationDetails.php @@ -4,7 +4,6 @@ namespace OpenFeature\implementation\flags; -use DateTime; use OpenFeature\interfaces\flags\EvaluationDetails as EvaluationDetailsInterface; use OpenFeature\interfaces\provider\ResolutionError; @@ -12,8 +11,8 @@ class EvaluationDetails implements EvaluationDetailsInterface { private string $flagKey = ''; - /** @var bool|string|int|float|DateTime|mixed[]|null $value */ - private bool | string | int | float | DateTime | array | null $value = null; + /** @var bool|string|int|float|mixed[]|null $value */ + private bool | string | int | float | array | null $value = null; private ?ResolutionError $error = null; private ?string $reason = null; private ?string $variant = null; @@ -38,17 +37,17 @@ public function setFlagKey(string $flagKey): void * ----------------- * The evaluation details structure's value field MUST contain the evaluated flag value. * - * @return bool|string|int|float|DateTime|mixed[]|null + * @return bool|string|int|float|mixed[]|null */ - public function getValue(): bool | string | int | float | DateTime | array | null + public function getValue(): bool | string | int | float | array | null { return $this->value; } /** - * @param bool|string|int|float|DateTime|mixed[]|null $value + * @param bool|string|int|float|mixed[]|null $value */ - public function setValue(bool | string | int | float | DateTime | array | null $value): void + public function setValue(bool | string | int | float | array | null $value): void { $this->value = $value; } diff --git a/src/implementation/flags/EvaluationDetailsBuilder.php b/src/implementation/flags/EvaluationDetailsBuilder.php index 7cb4dc6..61e8cf6 100644 --- a/src/implementation/flags/EvaluationDetailsBuilder.php +++ b/src/implementation/flags/EvaluationDetailsBuilder.php @@ -4,7 +4,6 @@ namespace OpenFeature\implementation\flags; -use DateTime; use OpenFeature\interfaces\flags\EvaluationDetails as EvaluationDetailsInterface; use OpenFeature\interfaces\provider\ResolutionError; @@ -25,9 +24,9 @@ public function withFlagKey(string $flagKey): EvaluationDetailsBuilder } /** - * @param bool|string|int|float|DateTime|mixed[]|null $value + * @param bool|string|int|float|mixed[]|null $value */ - public function withValue(bool | string | int | float | DateTime | array | null $value): EvaluationDetailsBuilder + public function withValue(bool | string | int | float | array | null $value): EvaluationDetailsBuilder { $this->details->setValue($value); diff --git a/src/implementation/flags/EvaluationDetailsFactory.php b/src/implementation/flags/EvaluationDetailsFactory.php index 6115799..b491a15 100644 --- a/src/implementation/flags/EvaluationDetailsFactory.php +++ b/src/implementation/flags/EvaluationDetailsFactory.php @@ -4,7 +4,6 @@ namespace OpenFeature\implementation\flags; -use DateTime; use OpenFeature\interfaces\flags\EvaluationDetails; use OpenFeature\interfaces\provider\ResolutionDetails; @@ -13,9 +12,9 @@ class EvaluationDetailsFactory /** * Provides a simple method for building EvaluationDetails from a given value\ * - * @param bool|string|int|float|DateTime|mixed[]|null $value + * @param bool|string|int|float|mixed[]|null $value */ - public static function from(string $flagKey, bool | string | int | float | DateTime | array | null $value): EvaluationDetails + public static function from(string $flagKey, bool | string | int | float | array | null $value): EvaluationDetails { return (new EvaluationDetailsBuilder()) ->withFlagKey($flagKey) diff --git a/src/implementation/flags/NoOpClient.php b/src/implementation/flags/NoOpClient.php index 792e262..c35fb44 100644 --- a/src/implementation/flags/NoOpClient.php +++ b/src/implementation/flags/NoOpClient.php @@ -4,7 +4,6 @@ namespace OpenFeature\implementation\flags; -use DateTime; use OpenFeature\implementation\common\Metadata; use OpenFeature\interfaces\flags\Client; use OpenFeature\interfaces\flags\EvaluationContext as EvaluationContextInterface; @@ -69,7 +68,7 @@ public function getObjectValue( return $defaultValue; } - public function getObjectDetails(string $flagKey, bool | string | int | float | DateTime | array | null $defaultValue, ?EvaluationContextInterface $context = null, ?EvaluationOptions $options = null): EvaluationDetails + public function getObjectDetails(string $flagKey, bool | string | int | float | array | null $defaultValue, ?EvaluationContextInterface $context = null, ?EvaluationOptions $options = null): EvaluationDetails { return EvaluationDetailsFactory::from($flagKey, $defaultValue); } diff --git a/src/implementation/hooks/AbstractHookContext.php b/src/implementation/hooks/AbstractHookContext.php index 0a1cefc..678238c 100644 --- a/src/implementation/hooks/AbstractHookContext.php +++ b/src/implementation/hooks/AbstractHookContext.php @@ -4,7 +4,6 @@ namespace OpenFeature\implementation\hooks; -use DateTime; use Exception; use OpenFeature\implementation\common\Metadata; use OpenFeature\implementation\flags\EvaluationContext; @@ -13,15 +12,14 @@ use OpenFeature\interfaces\flags\FlagValueType; use OpenFeature\interfaces\hooks\HookContext; -use function array_values; use function is_array; abstract class AbstractHookContext { - protected string $flagKey; - protected FlagValueType $type; - /** @var bool|string|int|float|DateTime|mixed[]|null $defaultValue */ - protected bool | string | int | float | DateTime | array | null $defaultValue = null; + protected string $flagKey = ''; + protected FlagValueType $type = FlagValueType::Boolean; + /** @var bool|string|int|float|mixed[]|null $defaultValue */ + protected bool | string | int | float | array | null $defaultValue = null; protected EvaluationContextInterface $evaluationContext; protected MetadataInterface $clientMetadata; protected MetadataInterface $providerMetadata; @@ -48,7 +46,7 @@ public function __construct(HookContext | array | null $hookContext = null) $this->clientMetadata = $hookContext->getClientMetadata(); $this->providerMetadata = $hookContext->getProviderMetadata(); } elseif (is_array($hookContext)) { - foreach (array_values(self::REQUIRED_PROPERTIES) as $requiredProperty) { + foreach (self::REQUIRED_PROPERTIES as $requiredProperty) { if (!isset($hookContext[$requiredProperty])) { throw new Exception('Required property missing from hook context'); } diff --git a/src/implementation/hooks/HookContextBuilder.php b/src/implementation/hooks/HookContextBuilder.php index 110d98a..88805dd 100644 --- a/src/implementation/hooks/HookContextBuilder.php +++ b/src/implementation/hooks/HookContextBuilder.php @@ -4,7 +4,6 @@ namespace OpenFeature\implementation\hooks; -use DateTime; use OpenFeature\interfaces\common\Metadata; use OpenFeature\interfaces\flags\EvaluationContext; use OpenFeature\interfaces\flags\FlagValueType; @@ -35,9 +34,9 @@ public function withType(FlagValueType $type): self } /** - * @param bool|string|int|float|DateTime|mixed[]|null $defaultValue + * @param bool|string|int|float|mixed[]|null $defaultValue */ - public function withDefaultValue(bool | string | int | float | DateTime | array | null $defaultValue): self + public function withDefaultValue(bool | string | int | float | array | null $defaultValue): self { $this->hookContext->setDefaultValue($defaultValue); diff --git a/src/implementation/hooks/HookContextFactory.php b/src/implementation/hooks/HookContextFactory.php index ce4be9a..d633661 100644 --- a/src/implementation/hooks/HookContextFactory.php +++ b/src/implementation/hooks/HookContextFactory.php @@ -4,7 +4,6 @@ namespace OpenFeature\implementation\hooks; -use DateTime; use OpenFeature\implementation\flags\EvaluationContext; use OpenFeature\interfaces\common\Metadata; use OpenFeature\interfaces\flags\EvaluationContext as EvaluationContextInterface; @@ -14,12 +13,12 @@ class HookContextFactory { /** - * @param bool|string|int|float|DateTime|mixed[]|null $defaultValue + * @param bool|string|int|float|mixed[]|null $defaultValue */ public static function from( string $flagKey, FlagValueType $type, - bool | string | int | float | DateTime | array | null $defaultValue, + bool | string | int | float | array | null $defaultValue, ?EvaluationContextInterface $evaluationContext, Metadata $clientMetadata, Metadata $providerMetadata, diff --git a/src/implementation/hooks/ImmutableHookContext.php b/src/implementation/hooks/ImmutableHookContext.php index b7f1baa..2cb6756 100644 --- a/src/implementation/hooks/ImmutableHookContext.php +++ b/src/implementation/hooks/ImmutableHookContext.php @@ -4,7 +4,6 @@ namespace OpenFeature\implementation\hooks; -use DateTime; use OpenFeature\interfaces\common\Metadata; use OpenFeature\interfaces\flags\EvaluationContext; use OpenFeature\interfaces\flags\FlagValueType; @@ -23,9 +22,9 @@ public function getType(): FlagValueType } /** - * @return bool|string|int|float|DateTime|mixed[]|null + * @return bool|string|int|float|mixed[]|null */ - public function getDefaultValue(): bool | string | int | float | DateTime | array | null + public function getDefaultValue(): bool | string | int | float | array | null { return $this->defaultValue; } diff --git a/src/implementation/hooks/MutableHookContext.php b/src/implementation/hooks/MutableHookContext.php index d7226f5..348e0b5 100644 --- a/src/implementation/hooks/MutableHookContext.php +++ b/src/implementation/hooks/MutableHookContext.php @@ -4,7 +4,6 @@ namespace OpenFeature\implementation\hooks; -use DateTime; use OpenFeature\interfaces\common\Metadata; use OpenFeature\interfaces\flags\EvaluationContext; use OpenFeature\interfaces\flags\FlagValueType; @@ -23,7 +22,7 @@ public function setType(FlagValueType $type): void $this->type = $type; } - public function setDefaultValue(bool | string | int | float | DateTime | array | null $defaultValue): void + public function setDefaultValue(bool | string | int | float | array | null $defaultValue): void { $this->defaultValue = $defaultValue; } diff --git a/src/implementation/provider/Reason.php b/src/implementation/provider/Reason.php deleted file mode 100644 index 2905b78..0000000 --- a/src/implementation/provider/Reason.php +++ /dev/null @@ -1,15 +0,0 @@ -value; } /** - * @param bool|string|int|float|DateTime|mixed[]|null $value + * @param bool|string|int|float|mixed[]|null $value */ - public function setValue(bool | string | int | float | DateTime | array | null $value): void + public function setValue(bool | string | int | float | array | null $value): void { $this->value = $value; } diff --git a/src/implementation/provider/ResolutionDetailsBuilder.php b/src/implementation/provider/ResolutionDetailsBuilder.php index 73ae2c7..4028f6b 100644 --- a/src/implementation/provider/ResolutionDetailsBuilder.php +++ b/src/implementation/provider/ResolutionDetailsBuilder.php @@ -4,7 +4,6 @@ namespace OpenFeature\implementation\provider; -use DateTime; use OpenFeature\interfaces\provider\ResolutionDetails as ResolutionDetailsInterface; use OpenFeature\interfaces\provider\ResolutionError; @@ -18,9 +17,9 @@ public function __construct() } /** - * @param bool|string|int|float|DateTime|mixed[]|null $value + * @param bool|string|int|float|mixed[]|null $value */ - public function withValue(bool | string | int | float | DateTime | array | null $value): ResolutionDetailsBuilder + public function withValue(bool | string | int | float | array | null $value): ResolutionDetailsBuilder { $this->details->setValue($value); diff --git a/src/implementation/provider/ResolutionDetailsFactory.php b/src/implementation/provider/ResolutionDetailsFactory.php index 79d2e1b..42289ea 100644 --- a/src/implementation/provider/ResolutionDetailsFactory.php +++ b/src/implementation/provider/ResolutionDetailsFactory.php @@ -4,15 +4,14 @@ namespace OpenFeature\implementation\provider; -use DateTime; use OpenFeature\interfaces\provider\ResolutionDetails as ResolutionDetailsInterface; class ResolutionDetailsFactory { /** - * @param bool|string|int|float|DateTime|mixed[]|null $value + * @param bool|string|int|float|mixed[]|null $value */ - public static function fromSuccess(bool | string | int | float | DateTime | array | null $value): ResolutionDetailsInterface + public static function fromSuccess(bool | string | int | float | array | null $value): ResolutionDetailsInterface { return (new ResolutionDetailsBuilder()) ->withValue($value) diff --git a/src/interfaces/common/TypeValuePair.php b/src/interfaces/common/TypeValuePair.php deleted file mode 100644 index 767ac57..0000000 --- a/src/interfaces/common/TypeValuePair.php +++ /dev/null @@ -1,17 +0,0 @@ - 'test-key']); + $expectedValue = new MutableHookContext(['flagKey' => 'test-key', 'type' => FlagValueType::Boolean]); $actualValue = (new HookContextBuilder())->withFlagKey('test-key')->asMutable()->build(); From 21be2ee9ff809fe9d6a0bf4efc7209cf8a0e37c4 Mon Sep 17 00:00:00 2001 From: Tom Carrio Date: Sun, 3 Dec 2023 23:25:23 -0500 Subject: [PATCH 10/10] test: add test for missing keys Signed-off-by: Tom Carrio --- tests/unit/HookContextBuilderTest.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/unit/HookContextBuilderTest.php b/tests/unit/HookContextBuilderTest.php index 6774b43..4f681cd 100644 --- a/tests/unit/HookContextBuilderTest.php +++ b/tests/unit/HookContextBuilderTest.php @@ -10,9 +10,17 @@ use OpenFeature\implementation\hooks\MutableHookContext; use OpenFeature\interfaces\flags\FlagValueType; use OpenFeature\interfaces\hooks\HookContext; +use Throwable; class HookContextBuilderTest extends TestCase { + public function testHookMissingKeysFromArray(): void + { + $this->expectException(Throwable::class); + + new MutableHookContext(['flagKey' => 'test-key']); + } + public function testAsMutable(): void { $expectedValue = new MutableHookContext(['flagKey' => 'test-key', 'type' => FlagValueType::Boolean]);