Preserve JSON arrays after filtering PHP records
An API promises a JSON array of products. After filtering out a hidden product, the response unexpectedly becomes a JSON object. PHP preserved the original numeric keys, leaving a gap in what was previously a list.
Reindex when the contract is a list
<?php
$rows = ['first', 'hidden', 'third'];
$visible = array_filter($rows, fn ($value) => $value !== 'hidden');
assert(json_encode($visible) === '{"0":"first","2":"third"}');
assert(json_encode(array_values($visible)) === '["first","third"]');
array_values deliberately discards the old keys. That is correct when the response represents an ordered list and the keys have no business meaning. It is wrong when the keys are customer IDs or supplier codes that callers need.
Test the response shape
An assertion that only checks whether the word third appears in a response would miss this bug. Decode the JSON and assert the expected shape, or compare the relevant JSON structure using your framework's response test helpers.
Consider the empty result too. A list endpoint should usually return [], rather than changing to null because no records matched. Document that choice so clients do not need three different parsing paths.
Do not apply reindexing automatically to every array before JSON encoding. Associative maps are useful and should stay maps. Decide the public contract first, then normalize only the structures that represent lists. This is especially relevant after array_filter, key-preserving collection operations, or removing a single element with unset.
Reference
Official documentation. The examples here illustrate the stated boundary; adapt them to your application and test its failure paths.