Skip to content

Fix removeColumn() throwing "Worksheet no longer exists" on a sheet with a table - #5059

Open
aswinkumar863 wants to merge 3 commits into
PHPOffice:masterfrom
aswinkumar863:fix/table-remove-column-crash
Open

aswinkumar863 wants to merge 3 commits into
PHPOffice:masterfrom
aswinkumar863:fix/table-remove-column-crash

Conversation

@aswinkumar863

Copy link
Copy Markdown
Contributor

This is:

  • a bugfix
  • a new feature
  • refactoring
  • additional unit tests

Checklist:

  • Changes are covered by unit tests
    • Changes are covered by existing unit tests
    • New unit tests have been added
  • Code style is respected
  • Commit message explains why the change is made (see https://github.com/erlang/otp/wiki/Writing-good-commit-messages)
  • CHANGELOG.md contains a short summary of the change and a link to the pull request if applicable
  • Documentation is updated as necessary

Why is this change needed?

Fixes #5058.

Removing (or inserting) a column that moves the header row of a table throws Worksheet no longer exists.

When a header cell changes, Cell::setValueExplicit() calls updateIfCellIsTableHeader(), which updates the structured references in all formulas. It reads those cells with getCell(), and that detaches the cell we are setting. After that, setValueExplicit() calls $this->getWorksheet() and fails.

The fix keeps the cell collection in a variable and gets the cell again after the header update, so it is attached again before its worksheet and style are used.

Added ColumnInsertRemoveTest with 3 tests. The column A test fails without the fix. The full test suite passes.

This is a different problem from #5050 (that one is in the loop at the end of Worksheet::removeColumn()).

aswinkumar863 and others added 3 commits October 17, 2026 18:25
When a table header cell's value changes, Cell::setValueExplicit() calls
updateIfCellIsTableHeader(), which rewrites structured references in
every formula of the workbook. That loop fetches other cells through
Worksheet::getCell(), and the cell collection detaches the current
(dirty) cell when it switches to another one. The rest of
setValueExplicit() then called $this->getWorksheet() on the detached
cell and threw "Worksheet no longer exists".

ReferenceHelper moves header cells with setValueExplicit() when a
column is inserted or removed, so e.g. removeColumn('A') on a sheet with
a table at A1:C4 always failed.

Keep a reference to the cell collection and retrieve the cell again
after the header update, which re-attaches it before its worksheet and
style are used.
@dkulyk

dkulyk commented Oct 19, 2026

Copy link
Copy Markdown
Contributor

While testing this branch: after removeColumn() on a sheet with a table, a structured reference to a removed column is rewritten to the column that moves into its place, instead of becoming #REF!.

$sheet->fromArray([['Name', 'Qty', 'Price'], ['a', 1, 2], ['b', 3, 4]]);
$sheet->setCellValue('J1', '=COUNTA(Sales[Name])');
$sheet->setCellValue('J2', '=SUM(Sales[Qty])');
$sheet->setCellValue('J3', '=SUM(Sales[Price])');
$sheet->addTable(new Table('A1:C3', 'Sales'));
$sheet->removeColumn('B');
Column removed Excel for Mac This branch (56cec70)
A =COUNTA(#REF!) =COUNTA(Sales[Qty])
B =SUM(#REF!) =SUM(Sales[Price])
C =SUM(#REF!), table A1:B3 =SUM(Sales[]), table stays A1:C3

ReferenceHelper::insertNewBefore() moves the cells with setValueExplicit(), and Cell::updateIfCellIsTableHeader() takes the header changing from Qty to Price for a rename and runs Table\Column::updateStructuredReferences().

Outside the scope of this PR; noting it here as it is the same code path.

This branch has not been deployed

No deployments
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.

Removing a column from a sheet with a table throws "Worksheet no longer exists"

3 participants