Skip to content

feat: add evaluateFlags() API for single-call flag evaluation#131

Open
dmarticus wants to merge 2 commits intomainfrom
posthog-code/php-evaluate-flags-api-v2
Open

feat: add evaluateFlags() API for single-call flag evaluation#131
dmarticus wants to merge 2 commits intomainfrom
posthog-code/php-evaluate-flags-api-v2

Conversation

@dmarticus
Copy link
Copy Markdown
Contributor

@dmarticus dmarticus commented Apr 27, 2026

Summary

Adds evaluateFlags(), a single-call flag evaluation API that returns a FeatureFlagEvaluations snapshot for one distinct id:

  • One /flags request per call; reads on the snapshot make zero further requests.
  • isEnabled($key) / getFlag($key) fire a deduped $feature_flag_called event with full metadata (id, version, reason, request_id) on first access. getFlagPayload($key) is silent.
  • only([...]) / onlyAccessed() return filtered clones for scoping which flags attach to a captured event.
  • capture(['flags' => $snapshot, …]) attaches $feature/<key> and $active_feature_flags without a fresh /flags round trip.
  • Phase 1 is purely additive — the existing getFeatureFlag / getFeatureFlagResult / getAllFlags / send_feature_flags paths keep working.

RFC · reference SDK PRs: posthog-python#539, posthog-js#3476.

Design decisions

  • Snapshot takes a tiny FeatureFlagEvaluationsHost interface (captureFlagCalledIfNeeded, logWarning) instead of a full Client, so unit tests can use a fake without spinning up the SDK.
  • Filtered clones get a fresh access set; reads on the child never back-propagate to the parent.
  • Empty-distinct_id snapshots short-circuit event emission inside recordAccess rather than at every call site, so isEnabled / getFlag still return sane values without leaking events with empty actors.
  • Locally-evaluated records omit $feature_flag_request_id (it's per-/flags-response) but emit locally_evaluated=true and reason "Evaluated locally" to match the existing single-flag local path.
  • flags= on capture() takes precedence over send_feature_flags, letting callers adopt the new path without ripping the old one out.
  • Drive-by fix: SizeLimitedHash::contains/add were comparing values to keys and pushing onto the outer map, so the per-distinct_id $feature_flag_called dedup never matched after the first event. The new snapshot path requires real dedup, so it's fixed here.

Phase 2 follow-ups (deliberately out of scope)

  • Deprecate getFeatureFlag, isFeatureEnabled, getFeatureFlagResult, getAllFlags, and the send_feature_flags capture option.
  • Plumb flag_definitions_loaded_at through the snapshot — posthog-php doesn't track that timestamp on the local poller today.

Created with PostHog Code

Phase 1 of the Server SDK Feature Flag Evaluations RFC. Mirrors the Node
(posthog-js#3476) and Python (posthog-python#539) implementations.

* `Client::evaluateFlags()` returns a `FeatureFlagEvaluations` snapshot. Reads
  on the snapshot do not trigger additional `/flags` requests; access via
  `isEnabled` / `getFlag` fires a deduped `$feature_flag_called` event the
  first time each key is touched. `getFlagPayload` is silent.
* `capture()` accepts a `flags` snapshot to attach `$feature/<key>` and
  `$active_feature_flags` properties without a fresh `/flags` round trip.
* The single-flag dedup is extracted to `Client::captureFlagCalledIfNeeded()`,
  shared by the legacy path and the snapshot.
* `flag_keys_to_evaluate` and `geoip_disable` are forwarded on the `/flags`
  request body when callers pass `flagKeys` or `disableGeoip`.
* New `feature_flags_log_warnings` option silences filter warnings emitted
  from `only()` / `onlyAccessed()`.

Also fixes a pre-existing bug in `SizeLimitedHash::contains/add` that caused
the per-distinct_id `$feature_flag_called` dedup to never match after the
first event. The new snapshot path requires real dedup, and existing tests
only ever made a single call so the bug was invisible until now.

Generated-By: PostHog Code
Task-Id: 1f29305a-ee56-456e-a341-8faa4eb8716d
The file pairs `require_once 'test/error_log_mock.php'` with class
declarations, matching the pattern in FeatureFlagLocalEvaluationTest.
The existing tests do the same thing; suppressing the rule per-file is
consistent with that precedent.

Generated-By: PostHog Code
Task-Id: 1f29305a-ee56-456e-a341-8faa4eb8716d
@dmarticus dmarticus marked this pull request as ready for review April 27, 2026 22:11
@dmarticus dmarticus requested a review from a team as a code owner April 27, 2026 22:11
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.

1 participant