From 3ecc47af4b75d87bacec669eb5f7d20d41eb74ff Mon Sep 17 00:00:00 2001 From: vi-verrone Date: Wed, 22 Jul 2026 19:38:31 +0200 Subject: [PATCH 1/6] feat: :sparkles: expose flag metadata through getMetadata() method on EvaluationDetails and ResolutionDetails getMetadata() and setMetadata() have been added to EvaluationDetails and ResolutionDetails to respect OpenFeature requirements 1.4.14 and 2.2.9 Signed-off-by: vi-verrone --- .../flags/EvaluationDetails.php | 36 +++++++++++++++ .../flags/EvaluationDetailsBuilder.php | 10 +++++ .../flags/EvaluationDetailsFactory.php | 1 + .../provider/ResolutionDetails.php | 36 +++++++++++++++ .../provider/ResolutionDetailsBuilder.php | 10 +++++ src/interfaces/flags/EvaluationDetails.php | 12 +++++ src/interfaces/provider/ResolutionDetails.php | 10 +++++ tests/unit/ProviderResolutionResultTest.php | 44 +++++++++++++++++++ 8 files changed, 159 insertions(+) diff --git a/src/implementation/flags/EvaluationDetails.php b/src/implementation/flags/EvaluationDetails.php index d96c46b..2f2c2b2 100644 --- a/src/implementation/flags/EvaluationDetails.php +++ b/src/implementation/flags/EvaluationDetails.php @@ -8,6 +8,8 @@ use OpenFeature\interfaces\flags\EvaluationDetails as EvaluationDetailsInterface; use OpenFeature\interfaces\provider\ResolutionError; +use function is_array; + class EvaluationDetails implements EvaluationDetailsInterface { private string $flagKey = ''; @@ -17,6 +19,8 @@ class EvaluationDetails implements EvaluationDetailsInterface private ?ResolutionError $error = null; private ?string $reason = null; private ?string $variant = null; + /** @var array|null $metadata */ + private ?array $metadata = null; public function __construct() { @@ -83,4 +87,36 @@ public function setVariant(?string $variant): void { $this->variant = $variant; } + + /** + * @param array|null $metadata + */ + public function setMetadata(?array $metadata): void + { + if (is_array($metadata)) { + $this->metadata = []; + foreach ($metadata as $key => $value) { + $this->metadata[$key] = $value; + } + } else { + $this->metadata = null; + } + } + + /** + * @return array|null + */ + public function getMetadata(): ?array + { + if ($this->metadata === null) { + return null; + } + /** @var array $metadata */ + $metadata = []; + foreach ($this->metadata as $key => $value) { + $metadata[$key] = $value; + } + + return $metadata; + } } diff --git a/src/implementation/flags/EvaluationDetailsBuilder.php b/src/implementation/flags/EvaluationDetailsBuilder.php index 7cb4dc6..f727021 100644 --- a/src/implementation/flags/EvaluationDetailsBuilder.php +++ b/src/implementation/flags/EvaluationDetailsBuilder.php @@ -55,6 +55,16 @@ public function withVariant(?string $variant): EvaluationDetailsBuilder return $this; } + /** + * @param array|null $metadata + */ + public function withMetadata(?array $metadata): EvaluationDetailsBuilder + { + $this->details->setMetadata($metadata); + + return $this; + } + public function build(): EvaluationDetailsInterface { return $this->details; diff --git a/src/implementation/flags/EvaluationDetailsFactory.php b/src/implementation/flags/EvaluationDetailsFactory.php index 6115799..a48b3c7 100644 --- a/src/implementation/flags/EvaluationDetailsFactory.php +++ b/src/implementation/flags/EvaluationDetailsFactory.php @@ -34,6 +34,7 @@ public static function fromResolution(string $flagKey, ResolutionDetails $detail ->withError($details->getError()) ->withReason($details->getReason()) ->withVariant($details->getVariant()) + ->withMetadata($details->getMetadata()) ->build(); } } diff --git a/src/implementation/provider/ResolutionDetails.php b/src/implementation/provider/ResolutionDetails.php index 29750d9..24a7ac1 100644 --- a/src/implementation/provider/ResolutionDetails.php +++ b/src/implementation/provider/ResolutionDetails.php @@ -8,6 +8,8 @@ use OpenFeature\interfaces\provider\ResolutionDetails as ResolutionDetailsInterface; use OpenFeature\interfaces\provider\ResolutionError; +use function is_array; + class ResolutionDetails implements ResolutionDetailsInterface { /** @var bool|string|int|float|DateTime|mixed[]|null $value */ @@ -15,6 +17,8 @@ class ResolutionDetails implements ResolutionDetailsInterface private ?ResolutionError $error = null; private ?string $reason = null; private ?string $variant = null; + /** @var array|null $metadata */ + private ?array $metadata = null; /** * @return bool|string|int|float|DateTime|mixed[]|null @@ -61,4 +65,36 @@ public function setVariant(?string $variant): void { $this->variant = $variant; } + + /** + * @param array|null $metadata + */ + public function setMetadata(?array $metadata): void + { + if (is_array($metadata)) { + $this->metadata = []; + foreach ($metadata as $key => $value) { + $this->metadata[$key] = $value; + } + } else { + $this->metadata = null; + } + } + + /** + * @return array|null + */ + public function getMetadata(): ?array + { + if ($this->metadata === null) { + return null; + } + /** @var array $metadata */ + $metadata = []; + foreach ($this->metadata as $key => $value) { + $metadata[$key] = $value; + } + + return $metadata; + } } diff --git a/src/implementation/provider/ResolutionDetailsBuilder.php b/src/implementation/provider/ResolutionDetailsBuilder.php index 73ae2c7..efbadc6 100644 --- a/src/implementation/provider/ResolutionDetailsBuilder.php +++ b/src/implementation/provider/ResolutionDetailsBuilder.php @@ -48,6 +48,16 @@ public function withVariant(string $variant): ResolutionDetailsBuilder return $this; } + /** + * @param array|null $metadata + */ + public function withMetadata(?array $metadata): ResolutionDetailsBuilder + { + $this->details->setMetadata($metadata); + + return $this; + } + public function build(): ResolutionDetailsInterface { return $this->details; diff --git a/src/interfaces/flags/EvaluationDetails.php b/src/interfaces/flags/EvaluationDetails.php index 4dd269e..077a040 100644 --- a/src/interfaces/flags/EvaluationDetails.php +++ b/src/interfaces/flags/EvaluationDetails.php @@ -64,4 +64,16 @@ public function getReason(): ?string; * the configured provider, if the field is set. */ public function getVariant(): ?string; + + /** + * ------------------ + * Requirement 1.4.14 + * ------------------ + * If the flag metadata field in the flag resolution structure returned by the configured + * provider is set, the evaluation details structure's flag metadata field MUST contain that + * value. Otherwise, it MUST contain an empty record. + * + * @return array|null + */ + public function getMetadata(): ?array; } diff --git a/src/interfaces/provider/ResolutionDetails.php b/src/interfaces/provider/ResolutionDetails.php index c50edb2..9fd5dd7 100644 --- a/src/interfaces/provider/ResolutionDetails.php +++ b/src/interfaces/provider/ResolutionDetails.php @@ -51,4 +51,14 @@ public function getReason(): ?string; * with a string identifier corresponding to the returned flag value. */ public function getVariant(): ?string; + + /** + * ----------------- + * Requirement 2.2.9 + * ----------------- + * The provider SHOULD populate the resolution details structure's flag metadata field. + * + * @return array|null + */ + public function getMetadata(): ?array; } diff --git a/tests/unit/ProviderResolutionResultTest.php b/tests/unit/ProviderResolutionResultTest.php index 577fc7d..7b8fd1a 100644 --- a/tests/unit/ProviderResolutionResultTest.php +++ b/tests/unit/ProviderResolutionResultTest.php @@ -34,6 +34,15 @@ private function details(bool | string | int | float | DateTime | array | null $ return (new ResolutionDetailsBuilder())->withValue($value)->build(); } + /** + * @param bool|string|int|float|DateTime|array|null $value + * @param array|null $metadata + */ + private function detailsWithMetadata(bool | string | int | float | DateTime | array | null $value, ?array $metadata): ResolutionDetails + { + return (new ResolutionDetailsBuilder())->withValue($value)->withMetadata($metadata)->build(); + } + public function testSuccessfulResult(): void { $details = $this->details(true); @@ -68,4 +77,39 @@ public function testEmptyResultNeitherSuccessNorError(): void $this->assertFalse($result->hasError()); $this->assertFalse($result->isSuccessful()); } + + public function testResultWithNotEmptyMetadata(): void + { + $details = $this->detailsWithMetadata(true, [ + 'test_bool' => true, + 'test_int' => 10, + 'test_string' => 'OK', + ]); + $result = new ProviderResolutionResult('TestProvider', $this->provider, null, null); + + $this->assertNull($result->getDetails()); + $this->assertNull($result->getError()); + $this->assertFalse($result->hasError()); + $this->assertFalse($result->isSuccessful()); + $this->assertIsArray($details->getMetadata()); + $this->assertArrayHasKey('test_bool', $details->getMetadata()); + $this->assertArrayHasKey('test_int', $details->getMetadata()); + $this->assertArrayHasKey('test_string', $details->getMetadata()); + $this->assertEquals(true, $details->getMetadata()['test_bool']); + $this->assertEquals('OK', $details->getMetadata()['test_string']); + $this->assertEquals(10, $details->getMetadata()['test_int']); + } + + public function testResultWithEmptyMetadata(): void + { + $details = $this->detailsWithMetadata(true, null); + $result = new ProviderResolutionResult('TestProvider', $this->provider, null, null); + + $this->assertNull($result->getDetails()); + $this->assertNull($result->getError()); + $this->assertFalse($result->hasError()); + $this->assertFalse($result->isSuccessful()); + $this->assertNull($details->getMetadata()); + $this->assertIsNotArray($details->getMetadata()); + } } From 64415d01bd6d3010ef4d1697a5c6bc11b8a0817c Mon Sep 17 00:00:00 2001 From: vi-verrone Date: Wed, 22 Jul 2026 20:42:16 +0200 Subject: [PATCH 2/6] refactor: :sparkles: Applied feedbacks from the PR's comments Signed-off-by: vi-verrone --- .../flags/EvaluationDetails.php | 6 ++--- src/interfaces/flags/EvaluationDetails.php | 4 +-- tests/unit/ProviderResolutionResultTest.php | 25 ++++++++++++++----- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/implementation/flags/EvaluationDetails.php b/src/implementation/flags/EvaluationDetails.php index 2f2c2b2..fd39c1b 100644 --- a/src/implementation/flags/EvaluationDetails.php +++ b/src/implementation/flags/EvaluationDetails.php @@ -104,12 +104,12 @@ public function setMetadata(?array $metadata): void } /** - * @return array|null + * @return array */ - public function getMetadata(): ?array + public function getMetadata(): array { if ($this->metadata === null) { - return null; + return []; } /** @var array $metadata */ $metadata = []; diff --git a/src/interfaces/flags/EvaluationDetails.php b/src/interfaces/flags/EvaluationDetails.php index 077a040..7a6ed67 100644 --- a/src/interfaces/flags/EvaluationDetails.php +++ b/src/interfaces/flags/EvaluationDetails.php @@ -73,7 +73,7 @@ public function getVariant(): ?string; * provider is set, the evaluation details structure's flag metadata field MUST contain that * value. Otherwise, it MUST contain an empty record. * - * @return array|null + * @return array */ - public function getMetadata(): ?array; + public function getMetadata(): array; } diff --git a/tests/unit/ProviderResolutionResultTest.php b/tests/unit/ProviderResolutionResultTest.php index 7b8fd1a..021f7ec 100644 --- a/tests/unit/ProviderResolutionResultTest.php +++ b/tests/unit/ProviderResolutionResultTest.php @@ -85,12 +85,12 @@ public function testResultWithNotEmptyMetadata(): void 'test_int' => 10, 'test_string' => 'OK', ]); - $result = new ProviderResolutionResult('TestProvider', $this->provider, null, null); + $result = new ProviderResolutionResult('TestProvider', $this->provider, $details, null); - $this->assertNull($result->getDetails()); + $this->assertSame($details, $result->getDetails()); $this->assertNull($result->getError()); $this->assertFalse($result->hasError()); - $this->assertFalse($result->isSuccessful()); + $this->assertTrue($result->isSuccessful()); $this->assertIsArray($details->getMetadata()); $this->assertArrayHasKey('test_bool', $details->getMetadata()); $this->assertArrayHasKey('test_int', $details->getMetadata()); @@ -101,14 +101,27 @@ public function testResultWithNotEmptyMetadata(): void } public function testResultWithEmptyMetadata(): void + { + $details = $this->detailsWithMetadata(true, []); + $result = new ProviderResolutionResult('TestProvider', $this->provider, $details, null); + + $this->assertSame($details, $result->getDetails()); + $this->assertNull($result->getError()); + $this->assertFalse($result->hasError()); + $this->assertTrue($result->isSuccessful()); + $this->assertIsArray($details->getMetadata()); + $this->assertSame([], $details->getMetadata()); + } + + public function testResultWithNullMetadata(): void { $details = $this->detailsWithMetadata(true, null); - $result = new ProviderResolutionResult('TestProvider', $this->provider, null, null); + $result = new ProviderResolutionResult('TestProvider', $this->provider, $details, null); - $this->assertNull($result->getDetails()); + $this->assertSame($details, $result->getDetails()); $this->assertNull($result->getError()); $this->assertFalse($result->hasError()); - $this->assertFalse($result->isSuccessful()); + $this->assertTrue($result->isSuccessful()); $this->assertNull($details->getMetadata()); $this->assertIsNotArray($details->getMetadata()); } From c00fb0c72a3265281fc2363e51e730cc44089e50 Mon Sep 17 00:00:00 2001 From: vi-verrone Date: Wed, 22 Jul 2026 21:32:32 +0200 Subject: [PATCH 3/6] refactor: :sparkles: Added float type to the type annotation for flag metadata Signed-off Signed-off-by: vi-verrone --- src/implementation/flags/EvaluationDetails.php | 8 ++++---- src/implementation/flags/EvaluationDetailsBuilder.php | 2 +- src/implementation/provider/ResolutionDetails.php | 8 ++++---- src/implementation/provider/ResolutionDetailsBuilder.php | 2 +- src/interfaces/flags/EvaluationDetails.php | 2 +- src/interfaces/provider/ResolutionDetails.php | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/implementation/flags/EvaluationDetails.php b/src/implementation/flags/EvaluationDetails.php index fd39c1b..744b21b 100644 --- a/src/implementation/flags/EvaluationDetails.php +++ b/src/implementation/flags/EvaluationDetails.php @@ -19,7 +19,7 @@ class EvaluationDetails implements EvaluationDetailsInterface private ?ResolutionError $error = null; private ?string $reason = null; private ?string $variant = null; - /** @var array|null $metadata */ + /** @var array|null $metadata */ private ?array $metadata = null; public function __construct() @@ -89,7 +89,7 @@ public function setVariant(?string $variant): void } /** - * @param array|null $metadata + * @param array|null $metadata */ public function setMetadata(?array $metadata): void { @@ -104,14 +104,14 @@ public function setMetadata(?array $metadata): void } /** - * @return array + * @return array */ public function getMetadata(): array { if ($this->metadata === null) { return []; } - /** @var array $metadata */ + /** @var array $metadata */ $metadata = []; foreach ($this->metadata as $key => $value) { $metadata[$key] = $value; diff --git a/src/implementation/flags/EvaluationDetailsBuilder.php b/src/implementation/flags/EvaluationDetailsBuilder.php index f727021..ce2a45d 100644 --- a/src/implementation/flags/EvaluationDetailsBuilder.php +++ b/src/implementation/flags/EvaluationDetailsBuilder.php @@ -56,7 +56,7 @@ public function withVariant(?string $variant): EvaluationDetailsBuilder } /** - * @param array|null $metadata + * @param array|null $metadata */ public function withMetadata(?array $metadata): EvaluationDetailsBuilder { diff --git a/src/implementation/provider/ResolutionDetails.php b/src/implementation/provider/ResolutionDetails.php index 24a7ac1..1f015f3 100644 --- a/src/implementation/provider/ResolutionDetails.php +++ b/src/implementation/provider/ResolutionDetails.php @@ -17,7 +17,7 @@ class ResolutionDetails implements ResolutionDetailsInterface private ?ResolutionError $error = null; private ?string $reason = null; private ?string $variant = null; - /** @var array|null $metadata */ + /** @var array|null $metadata */ private ?array $metadata = null; /** @@ -67,7 +67,7 @@ public function setVariant(?string $variant): void } /** - * @param array|null $metadata + * @param array|null $metadata */ public function setMetadata(?array $metadata): void { @@ -82,14 +82,14 @@ public function setMetadata(?array $metadata): void } /** - * @return array|null + * @return array|null */ public function getMetadata(): ?array { if ($this->metadata === null) { return null; } - /** @var array $metadata */ + /** @var array $metadata */ $metadata = []; foreach ($this->metadata as $key => $value) { $metadata[$key] = $value; diff --git a/src/implementation/provider/ResolutionDetailsBuilder.php b/src/implementation/provider/ResolutionDetailsBuilder.php index efbadc6..45579fa 100644 --- a/src/implementation/provider/ResolutionDetailsBuilder.php +++ b/src/implementation/provider/ResolutionDetailsBuilder.php @@ -49,7 +49,7 @@ public function withVariant(string $variant): ResolutionDetailsBuilder } /** - * @param array|null $metadata + * @param array|null $metadata */ public function withMetadata(?array $metadata): ResolutionDetailsBuilder { diff --git a/src/interfaces/flags/EvaluationDetails.php b/src/interfaces/flags/EvaluationDetails.php index 7a6ed67..7af0f51 100644 --- a/src/interfaces/flags/EvaluationDetails.php +++ b/src/interfaces/flags/EvaluationDetails.php @@ -73,7 +73,7 @@ public function getVariant(): ?string; * provider is set, the evaluation details structure's flag metadata field MUST contain that * value. Otherwise, it MUST contain an empty record. * - * @return array + * @return array */ public function getMetadata(): array; } diff --git a/src/interfaces/provider/ResolutionDetails.php b/src/interfaces/provider/ResolutionDetails.php index 9fd5dd7..64a2d42 100644 --- a/src/interfaces/provider/ResolutionDetails.php +++ b/src/interfaces/provider/ResolutionDetails.php @@ -58,7 +58,7 @@ public function getVariant(): ?string; * ----------------- * The provider SHOULD populate the resolution details structure's flag metadata field. * - * @return array|null + * @return array|null */ public function getMetadata(): ?array; } From 2e513ddaf3b44cf284416ff46a733822969c8955 Mon Sep 17 00:00:00 2001 From: vi-verrone Date: Mon, 27 Jul 2026 05:58:36 +0200 Subject: [PATCH 4/6] fix: :white_check_mark: added unit tests for code coverage Signed-off-by: vi-verrone vi.verrone@gmail.com Resolve issue #180 Signed-off-by: vi-verrone --- tests/unit/EvaluationDetailsMetadataTest.php | 134 +++++++++++++++++++ tests/unit/ResolutionDetailsMetadataTest.php | 95 +++++++++++++ 2 files changed, 229 insertions(+) create mode 100644 tests/unit/EvaluationDetailsMetadataTest.php create mode 100644 tests/unit/ResolutionDetailsMetadataTest.php diff --git a/tests/unit/EvaluationDetailsMetadataTest.php b/tests/unit/EvaluationDetailsMetadataTest.php new file mode 100644 index 0000000..aa370b2 --- /dev/null +++ b/tests/unit/EvaluationDetailsMetadataTest.php @@ -0,0 +1,134 @@ +provider = Mockery::mock(Provider::class); + $this->provider->shouldReceive('getMetadata->getName')->andReturn('TestProvider'); + } + + /** + * @param bool|string|int|float|DateTime|array|null $value + */ + private function details(bool | string | int | float | DateTime | array | null $value): EvaluationDetails + { + return (new EvaluationDetailsBuilder())->withValue($value)->build(); + } + + /** + * @param bool|string|int|float|DateTime|array|null $value + * @param array $metadata + */ + private function detailsWithMetadata(bool | string | int | float | DateTime | array | null $value, array $metadata): EvaluationDetails + { + return (new EvaluationDetailsBuilder())->withValue($value)->withMetadata($metadata)->build(); + } + + /** + * @param bool|string|int|float|DateTime|array|null $value + */ + private function resolution(bool | string | int | float | DateTime | array | null $value): ResolutionDetails + { + return (new ResolutionDetailsBuilder())->withValue($value)->build(); + } + + /** + * @param bool|string|int|float|DateTime|array|null $value + * @param array $metadata + */ + private function resolutionWithMetadata(bool | string | int | float | DateTime | array | null $value, array $metadata): ResolutionDetails + { + return (new ResolutionDetailsBuilder())->withValue($value)->withMetadata($metadata)->build(); + } + + public function testEvaluationResultWithEmptyMetadata(): void + { + $details = $this->details(true); + $metadata = $details->getMetadata(); + $this->assertNotNull($metadata); + $this->assertIsArray($metadata); + $this->assertEmpty($metadata); + } + + public function testEvaluationResultWithNotEmptyMetadata(): void + { + $details = $this->detailsWithMetadata(true, [ + 'bool_value' => true, + 'string_value' => 'OK', + ]); + $metadata = $details->getMetadata(); + $this->assertNotNull($metadata); + $this->assertIsArray($metadata); + $this->assertNotEmpty($metadata); + $this->assertArrayHasKey('bool_value', $metadata); + $this->assertArrayHasKey('string_value', $metadata); + $this->assertEquals(true, $metadata['bool_value']); + $this->assertEquals('OK', $metadata['string_value']); + } + + public function testEvaluationResultMetadataImmutability(): void + { + $details = $this->detailsWithMetadata(true, [ + 'bool_value' => true, + 'string_value' => 'OK', + ]); + $metadata = $details->getMetadata(); + // let's add a new key/value and let's change the value of "bool_value" + $metadata['number_value'] = 7; + $metadata['bool_value'] = false; + // get again the metadata + $newMetadata = $details->getMetadata(); + + $this->assertNotSame($metadata, $newMetadata); + $this->assertArrayNotHasKey('number_value', $newMetadata); + $this->assertArrayHasKey('bool_value', $newMetadata); + $this->assertNotEquals(false, $newMetadata['bool_value']); + $this->assertNotSameSize($metadata, $newMetadata); + } + + public function testEvaluationDetailsFromResolutionDetailsWithoutMetadata(): void + { + $resolution = $this->resolution(true); + $details = EvaluationDetailsFactory::fromResolution('test-key', $resolution); + + $this->assertNull($resolution->getMetadata()); + $this->assertNotNull($details->getMetadata()); + $this->assertIsArray($details->getMetadata()); + $this->assertEmpty($details->getMetadata()); + $this->assertNotSame($resolution->getMetadata(), $details->getMetadata()); + } + + public function testEvaluationDetailsFromResolutionDetailsWithMetadata(): void + { + $resolution = $this->resolutionWithMetadata(true, [ + 'key' => 'value', + ]); + $details = EvaluationDetailsFactory::fromResolution('test-key', $resolution); + + $this->assertNotNull($resolution->getMetadata()); + $this->assertNotNull($details->getMetadata()); + $this->assertIsArray($details->getMetadata()); + $this->assertNotEmpty($details->getMetadata()); + $this->assertArrayHasKey('key', $details->getMetadata()); + } +} diff --git a/tests/unit/ResolutionDetailsMetadataTest.php b/tests/unit/ResolutionDetailsMetadataTest.php new file mode 100644 index 0000000..0cb5bbb --- /dev/null +++ b/tests/unit/ResolutionDetailsMetadataTest.php @@ -0,0 +1,95 @@ +provider = Mockery::mock(Provider::class); + $this->provider->shouldReceive('getMetadata->getName')->andReturn('TestProvider'); + } + + /** + * @param bool|string|int|float|DateTime|array|null $value + */ + private function details(bool | string | int | float | DateTime | array | null $value): ResolutionDetails + { + return (new ResolutionDetailsBuilder())->withValue($value)->build(); + } + + /** + * @param bool|string|int|float|DateTime|array|null $value + * @param array $metadata + */ + private function detailsWithMetadata(bool | string | int | float | DateTime | array | null $value, array $metadata): ResolutionDetails + { + return (new ResolutionDetailsBuilder())->withValue($value)->withMetadata($metadata)->build(); + } + + public function testResolutionDetailsWithoutMetadata(): void + { + $details = $this->details(true); + $this->assertNull($details->getMetadata()); + } + + public function testResolutionDetailsWithEmptyMetadata(): void + { + $details = $this->detailsWithMetadata(true, []); + $metadata = $details->getMetadata(); + $this->assertNotNull($metadata); + $this->assertIsArray($metadata); + $this->assertEmpty($metadata); + } + + public function testResolutionDetailsWithNotEmptyMetadata(): void + { + $details = $this->detailsWithMetadata(true, [ + 'bool_value' => true, + 'string_value' => 'OK', + ]); + $metadata = $details->getMetadata(); + $this->assertNotNull($metadata); + $this->assertIsArray($metadata); + $this->assertNotEmpty($metadata); + $this->assertArrayHasKey('bool_value', $metadata); + $this->assertArrayHasKey('string_value', $metadata); + $this->assertEquals(true, $metadata['bool_value']); + $this->assertEquals('OK', $metadata['string_value']); + } + + public function testResolutionDetailsMetadataImmutability(): void + { + $details = $this->detailsWithMetadata(true, [ + 'bool_value' => true, + 'string_value' => 'OK', + ]); + $metadata = $details->getMetadata(); + // let's add a new key/value and let's change the value of "bool_value" + $metadata['number_value'] = 7; + $metadata['bool_value'] = false; + // get again the metadata + $newMetadata = $details->getMetadata(); + + $this->assertNotSame($metadata, $newMetadata); + $this->assertIsArray($newMetadata); + $this->assertArrayNotHasKey('number_value', $newMetadata); + $this->assertArrayHasKey('bool_value', $newMetadata); + $this->assertNotEquals(false, $newMetadata['bool_value']); + $this->assertNotSameSize($metadata, $newMetadata); + } +} From 2c4955d2bc3486568740f70fe160376f88e36376 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 25 Jul 2026 23:45:46 -0400 Subject: [PATCH 5/6] chore(main): release 2.3.0 (#177) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit :robot: I have created a release *beep* *boop* --- ## [2.3.0](https://github.com/open-feature/php-sdk/compare/2.2.0...2.3.0) (2026-06-10) ### ✨ New Features * support isolated API instances ([#171](https://github.com/open-feature/php-sdk/issues/171)) ([48301ee](https://github.com/open-feature/php-sdk/commit/48301ee460271b3f2b0703960d9d7d1251875eb7)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Signed-off-by: vi-verrone --- .release-please-manifest.json | 2 +- CHANGELOG.md | 7 +++++++ README.md | 4 ++-- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index a5d1cf2..9965a34 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.2.0" + ".": "2.3.0" } diff --git a/CHANGELOG.md b/CHANGELOG.md index f9be3b9..01b5d56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [2.3.0](https://github.com/open-feature/php-sdk/compare/2.2.0...2.3.0) (2026-06-10) + + +### ✨ New Features + +* support isolated API instances ([#171](https://github.com/open-feature/php-sdk/issues/171)) ([48301ee](https://github.com/open-feature/php-sdk/commit/48301ee460271b3f2b0703960d9d7d1251875eb7)) + ## [2.2.0](https://github.com/open-feature/php-sdk/compare/2.1.2...2.2.0) (2026-06-08) diff --git a/README.md b/README.md index c8951b7..c37e1ca 100644 --- a/README.md +++ b/README.md @@ -18,8 +18,8 @@ - - Release + + Release From 43dac9be786737a073ac4a2088e99b8ca5c5fb64 Mon Sep 17 00:00:00 2001 From: vi-verrone Date: Thu, 30 Jul 2026 11:16:16 +0200 Subject: [PATCH 6/6] feat: merge main into the PR branch Signed-off-by: vi-verrone