Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP Removing Rows or Columns that Include Edge Ranges #3528

Draft
wants to merge 41 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
99d4955
WIP Removing Rows or Columns that Include Edge Ranges
oleibman Apr 20, 2023
9296381
Scrutinizer
oleibman Apr 20, 2023
f95eac2
Merge branch 'master' into pr1449
oleibman Aug 31, 2023
8e39aff
Merge branch 'master' into pr1449
oleibman Sep 11, 2023
8bbf5da
Merge branch 'master' into pr1449
oleibman Sep 13, 2023
491fe13
Merge branch 'master' into pr1449
oleibman Sep 13, 2023
0fcbf0a
Merge branch 'master' into pr1449
oleibman Sep 26, 2023
c8d453d
Merge branch 'master' into pr1449
oleibman Dec 8, 2023
c6ce160
Merge branch 'master' into pr1449
oleibman Dec 12, 2023
e42afc2
Merge branch 'master' into pr1449
oleibman Jan 4, 2024
78f6bb0
Merge branch 'master' into pr1449
oleibman Jan 22, 2024
6400f0a
Merge branch 'master' into pr1449
oleibman Jan 22, 2024
60f3c83
Merge branch 'master' into pr1449
oleibman Feb 6, 2024
976ff1e
Merge branch 'master' into pr1449
oleibman Mar 15, 2024
dcaf90e
Merge branch 'master' into pr1449
oleibman May 1, 2024
30e44f7
Merge branch 'master' into pr1449
oleibman May 13, 2024
25c9bb1
Merge branch 'master' into pr1449
oleibman May 31, 2024
dd7c317
Merge branch 'master' into pr1449
oleibman Jun 3, 2024
6a4ff0e
Merge branch 'master' into pr1449
oleibman Jun 23, 2024
02ea557
Merge branch 'master' into pr1449
oleibman Jul 1, 2024
1e904b5
Merge branch 'master' into pr1449
oleibman Jul 1, 2024
7b64fe6
Merge branch 'master' into pr1449
oleibman Jul 23, 2024
48217b9
Merge branch 'master' into pr1449
oleibman Jul 31, 2024
56540d1
Merge branch 'master' into pr1449
oleibman Aug 9, 2024
9d794e0
Merge branch 'master' into pr1449
oleibman Aug 12, 2024
1f5c308
Merge branch 'master' into pr1449
oleibman Sep 29, 2024
a921d24
Merge branch 'master' into pr1449
oleibman Oct 7, 2024
d881cf8
Merge branch 'master' into pr1449
oleibman Oct 10, 2024
9ec4d1b
Merge branch 'master' into pr1449
oleibman Nov 10, 2024
75d6212
Merge branch 'master' into pr1449
oleibman Nov 21, 2024
8f3e6d1
Merge branch 'master' into pr1449
oleibman Nov 26, 2024
bd4eb2b
Merge branch 'master' into pr1449
oleibman Dec 11, 2024
d1e466d
Update HtmlImage2Test.php
oleibman Dec 11, 2024
727f06e
Merge branch 'master' into pr1449
oleibman Dec 11, 2024
ff405d6
Merge branch 'master' into pr1449
oleibman Dec 27, 2024
7895ce2
Merge branch 'master' into pr1449
oleibman Jan 12, 2025
312388a
Merge branch 'master' into pr1449
oleibman Jan 22, 2025
fc9f985
Merge branch 'master' into pr1449
oleibman Feb 8, 2025
be87f2d
Update main.yml
oleibman Feb 8, 2025
8abd795
Update ReferenceHelper.php
oleibman Feb 8, 2025
caf12eb
Merge branch 'master' into pr1449
oleibman Feb 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 50 additions & 4 deletions src/PhpSpreadsheet/CellReferenceHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,30 @@ class CellReferenceHelper

protected int $beforeColumn;

protected bool $beforeColumnAbsolute = false;

protected string $beforeColumnString;

protected int $beforeRow;

protected bool $beforeRowAbsolute = false;

protected int $numberOfColumns;

protected int $numberOfRows;

public function __construct(string $beforeCellAddress = 'A1', int $numberOfColumns = 0, int $numberOfRows = 0)
{
$this->beforeColumnAbsolute = $beforeCellAddress[0] === '$';
$this->beforeRowAbsolute = strpos($beforeCellAddress, '$', 1) !== false;
$this->beforeCellAddress = str_replace('$', '', $beforeCellAddress);
$this->numberOfColumns = $numberOfColumns;
$this->numberOfRows = $numberOfRows;

// Get coordinate of $beforeCellAddress
[$beforeColumn, $beforeRow] = Coordinate::coordinateFromString($beforeCellAddress);
$this->beforeColumn = Coordinate::columnIndexFromString($beforeColumn);
$this->beforeColumnString = $beforeColumn;
$this->beforeColumn = (int) Coordinate::columnIndexFromString($beforeColumn);
$this->beforeRow = (int) $beforeRow;
}

Expand All @@ -41,7 +50,7 @@ public function refreshRequired(string $beforeCellAddress, int $numberOfColumns,
|| $this->numberOfRows !== $numberOfRows;
}

public function updateCellReference(string $cellReference = 'A1', bool $includeAbsoluteReferences = false, bool $onlyAbsoluteReferences = false): string
public function updateCellReference(string $cellReference = 'A1', bool $includeAbsoluteReferences = false, bool $onlyAbsoluteReferences = false, ?bool $topLeft = null): string
{
if (Coordinate::coordinateIsRange($cellReference)) {
throw new Exception('Only single cell references may be passed to this method.');
Expand All @@ -62,8 +71,45 @@ public function updateCellReference(string $cellReference = 'A1', bool $includeA
$updateColumn = (($absoluteColumn !== '$') && $newColumnIndex >= $this->beforeColumn);
$updateRow = (($absoluteRow !== '$') && $newRowIndex >= $this->beforeRow);
} else {
$updateColumn = ($newColumnIndex >= $this->beforeColumn);
$updateRow = ($newRowIndex >= $this->beforeRow);
// A special case is removing the left/top or bottom/right edge of a range
// $topLeft is null if we aren't adjusting a range at all.
if (
$topLeft !== null
&& $this->numberOfColumns < 0
&& $newColumnIndex >= $this->beforeColumn + $this->numberOfColumns
&& $newColumnIndex <= $this->beforeColumn - 1
) {
if ($topLeft) {
$newColumnIndex = $this->beforeColumn + $this->numberOfColumns;
} else {
$newColumnIndex = $this->beforeColumn + $this->numberOfColumns - 1;
}
} elseif ($newColumnIndex >= $this->beforeColumn) {
// Create new column reference
$newColumnIndex += $this->numberOfColumns;
}
$newColumn = $absoluteColumn . Coordinate::stringFromColumnIndex($newColumnIndex);
//$updateColumn = ($newColumnIndex >= $this->beforeColumn);
$updateColumn = false;
// A special case is removing the left/top or bottom/right edge of a range
// $topLeft is null if we aren't adjusting a range at all.
if (
$topLeft !== null
&& $this->numberOfRows < 0
&& $newRowIndex >= $this->beforeRow + $this->numberOfRows
&& $newRowIndex <= $this->beforeRow - 1
) {
if ($topLeft) {
$newRowIndex = $this->beforeRow + $this->numberOfRows;
} else {
$newRowIndex = $this->beforeRow + $this->numberOfRows - 1;
}
} elseif ($newRowIndex >= $this->beforeRow) {
$newRowIndex = $newRowIndex + $this->numberOfRows;
}
$newRow = $absoluteRow . $newRowIndex;
//$updateRow = ($newRowIndex >= $this->beforeRow);
$updateRow = false;
}

// Create new column reference
Expand Down
42 changes: 26 additions & 16 deletions src/PhpSpreadsheet/ReferenceHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -194,8 +194,10 @@ protected function adjustHyperlinks(Worksheet $worksheet, int $numberOfColumns,
if ($cellReferenceHelper->cellAddressInDeleteRange($cellAddress) === true) {
$worksheet->setHyperlink($cellAddress, null);
} elseif ($cellAddress !== $newReference) {
$worksheet->setHyperlink($newReference, $value);
$worksheet->setHyperlink($cellAddress, null);
if ($newReference) {
$worksheet->setHyperlink($newReference, $value);
}
}
}
}
Expand Down Expand Up @@ -292,6 +294,9 @@ protected function adjustDataValidations(Worksheet $worksheet, int $numberOfColu
if ($cellAddress !== $newReference) {
$worksheet->setDataValidation($newReference, $dataValidation);
$worksheet->setDataValidation($cellAddress, null);
if ($newReference) {
$worksheet->setDataValidation($newReference, $dataValidation);
}
}
}
}
Expand All @@ -307,7 +312,9 @@ protected function adjustMergeCells(Worksheet $worksheet): void
$aNewMergeCells = []; // the new array of all merge cells
foreach ($aMergeCells as $cellAddress => &$value) {
$newReference = $this->updateCellReference($cellAddress);
$aNewMergeCells[$newReference] = $newReference;
if ($newReference) {
$aNewMergeCells[$newReference] = $newReference;
}
}
$worksheet->setMergeCells($aNewMergeCells); // replace the merge cells array
}
Expand All @@ -328,8 +335,10 @@ protected function adjustProtectedCells(Worksheet $worksheet, int $numberOfColum
foreach ($aProtectedCells as $cellAddress => $protectedRange) {
$newReference = $this->updateCellReference($cellAddress);
if ($cellAddress !== $newReference) {
$worksheet->protectCells($newReference, $protectedRange->getPassword(), true);
$worksheet->unprotectCells($cellAddress);
if ($newReference) {
$worksheet->protectCells($newReference, $protectedRange->getPassword(), true);
}
}
}
}
Expand Down Expand Up @@ -457,7 +466,8 @@ public function insertNewBefore(
$cell = $worksheet->getCell($coordinate);
$cellIndex = Coordinate::columnIndexFromString($cell->getColumn());

if ($cellIndex - 1 + $numberOfColumns < 0) {
// Don't update cells that are being removed
if ($numberOfColumns < 0 && $cellIndex >= $beforeColumn + $numberOfColumns && $cellIndex < $beforeColumn) {
continue;
}

Expand Down Expand Up @@ -633,8 +643,8 @@ public function updateFormulaReferences(
if ($matchCount > 0) {
foreach ($matches as $match) {
$fromString = self::sheetnameBeforeCells($match[2], $worksheetName, "{$match[3]}:{$match[4]}");
$modified3 = substr($this->updateCellReference('$A' . $match[3], $includeAbsoluteReferences, $onlyAbsoluteReferences), 2);
$modified4 = substr($this->updateCellReference('$A' . $match[4], $includeAbsoluteReferences, $onlyAbsoluteReferences), 2);
$modified3 = substr($this->updateCellReference('$A' . $match[3], $includeAbsoluteReferences, $onlyAbsoluteReferences, true), 2);
$modified4 = substr($this->updateCellReference('$A' . $match[4], $includeAbsoluteReferences, $onlyAbsoluteReferences, false), 2);

if ($match[3] . ':' . $match[4] !== $modified3 . ':' . $modified4) {
if (self::matchSheetName($match[2], $worksheetName)) {
Expand All @@ -657,8 +667,8 @@ public function updateFormulaReferences(
if ($matchCount > 0) {
foreach ($matches as $match) {
$fromString = self::sheetnameBeforeCells($match[2], $worksheetName, "{$match[3]}:{$match[4]}");
$modified3 = substr($this->updateCellReference($match[3] . '$1', $includeAbsoluteReferences, $onlyAbsoluteReferences), 0, -2);
$modified4 = substr($this->updateCellReference($match[4] . '$1', $includeAbsoluteReferences, $onlyAbsoluteReferences), 0, -2);
$modified3 = substr($this->updateCellReference($match[3] . '$1', $includeAbsoluteReferences, $onlyAbsoluteReferences, true), 0, -2);
$modified4 = substr($this->updateCellReference($match[4] . '$1', $includeAbsoluteReferences, $onlyAbsoluteReferences, false), 0, -2);

if ($match[3] . ':' . $match[4] !== $modified3 . ':' . $modified4) {
if (self::matchSheetName($match[2], $worksheetName)) {
Expand All @@ -681,8 +691,8 @@ public function updateFormulaReferences(
if ($matchCount > 0) {
foreach ($matches as $match) {
$fromString = self::sheetnameBeforeCells($match[2], $worksheetName, "{$match[3]}:{$match[4]}");
$modified3 = $this->updateCellReference($match[3], $includeAbsoluteReferences, $onlyAbsoluteReferences);
$modified4 = $this->updateCellReference($match[4], $includeAbsoluteReferences, $onlyAbsoluteReferences);
$modified3 = $this->updateCellReference($match[3], $includeAbsoluteReferences, $onlyAbsoluteReferences, true);
$modified4 = $this->updateCellReference($match[4], $includeAbsoluteReferences, $onlyAbsoluteReferences, false);

if ($match[3] . $match[4] !== $modified3 . $modified4) {
if (self::matchSheetName($match[2], $worksheetName)) {
Expand All @@ -709,7 +719,7 @@ public function updateFormulaReferences(
foreach ($matches as $match) {
$fromString = self::sheetnameBeforeCells($match[2], $worksheetName, "{$match[3]}");

$modified3 = $this->updateCellReference($match[3], $includeAbsoluteReferences, $onlyAbsoluteReferences);
$modified3 = $this->updateCellReference($match[3], $includeAbsoluteReferences, $onlyAbsoluteReferences, null);
if ($match[3] !== $modified3) {
if (self::matchSheetName($match[2], $worksheetName)) {
$toString = self::sheetnameBeforeCells($match[2], $worksheetName, "$modified3");
Expand Down Expand Up @@ -890,7 +900,7 @@ private function updateRowRangesAllWorksheets(string $formula, int $numberOfRows
*
* @return string Updated cell range
*/
private function updateCellReference(string $cellReference = 'A1', bool $includeAbsoluteReferences = false, bool $onlyAbsoluteReferences = false): string
private function updateCellReference(string $cellReference = 'A1', bool $includeAbsoluteReferences = false, bool $onlyAbsoluteReferences = false, ?bool $topLeft = null)
{
// Is it in another worksheet? Will not have to update anything.
if (str_contains($cellReference, '!')) {
Expand All @@ -902,7 +912,7 @@ private function updateCellReference(string $cellReference = 'A1', bool $include
/** @var CellReferenceHelper */
$cellReferenceHelper = $this->cellReferenceHelper;

return $cellReferenceHelper->updateCellReference($cellReference, $includeAbsoluteReferences, $onlyAbsoluteReferences);
return $cellReferenceHelper->updateCellReference($cellReference, $includeAbsoluteReferences, $onlyAbsoluteReferences, $topLeft);
}

// Range
Expand Down Expand Up @@ -1008,14 +1018,14 @@ private function updateCellRange(string $cellRange = 'A1:A1', bool $includeAbsol
$cellReferenceHelper = $this->cellReferenceHelper;
if (ctype_alpha($range[$i][$j])) {
$range[$i][$j] = Coordinate::coordinateFromString(
$cellReferenceHelper->updateCellReference($range[$i][$j] . '1', $includeAbsoluteReferences, $onlyAbsoluteReferences)
$cellReferenceHelper->updateCellReference($range[$i][$j] . '1', $includeAbsoluteReferences, $onlyAbsoluteReferences, null)
)[0];
} elseif (ctype_digit($range[$i][$j])) {
$range[$i][$j] = Coordinate::coordinateFromString(
$cellReferenceHelper->updateCellReference('A' . $range[$i][$j], $includeAbsoluteReferences, $onlyAbsoluteReferences)
$cellReferenceHelper->updateCellReference('A' . $range[$i][$j], $includeAbsoluteReferences, $onlyAbsoluteReferences, null)
)[1];
} else {
$range[$i][$j] = $cellReferenceHelper->updateCellReference($range[$i][$j], $includeAbsoluteReferences, $onlyAbsoluteReferences);
$range[$i][$j] = $cellReferenceHelper->updateCellReference($range[$i][$j], $includeAbsoluteReferences, $onlyAbsoluteReferences, null);
}
}
}
Expand Down
72 changes: 72 additions & 0 deletions tests/PhpSpreadsheetTests/Issue1449Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

namespace PhpOffice\PhpSpreadsheetTests;

use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PHPUnit\Framework\TestCase;

class Issue1449Test extends TestCase
{
/** @var bool */
private $skipTests = true;

public function testDeleteColumns(): void
{
$spreadsheet = new Spreadsheet();
$sheet1 = $spreadsheet->getActiveSheet();
$sheet2 = $spreadsheet->createSheet();
$sheet1->setTitle('Sheet1');
$sheet2->setTitle('Sheet2');
$sheet1->fromArray(
[
[3, 1, 2, 33, 1, 10, 20, 30, 40],
[4, 2, 3, 23, 2, 10, 20, 30, 40],
[5, 3, 4, 1, 3, 10, 20, 30, 40],
[6, 4, 6, 4, 3, 10, 20, 30, 40],
[7, 6, 6, 2, 2, 10, 20, 30, 40],
],
null,
'C1',
true
);
$sheet1->getCell('A1')->setValue('=SUM(C4:F7)');
$sheet2->getCell('A1')->setValue('=SUM(Sheet1!C3:G5)');
$sheet1->removeColumn('F', 4);
self::assertSame('=SUM(C4:E7)', $sheet1->getCell('A1')->getValue());
if (!$this->skipTests) {
// References on another sheet not working yet.
self::assertSame('=Sheet1!SUM(C3:E5)', $sheet2->getCell('A1')->getValue());
}
$spreadsheet->disconnectWorksheets();
}

public function testDeleteRows(): void
{
$spreadsheet = new Spreadsheet();
$sheet1 = $spreadsheet->getActiveSheet();
$sheet2 = $spreadsheet->createSheet();
$sheet1->setTitle('Sheet1');
$sheet2->setTitle('Sheet2');
$sheet1->fromArray(
[
[3, 1, 2, 33, 1, 10, 20, 30, 40],
[4, 2, 3, 23, 2, 10, 20, 30, 40],
[5, 3, 4, 1, 3, 10, 20, 30, 40],
[6, 4, 6, 4, 3, 10, 20, 30, 40],
[7, 6, 6, 2, 2, 10, 20, 30, 40],
],
null,
'C1',
true
);
$sheet1->getCell('A1')->setValue('=SUM(C4:F7)');
$sheet2->getCell('A1')->setValue('=SUM(Sheet1!C3:G5)');
$sheet1->removeRow(4, 2);
self::assertSame('=SUM(C4:F5)', $sheet1->getCell('A1')->getValue());
if (!$this->skipTests) {
// References on another sheet not working yet.
self::assertSame('=Sheet1!SUM(C3:G3)', $sheet2->getCell('A1')->getValue());
}
$spreadsheet->disconnectWorksheets();
}
}
2 changes: 1 addition & 1 deletion tests/PhpSpreadsheetTests/Reader/Html/HtmlImage2Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public function testCanInsertImageGoodProtocol(): void
if (getenv('SKIP_URL_IMAGE_TEST') === '1') {
self::markTestSkipped('Skipped due to setting of environment variable');
}
$imagePath = 'https://phpspreadsheet.readthedocs.io/en/latest/topics/images/01-03-filter-icon-1.png';
$imagePath = 'https://phpspreadsheet.readthedocs.io/en/stable/topics/images/01-03-filter-icon-1.png';
$html = '<table>
<tr>
<td><img src="' . $imagePath . '" alt="test image voilà"></td>
Expand Down