Skip to content

feat: add caller-supplied default value to FeatureFlagEvaluations::isEnabled() - #216

Draft
posthog[bot] wants to merge 1 commit into
mainfrom
posthog-code/feature-flag-evaluations-default-value
Draft

feat: add caller-supplied default value to FeatureFlagEvaluations::isEnabled()#216
posthog[bot] wants to merge 1 commit into
mainfrom
posthog-code/feature-flag-evaluations-default-value

Conversation

@posthog

@posthog posthog Bot commented Aug 8, 2026

Copy link
Copy Markdown

💡 Motivation and Context

Brings posthog-php into compliance with the is-feature-enabled spec in PostHog/sdk-specs, which is a hard SHALL with no server-SDK carve-out: "The SDK SHALL accept a caller-supplied boolean default ... and SHALL return it whenever the flag has no value ... A flag that has a value — including false — always wins over the caller-supplied default."

Per the compliance audit (compliance/posthog-php.md#n8), the legacy Client::isFeatureEnabled() lacks this, but that method is already deprecated in favor of Client::evaluateFlags() + FeatureFlagEvaluations::isEnabled(). The canonical, non-deprecated FeatureFlagEvaluations::isEnabled(string $key): bool has the same gap: it hardcodes ?? false on a miss, so a genuinely-disabled flag and an unresolved/unknown flag are indistinguishable, and there's no way for a caller to opt into a true fallback.

This PR fixes the canonical path (not the deprecated legacy method, per guidance not to invest further in deprecated APIs) by adding an optional defaultValue parameter.

What changed

  • FeatureFlagEvaluations::isEnabled(string $key, bool $defaultValue = false): bool — the default is returned only when the flag has no resolvable value; any real value (including false) still wins.
  • Added .changeset/feature-flag-evaluations-default-value.md per this repo's release process.
  • Updated api/public-api.json via composer api:update to reflect the new optional parameter.

Backwards compatibility

Purely additive — the new parameter is optional and defaults to false, which is exactly today's unconditional fallback behavior. No existing caller's behavior changes.

💚 How did you test it?

  • Added two new test cases to test/FeatureFlagEvaluationsTest.php:
    • testIsEnabledReturnsCallerSuppliedDefaultForUnknownKey — unknown key returns the caller-supplied default (true/false), and still defaults to false when omitted.
    • testIsEnabledDefaultDoesNotOverrideARealFalseValue — a flag that resolves to a real false is not overridden by defaultValue: true.
  • Ran the full suite: vendor/bin/phpunit — 447 tests, 3808 assertions, all passing.
  • Ran composer api:check (via scripts/check-public-api.php) to confirm the only public API delta is the new optional parameter, then composer api:update to refresh the snapshot.
  • Ran phpcs --standard=phpcs.xml against the changed files — clean.

📝 Checklist

  • I reviewed the submitted code.
  • I added tests to verify the changes.
  • I updated the docs if needed.
  • No breaking change; changeset entry added.

If releasing new changes

  • Added a .changeset/*.md change intent file

🤖 Agent context

Autonomy: Fully autonomous

This PR was produced by an unattended, scheduled agent run ("Daily SDK Compliance Implementer") that reads the compliance matrices in PostHog/sdk-specs and implements one backward-compatible remediation per run. The candidate was selected from the matrix's global open-gaps list, cross-checked against this repo's open PRs/branches for duplicates (none found — an old, already-merged 2022 branch, ff-remove-defaultvalue, was a false-positive name match, unrelated to this change), and implemented directly against the canonical (non-deprecated) isEnabled() API rather than the deprecated legacy method.


Created with PostHog Code

…bled()

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
@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

posthog-php Compliance Report

Date: 2026-08-08 16:08:33 UTC
Duration: 95255ms

✅ All Tests Passed!

46/46 tests passed


Capture Tests

29/29 tests passed

View Details
Test Status Duration
Format Validation.Event Has Required Fields 11ms
Format Validation.Event Has Uuid 5ms
Format Validation.Event Has Lib Properties 5ms
Format Validation.Distinct Id Is String 5ms
Format Validation.Token Is Present 4ms
Format Validation.Custom Properties Preserved 5ms
Format Validation.Event Has Timestamp 5ms
Retry Behavior.Retries On 503 5315ms
Retry Behavior.Does Not Retry On 400 2009ms
Retry Behavior.Does Not Retry On 401 2009ms
Retry Behavior.Respects Retry After Header 8015ms
Retry Behavior.Implements Backoff 15729ms
Retry Behavior.Retries On 500 5116ms
Retry Behavior.Retries On 502 5112ms
Retry Behavior.Retries On 504 5116ms
Retry Behavior.Max Retries Respected 16529ms
Deduplication.Generates Unique Uuids 11ms
Deduplication.Preserves Uuid On Retry 5113ms
Deduplication.Preserves Uuid And Timestamp On Retry 10322ms
Deduplication.Preserves Uuid And Timestamp On Batch Retry 5113ms
Deduplication.No Duplicate Events In Batch 11ms
Deduplication.Different Events Have Different Uuids 5ms
Compression.Sends Gzip When Enabled 5ms
Batch Format.Uses Proper Batch Structure 5ms
Batch Format.Flush With No Events Sends Nothing 3ms
Batch Format.Multiple Events Batched Together 9ms
Error Handling.Does Not Retry On 403 2007ms
Error Handling.Does Not Retry On 413 2009ms
Error Handling.Retries On 408 5115ms

Feature_Flags Tests

17/17 tests passed

View Details
Test Status Duration
Request Payload.Request With Person Properties Device Id 6ms
Request Payload.Flags Request Uses V2 Query Param 5ms
Request Payload.Flags Request Hits Flags Path Not Decide 4ms
Request Payload.Flags Request Omits Authorization Header 4ms
Request Payload.Token In Flags Body Matches Init 4ms
Request Payload.Groups Round Trip 4ms
Request Payload.Groups Default To Empty Object 4ms
Request Payload.Disable Geoip False Propagates As Geoip Disable False 4ms
Request Payload.Disable Geoip Omitted Defaults To False 4ms
Request Payload.Flag Keys To Evaluate Contains Only Requested Key 4ms
Request Lifecycle.No Flags Request On Init Alone 2ms
Request Lifecycle.No Flags Request On Normal Capture 5ms
Request Lifecycle.Two Flag Calls Produce Two Remote Requests 6ms
Request Lifecycle.Mock Response Value Is Returned To Caller 4ms
Retry Behavior.Retries Flags On 502 107ms
Retry Behavior.Retries Flags On 504 107ms
Side Effect Events.Get Feature Flag Captures Feature Flag Called Event 8ms

@posthog posthog Bot changed the title Add caller-supplied default value to FeatureFlagEvaluations::isEnabled() feat: add caller-supplied default value to FeatureFlagEvaluations::isEnabled() Aug 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants