Skip to content

fix: issues found in a full package audit - #58

Merged
kbond merged 6 commits into
zenstruck:1.xfrom
kbond:fix/audit
Aug 20, 2026
Merged

kbond merged 6 commits into
zenstruck:1.xfrom
kbond:fix/audit

Conversation

@kbond

@kbond kbond commented Aug 20, 2026

Copy link
Copy Markdown
Member

Findings from a full audit of the package (grid excluded), one commit each.

  • Pages counted more than it needed to. count() loaded the first page's items just to test for emptiness, and getIterator() re-counted on every iteration - for a collection that counts by iterating, that made paging over it O(n × pages). Page::totalCount() is now memoized too.
  • ArrayCollection::first() returned the default for a null first item, unable to distinguish it from an empty collection - unlike every other implementation.
  • first() compiled the query twice. It called getSQL() to check for a delete, then invalidated that compilation with setMaxResults(). It now checks the DQL, which is already built.
  • DoctrineSpec::instanceOf() never matched anything. The class name was bound as a parameter, so Doctrine compared the discriminator column to the FQCN (s0_.type IN ('App\Entity\Circle')). DQL needs it inlined. Single-table-inheritance fixtures were added to cover it.
  • Coverage for previously untested features: Util::stringify(), EntityResultQueryBuilder::modifyQuery()/cacheResult(), the cache specification, and ChainObjectRepositoryFactory::reset(). Also corrects the namespace of two test classes, which weren't PSR-4 autoloadable.
  • Documents that Batch::process() uses a single transaction for the whole loop rather than one per chunk.

@kbond
kbond marked this pull request as ready for review August 20, 2026 17:34
kbond added 6 commits August 20, 2026 13:38
`Pages::count()` loaded the first page's items to check for emptiness, and `getIterator()` re-counted on every iteration - expensive for collections that count by iterating. `Page::totalCount()` is now memoized.
`ArrayCollection::first()` couldn't distinguish an empty collection from one whose first item is `null`, unlike every other implementation.
`first()` compiled the query to SQL just to check for a delete, then invalidated that compilation with `setMaxResults()` - parsing the query twice on every call.
Passing it as a parameter made Doctrine compare the discriminator column to the FQCN, so the specification never matched anything.
Adds coverage for `Util::stringify()`, `modifyQuery()`, `cacheResult()`, the `cache` specification and `ChainObjectRepositoryFactory::reset()`, and fixes the namespace of two test classes.
@kbond
kbond merged commit c7688ad into zenstruck:1.x Aug 20, 2026
16 checks passed
@kbond
kbond deleted the fix/audit branch August 20, 2026 17:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant