Skip to content

Commit 914a826

Browse files
authored
feat: add evaluateFlags() API for single-call flag evaluation (#131)
* feat: add evaluateFlags() API for single-call flag evaluation 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 * chore: silence PSR1.SideEffects warning in evaluate_flags test 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 * fix: address review feedback on evaluate_flags() PR Mirrors the fixes applied to posthog-python#539 after review: * `onlyAccessed()` returns an empty snapshot when nothing has been accessed instead of warning + falling back to all flags. The fallback was contradictory with the method's name and surprising for callers doing `capture(flags: $snapshot->onlyAccessed())` early in a request before any flag had been read. * `FeatureFlagEvaluations` tracks `errorsWhileComputingFlags` and `quotaLimited` from the /flags response and combines them with the per-flag `flag_missing` error in `$feature_flag_called`, matching the granularity the single-flag path emits today. * `capture()` now logs a warning when both `flags` and `send_feature_flags` are passed; precedence is unchanged (snapshot wins) but the conflict is no longer silent. * Tightened the `flagKeys` docstring on `Client::evaluateFlags()` and the `PostHog::evaluateFlags()` facade so it's clear it scopes the underlying /flags request, distinct from the in-memory `only([keys])` filter. Generated-By: PostHog Code Task-Id: 1f29305a-ee56-456e-a341-8faa4eb8716d * feat: deprecate legacy single-flag methods and capture send_feature_flags Phase 2 of the Server SDK Feature Flag Evaluations RFC, shipped alongside Phase 1 (mirroring posthog-python#539's eda573d). * `Client::isFeatureEnabled()`, `Client::getFeatureFlag()`, and `Client::getFeatureFlagPayload()` (along with their `PostHog::*` static facades) now emit `E_USER_DEPRECATED` pointing at `evaluateFlags()`. * `capture(['send_feature_flags' => true])` emits the same deprecation when the legacy block actually runs (the existing precedence — snapshot wins over `send_feature_flags` — is unchanged). * `Client::isFeatureEnabled()` now calls `getFeatureFlagResult()` directly instead of routing through the public `getFeatureFlag()`, so a single user-level call surfaces exactly one deprecation warning, not two. * `Client::getFeatureFlagResult()` and `Client::getAllFlags()` are intentionally NOT deprecated — they expose data (rich single-flag result, arbitrary key list) that the new snapshot API doesn't yet cover. `getFeatureFlagPayload()` was already marked `@deprecated` in v4.0.0 with a message pointing at `getFeatureFlagResult()`. Updated the message to point at `evaluateFlags()` and added the runtime `trigger_error` so users who pin warnings to errors (or read PHP error logs) get the heads-up. Generated-By: PostHog Code Task-Id: 1f29305a-ee56-456e-a341-8faa4eb8716d * fix: address Dustin's review feedback on evaluateFlags() PR Seven items from the review on #131: * Skip the /flags request when local definitions cover every flag and they all evaluate without inconclusive results. The previous code always hit the server unless `onlyEvaluateLocally` was set, even when local eval could have answered everything. * Bring `$feature_flag_called` to parity with the legacy single-flag path: always emit `locally_evaluated` (true or false, not omitted for remote), and propagate `$feature_flag_evaluated_at` from the /flags response for remote records. * Missing flags now report `$feature_flag_response: null` (not `false`), matching the legacy single-flag path so consumers can distinguish "flag exists and is disabled" from "flag not found". * Defensively handle pre-decoded payloads in the /flags response. Some middleware deserializes JSON transparently; the snapshot now accepts an array/object payload as-is instead of feeding it to `json_decode`. * Deprecate `getFeatureFlagResult()` (and its `PostHog::*` static facade). Refactored `getFeatureFlagResult` body into a private `doGetFeatureFlagResult` helper so `isFeatureEnabled()` / `getFeatureFlag()` / `getFeatureFlagPayload()` can route through it without cascading deprecation warnings. * Drop the `feature_flags_log_warnings` config option. The only remaining warning (unknown keys passed to `only([...])`) doesn't warrant a dedicated knob; if you don't want the warning, fix the typo. Generated-By: PostHog Code Task-Id: 1f29305a-ee56-456e-a341-8faa4eb8716d
1 parent d70e432 commit 914a826

9 files changed

Lines changed: 1410 additions & 41 deletions

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
## Unreleased
2+
3+
* feat(flags): Add `evaluateFlags()` API for single-call flag evaluation. Returns a
4+
`FeatureFlagEvaluations` snapshot you can read repeatedly without further `/flags` requests; pass
5+
it to `capture()` via the new `flags` key to attach `$feature/<key>` and `$active_feature_flags`
6+
on the captured event without an extra round trip.
7+
* feat(flags): Deprecate `isFeatureEnabled()`, `getFeatureFlag()`, `getFeatureFlagResult()`,
8+
`getFeatureFlagPayload()`, and the `send_feature_flags` `capture()` option in favor of
9+
`evaluateFlags()`. Each emits an `E_USER_DEPRECATED` warning pointing at the new API; existing
10+
callers keep working unchanged until the next major version. `getAllFlags()` is intentionally
11+
*not* deprecated — it returns an arbitrary key list the snapshot API doesn't yet cover.
12+
* fix(flags): `SizeLimitedHash::contains()` and `add()` were storing entries on the outer map and
13+
comparing values to keys, so the per-distinct_id `$feature_flag_called` dedup never matched after
14+
the first event. Both helpers now operate on a per-key set as intended.
15+
116
## 4.2.4 - 2026-04-28
217

318
* [Full Changelog](https://github.com/PostHog/posthog-php/compare/4.2.3...4.2.4)

0 commit comments

Comments
 (0)