From 56d344a52e39f705832c2751ad2db3e1289b0d3d Mon Sep 17 00:00:00 2001 From: "posthog[bot]" <206114724+posthog[bot]@users.noreply.github.com> Date: Sat, 8 Aug 2026 16:05:28 +0000 Subject: [PATCH] feat: caller-supplied default value for FeatureFlagEvaluations::isEnabled() Adds an optional `$defaultValue` parameter so callers can distinguish "flag resolved to false" from "flag has no value" (missing key, not loaded, failed request). Generated-By: PostHog Code Task-Id: 11d33c8f-53c1-4d18-bd8f-2da5e8678f4e --- .../feature-flag-evaluations-default-value.md | 5 ++++ api/public-api.json | 10 +++++++ lib/FeatureFlagEvaluations.php | 8 ++++-- test/FeatureFlagEvaluationsTest.php | 28 +++++++++++++++++++ 4 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 .changeset/feature-flag-evaluations-default-value.md diff --git a/.changeset/feature-flag-evaluations-default-value.md b/.changeset/feature-flag-evaluations-default-value.md new file mode 100644 index 0000000..647fb21 --- /dev/null +++ b/.changeset/feature-flag-evaluations-default-value.md @@ -0,0 +1,5 @@ +--- +"posthog-php": minor +--- + +Add an optional `$defaultValue` parameter to `FeatureFlagEvaluations::isEnabled()`. Previously, an unknown/unresolved flag always collapsed to `false` with no way for a caller to distinguish "flag is off" from "flag has no value" — the spec requires the SDK to return a caller-supplied default in that case. `isEnabled($key, defaultValue: true)` now returns `true` when the flag has no resolvable value; a flag with a real value, including `false`, always wins over the default. Existing callers who don't pass it see identical behavior. diff --git a/api/public-api.json b/api/public-api.json index d2c9f7d..15e5e58 100644 --- a/api/public-api.json +++ b/api/public-api.json @@ -2695,6 +2695,16 @@ "default": null, "defaultConstant": null, "hasDefault": false + }, + { + "name": "defaultValue", + "type": "bool", + "byReference": false, + "variadic": false, + "optional": true, + "default": false, + "defaultConstant": null, + "hasDefault": true } ] }, diff --git a/lib/FeatureFlagEvaluations.php b/lib/FeatureFlagEvaluations.php index 137d6f6..b56fb41 100644 --- a/lib/FeatureFlagEvaluations.php +++ b/lib/FeatureFlagEvaluations.php @@ -52,17 +52,19 @@ public function getKeys(): array } /** - * Whether the flag is enabled for the snapshot's distinct id. Returns false for unknown keys. + * Whether the flag is enabled for the snapshot's distinct id. Returns $defaultValue for + * unknown keys; a flag with a real value (including false) always takes precedence. * * @param string $key Feature flag key. + * @param bool $defaultValue Value to return when the flag has no resolvable value. * @return bool */ - public function isEnabled(string $key): bool + public function isEnabled(string $key, bool $defaultValue = false): bool { $record = $this->flags[$key] ?? null; $this->recordAccess($key, $record); - return $record?->enabled ?? false; + return $record?->enabled ?? $defaultValue; } /** diff --git a/test/FeatureFlagEvaluationsTest.php b/test/FeatureFlagEvaluationsTest.php index 3c59364..12d3544 100644 --- a/test/FeatureFlagEvaluationsTest.php +++ b/test/FeatureFlagEvaluationsTest.php @@ -181,6 +181,34 @@ public function testUnknownKeyAccessRecordsFlagMissingError(): void $this->assertSame('flag_missing', $properties['$feature_flag_error']); } + public function testIsEnabledReturnsCallerSuppliedDefaultForUnknownKey(): void + { + $host = new FakeFlagEvaluationsHost(); + $snapshot = new FeatureFlagEvaluations( + 'user-1', + [], + [], + $host + ); + + $this->assertTrue($snapshot->isEnabled('does-not-exist', true)); + $this->assertFalse($snapshot->isEnabled('does-not-exist')); + $this->assertFalse($snapshot->isEnabled('does-not-exist', false)); + } + + public function testIsEnabledDefaultDoesNotOverrideARealFalseValue(): void + { + $host = new FakeFlagEvaluationsHost(); + $snapshot = new FeatureFlagEvaluations( + 'user-1', + ['flag-a' => $this->makeRecord('flag-a', false)], + [], + $host + ); + + $this->assertFalse($snapshot->isEnabled('flag-a', true)); + } + public function testOnlyWarnsOnUnknownKeys(): void { $host = new FakeFlagEvaluationsHost();