Repository navigation
Keep nested objects as objects when JsonManipulator rewrites a node - #13210
Merged
Seldaek merged 4 commits intoNov 3, 2026
Merged
Conversation
Adding or removing a dotted sub-key (e.g. `composer config --unset
platform.php` or `composer config extra.foo.bar baz`) decoded the
surrounding node with json_decode($json, true) and formatted it back, so
every nested `{}` came out as `[]` and objects keyed 0..n came out as
lists. In config this made the schema check fail ("config.allow-plugins :
Array value found, but an object or a boolean is required") and the
command reverted the file.
Only the levels that get modified are decoded to arrays now, deeper
objects stay stdClass, and format() no longer writes an object as a list
because its keys happen to be sequential.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
…hapes
Converting stdClass to an array one level at a time lost the object-ness of
the edited level, so objects with keys 0..n-1 were still written as lists,
and an emptied list was written as {}. Removing a list item now reindexes it.
isset() accepts a numeric offset into a string, so removing extra.foo.0 from a string value crashed with "Cannot unset string offsets". A "0" sub-key was also skipped by the falsy check.
Member
|
Thanks |
gemmaro
pushed a commit
to phpusers-ja/composer
that referenced
this pull request
Nov 9, 2026
…omposer#13210) * Keep nested objects as objects when JsonManipulator rewrites a node Adding or removing a dotted sub-key (e.g. `composer config --unset platform.php` or `composer config extra.foo.bar baz`) decoded the surrounding node with json_decode($json, true) and formatted it back, so every nested `{}` came out as `[]` and objects keyed 0..n came out as lists. In config this made the schema check fail ("config.allow-plugins : Array value found, but an object or a boolean is required") and the command reverted the file. Only the levels that get modified are decoded to arrays now, deeper objects stay stdClass, and format() no longer writes an object as a list because its keys happen to be sequential. Co-Authored-By: Claude Opus 5.5 <[email protected]> * Edit decoded objects in place so sub-key edits keep object and list shapes Converting stdClass to an array one level at a time lost the object-ness of the edited level, so objects with keys 0..n-1 were still written as lists, and an emptied list was written as {}. Removing a list item now reindexes it. * Skip removing a sub-key of a scalar value isset() accepts a numeric offset into a string, so removing extra.foo.0 from a string value crashed with "Cannot unset string offsets". A "0" sub-key was also skipped by the falsy check. * Add return type to sub-key edit test provider --------- Co-authored-by: Claude Opus 5.5 <[email protected]> Co-authored-by: Jordi Boggiano <[email protected]>
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.
When
composer configadds or removes a dotted sub-key, JsonManipulator decodes the surrounding node withjson_decode($json, true)and formats it back. Any nested{}comes back as[], and an object keyed"0","1"… comes back as a list.In
configthis breaks the schema check. A plain sequence of commands is enough:The last one fails and reverts the file:
In
extrathere's no schema, so it silently rewrites things instead, e.g."patches": {}becomes"patches": []aftercomposer config --unset extra.foo.bar.Now only the levels that get modified are turned into arrays and deeper objects stay
stdClass, soformat()writes them back as objects.format()also no longer writes an object as a list just because its keys are sequential. Added tests for the add/remove sub-key paths in JsonManipulatorTest.I used Claude Code to write this change.