Skip to content

Commit 5cba6d8

Browse files
authored
Merge pull request #2873 from PHPOffice/Row/Column_isEmpty-check
New functionality to allow checking whether a row or column is "empty"
2 parents e3471f8 + 0ef98ac commit 5cba6d8

File tree

9 files changed

+566
-16
lines changed

9 files changed

+566
-16
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ and this project adheres to [Semantic Versioning](https://semver.org).
1818
- Added Worksheet visibility in Ods Writer [PR #2850](https://github.com/PHPOffice/PhpSpreadsheet/pull/2850)
1919
- Allow Csv Reader to treat string as contents of file [Issue #1285](https://github.com/PHPOffice/PhpSpreadsheet/issues/1285) [PR #2792](https://github.com/PHPOffice/PhpSpreadsheet/pull/2792)
2020
- Allow Csv Reader to store null string rather than leave cell empty [Issue #2840](https://github.com/PHPOffice/PhpSpreadsheet/issues/2840) [PR #2842](https://github.com/PHPOffice/PhpSpreadsheet/pull/2842)
21+
- Provide new Worksheet methods to identify if a row or column is "empty", making allowance for different definitions of "empty":
22+
- Treat rows/columns containing no cell records as empty (default)
23+
- Treat cells containing a null value as empty
24+
- Treat cells containing an empty string as empty
2125

2226
### Changed
2327

phpstan-baseline.neon

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3615,11 +3615,6 @@ parameters:
36153615
count: 1
36163616
path: src/PhpSpreadsheet/Worksheet/CellIterator.php
36173617

3618-
-
3619-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Column\\:\\:\\$parent \\(PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\) does not accept PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\|null\\.$#"
3620-
count: 1
3621-
path: src/PhpSpreadsheet/Worksheet/Column.php
3622-
36233618
-
36243619
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Drawing\\\\Shadow\\:\\:\\$color \\(PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Color\\) does not accept PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Color\\|null\\.$#"
36253620
count: 1
@@ -3645,11 +3640,6 @@ parameters:
36453640
count: 1
36463641
path: src/PhpSpreadsheet/Worksheet/PageSetup.php
36473642

3648-
-
3649-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Row\\:\\:\\$worksheet \\(PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\) does not accept PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\|null\\.$#"
3650-
count: 1
3651-
path: src/PhpSpreadsheet/Worksheet/Row.php
3652-
36533643
-
36543644
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\SheetView\\:\\:\\$sheetViewTypes has no type specified\\.$#"
36553645
count: 1

src/PhpSpreadsheet/Worksheet/CellIterator.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
*/
1313
abstract class CellIterator implements Iterator
1414
{
15+
public const TREAT_NULL_VALUE_AS_EMPTY_CELL = 1;
16+
17+
public const TREAT_EMPTY_STRING_AS_EMPTY_CELL = 2;
18+
1519
/**
1620
* Worksheet to iterate.
1721
*

src/PhpSpreadsheet/Worksheet/Column.php

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ class Column
99
*
1010
* @var Worksheet
1111
*/
12-
private $parent;
12+
private $worksheet;
1313

1414
/**
1515
* Column index.
@@ -23,10 +23,10 @@ class Column
2323
*
2424
* @param string $columnIndex
2525
*/
26-
public function __construct(?Worksheet $parent = null, $columnIndex = 'A')
26+
public function __construct(Worksheet $worksheet, $columnIndex = 'A')
2727
{
2828
// Set parent and column index
29-
$this->parent = $parent;
29+
$this->worksheet = $worksheet;
3030
$this->columnIndex = $columnIndex;
3131
}
3232

@@ -36,7 +36,7 @@ public function __construct(?Worksheet $parent = null, $columnIndex = 'A')
3636
public function __destruct()
3737
{
3838
// @phpstan-ignore-next-line
39-
$this->parent = null;
39+
$this->worksheet = null;
4040
}
4141

4242
/**
@@ -57,6 +57,53 @@ public function getColumnIndex(): string
5757
*/
5858
public function getCellIterator($startRow = 1, $endRow = null)
5959
{
60-
return new ColumnCellIterator($this->parent, $this->columnIndex, $startRow, $endRow);
60+
return new ColumnCellIterator($this->worksheet, $this->columnIndex, $startRow, $endRow);
61+
}
62+
63+
/**
64+
* Returns a boolean true if the column contains no cells. By default, this means that no cell records exist in the
65+
* collection for this column. false will be returned otherwise.
66+
* This rule can be modified by passing a $definitionOfEmptyFlags value:
67+
* 1 - CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL If the only cells in the collection are null value
68+
* cells, then the column will be considered empty.
69+
* 2 - CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL If the only cells in the collection are empty
70+
* string value cells, then the column will be considered empty.
71+
* 3 - CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL | CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL
72+
* If the only cells in the collection are null value or empty string value cells, then the column
73+
* will be considered empty.
74+
*
75+
* @param int $definitionOfEmptyFlags
76+
* Possible Flag Values are:
77+
* CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL
78+
* CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL
79+
*/
80+
public function isEmpty(int $definitionOfEmptyFlags = 0): bool
81+
{
82+
$nullValueCellIsEmpty = (bool) ($definitionOfEmptyFlags & CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL);
83+
$emptyStringCellIsEmpty = (bool) ($definitionOfEmptyFlags & CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL);
84+
85+
$cellIterator = $this->getCellIterator();
86+
$cellIterator->setIterateOnlyExistingCells(true);
87+
foreach ($cellIterator as $cell) {
88+
$value = $cell->getValue();
89+
if ($value === null && $nullValueCellIsEmpty === true) {
90+
continue;
91+
}
92+
if ($value === '' && $emptyStringCellIsEmpty === true) {
93+
continue;
94+
}
95+
96+
return false;
97+
}
98+
99+
return true;
100+
}
101+
102+
/**
103+
* Returns bound worksheet.
104+
*/
105+
public function getWorksheet(): Worksheet
106+
{
107+
return $this->worksheet;
61108
}
62109
}

src/PhpSpreadsheet/Worksheet/Row.php

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ class Row
2323
*
2424
* @param int $rowIndex
2525
*/
26-
public function __construct(?Worksheet $worksheet = null, $rowIndex = 1)
26+
public function __construct(Worksheet $worksheet, $rowIndex = 1)
2727
{
2828
// Set parent and row index
2929
$this->worksheet = $worksheet;
@@ -59,6 +59,45 @@ public function getCellIterator($startColumn = 'A', $endColumn = null)
5959
return new RowCellIterator($this->worksheet, $this->rowIndex, $startColumn, $endColumn);
6060
}
6161

62+
/**
63+
* Returns a boolean true if the row contains no cells. By default, this means that no cell records exist in the
64+
* collection for this row. false will be returned otherwise.
65+
* This rule can be modified by passing a $definitionOfEmptyFlags value:
66+
* 1 - CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL If the only cells in the collection are null value
67+
* cells, then the row will be considered empty.
68+
* 2 - CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL If the only cells in the collection are empty
69+
* string value cells, then the row will be considered empty.
70+
* 3 - CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL | CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL
71+
* If the only cells in the collection are null value or empty string value cells, then the row
72+
* will be considered empty.
73+
*
74+
* @param int $definitionOfEmptyFlags
75+
* Possible Flag Values are:
76+
* CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL
77+
* CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL
78+
*/
79+
public function isEmpty(int $definitionOfEmptyFlags = 0): bool
80+
{
81+
$nullValueCellIsEmpty = (bool) ($definitionOfEmptyFlags & CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL);
82+
$emptyStringCellIsEmpty = (bool) ($definitionOfEmptyFlags & CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL);
83+
84+
$cellIterator = $this->getCellIterator();
85+
$cellIterator->setIterateOnlyExistingCells(true);
86+
foreach ($cellIterator as $cell) {
87+
$value = $cell->getValue();
88+
if ($value === null && $nullValueCellIsEmpty === true) {
89+
continue;
90+
}
91+
if ($value === '' && $emptyStringCellIsEmpty === true) {
92+
continue;
93+
}
94+
95+
return false;
96+
}
97+
98+
return true;
99+
}
100+
62101
/**
63102
* Returns bound worksheet.
64103
*/

src/PhpSpreadsheet/Worksheet/Worksheet.php

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3250,6 +3250,66 @@ public function copy()
32503250
return clone $this;
32513251
}
32523252

3253+
/**
3254+
* Returns a boolean true if the specified row contains no cells. By default, this means that no cell records
3255+
* exist in the collection for this row. false will be returned otherwise.
3256+
* This rule can be modified by passing a $definitionOfEmptyFlags value:
3257+
* 1 - CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL If the only cells in the collection are null value
3258+
* cells, then the row will be considered empty.
3259+
* 2 - CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL If the only cells in the collection are empty
3260+
* string value cells, then the row will be considered empty.
3261+
* 3 - CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL | CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL
3262+
* If the only cells in the collection are null value or empty string value cells, then the row
3263+
* will be considered empty.
3264+
*
3265+
* @param int $definitionOfEmptyFlags
3266+
* Possible Flag Values are:
3267+
* CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL
3268+
* CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL
3269+
*/
3270+
public function isEmptyRow(int $rowId, int $definitionOfEmptyFlags = 0): bool
3271+
{
3272+
try {
3273+
$iterator = new RowIterator($this, $rowId, $rowId);
3274+
$iterator->seek($rowId);
3275+
$row = $iterator->current();
3276+
} catch (Exception $e) {
3277+
return true;
3278+
}
3279+
3280+
return $row->isEmpty($definitionOfEmptyFlags);
3281+
}
3282+
3283+
/**
3284+
* Returns a boolean true if the specified column contains no cells. By default, this means that no cell records
3285+
* exist in the collection for this column. false will be returned otherwise.
3286+
* This rule can be modified by passing a $definitionOfEmptyFlags value:
3287+
* 1 - CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL If the only cells in the collection are null value
3288+
* cells, then the column will be considered empty.
3289+
* 2 - CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL If the only cells in the collection are empty
3290+
* string value cells, then the column will be considered empty.
3291+
* 3 - CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL | CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL
3292+
* If the only cells in the collection are null value or empty string value cells, then the column
3293+
* will be considered empty.
3294+
*
3295+
* @param int $definitionOfEmptyFlags
3296+
* Possible Flag Values are:
3297+
* CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL
3298+
* CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL
3299+
*/
3300+
public function isEmptyColumn(string $columnId, int $definitionOfEmptyFlags = 0): bool
3301+
{
3302+
try {
3303+
$iterator = new ColumnIterator($this, $columnId, $columnId);
3304+
$iterator->seek($columnId);
3305+
$column = $iterator->current();
3306+
} catch (Exception $e) {
3307+
return true;
3308+
}
3309+
3310+
return $column->isEmpty($definitionOfEmptyFlags);
3311+
}
3312+
32533313
/**
32543314
* Implement PHP __clone to create a deep clone, not just a shallow copy.
32553315
*/

0 commit comments

Comments
 (0)