Skip to content

Commit 55163f6

Browse files
committed
Add some methods
* `XlsxFastEditor::readArray()` * `XlsxFastEditor::cellOrderCompare()` * `XlsxFastEditorCell::column()`
1 parent c47ae06 commit 55163f6

File tree

5 files changed

+93
-14
lines changed

5 files changed

+93
-14
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,13 @@ try {
6666
$xlsxFastEditor->writeString($worksheetId1, 'D4', 'Hello');
6767
$xlsxFastEditor->writeHyperlink($sheet1, 'B4', 'https://example.net/'); // Only for cells with an existing hyperlink
6868

69+
// Read as array
70+
$table = $xlsxFastEditor->readArray($sheet1);
71+
$s = $table['B'][2];
72+
73+
$table = $xlsxFastEditor->readArrayWithHeaders($sheet1);
74+
$s = $table['columnName'][2];
75+
6976
// Regex search & replace operating globally on all the worksheets:
7077
$xlsxFastEditor->textReplace('/Hello/i', 'World');
7178

@@ -88,6 +95,7 @@ try {
8895

8996
// Methods for cells
9097
$cellName = $cell->name();
98+
$columnName = $cell->column();
9199
$fx = $cell->readFormula();
92100
$f = $cell->readFloat();
93101
$i = $cell->readInt();

src/XlsxFastEditor.php

Lines changed: 62 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -380,21 +380,71 @@ public function rowsIterator(int $sheetNumber): \Traversable
380380
}
381381

382382
/**
383-
* Sort cells within the same line, such as B3, AA3. Compare only the column part.
384-
* @param $ref1 A cell reference such as B3
385-
* @param $ref1 A cell reference such as AA3
383+
* Produce an array from a worksheet, indexed by column name (like `AB`) first, then line (like `12`).
384+
* Only the existing lines and cells are included.
385+
* @return array<string,array<int,null|string>> An array that can be access like `$array['AB'][12]`
386+
*/
387+
public function readArray(int $sheetNumber): array
388+
{
389+
$table = [];
390+
foreach ($this->rowsIterator($sheetNumber) as $row) {
391+
foreach ($row->cellsIterator() as $cell) {
392+
$table[$cell->column()][$row->number()] = $cell->readString();
393+
}
394+
}
395+
return $table;
396+
}
397+
398+
/**
399+
* Produce an array from a worksheet, indexed by column header (like `columnName`) first, then line (like `12`),
400+
* having the column header defined in the first existing line of the spreadsheet.
401+
* Only the existing lines and cells are included.
402+
* @return array<string,array<int,null|string>> An array that can be access like `$array['columnName'][12]`
403+
*/
404+
public function readArrayWithHeaders(int $sheetNumber): array
405+
{
406+
$table = [];
407+
$headers = [];
408+
$firstRow = true;
409+
foreach ($this->rowsIterator($sheetNumber) as $row) {
410+
if ($firstRow) {
411+
foreach ($row->cellsIterator() as $cell) {
412+
$headers[$cell->column()] = $cell->readString();
413+
}
414+
$firstRow = false;
415+
continue;
416+
}
417+
foreach ($row->cellsIterator() as $cell) {
418+
$header = $headers[$cell->column()] ?? $cell->column();
419+
$table[$header][$row->number()] = $cell->readString();
420+
}
421+
}
422+
return $table;
423+
}
424+
425+
/**
426+
* Sort cells (such as `B3`, `AA23`) on column first (such as `B`, `AA`) and then line (such as `3`, `23`).
427+
* @param $ref1 A cell reference such as `B3`
428+
* @param $ref1 A cell reference such as `AA23`
386429
* @return int -1 if $ref1 is before $ref2; 1 if $ref1 is greater than $ref2, and 0 if they are equal.
387-
* @internal
388430
*/
389-
public static function _columnOrderCompare(string $ref1, string $ref2): int
431+
public static function cellOrderCompare(string $ref1, string $ref2): int
390432
{
391-
$pattern = '/[^A-Z]+/';
392-
$column1 = preg_replace($pattern, '', $ref1) ?? '';
393-
$column2 = preg_replace($pattern, '', $ref2) ?? '';
394-
$length1 = strlen($column1);
395-
$length2 = strlen($column2);
396-
if ($length1 !== $length2) {
397-
return $length1 <=> $length2;
433+
if (preg_match('/^([A-Z]+)(\d+)$/', $ref1, $matches1) === 1 && preg_match('/^([A-Z]+)(\d+)$/', $ref2, $matches2) === 1) {
434+
$column1 = $matches1[1];
435+
$column2 = $matches2[1];
436+
$length1 = strlen($column1);
437+
$length2 = strlen($column2);
438+
if ($length1 !== $length2) {
439+
return $length1 <=> $length2;
440+
}
441+
$cmp = strcmp($column1, $column2);
442+
if ($cmp !== 0) {
443+
return $cmp;
444+
}
445+
$line1 = (int)$matches1[2];
446+
$line2 = (int)$matches2[2];
447+
return $line1 <=> $line2;
398448
}
399449
return strcmp($ref1, $ref2);
400450
}

src/XlsxFastEditorCell.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,17 @@ public function name(): string
4545
return $this->c->getAttribute('r');
4646
}
4747

48+
/**
49+
* Column name (e.g., `'D'`).
50+
*/
51+
public function column(): string
52+
{
53+
if (preg_match('/^([A-Z]+)/', $this->name(), $matches) == 0 || empty($matches[1])) {
54+
throw new XlsxFastEditorXmlException("Error querying column name for cell {$this->name()}!");
55+
}
56+
return $matches[1];
57+
}
58+
4859
/**
4960
* Access the previous existing cell, if any, null otherwise.
5061
*/
@@ -153,7 +164,6 @@ public function readString(): ?string
153164

154165
if ($this->c->getAttribute('t') === 's') {
155166
// Shared string
156-
157167
if (!ctype_digit($value)) {
158168
throw new XlsxFastEditorXmlException("Error querying XML fragment for shared string in cell {$this->name()}!");
159169
}

src/XlsxFastEditorRow.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ public function getCell(string $cellName, int $accessMode = XlsxFastEditor::ACCE
139139

140140
// Excel expects the cells to be sorted
141141
$sibling = $this->r->firstElementChild;
142-
while ($sibling !== null && XlsxFastEditor::_columnOrderCompare($sibling->getAttribute('r'), $cellName) < 0) {
142+
while ($sibling !== null && XlsxFastEditor::cellOrderCompare($sibling->getAttribute('r'), $cellName) < 0) {
143143
$sibling = $sibling->nextElementSibling;
144144
}
145145
$this->r->insertBefore($c, $sibling);

tests/test.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@
4242
assert($xlsxFastEditor->readInt($sheet2, 'c3') === -5 * 2);
4343
assert($xlsxFastEditor->readString($sheet2, 'B3') === 'déjà-vu');
4444

45+
assert($xlsxFastEditor->readArray($sheet1)['B'][2] === 'Hello');
46+
assert($xlsxFastEditor->readArrayWithHeaders($sheet1)['Strings'][2] === 'Hello');
47+
4548
// Navigation
4649
assert($xlsxFastEditor->getFirstRow($sheet1)?->number() === 1);
4750
assert($xlsxFastEditor->getRow($sheet1, 1)?->getFirstCell()?->name() === 'A1');
@@ -50,6 +53,14 @@
5053
assert($xlsxFastEditor->getRow($sheet1, 4)?->getCell('D4')?->name() === 'D4');
5154
assert($xlsxFastEditor->getLastRow($sheet1)?->number() === 4);
5255

56+
assert(XlsxFastEditor::cellOrderCompare('B3', 'AA23') < 0);
57+
assert(XlsxFastEditor::cellOrderCompare('AA23', 'AB23') < 0);
58+
assert(XlsxFastEditor::cellOrderCompare('BB22', 'BB123') < 0);
59+
assert(XlsxFastEditor::cellOrderCompare('AA23', 'AA23') === 0);
60+
assert(XlsxFastEditor::cellOrderCompare('AA23', 'B3') > 0);
61+
assert(XlsxFastEditor::cellOrderCompare('AB23', 'AA23') > 0);
62+
assert(XlsxFastEditor::cellOrderCompare('BB123', 'BB22') > 0);
63+
5364
// Iterators
5465
$nb = 0;
5566
foreach ($xlsxFastEditor->rowsIterator($sheet1) as $row) {

0 commit comments

Comments
 (0)