Add json_decode flags for duplicate JSON object keys#22473
Conversation
RFC 8259 allows the same name to appear more than once in a JSON object, but it does not say what a parser should do with the values. PHP has always used last-key-wins, which is reasonable as a default, yet there are real payloads where you want every value instead of silently dropping the earlier ones or always deep-merging nested structures. This adds two opt-in flags: - JSON_DUPLICATE_KEY_ARRAY keeps each duplicate value under the same key as a list. - JSON_DUPLICATE_KEY_MERGE recursively merges nested objects/arrays when the same key appears again; scalars still overwrite like today. The flags are mutually exclusive. If neither is passed, behavior is unchanged. The merge/array logic is hooked up through the existing parser method table so the default decode path stays on the original object_update implementation. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Benchmarked this against a clean Default / legacy path (
Also ran a separate output parity check (20 default-path payloads, assoc/object/scalars/nested dupes) — baseline and feature output is byte-identical. New flag paths (feature build only, as expected a bit slower on duplicate-heavy JSON because we actually do extra work):
Those are roughly on par with the default duplicate-key cases above — the extra cost only shows up when you opt in. The merge/array handlers are selected once via the parser method table at init time, so the hot path for normal Build/env if anyone wants to reproduce locally: (Comparison script is in my fork branch if useful; not part of the PR diff.) |
Summary
JSON objects can legally repeat the same key (RFC 8259 does not forbid it), but PHP has only ever given you one outcome: the last value wins. That is a fine default, yet it is not great when you are parsing data that actually relies on duplicate keys and you need either every value or a controlled merge — not an implicit overwrite or a full deep merge every time.
This PR adds two opt-in
json_decode()flags:JSON_DUPLICATE_KEY_ARRAY— collect duplicate values for the same key into a listJSON_DUPLICATE_KEY_MERGE— recursively merge nested objects/arrays when a key repeats; scalars still overwrite (same as today)If you do not pass either flag, nothing changes. The two flags cannot be combined.
The new behavior is wired through the existing JSON parser method table, so the default decode path keeps using the original
object_updateimplementation.Motivation
Mainly std compatibility / predictability around duplicate keys. Right now callers have to pre-process JSON or work around last-key-wins if their format uses repeated names on purpose. Having explicit flags makes that choice visible instead of hoping the parser does what you need.
Test plan
make test TESTS=ext/json/tests/— all JSON tests passHappy to move this through an RFC / internals discussion if that is required before merge.
Made with Cursor