Skip to content

Commit 57a7203

Browse files
authored
Phpstan and Xlsx Reader (#3044)
Eliminate most Phpstan messages in Xlsx Reader. In combination with similar changes to Xlsx Writer, baseline will shrink to just over 3,000 lines.
1 parent 5f33ec0 commit 57a7203

File tree

10 files changed

+109
-510
lines changed

10 files changed

+109
-510
lines changed

phpstan-baseline.neon

Lines changed: 0 additions & 450 deletions
Large diffs are not rendered by default.

src/PhpSpreadsheet/Reader/Xlsx.php

Lines changed: 52 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
3232
use PhpOffice\PhpSpreadsheet\Spreadsheet;
3333
use PhpOffice\PhpSpreadsheet\Style\Color;
34+
use PhpOffice\PhpSpreadsheet\Style\Font as StyleFont;
3435
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
3536
use PhpOffice\PhpSpreadsheet\Style\Style;
3637
use PhpOffice\PhpSpreadsheet\Worksheet\HeaderFooterDrawing;
@@ -293,7 +294,7 @@ public function listWorksheetInfo($filename)
293294
return $worksheetInfo;
294295
}
295296

296-
private static function castToBoolean($c)
297+
private static function castToBoolean(SimpleXMLElement $c): bool
297298
{
298299
$value = isset($c->v) ? (string) $c->v : null;
299300
if ($value == '0') {
@@ -305,17 +306,21 @@ private static function castToBoolean($c)
305306
return (bool) $c->v;
306307
}
307308

308-
private static function castToError($c)
309+
private static function castToError(SimpleXMLElement $c): ?string
309310
{
310311
return isset($c->v) ? (string) $c->v : null;
311312
}
312313

313-
private static function castToString($c)
314+
private static function castToString(SimpleXMLElement $c): ?string
314315
{
315316
return isset($c->v) ? (string) $c->v : null;
316317
}
317318

318-
private function castToFormula($c, $r, &$cellDataType, &$value, &$calculatedValue, &$sharedFormulas, $castBaseType): void
319+
/**
320+
* @param mixed $value
321+
* @param mixed $calculatedValue
322+
*/
323+
private function castToFormula(SimpleXMLElement $c, string $r, string &$cellDataType, &$value, &$calculatedValue, array &$sharedFormulas, string $castBaseType): void
319324
{
320325
$attr = $c->f->attributes();
321326
$cellDataType = 'f';
@@ -389,7 +394,7 @@ private function getFromZipArchive(ZipArchive $archive, $fileName = '')
389394
$contents = $archive->getFromName(substr($fileName, 1), 0, ZipArchive::FL_NOCASE);
390395
}
391396

392-
return $contents;
397+
return ($contents === false) ? '' : $contents;
393398
}
394399

395400
/**
@@ -1143,7 +1148,7 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
11431148
}
11441149

11451150
// Header/footer images
1146-
if ($xmlSheet && $xmlSheet->legacyDrawingHF && !$this->readDataOnly) {
1151+
if ($xmlSheet && $xmlSheet->legacyDrawingHF) {
11471152
if ($zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')) {
11481153
$relsWorksheet = $this->loadZipNoNamespace(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels', Namespaces::RELATIONSHIPS);
11491154
$vmlRelationship = '';
@@ -1550,7 +1555,7 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
15501555

15511556
break;
15521557
case '_xlnm.Print_Area':
1553-
$rangeSets = preg_split("/('?(?:.*?)'?(?:![A-Z0-9]+:[A-Z0-9]+)),?/", $extractedRange, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
1558+
$rangeSets = preg_split("/('?(?:.*?)'?(?:![A-Z0-9]+:[A-Z0-9]+)),?/", $extractedRange, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE) ?: [];
15541559
$newRangeSets = [];
15551560
foreach ($rangeSets as $rangeSet) {
15561561
[, $rangeSet] = Worksheet::extractSheetTitle($rangeSet, true);
@@ -1605,7 +1610,7 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
16051610
if (strpos((string) $definedName, '!') !== false) {
16061611
$range[0] = str_replace("''", "'", $range[0]);
16071612
$range[0] = str_replace("'", '', $range[0]);
1608-
if ($worksheet = $excel->getSheetByName($range[0])) {
1613+
if ($worksheet = $excel->getSheetByName($range[0])) { // @phpstan-ignore-line
16091614
$excel->addDefinedName(DefinedName::createInstance((string) $definedName['name'], $worksheet, $extractedRange, true, $scope));
16101615
} else {
16111616
$excel->addDefinedName(DefinedName::createInstance((string) $definedName['name'], $scope, $extractedRange, true, $scope));
@@ -1626,7 +1631,7 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
16261631
// Need to split on a comma or a space if not in quotes, and extract the first part.
16271632
$definedNameValueParts = preg_split("/[ ,](?=([^']*'[^']*')*[^']*$)/miuU", $definedRange);
16281633
// Extract sheet name
1629-
[$extractedSheetName] = Worksheet::extractSheetTitle((string) $definedNameValueParts[0], true);
1634+
[$extractedSheetName] = Worksheet::extractSheetTitle((string) $definedNameValueParts[0], true); // @phpstan-ignore-line
16301635
$extractedSheetName = trim($extractedSheetName, "'");
16311636

16321637
// Locate sheet
@@ -1673,7 +1678,7 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
16731678
if (isset($charts[$chartEntryRef])) {
16741679
$chartPositionRef = $charts[$chartEntryRef]['sheet'] . '!' . $charts[$chartEntryRef]['id'];
16751680
if (isset($chartDetails[$chartPositionRef])) {
1676-
$excel->getSheetByName($charts[$chartEntryRef]['sheet'])->addChart($objChart);
1681+
$excel->getSheetByName($charts[$chartEntryRef]['sheet'])->addChart($objChart); // @phpstan-ignore-line
16771682
$objChart->setWorksheet($excel->getSheetByName($charts[$chartEntryRef]['sheet']));
16781683
// For oneCellAnchor or absoluteAnchor positioned charts,
16791684
// toCoordinate is not in the data. Does it need to be calculated?
@@ -1721,36 +1726,37 @@ private function parseRichText(?SimpleXMLElement $is)
17211726
if (isset($is->t)) {
17221727
$value->createText(StringHelper::controlCharacterOOXML2PHP((string) $is->t));
17231728
} else {
1724-
if (is_object($is->r)) {
1729+
if (isset($is->r) && is_object($is->r)) {
17251730
/** @var SimpleXMLElement $run */
17261731
foreach ($is->r as $run) {
17271732
if (!isset($run->rPr)) {
17281733
$value->createText(StringHelper::controlCharacterOOXML2PHP((string) $run->t));
17291734
} else {
17301735
$objText = $value->createTextRun(StringHelper::controlCharacterOOXML2PHP((string) $run->t));
1736+
$objFont = $objText->getFont() ?? new StyleFont();
17311737

17321738
if (isset($run->rPr->rFont)) {
17331739
$attr = $run->rPr->rFont->attributes();
17341740
if (isset($attr['val'])) {
1735-
$objText->getFont()->setName((string) $attr['val']);
1741+
$objFont->setName((string) $attr['val']);
17361742
}
17371743
}
17381744
if (isset($run->rPr->sz)) {
17391745
$attr = $run->rPr->sz->attributes();
17401746
if (isset($attr['val'])) {
1741-
$objText->getFont()->setSize((float) $attr['val']);
1747+
$objFont->setSize((float) $attr['val']);
17421748
}
17431749
}
17441750
if (isset($run->rPr->color)) {
1745-
$objText->getFont()->setColor(new Color($this->styleReader->readColor($run->rPr->color)));
1751+
$objFont->setColor(new Color($this->styleReader->readColor($run->rPr->color)));
17461752
}
17471753
if (isset($run->rPr->b)) {
17481754
$attr = $run->rPr->b->attributes();
17491755
if (
17501756
(isset($attr['val']) && self::boolean((string) $attr['val'])) ||
17511757
(!isset($attr['val']))
17521758
) {
1753-
$objText->getFont()->setBold(true);
1759+
$objFont->setBold(true);
17541760
}
17551761
}
17561762
if (isset($run->rPr->i)) {
@@ -1759,27 +1765,27 @@ private function parseRichText(?SimpleXMLElement $is)
17591765
(isset($attr['val']) && self::boolean((string) $attr['val'])) ||
17601766
(!isset($attr['val']))
17611767
) {
1762-
$objText->getFont()->setItalic(true);
1768+
$objFont->setItalic(true);
17631769
}
17641770
}
17651771
if (isset($run->rPr->vertAlign)) {
17661772
$attr = $run->rPr->vertAlign->attributes();
17671773
if (isset($attr['val'])) {
17681774
$vertAlign = strtolower((string) $attr['val']);
17691775
if ($vertAlign == 'superscript') {
1770-
$objText->getFont()->setSuperscript(true);
1776+
$objFont->setSuperscript(true);
17711777
}
17721778
if ($vertAlign == 'subscript') {
1773-
$objText->getFont()->setSubscript(true);
1779+
$objFont->setSubscript(true);
17741780
}
17751781
}
17761782
}
17771783
if (isset($run->rPr->u)) {
17781784
$attr = $run->rPr->u->attributes();
17791785
if (!isset($attr['val'])) {
1780-
$objText->getFont()->setUnderline(\PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_SINGLE);
1786+
$objFont->setUnderline(\PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_SINGLE);
17811787
} else {
1782-
$objText->getFont()->setUnderline((string) $attr['val']);
1788+
$objFont->setUnderline((string) $attr['val']);
17831789
}
17841790
}
17851791
if (isset($run->rPr->strike)) {
@@ -1788,7 +1794,7 @@ private function parseRichText(?SimpleXMLElement $is)
17881794
(isset($attr['val']) && self::boolean((string) $attr['val'])) ||
17891795
(!isset($attr['val']))
17901796
) {
1791-
$objText->getFont()->setStrikethrough(true);
1797+
$objFont->setStrikethrough(true);
17921798
}
17931799
}
17941800
}
@@ -1841,17 +1847,30 @@ private function readRibbon(Spreadsheet $excel, string $customUITarget, ZipArchi
18411847
}
18421848
}
18431849

1850+
/**
1851+
* @param null|array|bool|SimpleXMLElement $array
1852+
* @param int|string $key
1853+
*
1854+
* @return mixed
1855+
*/
18441856
private static function getArrayItem($array, $key = 0)
18451857
{
1846-
return $array[$key] ?? null;
1858+
return ($array === null || is_bool($array)) ? null : ($array[$key] ?? null);
18471859
}
18481860

1861+
/**
1862+
* @param null|SimpleXMLElement|string $base
1863+
* @param null|SimpleXMLElement|string $add
1864+
*/
18491865
private static function dirAdd($base, $add): string
18501866
{
1867+
$base = (string) $base;
1868+
$add = (string) $add;
1869+
18511870
return (string) preg_replace('~[^/]+/\.\./~', '', dirname($base) . "/$add");
18521871
}
18531872

1854-
private static function toCSSArray($style): array
1873+
private static function toCSSArray(string $style): array
18551874
{
18561875
$style = self::stripWhiteSpaceFromStyleString($style);
18571876

@@ -1865,15 +1884,15 @@ private static function toCSSArray($style): array
18651884
}
18661885
if (strpos($item[1], 'pt') !== false) {
18671886
$item[1] = str_replace('pt', '', $item[1]);
1868-
$item[1] = Font::fontSizeToPixels($item[1]);
1887+
$item[1] = (string) Font::fontSizeToPixels((int) $item[1]);
18691888
}
18701889
if (strpos($item[1], 'in') !== false) {
18711890
$item[1] = str_replace('in', '', $item[1]);
1872-
$item[1] = Font::inchSizeToPixels($item[1]);
1891+
$item[1] = (string) Font::inchSizeToPixels((int) $item[1]);
18731892
}
18741893
if (strpos($item[1], 'cm') !== false) {
18751894
$item[1] = str_replace('cm', '', $item[1]);
1876-
$item[1] = Font::centimeterSizeToPixels($item[1]);
1895+
$item[1] = (string) Font::centimeterSizeToPixels((int) $item[1]);
18771896
}
18781897

18791898
$style[$item[0]] = $item[1];
@@ -1882,11 +1901,14 @@ private static function toCSSArray($style): array
18821901
return $style;
18831902
}
18841903

1885-
public static function stripWhiteSpaceFromStyleString($string): string
1904+
public static function stripWhiteSpaceFromStyleString(string $string): string
18861905
{
18871906
return trim(str_replace(["\r", "\n", ' '], '', $string), ';');
18881907
}
18891908

1909+
/**
1910+
* @param mixed $value
1911+
*/
18901912
private static function boolean($value): bool
18911913
{
18921914
if (is_object($value)) {
@@ -1955,7 +1977,7 @@ private static function getLockValue(SimpleXmlElement $protection, string $key):
19551977
return $returnValue;
19561978
}
19571979

1958-
private function readFormControlProperties(Spreadsheet $excel, $dir, $fileWorksheet, $docSheet, array &$unparsedLoadedData): void
1980+
private function readFormControlProperties(Spreadsheet $excel, string $dir, string $fileWorksheet, Worksheet $docSheet, array &$unparsedLoadedData): void
19591981
{
19601982
$zip = $this->zip;
19611983
if (!$zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')) {
@@ -1982,7 +2004,7 @@ private function readFormControlProperties(Spreadsheet $excel, $dir, $fileWorksh
19822004
unset($unparsedCtrlProps);
19832005
}
19842006

1985-
private function readPrinterSettings(Spreadsheet $excel, $dir, $fileWorksheet, $docSheet, array &$unparsedLoadedData): void
2007+
private function readPrinterSettings(Spreadsheet $excel, string $dir, string $fileWorksheet, Worksheet $docSheet, array &$unparsedLoadedData): void
19862008
{
19872009
$zip = $this->zip;
19882010
if (!$zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')) {
@@ -2070,7 +2092,7 @@ private function readAutoFilterTables(
20702092
if ($xmlSheet && $xmlSheet->autoFilter) {
20712093
// In older files, autofilter structure is defined in the worksheet file
20722094
(new AutoFilter($docSheet, $xmlSheet))->load();
2073-
} elseif ($xmlSheet && $xmlSheet->tableParts && $xmlSheet->tableParts['count'] > 0) {
2095+
} elseif ($xmlSheet && $xmlSheet->tableParts && (int) $xmlSheet->tableParts['count'] > 0) {
20742096
// But for Office365, MS decided to make it all just a bit more complicated
20752097
$this->readAutoFilterTablesInTablesFile($xmlSheet, $dir, $fileWorksheet, $zip, $docSheet);
20762098
}

src/PhpSpreadsheet/Reader/Xlsx/AutoFilter.php

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99

1010
class AutoFilter
1111
{
12+
/** @var Worksheet */
1213
private $worksheet;
1314

15+
/** @var SimpleXMLElement */
1416
private $worksheetXml;
1517

1618
public function __construct(Worksheet $workSheet, SimpleXMLElement $worksheetXml)
@@ -28,7 +30,7 @@ public function load(): void
2830
}
2931
}
3032

31-
private function readAutoFilter($autoFilterRange, $xmlSheet): void
33+
private function readAutoFilter(string $autoFilterRange, SimpleXMLElement $xmlSheet): void
3234
{
3335
$autoFilter = $this->worksheet->getAutoFilter();
3436
$autoFilter->setRange($autoFilterRange);
@@ -39,15 +41,15 @@ private function readAutoFilter($autoFilterRange, $xmlSheet): void
3941
if ($filterColumn->filters) {
4042
$column->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER);
4143
$filters = $filterColumn->filters;
42-
if ((isset($filters['blank'])) && ($filters['blank'] == 1)) {
44+
if ((isset($filters['blank'])) && ((int) $filters['blank'] == 1)) {
4345
// Operator is undefined, but always treated as EQUAL
44-
$column->createRule()->setRule(null, '')->setRuleType(Rule::AUTOFILTER_RULETYPE_FILTER);
46+
$column->createRule()->setRule('', '')->setRuleType(Rule::AUTOFILTER_RULETYPE_FILTER);
4547
}
4648
// Standard filters are always an OR join, so no join rule needs to be set
4749
// Entries can be either filter elements
4850
foreach ($filters->filter as $filterRule) {
4951
// Operator is undefined, but always treated as EQUAL
50-
$column->createRule()->setRule(null, (string) $filterRule['val'])->setRuleType(Rule::AUTOFILTER_RULETYPE_FILTER);
52+
$column->createRule()->setRule('', (string) $filterRule['val'])->setRuleType(Rule::AUTOFILTER_RULETYPE_FILTER);
5153
}
5254

5355
// Or Date Group elements
@@ -69,7 +71,7 @@ private function readDateRangeAutoFilter(SimpleXMLElement $filters, Column $colu
6971
foreach ($filters->dateGroupItem as $dateGroupItem) {
7072
// Operator is undefined, but always treated as EQUAL
7173
$column->createRule()->setRule(
72-
null,
74+
'',
7375
[
7476
'year' => (string) $dateGroupItem['year'],
7577
'month' => (string) $dateGroupItem['month'],
@@ -110,7 +112,7 @@ private function readDynamicAutoFilter(SimpleXMLElement $filterColumn, Column $c
110112
foreach ($filterColumn->dynamicFilter as $filterRule) {
111113
// Operator is undefined, but always treated as EQUAL
112114
$column->createRule()->setRule(
113-
null,
115+
'',
114116
(string) $filterRule['val'],
115117
(string) $filterRule['type']
116118
)->setRuleType(Rule::AUTOFILTER_RULETYPE_DYNAMICFILTER);

src/PhpSpreadsheet/Reader/Xlsx/BaseParserClass.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44

55
class BaseParserClass
66
{
7-
protected static function boolean($value)
7+
/**
8+
* @param mixed $value
9+
*/
10+
protected static function boolean($value): bool
811
{
912
if (is_object($value)) {
1013
$value = (string) $value;

0 commit comments

Comments
 (0)