Skip to content

Commit 784d706

Browse files
committed
feat: support ResolutionError, init test-harness
1 parent 37f13e1 commit 784d706

19 files changed

+128
-51
lines changed

Diff for: .gitmodules

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "test-harness"]
2+
path = test-harness
3+
url = https://github.com/open-feature/test-harness.git

Diff for: README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# OpenFeature SDK for PHP
22

33
[![Project Status: WIP – Initial development is in progress, but there has not yet been a stable, usable release suitable for the public.](https://www.repostatus.org/badges/latest/wip.svg)](https://www.repostatus.org/#wip)
4-
[![Specification](https://img.shields.io/static/v1?label=Specification&message=v0.4.0&color=yellow)](https://github.com/open-feature/spec/tree/v0.4.0)
4+
[![Specification](https://img.shields.io/static/v1?label=Specification&message=v0.5.0&color=yellow)](https://github.com/open-feature/spec/tree/v0.5.0)
55
[![Latest Stable Version](http://poser.pugx.org/open-feature/sdk/v)](https://packagist.org/packages/open-feature/sdk)
66
[![Total Downloads](http://poser.pugx.org/open-feature/sdk/downloads)](https://packagist.org/packages/open-feature/sdk)
77
[![License](http://poser.pugx.org/open-feature/sdk/license)](https://packagist.org/packages/open-feature/sdk)

Diff for: composer.json

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"psr/log": "^1.1"
1515
},
1616
"require-dev": {
17+
"behat/behat": "^3.11",
1718
"captainhook/captainhook": "^5.10",
1819
"captainhook/plugin-composer": "^5.3",
1920
"ergebnis/composer-normalize": "^2.25",

Diff for: src/OpenFeatureClient.php

+4-3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use OpenFeature\implementation\hooks\HookExecutor;
1919
use OpenFeature\implementation\hooks\HookHints;
2020
use OpenFeature\implementation\provider\Reason;
21+
use OpenFeature\implementation\provider\ResolutionError;
2122
use OpenFeature\interfaces\common\LoggerAwareTrait;
2223
use OpenFeature\interfaces\common\Metadata as MetadataInterface;
2324
use OpenFeature\interfaces\flags\API;
@@ -31,7 +32,7 @@
3132
use OpenFeature\interfaces\provider\ErrorCode;
3233
use OpenFeature\interfaces\provider\Provider;
3334
use OpenFeature\interfaces\provider\ResolutionDetails;
34-
use OpenFeature\interfaces\provider\ThrowableWithErrorCode;
35+
use OpenFeature\interfaces\provider\ThrowableWithResolutionError;
3536
use Psr\Log\LoggerAwareInterface;
3637
use Throwable;
3738

@@ -375,12 +376,12 @@ private function evaluateFlag(
375376
),
376377
);
377378

378-
$errorCode = $err instanceof ThrowableWithErrorCode ? $err->getErrorCode() : ErrorCode::GENERAL();
379+
$error = $err instanceof ThrowableWithResolutionError ? $err->getResolutionError() : new ResolutionError(ErrorCode::GENERAL());
379380

380381
$details = (new EvaluationDetailsBuilder())
381382
->withValue($defaultValue)
382383
->withReason(Reason::ERROR)
383-
->withErrorCode($errorCode)
384+
->withError($error)
384385
->build();
385386

386387
$hookExecutor->errorHooks($flagValueType, $hookContext, $err, $mergedRemainingHooks, $hookHints);

Diff for: src/implementation/flags/EvaluationDetails.php

+6-6
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@
66

77
use DateTime;
88
use OpenFeature\interfaces\flags\EvaluationDetails as EvaluationDetailsInterface;
9-
use OpenFeature\interfaces\provider\ErrorCode;
9+
use OpenFeature\interfaces\provider\ResolutionError;
1010

1111
class EvaluationDetails implements EvaluationDetailsInterface
1212
{
1313
private string $flagKey = '';
1414
/** @var bool|string|int|float|DateTime|mixed[]|null $value */
1515
private $value;
16-
private ?ErrorCode $errorCode = null;
16+
private ?ResolutionError $error = null;
1717
private ?string $reason = null;
1818
private ?string $variant = null;
1919

@@ -53,14 +53,14 @@ public function setValue($value): void
5353
$this->value = $value;
5454
}
5555

56-
public function getErrorCode(): ?ErrorCode
56+
public function getError(): ?ResolutionError
5757
{
58-
return $this->errorCode;
58+
return $this->error;
5959
}
6060

61-
public function setErrorCode(?ErrorCode $errorCode): void
61+
public function setError(?ResolutionError $error): void
6262
{
63-
$this->errorCode = $errorCode;
63+
$this->error = $error;
6464
}
6565

6666
public function getReason(): ?string

Diff for: src/implementation/flags/EvaluationDetailsBuilder.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
use DateTime;
88
use OpenFeature\interfaces\flags\EvaluationDetails as EvaluationDetailsInterface;
9-
use OpenFeature\interfaces\provider\ErrorCode;
9+
use OpenFeature\interfaces\provider\ResolutionError;
1010

1111
class EvaluationDetailsBuilder
1212
{
@@ -34,9 +34,9 @@ public function withValue($value): EvaluationDetailsBuilder
3434
return $this;
3535
}
3636

37-
public function withErrorCode(?ErrorCode $errorCode): EvaluationDetailsBuilder
37+
public function withError(?ResolutionError $errorCode): EvaluationDetailsBuilder
3838
{
39-
$this->details->setErrorCode($errorCode);
39+
$this->details->setError($errorCode);
4040

4141
return $this;
4242
}

Diff for: src/implementation/flags/EvaluationDetailsFactory.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public static function fromResolution(string $flagKey, ResolutionDetails $detail
3131
return (new EvaluationDetailsBuilder())
3232
->withFlagKey($flagKey)
3333
->withValue($details->getValue())
34-
->withErrorCode($details->getErrorCode())
34+
->withError($details->getError())
3535
->withReason($details->getReason())
3636
->withVariant($details->getVariant())
3737
->build();

Diff for: src/implementation/provider/ResolutionDetails.php

+6-6
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55
namespace OpenFeature\implementation\provider;
66

77
use DateTime;
8-
use OpenFeature\interfaces\provider\ErrorCode;
98
use OpenFeature\interfaces\provider\ResolutionDetails as ResolutionDetailsInterface;
9+
use OpenFeature\interfaces\provider\ResolutionError;
1010

1111
class ResolutionDetails implements ResolutionDetailsInterface
1212
{
1313
/** @var bool|string|int|float|DateTime|mixed[]|null $value */
1414
private $value = null;
15-
private ?ErrorCode $errorCode = null;
15+
private ?ResolutionError $error = null;
1616
private ?string $reason = null;
1717
private ?string $variant = null;
1818

@@ -32,14 +32,14 @@ public function setValue($value): void
3232
$this->value = $value;
3333
}
3434

35-
public function getErrorCode(): ?ErrorCode
35+
public function getError(): ?ResolutionError
3636
{
37-
return $this->errorCode;
37+
return $this->error;
3838
}
3939

40-
public function setErrorCode(?ErrorCode $errorCode): void
40+
public function setError(?ResolutionError $error): void
4141
{
42-
$this->errorCode = $errorCode;
42+
$this->error = $error;
4343
}
4444

4545
public function getReason(): ?string

Diff for: src/implementation/provider/ResolutionDetailsBuilder.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
namespace OpenFeature\implementation\provider;
66

77
use DateTime;
8-
use OpenFeature\interfaces\provider\ErrorCode;
98
use OpenFeature\interfaces\provider\ResolutionDetails as ResolutionDetailsInterface;
9+
use OpenFeature\interfaces\provider\ResolutionError;
1010

1111
class ResolutionDetailsBuilder
1212
{
@@ -27,9 +27,9 @@ public function withValue($value): ResolutionDetailsBuilder
2727
return $this;
2828
}
2929

30-
public function withErrorCode(ErrorCode $errorCode): ResolutionDetailsBuilder
30+
public function withError(ResolutionError $errorCode): ResolutionDetailsBuilder
3131
{
32-
$this->details->setErrorCode($errorCode);
32+
$this->details->setError($errorCode);
3333

3434
return $this;
3535
}

Diff for: src/implementation/provider/ResolutionError.php

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenFeature\implementation\provider;
6+
7+
use OpenFeature\interfaces\provider\ErrorCode;
8+
use OpenFeature\interfaces\provider\ResolutionError as ResolutionErrorInterface;
9+
10+
class ResolutionError implements ResolutionErrorInterface
11+
{
12+
private ErrorCode $code;
13+
private ?string $message;
14+
15+
public function __construct(ErrorCode $code, ?string $message = null)
16+
{
17+
$this->code = $code;
18+
$this->message = $message;
19+
}
20+
21+
public function getCode(): ErrorCode
22+
{
23+
return $this->code;
24+
}
25+
26+
public function getMessage(): ?string
27+
{
28+
return $this->message;
29+
}
30+
}

Diff for: src/interfaces/flags/EvaluationDetails.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
namespace OpenFeature\interfaces\flags;
66

77
use DateTime;
8-
use OpenFeature\interfaces\provider\ErrorCode;
8+
use OpenFeature\interfaces\provider\ResolutionError;
99

1010
/**
1111
* A structure representing the result of the flag evaluation process,
@@ -43,7 +43,7 @@ public function getValue();
4343
* MUST contain a string identifying an error occurred during flag evaluation and the
4444
* nature of the error.
4545
*/
46-
public function getErrorCode(): ?ErrorCode;
46+
public function getError(): ?ResolutionError;
4747

4848
/**
4949
* -----------------

Diff for: src/interfaces/provider/ResolutionDetails.php

+4-4
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@ public function getValue();
2727

2828
/**
2929
* ---------------
30-
* Requirement 2.7
30+
* Requirement 2.8
3131
* ---------------
32-
* In cases of normal execution, the provider MUST NOT populate the flag resolution structure's error code
33-
* field, or otherwise must populate it with a null or falsy value.
32+
* In cases of abnormal execution, the provider MUST indicate an error using the idioms of the implementation
33+
* language, with an associated error code and optional associated error message.
3434
*/
35-
public function getErrorCode(): ?ErrorCode;
35+
public function getError(): ?ResolutionError;
3636

3737
/**
3838
* ---------------

Diff for: src/interfaces/provider/ResolutionError.php

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenFeature\interfaces\provider;
6+
7+
/**
8+
* ---------------
9+
* Requirement 2.8
10+
* ---------------
11+
* In cases of abnormal execution, the provider MUST indicate an error using the idioms of the implementation
12+
* language, with an associated error code and optional associated error message.
13+
*/
14+
interface ResolutionError
15+
{
16+
/**
17+
* ---------------
18+
* Requirement 2.8
19+
* ---------------
20+
* In cases of abnormal execution, the provider MUST indicate an error using the idioms of the implementation
21+
* language, with an associated error code and optional associated error message.
22+
*/
23+
public function getCode(): ErrorCode;
24+
25+
/**
26+
* ---------------
27+
* Requirement 2.8
28+
* ---------------
29+
* In cases of abnormal execution, the provider MUST indicate an error using the idioms of the implementation
30+
* language, with an associated error code and optional associated error message.
31+
*/
32+
public function getMessage(): ?string;
33+
}

Diff for: src/interfaces/provider/ThrowableWithErrorCode.php

-12
This file was deleted.
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenFeature\interfaces\provider;
6+
7+
use Throwable;
8+
9+
interface ThrowableWithResolutionError extends Throwable
10+
{
11+
public function getResolutionError(): ResolutionError;
12+
}

Diff for: test-harness

Submodule test-harness added at 5153eac

Diff for: tests/unit/HooksTest.php

+1-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
use OpenFeature\interfaces\hooks\Hook;
2525
use OpenFeature\interfaces\hooks\HookContext;
2626
use OpenFeature\interfaces\hooks\HookHints as HookHintsInterface;
27-
use OpenFeature\interfaces\provider\ResolutionDetails;
2827
use Throwable;
2928

3029
use function method_exists;
@@ -79,7 +78,7 @@ public function testValuePropertiesMustBeImmutable(): void
7978

8079
$hook = $this->mockery(TestHook::class)->makePartial();
8180

82-
$hook->shouldReceive('after')->andReturnUsing(function (HookContext $context, ResolutionDetails $details, HookHints $hints) use ($testRunner, $expectedHookContext) {
81+
$hook->shouldReceive('before')->andReturnUsing(function (HookContext $context, HookHints $hints) use ($testRunner, $expectedHookContext) {
8382
// there are no setters on any interfaces provided by HookContext or its children...
8483
$testRunner->assertEquals($expectedHookContext, $context);
8584
});

Diff for: tests/unit/OpenFeatureClientTest.php

+7-3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use OpenFeature\implementation\flags\EvaluationOptions;
1919
use OpenFeature\implementation\provider\ResolutionDetailsBuilder;
2020
use OpenFeature\implementation\provider\ResolutionDetailsFactory;
21+
use OpenFeature\implementation\provider\ResolutionError;
2122
use OpenFeature\interfaces\hooks\Hook;
2223
use OpenFeature\interfaces\hooks\HookContext;
2324
use OpenFeature\interfaces\provider\ErrorCode;
@@ -488,13 +489,16 @@ public function testClientEvaluationDetailsAbnormalExecutionHasErrorCodeField():
488489
$expectedErrorCode = ErrorCode::FLAG_NOT_FOUND();
489490

490491
$mockProvider = $this->getDefaultMockProvider();
491-
$mockProvider->shouldReceive('resolveBooleanValue')->andReturns((new ResolutionDetailsBuilder())->withValue(true)->withErrorCode($expectedErrorCode)->build());
492+
$mockProvider->shouldReceive('resolveBooleanValue')->andReturns((new ResolutionDetailsBuilder())->withValue(true)->withError(new ResolutionError($expectedErrorCode))->build());
492493

493494
$api->setProvider($mockProvider);
494495

495496
$actualDetails = $client->getBooleanDetails('flagKey', false, null, null);
496497

497-
$this->assertEquals($expectedErrorCode, $actualDetails->getErrorCode());
498+
/** @var ResolutionError $resolutionError */
499+
$resolutionError = $actualDetails->getError();
500+
$this->assertNotNull($resolutionError);
501+
$this->assertEquals($expectedErrorCode, $resolutionError->getCode());
498502
}
499503

500504
/**
@@ -513,7 +517,7 @@ public function testClientEvaluationDetailsAbnormalExecutionHasReasonField(): vo
513517

514518
$mockProvider = $this->getDefaultMockProvider();
515519
$mockProvider->shouldReceive('resolveBooleanValue')
516-
->andReturns((new ResolutionDetailsBuilder())->withValue(true)->withErrorCode($expectedErrorCode)->withReason($expectedReason)->build());
520+
->andReturns((new ResolutionDetailsBuilder())->withValue(true)->withError(new ResolutionError($expectedErrorCode))->withReason($expectedReason)->build());
517521

518522
$api->setProvider($mockProvider);
519523

0 commit comments

Comments
 (0)