Skip to content

fix(context): forward all BucketingAttributes to ExperienceManager - #37

Merged
abbaseya merged 1 commit into
mainfrom
fix/context-forward-bucketing-attributes
May 21, 2026
Merged

fix(context): forward all BucketingAttributes to ExperienceManager#37
abbaseya merged 1 commit into
mainfrom
fix/context-forward-bucketing-attributes

Conversation

@abbaseya

Copy link
Copy Markdown
Contributor

Summary

Context::runExperience and Context::runExperiences rebuilt a fresh BucketingAttributes object with only four hardcoded keys (visitorProperties, locationProperties, updateVisitorProperties, environment) before forwarding to ExperienceManager::selectVariation / selectVariations. Every other BucketingAttributes field — enableTracking, forceVariationId, ignoreLocationProperties, typeCasting, experienceKeys — was silently dropped.

The most user-visible consequence: a caller invoking $context->runExperience($key, new BucketingAttributes(['enableTracking' => false])) still fires the bucketing track event, because by the time the call reaches DataManager::_getBucketingByField (which reads $attributes->enableTracking ?? true), the field has been discarded one layer up. Same for forceVariationId (the forced variation was never honored) and ignoreLocationProperties.

Root cause

A whitelist-then-rebuild pattern in Context instead of spread-then-override. Two transforms must happen at the Context layer:

  1. visitorProperties must be merged with stored visitor props via getVisitorProperties().
  2. environment must default to $this->environment when the caller omits it.

Everything else can pass through unmodified; BucketingAttributes::__construct already does ?? null defaulting per key, so extra/unknown fields are safe.

Fix

// before
new BucketingAttributes([
    'visitorProperties' => $visitorProperties,
    'locationProperties' => $attributes?->getLocationProperties(),
    'updateVisitorProperties' => $attributes?->getUpdateVisitorProperties(),
    'environment' => $attributes?->getEnvironment() ?? $this->environment,
])

// after
$forwardedData = $attributes ? get_object_vars($attributes) : [];
$forwardedData['visitorProperties'] = $visitorProperties;
$forwardedData['environment'] = $forwardedData['environment'] ?? $this->environment;
new BucketingAttributes($forwardedData)

Applied identically to runExperience and runExperiences.

get_object_vars() is the PHP analog to JS's object-spread; it copies all public properties of $attributes into an array, then we override the two we need to transform.

Tests

Added two cases in packages/Php-sdk/tests/ContextTest.php:

  • testRunExperienceForwardsAllBucketingAttributes
  • testRunExperiencesForwardsAllBucketingAttributes

Both wire a PHPUnit mock of ExperienceManagerInterface that captures the BucketingAttributes passed in and delegates to the real manager. They assert that enableTracking, forceVariationId, ignoreLocationProperties, and updateVisitorProperties survive the trip through Context. Use enableTracking: false to keep the path side-effect-free.

Test plan

  • phpunit packages/Php-sdk/tests/ContextTest.php → 39 tests pass, 208 assertions
  • phpunit packages/Php-sdk/tests/ → 176 tests pass, 539 assertions (no regressions)
  • Existing testRunExperience and testGetVariationsAcrossAllExperiences still pass — happy path unchanged

Notes

  • Mirrors the JS-SDK fix in fix(context): forward all BucketingAttributes to ExperienceManager javascript-sdk#382 — same bug, same shape, ported to PHP idiom.
  • Fix is API-surface-neutral. Callers that were never passing the dropped fields see no change. Callers that were passing them and silently getting the default behavior will now get the documented behavior they expected from the public BucketingAttributes type all along.
  • No changes to BucketingAttributes type, DataManager, ExperienceManager, or any downstream layer.

🤖 Generated with Claude Code

Context::runExperience and Context::runExperiences rebuilt a fresh
BucketingAttributes object with only four hardcoded keys
(visitorProperties, locationProperties, updateVisitorProperties,
environment) before forwarding to ExperienceManager::selectVariation
and selectVariations.

This silently dropped every other BucketingAttributes field —
enableTracking, forceVariationId, ignoreLocationProperties,
typeCasting, experienceKeys — so callers opting out of tracking,
forcing a variation, or ignoring location targeting were silently
ignored. DataManager destructures these by name with enableTracking
defaulting to true (DataManager::531), which is how the opt-out was
being lost.

Switch to get_object_vars-spread-then-override so all caller-supplied
attributes pass through while still applying the two transforms
Context must do: merging visitorProperties via getVisitorProperties()
and defaulting environment to the Context's own environment.

Add tests covering forwarding of enableTracking, forceVariationId,
ignoreLocationProperties, and updateVisitorProperties through both
runExperience and runExperiences.

Mirrors convertcom/javascript-sdk#382 — same bug, same fix shape
adapted to PHP.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@abbaseya abbaseya self-assigned this May 21, 2026
@abbaseya
abbaseya requested review from DmytroConvert and JosephSamirL and removed request for JosephSamirL May 21, 2026 01:07

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the runExperience and runExperiences methods in Context.php to ensure all bucketing attributes are correctly forwarded by dynamically capturing object variables. New unit tests were added to verify that attributes like enableTracking and forceVariationId are preserved. Feedback suggests using clone instead of get_object_vars for better performance and maintainability, and recommends refactoring the duplicated logic into a helper method to ensure consistency across the SDK.

Comment thread packages/Php-sdk/src/Context.php
Comment thread packages/Php-sdk/src/Context.php
@abbaseya
abbaseya merged commit afc0479 into main May 21, 2026
10 checks passed
@abbaseya
abbaseya deleted the fix/context-forward-bucketing-attributes branch May 21, 2026 17:02
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.

2 participants