Skip to content

Commit db33c80

Browse files
committed
Better typing of exceptions
1 parent 2b3e82d commit db33c80

9 files changed

+279
-40
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ try {
8989

9090
// Navigation methods for existing cells
9191
$cell = $row->getFirstCell();
92-
$cell = $row->getCell('D4');
92+
$cell = $row->getCellOrNull('D4');
9393
$cell = $cell->getPreviousCell();
9494
$cell = $cell->getNextCell();
9595
$cell = $row->getLastCell();

phpstan.neon

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,10 @@ parameters:
2222
strictCalls: true
2323
switchConditionsMatchingType: true
2424
uselessCast: true
25+
exceptions:
26+
check:
27+
missingCheckedExceptionInThrows: true
28+
tooWideThrowType: true
29+
implicitThrows: false
2530
includes:
2631
- vendor/phpstan/phpstan-strict-rules/rules.neon

src/XlsxFastEditor.php

Lines changed: 167 additions & 21 deletions
Large diffs are not rendered by default.

src/XlsxFastEditorCell.php

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,15 @@ public function __construct(XlsxFastEditor $editor, int $sheetNumber, \DOMElemen
2222
$this->c = $c;
2323
}
2424

25+
/**
26+
* @throws XlsxFastEditorXmlException
27+
*/
2528
private function getXPath(): \DOMXPath
2629
{
2730
if ($this->xpath === null) {
2831
$dom = $this->c->ownerDocument;
2932
if ($dom === null) {
30-
throw new XlsxFastEditorInputException("Internal error accessing cell {$this->name()}!");
33+
throw new XlsxFastEditorXmlException("Internal error accessing cell {$this->name()}!");
3134
}
3235
$xpath = new \DOMXPath($dom);
3336
$xpath->registerNamespace('o', XlsxFastEditor::_OXML_NAMESPACE);
@@ -47,6 +50,7 @@ public function name(): string
4750

4851
/**
4952
* Column name (e.g., `'D'`).
53+
* @throws XlsxFastEditorXmlException
5054
*/
5155
public function column(): string
5256
{
@@ -88,6 +92,7 @@ public function getNextCell(): ?XlsxFastEditorCell
8892

8993
/**
9094
* Access the parent row of the cell.
95+
* @throws XlsxFastEditorXmlException
9196
*/
9297
public function getRow(): XlsxFastEditorRow
9398
{
@@ -100,6 +105,7 @@ public function getRow(): XlsxFastEditorRow
100105

101106
/**
102107
* Read a formula in the given worksheet at the given cell location.
108+
* @throws XlsxFastEditorXmlException
103109
*/
104110
public function readFormula(): ?string
105111
{
@@ -119,10 +125,9 @@ private function value(): ?string
119125
$vs = $this->c->getElementsByTagName('v');
120126
if ($vs->length > 0) {
121127
$v = $vs[0];
122-
if (!($v instanceof \DOMElement)) {
123-
throw new XlsxFastEditorXmlException("Error querying XML value for cell {$this->name()}!");
128+
if ($v instanceof \DOMElement) {
129+
return $v->nodeValue;
124130
}
125-
return $v->nodeValue;
126131
}
127132
return null;
128133
}
@@ -141,14 +146,21 @@ public function readFloat(): ?float
141146

142147
/**
143148
* Read the date/time value of the cell, if any.
149+
* @throws XlsxFastEditorFileFormatException
150+
* @throws XlsxFastEditorXmlException
144151
*/
145152
public function readDateTime(): ?\DateTimeImmutable
146153
{
147154
$value = $this->readFloat();
148155
if ($value === null) {
149156
return null;
150157
}
151-
return XlsxFastEditor::excelDateToDateTime($value, $this->editor->getWorkbookDateSystem());
158+
try {
159+
return XlsxFastEditor::excelDateToDateTime($value, $this->editor->getWorkbookDateSystem());
160+
} catch (\InvalidArgumentException $iaex) {
161+
// Never happens
162+
return null;
163+
}
152164
}
153165

154166
/**
@@ -166,6 +178,8 @@ public function readInt(): ?int
166178
/**
167179
* Read the string value of the cell,
168180
* compatible with the shared string approach.
181+
* @throws XlsxFastEditorFileFormatException
182+
* @throws XlsxFastEditorXmlException
169183
*/
170184
public function readString(): ?string
171185
{
@@ -188,6 +202,8 @@ public function readString(): ?string
188202

189203
/**
190204
* Read the hyperlink value of the cell, if any.
205+
* @throws XlsxFastEditorFileFormatException
206+
* @throws XlsxFastEditorXmlException
191207
*/
192208
public function readHyperlink(): ?string
193209
{
@@ -196,12 +212,17 @@ public function readHyperlink(): ?string
196212
if (!is_string($rid) || $rid === '') {
197213
return null;
198214
}
199-
return $this->editor->_getHyperlink($this->sheetNumber, $rid);
215+
try {
216+
return $this->editor->_getHyperlink($this->sheetNumber, $rid);
217+
} catch (\InvalidArgumentException $iax) {
218+
throw new XlsxFastEditorXmlException("Error querying XML fragment for hyperlink in cell {$this->name()}!", $iax->getCode(), $iax);
219+
}
200220
}
201221

202222
/**
203223
* Clean the cell to have its value written.
204224
* @return \DOMElement The `<v>` value element of the provided cell, or null in case of error.
225+
* @throws XlsxFastEditorXmlException
205226
*/
206227
private function initCellValue(): \DOMElement
207228
{
@@ -224,9 +245,13 @@ private function initCellValue(): \DOMElement
224245
}
225246
if ($v === null) {
226247
// There was no existing <v>
227-
$v = $this->c->ownerDocument === null ? null : $this->c->ownerDocument->createElement('v');
248+
try {
249+
$v = $this->c->ownerDocument === null ? null : $this->c->ownerDocument->createElement('v');
250+
} catch (\DOMException $dex) {
251+
throw new XlsxFastEditorXmlException("Error creating value for cell {$this->name()}!", $dex->code, $dex);
252+
}
228253
if ($v == false) {
229-
throw new XlsxFastEditorXmlException('Error creating value for cell!');
254+
throw new XlsxFastEditorXmlException("Error creating value for cell {$this->name()}!");
230255
}
231256
$this->c->appendChild($v);
232257
}
@@ -237,6 +262,7 @@ private function initCellValue(): \DOMElement
237262
/**
238263
* Write a formulat, without changing the type/style of the cell.
239264
* Removes the formulas of the cell, if any.
265+
* @throws XlsxFastEditorXmlException
240266
*/
241267
public function writeFormula(string $value): void
242268
{
@@ -260,9 +286,13 @@ public function writeFormula(string $value): void
260286

261287
$dom = $this->c->ownerDocument;
262288
if ($dom === null) {
263-
throw new XlsxFastEditorInputException("Internal error accessing cell {$this->name()}!");
289+
throw new XlsxFastEditorXmlException("Internal error accessing cell {$this->name()}!");
290+
}
291+
try {
292+
$f = $dom->createElement('f', $value);
293+
} catch (\DOMException $dex) {
294+
throw new XlsxFastEditorXmlException("Error creating formulat for cell {$this->name()}!", $dex->code, $dex);
264295
}
265-
$f = $dom->createElement('f', $value);
266296
$this->c->appendChild($f);
267297

268298
$this->editor->_clearCalcChain();
@@ -272,8 +302,8 @@ public function writeFormula(string $value): void
272302
/**
273303
* Write a number, without changing the type/style of the cell.
274304
* Removes the formulas of the cell, if any.
275-
*
276305
* @param int|float $value
306+
* @throws XlsxFastEditorXmlException
277307
*/
278308
private function writeNumber($value): void
279309
{
@@ -286,6 +316,7 @@ private function writeNumber($value): void
286316
* Write a float, without changing the type/style of the cell.
287317
* Removes the formulas of the cell, if any.
288318
* @param float $value
319+
* @throws XlsxFastEditorXmlException
289320
*/
290321
public function writeFloat(float $value): void
291322
{
@@ -296,6 +327,7 @@ public function writeFloat(float $value): void
296327
* Write an integer, without changing the type/style of the cell.
297328
* Removes the formulas of the cell, if any.
298329
* @param int $value
330+
* @throws XlsxFastEditorXmlException
299331
*/
300332
public function writeInt(int $value): void
301333
{
@@ -305,6 +337,8 @@ public function writeInt(int $value): void
305337
/**
306338
* Write a string, without changing the type/style of the cell.
307339
* Removes the formulas of the cell, if any.
340+
* @throws XlsxFastEditorFileFormatException
341+
* @throws XlsxFastEditorXmlException
308342
*/
309343
public function writeString(string $value): void
310344
{
@@ -319,6 +353,8 @@ public function writeString(string $value): void
319353
* Replace the hyperlink of the cell, if that cell already has an hyperlink.
320354
* Warning: does not support the creation of a new hyperlink.
321355
* @return bool True if the hyperlink could be replaced, false otherwise.
356+
* @throws XlsxFastEditorFileFormatException
357+
* @throws XlsxFastEditorXmlException
322358
*/
323359
public function writeHyperlink(string $value): bool
324360
{
@@ -327,6 +363,10 @@ public function writeHyperlink(string $value): bool
327363
if (!is_string($rId) || $rId === '') {
328364
return false;
329365
}
330-
return $this->editor->_setHyperlink($this->sheetNumber, $rId, $value);
366+
try {
367+
return $this->editor->_setHyperlink($this->sheetNumber, $rId, $value);
368+
} catch (\InvalidArgumentException $iax) {
369+
throw new XlsxFastEditorXmlException("Error querying XML fragment for hyperlink in cell {$this->name()}!", $iax->getCode(), $iax);
370+
}
331371
}
332372
}

src/XlsxFastEditorInputException.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
namespace alexandrainst\XlsxFastEditor;
44

55
/**
6-
* Errors related to invalid user inputs.
6+
* Errors related to user inputs such as accessing unexisting cells.
77
*/
88
final class XlsxFastEditorInputException extends \Exception
99
{

src/XlsxFastEditorRow.php

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,15 @@ public function number(): int
3131
return (int)$this->r->getAttribute('r');
3232
}
3333

34+
/**
35+
* @throws XlsxFastEditorXmlException
36+
*/
3437
private function getXPath(): \DOMXPath
3538
{
3639
if ($this->xpath === null) {
3740
$dom = $this->r->ownerDocument;
3841
if ($dom === null) {
39-
throw new XlsxFastEditorInputException("Internal error accessing row {$this->number()}!");
42+
throw new XlsxFastEditorXmlException("Internal error accessing row {$this->number()}!");
4043
}
4144
$xpath = new \DOMXPath($dom);
4245
$xpath->registerNamespace('o', XlsxFastEditor::_OXML_NAMESPACE);
@@ -115,9 +118,16 @@ public function getFirstCell(): ?XlsxFastEditorCell
115118
* set to `XlsxFastEditor::ACCESS_MODE_AUTOCREATE` to auto-create the cell.
116119
* @return XlsxFastEditorCell|null A cell, potentially `null` if the cell does not exist and `$accessMode` is set to `XlsxFastEditor::ACCESS_MODE_NULL`
117120
* @phpstan-return ($accessMode is XlsxFastEditor::ACCESS_MODE_NULL ? XlsxFastEditorCell|null : XlsxFastEditorCell)
121+
* @internal
122+
* @throws \InvalidArgumentException if `$cellName` has an invalid format
123+
* @throws XlsxFastEditorInputException optionally if the corresponding cell does not exist, depending on choice of `$accessMode`
124+
* @throws XlsxFastEditorXmlException
118125
*/
119126
public function getCell(string $cellName, int $accessMode = XlsxFastEditor::ACCESS_MODE_NULL): ?XlsxFastEditorCell
120127
{
128+
if (!ctype_alnum($cellName)) {
129+
throw new \InvalidArgumentException("Invalid cell reference {$cellName}!");
130+
}
121131
$xpath = $this->getXPath();
122132
$cs = $xpath->query("./o:c[@r='$cellName'][1]", $this->r);
123133
$c = null;
@@ -131,7 +141,11 @@ public function getCell(string $cellName, int $accessMode = XlsxFastEditor::ACCE
131141
if ($c === null && $accessMode === XlsxFastEditor::ACCESS_MODE_AUTOCREATE) {
132142
// The cell <c> was not found
133143
$dom = $xpath->document;
134-
$c = $dom->createElement('c');
144+
try {
145+
$c = $dom->createElement('c');
146+
} catch (\DOMException $dex) {
147+
throw new XlsxFastEditorXmlException("Error creating cell {$this->sheetNumber}/{$cellName}!", $dex->code, $dex);
148+
}
135149
if ($c === false) {
136150
throw new XlsxFastEditorXmlException("Error creating cell {$this->sheetNumber}/{$cellName}!");
137151
}
@@ -155,6 +169,40 @@ public function getCell(string $cellName, int $accessMode = XlsxFastEditor::ACCE
155169
return new XlsxFastEditorCell($this->editor, $this->sheetNumber, $c);
156170
}
157171

172+
/**
173+
* Get the cell of the given name, or null if if does not exist.
174+
* @param $cellName Cell name such as `B4`
175+
* @return XlsxFastEditorCell|null A cell, potentially `null` if the cell does not exist
176+
* @throws \InvalidArgumentException if `$cellName` has an invalid format
177+
* @throws XlsxFastEditorXmlException
178+
*/
179+
public function getCellOrNull(string $cellName): ?XlsxFastEditorCell
180+
{
181+
try {
182+
return $this->getCell($cellName, XlsxFastEditor::ACCESS_MODE_NULL);
183+
} catch (XlsxFastEditorInputException $iex) {
184+
// Will not happen
185+
return null;
186+
}
187+
}
188+
189+
/**
190+
* Get the cell of the given name, or autocreate it if it does not already exist.
191+
* @param $cellName Cell name such as `B4`
192+
* @return XlsxFastEditorCell A cell
193+
* @throws \InvalidArgumentException if `$cellName` has an invalid format
194+
* @throws XlsxFastEditorXmlException
195+
*/
196+
public function getCellAutocreate(string $cellName): XlsxFastEditorCell
197+
{
198+
try {
199+
return $this->getCell($cellName, XlsxFastEditor::ACCESS_MODE_AUTOCREATE);
200+
} catch (XlsxFastEditorInputException $iex) {
201+
// Will not happen
202+
throw new XlsxFastEditorXmlException('Internal error with getCell!', $iex->getCode(), $iex);
203+
}
204+
}
205+
158206
/**
159207
* Get the last existing cell for a given line.
160208
* @return XlsxFastEditorCell|null The last cell of the given line if it exists, null otherwise.

src/XlsxFastEditorXmlException.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
namespace alexandrainst\XlsxFastEditor;
44

55
/**
6-
* Errors related to XML manipulation.
6+
* Errors related to XML manipulation, indicating that the XLSX document is internally damaged.
77
*/
88
final class XlsxFastEditorXmlException extends XlsxFastEditorException
99
{

src/XlsxFastEditorZipException.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
namespace alexandrainst\XlsxFastEditor;
44

55
/**
6-
* Errors related to ZIP operations.
6+
* Errors related to ZIP operations, indicating that the external structure of the XLSX document is damaged or with invalid disc access rights.
77
* Returns error codes from https://php.net/ziparchive.open
88
*/
99
final class XlsxFastEditorZipException extends XlsxFastEditorException

tests/test.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
assert($xlsxFastEditor->getRow($sheet1, 1)?->getFirstCell()?->name() === 'A1');
6161
assert($xlsxFastEditor->getRow($sheet1, 2)?->number() === 2);
6262
assert($xlsxFastEditor->getRow($sheet1, 3)?->getLastCell()?->name() === 'F3');
63-
assert($xlsxFastEditor->getRow($sheet1, 4)?->getCell('D4')?->name() === 'D4');
63+
assert($xlsxFastEditor->getRow($sheet1, 4)?->getCellOrNull('D4')?->name() === 'D4');
6464
assert($xlsxFastEditor->getLastRow($sheet1)?->number() === 4);
6565

6666
assert(XlsxFastEditor::cellOrderCompare('B3', 'AA23') < 0);

0 commit comments

Comments
 (0)