Preserve null values in v2 JSON request bodies#2042
Merged
jar-stripe merged 8 commits intomasterfrom Mar 24, 2026
Merged
Conversation
For v2 APIs (JSON-encoded POST), null has semantic meaning — it clears a field. Previously, formatParams() converted all null values to empty strings regardless of API version, making it impossible to send explicit null in v2 requests. formatParams() now accepts an $apiMode parameter. For v2, null values are preserved so json_encode produces "field": null. For v1, the existing null-to-empty-string behavior is unchanged. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Committed-By-Agent: claude
There was a problem hiding this comment.
Pull request overview
Updates Stripe PHP’s service-layer param formatting so null values keep their semantic meaning for v2 JSON request bodies (clear/unset), while preserving existing v1 form-encoding behavior.
Changes:
- Extend
AbstractService::formatParams()with anapiModeargument to preservenullfor v2 and convertnull -> ''for v1. - Update service request helpers (
request,requestStream,requestCollection,requestSearchResult) to derive API mode from the path and pass it through. - Add tests validating v1/v2 null handling, including nested nulls and request integration behavior.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
lib/Service/AbstractService.php |
Adds API-mode-aware param formatting and threads mode through request helper methods. |
tests/Stripe/Service/AbstractServiceTest.php |
Adds coverage for v1 null stringification vs v2 null preservation and request-level assertions. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
objectsToIds() was stripping null values from params before they could
reach the JSON encoder, defeating v2 explicit null handling. Add a
$serializeEmpty parameter that preserves nulls in associative arrays
when true (used for v2 POST bodies).
Also fixes an edge case where an associative array with all values
stripped would json_encode as [] instead of {} — cast to stdClass to
preserve the object type.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
Make callers explicitly specify v1/v2 API mode and null handling behavior instead of silently defaulting. This is a breaking change for external callers of Util::objectsToIds() (public method) — the $serializeEmpty parameter no longer defaults to false. Also fix docstring: V2 POST/PUT/PATCH → V2 POST (we don't support PUT or PATCH). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Committed-By-Agent: claude
More accurately describes what the parameter controls — whether null values are preserved in the output. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Committed-By-Agent: claude
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
The default was removed from formatParams() to require callers to specify v1 or v2. The original test was missing the apiMode argument. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Committed-By-Agent: claude
xavdid-stripe
approved these changes
Mar 24, 2026
assertArrayHasKey must come before assertNull to avoid a confusing 'undefined index' notice if the key is absent. Addresses PR review comment. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Committed-By-Agent: claude
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why?
v2 APIs use JSON-encoded POST bodies where
nullhas semantic meaning — it signals the intent to clear/unset a field. Two issues prevented this from working:AbstractService::formatParams()converted allnullvalues to empty strings, which is correct for v1 but strips null semantics for v2Util::objectsToIds()strippednullvalues from params entirely before they could reach the JSON encoderThis was identified as part of an audit across all 7 Stripe language SDKs (DEVSDK-2926) to verify explicit null handling. PHP was one of four languages where v2 null serialization was not working correctly.
What?
AbstractService::formatParams()to accept a required$apiModeparameter — for v2, null values are preserved; for v1, nulls are converted to empty strings for form encodingUtil::objectsToIds()to accept a required$serializeEmptyparameter — when true (v2 POST), null values in associative arrays are preserved instead of strippedCurlClient::constructUrlAndBody()passes$serializeEmpty = truefor v2 POST requestsjson_encodeas[]instead of{}— cast tostdClassto preserve the object typeChangelog
nullwhen set in V2 API metadata and params, enabling you to clear metadata entries and some unsettable properties for V2 APIs.Util::objectsToIds()method now has a required$serializeNullparameter to indicate if null values set in the object should be output in the resulting hash. This is relevant for V2 POST APIs to let callers clear emptyable values.See Also