Skip to content

Fix/oversized spreadsheet error php#700

Open
timseidel wants to merge 2 commits into
rubenarslan:masterfrom
timseidel:fix/oversized-spreadsheet-error-php
Open

Fix/oversized spreadsheet error php#700
timseidel wants to merge 2 commits into
rubenarslan:masterfrom
timseidel:fix/oversized-spreadsheet-error-php

Conversation

@timseidel

Copy link
Copy Markdown
Contributor

Fixed a fatal php oom error which I encountered when uploading a spreadsheet where excel had added a formatted cell at the very bottom of the spreadsheet, leading to getHighestDataRow() reporting 1,048,576 rows (the Excel maximum). The row iterator, running with setIterateOnlyExistingCells(false), then materializes a Cell object per row/column until PHP exhausts its 512 MB memory limit. This fatal memory error surfaces deep in PhpSpreadsheet (Coordinate.php) and cannot be caught by any try/catch block.

Fix:

  • Added SpreadsheetReader::MAX_DATA_ROWS = 50,000 as a hard ceiling. Both readChoicesSheet() and readSurveySheet() now refuse a sheet exceeding this limit with a clear, actionable error message telling the author to delete the empty rows below their data and re-upload — before any iteration begins.
  • Also fixed a pre-existing latent bug: when the choices sheet failed, readSurveySheet() still ran and its internal error guard re-wrapped the choices error into a duplicate, misattributed "Error in cell B2 (Survey Sheet)" message. The survey sheet is now skipped when the choices sheet has already errored.
  • Widened the catch in readItemTableFile() from PhpOffice\PhpSpreadsheet\Exception to \Throwable so reader-internal TypeErrors also degrade gracefully (note: the memory fatal itself remains uncatchable — the row cap is its guard).
    This is the row-axis counterpart to the v1.1.0 column-axis fix for the same class of spreadsheet bloat. Regression-tested in tests/SpreadsheetReaderRowCapTest.php (4 cases).

timseidel and others added 2 commits June 24, 2026 20:55
A stray styled/valued cell far down a sheet (typically the Excel max row
A1048576) inflated getHighestDataRow() to ~1,048,576. The row iterator,
running with setIterateOnlyExistingCells(false), then materialized a Cell
per row/column until PHP exhausted its 512MB limit — a fatal memory error
that surfaces deep in PhpSpreadsheet (Coordinate.php) and, being a fatal
rather than a \Throwable, cannot be caught.

readChoicesSheet() and readSurveySheet() now refuse a sheet reporting more
than SpreadsheetReader::MAX_DATA_ROWS (50,000) rows with a clear, actionable
error before iterating. This is the row-axis counterpart to the v1.1.0
column-axis fix for the same class of spreadsheet bloat.

Also widen the catch in readItemTableFile() from PhpSpreadsheet\Exception to
\Throwable so reader-internal TypeErrors degrade to a graceful error instead
of a 500 (the memory fatal stays uncatchable — the row cap is its guard).

Regression-tested in tests/SpreadsheetReaderRowCapTest.php.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When the choices sheet errored (e.g. the row-cap refusal), readItemTableFile()
still ran readSurveySheet(). Its "if ($this->errors)" guard cannot tell errors
it raises itself from ones carried over from the choices sheet, so it re-wrapped
and re-threw the choices error on the very first survey cell. That throw was
caught by the generic handler, producing a duplicate, misattributed cascade:

  1. The choices worksheet "choices" reports 1,048,576 rows... (clean)
  2. An error occured reading your excel file...                 (generic)
  3. Error in cell B2 (Survey Sheet): <the choices error again>  (duplicate)

Short-circuit: skip readSurveySheet() when the choices sheet already failed —
the survey sheet references those choice lists and is unreadable anyway. Leaves
exactly the one clean error. Latent cascade for any choices-sheet error,
exposed by the row-cap fix. Regression-tested.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant