From 614f1c945c0708f6aceecc614ea3fa7852b1de3e Mon Sep 17 00:00:00 2001 From: Tiago Malheiro Date: Wed, 17 Mar 2021 14:13:18 +0000 Subject: [PATCH 01/57] Fix #1933. Swapped chart axis options --- src/PhpSpreadsheet/Writer/Xlsx/Chart.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Chart.php b/src/PhpSpreadsheet/Writer/Xlsx/Chart.php index 583b262c..7f0d4ab2 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Chart.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Chart.php @@ -316,10 +316,10 @@ class Chart extends WriterPart if ($chartType === DataSeries::TYPE_BUBBLECHART) { $this->writeValueAxis($objWriter, $xAxisLabel, $chartType, $id1, $id2, $catIsMultiLevelSeries, $xAxis, $majorGridlines, $minorGridlines); } else { - $this->writeCategoryAxis($objWriter, $xAxisLabel, $id1, $id2, $catIsMultiLevelSeries, $yAxis); + $this->writeCategoryAxis($objWriter, $xAxisLabel, $id1, $id2, $catIsMultiLevelSeries, $xAxis); } - $this->writeValueAxis($objWriter, $yAxisLabel, $chartType, $id1, $id2, $valIsMultiLevelSeries, $xAxis, $majorGridlines, $minorGridlines); + $this->writeValueAxis($objWriter, $yAxisLabel, $chartType, $id1, $id2, $valIsMultiLevelSeries, $yAxis, $majorGridlines, $minorGridlines); } $objWriter->endElement(); From 55a41e8cae65e9994b664385a2d6d21e719ddab2 Mon Sep 17 00:00:00 2001 From: Mark Baker Date: Sat, 24 Apr 2021 16:20:27 +0200 Subject: [PATCH 02/57] Eliminate a big chunck of duplicated code for reading styles (#2021) * Eliminate a big chunk of duplicated code for reading styles --- phpstan-baseline.neon | 25 ---- src/PhpSpreadsheet/Reader/Xlsx.php | 154 +++------------------- src/PhpSpreadsheet/Reader/Xlsx/Styles.php | 45 ++++--- 3 files changed, 43 insertions(+), 181 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 6390e0fc..4b12f171 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -3940,31 +3940,6 @@ parameters: count: 1 path: src/PhpSpreadsheet/Reader/Xlsx.php - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\:\\:readColor\\(\\) has no return typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Xlsx.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\:\\:readColor\\(\\) has parameter \\$background with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Xlsx.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\:\\:readColor\\(\\) has parameter \\$color with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Xlsx.php - - - - message: "#^Parameter \\#1 \\$hex of static method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Color\\:\\:changeBrightness\\(\\) expects string, string\\|null given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Xlsx.php - - - - message: "#^Parameter \\#1 \\$pValue of method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\:\\:setSize\\(\\) expects float, string given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Xlsx.php - - message: "#^Cannot access property \\$r on SimpleXMLElement\\|null\\.$#" count: 2 diff --git a/src/PhpSpreadsheet/Reader/Xlsx.php b/src/PhpSpreadsheet/Reader/Xlsx.php index a7c71c73..52a6c238 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx.php +++ b/src/PhpSpreadsheet/Reader/Xlsx.php @@ -27,12 +27,8 @@ use PhpOffice\PhpSpreadsheet\Shared\File; use PhpOffice\PhpSpreadsheet\Shared\Font; use PhpOffice\PhpSpreadsheet\Shared\StringHelper; use PhpOffice\PhpSpreadsheet\Spreadsheet; -use PhpOffice\PhpSpreadsheet\Style\Border; -use PhpOffice\PhpSpreadsheet\Style\Borders; use PhpOffice\PhpSpreadsheet\Style\Color; -use PhpOffice\PhpSpreadsheet\Style\Fill; use PhpOffice\PhpSpreadsheet\Style\NumberFormat; -use PhpOffice\PhpSpreadsheet\Style\Protection; use PhpOffice\PhpSpreadsheet\Style\Style; use PhpOffice\PhpSpreadsheet\Worksheet\HeaderFooterDrawing; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; @@ -1570,31 +1566,6 @@ class Xlsx extends BaseReader return $excel; } - private static function readColor($color, $background = false) - { - if (isset($color['rgb'])) { - return (string) $color['rgb']; - } elseif (isset($color['indexed'])) { - return Color::indexedColor($color['indexed'] - 7, $background)->getARGB(); - } elseif (isset($color['theme'])) { - if (self::$theme !== null) { - $returnColour = self::$theme->getColourByIndex((int) $color['theme']); - if (isset($color['tint'])) { - $tintAdjust = (float) $color['tint']; - $returnColour = Color::changeBrightness($returnColour, $tintAdjust); - } - - return 'FF' . $returnColour; - } - } - - if ($background) { - return 'FFFFFFFF'; - } - - return 'FF000000'; - } - /** * @param SimpleXMLElement|stdClass $style */ @@ -1604,116 +1575,28 @@ class Xlsx extends BaseReader // font if (isset($style->font)) { - $docStyle->getFont()->setName((string) $style->font->name['val']); - $docStyle->getFont()->setSize((string) $style->font->sz['val']); - if (isset($style->font->b)) { - $docStyle->getFont()->setBold(!isset($style->font->b['val']) || self::boolean((string) $style->font->b['val'])); - } - if (isset($style->font->i)) { - $docStyle->getFont()->setItalic(!isset($style->font->i['val']) || self::boolean((string) $style->font->i['val'])); - } - if (isset($style->font->strike)) { - $docStyle->getFont()->setStrikethrough(!isset($style->font->strike['val']) || self::boolean((string) $style->font->strike['val'])); - } - $docStyle->getFont()->getColor()->setARGB(self::readColor($style->font->color)); - - if (isset($style->font->u) && !isset($style->font->u['val'])) { - $docStyle->getFont()->setUnderline(\PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_SINGLE); - } elseif (isset($style->font->u, $style->font->u['val'])) { - $docStyle->getFont()->setUnderline((string) $style->font->u['val']); - } - - if (isset($style->font->vertAlign, $style->font->vertAlign['val'])) { - $vertAlign = strtolower((string) $style->font->vertAlign['val']); - if ($vertAlign == 'superscript') { - $docStyle->getFont()->setSuperscript(true); - } - if ($vertAlign == 'subscript') { - $docStyle->getFont()->setSubscript(true); - } - } + Styles::readFontStyle($docStyle->getFont(), $style->font); } // fill if (isset($style->fill)) { - if ($style->fill->gradientFill) { - /** @var SimpleXMLElement $gradientFill */ - $gradientFill = $style->fill->gradientFill[0]; - if (!empty($gradientFill['type'])) { - $docStyle->getFill()->setFillType((string) $gradientFill['type']); - } - $docStyle->getFill()->setRotation((float) ($gradientFill['degree'])); - $gradientFill->registerXPathNamespace('sml', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'); - $docStyle->getFill()->getStartColor()->setARGB(self::readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=0]'))->color)); - $docStyle->getFill()->getEndColor()->setARGB(self::readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=1]'))->color)); - } elseif ($style->fill->patternFill) { - $patternType = (string) $style->fill->patternFill['patternType'] != '' ? (string) $style->fill->patternFill['patternType'] : Fill::FILL_NONE; - $docStyle->getFill()->setFillType($patternType); - if ($style->fill->patternFill->fgColor) { - $docStyle->getFill()->getStartColor()->setARGB(self::readColor($style->fill->patternFill->fgColor, true)); - } - if ($style->fill->patternFill->bgColor) { - $docStyle->getFill()->getEndColor()->setARGB(self::readColor($style->fill->patternFill->bgColor, true)); - } - } + Styles::readFillStyle($docStyle->getFill(), $style->fill); } // border if (isset($style->border)) { - $diagonalUp = self::boolean((string) $style->border['diagonalUp']); - $diagonalDown = self::boolean((string) $style->border['diagonalDown']); - if (!$diagonalUp && !$diagonalDown) { - $docStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_NONE); - } elseif ($diagonalUp && !$diagonalDown) { - $docStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_UP); - } elseif (!$diagonalUp && $diagonalDown) { - $docStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_DOWN); - } else { - $docStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_BOTH); - } - self::readBorder($docStyle->getBorders()->getLeft(), $style->border->left); - self::readBorder($docStyle->getBorders()->getRight(), $style->border->right); - self::readBorder($docStyle->getBorders()->getTop(), $style->border->top); - self::readBorder($docStyle->getBorders()->getBottom(), $style->border->bottom); - self::readBorder($docStyle->getBorders()->getDiagonal(), $style->border->diagonal); + Styles::readBorderStyle($docStyle->getBorders(), $style->border); } // alignment if (isset($style->alignment)) { - $docStyle->getAlignment()->setHorizontal((string) $style->alignment['horizontal']); - $docStyle->getAlignment()->setVertical((string) $style->alignment['vertical']); - - $textRotation = 0; - if ((int) $style->alignment['textRotation'] <= 90) { - $textRotation = (int) $style->alignment['textRotation']; - } elseif ((int) $style->alignment['textRotation'] > 90) { - $textRotation = 90 - (int) $style->alignment['textRotation']; - } - - $docStyle->getAlignment()->setTextRotation((int) $textRotation); - $docStyle->getAlignment()->setWrapText(self::boolean((string) $style->alignment['wrapText'])); - $docStyle->getAlignment()->setShrinkToFit(self::boolean((string) $style->alignment['shrinkToFit'])); - $docStyle->getAlignment()->setIndent((int) ((string) $style->alignment['indent']) > 0 ? (int) ((string) $style->alignment['indent']) : 0); - $docStyle->getAlignment()->setReadOrder((int) ((string) $style->alignment['readingOrder']) > 0 ? (int) ((string) $style->alignment['readingOrder']) : 0); + Styles::readAlignmentStyle($docStyle->getAlignment(), $style->alignment); } // protection if (isset($style->protection)) { - if (isset($style->protection['locked'])) { - if (self::boolean((string) $style->protection['locked'])) { - $docStyle->getProtection()->setLocked(Protection::PROTECTION_PROTECTED); - } else { - $docStyle->getProtection()->setLocked(Protection::PROTECTION_UNPROTECTED); - } - } - - if (isset($style->protection['hidden'])) { - if (self::boolean((string) $style->protection['hidden'])) { - $docStyle->getProtection()->setHidden(Protection::PROTECTION_PROTECTED); - } else { - $docStyle->getProtection()->setHidden(Protection::PROTECTION_UNPROTECTED); - } - } + Styles::readProtectionLocked($docStyle, $style->protection); + Styles::readProtectionHidden($docStyle, $style->protection); } // top-level style settings @@ -1722,19 +1605,6 @@ class Xlsx extends BaseReader } } - /** - * @param SimpleXMLElement $eleBorder - */ - private static function readBorder(Border $docBorder, $eleBorder): void - { - if (isset($eleBorder['style'])) { - $docBorder->setBorderStyle((string) $eleBorder['style']); - } - if (isset($eleBorder->color)) { - $docBorder->getColor()->setARGB(self::readColor($eleBorder->color)); - } - } - /** * @param SimpleXMLElement | null $is * @@ -1763,7 +1633,7 @@ class Xlsx extends BaseReader $objText->getFont()->setSize((float) $run->rPr->sz['val']); } if (isset($run->rPr->color)) { - $objText->getFont()->setColor(new Color(self::readColor($run->rPr->color))); + $objText->getFont()->setColor(new Color(Styles::readColor($run->rPr->color))); } if ( (isset($run->rPr->b['val']) && self::boolean((string) $run->rPr->b['val'])) || @@ -1949,11 +1819,17 @@ class Xlsx extends BaseReader } if ($xmlWorkbook->workbookProtection['revisionsPassword']) { - $excel->getSecurity()->setRevisionsPassword((string) $xmlWorkbook->workbookProtection['revisionsPassword'], true); + $excel->getSecurity()->setRevisionsPassword( + (string) $xmlWorkbook->workbookProtection['revisionsPassword'], + true + ); } if ($xmlWorkbook->workbookProtection['workbookPassword']) { - $excel->getSecurity()->setWorkbookPassword((string) $xmlWorkbook->workbookProtection['workbookPassword'], true); + $excel->getSecurity()->setWorkbookPassword( + (string) $xmlWorkbook->workbookProtection['workbookPassword'], + true + ); } } diff --git a/src/PhpSpreadsheet/Reader/Xlsx/Styles.php b/src/PhpSpreadsheet/Reader/Xlsx/Styles.php index c43ccc97..2b0c7016 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx/Styles.php +++ b/src/PhpSpreadsheet/Reader/Xlsx/Styles.php @@ -40,7 +40,7 @@ class Styles extends BaseParserClass $this->cellStyles = $cellStyles; } - private static function readFontStyle(Font $fontStyle, SimpleXMLElement $fontStyleXml): void + public static function readFontStyle(Font $fontStyle, SimpleXMLElement $fontStyleXml): void { $fontStyle->setName((string) $fontStyleXml->name['val']); $fontStyle->setSize((float) $fontStyleXml->sz['val']); @@ -52,7 +52,9 @@ class Styles extends BaseParserClass $fontStyle->setItalic(!isset($fontStyleXml->i['val']) || self::boolean((string) $fontStyleXml->i['val'])); } if (isset($fontStyleXml->strike)) { - $fontStyle->setStrikethrough(!isset($fontStyleXml->strike['val']) || self::boolean((string) $fontStyleXml->strike['val'])); + $fontStyle->setStrikethrough( + !isset($fontStyleXml->strike['val']) || self::boolean((string) $fontStyleXml->strike['val']) + ); } $fontStyle->getColor()->setARGB(self::readColor($fontStyleXml->color)); @@ -84,7 +86,7 @@ class Styles extends BaseParserClass } } - private static function readFillStyle(Fill $fillStyle, SimpleXMLElement $fillStyleXml): void + public static function readFillStyle(Fill $fillStyle, SimpleXMLElement $fillStyleXml): void { if ($fillStyleXml->gradientFill) { /** @var SimpleXMLElement $gradientFill */ @@ -94,15 +96,20 @@ class Styles extends BaseParserClass } $fillStyle->setRotation((float) ($gradientFill['degree'])); $gradientFill->registerXPathNamespace('sml', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'); - $fillStyle->getStartColor()->setARGB(self::readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=0]'))->color)); - $fillStyle->getEndColor()->setARGB(self::readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=1]'))->color)); + $fillStyle->getStartColor()->setARGB( + self::readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=0]'))->color) + ); + $fillStyle->getEndColor()->setARGB( + self::readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=1]'))->color) + ); } elseif ($fillStyleXml->patternFill) { - $patternType = (string) $fillStyleXml->patternFill['patternType'] != '' ? (string) $fillStyleXml->patternFill['patternType'] : Fill::FILL_NONE; + $patternType = (string) $fillStyleXml->patternFill['patternType'] != '' + ? (string) $fillStyleXml->patternFill['patternType'] + : Fill::FILL_NONE; + $fillStyle->setFillType($patternType); if ($fillStyleXml->patternFill->fgColor) { $fillStyle->getStartColor()->setARGB(self::readColor($fillStyleXml->patternFill->fgColor, true)); - } else { - $fillStyle->getStartColor()->setARGB('FF000000'); } if ($fillStyleXml->patternFill->bgColor) { $fillStyle->getEndColor()->setARGB(self::readColor($fillStyleXml->patternFill->bgColor, true)); @@ -110,7 +117,7 @@ class Styles extends BaseParserClass } } - private static function readBorderStyle(Borders $borderStyle, SimpleXMLElement $borderStyleXml): void + public static function readBorderStyle(Borders $borderStyle, SimpleXMLElement $borderStyleXml): void { $diagonalUp = self::boolean((string) $borderStyleXml['diagonalUp']); $diagonalDown = self::boolean((string) $borderStyleXml['diagonalDown']); @@ -141,7 +148,7 @@ class Styles extends BaseParserClass } } - private static function readAlignmentStyle(Alignment $alignment, SimpleXMLElement $alignmentXml): void + public static function readAlignmentStyle(Alignment $alignment, SimpleXMLElement $alignmentXml): void { $alignment->setHorizontal((string) $alignmentXml['horizontal']); $alignment->setVertical((string) $alignmentXml['vertical']); @@ -156,8 +163,12 @@ class Styles extends BaseParserClass $alignment->setTextRotation((int) $textRotation); $alignment->setWrapText(self::boolean((string) $alignmentXml['wrapText'])); $alignment->setShrinkToFit(self::boolean((string) $alignmentXml['shrinkToFit'])); - $alignment->setIndent((int) ((string) $alignmentXml['indent']) > 0 ? (int) ((string) $alignmentXml['indent']) : 0); - $alignment->setReadOrder((int) ((string) $alignmentXml['readingOrder']) > 0 ? (int) ((string) $alignmentXml['readingOrder']) : 0); + $alignment->setIndent( + (int) ((string) $alignmentXml['indent']) > 0 ? (int) ((string) $alignmentXml['indent']) : 0 + ); + $alignment->setReadOrder( + (int) ((string) $alignmentXml['readingOrder']) > 0 ? (int) ((string) $alignmentXml['readingOrder']) : 0 + ); } private function readStyle(Style $docStyle, $style): void @@ -186,8 +197,8 @@ class Styles extends BaseParserClass // protection if (isset($style->protection)) { - $this->readProtectionLocked($docStyle, $style); - $this->readProtectionHidden($docStyle, $style); + self::readProtectionLocked($docStyle, $style); + self::readProtectionHidden($docStyle, $style); } // top-level style settings @@ -196,7 +207,7 @@ class Styles extends BaseParserClass } } - private function readProtectionLocked(Style $docStyle, $style): void + public static function readProtectionLocked(Style $docStyle, $style): void { if (isset($style->protection['locked'])) { if (self::boolean((string) $style->protection['locked'])) { @@ -207,7 +218,7 @@ class Styles extends BaseParserClass } } - private function readProtectionHidden(Style $docStyle, $style): void + public static function readProtectionHidden(Style $docStyle, $style): void { if (isset($style->protection['hidden'])) { if (self::boolean((string) $style->protection['hidden'])) { @@ -218,7 +229,7 @@ class Styles extends BaseParserClass } } - private static function readColor($color, $background = false) + public static function readColor($color, $background = false) { if (isset($color['rgb'])) { return (string) $color['rgb']; From 3b8ef420ace9382707bcf2db5ab53a2f203caa09 Mon Sep 17 00:00:00 2001 From: Mark Baker Date: Sat, 24 Apr 2021 16:49:06 +0200 Subject: [PATCH 03/57] Issue 1967 conditional formatting data bar problems (#2012) * Only store additional formatting objects for DataBars * - Ensure that Xlsx Reader/Writer can still read DataBar Conditional Formatting, while ignoring IconTypes that are triggering errors - Ensure that Xls Writer only writes CF Headers if there are non-DataBar CF Records that it needs to write - Ensure that Xlsx Reader only reads SheetView data to retrieve currently selected cells after it has read CF Records that can otherwise change the currently selected cell * Try to apply proper support for cell ranges in conditional formatting --- src/PhpSpreadsheet/Reader/Xlsx.php | 12 +++--- .../Reader/Xlsx/ConditionalStyles.php | 4 +- .../ConditionalFormattingRuleExtension.php | 13 +++++-- src/PhpSpreadsheet/Writer/Xls/Worksheet.php | 37 +++++++++++-------- 4 files changed, 41 insertions(+), 25 deletions(-) diff --git a/src/PhpSpreadsheet/Reader/Xlsx.php b/src/PhpSpreadsheet/Reader/Xlsx.php index 52a6c238..f07ac008 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx.php +++ b/src/PhpSpreadsheet/Reader/Xlsx.php @@ -630,6 +630,12 @@ class Xlsx extends BaseReader } if ($xmlSheet) { + // Setting Conditional Styles adjusts selected cells, so we need to execute this + // before reading the sheet view data to get the actual selected cells + if (!$this->readDataOnly && $xmlSheet->conditionalFormatting) { + (new ConditionalStyles($docSheet, $xmlSheet, $dxfs))->load(); + } + if (isset($xmlSheet->sheetViews, $xmlSheet->sheetViews->sheetView)) { $sheetViews = new SheetViews($xmlSheet->sheetViews->sheetView, $docSheet); $sheetViews->load(); @@ -763,10 +769,6 @@ class Xlsx extends BaseReader } } - if (!$this->readDataOnly && $xmlSheet && $xmlSheet->conditionalFormatting) { - (new ConditionalStyles($docSheet, $xmlSheet, $dxfs))->load(); - } - $aKeys = ['sheet', 'objects', 'scenarios', 'formatCells', 'formatColumns', 'formatRows', 'insertColumns', 'insertRows', 'insertHyperlinks', 'deleteColumns', 'deleteRows', 'selectLockedCells', 'sort', 'autoFilter', 'pivotTables', 'selectUnlockedCells']; if (!$this->readDataOnly && $xmlSheet && $xmlSheet->sheetProtection) { foreach ($aKeys as $key) { @@ -1601,7 +1603,7 @@ class Xlsx extends BaseReader // top-level style settings if (isset($style->quotePrefix)) { - $docStyle->setQuotePrefix($style->quotePrefix); + $docStyle->setQuotePrefix((bool) $style->quotePrefix); } } diff --git a/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php b/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php index 7f96956f..5f6cb4dc 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php +++ b/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php @@ -98,7 +98,9 @@ class ConditionalStyles } if (isset($cfRule->dataBar)) { - $objConditional->setDataBar($this->readDataBarOfConditionalRule($cfRule, $conditionalFormattingRuleExtensions)); + $objConditional->setDataBar( + $this->readDataBarOfConditionalRule($cfRule, $conditionalFormattingRuleExtensions) + ); } else { $objConditional->setStyle(clone $this->dxfs[(int) ($cfRule['dxfId'])]); } diff --git a/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormattingRuleExtension.php b/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormattingRuleExtension.php index 899bbe43..0797c2e8 100644 --- a/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormattingRuleExtension.php +++ b/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormattingRuleExtension.php @@ -2,6 +2,7 @@ namespace PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting; +use PhpOffice\PhpSpreadsheet\Style\Conditional; use SimpleXMLElement; class ConditionalFormattingRuleExtension @@ -62,19 +63,23 @@ class ConditionalFormattingRuleExtension $conditionalFormattingRuleExtensionXml = $extLst->ext; } } + if ($conditionalFormattingRuleExtensionXml) { $ns = $conditionalFormattingRuleExtensionXml->getNamespaces(true); $extFormattingsXml = $conditionalFormattingRuleExtensionXml->children($ns['x14']); foreach ($extFormattingsXml->children($ns['x14']) as $extFormattingXml) { $extCfRuleXml = $extFormattingXml->cfRule; + if (((string) $extCfRuleXml->attributes()->type) !== Conditional::CONDITION_DATABAR) { + continue; + } + $extFormattingRuleObj = new self((string) $extCfRuleXml->attributes()->id); $extFormattingRuleObj->setSqref((string) $extFormattingXml->children($ns['xm'])->sqref); $conditionalFormattingRuleExtensions[$extFormattingRuleObj->getId()] = $extFormattingRuleObj; $extDataBarObj = new ConditionalDataBarExtension(); $extFormattingRuleObj->setDataBarExt($extDataBarObj); - $dataBarXml = $extCfRuleXml->dataBar; self::parseExtDataBarAttributesFromXml($extDataBarObj, $dataBarXml); self::parseExtDataBarElementChildrenFromXml($extDataBarObj, $dataBarXml, $ns); @@ -85,8 +90,10 @@ class ConditionalFormattingRuleExtension return $conditionalFormattingRuleExtensions; } - private static function parseExtDataBarAttributesFromXml(ConditionalDataBarExtension $extDataBarObj, SimpleXMLElement $dataBarXml): void - { + private static function parseExtDataBarAttributesFromXml( + ConditionalDataBarExtension $extDataBarObj, + SimpleXMLElement $dataBarXml + ): void { $dataBarAttribute = $dataBarXml->attributes(); if ($dataBarAttribute->minLength) { $extDataBarObj->setMinLength((int) $dataBarAttribute->minLength); diff --git a/src/PhpSpreadsheet/Writer/Xls/Worksheet.php b/src/PhpSpreadsheet/Writer/Xls/Worksheet.php index 7051ab2d..94cb6168 100644 --- a/src/PhpSpreadsheet/Writer/Xls/Worksheet.php +++ b/src/PhpSpreadsheet/Writer/Xls/Worksheet.php @@ -539,16 +539,21 @@ class Worksheet extends BIFFwriter $arrConditionalStyles = $phpSheet->getConditionalStylesCollection(); if (!empty($arrConditionalStyles)) { $arrConditional = []; - // @TODO CFRule & CFHeader - // Write CFHEADER record - $this->writeCFHeader(); + + $cfHeaderWritten = false; // Write ConditionalFormattingTable records foreach ($arrConditionalStyles as $cellCoordinate => $conditionalStyles) { foreach ($conditionalStyles as $conditional) { + /** @var Conditional $conditional */ if ( - $conditional->getConditionType() == Conditional::CONDITION_EXPRESSION - || $conditional->getConditionType() == Conditional::CONDITION_CELLIS + $conditional->getConditionType() == Conditional::CONDITION_EXPRESSION || + $conditional->getConditionType() == Conditional::CONDITION_CELLIS ) { + // Write CFHEADER record (only if there are Conditional Styles that we are able to write) + if ($cfHeaderWritten === false) { + $this->writeCFHeader(); + $cfHeaderWritten = true; + } if (!isset($arrConditional[$conditional->getHashCode()])) { // This hash code has been handled $arrConditional[$conditional->getHashCode()] = true; @@ -4352,25 +4357,25 @@ class Worksheet extends BIFFwriter foreach ($this->phpSheet->getConditionalStylesCollection() as $cellCoordinate => $conditionalStyles) { foreach ($conditionalStyles as $conditional) { if ( - $conditional->getConditionType() == Conditional::CONDITION_EXPRESSION - || $conditional->getConditionType() == Conditional::CONDITION_CELLIS + $conditional->getConditionType() == Conditional::CONDITION_EXPRESSION || + $conditional->getConditionType() == Conditional::CONDITION_CELLIS ) { if (!in_array($conditional->getHashCode(), $arrConditional)) { $arrConditional[] = $conditional->getHashCode(); } // Cells - $arrCoord = Coordinate::indexesFromString($cellCoordinate); - if ($numColumnMin === null || ($numColumnMin > $arrCoord[0])) { - $numColumnMin = $arrCoord[0]; + $rangeCoordinates = Coordinate::rangeBoundaries($cellCoordinate); + if ($numColumnMin === null || ($numColumnMin > $rangeCoordinates[0][0])) { + $numColumnMin = $rangeCoordinates[0][0]; } - if ($numColumnMax === null || ($numColumnMax < $arrCoord[0])) { - $numColumnMax = $arrCoord[0]; + if ($numColumnMax === null || ($numColumnMax < $rangeCoordinates[1][0])) { + $numColumnMax = $rangeCoordinates[1][0]; } - if ($numRowMin === null || ($numRowMin > $arrCoord[1])) { - $numRowMin = $arrCoord[1]; + if ($numRowMin === null || ($numRowMin > $rangeCoordinates[0][1])) { + $numRowMin = (int) $rangeCoordinates[0][1]; } - if ($numRowMax === null || ($numRowMax < $arrCoord[1])) { - $numRowMax = $arrCoord[1]; + if ($numRowMax === null || ($numRowMax < $rangeCoordinates[1][1])) { + $numRowMax = (int) $rangeCoordinates[1][1]; } } } From 557601f5847dc0236544aa9d2d2252ec6ac07ec2 Mon Sep 17 00:00:00 2001 From: Mark Baker Date: Sat, 24 Apr 2021 17:10:09 +0200 Subject: [PATCH 04/57] Xls writer improve styles (#2014) * Extract 3 separate (duplicated) switch statements for mapping colours in conditional formatting rules into a single class * Replace switch statement with an array map for colour lookup * Eliminate duplicate colour map entries * Apply some stricter type checking * Extract Cell Data Validations * Extract Error Code mappings --- phpstan-baseline.neon | 45 - src/PhpSpreadsheet/Reader/Xls/Color/BIFF8.php | 112 +- src/PhpSpreadsheet/Worksheet/Worksheet.php | 4 +- .../Writer/Xls/CellDataValidation.php | 78 + src/PhpSpreadsheet/Writer/Xls/ErrorCode.php | 28 + .../Writer/Xls/Style/CellAlignment.php | 59 + .../Writer/Xls/Style/CellBorder.php | 39 + .../Writer/Xls/Style/CellFill.php | 46 + .../Writer/Xls/Style/ColorMap.php | 80 + src/PhpSpreadsheet/Writer/Xls/Worksheet.php | 1334 +---------------- src/PhpSpreadsheet/Writer/Xls/Xf.php | 170 +-- 11 files changed, 465 insertions(+), 1530 deletions(-) create mode 100644 src/PhpSpreadsheet/Writer/Xls/CellDataValidation.php create mode 100644 src/PhpSpreadsheet/Writer/Xls/ErrorCode.php create mode 100644 src/PhpSpreadsheet/Writer/Xls/Style/CellAlignment.php create mode 100644 src/PhpSpreadsheet/Writer/Xls/Style/CellBorder.php create mode 100644 src/PhpSpreadsheet/Writer/Xls/Style/CellFill.php create mode 100644 src/PhpSpreadsheet/Writer/Xls/Style/ColorMap.php diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 4b12f171..2228974e 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -4735,11 +4735,6 @@ parameters: count: 4 path: src/PhpSpreadsheet/ReferenceHelper.php - - - message: "#^Parameter \\#2 \\$pPassword of method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\:\\:protectCells\\(\\) expects string, array given\\.$#" - count: 1 - path: src/PhpSpreadsheet/ReferenceHelper.php - - message: "#^Parameter \\#1 \\$index of method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\RowDimension\\:\\:setRowIndex\\(\\) expects int, string given\\.$#" count: 1 @@ -7405,16 +7400,6 @@ parameters: count: 1 path: src/PhpSpreadsheet/Writer/Xls/Worksheet.php - - - message: "#^Parameter \\#2 \\$height of method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xls\\\\Worksheet\\:\\:writeRow\\(\\) expects int, float given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Writer/Xls/Worksheet.php - - - - message: "#^Parameter \\#4 \\$hidden of method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xls\\\\Worksheet\\:\\:writeRow\\(\\) expects bool, string given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Writer/Xls/Worksheet.php - - message: "#^Cannot call method getHashCode\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#" count: 1 @@ -7445,11 +7430,6 @@ parameters: count: 1 path: src/PhpSpreadsheet/Writer/Xls/Worksheet.php - - - message: "#^Parameter \\#1 \\$hexadecimal_number of function hexdec expects string, array given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Writer/Xls/Worksheet.php - - message: "#^Parameter \\#1 \\$coordinates of static method PhpOffice\\\\PhpSpreadsheet\\\\Cell\\\\Coordinate\\:\\:indexesFromString\\(\\) expects string, string\\|null given\\.$#" count: 1 @@ -7550,26 +7530,6 @@ parameters: count: 1 path: src/PhpSpreadsheet/Writer/Xls/Worksheet.php - - - message: "#^Parameter \\#1 \\$value of static method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\StringHelper\\:\\:UTF8toBIFF8UnicodeLong\\(\\) expects string, string\\|null given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Writer/Xls/Worksheet.php - - - - message: "#^Parameter \\#1 \\$fillType of static method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xls\\\\Xf\\:\\:mapFillType\\(\\) expects string, string\\|null given\\.$#" - count: 2 - path: src/PhpSpreadsheet/Writer/Xls/Xf.php - - - - message: "#^Parameter \\#1 \\$hAlign of method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xls\\\\Xf\\:\\:mapHAlign\\(\\) expects string, string\\|null given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Writer/Xls/Xf.php - - - - message: "#^Parameter \\#1 \\$vAlign of static method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xls\\\\Xf\\:\\:mapVAlign\\(\\) expects string, string\\|null given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Writer/Xls/Xf.php - - message: "#^Parameter \\#1 \\$textRotation of static method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xls\\\\Xf\\:\\:mapTextRotation\\(\\) expects int, int\\|null given\\.$#" count: 1 @@ -8080,11 +8040,6 @@ parameters: count: 2 path: src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php - - - message: "#^Parameter \\#2 \\$value of method XMLWriter\\:\\:writeAttribute\\(\\) expects string, array given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php - - message: "#^Parameter \\#2 \\$value of method XMLWriter\\:\\:writeAttribute\\(\\) expects string, array\\\\|string given\\.$#" count: 2 diff --git a/src/PhpSpreadsheet/Reader/Xls/Color/BIFF8.php b/src/PhpSpreadsheet/Reader/Xls/Color/BIFF8.php index 5c109fb0..5d8b5ab8 100644 --- a/src/PhpSpreadsheet/Reader/Xls/Color/BIFF8.php +++ b/src/PhpSpreadsheet/Reader/Xls/Color/BIFF8.php @@ -5,62 +5,62 @@ namespace PhpOffice\PhpSpreadsheet\Reader\Xls\Color; class BIFF8 { protected static $map = [ - 0x08 => '000000', - 0x09 => 'FFFFFF', - 0x0A => 'FF0000', - 0x0B => '00FF00', - 0x0C => '0000FF', - 0x0D => 'FFFF00', - 0x0E => 'FF00FF', - 0x0F => '00FFFF', - 0x10 => '800000', - 0x11 => '008000', - 0x12 => '000080', - 0x13 => '808000', - 0x14 => '800080', - 0x15 => '008080', - 0x16 => 'C0C0C0', - 0x17 => '808080', - 0x18 => '9999FF', - 0x19 => '993366', - 0x1A => 'FFFFCC', - 0x1B => 'CCFFFF', - 0x1C => '660066', - 0x1D => 'FF8080', - 0x1E => '0066CC', - 0x1F => 'CCCCFF', - 0x20 => '000080', - 0x21 => 'FF00FF', - 0x22 => 'FFFF00', - 0x23 => '00FFFF', - 0x24 => '800080', - 0x25 => '800000', - 0x26 => '008080', - 0x27 => '0000FF', - 0x28 => '00CCFF', - 0x29 => 'CCFFFF', - 0x2A => 'CCFFCC', - 0x2B => 'FFFF99', - 0x2C => '99CCFF', - 0x2D => 'FF99CC', - 0x2E => 'CC99FF', - 0x2F => 'FFCC99', - 0x30 => '3366FF', - 0x31 => '33CCCC', - 0x32 => '99CC00', - 0x33 => 'FFCC00', - 0x34 => 'FF9900', - 0x35 => 'FF6600', - 0x36 => '666699', - 0x37 => '969696', - 0x38 => '003366', - 0x39 => '339966', - 0x3A => '003300', - 0x3B => '333300', - 0x3C => '993300', - 0x3D => '993366', - 0x3E => '333399', - 0x3F => '333333', + '000000' => 0x08, + 'FFFFFF' => 0x09, + 'FF0000' => 0x0A, + '00FF00' => 0x0B, + '0000FF' => 0x0C, + 'FFFF00' => 0x0D, + 'FF00FF' => 0x0E, + '00FFFF' => 0x0F, + '800000' => 0x10, + '008000' => 0x11, + '000080' => 0x12, + '808000' => 0x13, + '800080' => 0x14, + '008080' => 0x15, + 'C0C0C0' => 0x16, + '808080' => 0x17, + '9999FF' => 0x18, + '993366' => 0x19, + 'FFFFCC' => 0x1A, + 'CCFFFF' => 0x1B, + '660066' => 0x1C, + 'FF8080' => 0x1D, + '0066CC' => 0x1E, + 'CCCCFF' => 0x1F, + // '000080' => 0x20, + // 'FF00FF' => 0x21, + // 'FFFF00' => 0x22, + // '00FFFF' => 0x23, + // '800080' => 0x24, + // '800000' => 0x25, + // '008080' => 0x26, + // '0000FF' => 0x27, + '00CCFF' => 0x28, + // 'CCFFFF' => 0x29, + 'CCFFCC' => 0x2A, + 'FFFF99' => 0x2B, + '99CCFF' => 0x2C, + 'FF99CC' => 0x2D, + 'CC99FF' => 0x2E, + 'FFCC99' => 0x2F, + '3366FF' => 0x30, + '33CCCC' => 0x31, + '99CC00' => 0x32, + 'FFCC00' => 0x33, + 'FF9900' => 0x34, + 'FF6600' => 0x35, + '666699' => 0x36, + '969696' => 0x37, + '003366' => 0x38, + '339966' => 0x39, + '003300' => 0x3A, + '333300' => 0x3B, + '993300' => 0x3C, + // '993366' => 0x3D, + '333399' => 0x3E, + '333333' => 0x3F, ]; /** diff --git a/src/PhpSpreadsheet/Worksheet/Worksheet.php b/src/PhpSpreadsheet/Worksheet/Worksheet.php index 2ecd210c..9e1f0a19 100644 --- a/src/PhpSpreadsheet/Worksheet/Worksheet.php +++ b/src/PhpSpreadsheet/Worksheet/Worksheet.php @@ -194,7 +194,7 @@ class Worksheet implements IComparable /** * Collection of protected cell ranges. * - * @var array + * @var string[] */ private $protectedCells = []; @@ -1871,7 +1871,7 @@ class Worksheet implements IComparable /** * Get protected cells. * - * @return array[] + * @return string[] */ public function getProtectedCells() { diff --git a/src/PhpSpreadsheet/Writer/Xls/CellDataValidation.php b/src/PhpSpreadsheet/Writer/Xls/CellDataValidation.php new file mode 100644 index 00000000..7e9b3cfa --- /dev/null +++ b/src/PhpSpreadsheet/Writer/Xls/CellDataValidation.php @@ -0,0 +1,78 @@ + + */ + protected static $validationTypeMap = [ + DataValidation::TYPE_NONE => 0x00, + DataValidation::TYPE_WHOLE => 0x01, + DataValidation::TYPE_DECIMAL => 0x02, + DataValidation::TYPE_LIST => 0x03, + DataValidation::TYPE_DATE => 0x04, + DataValidation::TYPE_TIME => 0x05, + DataValidation::TYPE_TEXTLENGTH => 0x06, + DataValidation::TYPE_CUSTOM => 0x07, + ]; + + /** + * @var array + */ + protected static $errorStyleMap = [ + DataValidation::STYLE_STOP => 0x00, + DataValidation::STYLE_WARNING => 0x01, + DataValidation::STYLE_INFORMATION => 0x02, + ]; + + /** + * @var array + */ + protected static $operatorMap = [ + DataValidation::OPERATOR_BETWEEN => 0x00, + DataValidation::OPERATOR_NOTBETWEEN => 0x01, + DataValidation::OPERATOR_EQUAL => 0x02, + DataValidation::OPERATOR_NOTEQUAL => 0x03, + DataValidation::OPERATOR_GREATERTHAN => 0x04, + DataValidation::OPERATOR_LESSTHAN => 0x05, + DataValidation::OPERATOR_GREATERTHANOREQUAL => 0x06, + DataValidation::OPERATOR_LESSTHANOREQUAL => 0x07, + ]; + + public static function type(DataValidation $dataValidation): int + { + $validationType = $dataValidation->getType(); + + if (is_string($validationType) && array_key_exists($validationType, self::$validationTypeMap)) { + return self::$validationTypeMap[$validationType]; + } + + return self::$validationTypeMap[DataValidation::TYPE_NONE]; + } + + public static function errorStyle(DataValidation $dataValidation): int + { + $errorStyle = $dataValidation->getErrorStyle(); + + if (is_string($errorStyle) && array_key_exists($errorStyle, self::$errorStyleMap)) { + return self::$errorStyleMap[$errorStyle]; + } + + return self::$errorStyleMap[DataValidation::STYLE_STOP]; + } + + public static function operator(DataValidation $dataValidation): int + { + $operator = $dataValidation->getOperator(); + + if (is_string($operator) && array_key_exists($operator, self::$operatorMap)) { + return self::$operatorMap[$operator]; + } + + return self::$operatorMap[DataValidation::OPERATOR_BETWEEN]; + } +} diff --git a/src/PhpSpreadsheet/Writer/Xls/ErrorCode.php b/src/PhpSpreadsheet/Writer/Xls/ErrorCode.php new file mode 100644 index 00000000..7a864f50 --- /dev/null +++ b/src/PhpSpreadsheet/Writer/Xls/ErrorCode.php @@ -0,0 +1,28 @@ + + */ + protected static $errorCodeMap = [ + '#NULL!' => 0x00, + '#DIV/0!' => 0x07, + '#VALUE!' => 0x0F, + '#REF!' => 0x17, + '#NAME?' => 0x1D, + '#NUM!' => 0x24, + '#N/A' => 0x2A, + ]; + + public static function error(string $errorCode): int + { + if (array_key_exists($errorCode, self::$errorCodeMap)) { + return self::$errorCodeMap[$errorCode]; + } + + return 0; + } +} diff --git a/src/PhpSpreadsheet/Writer/Xls/Style/CellAlignment.php b/src/PhpSpreadsheet/Writer/Xls/Style/CellAlignment.php new file mode 100644 index 00000000..711d88d2 --- /dev/null +++ b/src/PhpSpreadsheet/Writer/Xls/Style/CellAlignment.php @@ -0,0 +1,59 @@ + + */ + private static $horizontalMap = [ + Alignment::HORIZONTAL_GENERAL => 0, + Alignment::HORIZONTAL_LEFT => 1, + Alignment::HORIZONTAL_RIGHT => 3, + Alignment::HORIZONTAL_CENTER => 2, + Alignment::HORIZONTAL_CENTER_CONTINUOUS => 6, + Alignment::HORIZONTAL_JUSTIFY => 5, + ]; + + /** + * @var array + */ + private static $verticalMap = [ + Alignment::VERTICAL_BOTTOM => 2, + Alignment::VERTICAL_TOP => 0, + Alignment::VERTICAL_CENTER => 1, + Alignment::VERTICAL_JUSTIFY => 3, + ]; + + public static function horizontal(Alignment $alignment): int + { + $horizontalAlignment = $alignment->getHorizontal(); + + if (is_string($horizontalAlignment) && array_key_exists($horizontalAlignment, self::$horizontalMap)) { + return self::$horizontalMap[$horizontalAlignment]; + } + + return self::$horizontalMap[Alignment::HORIZONTAL_GENERAL]; + } + + public static function wrap(Alignment $alignment): int + { + $wrap = $alignment->getWrapText(); + + return ($wrap === true) ? 1 : 0; + } + + public static function vertical(Alignment $alignment): int + { + $verticalAlignment = $alignment->getVertical(); + + if (is_string($verticalAlignment) && array_key_exists($verticalAlignment, self::$verticalMap)) { + return self::$verticalMap[$verticalAlignment]; + } + + return self::$verticalMap[Alignment::VERTICAL_BOTTOM]; + } +} diff --git a/src/PhpSpreadsheet/Writer/Xls/Style/CellBorder.php b/src/PhpSpreadsheet/Writer/Xls/Style/CellBorder.php new file mode 100644 index 00000000..8d47d6aa --- /dev/null +++ b/src/PhpSpreadsheet/Writer/Xls/Style/CellBorder.php @@ -0,0 +1,39 @@ + + */ + protected static $styleMap = [ + Border::BORDER_NONE => 0x00, + Border::BORDER_THIN => 0x01, + Border::BORDER_MEDIUM => 0x02, + Border::BORDER_DASHED => 0x03, + Border::BORDER_DOTTED => 0x04, + Border::BORDER_THICK => 0x05, + Border::BORDER_DOUBLE => 0x06, + Border::BORDER_HAIR => 0x07, + Border::BORDER_MEDIUMDASHED => 0x08, + Border::BORDER_DASHDOT => 0x09, + Border::BORDER_MEDIUMDASHDOT => 0x0A, + Border::BORDER_DASHDOTDOT => 0x0B, + Border::BORDER_MEDIUMDASHDOTDOT => 0x0C, + Border::BORDER_SLANTDASHDOT => 0x0D, + ]; + + public static function style(Border $border): int + { + $borderStyle = $border->getBorderStyle(); + + if (is_string($borderStyle) && array_key_exists($borderStyle, self::$styleMap)) { + return self::$styleMap[$borderStyle]; + } + + return self::$styleMap[Border::BORDER_NONE]; + } +} diff --git a/src/PhpSpreadsheet/Writer/Xls/Style/CellFill.php b/src/PhpSpreadsheet/Writer/Xls/Style/CellFill.php new file mode 100644 index 00000000..f5a8c470 --- /dev/null +++ b/src/PhpSpreadsheet/Writer/Xls/Style/CellFill.php @@ -0,0 +1,46 @@ + + */ + protected static $fillStyleMap = [ + Fill::FILL_NONE => 0x00, + Fill::FILL_SOLID => 0x01, + Fill::FILL_PATTERN_MEDIUMGRAY => 0x02, + Fill::FILL_PATTERN_DARKGRAY => 0x03, + Fill::FILL_PATTERN_LIGHTGRAY => 0x04, + Fill::FILL_PATTERN_DARKHORIZONTAL => 0x05, + Fill::FILL_PATTERN_DARKVERTICAL => 0x06, + Fill::FILL_PATTERN_DARKDOWN => 0x07, + Fill::FILL_PATTERN_DARKUP => 0x08, + Fill::FILL_PATTERN_DARKGRID => 0x09, + Fill::FILL_PATTERN_DARKTRELLIS => 0x0A, + Fill::FILL_PATTERN_LIGHTHORIZONTAL => 0x0B, + Fill::FILL_PATTERN_LIGHTVERTICAL => 0x0C, + Fill::FILL_PATTERN_LIGHTDOWN => 0x0D, + Fill::FILL_PATTERN_LIGHTUP => 0x0E, + Fill::FILL_PATTERN_LIGHTGRID => 0x0F, + Fill::FILL_PATTERN_LIGHTTRELLIS => 0x10, + Fill::FILL_PATTERN_GRAY125 => 0x11, + Fill::FILL_PATTERN_GRAY0625 => 0x12, + Fill::FILL_GRADIENT_LINEAR => 0x00, // does not exist in BIFF8 + Fill::FILL_GRADIENT_PATH => 0x00, // does not exist in BIFF8 + ]; + + public static function style(Fill $fill): int + { + $fillStyle = $fill->getFillType(); + + if (is_string($fillStyle) && array_key_exists($fillStyle, self::$fillStyleMap)) { + return self::$fillStyleMap[$fillStyle]; + } + + return self::$fillStyleMap[Fill::FILL_NONE]; + } +} diff --git a/src/PhpSpreadsheet/Writer/Xls/Style/ColorMap.php b/src/PhpSpreadsheet/Writer/Xls/Style/ColorMap.php new file mode 100644 index 00000000..e3a6b206 --- /dev/null +++ b/src/PhpSpreadsheet/Writer/Xls/Style/ColorMap.php @@ -0,0 +1,80 @@ + + */ + private static $colorMap = [ + '#000000' => 0x08, + '#FFFFFF' => 0x09, + '#FF0000' => 0x0A, + '#00FF00' => 0x0B, + '#0000FF' => 0x0C, + '#FFFF00' => 0x0D, + '#FF00FF' => 0x0E, + '#00FFFF' => 0x0F, + '#800000' => 0x10, + '#008000' => 0x11, + '#000080' => 0x12, + '#808000' => 0x13, + '#800080' => 0x14, + '#008080' => 0x15, + '#C0C0C0' => 0x16, + '#808080' => 0x17, + '#9999FF' => 0x18, + '#993366' => 0x19, + '#FFFFCC' => 0x1A, + '#CCFFFF' => 0x1B, + '#660066' => 0x1C, + '#FF8080' => 0x1D, + '#0066CC' => 0x1E, + '#CCCCFF' => 0x1F, + // '#000080' => 0x20, + // '#FF00FF' => 0x21, + // '#FFFF00' => 0x22, + // '#00FFFF' => 0x23, + // '#800080' => 0x24, + // '#800000' => 0x25, + // '#008080' => 0x26, + // '#0000FF' => 0x27, + '#00CCFF' => 0x28, + // '#CCFFFF' => 0x29, + '#CCFFCC' => 0x2A, + '#FFFF99' => 0x2B, + '#99CCFF' => 0x2C, + '#FF99CC' => 0x2D, + '#CC99FF' => 0x2E, + '#FFCC99' => 0x2F, + '#3366FF' => 0x30, + '#33CCCC' => 0x31, + '#99CC00' => 0x32, + '#FFCC00' => 0x33, + '#FF9900' => 0x34, + '#FF6600' => 0x35, + '#666699' => 0x36, + '#969696' => 0x37, + '#003366' => 0x38, + '#339966' => 0x39, + '#003300' => 0x3A, + '#333300' => 0x3B, + '#993300' => 0x3C, + // '#993366' => 0x3D, + '#333399' => 0x3E, + '#333333' => 0x3F, + ]; + + public static function lookup(Color $color, int $defaultIndex = 0x00): int + { + $colorRgb = $color->getRGB(); + if (is_string($colorRgb) && array_key_exists("#{$colorRgb}", self::$colorMap)) { + return self::$colorMap["#{$colorRgb}"]; + } + + return $defaultIndex; + } +} diff --git a/src/PhpSpreadsheet/Writer/Xls/Worksheet.php b/src/PhpSpreadsheet/Writer/Xls/Worksheet.php index 94cb6168..84844d3d 100644 --- a/src/PhpSpreadsheet/Writer/Xls/Worksheet.php +++ b/src/PhpSpreadsheet/Writer/Xls/Worksheet.php @@ -5,17 +5,14 @@ namespace PhpOffice\PhpSpreadsheet\Writer\Xls; use GdImage; use PhpOffice\PhpSpreadsheet\Cell\Coordinate; use PhpOffice\PhpSpreadsheet\Cell\DataType; -use PhpOffice\PhpSpreadsheet\Cell\DataValidation; use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException; use PhpOffice\PhpSpreadsheet\RichText\RichText; use PhpOffice\PhpSpreadsheet\RichText\Run; use PhpOffice\PhpSpreadsheet\Shared\StringHelper; use PhpOffice\PhpSpreadsheet\Shared\Xls; -use PhpOffice\PhpSpreadsheet\Style\Alignment; use PhpOffice\PhpSpreadsheet\Style\Border; use PhpOffice\PhpSpreadsheet\Style\Color; use PhpOffice\PhpSpreadsheet\Style\Conditional; -use PhpOffice\PhpSpreadsheet\Style\Fill; use PhpOffice\PhpSpreadsheet\Style\Protection; use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup; use PhpOffice\PhpSpreadsheet\Worksheet\SheetView; @@ -259,12 +256,12 @@ class Worksheet extends BIFFwriter $maxC = $this->phpSheet->getHighestColumn(); // Determine lowest and highest column and row + $this->firstRowIndex = $minR; $this->lastRowIndex = ($maxR > 65535) ? 65535 : $maxR; $this->firstColumnIndex = Coordinate::columnIndexFromString($minC); $this->lastColumnIndex = Coordinate::columnIndexFromString($maxC); -// if ($this->firstColumnIndex > 255) $this->firstColumnIndex = 255; if ($this->lastColumnIndex > 255) { $this->lastColumnIndex = 255; } @@ -393,7 +390,7 @@ class Worksheet extends BIFFwriter // Row dimensions foreach ($phpSheet->getRowDimensions() as $rowDimension) { $xfIndex = $rowDimension->getXfIndex() + 15; // there are 15 cellXfs - $this->writeRow($rowDimension->getRowIndex() - 1, $rowDimension->getRowHeight(), $xfIndex, ($rowDimension->getVisible() ? '0' : '1'), $rowDimension->getOutlineLevel()); + $this->writeRow($rowDimension->getRowIndex() - 1, (int) $rowDimension->getRowHeight(), $xfIndex, $rowDimension->getVisible(), $rowDimension->getOutlineLevel()); } // Write Cells @@ -413,7 +410,6 @@ class Worksheet extends BIFFwriter $cVal = $cell->getValue(); if ($cVal instanceof RichText) { $arrcRun = []; - $str_len = StringHelper::countCharacters($cVal->getPlainText(), 'UTF-8'); $str_pos = 0; $elements = $cVal->getRichTextElements(); foreach ($elements as $element) { @@ -476,7 +472,7 @@ class Worksheet extends BIFFwriter break; case DataType::TYPE_ERROR: - $this->writeBoolErr($row, $column, self::mapErrorCode($cVal), 1, $xfIndex); + $this->writeBoolErr($row, $column, ErrorCode::error($cVal), 1, $xfIndex); break; } @@ -841,7 +837,7 @@ class Worksheet extends BIFFwriter $errorCodes = DataType::getErrorCodes(); if (isset($errorCodes[$calculatedValue])) { // Error value - $num = pack('CCCvCv', 0x02, 0x00, self::mapErrorCode($calculatedValue), 0x00, 0x00, 0xFFFF); + $num = pack('CCCvCv', 0x02, 0x00, ErrorCode::error($calculatedValue), 0x00, 0x00, 0xFFFF); } elseif ($calculatedValue === '') { // Empty string (and BIFF8) $num = pack('CCCvCv', 0x03, 0x00, 0x00, 0x00, 0x00, 0xFFFF); @@ -873,7 +869,7 @@ class Worksheet extends BIFFwriter // Parse the formula using the parser in Parser.php try { - $error = $this->parser->parse($formula); + $this->parser->parse($formula); $formula = $this->parser->toReversePolish(); $formlen = strlen($formula); // Length of the binary string @@ -1172,7 +1168,7 @@ class Worksheet extends BIFFwriter } // Use writeRow($row, null, $XF) to set XF format without setting height - if ($height != null) { + if ($height !== null) { $miyRw = $height * 20; // row height } else { $miyRw = 0xff; // default row height is 256 @@ -1524,7 +1520,7 @@ class Worksheet extends BIFFwriter /** * Write BIFF record RANGEPROTECTION. * - * Openoffice.org's Documentaion of the Microsoft Excel File Format uses term RANGEPROTECTION for these records + * Openoffice.org's Documentation of the Microsoft Excel File Format uses term RANGEPROTECTION for these records * Microsoft Office Excel 97-2007 Binary File Format Specification uses term FEAT for these records */ private function writeRangeProtection(): void @@ -2633,67 +2629,16 @@ class Worksheet extends BIFFwriter $record = 0x01BE; // Record identifier foreach ($dataValidationCollection as $cellCoordinate => $dataValidation) { - // initialize record data - $data = ''; - // options $options = 0x00000000; // data type - $type = 0x00; - switch ($dataValidation->getType()) { - case DataValidation::TYPE_NONE: - $type = 0x00; - - break; - case DataValidation::TYPE_WHOLE: - $type = 0x01; - - break; - case DataValidation::TYPE_DECIMAL: - $type = 0x02; - - break; - case DataValidation::TYPE_LIST: - $type = 0x03; - - break; - case DataValidation::TYPE_DATE: - $type = 0x04; - - break; - case DataValidation::TYPE_TIME: - $type = 0x05; - - break; - case DataValidation::TYPE_TEXTLENGTH: - $type = 0x06; - - break; - case DataValidation::TYPE_CUSTOM: - $type = 0x07; - - break; - } + $type = CellDataValidation::type($dataValidation); $options |= $type << 0; // error style - $errorStyle = 0x00; - switch ($dataValidation->getErrorStyle()) { - case DataValidation::STYLE_STOP: - $errorStyle = 0x00; - - break; - case DataValidation::STYLE_WARNING: - $errorStyle = 0x01; - - break; - case DataValidation::STYLE_INFORMATION: - $errorStyle = 0x02; - - break; - } + $errorStyle = CellDataValidation::errorStyle($dataValidation); $options |= $errorStyle << 4; @@ -2715,41 +2660,7 @@ class Worksheet extends BIFFwriter $options |= $dataValidation->getShowErrorMessage() << 19; // condition operator - $operator = 0x00; - switch ($dataValidation->getOperator()) { - case DataValidation::OPERATOR_BETWEEN: - $operator = 0x00; - - break; - case DataValidation::OPERATOR_NOTBETWEEN: - $operator = 0x01; - - break; - case DataValidation::OPERATOR_EQUAL: - $operator = 0x02; - - break; - case DataValidation::OPERATOR_NOTEQUAL: - $operator = 0x03; - - break; - case DataValidation::OPERATOR_GREATERTHAN: - $operator = 0x04; - - break; - case DataValidation::OPERATOR_LESSTHAN: - $operator = 0x05; - - break; - case DataValidation::OPERATOR_GREATERTHANOREQUAL: - $operator = 0x06; - - break; - case DataValidation::OPERATOR_LESSTHANOREQUAL: - $operator = 0x07; - - break; - } + $operator = CellDataValidation::operator($dataValidation); $options |= $operator << 20; @@ -2819,35 +2730,6 @@ class Worksheet extends BIFFwriter } } - /** - * Map Error code. - * - * @param string $errorCode - * - * @return int - */ - private static function mapErrorCode($errorCode) - { - switch ($errorCode) { - case '#NULL!': - return 0x00; - case '#DIV/0!': - return 0x07; - case '#VALUE!': - return 0x0F; - case '#REF!': - return 0x17; - case '#NAME?': - return 0x1D; - case '#NUM!': - return 0x24; - case '#N/A': - return 0x2A; - } - - return 0; - } - /** * Write PLV Record. */ @@ -2954,12 +2836,12 @@ class Worksheet extends BIFFwriter // $flags : Option flags // Alignment - $bAlignHz = ($conditional->getStyle()->getAlignment()->getHorizontal() == null ? 1 : 0); - $bAlignVt = ($conditional->getStyle()->getAlignment()->getVertical() == null ? 1 : 0); - $bAlignWrapTx = ($conditional->getStyle()->getAlignment()->getWrapText() == false ? 1 : 0); - $bTxRotation = ($conditional->getStyle()->getAlignment()->getTextRotation() == null ? 1 : 0); - $bIndent = ($conditional->getStyle()->getAlignment()->getIndent() == 0 ? 1 : 0); - $bShrinkToFit = ($conditional->getStyle()->getAlignment()->getShrinkToFit() == false ? 1 : 0); + $bAlignHz = ($conditional->getStyle()->getAlignment()->getHorizontal() === null ? 1 : 0); + $bAlignVt = ($conditional->getStyle()->getAlignment()->getVertical() === null ? 1 : 0); + $bAlignWrapTx = ($conditional->getStyle()->getAlignment()->getWrapText() === false ? 1 : 0); + $bTxRotation = ($conditional->getStyle()->getAlignment()->getTextRotation() === null ? 1 : 0); + $bIndent = ($conditional->getStyle()->getAlignment()->getIndent() === 0 ? 1 : 0); + $bShrinkToFit = ($conditional->getStyle()->getAlignment()->getShrinkToFit() === false ? 1 : 0); if ($bAlignHz == 0 || $bAlignVt == 0 || $bAlignWrapTx == 0 || $bTxRotation == 0 || $bIndent == 0 || $bShrinkToFit == 0) { $bFormatAlign = 1; } else { @@ -2988,7 +2870,7 @@ class Worksheet extends BIFFwriter $bFormatBorder = 0; } // Pattern - $bFillStyle = ($conditional->getStyle()->getFill()->getFillType() == null ? 0 : 1); + $bFillStyle = ($conditional->getStyle()->getFill()->getFillType() === null ? 0 : 1); $bFillColor = ($conditional->getStyle()->getFill()->getStartColor()->getARGB() == null ? 0 : 1); $bFillColorBg = ($conditional->getStyle()->getFill()->getEndColor()->getARGB() == null ? 0 : 1); if ($bFillStyle == 0 || $bFillColor == 0 || $bFillColorBg == 0) { @@ -2998,14 +2880,14 @@ class Worksheet extends BIFFwriter } // Font if ( - $conditional->getStyle()->getFont()->getName() != null - || $conditional->getStyle()->getFont()->getSize() != null - || $conditional->getStyle()->getFont()->getBold() != null - || $conditional->getStyle()->getFont()->getItalic() != null - || $conditional->getStyle()->getFont()->getSuperscript() != null - || $conditional->getStyle()->getFont()->getSubscript() != null - || $conditional->getStyle()->getFont()->getUnderline() != null - || $conditional->getStyle()->getFont()->getStrikethrough() != null + $conditional->getStyle()->getFont()->getName() !== null + || $conditional->getStyle()->getFont()->getSize() !== null + || $conditional->getStyle()->getFont()->getBold() !== null + || $conditional->getStyle()->getFont()->getItalic() !== null + || $conditional->getStyle()->getFont()->getSuperscript() !== null + || $conditional->getStyle()->getFont()->getSubscript() !== null + || $conditional->getStyle()->getFont()->getUnderline() !== null + || $conditional->getStyle()->getFont()->getStrikethrough() !== null || $conditional->getStyle()->getFont()->getColor()->getARGB() != null ) { $bFormatFont = 1; @@ -3060,14 +2942,14 @@ class Worksheet extends BIFFwriter // Data Blocks if ($bFormatFont == 1) { // Font Name - if ($conditional->getStyle()->getFont()->getName() == null) { + if ($conditional->getStyle()->getFont()->getName() === null) { $dataBlockFont = pack('VVVVVVVV', 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000); $dataBlockFont .= pack('VVVVVVVV', 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000); } else { $dataBlockFont = StringHelper::UTF8toBIFF8UnicodeLong($conditional->getStyle()->getFont()->getName()); } // Font Size - if ($conditional->getStyle()->getFont()->getSize() == null) { + if ($conditional->getStyle()->getFont()->getSize() === null) { $dataBlockFont .= pack('V', 20 * 11); } else { $dataBlockFont .= pack('V', 20 * $conditional->getStyle()->getFont()->getSize()); @@ -3075,16 +2957,16 @@ class Worksheet extends BIFFwriter // Font Options $dataBlockFont .= pack('V', 0); // Font weight - if ($conditional->getStyle()->getFont()->getBold() == true) { + if ($conditional->getStyle()->getFont()->getBold() === true) { $dataBlockFont .= pack('v', 0x02BC); } else { $dataBlockFont .= pack('v', 0x0190); } // Escapement type - if ($conditional->getStyle()->getFont()->getSubscript() == true) { + if ($conditional->getStyle()->getFont()->getSubscript() === true) { $dataBlockFont .= pack('v', 0x02); $fontEscapement = 0; - } elseif ($conditional->getStyle()->getFont()->getSuperscript() == true) { + } elseif ($conditional->getStyle()->getFont()->getSuperscript() === true) { $dataBlockFont .= pack('v', 0x01); $fontEscapement = 0; } else { @@ -3127,242 +3009,14 @@ class Worksheet extends BIFFwriter // Not used (3) $dataBlockFont .= pack('vC', 0x0000, 0x00); // Font color index - switch ($conditional->getStyle()->getFont()->getColor()->getRGB()) { - case '000000': - $colorIdx = 0x08; + $colorIdx = Style\ColorMap::lookup($conditional->getStyle()->getFont()->getColor(), 0x00); - break; - case 'FFFFFF': - $colorIdx = 0x09; - - break; - case 'FF0000': - $colorIdx = 0x0A; - - break; - case '00FF00': - $colorIdx = 0x0B; - - break; - case '0000FF': - $colorIdx = 0x0C; - - break; - case 'FFFF00': - $colorIdx = 0x0D; - - break; - case 'FF00FF': - $colorIdx = 0x0E; - - break; - case '00FFFF': - $colorIdx = 0x0F; - - break; - case '800000': - $colorIdx = 0x10; - - break; - case '008000': - $colorIdx = 0x11; - - break; - case '000080': - $colorIdx = 0x12; - - break; - case '808000': - $colorIdx = 0x13; - - break; - case '800080': - $colorIdx = 0x14; - - break; - case '008080': - $colorIdx = 0x15; - - break; - case 'C0C0C0': - $colorIdx = 0x16; - - break; - case '808080': - $colorIdx = 0x17; - - break; - case '9999FF': - $colorIdx = 0x18; - - break; - case '993366': - $colorIdx = 0x19; - - break; - case 'FFFFCC': - $colorIdx = 0x1A; - - break; - case 'CCFFFF': - $colorIdx = 0x1B; - - break; - case '660066': - $colorIdx = 0x1C; - - break; - case 'FF8080': - $colorIdx = 0x1D; - - break; - case '0066CC': - $colorIdx = 0x1E; - - break; - case 'CCCCFF': - $colorIdx = 0x1F; - - break; - case '000080': - $colorIdx = 0x20; - - break; - case 'FF00FF': - $colorIdx = 0x21; - - break; - case 'FFFF00': - $colorIdx = 0x22; - - break; - case '00FFFF': - $colorIdx = 0x23; - - break; - case '800080': - $colorIdx = 0x24; - - break; - case '800000': - $colorIdx = 0x25; - - break; - case '008080': - $colorIdx = 0x26; - - break; - case '0000FF': - $colorIdx = 0x27; - - break; - case '00CCFF': - $colorIdx = 0x28; - - break; - case 'CCFFFF': - $colorIdx = 0x29; - - break; - case 'CCFFCC': - $colorIdx = 0x2A; - - break; - case 'FFFF99': - $colorIdx = 0x2B; - - break; - case '99CCFF': - $colorIdx = 0x2C; - - break; - case 'FF99CC': - $colorIdx = 0x2D; - - break; - case 'CC99FF': - $colorIdx = 0x2E; - - break; - case 'FFCC99': - $colorIdx = 0x2F; - - break; - case '3366FF': - $colorIdx = 0x30; - - break; - case '33CCCC': - $colorIdx = 0x31; - - break; - case '99CC00': - $colorIdx = 0x32; - - break; - case 'FFCC00': - $colorIdx = 0x33; - - break; - case 'FF9900': - $colorIdx = 0x34; - - break; - case 'FF6600': - $colorIdx = 0x35; - - break; - case '666699': - $colorIdx = 0x36; - - break; - case '969696': - $colorIdx = 0x37; - - break; - case '003366': - $colorIdx = 0x38; - - break; - case '339966': - $colorIdx = 0x39; - - break; - case '003300': - $colorIdx = 0x3A; - - break; - case '333300': - $colorIdx = 0x3B; - - break; - case '993300': - $colorIdx = 0x3C; - - break; - case '993366': - $colorIdx = 0x3D; - - break; - case '333399': - $colorIdx = 0x3E; - - break; - case '333333': - $colorIdx = 0x3F; - - break; - default: - $colorIdx = 0x00; - - break; - } $dataBlockFont .= pack('V', $colorIdx); // Not used (4) $dataBlockFont .= pack('V', 0x00000000); // Options flags for modified font attributes $optionsFlags = 0; - $optionsFlagsBold = ($conditional->getStyle()->getFont()->getBold() == null ? 1 : 0); + $optionsFlagsBold = ($conditional->getStyle()->getFont()->getBold() === null ? 1 : 0); $optionsFlags |= (1 == $optionsFlagsBold ? 0x00000002 : 0); $optionsFlags |= (1 == 1 ? 0x00000008 : 0); $optionsFlags |= (1 == 1 ? 0x00000010 : 0); @@ -3382,58 +3036,11 @@ class Worksheet extends BIFFwriter // Always $dataBlockFont .= pack('v', 0x0001); } - if ($bFormatAlign == 1) { - $blockAlign = 0; + if ($bFormatAlign === 1) { // Alignment and text break - switch ($conditional->getStyle()->getAlignment()->getHorizontal()) { - case Alignment::HORIZONTAL_GENERAL: - $blockAlign = 0; - - break; - case Alignment::HORIZONTAL_LEFT: - $blockAlign = 1; - - break; - case Alignment::HORIZONTAL_RIGHT: - $blockAlign = 3; - - break; - case Alignment::HORIZONTAL_CENTER: - $blockAlign = 2; - - break; - case Alignment::HORIZONTAL_CENTER_CONTINUOUS: - $blockAlign = 6; - - break; - case Alignment::HORIZONTAL_JUSTIFY: - $blockAlign = 5; - - break; - } - if ($conditional->getStyle()->getAlignment()->getWrapText() == true) { - $blockAlign |= 1 << 3; - } else { - $blockAlign |= 0 << 3; - } - switch ($conditional->getStyle()->getAlignment()->getVertical()) { - case Alignment::VERTICAL_BOTTOM: - $blockAlign = 2 << 4; - - break; - case Alignment::VERTICAL_TOP: - $blockAlign = 0 << 4; - - break; - case Alignment::VERTICAL_CENTER: - $blockAlign = 1 << 4; - - break; - case Alignment::VERTICAL_JUSTIFY: - $blockAlign = 3 << 4; - - break; - } + $blockAlign = Style\CellAlignment::horizontal($conditional->getStyle()->getAlignment()); + $blockAlign |= Style\CellAlignment::wrap($conditional->getStyle()->getAlignment()) << 3; + $blockAlign |= Style\CellAlignment::vertical($conditional->getStyle()->getAlignment()) << 4; $blockAlign |= 0 << 7; // Text rotation angle @@ -3441,7 +3048,7 @@ class Worksheet extends BIFFwriter // Indentation $blockIndent = $conditional->getStyle()->getAlignment()->getIndent(); - if ($conditional->getStyle()->getAlignment()->getShrinkToFit() == true) { + if ($conditional->getStyle()->getAlignment()->getShrinkToFit() === true) { $blockIndent |= 1 << 4; } else { $blockIndent |= 0 << 4; @@ -3453,240 +3060,11 @@ class Worksheet extends BIFFwriter $dataBlockAlign = pack('CCvvv', $blockAlign, $blockRotation, $blockIndent, $blockIndentRelative, 0x0000); } - if ($bFormatBorder == 1) { - $blockLineStyle = 0; - switch ($conditional->getStyle()->getBorders()->getLeft()->getBorderStyle()) { - case Border::BORDER_NONE: - $blockLineStyle |= 0x00; - - break; - case Border::BORDER_THIN: - $blockLineStyle |= 0x01; - - break; - case Border::BORDER_MEDIUM: - $blockLineStyle |= 0x02; - - break; - case Border::BORDER_DASHED: - $blockLineStyle |= 0x03; - - break; - case Border::BORDER_DOTTED: - $blockLineStyle |= 0x04; - - break; - case Border::BORDER_THICK: - $blockLineStyle |= 0x05; - - break; - case Border::BORDER_DOUBLE: - $blockLineStyle |= 0x06; - - break; - case Border::BORDER_HAIR: - $blockLineStyle |= 0x07; - - break; - case Border::BORDER_MEDIUMDASHED: - $blockLineStyle |= 0x08; - - break; - case Border::BORDER_DASHDOT: - $blockLineStyle |= 0x09; - - break; - case Border::BORDER_MEDIUMDASHDOT: - $blockLineStyle |= 0x0A; - - break; - case Border::BORDER_DASHDOTDOT: - $blockLineStyle |= 0x0B; - - break; - case Border::BORDER_MEDIUMDASHDOTDOT: - $blockLineStyle |= 0x0C; - - break; - case Border::BORDER_SLANTDASHDOT: - $blockLineStyle |= 0x0D; - - break; - } - switch ($conditional->getStyle()->getBorders()->getRight()->getBorderStyle()) { - case Border::BORDER_NONE: - $blockLineStyle |= 0x00 << 4; - - break; - case Border::BORDER_THIN: - $blockLineStyle |= 0x01 << 4; - - break; - case Border::BORDER_MEDIUM: - $blockLineStyle |= 0x02 << 4; - - break; - case Border::BORDER_DASHED: - $blockLineStyle |= 0x03 << 4; - - break; - case Border::BORDER_DOTTED: - $blockLineStyle |= 0x04 << 4; - - break; - case Border::BORDER_THICK: - $blockLineStyle |= 0x05 << 4; - - break; - case Border::BORDER_DOUBLE: - $blockLineStyle |= 0x06 << 4; - - break; - case Border::BORDER_HAIR: - $blockLineStyle |= 0x07 << 4; - - break; - case Border::BORDER_MEDIUMDASHED: - $blockLineStyle |= 0x08 << 4; - - break; - case Border::BORDER_DASHDOT: - $blockLineStyle |= 0x09 << 4; - - break; - case Border::BORDER_MEDIUMDASHDOT: - $blockLineStyle |= 0x0A << 4; - - break; - case Border::BORDER_DASHDOTDOT: - $blockLineStyle |= 0x0B << 4; - - break; - case Border::BORDER_MEDIUMDASHDOTDOT: - $blockLineStyle |= 0x0C << 4; - - break; - case Border::BORDER_SLANTDASHDOT: - $blockLineStyle |= 0x0D << 4; - - break; - } - switch ($conditional->getStyle()->getBorders()->getTop()->getBorderStyle()) { - case Border::BORDER_NONE: - $blockLineStyle |= 0x00 << 8; - - break; - case Border::BORDER_THIN: - $blockLineStyle |= 0x01 << 8; - - break; - case Border::BORDER_MEDIUM: - $blockLineStyle |= 0x02 << 8; - - break; - case Border::BORDER_DASHED: - $blockLineStyle |= 0x03 << 8; - - break; - case Border::BORDER_DOTTED: - $blockLineStyle |= 0x04 << 8; - - break; - case Border::BORDER_THICK: - $blockLineStyle |= 0x05 << 8; - - break; - case Border::BORDER_DOUBLE: - $blockLineStyle |= 0x06 << 8; - - break; - case Border::BORDER_HAIR: - $blockLineStyle |= 0x07 << 8; - - break; - case Border::BORDER_MEDIUMDASHED: - $blockLineStyle |= 0x08 << 8; - - break; - case Border::BORDER_DASHDOT: - $blockLineStyle |= 0x09 << 8; - - break; - case Border::BORDER_MEDIUMDASHDOT: - $blockLineStyle |= 0x0A << 8; - - break; - case Border::BORDER_DASHDOTDOT: - $blockLineStyle |= 0x0B << 8; - - break; - case Border::BORDER_MEDIUMDASHDOTDOT: - $blockLineStyle |= 0x0C << 8; - - break; - case Border::BORDER_SLANTDASHDOT: - $blockLineStyle |= 0x0D << 8; - - break; - } - switch ($conditional->getStyle()->getBorders()->getBottom()->getBorderStyle()) { - case Border::BORDER_NONE: - $blockLineStyle |= 0x00 << 12; - - break; - case Border::BORDER_THIN: - $blockLineStyle |= 0x01 << 12; - - break; - case Border::BORDER_MEDIUM: - $blockLineStyle |= 0x02 << 12; - - break; - case Border::BORDER_DASHED: - $blockLineStyle |= 0x03 << 12; - - break; - case Border::BORDER_DOTTED: - $blockLineStyle |= 0x04 << 12; - - break; - case Border::BORDER_THICK: - $blockLineStyle |= 0x05 << 12; - - break; - case Border::BORDER_DOUBLE: - $blockLineStyle |= 0x06 << 12; - - break; - case Border::BORDER_HAIR: - $blockLineStyle |= 0x07 << 12; - - break; - case Border::BORDER_MEDIUMDASHED: - $blockLineStyle |= 0x08 << 12; - - break; - case Border::BORDER_DASHDOT: - $blockLineStyle |= 0x09 << 12; - - break; - case Border::BORDER_MEDIUMDASHDOT: - $blockLineStyle |= 0x0A << 12; - - break; - case Border::BORDER_DASHDOTDOT: - $blockLineStyle |= 0x0B << 12; - - break; - case Border::BORDER_MEDIUMDASHDOTDOT: - $blockLineStyle |= 0x0C << 12; - - break; - case Border::BORDER_SLANTDASHDOT: - $blockLineStyle |= 0x0D << 12; - - break; - } + if ($bFormatBorder === 1) { + $blockLineStyle = Style\CellBorder::style($conditional->getStyle()->getBorders()->getLeft()); + $blockLineStyle |= Style\CellBorder::style($conditional->getStyle()->getBorders()->getRight()) << 4; + $blockLineStyle |= Style\CellBorder::style($conditional->getStyle()->getBorders()->getTop()) << 8; + $blockLineStyle |= Style\CellBorder::style($conditional->getStyle()->getBorders()->getBottom()) << 12; // TODO writeCFRule() => $blockLineStyle => Index Color for left line // TODO writeCFRule() => $blockLineStyle => Index Color for right line @@ -3696,636 +3074,32 @@ class Worksheet extends BIFFwriter // TODO writeCFRule() => $blockColor => Index Color for top line // TODO writeCFRule() => $blockColor => Index Color for bottom line // TODO writeCFRule() => $blockColor => Index Color for diagonal line - switch ($conditional->getStyle()->getBorders()->getDiagonal()->getBorderStyle()) { - case Border::BORDER_NONE: - $blockColor |= 0x00 << 21; - - break; - case Border::BORDER_THIN: - $blockColor |= 0x01 << 21; - - break; - case Border::BORDER_MEDIUM: - $blockColor |= 0x02 << 21; - - break; - case Border::BORDER_DASHED: - $blockColor |= 0x03 << 21; - - break; - case Border::BORDER_DOTTED: - $blockColor |= 0x04 << 21; - - break; - case Border::BORDER_THICK: - $blockColor |= 0x05 << 21; - - break; - case Border::BORDER_DOUBLE: - $blockColor |= 0x06 << 21; - - break; - case Border::BORDER_HAIR: - $blockColor |= 0x07 << 21; - - break; - case Border::BORDER_MEDIUMDASHED: - $blockColor |= 0x08 << 21; - - break; - case Border::BORDER_DASHDOT: - $blockColor |= 0x09 << 21; - - break; - case Border::BORDER_MEDIUMDASHDOT: - $blockColor |= 0x0A << 21; - - break; - case Border::BORDER_DASHDOTDOT: - $blockColor |= 0x0B << 21; - - break; - case Border::BORDER_MEDIUMDASHDOTDOT: - $blockColor |= 0x0C << 21; - - break; - case Border::BORDER_SLANTDASHDOT: - $blockColor |= 0x0D << 21; - - break; - } + $blockColor |= Style\CellBorder::style($conditional->getStyle()->getBorders()->getDiagonal()) << 21; $dataBlockBorder = pack('vv', $blockLineStyle, $blockColor); } - if ($bFormatFill == 1) { - // Fill Patern Style - $blockFillPatternStyle = 0; - switch ($conditional->getStyle()->getFill()->getFillType()) { - case Fill::FILL_NONE: - $blockFillPatternStyle = 0x00; + if ($bFormatFill === 1) { + // Fill Pattern Style + $blockFillPatternStyle = Style\CellFill::style($conditional->getStyle()->getFill()); + // Background Color + $colorIdxBg = Style\ColorMap::lookup($conditional->getStyle()->getFill()->getStartColor(), 0x41); + // Foreground Color + $colorIdxFg = Style\ColorMap::lookup($conditional->getStyle()->getFill()->getEndColor(), 0x40); - break; - case Fill::FILL_SOLID: - $blockFillPatternStyle = 0x01; - - break; - case Fill::FILL_PATTERN_MEDIUMGRAY: - $blockFillPatternStyle = 0x02; - - break; - case Fill::FILL_PATTERN_DARKGRAY: - $blockFillPatternStyle = 0x03; - - break; - case Fill::FILL_PATTERN_LIGHTGRAY: - $blockFillPatternStyle = 0x04; - - break; - case Fill::FILL_PATTERN_DARKHORIZONTAL: - $blockFillPatternStyle = 0x05; - - break; - case Fill::FILL_PATTERN_DARKVERTICAL: - $blockFillPatternStyle = 0x06; - - break; - case Fill::FILL_PATTERN_DARKDOWN: - $blockFillPatternStyle = 0x07; - - break; - case Fill::FILL_PATTERN_DARKUP: - $blockFillPatternStyle = 0x08; - - break; - case Fill::FILL_PATTERN_DARKGRID: - $blockFillPatternStyle = 0x09; - - break; - case Fill::FILL_PATTERN_DARKTRELLIS: - $blockFillPatternStyle = 0x0A; - - break; - case Fill::FILL_PATTERN_LIGHTHORIZONTAL: - $blockFillPatternStyle = 0x0B; - - break; - case Fill::FILL_PATTERN_LIGHTVERTICAL: - $blockFillPatternStyle = 0x0C; - - break; - case Fill::FILL_PATTERN_LIGHTDOWN: - $blockFillPatternStyle = 0x0D; - - break; - case Fill::FILL_PATTERN_LIGHTUP: - $blockFillPatternStyle = 0x0E; - - break; - case Fill::FILL_PATTERN_LIGHTGRID: - $blockFillPatternStyle = 0x0F; - - break; - case Fill::FILL_PATTERN_LIGHTTRELLIS: - $blockFillPatternStyle = 0x10; - - break; - case Fill::FILL_PATTERN_GRAY125: - $blockFillPatternStyle = 0x11; - - break; - case Fill::FILL_PATTERN_GRAY0625: - $blockFillPatternStyle = 0x12; - - break; - case Fill::FILL_GRADIENT_LINEAR: - $blockFillPatternStyle = 0x00; - - break; // does not exist in BIFF8 - case Fill::FILL_GRADIENT_PATH: - $blockFillPatternStyle = 0x00; - - break; // does not exist in BIFF8 - default: - $blockFillPatternStyle = 0x00; - - break; - } - // Color - switch ($conditional->getStyle()->getFill()->getStartColor()->getRGB()) { - case '000000': - $colorIdxBg = 0x08; - - break; - case 'FFFFFF': - $colorIdxBg = 0x09; - - break; - case 'FF0000': - $colorIdxBg = 0x0A; - - break; - case '00FF00': - $colorIdxBg = 0x0B; - - break; - case '0000FF': - $colorIdxBg = 0x0C; - - break; - case 'FFFF00': - $colorIdxBg = 0x0D; - - break; - case 'FF00FF': - $colorIdxBg = 0x0E; - - break; - case '00FFFF': - $colorIdxBg = 0x0F; - - break; - case '800000': - $colorIdxBg = 0x10; - - break; - case '008000': - $colorIdxBg = 0x11; - - break; - case '000080': - $colorIdxBg = 0x12; - - break; - case '808000': - $colorIdxBg = 0x13; - - break; - case '800080': - $colorIdxBg = 0x14; - - break; - case '008080': - $colorIdxBg = 0x15; - - break; - case 'C0C0C0': - $colorIdxBg = 0x16; - - break; - case '808080': - $colorIdxBg = 0x17; - - break; - case '9999FF': - $colorIdxBg = 0x18; - - break; - case '993366': - $colorIdxBg = 0x19; - - break; - case 'FFFFCC': - $colorIdxBg = 0x1A; - - break; - case 'CCFFFF': - $colorIdxBg = 0x1B; - - break; - case '660066': - $colorIdxBg = 0x1C; - - break; - case 'FF8080': - $colorIdxBg = 0x1D; - - break; - case '0066CC': - $colorIdxBg = 0x1E; - - break; - case 'CCCCFF': - $colorIdxBg = 0x1F; - - break; - case '000080': - $colorIdxBg = 0x20; - - break; - case 'FF00FF': - $colorIdxBg = 0x21; - - break; - case 'FFFF00': - $colorIdxBg = 0x22; - - break; - case '00FFFF': - $colorIdxBg = 0x23; - - break; - case '800080': - $colorIdxBg = 0x24; - - break; - case '800000': - $colorIdxBg = 0x25; - - break; - case '008080': - $colorIdxBg = 0x26; - - break; - case '0000FF': - $colorIdxBg = 0x27; - - break; - case '00CCFF': - $colorIdxBg = 0x28; - - break; - case 'CCFFFF': - $colorIdxBg = 0x29; - - break; - case 'CCFFCC': - $colorIdxBg = 0x2A; - - break; - case 'FFFF99': - $colorIdxBg = 0x2B; - - break; - case '99CCFF': - $colorIdxBg = 0x2C; - - break; - case 'FF99CC': - $colorIdxBg = 0x2D; - - break; - case 'CC99FF': - $colorIdxBg = 0x2E; - - break; - case 'FFCC99': - $colorIdxBg = 0x2F; - - break; - case '3366FF': - $colorIdxBg = 0x30; - - break; - case '33CCCC': - $colorIdxBg = 0x31; - - break; - case '99CC00': - $colorIdxBg = 0x32; - - break; - case 'FFCC00': - $colorIdxBg = 0x33; - - break; - case 'FF9900': - $colorIdxBg = 0x34; - - break; - case 'FF6600': - $colorIdxBg = 0x35; - - break; - case '666699': - $colorIdxBg = 0x36; - - break; - case '969696': - $colorIdxBg = 0x37; - - break; - case '003366': - $colorIdxBg = 0x38; - - break; - case '339966': - $colorIdxBg = 0x39; - - break; - case '003300': - $colorIdxBg = 0x3A; - - break; - case '333300': - $colorIdxBg = 0x3B; - - break; - case '993300': - $colorIdxBg = 0x3C; - - break; - case '993366': - $colorIdxBg = 0x3D; - - break; - case '333399': - $colorIdxBg = 0x3E; - - break; - case '333333': - $colorIdxBg = 0x3F; - - break; - default: - $colorIdxBg = 0x41; - - break; - } - // Fg Color - switch ($conditional->getStyle()->getFill()->getEndColor()->getRGB()) { - case '000000': - $colorIdxFg = 0x08; - - break; - case 'FFFFFF': - $colorIdxFg = 0x09; - - break; - case 'FF0000': - $colorIdxFg = 0x0A; - - break; - case '00FF00': - $colorIdxFg = 0x0B; - - break; - case '0000FF': - $colorIdxFg = 0x0C; - - break; - case 'FFFF00': - $colorIdxFg = 0x0D; - - break; - case 'FF00FF': - $colorIdxFg = 0x0E; - - break; - case '00FFFF': - $colorIdxFg = 0x0F; - - break; - case '800000': - $colorIdxFg = 0x10; - - break; - case '008000': - $colorIdxFg = 0x11; - - break; - case '000080': - $colorIdxFg = 0x12; - - break; - case '808000': - $colorIdxFg = 0x13; - - break; - case '800080': - $colorIdxFg = 0x14; - - break; - case '008080': - $colorIdxFg = 0x15; - - break; - case 'C0C0C0': - $colorIdxFg = 0x16; - - break; - case '808080': - $colorIdxFg = 0x17; - - break; - case '9999FF': - $colorIdxFg = 0x18; - - break; - case '993366': - $colorIdxFg = 0x19; - - break; - case 'FFFFCC': - $colorIdxFg = 0x1A; - - break; - case 'CCFFFF': - $colorIdxFg = 0x1B; - - break; - case '660066': - $colorIdxFg = 0x1C; - - break; - case 'FF8080': - $colorIdxFg = 0x1D; - - break; - case '0066CC': - $colorIdxFg = 0x1E; - - break; - case 'CCCCFF': - $colorIdxFg = 0x1F; - - break; - case '000080': - $colorIdxFg = 0x20; - - break; - case 'FF00FF': - $colorIdxFg = 0x21; - - break; - case 'FFFF00': - $colorIdxFg = 0x22; - - break; - case '00FFFF': - $colorIdxFg = 0x23; - - break; - case '800080': - $colorIdxFg = 0x24; - - break; - case '800000': - $colorIdxFg = 0x25; - - break; - case '008080': - $colorIdxFg = 0x26; - - break; - case '0000FF': - $colorIdxFg = 0x27; - - break; - case '00CCFF': - $colorIdxFg = 0x28; - - break; - case 'CCFFFF': - $colorIdxFg = 0x29; - - break; - case 'CCFFCC': - $colorIdxFg = 0x2A; - - break; - case 'FFFF99': - $colorIdxFg = 0x2B; - - break; - case '99CCFF': - $colorIdxFg = 0x2C; - - break; - case 'FF99CC': - $colorIdxFg = 0x2D; - - break; - case 'CC99FF': - $colorIdxFg = 0x2E; - - break; - case 'FFCC99': - $colorIdxFg = 0x2F; - - break; - case '3366FF': - $colorIdxFg = 0x30; - - break; - case '33CCCC': - $colorIdxFg = 0x31; - - break; - case '99CC00': - $colorIdxFg = 0x32; - - break; - case 'FFCC00': - $colorIdxFg = 0x33; - - break; - case 'FF9900': - $colorIdxFg = 0x34; - - break; - case 'FF6600': - $colorIdxFg = 0x35; - - break; - case '666699': - $colorIdxFg = 0x36; - - break; - case '969696': - $colorIdxFg = 0x37; - - break; - case '003366': - $colorIdxFg = 0x38; - - break; - case '339966': - $colorIdxFg = 0x39; - - break; - case '003300': - $colorIdxFg = 0x3A; - - break; - case '333300': - $colorIdxFg = 0x3B; - - break; - case '993300': - $colorIdxFg = 0x3C; - - break; - case '993366': - $colorIdxFg = 0x3D; - - break; - case '333399': - $colorIdxFg = 0x3E; - - break; - case '333333': - $colorIdxFg = 0x3F; - - break; - default: - $colorIdxFg = 0x40; - - break; - } $dataBlockFill = pack('v', $blockFillPatternStyle); $dataBlockFill .= pack('v', $colorIdxFg | ($colorIdxBg << 7)); } $data = pack('CCvvVv', $type, $operatorType, $szValue1, $szValue2, $flags, 0x0000); - if ($bFormatFont == 1) { // Block Formatting : OK + if ($bFormatFont === 1) { // Block Formatting : OK $data .= $dataBlockFont; } - if ($bFormatAlign == 1) { + if ($bFormatAlign === 1) { $data .= $dataBlockAlign; } - if ($bFormatBorder == 1) { + if ($bFormatBorder === 1) { $data .= $dataBlockBorder; } - if ($bFormatFill == 1) { // Block Formatting : OK + if ($bFormatFill === 1) { // Block Formatting : OK $data .= $dataBlockFill; } if ($bFormatProt == 1) { diff --git a/src/PhpSpreadsheet/Writer/Xls/Xf.php b/src/PhpSpreadsheet/Writer/Xls/Xf.php index 6f1d1cb2..b2dbc67a 100644 --- a/src/PhpSpreadsheet/Writer/Xls/Xf.php +++ b/src/PhpSpreadsheet/Writer/Xls/Xf.php @@ -3,11 +3,12 @@ namespace PhpOffice\PhpSpreadsheet\Writer\Xls; use PhpOffice\PhpSpreadsheet\Style\Alignment; -use PhpOffice\PhpSpreadsheet\Style\Border; use PhpOffice\PhpSpreadsheet\Style\Borders; -use PhpOffice\PhpSpreadsheet\Style\Fill; use PhpOffice\PhpSpreadsheet\Style\Protection; use PhpOffice\PhpSpreadsheet\Style\Style; +use PhpOffice\PhpSpreadsheet\Writer\Xls\Style\CellAlignment; +use PhpOffice\PhpSpreadsheet\Writer\Xls\Style\CellBorder; +use PhpOffice\PhpSpreadsheet\Writer\Xls\Style\CellFill; // Original file header of PEAR::Spreadsheet_Excel_Writer_Format (used as the base for this class): // ----------------------------------------------------------------------------------------- @@ -176,30 +177,30 @@ class Xf $atr_num = ($this->numberFormatIndex != 0) ? 1 : 0; $atr_fnt = ($this->fontIndex != 0) ? 1 : 0; $atr_alc = ((int) $this->style->getAlignment()->getWrapText()) ? 1 : 0; - $atr_bdr = (self::mapBorderStyle($this->style->getBorders()->getBottom()->getBorderStyle()) || - self::mapBorderStyle($this->style->getBorders()->getTop()->getBorderStyle()) || - self::mapBorderStyle($this->style->getBorders()->getLeft()->getBorderStyle()) || - self::mapBorderStyle($this->style->getBorders()->getRight()->getBorderStyle())) ? 1 : 0; + $atr_bdr = (CellBorder::style($this->style->getBorders()->getBottom()) || + CellBorder::style($this->style->getBorders()->getTop()) || + CellBorder::style($this->style->getBorders()->getLeft()) || + CellBorder::style($this->style->getBorders()->getRight())) ? 1 : 0; $atr_pat = ($this->foregroundColor != 0x40) ? 1 : 0; $atr_pat = ($this->backgroundColor != 0x41) ? 1 : $atr_pat; - $atr_pat = self::mapFillType($this->style->getFill()->getFillType()) ? 1 : $atr_pat; + $atr_pat = CellFill::style($this->style->getFill()) ? 1 : $atr_pat; $atr_prot = self::mapLocked($this->style->getProtection()->getLocked()) | self::mapHidden($this->style->getProtection()->getHidden()); // Zero the default border colour if the border has not been set. - if (self::mapBorderStyle($this->style->getBorders()->getBottom()->getBorderStyle()) == 0) { + if (CellBorder::style($this->style->getBorders()->getBottom()) == 0) { $this->bottomBorderColor = 0; } - if (self::mapBorderStyle($this->style->getBorders()->getTop()->getBorderStyle()) == 0) { + if (CellBorder::style($this->style->getBorders()->getTop()) == 0) { $this->topBorderColor = 0; } - if (self::mapBorderStyle($this->style->getBorders()->getRight()->getBorderStyle()) == 0) { + if (CellBorder::style($this->style->getBorders()->getRight()) == 0) { $this->rightBorderColor = 0; } - if (self::mapBorderStyle($this->style->getBorders()->getLeft()->getBorderStyle()) == 0) { + if (CellBorder::style($this->style->getBorders()->getLeft()) == 0) { $this->leftBorderColor = 0; } - if (self::mapBorderStyle($this->style->getBorders()->getDiagonal()->getBorderStyle()) == 0) { + if (CellBorder::style($this->style->getBorders()->getDiagonal()) == 0) { $this->diagColor = 0; } @@ -209,9 +210,10 @@ class Xf $ifnt = $this->fontIndex; // Index to FONT record $ifmt = $this->numberFormatIndex; // Index to FORMAT record - $align = $this->mapHAlign($this->style->getAlignment()->getHorizontal()); // Alignment - $align |= (int) $this->style->getAlignment()->getWrapText() << 3; - $align |= self::mapVAlign($this->style->getAlignment()->getVertical()) << 4; + // Alignment + $align = CellAlignment::horizontal($this->style->getAlignment()); + $align |= CellAlignment::wrap($this->style->getAlignment()) << 3; + $align |= CellAlignment::vertical($this->style->getAlignment()) << 4; $align |= $this->textJustLast << 7; $used_attrib = $atr_num << 2; @@ -224,10 +226,10 @@ class Xf $icv = $this->foregroundColor; // fg and bg pattern colors $icv |= $this->backgroundColor << 7; - $border1 = self::mapBorderStyle($this->style->getBorders()->getLeft()->getBorderStyle()); // Border line style and color - $border1 |= self::mapBorderStyle($this->style->getBorders()->getRight()->getBorderStyle()) << 4; - $border1 |= self::mapBorderStyle($this->style->getBorders()->getTop()->getBorderStyle()) << 8; - $border1 |= self::mapBorderStyle($this->style->getBorders()->getBottom()->getBorderStyle()) << 12; + $border1 = CellBorder::style($this->style->getBorders()->getLeft()); // Border line style and color + $border1 |= CellBorder::style($this->style->getBorders()->getRight()) << 4; + $border1 |= CellBorder::style($this->style->getBorders()->getTop()) << 8; + $border1 |= CellBorder::style($this->style->getBorders()->getBottom()) << 12; $border1 |= $this->leftBorderColor << 16; $border1 |= $this->rightBorderColor << 23; @@ -242,8 +244,8 @@ class Xf $border2 = $this->topBorderColor; // Border color $border2 |= $this->bottomBorderColor << 7; $border2 |= $this->diagColor << 14; - $border2 |= self::mapBorderStyle($this->style->getBorders()->getDiagonal()->getBorderStyle()) << 21; - $border2 |= self::mapFillType($this->style->getFill()->getFillType()) << 26; + $border2 |= CellBorder::style($this->style->getBorders()->getDiagonal()) << 21; + $border2 |= CellFill::style($this->style->getFill()) << 26; $header = pack('vv', $record, $length); @@ -359,132 +361,6 @@ class Xf $this->fontIndex = $value; } - /** - * Map of BIFF2-BIFF8 codes for border styles. - * - * @var int[] - */ - private static $mapBorderStyles = [ - Border::BORDER_NONE => 0x00, - Border::BORDER_THIN => 0x01, - Border::BORDER_MEDIUM => 0x02, - Border::BORDER_DASHED => 0x03, - Border::BORDER_DOTTED => 0x04, - Border::BORDER_THICK => 0x05, - Border::BORDER_DOUBLE => 0x06, - Border::BORDER_HAIR => 0x07, - Border::BORDER_MEDIUMDASHED => 0x08, - Border::BORDER_DASHDOT => 0x09, - Border::BORDER_MEDIUMDASHDOT => 0x0A, - Border::BORDER_DASHDOTDOT => 0x0B, - Border::BORDER_MEDIUMDASHDOTDOT => 0x0C, - Border::BORDER_SLANTDASHDOT => 0x0D, - ]; - - /** - * Map border style. - * - * @param string $borderStyle - * - * @return int - */ - private static function mapBorderStyle($borderStyle) - { - return self::$mapBorderStyles[$borderStyle] ?? 0; - } - - /** - * Map of BIFF2-BIFF8 codes for fill types. - * - * @var int[] - */ - private static $mapFillTypes = [ - Fill::FILL_NONE => 0x00, - Fill::FILL_SOLID => 0x01, - Fill::FILL_PATTERN_MEDIUMGRAY => 0x02, - Fill::FILL_PATTERN_DARKGRAY => 0x03, - Fill::FILL_PATTERN_LIGHTGRAY => 0x04, - Fill::FILL_PATTERN_DARKHORIZONTAL => 0x05, - Fill::FILL_PATTERN_DARKVERTICAL => 0x06, - Fill::FILL_PATTERN_DARKDOWN => 0x07, - Fill::FILL_PATTERN_DARKUP => 0x08, - Fill::FILL_PATTERN_DARKGRID => 0x09, - Fill::FILL_PATTERN_DARKTRELLIS => 0x0A, - Fill::FILL_PATTERN_LIGHTHORIZONTAL => 0x0B, - Fill::FILL_PATTERN_LIGHTVERTICAL => 0x0C, - Fill::FILL_PATTERN_LIGHTDOWN => 0x0D, - Fill::FILL_PATTERN_LIGHTUP => 0x0E, - Fill::FILL_PATTERN_LIGHTGRID => 0x0F, - Fill::FILL_PATTERN_LIGHTTRELLIS => 0x10, - Fill::FILL_PATTERN_GRAY125 => 0x11, - Fill::FILL_PATTERN_GRAY0625 => 0x12, - Fill::FILL_GRADIENT_LINEAR => 0x00, // does not exist in BIFF8 - Fill::FILL_GRADIENT_PATH => 0x00, // does not exist in BIFF8 - ]; - - /** - * Map fill type. - * - * @param string $fillType - * - * @return int - */ - private static function mapFillType($fillType) - { - return self::$mapFillTypes[$fillType] ?? 0; - } - - /** - * Map of BIFF2-BIFF8 codes for horizontal alignment. - * - * @var int[] - */ - private static $mapHAlignments = [ - Alignment::HORIZONTAL_GENERAL => 0, - Alignment::HORIZONTAL_LEFT => 1, - Alignment::HORIZONTAL_CENTER => 2, - Alignment::HORIZONTAL_RIGHT => 3, - Alignment::HORIZONTAL_FILL => 4, - Alignment::HORIZONTAL_JUSTIFY => 5, - Alignment::HORIZONTAL_CENTER_CONTINUOUS => 6, - ]; - - /** - * Map to BIFF2-BIFF8 codes for horizontal alignment. - * - * @param string $hAlign - * - * @return int - */ - private function mapHAlign($hAlign) - { - return self::$mapHAlignments[$hAlign] ?? 0; - } - - /** - * Map of BIFF2-BIFF8 codes for vertical alignment. - * - * @var int[] - */ - private static $mapVAlignments = [ - Alignment::VERTICAL_TOP => 0, - Alignment::VERTICAL_CENTER => 1, - Alignment::VERTICAL_BOTTOM => 2, - Alignment::VERTICAL_JUSTIFY => 3, - ]; - - /** - * Map to BIFF2-BIFF8 codes for vertical alignment. - * - * @param string $vAlign - * - * @return int - */ - private static function mapVAlign($vAlign) - { - return self::$mapVAlignments[$vAlign] ?? 2; - } - /** * Map to BIFF8 codes for text rotation angle. * From a01a4012281b650d1d5e76a3a134c989c6c28745 Mon Sep 17 00:00:00 2001 From: oleibman Date: Sat, 24 Apr 2021 09:12:17 -0700 Subject: [PATCH 05/57] MathTrig - Fix Phpstan Accomodations (#2020) * MathTrig - Fix Phpstan Accomodations This should be the last of my mass changes to MathTrig. All he Phpstan violations found in baseline which are part of MathTrig are now fixed and removed from baseline. There were about 20 of these. --- phpstan-baseline.neon | 140 ------------------ src/PhpSpreadsheet/Calculation/MathTrig.php | 14 +- .../Calculation/MathTrig/Arabic.php | 10 +- .../Calculation/MathTrig/Base.php | 2 +- .../Calculation/MathTrig/Helpers.php | 4 +- .../Calculation/MathTrig/Lcm.php | 2 +- .../Calculation/MathTrig/Roman.php | 11 +- .../Calculation/MathTrig/RoundDown.php | 2 +- .../Calculation/MathTrig/RoundUp.php | 2 +- .../Calculation/MathTrig/Subtotal.php | 20 ++- .../Calculation/MathTrig/Trunc.php | 2 +- .../Functions/MathTrig/SubTotalTest.php | 14 +- 12 files changed, 54 insertions(+), 169 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 2228974e..1b57a748 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1230,136 +1230,6 @@ parameters: count: 1 path: src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\MathTrig\\:\\:COMBIN\\(\\) should return int\\|string but returns float\\|int\\|string\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/MathTrig.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\MathTrig\\:\\:EVEN\\(\\) should return int\\|string but returns float\\|string\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/MathTrig.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\MathTrig\\:\\:FACT\\(\\) should return int\\|string but returns float\\|int\\|string\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/MathTrig.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\MathTrig\\:\\:FACTDOUBLE\\(\\) should return int\\|string but returns float\\|int\\|string\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/MathTrig.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\MathTrig\\:\\:MOD\\(\\) should return int\\|string but returns float\\|int\\|string\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/MathTrig.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\MathTrig\\:\\:ODD\\(\\) should return int\\|string but returns float\\|string\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/MathTrig.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\MathTrig\\:\\:SUMIFS\\(\\) should return float\\|string but returns float\\|string\\|null\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/MathTrig.php - - - - message: "#^Else branch is unreachable because ternary operator condition is always true\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/MathTrig/Arabic.php - - - - message: "#^Parameter \\#1 \\$number of function base_convert expects string, int\\<0, 9007199254740991\\> given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/MathTrig/Base.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\MathTrig\\\\Helpers\\:\\:validateNumericNullBool\\(\\) should return float\\|int but returns float\\|int\\|string\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/MathTrig/Helpers.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\MathTrig\\\\Helpers\\:\\:validateNumericNullSubstitution\\(\\) should return float\\|int but returns float\\|int\\|string\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/MathTrig/Helpers.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\MathTrig\\\\Lcm\\:\\:factors\\(\\) has no return typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/MathTrig/Lcm.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\MathTrig\\\\Lcm\\:\\:factors\\(\\) has parameter \\$value with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/MathTrig/Lcm.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\MathTrig\\\\Roman\\:\\:romanCut\\(\\) has no return typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/MathTrig/Roman.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\MathTrig\\\\Roman\\:\\:romanCut\\(\\) has parameter \\$n with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/MathTrig/Roman.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\MathTrig\\\\Roman\\:\\:romanCut\\(\\) has parameter \\$num with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/MathTrig/Roman.php - - - - message: "#^Parameter \\#2 \\$precision of function round expects int, float\\|int given\\.$#" - count: 2 - path: src/PhpSpreadsheet/Calculation/MathTrig/RoundDown.php - - - - message: "#^Parameter \\#2 \\$precision of function round expects int, float\\|int given\\.$#" - count: 2 - path: src/PhpSpreadsheet/Calculation/MathTrig/RoundUp.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\MathTrig\\\\Subtotal\\:\\:filterHiddenArgs\\(\\) has no return typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\MathTrig\\\\Subtotal\\:\\:filterHiddenArgs\\(\\) has parameter \\$args with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\MathTrig\\\\Subtotal\\:\\:filterHiddenArgs\\(\\) has parameter \\$cellReference with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\MathTrig\\\\Subtotal\\:\\:filterFormulaArgs\\(\\) has no return typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\MathTrig\\\\Subtotal\\:\\:filterFormulaArgs\\(\\) has parameter \\$args with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\MathTrig\\\\Subtotal\\:\\:filterFormulaArgs\\(\\) has parameter \\$cellReference with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php - - - - message: "#^Parameter \\#1 \\$function of function call_user_func_array expects callable\\(\\)\\: mixed, array\\('PhpOffice…'\\|'PhpOffice…'\\|'PhpOffice…'\\|'PhpOffice…'\\|'PhpOffice…'\\|'PhpOffice…'\\|'PhpOffice…'\\|'PhpOffice…', string\\) given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php - - - - message: "#^Parameter \\#1 \\$str of function rtrim expects string, int given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/MathTrig/Trunc.php - - message: "#^Binary operation \"\\-\" between float\\|int and float\\|string results in an error\\.$#" count: 4 @@ -8095,16 +7965,6 @@ parameters: count: 1 path: tests/PhpSpreadsheetTests/Calculation/Engine/RangeTest.php - - - message: "#^Cannot call method setVisible\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\ColumnDimension\\|null\\.$#" - count: 1 - path: tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/SubTotalTest.php - - - - message: "#^Cannot call method setVisible\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\RowDimension\\|null\\.$#" - count: 1 - path: tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/SubTotalTest.php - - message: "#^Cannot call method setAutoSize\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\ColumnDimension\\|null\\.$#" count: 1 diff --git a/src/PhpSpreadsheet/Calculation/MathTrig.php b/src/PhpSpreadsheet/Calculation/MathTrig.php index 121efde5..8398ba13 100644 --- a/src/PhpSpreadsheet/Calculation/MathTrig.php +++ b/src/PhpSpreadsheet/Calculation/MathTrig.php @@ -124,7 +124,7 @@ class MathTrig * @param int $numObjs Number of different objects * @param int $numInSet Number of objects in each combination * - * @return int|string Number of combinations, or a string containing an error + * @return float|int|string Number of combinations, or a string containing an error */ public static function COMBIN($numObjs, $numInSet) { @@ -150,7 +150,7 @@ class MathTrig * * @param float $number Number to round * - * @return int|string Rounded Number, or a string containing an error + * @return float|int|string Rounded Number, or a string containing an error */ public static function EVEN($number) { @@ -186,7 +186,7 @@ class MathTrig * * @param float $factVal Factorial Value * - * @return int|string Factorial, or a string containing an error + * @return float|int|string Factorial, or a string containing an error */ public static function FACT($factVal) { @@ -208,7 +208,7 @@ class MathTrig * * @param float $factVal Factorial Value * - * @return int|string Double Factorial, or a string containing an error + * @return float|int|string Double Factorial, or a string containing an error */ public static function FACTDOUBLE($factVal) { @@ -452,7 +452,7 @@ class MathTrig * @param int $a Dividend * @param int $b Divisor * - * @return int|string Remainder, or a string containing an error + * @return float|int|string Remainder, or a string containing an error */ public static function MOD($a = 1, $b = 1) { @@ -510,7 +510,7 @@ class MathTrig * * @param float $number Number to round * - * @return int|string Rounded Number, or a string containing an error + * @return float|int|string Rounded Number, or a string containing an error */ public static function ODD($number) { @@ -822,7 +822,7 @@ class MathTrig * * @param mixed $args Data values * - * @return float|string + * @return null|float|string */ public static function SUMIFS(...$args) { diff --git a/src/PhpSpreadsheet/Calculation/MathTrig/Arabic.php b/src/PhpSpreadsheet/Calculation/MathTrig/Arabic.php index 320856b9..b3d8c330 100644 --- a/src/PhpSpreadsheet/Calculation/MathTrig/Arabic.php +++ b/src/PhpSpreadsheet/Calculation/MathTrig/Arabic.php @@ -47,11 +47,19 @@ class Arabic return $sum; } + /** + * @param mixed $value + */ + private static function mollifyScrutinizer($value): array + { + return is_array($value) ? $value : []; + } + private static function strSplit(string $roman): array { $rslt = str_split($roman); - return is_array($rslt) ? $rslt : []; + return self::mollifyScrutinizer($rslt); } /** diff --git a/src/PhpSpreadsheet/Calculation/MathTrig/Base.php b/src/PhpSpreadsheet/Calculation/MathTrig/Base.php index 6f44a122..373bcf08 100644 --- a/src/PhpSpreadsheet/Calculation/MathTrig/Base.php +++ b/src/PhpSpreadsheet/Calculation/MathTrig/Base.php @@ -36,7 +36,7 @@ class Base return Functions::NAN(); // Numeric range constraints } - $outcome = strtoupper((string) base_convert($number, 10, $radix)); + $outcome = strtoupper((string) base_convert((string) $number, 10, $radix)); if ($minLength !== null) { $outcome = str_pad($outcome, (int) $minLength, '0', STR_PAD_LEFT); // String padding } diff --git a/src/PhpSpreadsheet/Calculation/MathTrig/Helpers.php b/src/PhpSpreadsheet/Calculation/MathTrig/Helpers.php index 26716467..b89644a9 100644 --- a/src/PhpSpreadsheet/Calculation/MathTrig/Helpers.php +++ b/src/PhpSpreadsheet/Calculation/MathTrig/Helpers.php @@ -34,7 +34,7 @@ class Helpers return (int) $number; } if (is_numeric($number)) { - return $number; + return 0 + $number; } throw new Exception(Functions::VALUE()); @@ -55,7 +55,7 @@ class Helpers return $substitute; } if (is_numeric($number)) { - return $number; + return 0 + $number; } throw new Exception(Functions::VALUE()); diff --git a/src/PhpSpreadsheet/Calculation/MathTrig/Lcm.php b/src/PhpSpreadsheet/Calculation/MathTrig/Lcm.php index 5c28684f..cc1e16bc 100644 --- a/src/PhpSpreadsheet/Calculation/MathTrig/Lcm.php +++ b/src/PhpSpreadsheet/Calculation/MathTrig/Lcm.php @@ -10,7 +10,7 @@ class Lcm // // Private method to return an array of the factors of the input value // - private static function factors($value) + private static function factors(float $value): array { $startVal = floor(sqrt($value)); diff --git a/src/PhpSpreadsheet/Calculation/MathTrig/Roman.php b/src/PhpSpreadsheet/Calculation/MathTrig/Roman.php index 0ee7a919..865a5101 100644 --- a/src/PhpSpreadsheet/Calculation/MathTrig/Roman.php +++ b/src/PhpSpreadsheet/Calculation/MathTrig/Roman.php @@ -776,19 +776,14 @@ class Roman const MAX_ROMAN_VALUE = 3999; const MAX_ROMAN_STYLE = 4; - private static function romanCut($num, $n) - { - return ($num - ($num % $n)) / $n; - } - private static function valueOk(int $aValue, int $style): string { $origValue = $aValue; - $m = self::romanCut($aValue, 1000); + $m = \intdiv($aValue, 1000); $aValue %= 1000; - $c = self::romanCut($aValue, 100); + $c = \intdiv($aValue, 100); $aValue %= 100; - $t = self::romanCut($aValue, 10); + $t = \intdiv($aValue, 10); $aValue %= 10; $result = self::THOUSANDS[$m] . self::HUNDREDS[$c] . self::TENS[$t] . self::ONES[$aValue]; if ($style > 0) { diff --git a/src/PhpSpreadsheet/Calculation/MathTrig/RoundDown.php b/src/PhpSpreadsheet/Calculation/MathTrig/RoundDown.php index e843a6f9..0054a227 100644 --- a/src/PhpSpreadsheet/Calculation/MathTrig/RoundDown.php +++ b/src/PhpSpreadsheet/Calculation/MathTrig/RoundDown.php @@ -20,7 +20,7 @@ class RoundDown { try { $number = Helpers::validateNumericNullBool($number); - $digits = Helpers::validateNumericNullSubstitution($digits, null); + $digits = (int) Helpers::validateNumericNullSubstitution($digits, null); } catch (Exception $e) { return $e->getMessage(); } diff --git a/src/PhpSpreadsheet/Calculation/MathTrig/RoundUp.php b/src/PhpSpreadsheet/Calculation/MathTrig/RoundUp.php index c6704ad2..6b2e6d1a 100644 --- a/src/PhpSpreadsheet/Calculation/MathTrig/RoundUp.php +++ b/src/PhpSpreadsheet/Calculation/MathTrig/RoundUp.php @@ -20,7 +20,7 @@ class RoundUp { try { $number = Helpers::validateNumericNullBool($number); - $digits = Helpers::validateNumericNullSubstitution($digits, null); + $digits = (int) Helpers::validateNumericNullSubstitution($digits, null); } catch (Exception $e) { return $e->getMessage(); } diff --git a/src/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php b/src/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php index 5477d936..0700a5d8 100644 --- a/src/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php +++ b/src/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php @@ -8,7 +8,11 @@ use PhpOffice\PhpSpreadsheet\Calculation\Statistical; class Subtotal { - protected static function filterHiddenArgs($cellReference, $args) + /** + * @param mixed $cellReference + * @param mixed $args + */ + protected static function filterHiddenArgs($cellReference, $args): array { return array_filter( $args, @@ -21,7 +25,11 @@ class Subtotal ); } - protected static function filterFormulaArgs($cellReference, $args) + /** + * @param mixed $cellReference + * @param mixed $args + */ + protected static function filterFormulaArgs($cellReference, $args): array { return array_filter( $args, @@ -42,6 +50,7 @@ class Subtotal ); } + /** @var callable[] */ private const CALL_FUNCTIONS = [ 1 => [Statistical\Averages::class, 'AVERAGE'], [Statistical\Counts::class, 'COUNT'], // 2 @@ -67,7 +76,7 @@ class Subtotal * list * Numbers 101 to 111 shadow the functions of 1 to 11 * but ignore any values in the range that are - * in hidden rows or columns + * in hidden rows * @param mixed[] $args A mixed data series of values * * @return float|string @@ -91,7 +100,10 @@ class Subtotal $aArgs = self::filterFormulaArgs($cellReference, $aArgs); if (array_key_exists($subtotal, self::CALL_FUNCTIONS)) { - return call_user_func_array(self::CALL_FUNCTIONS[$subtotal], $aArgs); + /** @var callable */ + $call = self::CALL_FUNCTIONS[$subtotal]; + + return call_user_func_array($call, $aArgs); } return Functions::VALUE(); diff --git a/src/PhpSpreadsheet/Calculation/MathTrig/Trunc.php b/src/PhpSpreadsheet/Calculation/MathTrig/Trunc.php index 57c389e6..0a1c827e 100644 --- a/src/PhpSpreadsheet/Calculation/MathTrig/Trunc.php +++ b/src/PhpSpreadsheet/Calculation/MathTrig/Trunc.php @@ -30,7 +30,7 @@ class Trunc // Truncate $adjust = 10 ** $digits; - if (($digits > 0) && (rtrim((int) ((abs($value) - abs((int) $value)) * $adjust), '0') < $adjust / 10)) { + if (($digits > 0) && (rtrim((string) (int) ((abs($value) - abs((int) $value)) * $adjust), '0') < $adjust / 10)) { return $value; } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/SubTotalTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/SubTotalTest.php index fde1d16e..be63f694 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/SubTotalTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/SubTotalTest.php @@ -56,7 +56,12 @@ class SubTotalTest extends AllSetupTeardown 'L' => false, ]; foreach ($hiddenColumns as $col => $hidden) { - $sheet->getColumnDimension($col)->setVisible($hidden); + $colDim = $sheet->getColumnDimension($col); + if ($colDim === null) { + self::fail('Unexpected null column dimension'); + } else { + $colDim->setVisible($hidden); + } } $sheet->getCell('D2')->setValue("=SUBTOTAL($type, A1:$maxCol$maxRow)"); $result = $sheet->getCell('D2')->getCalculatedValue(); @@ -91,7 +96,12 @@ class SubTotalTest extends AllSetupTeardown '12' => false, ]; foreach ($visibleRows as $row => $visible) { - $sheet->getRowDimension($row)->setVisible($visible); + $rowDim = $sheet->getRowDimension($row); + if ($rowDim === null) { + self::fail('Unexpected null row dimension'); + } else { + $rowDim->setVisible($visible); + } } $sheet->getCell('D2')->setValue("=SUBTOTAL($type, A1:$maxCol$maxRow)"); $result = $sheet->getCell('D2')->getCalculatedValue(); From 1e8ff9f8525a79d2b766c5d986936636735750dc Mon Sep 17 00:00:00 2001 From: oleibman Date: Sat, 24 Apr 2021 09:56:58 -0700 Subject: [PATCH 06/57] DateTimeExcel - Change Names of funcWhatever to evaluate (#2015) * DateTimeExcel - Change Names of funcWhatever to evaluate Per discussions while MathTrig was being broken up, this would help standardize the code. This PR applies that standardization to the DateTimeExcel family of functions. The deprecation messages in DateTime.php are changed to match the style used in PR #2005. All Phpstan grandfathered errors (about 25) in DateTimeExcel are fixed and removed from baseline. A small number (about 5) of phpstan annotations in the source members in that directory are also fixed and eliminated. --- phpstan-baseline.neon | 135 --------- .../Calculation/Calculation.php | 46 ++-- src/PhpSpreadsheet/Calculation/DateTime.php | 256 ++++++++++++------ .../Calculation/DateTimeExcel/DateDif.php | 2 +- .../Calculation/DateTimeExcel/DateValue.php | 12 +- .../Calculation/DateTimeExcel/Datefunc.php | 8 +- .../Calculation/DateTimeExcel/Day.php | 6 +- .../Calculation/DateTimeExcel/Days.php | 2 +- .../Calculation/DateTimeExcel/Days360.php | 2 +- .../Calculation/DateTimeExcel/EDate.php | 2 +- .../Calculation/DateTimeExcel/EoMonth.php | 2 +- .../Calculation/DateTimeExcel/Helpers.php | 20 +- .../Calculation/DateTimeExcel/Hour.php | 2 +- .../Calculation/DateTimeExcel/IsoWeekNum.php | 6 +- .../Calculation/DateTimeExcel/Minute.php | 2 +- .../Calculation/DateTimeExcel/Month.php | 2 +- .../Calculation/DateTimeExcel/NetworkDays.php | 11 +- .../Calculation/DateTimeExcel/Now.php | 2 +- .../Calculation/DateTimeExcel/Second.php | 2 +- .../Calculation/DateTimeExcel/Time.php | 11 +- .../Calculation/DateTimeExcel/TimeValue.php | 4 +- .../Calculation/DateTimeExcel/Today.php | 2 +- .../Calculation/DateTimeExcel/WeekDay.php | 7 +- .../Calculation/DateTimeExcel/WeekNum.php | 2 +- .../Calculation/DateTimeExcel/WorkDay.php | 32 ++- .../Calculation/DateTimeExcel/Year.php | 2 +- .../Calculation/DateTimeExcel/YearFrac.php | 26 +- .../Calculation/Financial/Amortization.php | 6 +- .../CashFlow/Variable/NonPeriodic.php | 5 +- .../Calculation/Financial/Coupons.php | 14 +- .../Financial/Securities/AccruedInterest.php | 6 +- .../Financial/Securities/Price.php | 12 +- .../Financial/Securities/Rates.php | 4 +- .../Financial/Securities/Yields.php | 12 +- .../Calculation/Financial/TreasuryBill.php | 6 +- .../Calculation/TextData/Format.php | 6 +- src/PhpSpreadsheet/Shared/Date.php | 4 +- src/PhpSpreadsheet/Worksheet/AutoFilter.php | 2 +- .../Functions/DateTime/DateTest.php | 8 +- .../Functions/DateTime/DateValueTest.php | 14 +- .../Functions/DateTime/DaysTest.php | 4 +- .../Functions/DateTime/EDateTest.php | 4 +- .../Functions/DateTime/EoMonthTest.php | 4 +- .../Functions/DateTime/TimeTest.php | 8 +- .../Functions/DateTime/TimeValueTest.php | 4 +- .../Functions/DateTime/WeekDayTest.php | 6 +- 46 files changed, 356 insertions(+), 379 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 1b57a748..84fb7fd8 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -340,141 +340,6 @@ parameters: count: 1 path: src/PhpSpreadsheet/Calculation/Database/DatabaseAbstract.php - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\DateTime\\:\\:NETWORKDAYS\\(\\) has parameter \\$dateArgs with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/DateTime.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\DateTime\\:\\:WORKDAY\\(\\) has parameter \\$dateArgs with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/DateTime.php - - - - message: "#^Parameter \\#3 \\$subject of function str_replace expects array\\|string, string\\|null given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php - - - - message: "#^Parameter \\#1 \\$haystack of function strpos expects string, float\\|int\\|string given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php - - - - message: "#^Parameter \\#1 \\$PHPDateArray of static method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\DateTimeExcel\\\\DateValue\\:\\:finalResults\\(\\) expects array\\|false, array\\|bool given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php - - - - message: "#^Parameter \\#1 \\$testVal1 of static method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\DateTimeExcel\\\\Helpers\\:\\:adjustYear\\(\\) expects string, string\\|false given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php - - - - message: "#^Parameter \\#2 \\$testVal2 of static method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\DateTimeExcel\\\\Helpers\\:\\:adjustYear\\(\\) expects string, string\\|false given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php - - - - message: "#^Parameter \\#1 \\$value of static method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\StringHelper\\:\\:testStringAsNumeric\\(\\) expects string, float\\|int\\|string given\\.$#" - count: 2 - path: src/PhpSpreadsheet/Calculation/DateTimeExcel/Datefunc.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\DateTimeExcel\\\\Day\\:\\:weirdCondition\\(\\) has parameter \\$dateValue with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/DateTimeExcel/Day.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\DateTimeExcel\\\\Helpers\\:\\:adjustDateByMonths\\(\\) has no return typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\DateTimeExcel\\\\Helpers\\:\\:adjustDateByMonths\\(\\) has parameter \\$adjustmentMonths with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\DateTimeExcel\\\\Helpers\\:\\:adjustDateByMonths\\(\\) has parameter \\$dateValue with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php - - - - message: "#^Parameter \\#1 \\$excelTimestamp of static method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Date\\:\\:excelToTimestamp\\(\\) expects float\\|int, bool\\|float given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\DateTimeExcel\\\\Helpers\\:\\:validateNumericNull\\(\\) should return float\\|int but returns float\\|int\\|string\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\DateTimeExcel\\\\IsoWeekNum\\:\\:apparentBug\\(\\) has parameter \\$dateValue with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/DateTimeExcel/IsoWeekNum.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\DateTimeExcel\\\\NetworkDays\\:\\:funcNetworkDays\\(\\) has parameter \\$dateArgs with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/DateTimeExcel/NetworkDays.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\DateTimeExcel\\\\NetworkDays\\:\\:applySign\\(\\) has no return typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/DateTimeExcel/NetworkDays.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\DateTimeExcel\\\\Time\\:\\:toIntWithNullBool\\(\\) has parameter \\$value with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/DateTimeExcel/Time.php - - - - message: "#^Cannot access offset 0 on array\\\\|false\\.$#" - count: 3 - path: src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeValue.php - - - - message: "#^Parameter \\#1 \\$var of function count expects array\\|Countable, array\\\\|false given\\.$#" - count: 2 - path: src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeValue.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\DateTimeExcel\\\\WeekDay\\:\\:validateStyle\\(\\) has parameter \\$style with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/DateTimeExcel/WeekDay.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\DateTimeExcel\\\\WorkDay\\:\\:funcWorkDay\\(\\) has parameter \\$dateArgs with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php - - - - message: "#^Binary operation \"\\-\" between 7 and int\\|string results in an error\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php - - - - message: "#^Binary operation \"\\-\" between 7 and int\\<5, max\\>\\|string results in an error\\.$#" - count: 2 - path: src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php - - - - message: "#^Binary operation \"\\-\" between 4 and int\\<5, max\\>\\|string results in an error\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php - - - - message: "#^Binary operation \"\\-\" between int\\|string and int\\|string results in an error\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php - - - - message: "#^Binary operation \"\\*\" between 100 and int\\|string results in an error\\.$#" - count: 2 - path: src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Engine\\\\Logger\\:\\:writeDebugLog\\(\\) has parameter \\$args with no typehint specified\\.$#" count: 1 diff --git a/src/PhpSpreadsheet/Calculation/Calculation.php b/src/PhpSpreadsheet/Calculation/Calculation.php index ec85df69..97bd1779 100644 --- a/src/PhpSpreadsheet/Calculation/Calculation.php +++ b/src/PhpSpreadsheet/Calculation/Calculation.php @@ -755,17 +755,17 @@ class Calculation ], 'DATE' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\Datefunc::class, 'funcDate'], + 'functionCall' => [DateTimeExcel\Datefunc::class, 'evaluate'], 'argumentCount' => '3', ], 'DATEDIF' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\DateDif::class, 'funcDateDif'], + 'functionCall' => [DateTimeExcel\DateDif::class, 'evaluate'], 'argumentCount' => '2,3', ], 'DATEVALUE' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\DateValue::class, 'funcDateValue'], + 'functionCall' => [DateTimeExcel\DateValue::class, 'evaluate'], 'argumentCount' => '1', ], 'DAVERAGE' => [ @@ -775,17 +775,17 @@ class Calculation ], 'DAY' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\Day::class, 'funcDay'], + 'functionCall' => [DateTimeExcel\Day::class, 'evaluate'], 'argumentCount' => '1', ], 'DAYS' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\Days::class, 'funcDays'], + 'functionCall' => [DateTimeExcel\Days::class, 'evaluate'], 'argumentCount' => '2', ], 'DAYS360' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\Days360::class, 'funcDays360'], + 'functionCall' => [DateTimeExcel\Days360::class, 'evaluate'], 'argumentCount' => '2,3', ], 'DB' => [ @@ -920,7 +920,7 @@ class Calculation ], 'EDATE' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\EDate::class, 'funcEDate'], + 'functionCall' => [DateTimeExcel\EDate::class, 'evaluate'], 'argumentCount' => '2', ], 'EFFECT' => [ @@ -935,7 +935,7 @@ class Calculation ], 'EOMONTH' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\EoMonth::class, 'funcEoMonth'], + 'functionCall' => [DateTimeExcel\EoMonth::class, 'evaluate'], 'argumentCount' => '2', ], 'ERF' => [ @@ -1237,7 +1237,7 @@ class Calculation ], 'HOUR' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\Hour::class, 'funcHour'], + 'functionCall' => [DateTimeExcel\Hour::class, 'evaluate'], 'argumentCount' => '1', ], 'HYPERLINK' => [ @@ -1501,7 +1501,7 @@ class Calculation ], 'ISOWEEKNUM' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\IsoWeekNum::class, 'funcIsoWeekNum'], + 'functionCall' => [DateTimeExcel\IsoWeekNum::class, 'evaluate'], 'argumentCount' => '1', ], 'ISPMT' => [ @@ -1681,7 +1681,7 @@ class Calculation ], 'MINUTE' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\Minute::class, 'funcMinute'], + 'functionCall' => [DateTimeExcel\Minute::class, 'evaluate'], 'argumentCount' => '1', ], 'MINVERSE' => [ @@ -1721,7 +1721,7 @@ class Calculation ], 'MONTH' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\Month::class, 'funcMonth'], + 'functionCall' => [DateTimeExcel\Month::class, 'evaluate'], 'argumentCount' => '1', ], 'MROUND' => [ @@ -1761,7 +1761,7 @@ class Calculation ], 'NETWORKDAYS' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\NetworkDays::class, 'funcNetworkDays'], + 'functionCall' => [DateTimeExcel\NetworkDays::class, 'evaluate'], 'argumentCount' => '2-3', ], 'NETWORKDAYS.INTL' => [ @@ -1821,7 +1821,7 @@ class Calculation ], 'NOW' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\Now::class, 'funcNow'], + 'functionCall' => [DateTimeExcel\Now::class, 'evaluate'], 'argumentCount' => '0', ], 'NPER' => [ @@ -2175,7 +2175,7 @@ class Calculation ], 'SECOND' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\Second::class, 'funcSecond'], + 'functionCall' => [DateTimeExcel\Second::class, 'evaluate'], 'argumentCount' => '1', ], 'SEQUENCE' => [ @@ -2421,12 +2421,12 @@ class Calculation ], 'TIME' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\Time::class, 'funcTime'], + 'functionCall' => [DateTimeExcel\Time::class, 'evaluate'], 'argumentCount' => '3', ], 'TIMEVALUE' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\TimeValue::class, 'funcTimeValue'], + 'functionCall' => [DateTimeExcel\TimeValue::class, 'evaluate'], 'argumentCount' => '1', ], 'TINV' => [ @@ -2446,7 +2446,7 @@ class Calculation ], 'TODAY' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\Today::class, 'funcToday'], + 'functionCall' => [DateTimeExcel\Today::class, 'evaluate'], 'argumentCount' => '0', ], 'TRANSPOSE' => [ @@ -2571,12 +2571,12 @@ class Calculation ], 'WEEKDAY' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\WeekDay::class, 'funcWeekDay'], + 'functionCall' => [DateTimeExcel\WeekDay::class, 'evaluate'], 'argumentCount' => '1,2', ], 'WEEKNUM' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\WeekNum::class, 'funcWeekNum'], + 'functionCall' => [DateTimeExcel\WeekNum::class, 'evaluate'], 'argumentCount' => '1,2', ], 'WEIBULL' => [ @@ -2591,7 +2591,7 @@ class Calculation ], 'WORKDAY' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\WorkDay::class, 'funcWorkDay'], + 'functionCall' => [DateTimeExcel\WorkDay::class, 'evaluate'], 'argumentCount' => '2-3', ], 'WORKDAY.INTL' => [ @@ -2626,12 +2626,12 @@ class Calculation ], 'YEAR' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\Year::class, 'funcYear'], + 'functionCall' => [DateTimeExcel\Year::class, 'evaluate'], 'argumentCount' => '1', ], 'YEARFRAC' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\YearFrac::class, 'funcYearFrac'], + 'functionCall' => [DateTimeExcel\YearFrac::class, 'evaluate'], 'argumentCount' => '2,3', ], 'YIELD' => [ diff --git a/src/PhpSpreadsheet/Calculation/DateTime.php b/src/PhpSpreadsheet/Calculation/DateTime.php index 7643ed0b..c0a38f56 100644 --- a/src/PhpSpreadsheet/Calculation/DateTime.php +++ b/src/PhpSpreadsheet/Calculation/DateTime.php @@ -9,7 +9,10 @@ class DateTime /** * Identify if a year is a leap year or not. * - * @Deprecated 2.0.0 Use the method isLeapYear in the DateTimeExcel\Helpers class instead + * @Deprecated 1.18.0 + * + * @See DateTimeExcel\Helpers::isLeapYear() + * Use the isLeapYear method in the DateTimeExcel\Helpers class instead * * @param int|string $year The year to test * @@ -23,7 +26,10 @@ class DateTime /** * getDateValue. * - * @Deprecated 2.0.0 Use the method getDateValue in the DateTimeExcel\Helpers class instead + * @Deprecated 1.18.0 + * + * @See DateTimeExcel\Helpers::getDateValue() + * Use the getDateValue method in the DateTimeExcel\Helpers class instead * * @param mixed $dateValue * @@ -49,17 +55,20 @@ class DateTime * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date * and time format of your regional settings. PhpSpreadsheet does not change cell formatting in this way. * - * @Deprecated 2.0.0 Use the funcNow method in the DateTimeExcel\Now class instead - * * Excel Function: * NOW() * + * @Deprecated 1.18.0 + * + * @See DateTimeExcel\Now::evaluate() + * Use the evaluate method in the DateTimeExcel\Now class instead + * * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object, * depending on the value of the ReturnDateType flag */ public static function DATETIMENOW() { - return DateTimeExcel\Now::funcNow(); + return DateTimeExcel\Now::evaluate(); } /** @@ -73,17 +82,20 @@ class DateTime * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date * and time format of your regional settings. PhpSpreadsheet does not change cell formatting in this way. * - * @Deprecated 2.0.0 Use the funcToday method in the DateTimeExcel\Today class instead - * * Excel Function: * TODAY() * + * @Deprecated 1.18.0 + * + * @See DateTimeExcel\Today::evaluate() + * Use the evaluate method in the DateTimeExcel\Today class instead + * * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object, * depending on the value of the ReturnDateType flag */ public static function DATENOW() { - return DateTimeExcel\Today::funcToday(); + return DateTimeExcel\Today::evaluate(); } /** @@ -94,11 +106,15 @@ class DateTime * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date * format of your regional settings. PhpSpreadsheet does not change cell formatting in this way. * - * @Deprecated 2.0.0 Use the funcDate method in the DateTimeExcel\Date class instead * * Excel Function: * DATE(year,month,day) * + * @Deprecated 1.18.0 + * + * @See DateTimeExcel\Datefunc::evaluate() + * Use the evaluate method in the DateTimeExcel\Datefunc class instead + * * PhpSpreadsheet is a lot more forgiving than MS Excel when passing non numeric values to this function. * A Month name or abbreviation (English only at this point) such as 'January' or 'Jan' will still be accepted, * as will a day value with a suffix (e.g. '21st' rather than simply 21); again only English language. @@ -139,7 +155,7 @@ class DateTime */ public static function DATE($year = 0, $month = 1, $day = 1) { - return DateTimeExcel\Datefunc::funcDate($year, $month, $day); + return DateTimeExcel\Datefunc::evaluate($year, $month, $day); } /** @@ -150,11 +166,14 @@ class DateTime * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the time * format of your regional settings. PhpSpreadsheet does not change cell formatting in this way. * - * @Deprecated 2.0.0 Use the funcTime method in the DateTimeExcel\Time class instead - * * Excel Function: * TIME(hour,minute,second) * + * @Deprecated 1.18.0 + * + * @See DateTimeExcel\Time::evaluate() + * Use the evaluate method in the DateTimeExcel\Time class instead + * * @param int $hour A number from 0 (zero) to 32767 representing the hour. * Any value greater than 23 will be divided by 24 and the remainder * will be treated as the hour value. For example, TIME(27,0,0) = @@ -172,7 +191,7 @@ class DateTime */ public static function TIME($hour = 0, $minute = 0, $second = 0) { - return DateTimeExcel\Time::funcTime($hour, $minute, $second); + return DateTimeExcel\Time::evaluate($hour, $minute, $second); } /** @@ -185,11 +204,14 @@ class DateTime * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date * format of your regional settings. PhpSpreadsheet does not change cell formatting in this way. * - * @Deprecated 2.0.0 Use the funcDateValue method in the DateTimeExcel\DateValue class instead - * * Excel Function: * DATEVALUE(dateValue) * + * @Deprecated 1.18.0 + * + * @See DateTimeExcel\DateValue::evaluate() + * Use the evaluate method in the DateTimeExcel\DateValue class instead + * * @param string $dateValue Text that represents a date in a Microsoft Excel date format. * For example, "1/30/2008" or "30-Jan-2008" are text strings within * quotation marks that represent dates. Using the default date @@ -204,7 +226,7 @@ class DateTime */ public static function DATEVALUE($dateValue) { - return DateTimeExcel\DateValue::funcDateValue($dateValue); + return DateTimeExcel\DateValue::evaluate($dateValue); } /** @@ -217,11 +239,14 @@ class DateTime * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the time * format of your regional settings. PhpSpreadsheet does not change cell formatting in this way. * - * @Deprecated 2.0.0 Use the funcTimeValue method in the DateTimeExcel\TimeValue class instead - * * Excel Function: * TIMEVALUE(timeValue) * + * @Deprecated 1.18.0 + * + * @See DateTimeExcel\TimeValue::evaluate() + * Use the evaluate method in the DateTimeExcel\TimeValue class instead + * * @param string $timeValue A text string that represents a time in any one of the Microsoft * Excel time formats; for example, "6:45 PM" and "18:45" text strings * within quotation marks that represent time. @@ -232,13 +257,19 @@ class DateTime */ public static function TIMEVALUE($timeValue) { - return DateTimeExcel\TimeValue::funcTimeValue($timeValue); + return DateTimeExcel\TimeValue::evaluate($timeValue); } /** * DATEDIF. * - * @Deprecated 2.0.0 Use the funcDateDif method in the DateTimeExcel\DateDif class instead + * Excel Function: + * DATEDIF(startdate, enddate, unit) + * + * @Deprecated 1.18.0 + * + * @See DateTimeExcel\DateDif::evaluate() + * Use the evaluate method in the DateTimeExcel\DateDif class instead * * @param mixed $startDate Excel date serial value, PHP date/time stamp, PHP DateTime object * or a standard date string @@ -250,7 +281,7 @@ class DateTime */ public static function DATEDIF($startDate = 0, $endDate = 0, $unit = 'D') { - return DateTimeExcel\DateDif::funcDateDif($startDate, $endDate, $unit); + return DateTimeExcel\DateDif::evaluate($startDate, $endDate, $unit); } /** @@ -258,11 +289,14 @@ class DateTime * * Returns the number of days between two dates * - * @Deprecated 2.0.0 Use the funcDays method in the DateTimeExcel\Days class instead - * * Excel Function: * DAYS(endDate, startDate) * + * @Deprecated 1.18.0 + * + * @See DateTimeExcel\Days::evaluate() + * Use the evaluate method in the DateTimeExcel\Days class instead + * * @param DateTimeInterface|float|int|string $endDate Excel date serial value (float), * PHP date timestamp (integer), PHP DateTime object, or a standard date string * @param DateTimeInterface|float|int|string $startDate Excel date serial value (float), @@ -272,7 +306,7 @@ class DateTime */ public static function DAYS($endDate = 0, $startDate = 0) { - return DateTimeExcel\Days::funcDays($endDate, $startDate); + return DateTimeExcel\Days::evaluate($endDate, $startDate); } /** @@ -282,11 +316,14 @@ class DateTime * which is used in some accounting calculations. Use this function to help compute payments if * your accounting system is based on twelve 30-day months. * - * @Deprecated 2.0.0 Use the funcDays360 method in the DateTimeExcel\Days360 class instead - * * Excel Function: * DAYS360(startDate,endDate[,method]) * + * @Deprecated 1.18.0 + * + * @See DateTimeExcel\Days360::evaluate() + * Use the evaluate method in the DateTimeExcel\Days360 class instead + * * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer), * PHP DateTime object, or a standard date string * @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer), @@ -307,7 +344,7 @@ class DateTime */ public static function DAYS360($startDate = 0, $endDate = 0, $method = false) { - return DateTimeExcel\Days360::funcDays360($startDate, $endDate, $method); + return DateTimeExcel\Days360::evaluate($startDate, $endDate, $method); } /** @@ -318,10 +355,14 @@ class DateTime * Use the YEARFRAC worksheet function to identify the proportion of a whole year's benefits or * obligations to assign to a specific term. * - * @Deprecated 2.0.0 Use the funcYearFrac method in the DateTimeExcel\YearFrac class instead - * * Excel Function: * YEARFRAC(startDate,endDate[,method]) + * + * @Deprecated 1.18.0 + * + * @See DateTimeExcel\YearFrac::evaluate() + * Use the evaluate method in the DateTimeExcel\YearFrac class instead + * * See https://lists.oasis-open.org/archives/office-formula/200806/msg00039.html * for description of algorithm used in Excel * @@ -340,7 +381,7 @@ class DateTime */ public static function YEARFRAC($startDate = 0, $endDate = 0, $method = 0) { - return DateTimeExcel\YearFrac::funcYearFrac($startDate, $endDate, $method); + return DateTimeExcel\YearFrac::evaluate($startDate, $endDate, $method); } /** @@ -351,21 +392,25 @@ class DateTime * Use NETWORKDAYS to calculate employee benefits that accrue based on the number of days * worked during a specific term. * - * @Deprecated 2.0.0 Use the funcNetworkDays method in the DateTimeExcel\NetworkDays class instead - * * Excel Function: * NETWORKDAYS(startDate,endDate[,holidays[,holiday[,...]]]) * + * @Deprecated 1.18.0 + * + * @See DateTimeExcel\NetworkDays::evaluate() + * Use the evaluate method in the DateTimeExcel\NetworkDays class instead + * * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer), * PHP DateTime object, or a standard date string * @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer), * PHP DateTime object, or a standard date string + * @param mixed $dateArgs * * @return int|string Interval between the dates */ public static function NETWORKDAYS($startDate, $endDate, ...$dateArgs) { - return DateTimeExcel\NetworkDays::funcNetworkDays($startDate, $endDate, ...$dateArgs); + return DateTimeExcel\NetworkDays::evaluate($startDate, $endDate, ...$dateArgs); } /** @@ -376,23 +421,27 @@ class DateTime * Use WORKDAY to exclude weekends or holidays when you calculate invoice due dates, expected * delivery times, or the number of days of work performed. * - * @Deprecated 2.0.0 Use the funcWorkDay method in the DateTimeExcel\WorkDay class instead - * * Excel Function: * WORKDAY(startDate,endDays[,holidays[,holiday[,...]]]) * + * @Deprecated 1.18.0 + * + * @See DateTimeExcel\WorkDay::evaluate() + * Use the evaluate method in the DateTimeExcel\WorkDay class instead + * * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer), * PHP DateTime object, or a standard date string * @param int $endDays The number of nonweekend and nonholiday days before or after * startDate. A positive value for days yields a future date; a * negative value yields a past date. + * @param mixed $dateArgs * * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object, * depending on the value of the ReturnDateType flag */ public static function WORKDAY($startDate, $endDays, ...$dateArgs) { - return DateTimeExcel\WorkDay::funcWorkDay($startDate, $endDays, ...$dateArgs); + return DateTimeExcel\WorkDay::evaluate($startDate, $endDays, ...$dateArgs); } /** @@ -401,11 +450,14 @@ class DateTime * Returns the day of the month, for a specified date. The day is given as an integer * ranging from 1 to 31. * - * @Deprecated 2.0.0 Use the funcDay method in the DateTimeExcel\Day class instead - * * Excel Function: * DAY(dateValue) * + * @Deprecated 1.18.0 + * + * @See DateTimeExcel\Day::evaluate() + * Use the evaluate method in the DateTimeExcel\Day class instead + * * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer), * PHP DateTime object, or a standard date string * @@ -413,7 +465,7 @@ class DateTime */ public static function DAYOFMONTH($dateValue = 1) { - return DateTimeExcel\Day::funcDay($dateValue); + return DateTimeExcel\Day::evaluate($dateValue); } /** @@ -422,11 +474,14 @@ class DateTime * Returns the day of the week for a specified date. The day is given as an integer * ranging from 0 to 7 (dependent on the requested style). * - * @Deprecated 2.0.0 Use the funcWeekDay method in the DateTimeExcel\WeekDay class instead - * * Excel Function: * WEEKDAY(dateValue[,style]) * + * @Deprecated 1.18.0 + * + * @See DateTimeExcel\WeekDay::evaluate() + * Use the evaluate method in the DateTimeExcel\WeekDay class instead + * * @param float|int|string $dateValue Excel date serial value (float), PHP date timestamp (integer), * PHP DateTime object, or a standard date string * @param int $style A number that determines the type of return value @@ -438,13 +493,13 @@ class DateTime */ public static function WEEKDAY($dateValue = 1, $style = 1) { - return DateTimeExcel\WeekDay::funcWeekDay($dateValue, $style); + return DateTimeExcel\WeekDay::evaluate($dateValue, $style); } /** * STARTWEEK_SUNDAY. * - * @Deprecated 2.0.0 + * @Deprecated 1.18.0 * * @see Use DateTimeExcel\Constants\STARTWEEK_SUNDAY instead */ @@ -453,7 +508,7 @@ class DateTime /** * STARTWEEK_MONDAY. * - * @Deprecated 2.0.0 + * @Deprecated 1.18.0 * * @see Use DateTimeExcel\Constants\STARTWEEK_MONDAY instead */ @@ -462,7 +517,7 @@ class DateTime /** * STARTWEEK_MONDAY_ALT. * - * @Deprecated 2.0.0 + * @Deprecated 1.18.0 * * @see Use DateTimeExcel\Constants\STARTWEEK_MONDAY_ALT instead */ @@ -471,7 +526,7 @@ class DateTime /** * STARTWEEK_TUESDAY. * - * @Deprecated 2.0.0 + * @Deprecated 1.18.0 * * @see Use DateTimeExcel\Constants\STARTWEEK_TUESDAY instead */ @@ -480,7 +535,7 @@ class DateTime /** * STARTWEEK_WEDNESDAY. * - * @Deprecated 2.0.0 + * @Deprecated 1.18.0 * * @see Use DateTimeExcel\Constants\STARTWEEK_WEDNESDAY instead */ @@ -489,7 +544,7 @@ class DateTime /** * STARTWEEK_THURSDAY. * - * @Deprecated 2.0.0 + * @Deprecated 1.18.0 * * @see Use DateTimeExcel\Constants\STARTWEEK_THURSDAY instead */ @@ -498,7 +553,7 @@ class DateTime /** * STARTWEEK_FRIDAY. * - * @Deprecated 2.0.0 + * @Deprecated 1.18.0 * * @see Use DateTimeExcel\Constants\STARTWEEK_FRIDAY instead */ @@ -507,7 +562,7 @@ class DateTime /** * STARTWEEK_SATURDAY. * - * @Deprecated 2.0.0 + * @Deprecated 1.18.0 * * @see Use DateTimeExcel\Constants\STARTWEEK_SATURDAY instead */ @@ -516,7 +571,7 @@ class DateTime /** * STARTWEEK_SUNDAY_ALT. * - * @Deprecated 2.0.0 + * @Deprecated 1.18.0 * * @see Use DateTimeExcel\Constants\STARTWEEK_SUNDAY_ALT instead */ @@ -525,7 +580,7 @@ class DateTime /** * DOW_SUNDAY. * - * @Deprecated 2.0.0 + * @Deprecated 1.18.0 * * @see Use DateTimeExcel\Constants\DOW_SUNDAY instead */ @@ -534,7 +589,7 @@ class DateTime /** * DOW_MONDAY. * - * @Deprecated 2.0.0 + * @Deprecated 1.18.0 * * @see Use DateTimeExcel\Constants\DOW_MONDAY instead */ @@ -543,7 +598,7 @@ class DateTime /** * DOW_TUESDAY. * - * @Deprecated 2.0.0 + * @Deprecated 1.18.0 * * @see Use DateTimeExcel\Constants\DOW_TUESDAY instead */ @@ -552,7 +607,7 @@ class DateTime /** * DOW_WEDNESDAY. * - * @Deprecated 2.0.0 + * @Deprecated 1.18.0 * * @see Use DateTimeExcel\Constants\DOW_WEDNESDAY instead */ @@ -561,7 +616,7 @@ class DateTime /** * DOW_THURSDAY. * - * @Deprecated 2.0.0 + * @Deprecated 1.18.0 * * @see Use DateTimeExcel\Constants\DOW_THURSDAY instead */ @@ -570,7 +625,7 @@ class DateTime /** * DOW_FRIDAY. * - * @Deprecated 2.0.0 + * @Deprecated 1.18.0 * * @see Use DateTimeExcel\Constants\DOW_FRIDAY instead */ @@ -579,7 +634,7 @@ class DateTime /** * DOW_SATURDAY. * - * @Deprecated 2.0.0 + * @Deprecated 1.18.0 * * @see Use DateTimeExcel\Constants\DOW_SATURDAY instead */ @@ -588,7 +643,7 @@ class DateTime /** * STARTWEEK_MONDAY_ISO. * - * @Deprecated 2.0.0 + * @Deprecated 1.18.0 * * @see Use DateTimeExcel\Constants\STARTWEEK_MONDAY_ISO instead */ @@ -597,7 +652,7 @@ class DateTime /** * METHODARR. * - * @Deprecated 2.0.0 + * @Deprecated 1.18.0 * * @see Use DateTimeExcel\Constants\METHODARR instead */ @@ -624,11 +679,14 @@ class DateTime * three days or less in the first week of January, the WEEKNUM function returns week numbers * that are incorrect according to the European standard. * - * @Deprecated 2.0.0 Use the funcWeekNum method in the DateTimeExcel\WeekNum class instead - * * Excel Function: * WEEKNUM(dateValue[,style]) * + * @Deprecated 1.18.0 + * + * @See DateTimeExcel\WeekNum::evaluate() + * Use the evaluate method in the DateTimeExcel\WeekNum class instead + * * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer), * PHP DateTime object, or a standard date string * @param int $method Week begins on Sunday or Monday @@ -647,7 +705,7 @@ class DateTime */ public static function WEEKNUM($dateValue = 1, $method = self::STARTWEEK_SUNDAY) { - return DateTimeExcel\WeekNum::funcWeekNum($dateValue, $method); + return DateTimeExcel\WeekNum::evaluate($dateValue, $method); } /** @@ -655,11 +713,14 @@ class DateTime * * Returns the ISO 8601 week number of the year for a specified date. * - * @Deprecated 2.0.0 Use the funcIsoWeeknum method in the DateTimeExcel\IsoWeekNum class instead - * * Excel Function: * ISOWEEKNUM(dateValue) * + * @Deprecated 1.18.0 + * + * @See DateTimeExcel\IsoWeekNum::evaluate() + * Use the evaluate method in the DateTimeExcel\IsoWeekNum class instead + * * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer), * PHP DateTime object, or a standard date string * @@ -667,7 +728,7 @@ class DateTime */ public static function ISOWEEKNUM($dateValue = 1) { - return DateTimeExcel\IsoWeekNum::funcIsoWeekNum($dateValue); + return DateTimeExcel\IsoWeekNum::evaluate($dateValue); } /** @@ -676,11 +737,14 @@ class DateTime * Returns the month of a date represented by a serial number. * The month is given as an integer, ranging from 1 (January) to 12 (December). * - * @Deprecated 2.0.0 Use the funcMonth method in the DateTimeExcel\Month class instead - * * Excel Function: * MONTH(dateValue) * + * @Deprecated 1.18.0 + * + * @See DateTimeExcel\Month::evaluate() + * Use the evaluate method in the DateTimeExcel\Month class instead + * * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer), * PHP DateTime object, or a standard date string * @@ -688,7 +752,7 @@ class DateTime */ public static function MONTHOFYEAR($dateValue = 1) { - return DateTimeExcel\Month::funcMonth($dateValue); + return DateTimeExcel\Month::evaluate($dateValue); } /** @@ -697,11 +761,14 @@ class DateTime * Returns the year corresponding to a date. * The year is returned as an integer in the range 1900-9999. * - * @Deprecated 2.0.0 Use the funcYear method in the DateTimeExcel\Year class instead - * * Excel Function: * YEAR(dateValue) * + * @Deprecated 1.18.0 + * + * @See DateTimeExcel\Year::evaluate() + * Use the evaluate method in the DateTimeExcel\Year class instead + * * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer), * PHP DateTime object, or a standard date string * @@ -709,7 +776,7 @@ class DateTime */ public static function YEAR($dateValue = 1) { - return DateTimeExcel\Year::funcYear($dateValue); + return DateTimeExcel\Year::evaluate($dateValue); } /** @@ -718,11 +785,14 @@ class DateTime * Returns the hour of a time value. * The hour is given as an integer, ranging from 0 (12:00 A.M.) to 23 (11:00 P.M.). * - * @Deprecated 2.0.0 Use the funcHour method in the DateTimeExcel\Hour class instead - * * Excel Function: * HOUR(timeValue) * + * @Deprecated 1.18.0 + * + * @See DateTimeExcel\Hour::evaluate() + * Use the evaluate method in the DateTimeExcel\Hour class instead + * * @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer), * PHP DateTime object, or a standard time string * @@ -730,7 +800,7 @@ class DateTime */ public static function HOUROFDAY($timeValue = 0) { - return DateTimeExcel\Hour::funcHour($timeValue); + return DateTimeExcel\Hour::evaluate($timeValue); } /** @@ -739,11 +809,14 @@ class DateTime * Returns the minutes of a time value. * The minute is given as an integer, ranging from 0 to 59. * - * @Deprecated 2.0.0 Use the funcMinute method in the DateTimeExcel\Minute class instead - * * Excel Function: * MINUTE(timeValue) * + * @Deprecated 1.18.0 + * + * @See DateTimeExcel\Minute::evaluate() + * Use the evaluate method in the DateTimeExcel\Minute class instead + * * @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer), * PHP DateTime object, or a standard time string * @@ -751,7 +824,7 @@ class DateTime */ public static function MINUTE($timeValue = 0) { - return DateTimeExcel\Minute::funcMinute($timeValue); + return DateTimeExcel\Minute::evaluate($timeValue); } /** @@ -760,11 +833,14 @@ class DateTime * Returns the seconds of a time value. * The second is given as an integer in the range 0 (zero) to 59. * - * @Deprecated 2.0.0 Use the funcSecond method in the DateTimeExcel\Second class instead - * * Excel Function: * SECOND(timeValue) * + * @Deprecated 1.18.0 + * + * @See DateTimeExcel\Second::evaluate() + * Use the evaluate method in the DateTimeExcel\Second class instead + * * @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer), * PHP DateTime object, or a standard time string * @@ -772,7 +848,7 @@ class DateTime */ public static function SECOND($timeValue = 0) { - return DateTimeExcel\Second::funcSecond($timeValue); + return DateTimeExcel\Second::evaluate($timeValue); } /** @@ -783,11 +859,14 @@ class DateTime * Use EDATE to calculate maturity dates or due dates that fall on the same day of the month * as the date of issue. * - * @Deprecated 2.0.0 Use the funcEDate method in the DateTimeExcel\EDate class instead - * * Excel Function: * EDATE(dateValue,adjustmentMonths) * + * @Deprecated 1.18.0 + * + * @See DateTimeExcel\Edate::evaluate() + * Use the evaluate method in the DateTimeExcel\Edate class instead + * * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer), * PHP DateTime object, or a standard date string * @param int $adjustmentMonths The number of months before or after start_date. @@ -799,7 +878,7 @@ class DateTime */ public static function EDATE($dateValue = 1, $adjustmentMonths = 0) { - return DateTimeExcel\EDate::funcEDate($dateValue, $adjustmentMonths); + return DateTimeExcel\EDate::evaluate($dateValue, $adjustmentMonths); } /** @@ -809,11 +888,14 @@ class DateTime * before or after start_date. * Use EOMONTH to calculate maturity dates or due dates that fall on the last day of the month. * - * @Deprecated 2.0.0 Use the funcEoMonth method in the DateTimeExcel\EoMonth class instead - * * Excel Function: * EOMONTH(dateValue,adjustmentMonths) * + * @Deprecated 1.18.0 + * + * @See DateTimeExcel\EoMonth::evaluate() + * Use the evaluate method in the DateTimeExcel\EoMonth class instead + * * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer), * PHP DateTime object, or a standard date string * @param int $adjustmentMonths The number of months before or after start_date. @@ -825,6 +907,6 @@ class DateTime */ public static function EOMONTH($dateValue = 1, $adjustmentMonths = 0) { - return DateTimeExcel\EoMonth::funcEoMonth($dateValue, $adjustmentMonths); + return DateTimeExcel\EoMonth::evaluate($dateValue, $adjustmentMonths); } } diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateDif.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateDif.php index c0d1fa4b..26eb1dbf 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateDif.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateDif.php @@ -21,7 +21,7 @@ class DateDif * * @return int|string Interval between the dates */ - public static function funcDateDif($startDate, $endDate, $unit = 'D') + public static function evaluate($startDate, $endDate, $unit = 'D') { try { $startDate = Helpers::getDateValue($startDate); diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php index 86b8d3d9..ee872f31 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php @@ -33,13 +33,13 @@ class DateValue * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object, * depending on the value of the ReturnDateType flag */ - public static function funcDateValue($dateValue) + public static function evaluate($dateValue) { $dti = new DateTimeImmutable(); $baseYear = Date::getExcelCalendar(); $dateValue = trim(Functions::flattenSingleValue($dateValue), '"'); // Strip any ordinals because they're allowed in Excel (English only) - $dateValue = preg_replace('/(\d)(st|nd|rd|th)([ -\/])/Ui', '$1$3', $dateValue); + $dateValue = preg_replace('/(\d)(st|nd|rd|th)([ -\/])/Ui', '$1$3', $dateValue) ?? ''; // Convert separators (/ . or space) to hyphens (should also handle dot used for ordinals in some countries, e.g. Denmark, Germany) $dateValue = str_replace(['/', '.', '-', ' '], ' ', $dateValue); @@ -59,7 +59,7 @@ class DateValue } if (count($t1) === 1) { // We've been fed a time value without any date - return ((strpos($t, ':') === false)) ? Functions::Value() : 0.0; + return ((strpos((string) $t, ':') === false)) ? Functions::Value() : 0.0; } unset($t); @@ -105,7 +105,7 @@ class DateValue $testVal1 = strtok($dateValue, '- '); $testVal2 = strtok('- '); $testVal3 = strtok('- ') ?: $dti->format('Y'); - Helpers::adjustYear($testVal1, $testVal2, $testVal3); + Helpers::adjustYear((string) $testVal1, (string) $testVal2, $testVal3); $PHPDateArray = date_parse($testVal1 . '-' . $testVal2 . '-' . $testVal3); if (($PHPDateArray === false) || ($PHPDateArray['error_count'] > 0)) { $PHPDateArray = date_parse($testVal2 . '-' . $testVal1 . '-' . $testVal3); @@ -118,7 +118,7 @@ class DateValue /** * Final results. * - * @param array|false $PHPDateArray + * @param array|bool $PHPDateArray * * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object, * depending on the value of the ReturnDateType flag @@ -126,7 +126,7 @@ class DateValue private static function finalResults($PHPDateArray, DateTimeImmutable $dti, int $baseYear) { $retValue = Functions::Value(); - if (($PHPDateArray !== false) && ($PHPDateArray['error_count'] == 0)) { + if (is_array($PHPDateArray) && $PHPDateArray['error_count'] == 0) { // Execute function Helpers::replaceIfEmpty($PHPDateArray['year'], $dti->format('Y')); if ($PHPDateArray['year'] < $baseYear) { diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Datefunc.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Datefunc.php index ec8be2df..e8aa02cb 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Datefunc.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Datefunc.php @@ -58,7 +58,7 @@ class Datefunc * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object, * depending on the value of the ReturnDateType flag */ - public static function funcDate($year, $month, $day) + public static function evaluate($year, $month, $day) { $baseYear = Date::getExcelCalendar(); @@ -85,7 +85,7 @@ class Datefunc private static function getYear($year, int $baseYear): int { $year = Functions::flattenSingleValue($year); - $year = ($year !== null) ? StringHelper::testStringAsNumeric($year) : 0; + $year = ($year !== null) ? StringHelper::testStringAsNumeric((string) $year) : 0; if (!is_numeric($year)) { throw new Exception(Functions::VALUE()); } @@ -118,7 +118,7 @@ class Datefunc $month = Date::monthStringToNumber($month); } - $month = ($month !== null) ? StringHelper::testStringAsNumeric($month) : 0; + $month = ($month !== null) ? StringHelper::testStringAsNumeric((string) $month) : 0; if (!is_numeric($month)) { throw new Exception(Functions::VALUE()); } @@ -139,7 +139,7 @@ class Datefunc $day = Date::dayStringToNumber($day); } - $day = ($day !== null) ? StringHelper::testStringAsNumeric($day) : 0; + $day = ($day !== null) ? StringHelper::testStringAsNumeric((string) $day) : 0; if (!is_numeric($day)) { throw new Exception(Functions::VALUE()); } diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Day.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Day.php index 6ab27184..bc57644d 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Day.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Day.php @@ -22,7 +22,7 @@ class Day * * @return int|string Day of the month */ - public static function funcDay($dateValue) + public static function evaluate($dateValue) { $weirdResult = self::weirdCondition($dateValue); if ($weirdResult >= 0) { @@ -41,6 +41,10 @@ class Day return (int) $PHPDateObject->format('j'); } + /** + * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer), + * PHP DateTime object, or a standard date string + */ private static function weirdCondition($dateValue): int { // Excel does not treat 0 consistently for DAY vs. (MONTH or YEAR) diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days.php index 2c814e8e..bd37e481 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days.php @@ -24,7 +24,7 @@ class Days * * @return int|string Number of days between start date and end date or an error */ - public static function funcDays($endDate, $startDate) + public static function evaluate($endDate, $startDate) { try { $startDate = Helpers::getDateValue($startDate); diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days360.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days360.php index 47de02c3..74de95ad 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days360.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days360.php @@ -36,7 +36,7 @@ class Days360 * * @return int|string Number of days between start date and end date */ - public static function funcDays360($startDate = 0, $endDate = 0, $method = false) + public static function evaluate($startDate = 0, $endDate = 0, $method = false) { try { $startDate = Helpers::getDateValue($startDate); diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/EDate.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/EDate.php index 43af694f..bb234097 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/EDate.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/EDate.php @@ -27,7 +27,7 @@ class EDate * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object, * depending on the value of the ReturnDateType flag */ - public static function funcEDate($dateValue, $adjustmentMonths) + public static function evaluate($dateValue, $adjustmentMonths) { try { $dateValue = Helpers::getDateValue($dateValue, false); diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/EoMonth.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/EoMonth.php index 6b39a609..54449c5d 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/EoMonth.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/EoMonth.php @@ -26,7 +26,7 @@ class EoMonth * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object, * depending on the value of the ReturnDateType flag */ - public static function funcEoMonth($dateValue, $adjustmentMonths) + public static function evaluate($dateValue, $adjustmentMonths) { try { $dateValue = Helpers::getDateValue($dateValue, false); diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php index 5b3a8067..00f12ddc 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php @@ -43,7 +43,7 @@ class Helpers if (!is_numeric($dateValue)) { $saveReturnDateType = Functions::getReturnDateType(); Functions::setReturnDateType(Functions::RETURNDATE_EXCEL); - $dateValue = DateValue::funcDateValue($dateValue); + $dateValue = DateValue::evaluate($dateValue); Functions::setReturnDateType($saveReturnDateType); if (!is_numeric($dateValue)) { throw new Exception(Functions::VALUE()); @@ -67,13 +67,18 @@ class Helpers { $saveReturnDateType = Functions::getReturnDateType(); Functions::setReturnDateType(Functions::RETURNDATE_EXCEL); - $timeValue = TimeValue::funcTimeValue($timeValue); + $timeValue = TimeValue::evaluate($timeValue); Functions::setReturnDateType($saveReturnDateType); return $timeValue; } - public static function adjustDateByMonths($dateValue = 0, $adjustmentMonths = 0) + /** + * Adjust date by given months. + * + * @param mixed $dateValue + */ + public static function adjustDateByMonths($dateValue = 0, float $adjustmentMonths = 0): DateTime { // Execute function $PHPDateObject = Date::excelToDateTimeObject($dateValue); @@ -194,8 +199,10 @@ class Helpers return (float) Date::PHPToExcel($PHPDateObject); } // RETURNDATE_UNIX_TIMESTAMP + $stamp = Date::PHPToExcel($PHPDateObject); + $stamp = is_bool($stamp) ? ((int) $stamp) : $stamp; - return (int) Date::excelToTimestamp(Date::PHPToExcel($PHPDateObject)); + return (int) Date::excelToTimestamp($stamp); } private static function baseDate(): int @@ -239,9 +246,12 @@ class Helpers if ($number === null) { return 0; } - if (is_numeric($number)) { + if (is_int($number)) { return $number; } + if (is_numeric($number)) { + return (float) $number; + } throw new Exception(Functions::VALUE()); } diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Hour.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Hour.php index 98d4570d..84869036 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Hour.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Hour.php @@ -22,7 +22,7 @@ class Hour * * @return int|string Hour */ - public static function funcHour($timeValue) + public static function evaluate($timeValue) { try { $timeValue = Functions::flattenSingleValue($timeValue); diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/IsoWeekNum.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/IsoWeekNum.php index 41959d9a..7d2d65dc 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/IsoWeekNum.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/IsoWeekNum.php @@ -20,7 +20,7 @@ class IsoWeekNum * * @return int|string Week Number */ - public static function funcIsoWeekNum($dateValue) + public static function evaluate($dateValue) { if (self::apparentBug($dateValue)) { return 52; @@ -39,6 +39,10 @@ class IsoWeekNum return (int) $PHPDateObject->format('W'); } + /** + * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer), + * PHP DateTime object, or a standard date string + */ private static function apparentBug($dateValue): bool { if (Date::getExcelCalendar() !== DATE::CALENDAR_MAC_1904) { diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Minute.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Minute.php index a1747ec9..1a9d5c21 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Minute.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Minute.php @@ -22,7 +22,7 @@ class Minute * * @return int|string Minute */ - public static function funcMinute($timeValue) + public static function evaluate($timeValue) { try { $timeValue = Functions::flattenSingleValue($timeValue); diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Month.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Month.php index a9fb8ece..092370ba 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Month.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Month.php @@ -21,7 +21,7 @@ class Month * * @return int|string Month of the year */ - public static function funcMonth($dateValue) + public static function evaluate($dateValue) { try { $dateValue = Helpers::getDateValue($dateValue); diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/NetworkDays.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/NetworkDays.php index c700c834..5c586913 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/NetworkDays.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/NetworkDays.php @@ -23,10 +23,11 @@ class NetworkDays * PHP DateTime object, or a standard date string * @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer), * PHP DateTime object, or a standard date string + * @param mixed $dateArgs * * @return int|string Interval between the dates */ - public static function funcNetworkDays($startDate, $endDate, ...$dateArgs) + public static function evaluate($startDate, $endDate, ...$dateArgs) { try { // Retrieve the mandatory start and end date that are referenced in the function definition @@ -55,7 +56,7 @@ class NetworkDays $holidayCountedArray = []; foreach ($holidayArray as $holidayDate) { if (($holidayDate >= $startDate) && ($holidayDate <= $endDate)) { - if ((WeekDay::funcWeekDay($holidayDate, 2) < 6) && (!in_array($holidayDate, $holidayCountedArray))) { + if ((WeekDay::evaluate($holidayDate, 2) < 6) && (!in_array($holidayDate, $holidayCountedArray))) { --$partWeekDays; $holidayCountedArray[] = $holidayDate; } @@ -67,7 +68,7 @@ class NetworkDays private static function calcStartDow(float $startDate): int { - $startDow = 6 - (int) WeekDay::funcWeekDay($startDate, 2); + $startDow = 6 - (int) WeekDay::evaluate($startDate, 2); if ($startDow < 0) { $startDow = 5; } @@ -77,7 +78,7 @@ class NetworkDays private static function calcEndDow(float $endDate): int { - $endDow = (int) WeekDay::funcWeekDay($endDate, 2); + $endDow = (int) WeekDay::evaluate($endDate, 2); if ($endDow >= 6) { $endDow = 0; } @@ -95,7 +96,7 @@ class NetworkDays return $partWeekDays; } - private static function applySign(int $result, float $sDate, float $eDate) + private static function applySign(int $result, float $sDate, float $eDate): int { return ($sDate > $eDate) ? -$result : $result; } diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Now.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Now.php index 6e6bd171..a0dea86f 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Now.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Now.php @@ -24,7 +24,7 @@ class Now * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object, * depending on the value of the ReturnDateType flag */ - public static function funcNow() + public static function evaluate() { $dti = new DateTimeImmutable(); $dateArray = date_parse($dti->format('c')); diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Second.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Second.php index c4749993..c3e15d86 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Second.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Second.php @@ -22,7 +22,7 @@ class Second * * @return int|string Minute */ - public static function funcSecond($timeValue) + public static function evaluate($timeValue) { try { $timeValue = Functions::flattenSingleValue($timeValue); diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Time.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Time.php index 450f9d50..7f29ba2c 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Time.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Time.php @@ -20,14 +20,14 @@ class Time * Excel Function: * TIME(hour,minute,second) * - * @param int $hour A number from 0 (zero) to 32767 representing the hour. + * @param mixed $hour A number from 0 (zero) to 32767 representing the hour. * Any value greater than 23 will be divided by 24 and the remainder * will be treated as the hour value. For example, TIME(27,0,0) = * TIME(3,0,0) = .125 or 3:00 AM. - * @param int $minute A number from 0 to 32767 representing the minute. + * @param mixed $minute A number from 0 to 32767 representing the minute. * Any value greater than 59 will be converted to hours and minutes. * For example, TIME(0,750,0) = TIME(12,30,0) = .520833 or 12:30 PM. - * @param int $second A number from 0 to 32767 representing the second. + * @param mixed $second A number from 0 to 32767 representing the second. * Any value greater than 59 will be converted to hours, minutes, * and seconds. For example, TIME(0,0,2000) = TIME(0,33,22) = .023148 * or 12:33:20 AM @@ -35,7 +35,7 @@ class Time * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object, * depending on the value of the ReturnDateType flag */ - public static function funcTime($hour, $minute, $second) + public static function evaluate($hour, $minute, $second) { try { $hour = self::toIntWithNullBool($hour); @@ -100,6 +100,9 @@ class Time } } + /** + * @param mixed $value expect int + */ private static function toIntWithNullBool($value): int { $value = Functions::flattenSingleValue($value); diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeValue.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeValue.php index 2366b1d6..46fa4136 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeValue.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeValue.php @@ -29,12 +29,12 @@ class TimeValue * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object, * depending on the value of the ReturnDateType flag */ - public static function funcTimeValue($timeValue) + public static function evaluate($timeValue) { $timeValue = trim(Functions::flattenSingleValue($timeValue), '"'); $timeValue = str_replace(['/', '.'], '-', $timeValue); - $arraySplit = preg_split('/[\/:\-\s]/', $timeValue); + $arraySplit = preg_split('/[\/:\-\s]/', $timeValue) ?: []; if ((count($arraySplit) == 2 || count($arraySplit) == 3) && $arraySplit[0] > 24) { $arraySplit[0] = ($arraySplit[0] % 24); $timeValue = implode(':', $arraySplit); diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Today.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Today.php index 5e459410..545f0efb 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Today.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Today.php @@ -24,7 +24,7 @@ class Today * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object, * depending on the value of the ReturnDateType flag */ - public static function funcToday() + public static function evaluate() { $dti = new DateTimeImmutable(); $dateArray = date_parse($dti->format('c')); diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/WeekDay.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/WeekDay.php index ea4fe340..6745557d 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/WeekDay.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/WeekDay.php @@ -19,14 +19,14 @@ class WeekDay * * @param null|float|int|string $dateValue Excel date serial value (float), PHP date timestamp (integer), * PHP DateTime object, or a standard date string - * @param int $style A number that determines the type of return value + * @param mixed $style A number that determines the type of return value * 1 or omitted Numbers 1 (Sunday) through 7 (Saturday). * 2 Numbers 1 (Monday) through 7 (Sunday). * 3 Numbers 0 (Monday) through 6 (Sunday). * * @return int|string Day of the week value */ - public static function funcWeekDay($dateValue, $style = 1) + public static function evaluate($dateValue, $style = 1) { try { $dateValue = Helpers::getDateValue($dateValue); @@ -58,6 +58,9 @@ class WeekDay return $DoW; } + /** + * @param mixed $style expect int + */ private static function validateStyle($style): int { $style = Functions::flattenSingleValue($style); diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/WeekNum.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/WeekNum.php index 9b2de4d0..35634e4e 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/WeekNum.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/WeekNum.php @@ -38,7 +38,7 @@ class WeekNum * * @return int|string Week Number */ - public static function funcWeekNum($dateValue, $method = Constants::STARTWEEK_SUNDAY) + public static function evaluate($dateValue, $method = Constants::STARTWEEK_SUNDAY) { $origDateValueNull = empty($dateValue); diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php index 09816d33..502cc0d6 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php @@ -24,11 +24,12 @@ class WorkDay * @param int $endDays The number of nonweekend and nonholiday days before or after * startDate. A positive value for days yields a future date; a * negative value yields a past date. + * @param mixed $dateArgs * * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object, * depending on the value of the ReturnDateType flag */ - public static function funcWorkDay($startDate, $endDays, ...$dateArgs) + public static function evaluate($startDate, $endDays, ...$dateArgs) { // Retrieve the mandatory start date and days that are referenced in the function definition try { @@ -65,8 +66,8 @@ class WorkDay { // Adjust the start date if it falls over a weekend - $startDoW = WeekDay::funcWeekDay($startDate, 3); - if (WeekDay::funcWeekDay($startDate, 3) >= 5) { + $startDoW = self::getWeekDay($startDate, 3); + if (self::getWeekDay($startDate, 3) >= 5) { $startDate += 7 - $startDoW; --$endDays; } @@ -77,7 +78,7 @@ class WorkDay while ($endDays > 0) { ++$endDate; // Adjust the calculated end date if it falls over a weekend - $endDow = WeekDay::funcWeekDay($endDate, 3); + $endDow = self::getWeekDay($endDate, 3); if ($endDow >= 5) { $endDate += 7 - $endDow; } @@ -96,7 +97,7 @@ class WorkDay { $holidayCountedArray = $holidayDates = []; foreach ($holidayArray as $holidayDate) { - if (WeekDay::funcWeekDay($holidayDate, 3) < 5) { + if (self::getWeekDay($holidayDate, 3) < 5) { $holidayDates[] = $holidayDate; } } @@ -109,7 +110,7 @@ class WorkDay } } // Adjust the calculated end date if it falls over a weekend - $endDoW = WeekDay::funcWeekDay($endDate, 3); + $endDoW = self::getWeekDay($endDate, 3); if ($endDoW >= 5) { $endDate += 7 - $endDoW; } @@ -127,9 +128,8 @@ class WorkDay { // Adjust the start date if it falls over a weekend - $startDoW = WeekDay::funcWeekDay($startDate, 3); - if (WeekDay::funcWeekDay($startDate, 3) >= 5) { - // @phpstan-ignore-next-line + $startDoW = self::getWeekDay($startDate, 3); + if (self::getWeekDay($startDate, 3) >= 5) { $startDate += -$startDoW + 4; ++$endDays; } @@ -140,7 +140,7 @@ class WorkDay while ($endDays < 0) { --$endDate; // Adjust the calculated end date if it falls over a weekend - $endDow = WeekDay::funcWeekDay($endDate, 3); + $endDow = self::getWeekDay($endDate, 3); if ($endDow >= 5) { $endDate += 4 - $endDow; } @@ -159,7 +159,7 @@ class WorkDay { $holidayCountedArray = $holidayDates = []; foreach ($holidayArray as $holidayDate) { - if (WeekDay::funcWeekDay($holidayDate, 3) < 5) { + if (self::getWeekDay($holidayDate, 3) < 5) { $holidayDates[] = $holidayDate; } } @@ -172,13 +172,19 @@ class WorkDay } } // Adjust the calculated end date if it falls over a weekend - $endDoW = WeekDay::funcWeekDay($endDate, 3); + $endDoW = self::getWeekDay($endDate, 3); if ($endDoW >= 5) { - // @phpstan-ignore-next-line $endDate += -$endDoW + 4; } } return $endDate; } + + private static function getWeekDay(float $date, int $wd): int + { + $result = WeekDay::evaluate($date, $wd); + + return is_string($result) ? -1 : $result; + } } diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Year.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Year.php index 5fcac739..2b7a10c6 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Year.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Year.php @@ -21,7 +21,7 @@ class Year * * @return int|string Year */ - public static function funcYear($dateValue) + public static function evaluate($dateValue) { try { $dateValue = Helpers::getDateValue($dateValue); diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php index a99b1c7f..3d49be47 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php @@ -34,7 +34,7 @@ class YearFrac * * @return float|string fraction of the year, or a string containing an error */ - public static function funcYearFrac($startDate, $endDate, $method = 0) + public static function evaluate($startDate, $endDate, $method = 0) { try { $method = (int) Helpers::validateNumericNull($method); @@ -50,15 +50,15 @@ class YearFrac switch ($method) { case 0: - return Days360::funcDays360($startDate, $endDate) / 360; + return Days360::evaluate($startDate, $endDate) / 360; case 1: return self::method1($startDate, $endDate); case 2: - return DateDif::funcDateDif($startDate, $endDate) / 360; + return DateDif::evaluate($startDate, $endDate) / 360; case 3: - return DateDif::funcDateDif($startDate, $endDate) / 365; + return DateDif::evaluate($startDate, $endDate) / 365; case 4: - return Days360::funcDays360($startDate, $endDate, true) / 360; + return Days360::evaluate($startDate, $endDate, true) / 360; } return Functions::NAN(); @@ -74,7 +74,7 @@ class YearFrac { if (Functions::getCompatibilityMode() !== Functions::COMPATIBILITY_OPENOFFICE && Date::getExcelCalendar() !== Date::CALENDAR_MAC_1904) { if ($endDate === null && $startDate !== null) { - if (Month::funcMonth($sDate) == 12 && Day::funcDay($sDate) === 31 && $method === 0) { + if (Month::evaluate($sDate) == 12 && Day::evaluate($sDate) === 31 && $method === 0) { $sDate += 2; } else { ++$sDate; @@ -87,14 +87,14 @@ class YearFrac private static function method1(float $startDate, float $endDate): float { - $days = DateDif::funcDateDif($startDate, $endDate); - $startYear = Year::funcYear($startDate); - $endYear = Year::funcYear($endDate); + $days = DateDif::evaluate($startDate, $endDate); + $startYear = (int) Year::evaluate($startDate); + $endYear = (int) Year::evaluate($endDate); $years = $endYear - $startYear + 1; - $startMonth = Month::funcMonth($startDate); - $startDay = Day::funcDay($startDate); - $endMonth = Month::funcMonth($endDate); - $endDay = Day::funcDay($endDate); + $startMonth = (int) Month::evaluate($startDate); + $startDay = (int) Day::evaluate($startDate); + $endMonth = (int) Month::evaluate($endDate); + $endDay = (int) Day::evaluate($endDate); $startMonthDay = 100 * $startMonth + $startDay; $endMonthDay = 100 * $endMonth + $endDay; if ($years == 1) { diff --git a/src/PhpSpreadsheet/Calculation/Financial/Amortization.php b/src/PhpSpreadsheet/Calculation/Financial/Amortization.php index 2ea0f4fe..c10e3304 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/Amortization.php +++ b/src/PhpSpreadsheet/Calculation/Financial/Amortization.php @@ -70,7 +70,7 @@ class Amortization return $e->getMessage(); } - $yearFrac = DateTimeExcel\YearFrac::funcYearFrac($purchased, $firstPeriod, $basis); + $yearFrac = DateTimeExcel\YearFrac::evaluate($purchased, $firstPeriod, $basis); if (is_string($yearFrac)) { return $yearFrac; } @@ -160,8 +160,8 @@ class Amortization $fOneRate = $cost * $rate; $fCostDelta = $cost - $salvage; // Note, quirky variation for leap years on the YEARFRAC for this function - $purchasedYear = DateTimeExcel\Year::funcYear($purchased); - $yearFrac = DateTimeExcel\YearFrac::funcYearFrac($purchased, $firstPeriod, $basis); + $purchasedYear = DateTimeExcel\Year::evaluate($purchased); + $yearFrac = DateTimeExcel\YearFrac::evaluate($purchased, $firstPeriod, $basis); if (is_string($yearFrac)) { return $yearFrac; } diff --git a/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php b/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php index 40df776f..251ae46d 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php +++ b/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php @@ -196,10 +196,9 @@ class NonPeriodic return $e->getMessage(); } if ($date0 > $datei) { - /** @phpstan-ignore-next-line */ - $dif = $ordered ? Functions::NAN() : -DateTimeExcel\DateDif::funcDateDif($datei, $date0, 'd'); + $dif = $ordered ? Functions::NAN() : -((int) DateTimeExcel\DateDif::evaluate($datei, $date0, 'd')); } else { - $dif = DateTimeExcel\DateDif::funcDateDif($date0, $datei, 'd'); + $dif = DateTimeExcel\DateDif::evaluate($date0, $datei, 'd'); } if (!is_numeric($dif)) { return $dif; diff --git a/src/PhpSpreadsheet/Calculation/Financial/Coupons.php b/src/PhpSpreadsheet/Calculation/Financial/Coupons.php index b95625cf..7f8f4b7c 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/Coupons.php +++ b/src/PhpSpreadsheet/Calculation/Financial/Coupons.php @@ -63,17 +63,17 @@ class Coupons return $e->getMessage(); } - $daysPerYear = Helpers::daysPerYear(DateTimeExcel\Year::funcYear($settlement), $basis); + $daysPerYear = Helpers::daysPerYear(DateTimeExcel\Year::evaluate($settlement), $basis); if (is_string($daysPerYear)) { return Functions::VALUE(); } $prev = self::couponFirstPeriodDate($settlement, $maturity, $frequency, self::PERIOD_DATE_PREVIOUS); if ($basis === FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL) { - return abs(DateTimeExcel\Days::funcDays($prev, $settlement)); + return abs((float) DateTimeExcel\Days::evaluate($prev, $settlement)); } - return DateTimeExcel\YearFrac::funcYearFrac($prev, $settlement, $basis) * $daysPerYear; + return DateTimeExcel\YearFrac::evaluate($prev, $settlement, $basis) * $daysPerYear; } /** @@ -133,7 +133,7 @@ class Coupons case FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL: // Actual/actual if ($frequency == FinancialConstants::FREQUENCY_ANNUAL) { - $daysPerYear = Helpers::daysPerYear(DateTimeExcel\Year::funcYear($settlement), $basis); + $daysPerYear = Helpers::daysPerYear(DateTimeExcel\Year::evaluate($settlement), $basis); return $daysPerYear / $frequency; } @@ -197,7 +197,7 @@ class Coupons return $e->getMessage(); } - $daysPerYear = Helpers::daysPerYear(DateTimeExcel\Year::funcYear($settlement), $basis); + $daysPerYear = Helpers::daysPerYear(DateTimeExcel\Year::evaluate($settlement), $basis); $next = self::couponFirstPeriodDate($settlement, $maturity, $frequency, self::PERIOD_DATE_NEXT); if ($basis === FinancialConstants::BASIS_DAYS_PER_YEAR_NASD) { @@ -208,7 +208,7 @@ class Coupons } } - return DateTimeExcel\YearFrac::funcYearFrac($settlement, $next, $basis) * $daysPerYear; + return DateTimeExcel\YearFrac::evaluate($settlement, $next, $basis) * $daysPerYear; } /** @@ -316,7 +316,7 @@ class Coupons return $e->getMessage(); } - $yearsBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::funcYearFrac( + $yearsBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::evaluate( $settlement, $maturity, FinancialConstants::BASIS_DAYS_PER_YEAR_NASD diff --git a/src/PhpSpreadsheet/Calculation/Financial/Securities/AccruedInterest.php b/src/PhpSpreadsheet/Calculation/Financial/Securities/AccruedInterest.php index 004b47f3..707423b5 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/Securities/AccruedInterest.php +++ b/src/PhpSpreadsheet/Calculation/Financial/Securities/AccruedInterest.php @@ -78,12 +78,12 @@ class AccruedInterest return $e->getMessage(); } - $daysBetweenIssueAndSettlement = YearFrac::funcYearFrac($issue, $settlement, $basis); + $daysBetweenIssueAndSettlement = YearFrac::evaluate($issue, $settlement, $basis); if (!is_numeric($daysBetweenIssueAndSettlement)) { // return date error return $daysBetweenIssueAndSettlement; } - $daysBetweenFirstInterestAndSettlement = YearFrac::funcYearFrac($firstInterest, $settlement, $basis); + $daysBetweenFirstInterestAndSettlement = YearFrac::evaluate($firstInterest, $settlement, $basis); if (!is_numeric($daysBetweenFirstInterestAndSettlement)) { // return date error return $daysBetweenFirstInterestAndSettlement; @@ -140,7 +140,7 @@ class AccruedInterest return $e->getMessage(); } - $daysBetweenIssueAndSettlement = YearFrac::funcYearFrac($issue, $settlement, $basis); + $daysBetweenIssueAndSettlement = YearFrac::evaluate($issue, $settlement, $basis); if (!is_numeric($daysBetweenIssueAndSettlement)) { // return date error return $daysBetweenIssueAndSettlement; diff --git a/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php b/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php index 158347e7..2c43254c 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php +++ b/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php @@ -134,7 +134,7 @@ class Price return $e->getMessage(); } - $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::funcYearFrac($settlement, $maturity, $basis); + $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::evaluate($settlement, $maturity, $basis); if (!is_numeric($daysBetweenSettlementAndMaturity)) { // return date error return $daysBetweenSettlementAndMaturity; @@ -194,23 +194,23 @@ class Price return $e->getMessage(); } - $daysPerYear = Helpers::daysPerYear(DateTimeExcel\Year::funcYear($settlement), $basis); + $daysPerYear = Helpers::daysPerYear(DateTimeExcel\Year::evaluate($settlement), $basis); if (!is_numeric($daysPerYear)) { return $daysPerYear; } - $daysBetweenIssueAndSettlement = DateTimeExcel\YearFrac::funcYearFrac($issue, $settlement, $basis); + $daysBetweenIssueAndSettlement = DateTimeExcel\YearFrac::evaluate($issue, $settlement, $basis); if (!is_numeric($daysBetweenIssueAndSettlement)) { // return date error return $daysBetweenIssueAndSettlement; } $daysBetweenIssueAndSettlement *= $daysPerYear; - $daysBetweenIssueAndMaturity = DateTimeExcel\YearFrac::funcYearFrac($issue, $maturity, $basis); + $daysBetweenIssueAndMaturity = DateTimeExcel\YearFrac::evaluate($issue, $maturity, $basis); if (!is_numeric($daysBetweenIssueAndMaturity)) { // return date error return $daysBetweenIssueAndMaturity; } $daysBetweenIssueAndMaturity *= $daysPerYear; - $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::funcYearFrac($settlement, $maturity, $basis); + $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::evaluate($settlement, $maturity, $basis); if (!is_numeric($daysBetweenSettlementAndMaturity)) { // return date error return $daysBetweenSettlementAndMaturity; @@ -272,7 +272,7 @@ class Price if ($investment <= 0) { return Functions::NAN(); } - $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::funcYearFrac($settlement, $maturity, $basis); + $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::evaluate($settlement, $maturity, $basis); if (!is_numeric($daysBetweenSettlementAndMaturity)) { // return date error return $daysBetweenSettlementAndMaturity; diff --git a/src/PhpSpreadsheet/Calculation/Financial/Securities/Rates.php b/src/PhpSpreadsheet/Calculation/Financial/Securities/Rates.php index 5a32d1d1..3a46b40b 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/Securities/Rates.php +++ b/src/PhpSpreadsheet/Calculation/Financial/Securities/Rates.php @@ -63,7 +63,7 @@ class Rates return Functions::NAN(); } - $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::funcYearFrac($settlement, $maturity, $basis); + $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::evaluate($settlement, $maturity, $basis); if (!is_numeric($daysBetweenSettlementAndMaturity)) { // return date error return $daysBetweenSettlementAndMaturity; @@ -126,7 +126,7 @@ class Rates return Functions::NAN(); } - $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::funcYearFrac($settlement, $maturity, $basis); + $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::evaluate($settlement, $maturity, $basis); if (!is_numeric($daysBetweenSettlementAndMaturity)) { // return date error return $daysBetweenSettlementAndMaturity; diff --git a/src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php b/src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php index bdd638fa..416a9c50 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php +++ b/src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php @@ -57,11 +57,11 @@ class Yields return $e->getMessage(); } - $daysPerYear = Helpers::daysPerYear(DateTimeExcel\Year::funcYear($settlement), $basis); + $daysPerYear = Helpers::daysPerYear(DateTimeExcel\Year::evaluate($settlement), $basis); if (!is_numeric($daysPerYear)) { return $daysPerYear; } - $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::funcYearFrac($settlement, $maturity, $basis); + $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::evaluate($settlement, $maturity, $basis); if (!is_numeric($daysBetweenSettlementAndMaturity)) { // return date error return $daysBetweenSettlementAndMaturity; @@ -122,23 +122,23 @@ class Yields return $e->getMessage(); } - $daysPerYear = Helpers::daysPerYear(DateTimeExcel\Year::funcYear($settlement), $basis); + $daysPerYear = Helpers::daysPerYear(DateTimeExcel\Year::evaluate($settlement), $basis); if (!is_numeric($daysPerYear)) { return $daysPerYear; } - $daysBetweenIssueAndSettlement = DateTimeExcel\YearFrac::funcYearFrac($issue, $settlement, $basis); + $daysBetweenIssueAndSettlement = DateTimeExcel\YearFrac::evaluate($issue, $settlement, $basis); if (!is_numeric($daysBetweenIssueAndSettlement)) { // return date error return $daysBetweenIssueAndSettlement; } $daysBetweenIssueAndSettlement *= $daysPerYear; - $daysBetweenIssueAndMaturity = DateTimeExcel\YearFrac::funcYearFrac($issue, $maturity, $basis); + $daysBetweenIssueAndMaturity = DateTimeExcel\YearFrac::evaluate($issue, $maturity, $basis); if (!is_numeric($daysBetweenIssueAndMaturity)) { // return date error return $daysBetweenIssueAndMaturity; } $daysBetweenIssueAndMaturity *= $daysPerYear; - $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::funcYearFrac($settlement, $maturity, $basis); + $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::evaluate($settlement, $maturity, $basis); if (!is_numeric($daysBetweenSettlementAndMaturity)) { // return date error return $daysBetweenSettlementAndMaturity; diff --git a/src/PhpSpreadsheet/Calculation/Financial/TreasuryBill.php b/src/PhpSpreadsheet/Calculation/Financial/TreasuryBill.php index 31146c31..2e186d52 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/TreasuryBill.php +++ b/src/PhpSpreadsheet/Calculation/Financial/TreasuryBill.php @@ -43,7 +43,7 @@ class TreasuryBill $daysBetweenSettlementAndMaturity = $maturity - $settlement; $daysPerYear = Helpers::daysPerYear( - DateTimeExcel\Year::funcYear($maturity), + DateTimeExcel\Year::evaluate($maturity), FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL ); @@ -88,7 +88,7 @@ class TreasuryBill $daysBetweenSettlementAndMaturity = $maturity - $settlement; $daysPerYear = Helpers::daysPerYear( - DateTimeExcel\Year::funcYear($maturity), + DateTimeExcel\Year::evaluate($maturity), FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL ); @@ -134,7 +134,7 @@ class TreasuryBill $daysBetweenSettlementAndMaturity = $maturity - $settlement; $daysPerYear = Helpers::daysPerYear( - DateTimeExcel\Year::funcYear($maturity), + DateTimeExcel\Year::evaluate($maturity), FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL ); diff --git a/src/PhpSpreadsheet/Calculation/TextData/Format.php b/src/PhpSpreadsheet/Calculation/TextData/Format.php index 7b0e6efa..3b7f0486 100644 --- a/src/PhpSpreadsheet/Calculation/TextData/Format.php +++ b/src/PhpSpreadsheet/Calculation/TextData/Format.php @@ -98,7 +98,7 @@ class Format $format = Functions::flattenSingleValue($format); if ((is_string($value)) && (!is_numeric($value)) && Date::isDateTimeFormatCode($format)) { - $value = DateTimeExcel\DateValue::funcDateValue($value); + $value = DateTimeExcel\DateValue::evaluate($value); } return (string) NumberFormat::toFormattedString($value, $format); @@ -129,14 +129,14 @@ class Format Functions::setReturnDateType(Functions::RETURNDATE_EXCEL); if (strpos($value, ':') !== false) { - $timeValue = DateTimeExcel\TimeValue::funcTimeValue($value); + $timeValue = DateTimeExcel\TimeValue::evaluate($value); if ($timeValue !== Functions::VALUE()) { Functions::setReturnDateType($dateSetting); return $timeValue; } } - $dateValue = DateTimeExcel\DateValue::funcDateValue($value); + $dateValue = DateTimeExcel\DateValue::evaluate($value); if ($dateValue !== Functions::VALUE()) { Functions::setReturnDateType($dateSetting); diff --git a/src/PhpSpreadsheet/Shared/Date.php b/src/PhpSpreadsheet/Shared/Date.php index 898dd523..a56ca3df 100644 --- a/src/PhpSpreadsheet/Shared/Date.php +++ b/src/PhpSpreadsheet/Shared/Date.php @@ -437,14 +437,14 @@ class Date return false; } - $dateValueNew = DateTimeExcel\DateValue::funcDateValue($dateValue); + $dateValueNew = DateTimeExcel\DateValue::evaluate($dateValue); if ($dateValueNew === Functions::VALUE()) { return false; } if (strpos($dateValue, ':') !== false) { - $timeValue = DateTimeExcel\TimeValue::funcTimeValue($dateValue); + $timeValue = DateTimeExcel\TimeValue::evaluate($dateValue); if ($timeValue === Functions::VALUE()) { return false; } diff --git a/src/PhpSpreadsheet/Worksheet/AutoFilter.php b/src/PhpSpreadsheet/Worksheet/AutoFilter.php index 1a710e40..394a3896 100644 --- a/src/PhpSpreadsheet/Worksheet/AutoFilter.php +++ b/src/PhpSpreadsheet/Worksheet/AutoFilter.php @@ -472,7 +472,7 @@ class AutoFilter $val = $maxVal = null; $ruleValues = []; - $baseDate = DateTimeExcel\Now::funcNow(); + $baseDate = DateTimeExcel\Now::evaluate(); // Calculate start/end dates for the required date range based on current date switch ($dynamicRuleType) { case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTWEEK: diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/DateTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/DateTest.php index d790777b..28988ded 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/DateTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/DateTest.php @@ -29,7 +29,7 @@ class DateTest extends AllSetupTeardown { self::setUnixReturn(); - $result = Datefunc::funcDate(2012, 1, 31); // 32-bit safe + $result = Datefunc::evaluate(2012, 1, 31); // 32-bit safe self::assertEquals(1327968000, $result); } @@ -37,7 +37,7 @@ class DateTest extends AllSetupTeardown { self::setObjectReturn(); - $result = Datefunc::funcDate(2012, 1, 31); + $result = Datefunc::evaluate(2012, 1, 31); // Must return an object... self::assertIsObject($result); // ... of the correct type @@ -50,10 +50,10 @@ class DateTest extends AllSetupTeardown { self::setMac1904(); - $result = Datefunc::funcDate(1918, 11, 11); + $result = Datefunc::evaluate(1918, 11, 11); self::assertEquals($result, 5428); - $result = Datefunc::funcDate(1901, 1, 31); + $result = Datefunc::evaluate(1901, 1, 31); self::assertEquals($result, '#NUM!'); } } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/DateValueTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/DateValueTest.php index 2d422e0a..13e56671 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/DateValueTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/DateValueTest.php @@ -26,7 +26,7 @@ class DateValueTest extends AllSetupTeardown if (is_string($expectedResult)) { $replYMD = str_replace('Y', date('Y'), $expectedResult); if ($replYMD !== $expectedResult) { - $expectedResult = DateValue::funcDateValue($replYMD); + $expectedResult = DateValue::evaluate($replYMD); } } $this->sheet->getCell("A$row")->setValue("=DATEVALUE($dateValue)"); @@ -46,7 +46,7 @@ class DateValueTest extends AllSetupTeardown { self::setUnixReturn(); - $result = DateValue::funcDateValue('2012-1-31'); + $result = DateValue::evaluate('2012-1-31'); self::assertEquals(1327968000, $result); self::assertEqualsWithDelta(1327968000, $result, 1E-8); } @@ -55,7 +55,7 @@ class DateValueTest extends AllSetupTeardown { self::setObjectReturn(); - $result = DateValue::funcDateValue('2012-1-31'); + $result = DateValue::evaluate('2012-1-31'); // Must return an object... self::assertIsObject($result); // ... of the correct type @@ -67,9 +67,9 @@ class DateValueTest extends AllSetupTeardown public function testDATEVALUEwith1904Calendar(): void { self::setMac1904(); - self::assertEquals(5428, DateValue::funcDateValue('1918-11-11')); - self::assertEquals(0, DateValue::funcDateValue('1904-01-01')); - self::assertEquals('#VALUE!', DateValue::funcDateValue('1903-12-31')); - self::assertEquals('#VALUE!', DateValue::funcDateValue('1900-02-29')); + self::assertEquals(5428, DateValue::evaluate('1918-11-11')); + self::assertEquals(0, DateValue::evaluate('1904-01-01')); + self::assertEquals('#VALUE!', DateValue::evaluate('1903-12-31')); + self::assertEquals('#VALUE!', DateValue::evaluate('1900-02-29')); } } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/DaysTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/DaysTest.php index 8c65622a..b88ba391 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/DaysTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/DaysTest.php @@ -33,13 +33,13 @@ class DaysTest extends AllSetupTeardown { $obj1 = new DateTime('2000-3-31'); $obj2 = new DateTimeImmutable('2000-2-29'); - self::assertSame(31, Days::funcDays($obj1, $obj2)); + self::assertSame(31, Days::evaluate($obj1, $obj2)); } public function testNonDateObject(): void { $obj1 = new Exception(); $obj2 = new DateTimeImmutable('2000-2-29'); - self::assertSame('#VALUE!', Days::funcDays($obj1, $obj2)); + self::assertSame('#VALUE!', Days::evaluate($obj1, $obj2)); } } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/EDateTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/EDateTest.php index efb34d0d..8943911b 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/EDateTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/EDateTest.php @@ -29,7 +29,7 @@ class EDateTest extends AllSetupTeardown { self::setUnixReturn(); - $result = EDate::funcEDate('2012-1-26', -1); + $result = EDate::evaluate('2012-1-26', -1); self::assertEquals(1324857600, $result); self::assertEqualsWithDelta(1324857600, $result, 1E-8); } @@ -38,7 +38,7 @@ class EDateTest extends AllSetupTeardown { self::setObjectReturn(); - $result = EDate::funcEDate('2012-1-26', -1); + $result = EDate::evaluate('2012-1-26', -1); // Must return an object... self::assertIsObject($result); // ... of the correct type diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/EoMonthTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/EoMonthTest.php index 6db76f58..eabaf494 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/EoMonthTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/EoMonthTest.php @@ -29,7 +29,7 @@ class EoMonthTest extends AllSetupTeardown { self::setUnixReturn(); - $result = EoMonth::funcEomonth('2012-1-26', -1); + $result = EoMonth::evaluate('2012-1-26', -1); self::assertEquals(1325289600, $result); } @@ -37,7 +37,7 @@ class EoMonthTest extends AllSetupTeardown { self::setObjectReturn(); - $result = EoMonth::funcEomonth('2012-1-26', -1); + $result = EoMonth::evaluate('2012-1-26', -1); // Must return an object... self::assertIsObject($result); // ... of the correct type diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/TimeTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/TimeTest.php index d6910024..053385e7 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/TimeTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/TimeTest.php @@ -31,7 +31,7 @@ class TimeTest extends AllSetupTeardown { self::setUnixReturn(); - $result = Time::funcTime(7, 30, 20); + $result = Time::evaluate(7, 30, 20); self::assertEqualsWithDelta(27020, $result, 1E-8); } @@ -39,7 +39,7 @@ class TimeTest extends AllSetupTeardown { self::setObjectReturn(); - $result = Time::funcTime(7, 30, 20); + $result = Time::evaluate(7, 30, 20); // Must return an object... self::assertIsObject($result); // ... of the correct type @@ -51,13 +51,13 @@ class TimeTest extends AllSetupTeardown public function testTIME1904(): void { self::setMac1904(); - $result = Time::funcTime(0, 0, 0); + $result = Time::evaluate(0, 0, 0); self::assertEquals(0, $result); } public function testTIME1900(): void { - $result = Time::funcTime(0, 0, 0); + $result = Time::evaluate(0, 0, 0); self::assertEquals(0, $result); } } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/TimeValueTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/TimeValueTest.php index ac4d3dbd..1eea77cb 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/TimeValueTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/TimeValueTest.php @@ -31,7 +31,7 @@ class TimeValueTest extends AllSetupTeardown { self::setUnixReturn(); - $result = TimeValue::funcTimeValue('7:30:20'); + $result = TimeValue::evaluate('7:30:20'); self::assertEquals(23420, $result); self::assertEqualsWithDelta(23420, $result, 1E-8); } @@ -40,7 +40,7 @@ class TimeValueTest extends AllSetupTeardown { self::setObjectReturn(); - $result = TimeValue::funcTimeValue('7:30:20'); + $result = TimeValue::evaluate('7:30:20'); // Must return an object... self::assertIsObject($result); // ... of the correct type diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/WeekDayTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/WeekDayTest.php index c95ce9cc..30898988 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/WeekDayTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/WeekDayTest.php @@ -28,8 +28,8 @@ class WeekDayTest extends AllSetupTeardown public function testWEEKDAYwith1904Calendar(): void { self::setMac1904(); - self::assertEquals(7, WeekDay::funcWeekDay('1904-01-02')); - self::assertEquals(6, WeekDay::funcWeekDay('1904-01-01')); - self::assertEquals(6, WeekDay::funcWeekDay(null)); + self::assertEquals(7, WeekDay::evaluate('1904-01-02')); + self::assertEquals(6, WeekDay::evaluate('1904-01-01')); + self::assertEquals(6, WeekDay::evaluate(null)); } } From 2e39ece3739fad95a0fda0dbf9dfc6c757a585bb Mon Sep 17 00:00:00 2001 From: Adrien Crivelli Date: Sun, 25 Apr 2021 15:31:56 +0900 Subject: [PATCH 07/57] BREAKING `Worksheet::getCellByColumnAndRow()` cannot return null anymore `Worksheet::getCellByColumnAndRow()` used to optionnaly return null if passed a second argument. This second argument was removed entirely and the method always returns a Cell (possibly creating it if needed). This make the API more predictable and easier to do static analysis with tools such as PHPStan. If you relied on that second parameter, you should instead use the `Worksheet::cellExistsByColumnAndRow()` before calling `getCellByColumnAndRow()`. --- phpstan-baseline.neon | 25 ---------------------- src/PhpSpreadsheet/Worksheet/Worksheet.php | 13 +++++------ 2 files changed, 7 insertions(+), 31 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 84fb7fd8..a606d636 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -4515,11 +4515,6 @@ parameters: count: 1 path: src/PhpSpreadsheet/ReferenceHelper.php - - - message: "#^Cannot call method setXfIndex\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Cell\\\\Cell\\|null\\.$#" - count: 1 - path: src/PhpSpreadsheet/ReferenceHelper.php - - message: "#^Parameter \\#1 \\$columnIndex of static method PhpOffice\\\\PhpSpreadsheet\\\\Cell\\\\Coordinate\\:\\:stringFromColumnIndex\\(\\) expects int, float\\|int given\\.$#" count: 1 @@ -5955,16 +5950,6 @@ parameters: count: 1 path: src/PhpSpreadsheet/Style/Style.php - - - message: "#^Cannot call method getXfIndex\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Cell\\\\Cell\\|null\\.$#" - count: 2 - path: src/PhpSpreadsheet/Style/Style.php - - - - message: "#^Cannot call method setXfIndex\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Cell\\\\Cell\\|null\\.$#" - count: 1 - path: src/PhpSpreadsheet/Style/Style.php - - message: "#^Result of && is always true\\.$#" count: 1 @@ -6355,16 +6340,6 @@ parameters: count: 1 path: src/PhpSpreadsheet/Worksheet/Worksheet.php - - - message: "#^Cannot call method setValue\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Cell\\\\Cell\\|null\\.$#" - count: 1 - path: src/PhpSpreadsheet/Worksheet/Worksheet.php - - - - message: "#^Cannot call method setValueExplicit\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Cell\\\\Cell\\|null\\.$#" - count: 1 - path: src/PhpSpreadsheet/Worksheet/Worksheet.php - - message: "#^Parameter \\#2 \\$start of function substr expects int, int\\<0, max\\>\\|false given\\.$#" count: 2 diff --git a/src/PhpSpreadsheet/Worksheet/Worksheet.php b/src/PhpSpreadsheet/Worksheet/Worksheet.php index 9e1f0a19..63166980 100644 --- a/src/PhpSpreadsheet/Worksheet/Worksheet.php +++ b/src/PhpSpreadsheet/Worksheet/Worksheet.php @@ -1263,22 +1263,23 @@ class Worksheet implements IComparable * * @param int $columnIndex Numeric column coordinate of the cell * @param int $row Numeric row coordinate of the cell - * @param bool $createIfNotExists Flag indicating whether a new cell should be created if it doesn't - * already exist, or a null should be returned instead * - * @return null|Cell Cell that was found/created or null + * @return Cell Cell that was found/created or null */ - public function getCellByColumnAndRow($columnIndex, $row, $createIfNotExists = true) + public function getCellByColumnAndRow($columnIndex, $row): Cell { $columnLetter = Coordinate::stringFromColumnIndex($columnIndex); $coordinate = $columnLetter . $row; if ($this->cellCollection->has($coordinate)) { - return $this->cellCollection->get($coordinate); + /** @var Cell $cell */ + $cell = $this->cellCollection->get($coordinate); + + return $cell; } // Create new cell object, if required - return $createIfNotExists ? $this->createNewCell($coordinate) : null; + return $this->createNewCell($coordinate); } /** From 4e2259c13582ef705f02f279b3c4c506a63255a4 Mon Sep 17 00:00:00 2001 From: Adrien Crivelli Date: Sun, 25 Apr 2021 16:10:09 +0900 Subject: [PATCH 08/57] BREAKING `Worksheet::getRowDimension()` and `Worksheet::getColumnDimension()` cannot return null anymore Both methods used to optionally return null if passed a second argument. This second argument was removed entirely and the method always returns a RowDimension or ColumnDimension respectively (possibly creating it if needed). This make the API more predictable and easier to do static analysis with tools such as PHPStan. If you relied on that second parameter, you should instead use the `Worksheet::getRowDimensions()` or `Worksheet::getColumnDimensions()` and check for existence yourself before calling the getters. --- phpstan-baseline.neon | 270 ------------------ src/PhpSpreadsheet/Reader/Html.php | 20 +- src/PhpSpreadsheet/Reader/Xls.php | 4 +- src/PhpSpreadsheet/Worksheet/Worksheet.php | 29 +- .../Functions/MathTrig/SubTotalTest.php | 16 +- .../Functional/ColumnWidthTest.php | 2 + 6 files changed, 24 insertions(+), 317 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index a606d636..fa824fba 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -2590,31 +2590,11 @@ parameters: count: 2 path: src/PhpSpreadsheet/Reader/Gnumeric.php - - - message: "#^Cannot call method setWidth\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\ColumnDimension\\|null\\.$#" - count: 3 - path: src/PhpSpreadsheet/Reader/Gnumeric.php - - - - message: "#^Cannot call method setVisible\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\ColumnDimension\\|null\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Gnumeric.php - - message: "#^Offset 'DefaultSizePts' does not exist on SimpleXMLElement\\|null\\.$#" count: 2 path: src/PhpSpreadsheet/Reader/Gnumeric.php - - - message: "#^Cannot call method setRowHeight\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\RowDimension\\|null\\.$#" - count: 2 - path: src/PhpSpreadsheet/Reader/Gnumeric.php - - - - message: "#^Cannot call method setVisible\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\RowDimension\\|null\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Gnumeric.php - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Gnumeric\\:\\:parseBorderAttributes\\(\\) has no return typehint specified\\.$#" count: 1 @@ -2760,16 +2740,6 @@ parameters: count: 1 path: src/PhpSpreadsheet/Reader/Html.php - - - message: "#^Cannot call method setRowHeight\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\RowDimension\\|null\\.$#" - count: 4 - path: src/PhpSpreadsheet/Reader/Html.php - - - - message: "#^Cannot call method setWidth\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\ColumnDimension\\|null\\.$#" - count: 3 - path: src/PhpSpreadsheet/Reader/Html.php - - message: "#^Parameter \\#2 \\$styleValue of method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Html\\:\\:setBorderStyle\\(\\) expects string, string\\|null given\\.$#" count: 5 @@ -3020,11 +2990,6 @@ parameters: count: 3 path: src/PhpSpreadsheet/Reader/Slk.php - - - message: "#^Cannot call method setWidth\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\ColumnDimension\\|null\\.$#" - count: 3 - path: src/PhpSpreadsheet/Reader/Slk.php - - message: "#^Call to an undefined method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\|PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\\\DgContainer\\|PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\\\DgContainer\\\\SpgrContainer\\|PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\\\DgContainer\\\\SpgrContainer\\\\SpContainer\\|PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\\\DggContainer\\|PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\\\DggContainer\\\\BstoreContainer\\|PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Escher\\\\DggContainer\\\\BstoreContainer\\\\BSE\\:\\:getDgContainer\\(\\)\\.$#" count: 1 @@ -3225,56 +3190,6 @@ parameters: count: 1 path: src/PhpSpreadsheet/Reader/Xls.php - - - message: "#^Cannot call method setWidth\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\ColumnDimension\\|null\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Xls.php - - - - message: "#^Cannot call method setVisible\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\ColumnDimension\\|null\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Xls.php - - - - message: "#^Cannot call method setOutlineLevel\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\ColumnDimension\\|null\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Xls.php - - - - message: "#^Cannot call method setCollapsed\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\ColumnDimension\\|null\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Xls.php - - - - message: "#^Cannot call method setXfIndex\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\ColumnDimension\\|null\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Xls.php - - - - message: "#^Cannot call method setRowHeight\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\RowDimension\\|null\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Xls.php - - - - message: "#^Cannot call method setOutlineLevel\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\RowDimension\\|null\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Xls.php - - - - message: "#^Cannot call method setCollapsed\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\RowDimension\\|null\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Xls.php - - - - message: "#^Cannot call method setVisible\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\RowDimension\\|null\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Xls.php - - - - message: "#^Cannot call method setXfIndex\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\RowDimension\\|null\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Xls.php - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xls\\:\\:includeCellRangeFiltered\\(\\) has no return typehint specified\\.$#" count: 1 @@ -4450,16 +4365,6 @@ parameters: count: 1 path: src/PhpSpreadsheet/Reader/Xml.php - - - message: "#^Cannot call method setWidth\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\ColumnDimension\\|null\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Xml.php - - - - message: "#^Cannot call method setRowHeight\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\RowDimension\\|null\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Xml.php - - message: "#^Parameter \\#2 \\$cmp_function of function uksort expects callable\\(mixed, mixed\\)\\: int, array\\('self', 'cellReverseSort'\\) given\\.$#" count: 4 @@ -4475,46 +4380,6 @@ parameters: count: 1 path: src/PhpSpreadsheet/ReferenceHelper.php - - - message: "#^Cannot call method getRowHeight\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\RowDimension\\|null\\.$#" - count: 1 - path: src/PhpSpreadsheet/ReferenceHelper.php - - - - message: "#^Cannot call method setRowHeight\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\RowDimension\\|null\\.$#" - count: 1 - path: src/PhpSpreadsheet/ReferenceHelper.php - - - - message: "#^Cannot call method getVisible\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\RowDimension\\|null\\.$#" - count: 1 - path: src/PhpSpreadsheet/ReferenceHelper.php - - - - message: "#^Cannot call method setVisible\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\RowDimension\\|null\\.$#" - count: 1 - path: src/PhpSpreadsheet/ReferenceHelper.php - - - - message: "#^Cannot call method getOutlineLevel\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\RowDimension\\|null\\.$#" - count: 1 - path: src/PhpSpreadsheet/ReferenceHelper.php - - - - message: "#^Cannot call method setOutlineLevel\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\RowDimension\\|null\\.$#" - count: 1 - path: src/PhpSpreadsheet/ReferenceHelper.php - - - - message: "#^Cannot call method getCollapsed\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\RowDimension\\|null\\.$#" - count: 1 - path: src/PhpSpreadsheet/ReferenceHelper.php - - - - message: "#^Cannot call method setCollapsed\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\RowDimension\\|null\\.$#" - count: 1 - path: src/PhpSpreadsheet/ReferenceHelper.php - - message: "#^Parameter \\#1 \\$columnIndex of static method PhpOffice\\\\PhpSpreadsheet\\\\Cell\\\\Coordinate\\:\\:stringFromColumnIndex\\(\\) expects int, float\\|int given\\.$#" count: 1 @@ -5930,26 +5795,6 @@ parameters: count: 1 path: src/PhpSpreadsheet/Style/Style.php - - - message: "#^Cannot call method getXfIndex\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\ColumnDimension\\|null\\.$#" - count: 2 - path: src/PhpSpreadsheet/Style/Style.php - - - - message: "#^Cannot call method setXfIndex\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\ColumnDimension\\|null\\.$#" - count: 1 - path: src/PhpSpreadsheet/Style/Style.php - - - - message: "#^Cannot call method getXfIndex\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\RowDimension\\|null\\.$#" - count: 3 - path: src/PhpSpreadsheet/Style/Style.php - - - - message: "#^Cannot call method setXfIndex\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\RowDimension\\|null\\.$#" - count: 1 - path: src/PhpSpreadsheet/Style/Style.php - - message: "#^Result of && is always true\\.$#" count: 1 @@ -6040,11 +5885,6 @@ parameters: count: 2 path: src/PhpSpreadsheet/Worksheet/AutoFilter.php - - - message: "#^Cannot call method setVisible\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\RowDimension\\|null\\.$#" - count: 2 - path: src/PhpSpreadsheet/Worksheet/AutoFilter.php - - message: "#^Cannot assign offset 'year' to array\\\\|string\\.$#" count: 1 @@ -6320,11 +6160,6 @@ parameters: count: 1 path: src/PhpSpreadsheet/Worksheet/Worksheet.php - - - message: "#^Cannot call method setWidth\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\ColumnDimension\\|null\\.$#" - count: 1 - path: src/PhpSpreadsheet/Worksheet/Worksheet.php - - message: "#^If condition is always true\\.$#" count: 2 @@ -6345,11 +6180,6 @@ parameters: count: 2 path: src/PhpSpreadsheet/Worksheet/Worksheet.php - - - message: "#^Parameter \\#1 \\$pRow of method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\:\\:getRowDimension\\(\\) expects int, string given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Worksheet/Worksheet.php - - message: "#^Result of && is always true\\.$#" count: 1 @@ -7760,31 +7590,6 @@ parameters: count: 1 path: src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php - - - message: "#^Cannot call method getCollapsed\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\RowDimension\\|null\\.$#" - count: 2 - path: src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php - - - - message: "#^Cannot call method getOutlineLevel\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\RowDimension\\|null\\.$#" - count: 3 - path: src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php - - - - message: "#^Cannot call method getRowHeight\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\RowDimension\\|null\\.$#" - count: 3 - path: src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php - - - - message: "#^Cannot call method getVisible\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\RowDimension\\|null\\.$#" - count: 2 - path: src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php - - - - message: "#^Cannot call method getXfIndex\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\RowDimension\\|null\\.$#" - count: 3 - path: src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php - - message: "#^Parameter \\#2 \\$content of method XMLWriter\\:\\:writeElement\\(\\) expects string\\|null, int\\|string given\\.$#" count: 1 @@ -7805,11 +7610,6 @@ parameters: count: 1 path: tests/PhpSpreadsheetTests/Calculation/Engine/RangeTest.php - - - message: "#^Cannot call method setAutoSize\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\ColumnDimension\\|null\\.$#" - count: 1 - path: tests/PhpSpreadsheetTests/Calculation/XlfnFunctionsTest.php - - message: "#^Call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertSame\\(\\) with arguments PhpOffice\\\\PhpSpreadsheet\\\\Cell\\\\Cell, null and 'should get exact…' will always evaluate to false\\.$#" count: 1 @@ -7835,16 +7635,6 @@ parameters: count: 1 path: tests/PhpSpreadsheetTests/Functional/ColumnWidthTest.php - - - message: "#^Cannot call method setWidth\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\ColumnDimension\\|null\\.$#" - count: 1 - path: tests/PhpSpreadsheetTests/Functional/ColumnWidthTest.php - - - - message: "#^Cannot call method getWidth\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\ColumnDimension\\|null\\.$#" - count: 1 - path: tests/PhpSpreadsheetTests/Functional/ColumnWidthTest.php - - message: "#^Method PhpOffice\\\\PhpSpreadsheetTests\\\\Functional\\\\CommentsTest\\:\\:testComments\\(\\) has parameter \\$format with no typehint specified\\.$#" count: 1 @@ -7855,11 +7645,6 @@ parameters: count: 2 path: tests/PhpSpreadsheetTests/Functional/ConditionalStopIfTrueTest.php - - - message: "#^Cannot call method setAutoSize\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\ColumnDimension\\|null\\.$#" - count: 1 - path: tests/PhpSpreadsheetTests/Functional/ConditionalTextTest.php - - message: "#^Parameter \\#1 \\$im of function imagecolorallocate expects resource, resource\\|false given\\.$#" count: 1 @@ -7955,26 +7740,6 @@ parameters: count: 1 path: tests/PhpSpreadsheetTests/Reader/CsvTest.php - - - message: "#^Cannot call method getVisible\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\RowDimension\\|null\\.$#" - count: 1 - path: tests/PhpSpreadsheetTests/Reader/Gnumeric/GnumericLoadTest.php - - - - message: "#^Cannot call method getRowHeight\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\RowDimension\\|null\\.$#" - count: 2 - path: tests/PhpSpreadsheetTests/Reader/Html/HtmlTagsTest.php - - - - message: "#^Cannot call method getWidth\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\ColumnDimension\\|null\\.$#" - count: 2 - path: tests/PhpSpreadsheetTests/Reader/Html/HtmlTest.php - - - - message: "#^Cannot call method getRowHeight\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\RowDimension\\|null\\.$#" - count: 2 - path: tests/PhpSpreadsheetTests/Reader/Html/HtmlTest.php - - message: "#^Unreachable statement \\- code above always terminates\\.$#" count: 1 @@ -8065,26 +7830,6 @@ parameters: count: 1 path: tests/PhpSpreadsheetTests/Reader/Xlsx/ConditionalFormattingDataBarXlsxTest.php - - - message: "#^Cannot call method getRowHeight\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\RowDimension\\|null\\.$#" - count: 1 - path: tests/PhpSpreadsheetTests/Reader/XlsxTest.php - - - - message: "#^Cannot call method getVisible\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\RowDimension\\|null\\.$#" - count: 1 - path: tests/PhpSpreadsheetTests/Reader/XlsxTest.php - - - - message: "#^Cannot call method getWidth\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\ColumnDimension\\|null\\.$#" - count: 1 - path: tests/PhpSpreadsheetTests/Reader/XlsxTest.php - - - - message: "#^Cannot call method getVisible\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\ColumnDimension\\|null\\.$#" - count: 1 - path: tests/PhpSpreadsheetTests/Reader/XlsxTest.php - - message: "#^Method PhpOffice\\\\PhpSpreadsheetTests\\\\Reader\\\\XlsxTest\\:\\:testStripsWhiteSpaceFromStyleString\\(\\) has parameter \\$string with no typehint specified\\.$#" count: 1 @@ -8240,21 +7985,6 @@ parameters: count: 1 path: tests/PhpSpreadsheetTests/Writer/Html/ImagesRootTest.php - - - message: "#^Cannot call method setVisible\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\ColumnDimension\\|null\\.$#" - count: 2 - path: tests/PhpSpreadsheetTests/Writer/Html/VisibilityTest.php - - - - message: "#^Cannot call method setVisible\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\RowDimension\\|null\\.$#" - count: 3 - path: tests/PhpSpreadsheetTests/Writer/Html/VisibilityTest.php - - - - message: "#^Cannot call method setRowHeight\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\RowDimension\\|null\\.$#" - count: 1 - path: tests/PhpSpreadsheetTests/Writer/Html/VisibilityTest.php - - message: "#^Parameter \\#1 \\$options of static method PhpOffice\\\\PhpSpreadsheet\\\\Settings\\:\\:setLibXmlLoaderOptions\\(\\) expects int, null given\\.$#" count: 1 diff --git a/src/PhpSpreadsheet/Reader/Html.php b/src/PhpSpreadsheet/Reader/Html.php index f235f9b1..1a0ef5d3 100644 --- a/src/PhpSpreadsheet/Reader/Html.php +++ b/src/PhpSpreadsheet/Reader/Html.php @@ -220,13 +220,13 @@ class Html extends BaseReader /** * Set input encoding. * - * @deprecated no use is made of this property - * * @param string $pValue Input encoding, eg: 'ANSI' * * @return $this * * @codeCoverageIgnore + * + * @deprecated no use is made of this property */ public function setInputEncoding($pValue) { @@ -238,11 +238,11 @@ class Html extends BaseReader /** * Get input encoding. * - * @deprecated no use is made of this property - * * @return string * * @codeCoverageIgnore + * + * @deprecated no use is made of this property */ public function getInputEncoding() { @@ -620,7 +620,7 @@ class Html extends BaseReader $cellContent .= $domText; } // but if we have a rich text run instead, we need to append it correctly - // TODO + // TODO } elseif ($child instanceof DOMElement) { $this->processDomElementBody($sheet, $row, $column, $cellContent, $child); } @@ -878,14 +878,14 @@ class Html extends BaseReader case 'width': $sheet->getColumnDimension($column)->setWidth( - str_replace('px', '', $styleValue) + (float) str_replace('px', '', $styleValue) ); break; case 'height': $sheet->getRowDimension($row)->setRowHeight( - str_replace('px', '', $styleValue) + (float) str_replace('px', '', $styleValue) ); break; @@ -922,8 +922,8 @@ class Html extends BaseReader } /** - * @param string $column - * @param int $row + * @param string $column + * @param int $row */ private function insertImage(Worksheet $sheet, $column, $row, array $attributes): void { @@ -990,7 +990,7 @@ class Html extends BaseReader /** * Map html border style to PhpSpreadsheet border style. * - * @param string $style + * @param string $style * * @return null|string */ diff --git a/src/PhpSpreadsheet/Reader/Xls.php b/src/PhpSpreadsheet/Reader/Xls.php index c389105b..c60b3de4 100644 --- a/src/PhpSpreadsheet/Reader/Xls.php +++ b/src/PhpSpreadsheet/Reader/Xls.php @@ -3634,7 +3634,7 @@ class Xls extends BaseReader $level = (0x0700 & self::getUInt2d($recordData, 8)) >> 8; // bit: 12; mask: 0x1000; 1 = collapsed - $isCollapsed = (0x1000 & self::getUInt2d($recordData, 8)) >> 12; + $isCollapsed = (bool) ((0x1000 & self::getUInt2d($recordData, 8)) >> 12); // offset: 10; size: 2; not used @@ -3704,7 +3704,7 @@ class Xls extends BaseReader $this->phpSheet->getRowDimension($r + 1)->setOutlineLevel($level); // bit: 4; mask: 0x00000010; 1 = outline group start or ends here... and is collapsed - $isCollapsed = (0x00000010 & self::getInt4d($recordData, 12)) >> 4; + $isCollapsed = (bool) ((0x00000010 & self::getInt4d($recordData, 12)) >> 4); $this->phpSheet->getRowDimension($r + 1)->setCollapsed($isCollapsed); // bit: 5; mask: 0x00000020; 1 = row is hidden diff --git a/src/PhpSpreadsheet/Worksheet/Worksheet.php b/src/PhpSpreadsheet/Worksheet/Worksheet.php index 63166980..1dbf2a1a 100644 --- a/src/PhpSpreadsheet/Worksheet/Worksheet.php +++ b/src/PhpSpreadsheet/Worksheet/Worksheet.php @@ -1296,7 +1296,7 @@ class Worksheet implements IComparable $this->cellCollectionIsSorted = false; // Coordinates - $aCoordinates = Coordinate::coordinateFromString($pCoordinate); + [$column, $row] = Coordinate::coordinateFromString($pCoordinate); $aIndexes = Coordinate::indexesFromString($pCoordinate); if ($this->cachedHighestColumn < $aIndexes[0]) { $this->cachedHighestColumn = $aIndexes[0]; @@ -1307,8 +1307,8 @@ class Worksheet implements IComparable // Cell needs appropriate xfIndex from dimensions records // but don't create dimension records if they don't already exist - $rowDimension = $this->getRowDimension($aCoordinates[1], false); - $columnDimension = $this->getColumnDimension($aCoordinates[0], false); + $rowDimension = $this->rowDimensions[$row] ?? null; + $columnDimension = $this->columnDimensions[$column] ?? null; if ($rowDimension !== null && $rowDimension->getXfIndex() > 0) { // then there is a row dimension with explicit style, assign it to the cell @@ -1353,20 +1353,11 @@ class Worksheet implements IComparable * Get row dimension at a specific row. * * @param int $pRow Numeric index of the row - * @param bool $create - * - * @return null|RowDimension */ - public function getRowDimension($pRow, $create = true) + public function getRowDimension(int $pRow): RowDimension { - // Found - $found = null; - // Get row dimension if (!isset($this->rowDimensions[$pRow])) { - if (!$create) { - return null; - } $this->rowDimensions[$pRow] = new RowDimension($pRow); $this->cachedHighestRow = max($this->cachedHighestRow, $pRow); @@ -1379,20 +1370,14 @@ class Worksheet implements IComparable * Get column dimension at a specific column. * * @param string $pColumn String index of the column eg: 'A' - * @param bool $create - * - * @return null|ColumnDimension */ - public function getColumnDimension($pColumn, $create = true) + public function getColumnDimension(string $pColumn): ColumnDimension { // Uppercase coordinate $pColumn = strtoupper($pColumn); // Fetch dimensions if (!isset($this->columnDimensions[$pColumn])) { - if (!$create) { - return null; - } $this->columnDimensions[$pColumn] = new ColumnDimension($pColumn); $columnIndex = Coordinate::columnIndexFromString($pColumn); @@ -1408,10 +1393,8 @@ class Worksheet implements IComparable * Get column dimension at a specific column by using numeric cell coordinates. * * @param int $columnIndex Numeric column coordinate of the cell - * - * @return null|ColumnDimension */ - public function getColumnDimensionByColumn($columnIndex) + public function getColumnDimensionByColumn(int $columnIndex): ColumnDimension { return $this->getColumnDimension(Coordinate::stringFromColumnIndex($columnIndex)); } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/SubTotalTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/SubTotalTest.php index be63f694..366b7993 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/SubTotalTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/SubTotalTest.php @@ -56,12 +56,8 @@ class SubTotalTest extends AllSetupTeardown 'L' => false, ]; foreach ($hiddenColumns as $col => $hidden) { - $colDim = $sheet->getColumnDimension($col); - if ($colDim === null) { - self::fail('Unexpected null column dimension'); - } else { - $colDim->setVisible($hidden); - } + $columnDimension = $sheet->getColumnDimension($col); + $columnDimension->setVisible($hidden); } $sheet->getCell('D2')->setValue("=SUBTOTAL($type, A1:$maxCol$maxRow)"); $result = $sheet->getCell('D2')->getCalculatedValue(); @@ -96,12 +92,8 @@ class SubTotalTest extends AllSetupTeardown '12' => false, ]; foreach ($visibleRows as $row => $visible) { - $rowDim = $sheet->getRowDimension($row); - if ($rowDim === null) { - self::fail('Unexpected null row dimension'); - } else { - $rowDim->setVisible($visible); - } + $rowDimension = $sheet->getRowDimension($row); + $rowDimension->setVisible($visible); } $sheet->getCell('D2')->setValue("=SUBTOTAL($type, A1:$maxCol$maxRow)"); $result = $sheet->getCell('D2')->getCalculatedValue(); diff --git a/tests/PhpSpreadsheetTests/Functional/ColumnWidthTest.php b/tests/PhpSpreadsheetTests/Functional/ColumnWidthTest.php index 38117059..41122885 100644 --- a/tests/PhpSpreadsheetTests/Functional/ColumnWidthTest.php +++ b/tests/PhpSpreadsheetTests/Functional/ColumnWidthTest.php @@ -3,6 +3,7 @@ namespace PhpOffice\PhpSpreadsheetTests\Functional; use PhpOffice\PhpSpreadsheet\Spreadsheet; +use PhpOffice\PhpSpreadsheet\Worksheet\ColumnDimension; class ColumnWidthTest extends AbstractFunctional { @@ -36,6 +37,7 @@ class ColumnWidthTest extends AbstractFunctional self::assertArrayHasKey('A', $columnDimensions); $column = array_shift($columnDimensions); + self::assertInstanceOf(ColumnDimension::class, $column); self::assertEquals(20, $column->getWidth()); } } From 27eac4d64949ce3140bcc2286fcee908ef3aea4e Mon Sep 17 00:00:00 2001 From: Mark Baker Date: Sun, 25 Apr 2021 13:48:42 +0200 Subject: [PATCH 09/57] Some refactoring improvements to parsing Style information in the Xls Reader (#2025) * Some refactoring improvements to parsing Style information in the Xls Reader --- phpstan-baseline.neon | 10 -- src/PhpSpreadsheet/Reader/Xls.php | 92 ++----------------- .../Reader/Xls/Style/Border.php | 19 ++-- .../Reader/Xls/Style/CellAlignment.php | 50 ++++++++++ .../Reader/Xls/Style/CellFont.php | 39 ++++++++ .../Reader/Xls/Style/FillPattern.php | 9 +- 6 files changed, 108 insertions(+), 111 deletions(-) create mode 100644 src/PhpSpreadsheet/Reader/Xls/Style/CellAlignment.php create mode 100644 src/PhpSpreadsheet/Reader/Xls/Style/CellFont.php diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index fa824fba..e9a33fc8 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -3375,16 +3375,6 @@ parameters: count: 1 path: src/PhpSpreadsheet/Reader/Xls/RC4.php - - - message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xls\\\\Style\\\\Border\\:\\:\\$map has no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Xls/Style/Border.php - - - - message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xls\\\\Style\\\\FillPattern\\:\\:\\$map has no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Xls/Style/FillPattern.php - - message: "#^Cannot access property \\$Relationship on SimpleXMLElement\\|false\\.$#" count: 12 diff --git a/src/PhpSpreadsheet/Reader/Xls.php b/src/PhpSpreadsheet/Reader/Xls.php index c60b3de4..470bbacc 100644 --- a/src/PhpSpreadsheet/Reader/Xls.php +++ b/src/PhpSpreadsheet/Reader/Xls.php @@ -7,6 +7,7 @@ use PhpOffice\PhpSpreadsheet\Cell\DataType; use PhpOffice\PhpSpreadsheet\Cell\DataValidation; use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException; use PhpOffice\PhpSpreadsheet\NamedRange; +use PhpOffice\PhpSpreadsheet\Reader\Xls\Style\CellFont; use PhpOffice\PhpSpreadsheet\RichText\RichText; use PhpOffice\PhpSpreadsheet\Shared\CodePage; use PhpOffice\PhpSpreadsheet\Shared\Date; @@ -2067,39 +2068,11 @@ class Xls extends BaseReader // offset: 8; size: 2; escapement type $escapement = self::getUInt2d($recordData, 8); - switch ($escapement) { - case 0x0001: - $objFont->setSuperscript(true); - - break; - case 0x0002: - $objFont->setSubscript(true); - - break; - } + CellFont::escapement($objFont, $escapement); // offset: 10; size: 1; underline type $underlineType = ord($recordData[10]); - switch ($underlineType) { - case 0x00: - break; // no underline - case 0x01: - $objFont->setUnderline(Font::UNDERLINE_SINGLE); - - break; - case 0x02: - $objFont->setUnderline(Font::UNDERLINE_DOUBLE); - - break; - case 0x21: - $objFont->setUnderline(Font::UNDERLINE_SINGLEACCOUNTING); - - break; - case 0x22: - $objFont->setUnderline(Font::UNDERLINE_DOUBLEACCOUNTING); - - break; - } + CellFont::underline($objFont, $underlineType); // offset: 11; size: 1; font family // offset: 12; size: 1; character set @@ -2219,68 +2192,15 @@ class Xls extends BaseReader // offset: 6; size: 1; Alignment and text break // bit 2-0, mask 0x07; horizontal alignment $horAlign = (0x07 & ord($recordData[6])) >> 0; - switch ($horAlign) { - case 0: - $objStyle->getAlignment()->setHorizontal(Alignment::HORIZONTAL_GENERAL); + Xls\Style\CellAlignment::horizontal($objStyle->getAlignment(), $horAlign); - break; - case 1: - $objStyle->getAlignment()->setHorizontal(Alignment::HORIZONTAL_LEFT); - - break; - case 2: - $objStyle->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER); - - break; - case 3: - $objStyle->getAlignment()->setHorizontal(Alignment::HORIZONTAL_RIGHT); - - break; - case 4: - $objStyle->getAlignment()->setHorizontal(Alignment::HORIZONTAL_FILL); - - break; - case 5: - $objStyle->getAlignment()->setHorizontal(Alignment::HORIZONTAL_JUSTIFY); - - break; - case 6: - $objStyle->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER_CONTINUOUS); - - break; - } // bit 3, mask 0x08; wrap text $wrapText = (0x08 & ord($recordData[6])) >> 3; - switch ($wrapText) { - case 0: - $objStyle->getAlignment()->setWrapText(false); + Xls\Style\CellAlignment::wrap($objStyle->getAlignment(), $wrapText); - break; - case 1: - $objStyle->getAlignment()->setWrapText(true); - - break; - } // bit 6-4, mask 0x70; vertical alignment $vertAlign = (0x70 & ord($recordData[6])) >> 4; - switch ($vertAlign) { - case 0: - $objStyle->getAlignment()->setVertical(Alignment::VERTICAL_TOP); - - break; - case 1: - $objStyle->getAlignment()->setVertical(Alignment::VERTICAL_CENTER); - - break; - case 2: - $objStyle->getAlignment()->setVertical(Alignment::VERTICAL_BOTTOM); - - break; - case 3: - $objStyle->getAlignment()->setVertical(Alignment::VERTICAL_JUSTIFY); - - break; - } + Xls\Style\CellAlignment::vertical($objStyle->getAlignment(), $vertAlign); if ($this->version == self::XLS_BIFF8) { // offset: 7; size: 1; XF_ROTATION: Text rotation angle diff --git a/src/PhpSpreadsheet/Reader/Xls/Style/Border.php b/src/PhpSpreadsheet/Reader/Xls/Style/Border.php index 91cbe36f..d832eb02 100644 --- a/src/PhpSpreadsheet/Reader/Xls/Style/Border.php +++ b/src/PhpSpreadsheet/Reader/Xls/Style/Border.php @@ -6,7 +6,10 @@ use PhpOffice\PhpSpreadsheet\Style\Border as StyleBorder; class Border { - protected static $map = [ + /** + * @var array + */ + protected static $borderStyleMap = [ 0x00 => StyleBorder::BORDER_NONE, 0x01 => StyleBorder::BORDER_THIN, 0x02 => StyleBorder::BORDER_MEDIUM, @@ -23,18 +26,10 @@ class Border 0x0D => StyleBorder::BORDER_SLANTDASHDOT, ]; - /** - * Map border style - * OpenOffice documentation: 2.5.11. - * - * @param int $index - * - * @return string - */ - public static function lookup($index) + public static function lookup(int $index): string { - if (isset(self::$map[$index])) { - return self::$map[$index]; + if (isset(self::$borderStyleMap[$index])) { + return self::$borderStyleMap[$index]; } return StyleBorder::BORDER_NONE; diff --git a/src/PhpSpreadsheet/Reader/Xls/Style/CellAlignment.php b/src/PhpSpreadsheet/Reader/Xls/Style/CellAlignment.php new file mode 100644 index 00000000..f03d47fa --- /dev/null +++ b/src/PhpSpreadsheet/Reader/Xls/Style/CellAlignment.php @@ -0,0 +1,50 @@ + + */ + protected static $horizontalAlignmentMap = [ + 0 => Alignment::HORIZONTAL_GENERAL, + 1 => Alignment::HORIZONTAL_LEFT, + 2 => Alignment::HORIZONTAL_CENTER, + 3 => Alignment::HORIZONTAL_RIGHT, + 4 => Alignment::HORIZONTAL_FILL, + 5 => Alignment::HORIZONTAL_JUSTIFY, + 6 => Alignment::HORIZONTAL_CENTER_CONTINUOUS, + ]; + + /** + * @var array + */ + protected static $verticalAlignmentMap = [ + 0 => Alignment::VERTICAL_TOP, + 1 => Alignment::VERTICAL_CENTER, + 2 => Alignment::VERTICAL_BOTTOM, + 3 => Alignment::VERTICAL_JUSTIFY, + ]; + + public static function horizontal(Alignment $alignment, int $horizontal): void + { + if (array_key_exists($horizontal, self::$horizontalAlignmentMap)) { + $alignment->setHorizontal(self::$horizontalAlignmentMap[$horizontal]); + } + } + + public static function vertical(Alignment $alignment, int $vertical): void + { + if (array_key_exists($vertical, self::$verticalAlignmentMap)) { + $alignment->setVertical(self::$verticalAlignmentMap[$vertical]); + } + } + + public static function wrap(Alignment $alignment, int $wrap): void + { + $alignment->setWrapText((bool) $wrap); + } +} diff --git a/src/PhpSpreadsheet/Reader/Xls/Style/CellFont.php b/src/PhpSpreadsheet/Reader/Xls/Style/CellFont.php new file mode 100644 index 00000000..e975be4d --- /dev/null +++ b/src/PhpSpreadsheet/Reader/Xls/Style/CellFont.php @@ -0,0 +1,39 @@ +setSuperscript(true); + + break; + case 0x0002: + $font->setSubscript(true); + + break; + } + } + + /** + * @var array + */ + protected static $underlineMap = [ + 0x01 => Font::UNDERLINE_SINGLE, + 0x02 => Font::UNDERLINE_DOUBLE, + 0x21 => Font::UNDERLINE_SINGLEACCOUNTING, + 0x22 => Font::UNDERLINE_DOUBLEACCOUNTING, + ]; + + public static function underline(Font $font, int $underline): void + { + if (array_key_exists($underline, self::$underlineMap)) { + $font->setUnderline(self::$underlineMap[$underline]); + } + } +} diff --git a/src/PhpSpreadsheet/Reader/Xls/Style/FillPattern.php b/src/PhpSpreadsheet/Reader/Xls/Style/FillPattern.php index 7b85c088..32ab5c85 100644 --- a/src/PhpSpreadsheet/Reader/Xls/Style/FillPattern.php +++ b/src/PhpSpreadsheet/Reader/Xls/Style/FillPattern.php @@ -6,7 +6,10 @@ use PhpOffice\PhpSpreadsheet\Style\Fill; class FillPattern { - protected static $map = [ + /** + * @var array + */ + protected static $fillPatternMap = [ 0x00 => Fill::FILL_NONE, 0x01 => Fill::FILL_SOLID, 0x02 => Fill::FILL_PATTERN_MEDIUMGRAY, @@ -38,8 +41,8 @@ class FillPattern */ public static function lookup($index) { - if (isset(self::$map[$index])) { - return self::$map[$index]; + if (isset(self::$fillPatternMap[$index])) { + return self::$fillPatternMap[$index]; } return Fill::FILL_NONE; From 8d7be25823aaf3d6051f7daece475179a856259f Mon Sep 17 00:00:00 2001 From: Mark Baker Date: Tue, 27 Apr 2021 19:10:37 +0200 Subject: [PATCH 10/57] Improve Range handling in the Calculation Engine for Row and Column ranges (#2028) * Improve Range handling in the Calculation Engine for Row and Column ranges --- CHANGELOG.md | 1 + .../Calculation/Calculation.php | 108 ++++++++++++------ .../Calculation/MathTrig/Sum.php | 6 +- .../Calculation/RowColumnReferenceTest.php | 84 ++++++++++++++ 4 files changed, 162 insertions(+), 37 deletions(-) create mode 100644 tests/PhpSpreadsheetTests/Calculation/RowColumnReferenceTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index bb09f628..21762f89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). ### Added +- Improved support for Row and Column ranges in formulae [Issue #1755](https://github.com/PHPOffice/PhpSpreadsheet/issues/1755) [PR #2028](https://github.com/PHPOffice/PhpSpreadsheet/pull/2028) - Implemented the CHITEST(), CHISQ.DIST() and CHISQ.INV() and equivalent Statistical functions, for both left- and right-tailed distributions. - Support for ActiveSheet and SelectedCells in the ODS Reader and Writer. [PR #1908](https://github.com/PHPOffice/PhpSpreadsheet/pull/1908) diff --git a/src/PhpSpreadsheet/Calculation/Calculation.php b/src/PhpSpreadsheet/Calculation/Calculation.php index 97bd1779..abd997a3 100644 --- a/src/PhpSpreadsheet/Calculation/Calculation.php +++ b/src/PhpSpreadsheet/Calculation/Calculation.php @@ -30,6 +30,9 @@ class Calculation const CALCULATION_REGEXP_CELLREF = '((([^\s,!&%^\/\*\+<>=-]*)|(\'[^\']*\')|(\"[^\"]*\"))!)?\$?\b([a-z]{1,3})\$?(\d{1,7})(?![\w.])'; // Cell reference (with or without a sheet reference) ensuring absolute/relative const CALCULATION_REGEXP_CELLREF_RELATIVE = '((([^\s\(,!&%^\/\*\+<>=-]*)|(\'[^\']*\')|(\"[^\"]*\"))!)?(\$?\b[a-z]{1,3})(\$?\d{1,7})(?![\w.])'; + const CALCULATION_REGEXP_COLUMN_RANGE = '(((([^\s\(,!&%^\/\*\+<>=-]*)|(\'[^\']*\')|(\"[^\"]*\"))!)?(\$?[a-z]{1,3})):(?![.*])'; + const CALCULATION_REGEXP_ROW_RANGE = '(((([^\s\(,!&%^\/\*\+<>=-]*)|(\'[^\']*\')|(\"[^\"]*\"))!)?(\$?[1-9][0-9]{0,6})):(?![.*])'; + // Cell reference (with or without a sheet reference) ensuring absolute/relative // Cell ranges ensuring absolute/relative const CALCULATION_REGEXP_COLUMNRANGE_RELATIVE = '(\$?[a-z]{1,3}):(\$?[a-z]{1,3})'; const CALCULATION_REGEXP_ROWRANGE_RELATIVE = '(\$?\d{1,7}):(\$?\d{1,7})'; @@ -3798,6 +3801,8 @@ class Calculation $regexpMatchString = '/^(' . self::CALCULATION_REGEXP_FUNCTION . '|' . self::CALCULATION_REGEXP_CELLREF . + '|' . self::CALCULATION_REGEXP_COLUMN_RANGE . + '|' . self::CALCULATION_REGEXP_ROW_RANGE . '|' . self::CALCULATION_REGEXP_NUMBER . '|' . self::CALCULATION_REGEXP_STRING . '|' . self::CALCULATION_REGEXP_OPENBRACE . @@ -3866,7 +3871,8 @@ class Calculation $opCharacter .= $formula[++$index]; } // Find out if we're currently at the beginning of a number, variable, cell reference, function, parenthesis or operand - $isOperandOrFunction = preg_match($regexpMatchString, substr($formula, $index), $match); + $isOperandOrFunction = (bool) preg_match($regexpMatchString, substr($formula, $index), $match); + if ($opCharacter == '-' && !$expectingOperator) { // Is it a negation instead of a minus? // Put a negation on the stack $stack->push('Unary Operator', '~', null, $currentCondition, $currentOnlyIf, $currentOnlyIfNot); @@ -4038,6 +4044,7 @@ class Calculation $expectingOperand = false; $val = $match[1]; $length = strlen($val); + if (preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/miu', $val, $matches)) { $val = preg_replace('/\s/u', '', $val); if (isset(self::$phpSpreadsheetFunctions[strtoupper($matches[1])]) || isset(self::$controlFunctions[strtoupper($matches[1])])) { // it's a function @@ -4074,7 +4081,7 @@ class Calculation // Should only be applied to the actual cell column, not the worksheet name // If the last entry on the stack was a : operator, then we have a cell range reference $testPrevOp = $stack->last(1); - if ($testPrevOp !== null && $testPrevOp['value'] == ':') { + if ($testPrevOp !== null && $testPrevOp['value'] === ':') { // If we have a worksheet reference, then we're playing with a 3D reference if ($matches[2] == '') { // Otherwise, we 'inherit' the worksheet reference from the start cell reference @@ -4091,62 +4098,57 @@ class Calculation return $this->raiseFormulaError('3D Range references are not yet supported'); } } + } elseif (strpos($val, '!') === false && $pCellParent !== null) { + $worksheet = $pCellParent->getTitle(); + $val = "'{$worksheet}'!{$val}"; } $outputItem = $stack->getStackItem('Cell Reference', $val, $val, $currentCondition, $currentOnlyIf, $currentOnlyIfNot); $output[] = $outputItem; } else { // it's a variable, constant, string, number or boolean + $localeConstant = false; + $stackItemType = 'Value'; + $stackItemReference = null; + // If the last entry on the stack was a : operator, then we may have a row or column range reference $testPrevOp = $stack->last(1); if ($testPrevOp !== null && $testPrevOp['value'] === ':') { + $stackItemType = 'Cell Reference'; $startRowColRef = $output[count($output) - 1]['value']; [$rangeWS1, $startRowColRef] = Worksheet::extractSheetTitle($startRowColRef, true); $rangeSheetRef = $rangeWS1; - if ($rangeWS1 != '') { + if ($rangeWS1 !== '') { $rangeWS1 .= '!'; } + $rangeSheetRef = trim($rangeSheetRef, "'"); [$rangeWS2, $val] = Worksheet::extractSheetTitle($val, true); - if ($rangeWS2 != '') { + if ($rangeWS2 !== '') { $rangeWS2 .= '!'; } else { $rangeWS2 = $rangeWS1; } + $refSheet = $pCellParent; - if ($pCellParent !== null && $rangeSheetRef !== $pCellParent->getTitle()) { + if ($pCellParent !== null && $rangeSheetRef !== '' && $rangeSheetRef !== $pCellParent->getTitle()) { $refSheet = $pCellParent->getParent()->getSheetByName($rangeSheetRef); } - if ( - (is_int($startRowColRef)) && (ctype_digit($val)) && - ($startRowColRef <= 1048576) && ($val <= 1048576) - ) { - // Row range - $endRowColRef = ($refSheet !== null) ? $refSheet->getHighestColumn() : 'XFD'; // Max 16,384 columns for Excel2007 - $output[count($output) - 1]['value'] = $rangeWS1 . 'A' . $startRowColRef; - $val = $rangeWS2 . $endRowColRef . $val; - } elseif ( - (ctype_alpha($startRowColRef)) && (ctype_alpha($val)) && - (strlen($startRowColRef) <= 3) && (strlen($val) <= 3) - ) { - // Column range - $endRowColRef = ($refSheet !== null) ? $refSheet->getHighestRow() : 1048576; // Max 1,048,576 rows for Excel2007 - $output[count($output) - 1]['value'] = $rangeWS1 . strtoupper($startRowColRef) . '1'; - $val = $rangeWS2 . $val . $endRowColRef; - } - } - $localeConstant = false; - $stackItemType = 'Value'; - $stackItemReference = null; - if ($opCharacter == self::FORMULA_STRING_QUOTE) { + if (ctype_digit($val) && $val <= 1048576) { + // Row range + $stackItemType = 'Row Reference'; + $endRowColRef = ($refSheet !== null) ? $refSheet->getHighestDataColumn($val) : 'XFD'; // Max 16,384 columns for Excel2007 + $val = "{$rangeWS2}{$endRowColRef}{$val}"; + } elseif (ctype_alpha($val) && strlen($val) <= 3) { + // Column range + $stackItemType = 'Column Reference'; + $endRowColRef = ($refSheet !== null) ? $refSheet->getHighestDataRow($val) : 1048576; // Max 1,048,576 rows for Excel2007 + $val = "{$rangeWS2}{$val}{$endRowColRef}"; + } + $stackItemReference = $val; + } elseif ($opCharacter == self::FORMULA_STRING_QUOTE) { // UnEscape any quotes within the string $val = self::wrapResult(str_replace('""', self::FORMULA_STRING_QUOTE, self::unwrapResult($val))); - } elseif (is_numeric($val)) { - if ((strpos($val, '.') !== false) || (stripos($val, 'e') !== false) || ($val > PHP_INT_MAX) || ($val < -PHP_INT_MAX)) { - $val = (float) $val; - } else { - $val = (int) $val; - } } elseif (isset(self::$excelConstants[trim(strtoupper($val))])) { $stackItemType = 'Constant'; $excelConstant = trim(strtoupper($val)); @@ -4154,10 +4156,41 @@ class Calculation } elseif (($localeConstant = array_search(trim(strtoupper($val)), self::$localeBoolean)) !== false) { $stackItemType = 'Constant'; $val = self::$excelConstants[$localeConstant]; + } elseif ( + preg_match('/^' . self::CALCULATION_REGEXP_ROW_RANGE . '/miu', substr($formula, $index), $rowRangeReference) + ) { + $val = $rowRangeReference[1]; + $length = strlen($rowRangeReference[1]); + $stackItemType = 'Row Reference'; + $column = 'A'; + if (($testPrevOp !== null && $testPrevOp['value'] === ':') && $pCellParent !== null) { + $column = $pCellParent->getHighestDataColumn($val); + } + $val = "{$rowRangeReference[2]}{$column}{$rowRangeReference[7]}"; + $stackItemReference = $val; + } elseif ( + preg_match('/^' . self::CALCULATION_REGEXP_COLUMN_RANGE . '/miu', substr($formula, $index), $columnRangeReference) + ) { + $val = $columnRangeReference[1]; + $length = strlen($val); + $stackItemType = 'Column Reference'; + $row = '1'; + if (($testPrevOp !== null && $testPrevOp['value'] === ':') && $pCellParent !== null) { + $row = $pCellParent->getHighestDataRow($val); + } + $val = "{$val}{$row}"; + $stackItemReference = $val; } elseif (preg_match('/^' . self::CALCULATION_REGEXP_DEFINEDNAME . '.*/miu', $val, $match)) { $stackItemType = 'Defined Name'; $stackItemReference = $val; + } elseif (is_numeric($val)) { + if ((strpos($val, '.') !== false) || (stripos($val, 'e') !== false) || ($val > PHP_INT_MAX) || ($val < -PHP_INT_MAX)) { + $val = (float) $val; + } else { + $val = (int) $val; + } } + $details = $stack->getStackItem($stackItemType, $val, $stackItemReference, $currentCondition, $currentOnlyIf, $currentOnlyIfNot); if ($localeConstant) { $details['localeValue'] = $localeConstant; @@ -4431,6 +4464,7 @@ class Calculation } else { return $this->raiseFormulaError('Unable to access Cell Reference'); } + $stack->push('Cell Reference', $cellValue, $cellRef); } else { $stack->push('Error', Functions::REF(), null); @@ -4564,6 +4598,7 @@ class Calculation } } elseif (preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '$/i', $token, $matches)) { $cellRef = null; + if (isset($matches[8])) { if ($pCell === null) { // We can't access the range, so return a REF error @@ -4596,7 +4631,7 @@ class Calculation } } else { if ($pCell === null) { - // We can't access the cell, so return a REF error + // We can't access the cell, so return a REF error $cellValue = Functions::REF(); } else { $cellRef = $matches[6] . $matches[7]; @@ -4613,6 +4648,7 @@ class Calculation $cellValue = $this->extractCellRange($cellRef, $this->spreadsheet->getSheetByName($matches[2]), false); $pCell->attach($pCellParent); } else { + $cellRef = ($cellSheet !== null) ? "{$matches[2]}!{$cellRef}" : $cellRef; $cellValue = null; } } else { @@ -4631,7 +4667,8 @@ class Calculation } } } - $stack->push('Value', $cellValue, $cellRef); + + $stack->push('Cell Value', $cellValue, $cellRef); if (isset($storeKey)) { $branchStore[$storeKey] = $cellValue; } @@ -5116,6 +5153,7 @@ class Calculation if ($pSheet !== null) { $pSheetName = $pSheet->getTitle(); + if (strpos($pRange, '!') !== false) { [$pSheetName, $pRange] = Worksheet::extractSheetTitle($pRange, true); $pSheet = $this->spreadsheet->getSheetByName($pSheetName); diff --git a/src/PhpSpreadsheet/Calculation/MathTrig/Sum.php b/src/PhpSpreadsheet/Calculation/MathTrig/Sum.php index cd29248b..ab3a9a07 100644 --- a/src/PhpSpreadsheet/Calculation/MathTrig/Sum.php +++ b/src/PhpSpreadsheet/Calculation/MathTrig/Sum.php @@ -50,11 +50,13 @@ class Sum public static function funcSumNoStrings(...$args) { $returnValue = 0; - // Loop through the arguments foreach (Functions::flattenArray($args) as $arg) { // Is it a numeric value? - if (is_numeric($arg)) { + if (is_numeric($arg) || empty($arg)) { + if (is_string($arg)) { + $arg = (int) $arg; + } $returnValue += $arg; } elseif (Functions::isError($arg)) { return $arg; diff --git a/tests/PhpSpreadsheetTests/Calculation/RowColumnReferenceTest.php b/tests/PhpSpreadsheetTests/Calculation/RowColumnReferenceTest.php new file mode 100644 index 00000000..8c9d23f7 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Calculation/RowColumnReferenceTest.php @@ -0,0 +1,84 @@ +spreadSheet = new Spreadsheet(); + + $dataSheet = new Worksheet($this->spreadSheet, 'data sheet'); + $this->spreadSheet->addSheet($dataSheet, 0); + $dataSheet->setCellValue('B1', 1.1); + $dataSheet->setCellValue('B2', 2.2); + $dataSheet->setCellValue('B3', 4.4); + $dataSheet->setCellValue('C3', 8.8); + $dataSheet->setCellValue('D3', 16.16); + + $calcSheet = new Worksheet($this->spreadSheet, 'summary sheet'); + $this->spreadSheet->addSheet($calcSheet, 1); + $calcSheet->setCellValue('B1', 2.2); + $calcSheet->setCellValue('B2', 4.4); + $calcSheet->setCellValue('B3', 8.8); + $calcSheet->setCellValue('C3', 16.16); + $calcSheet->setCellValue('D3', 32.32); + + $this->spreadSheet->setActiveSheetIndexByName('summary sheet'); + } + + /** + * @dataProvider providerCurrentWorksheetFormulae + */ + public function testCurrentWorksheet(string $formula, float $expectedResult): void + { + $worksheet = $this->spreadSheet->getActiveSheet(); + + $worksheet->setCellValue('A1', $formula); + + $result = $worksheet->getCell('A1')->getCalculatedValue(); + self::assertSame($expectedResult, $result); + } + + public function providerCurrentWorksheetFormulae(): array + { + return [ + 'relative range in active worksheet' => ['=SUM(B1:B3)', 15.4], + 'range with absolute columns in active worksheet' => ['=SUM($B1:$B3)', 15.4], + 'range with absolute rows in active worksheet' => ['=SUM(B$1:B$3)', 15.4], + 'range with absolute columns and rows in active worksheet' => ['=SUM($B$1:$B$3)', 15.4], + 'another relative range in active worksheet' => ['=SUM(B3:D3)', 57.28], + 'relative column range in active worksheet' => ['=SUM(B:B)', 15.4], + 'absolute column range in active worksheet' => ['=SUM($B:$B)', 15.4], + 'relative row range in active worksheet' => ['=SUM(3:3)', 57.28], + 'absolute row range in active worksheet' => ['=SUM($3:$3)', 57.28], + 'relative range in specified active worksheet' => ['=SUM(\'summary sheet\'!B1:B3)', 15.4], + 'range with absolute columns in specified active worksheet' => ['=SUM(\'summary sheet\'!$B1:$B3)', 15.4], + 'range with absolute rows in specified active worksheet' => ['=SUM(\'summary sheet\'!B$1:B$3)', 15.4], + 'range with absolute columns and rows in specified active worksheet' => ['=SUM(\'summary sheet\'!$B$1:$B$3)', 15.4], + 'another relative range in specified active worksheet' => ['=SUM(\'summary sheet\'!B3:D3)', 57.28], + 'relative column range in specified active worksheet' => ['=SUM(\'summary sheet\'!B:B)', 15.4], + 'absolute column range in specified active worksheet' => ['=SUM(\'summary sheet\'!$B:$B)', 15.4], + 'relative row range in specified active worksheet' => ['=SUM(\'summary sheet\'!3:3)', 57.28], + 'absolute row range in specified active worksheet' => ['=SUM(\'summary sheet\'!$3:$3)', 57.28], + 'relative range in specified other worksheet' => ['=SUM(\'data sheet\'!B1:B3)', 7.7], + 'range with absolute columns in specified other worksheet' => ['=SUM(\'data sheet\'!$B1:$B3)', 7.7], + 'range with absolute rows in specified other worksheet' => ['=SUM(\'data sheet\'!B$1:B$3)', 7.7], + 'range with absolute columns and rows in specified other worksheet' => ['=SUM(\'data sheet\'!$B$1:$B$3)', 7.7], + 'another relative range in specified other worksheet' => ['=SUM(\'data sheet\'!B3:D3)', 29.36], + 'relative column range in specified other worksheet' => ['=SUM(\'data sheet\'!B:B)', 7.7], + 'absolute column range in specified other worksheet' => ['=SUM(\'data sheet\'!$B:$B)', 7.7], + 'relative row range in specified other worksheet' => ['=SUM(\'data sheet\'!3:3)', 29.36], + 'absolute row range in specified other worksheet' => ['=SUM(\'data sheet\'!$3:$3)', 29.36], + ]; + } +} From d118a7070bfd336892ffe76aa3b8756c7eeecdb5 Mon Sep 17 00:00:00 2001 From: Mark Baker Date: Wed, 28 Apr 2021 14:08:20 +0200 Subject: [PATCH 11/57] Completion of refactoring for Excel Lookup and Reference functions (#2030) * Completion of refactoring for Excel Lookup and Reference functions * Fix LookupRef tests checking for cell existence * Fix a couple of now invalid callable references in the Calculation Engine lookup table --- phpstan-baseline.neon | 10 -- .../Calculation/Calculation.php | 14 +-- src/PhpSpreadsheet/Calculation/DateTime.php | 3 + src/PhpSpreadsheet/Calculation/LookupRef.php | 119 ++++++++---------- .../Calculation/LookupRef/Formula.php | 43 +++++++ .../Calculation/LookupRef/Hyperlink.php | 40 ++++++ .../Calculation/LookupRef/Selection.php | 46 +++++++ src/PhpSpreadsheet/Calculation/MathTrig.php | 3 + .../Calculation/LookupRefTest.php | 4 + 9 files changed, 195 insertions(+), 87 deletions(-) create mode 100644 src/PhpSpreadsheet/Calculation/LookupRef/Formula.php create mode 100644 src/PhpSpreadsheet/Calculation/LookupRef/Hyperlink.php create mode 100644 src/PhpSpreadsheet/Calculation/LookupRef/Selection.php diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index e9a33fc8..e07db455 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -825,16 +825,6 @@ parameters: count: 1 path: src/PhpSpreadsheet/Calculation/LookupRef.php - - - message: "#^Call to function is_bool\\(\\) with float\\|int\\|\\(string&numeric\\) will always evaluate to false\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef.php - - - - message: "#^Cannot call method getCell\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\|null\\.$#" - count: 2 - path: src/PhpSpreadsheet/Calculation/LookupRef.php - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\Address\\:\\:sheetName\\(\\) has no return typehint specified\\.$#" count: 1 diff --git a/src/PhpSpreadsheet/Calculation/Calculation.php b/src/PhpSpreadsheet/Calculation/Calculation.php index abd997a3..35769434 100644 --- a/src/PhpSpreadsheet/Calculation/Calculation.php +++ b/src/PhpSpreadsheet/Calculation/Calculation.php @@ -531,7 +531,7 @@ class Calculation ], 'CHOOSE' => [ 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE, - 'functionCall' => [LookupRef::class, 'CHOOSE'], + 'functionCall' => [LookupRef\Selection::class, 'CHOOSE'], 'argumentCount' => '2+', ], 'CLEAN' => [ @@ -793,7 +793,7 @@ class Calculation ], 'DB' => [ 'category' => Category::CATEGORY_FINANCIAL, - 'functionCall' => [Financial::class, 'DB'], + 'functionCall' => [Financial\Depreciation::class, 'DB'], 'argumentCount' => '4,5', ], 'DBCS' => [ @@ -813,7 +813,7 @@ class Calculation ], 'DDB' => [ 'category' => Category::CATEGORY_FINANCIAL, - 'functionCall' => [Financial::class, 'DDB'], + 'functionCall' => [Financial\Depreciation::class, 'DDB'], 'argumentCount' => '4,5', ], 'DEC2BIN' => [ @@ -1118,7 +1118,7 @@ class Calculation ], 'FORMULATEXT' => [ 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE, - 'functionCall' => [LookupRef::class, 'FORMULATEXT'], + 'functionCall' => [LookupRef\Formula::class, 'text'], 'argumentCount' => '1', 'passCellReference' => true, 'passByReference' => [true], @@ -1245,7 +1245,7 @@ class Calculation ], 'HYPERLINK' => [ 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE, - 'functionCall' => [LookupRef::class, 'HYPERLINK'], + 'functionCall' => [LookupRef\Hyperlink::class, 'set'], 'argumentCount' => '1,2', 'passCellReference' => true, ], @@ -2604,7 +2604,7 @@ class Calculation ], 'XIRR' => [ 'category' => Category::CATEGORY_FINANCIAL, - 'functionCall' => [Financial::class, 'XIRR'], + 'functionCall' => [Financial\CashFlow\Variable\NonPeriodic::class, 'rate'], 'argumentCount' => '2,3', ], 'XLOOKUP' => [ @@ -2614,7 +2614,7 @@ class Calculation ], 'XNPV' => [ 'category' => Category::CATEGORY_FINANCIAL, - 'functionCall' => [Financial::class, 'XNPV'], + 'functionCall' => [Financial\CashFlow\Variable\NonPeriodic::class, 'presentValue'], 'argumentCount' => '3', ], 'XMATCH' => [ diff --git a/src/PhpSpreadsheet/Calculation/DateTime.php b/src/PhpSpreadsheet/Calculation/DateTime.php index c0a38f56..4959c957 100644 --- a/src/PhpSpreadsheet/Calculation/DateTime.php +++ b/src/PhpSpreadsheet/Calculation/DateTime.php @@ -4,6 +4,9 @@ namespace PhpOffice\PhpSpreadsheet\Calculation; use DateTimeInterface; +/** + * @deprecated 1.18.0 + */ class DateTime { /** diff --git a/src/PhpSpreadsheet/Calculation/LookupRef.php b/src/PhpSpreadsheet/Calculation/LookupRef.php index 5adf4f09..c213396d 100644 --- a/src/PhpSpreadsheet/Calculation/LookupRef.php +++ b/src/PhpSpreadsheet/Calculation/LookupRef.php @@ -13,6 +13,9 @@ use PhpOffice\PhpSpreadsheet\Calculation\LookupRef\VLookup; use PhpOffice\PhpSpreadsheet\Cell\Cell; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; +/** + * @deprecated 1.18.0 + */ class LookupRef { /** @@ -25,7 +28,8 @@ class LookupRef * * @Deprecated 1.18.0 * - * @see Use the cell() method in the LookupRef\Address class instead + * @see LookupRef\Address::cell() + * Use the cell() method in the LookupRef\Address class instead * * @param mixed $row Row number to use in the cell reference * @param mixed $column Column number to use in the cell reference @@ -61,11 +65,12 @@ class LookupRef * * @Deprecated 1.18.0 * - * @see Use the COLUMN() method in the LookupRef\RowColumnInformation class instead + * @see LookupRef\RowColumnInformation::COLUMN() + * Use the COLUMN() method in the LookupRef\RowColumnInformation class instead * * @param null|array|string $cellAddress A reference to a range of cells for which you want the column numbers * - * @return int|int[] + * @return int|int[]|string */ public static function COLUMN($cellAddress = null, ?Cell $cell = null) { @@ -82,7 +87,8 @@ class LookupRef * * @Deprecated 1.18.0 * - * @see Use the COLUMNS() method in the LookupRef\RowColumnInformation class instead + * @see LookupRef\RowColumnInformation::COLUMNS() + * Use the COLUMNS() method in the LookupRef\RowColumnInformation class instead * * @param null|array|string $cellAddress An array or array formula, or a reference to a range of cells * for which you want the number of columns @@ -109,7 +115,8 @@ class LookupRef * * @Deprecated 1.18.0 * - * @see Use the ROW() method in the LookupRef\RowColumnInformation class instead + * @see LookupRef\RowColumnInformation::ROW() + * Use the ROW() method in the LookupRef\RowColumnInformation class instead * * @param null|array|string $cellAddress A reference to a range of cells for which you want the row numbers * @@ -130,7 +137,8 @@ class LookupRef * * @Deprecated 1.18.0 * - * @see Use the ROWS() method in the LookupRef\RowColumnInformation class instead + * @see LookupRef\RowColumnInformation::ROWS() + * Use the ROWS() method in the LookupRef\RowColumnInformation class instead * * @param null|array|string $cellAddress An array or array formula, or a reference to a range of cells * for which you want the number of rows @@ -148,29 +156,20 @@ class LookupRef * Excel Function: * =HYPERLINK(linkURL,displayName) * + * @Deprecated 1.18.0 + * + * @see LookupRef\Hyperlink::set() + * Use the set() method in the LookupRef\Hyperlink class instead + * * @param mixed $linkURL Expect string. Value to check, is also the value returned when no error * @param mixed $displayName Expect string. Value to return when testValue is an error condition * @param Cell $pCell The cell to set the hyperlink in * - * @return mixed The value of $displayName (or $linkURL if $displayName was blank) + * @return string The value of $displayName (or $linkURL if $displayName was blank) */ public static function HYPERLINK($linkURL = '', $displayName = null, ?Cell $pCell = null) { - $linkURL = ($linkURL === null) ? '' : Functions::flattenSingleValue($linkURL); - $displayName = ($displayName === null) ? '' : Functions::flattenSingleValue($displayName); - - if ((!is_object($pCell)) || (trim($linkURL) == '')) { - return Functions::REF(); - } - - if ((is_object($displayName)) || trim($displayName) == '') { - $displayName = $linkURL; - } - - $pCell->getHyperlink()->setUrl($linkURL); - $pCell->getHyperlink()->setTooltip($displayName); - - return $displayName; + return LookupRef\Hyperlink::set($linkURL, $displayName, $pCell); } /** @@ -184,7 +183,8 @@ class LookupRef * * @Deprecated 1.18.0 * - * @see Use the INDIRECT() method in the LookupRef\Indirect class instead + * @see LookupRef\Indirect::INDIRECT() + * Use the INDIRECT() method in the LookupRef\Indirect class instead * * NOTE - INDIRECT() does not yet support the optional a1 parameter introduced in Excel 2010 * @@ -192,8 +192,6 @@ class LookupRef * @param Cell $pCell The current cell (containing this formula) * * @return array|string An array containing a cell or range of cells, or a string on error - * - * @TODO Support for the optional a1 parameter introduced in Excel 2010 */ public static function INDIRECT($cellAddress, Cell $pCell) { @@ -212,7 +210,8 @@ class LookupRef * * @Deprecated 1.18.0 * - * @see Use the OFFSET() method in the LookupRef\Offset class instead + * @see LookupRef\Offset::OFFSET() + * Use the OFFSET() method in the LookupRef\Offset class instead * * @param null|string $cellAddress The reference from which you want to base the offset. * Reference must refer to a cell or range of adjacent cells; @@ -248,31 +247,16 @@ class LookupRef * Excel Function: * =CHOOSE(index_num, value1, [value2], ...) * + * @Deprecated 1.18.0 + * + * @see LookupRef\Selection::choose() + * Use the choose() method in the LookupRef\Selection class instead + * * @return mixed The selected value */ public static function CHOOSE(...$chooseArgs) { - $chosenEntry = Functions::flattenArray(array_shift($chooseArgs)); - $entryCount = count($chooseArgs) - 1; - - if (is_array($chosenEntry)) { - $chosenEntry = array_shift($chosenEntry); - } - if ((is_numeric($chosenEntry)) && (!is_bool($chosenEntry))) { - --$chosenEntry; - } else { - return Functions::VALUE(); - } - $chosenEntry = floor($chosenEntry); - if (($chosenEntry < 0) || ($chosenEntry > $entryCount)) { - return Functions::VALUE(); - } - - if (is_array($chooseArgs[$chosenEntry])) { - return Functions::flattenArray($chooseArgs[$chosenEntry]); - } - - return $chooseArgs[$chosenEntry]; + return LookupRef\Selection::choose(...$chooseArgs); } /** @@ -285,7 +269,8 @@ class LookupRef * * @Deprecated 1.18.0 * - * @see Use the MATCH() method in the LookupRef\ExcelMatch class instead + * @see LookupRef\ExcelMatch::MATCH() + * Use the MATCH() method in the LookupRef\ExcelMatch class instead * * @param mixed $lookupValue The value that you want to match in lookup_array * @param mixed $lookupArray The range of cells being searched @@ -309,7 +294,8 @@ class LookupRef * * @Deprecated 1.18.0 * - * @see Use the index() method in the LookupRef\Matrix class instead + * @see LookupRef\Matrix::index() + * Use the index() method in the LookupRef\Matrix class instead * * @param mixed $rowNum The row in the array or range from which to return a value. * If row_num is omitted, column_num is required. @@ -329,7 +315,8 @@ class LookupRef * * @Deprecated 1.18.0 * - * @see Use the transpose() method in the LookupRef\Matrix class instead + * @see LookupRef\Matrix::transpose() + * Use the transpose() method in the LookupRef\Matrix class instead * * @param array $matrixData A matrix of values * @@ -350,7 +337,8 @@ class LookupRef * * @Deprecated 1.18.0 * - * @see Use the lookup() method in the LookupRef\VLookup class instead + * @see LookupRef\VLookup::lookup() + * Use the lookup() method in the LookupRef\VLookup class instead * * @param mixed $lookup_value The value that you want to match in lookup_array * @param mixed $lookup_array The range of cells being searched @@ -372,7 +360,8 @@ class LookupRef * * @Deprecated 1.18.0 * - * @see Use the lookup() method in the LookupRef\HLookup class instead + * @see LookupRef\HLookup::lookup() + * Use the lookup() method in the LookupRef\HLookup class instead * * @param mixed $lookup_value The value that you want to match in lookup_array * @param mixed $lookup_array The range of cells being searched @@ -393,7 +382,8 @@ class LookupRef * * @Deprecated 1.18.0 * - * @see Use the lookup() method in the LookupRef\Lookup class instead + * @see LookupRef\Lookup::lookup() + * Use the lookup() method in the LookupRef\Lookup class instead * * @param mixed $lookup_value The value that you want to match in lookup_array * @param mixed $lookup_vector The range of cells being searched @@ -409,6 +399,11 @@ class LookupRef /** * FORMULATEXT. * + * @Deprecated 1.18.0 + * + * @see LookupRef\Formula::text() + * Use the text() method in the LookupRef\Formula class instead + * * @param mixed $cellReference The cell to check * @param Cell $pCell The current cell (containing this formula) * @@ -416,22 +411,6 @@ class LookupRef */ public static function FORMULATEXT($cellReference = '', ?Cell $pCell = null) { - if ($pCell === null) { - return Functions::REF(); - } - - preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $cellReference, $matches); - - $cellReference = $matches[6] . $matches[7]; - $worksheetName = trim($matches[3], "'"); - $worksheet = (!empty($worksheetName)) - ? $pCell->getWorksheet()->getParent()->getSheetByName($worksheetName) - : $pCell->getWorksheet(); - - if (!$worksheet->getCell($cellReference)->isFormula()) { - return Functions::NA(); - } - - return $worksheet->getCell($cellReference)->getValue(); + return LookupRef\Formula::text($cellReference, $pCell); } } diff --git a/src/PhpSpreadsheet/Calculation/LookupRef/Formula.php b/src/PhpSpreadsheet/Calculation/LookupRef/Formula.php new file mode 100644 index 00000000..d96b5040 --- /dev/null +++ b/src/PhpSpreadsheet/Calculation/LookupRef/Formula.php @@ -0,0 +1,43 @@ +getWorksheet()->getParent()->getSheetByName($worksheetName) + : $pCell->getWorksheet(); + + if ( + $worksheet === null || + !$worksheet->cellExists($cellReference) || + !$worksheet->getCell($cellReference)->isFormula() + ) { + return Functions::NA(); + } + + return $worksheet->getCell($cellReference)->getValue(); + } +} diff --git a/src/PhpSpreadsheet/Calculation/LookupRef/Hyperlink.php b/src/PhpSpreadsheet/Calculation/LookupRef/Hyperlink.php new file mode 100644 index 00000000..d0324964 --- /dev/null +++ b/src/PhpSpreadsheet/Calculation/LookupRef/Hyperlink.php @@ -0,0 +1,40 @@ +getHyperlink()->setUrl($linkURL); + $pCell->getHyperlink()->setTooltip($displayName); + + return $displayName; + } +} diff --git a/src/PhpSpreadsheet/Calculation/LookupRef/Selection.php b/src/PhpSpreadsheet/Calculation/LookupRef/Selection.php new file mode 100644 index 00000000..6c18d73b --- /dev/null +++ b/src/PhpSpreadsheet/Calculation/LookupRef/Selection.php @@ -0,0 +1,46 @@ + $entryCount)) { + return Functions::VALUE(); + } + + if (is_array($chooseArgs[$chosenEntry])) { + return Functions::flattenArray($chooseArgs[$chosenEntry]); + } + + return $chooseArgs[$chosenEntry]; + } +} diff --git a/src/PhpSpreadsheet/Calculation/MathTrig.php b/src/PhpSpreadsheet/Calculation/MathTrig.php index 8398ba13..fad381b3 100644 --- a/src/PhpSpreadsheet/Calculation/MathTrig.php +++ b/src/PhpSpreadsheet/Calculation/MathTrig.php @@ -2,6 +2,9 @@ namespace PhpOffice\PhpSpreadsheet\Calculation; +/** + * @deprecated 1.18.0 + */ class MathTrig { /** diff --git a/tests/PhpSpreadsheetTests/Calculation/LookupRefTest.php b/tests/PhpSpreadsheetTests/Calculation/LookupRefTest.php index af8b8079..9f4c90b3 100644 --- a/tests/PhpSpreadsheetTests/Calculation/LookupRefTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/LookupRefTest.php @@ -41,6 +41,8 @@ class LookupRefTest extends TestCase $remoteSheet = $this->getMockBuilder(Worksheet::class) ->disableOriginalConstructor() ->getMock(); + $remoteSheet->method('cellExists') + ->willReturn(true); $remoteSheet->method('getCell') ->willReturn($remoteCell); @@ -53,6 +55,8 @@ class LookupRefTest extends TestCase $sheet = $this->getMockBuilder(Worksheet::class) ->disableOriginalConstructor() ->getMock(); + $sheet->method('cellExists') + ->willReturn(true); $sheet->method('getCell') ->willReturn($remoteCell); $sheet->method('getParent') From 44ebae943861803bdd241846ba17fe29c283e61c Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Wed, 28 Apr 2021 14:20:32 +0200 Subject: [PATCH 12/57] Fix a couple of now invalid callable references in the Calculation Engine lookup table --- src/PhpSpreadsheet/Calculation/Calculation.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PhpSpreadsheet/Calculation/Calculation.php b/src/PhpSpreadsheet/Calculation/Calculation.php index abd997a3..a17c4674 100644 --- a/src/PhpSpreadsheet/Calculation/Calculation.php +++ b/src/PhpSpreadsheet/Calculation/Calculation.php @@ -1406,7 +1406,7 @@ class Calculation ], 'INDEX' => [ 'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE, - 'functionCall' => [LookupRef::class, 'INDEX'], + 'functionCall' => [LookupRef\Matrix::class, 'index'], 'argumentCount' => '1-4', ], 'INDIRECT' => [ @@ -2419,7 +2419,7 @@ class Calculation ], 'TEXTJOIN' => [ 'category' => Category::CATEGORY_TEXT_AND_DATA, - 'functionCall' => [TextData::class, 'TEXTJOIN'], + 'functionCall' => [TextData\Concatenate::class, 'TEXTJOIN'], 'argumentCount' => '3+', ], 'TIME' => [ From 475874bed3aadcf5a1af3ee1a5784bd4ac2d1577 Mon Sep 17 00:00:00 2001 From: Mark Baker Date: Wed, 28 Apr 2021 17:10:36 +0200 Subject: [PATCH 13/57] Initial implementation of the URLENCODE() web function (#2031) * Initial implementation of the URLENCODE() web function --- .../Calculation/Calculation.php | 4 +- src/PhpSpreadsheet/Calculation/Database.php | 36 ++++++--- src/PhpSpreadsheet/Calculation/Web.php | 40 ++-------- .../Calculation/Web/Service.php | 75 +++++++++++++++++++ .../Functions/Web/UrlEncodeTest.php | 26 +++++++ tests/data/Calculation/Web/URLENCODE.php | 12 +++ 6 files changed, 146 insertions(+), 47 deletions(-) create mode 100644 src/PhpSpreadsheet/Calculation/Web/Service.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Web/UrlEncodeTest.php create mode 100644 tests/data/Calculation/Web/URLENCODE.php diff --git a/src/PhpSpreadsheet/Calculation/Calculation.php b/src/PhpSpreadsheet/Calculation/Calculation.php index f24cbeb5..1a0fb6d7 100644 --- a/src/PhpSpreadsheet/Calculation/Calculation.php +++ b/src/PhpSpreadsheet/Calculation/Calculation.php @@ -933,7 +933,7 @@ class Calculation ], 'ENCODEURL' => [ 'category' => Category::CATEGORY_WEB, - 'functionCall' => [Functions::class, 'DUMMY'], + 'functionCall' => [Web\Service::class, 'urlEncode'], 'argumentCount' => '1', ], 'EOMONTH' => [ @@ -2569,7 +2569,7 @@ class Calculation ], 'WEBSERVICE' => [ 'category' => Category::CATEGORY_WEB, - 'functionCall' => [Web::class, 'WEBSERVICE'], + 'functionCall' => [Web\Service::class, 'webService'], 'argumentCount' => '1', ], 'WEEKDAY' => [ diff --git a/src/PhpSpreadsheet/Calculation/Database.php b/src/PhpSpreadsheet/Calculation/Database.php index a4c4d7d2..65031674 100644 --- a/src/PhpSpreadsheet/Calculation/Database.php +++ b/src/PhpSpreadsheet/Calculation/Database.php @@ -17,7 +17,8 @@ class Database * * @Deprecated 1.17.0 * - * @see Use the evaluate() method in the Database\DAverage class instead + * @see Database\DAverage::evaluate() + * Use the evaluate() method in the Database\DAverage class instead * * @param mixed[] $database The range of cells that makes up the list or database. * A database is a list of related data in which rows of related @@ -52,7 +53,8 @@ class Database * * @Deprecated 1.17.0 * - * @see Use the evaluate() method in the Database\DCount class instead + * @see Database\DCount::evaluate() + * Use the evaluate() method in the Database\DCount class instead * * @param mixed[] $database The range of cells that makes up the list or database. * A database is a list of related data in which rows of related @@ -89,7 +91,8 @@ class Database * * @Deprecated 1.17.0 * - * @see Use the evaluate() method in the Database\DCountA class instead + * @see Database\DCountA::evaluate() + * Use the evaluate() method in the Database\DCountA class instead * * @param mixed[] $database The range of cells that makes up the list or database. * A database is a list of related data in which rows of related @@ -124,7 +127,8 @@ class Database * * @Deprecated 1.17.0 * - * @see Use the evaluate() method in the Database\DGet class instead + * @see Database\DGet::evaluate() + * Use the evaluate() method in the Database\DGet class instead * * @param mixed[] $database The range of cells that makes up the list or database. * A database is a list of related data in which rows of related @@ -159,7 +163,8 @@ class Database * * @Deprecated 1.17.0 * - * @see Use the evaluate() method in the Database\DMax class instead + * @see Database\DMax::evaluate() + * Use the evaluate() method in the Database\DMax class instead * * @param mixed[] $database The range of cells that makes up the list or database. * A database is a list of related data in which rows of related @@ -194,7 +199,8 @@ class Database * * @Deprecated 1.17.0 * - * @see Use the evaluate() method in the Database\DMin class instead + * @see Database\DMin::evaluate() + * Use the evaluate() method in the Database\DMin class instead * * @param mixed[] $database The range of cells that makes up the list or database. * A database is a list of related data in which rows of related @@ -228,7 +234,8 @@ class Database * * @Deprecated 1.17.0 * - * @see Use the evaluate() method in the Database\DProduct class instead + * @see Database\DProduct::evaluate() + * Use the evaluate() method in the Database\DProduct class instead * * @param mixed[] $database The range of cells that makes up the list or database. * A database is a list of related data in which rows of related @@ -263,7 +270,8 @@ class Database * * @Deprecated 1.17.0 * - * @see Use the evaluate() method in the Database\DStDev class instead + * @see Database\DStDev::evaluate() + * Use the evaluate() method in the Database\DStDev class instead * * @param mixed[] $database The range of cells that makes up the list or database. * A database is a list of related data in which rows of related @@ -298,7 +306,8 @@ class Database * * @Deprecated 1.17.0 * - * @see Use the evaluate() method in the Database\DStDevP class instead + * @see Database\DStDevP::evaluate() + * Use the evaluate() method in the Database\DStDevP class instead * * @param mixed[] $database The range of cells that makes up the list or database. * A database is a list of related data in which rows of related @@ -332,7 +341,8 @@ class Database * * @Deprecated 1.17.0 * - * @see Use the evaluate() method in the Database\DSum class instead + * @see Database\DSum::evaluate() + * Use the evaluate() method in the Database\DSum class instead * * @param mixed[] $database The range of cells that makes up the list or database. * A database is a list of related data in which rows of related @@ -367,7 +377,8 @@ class Database * * @Deprecated 1.17.0 * - * @see Use the evaluate() method in the Database\DVar class instead + * @see Database\DVar::evaluate() + * Use the evaluate() method in the Database\DVar class instead * * @param mixed[] $database The range of cells that makes up the list or database. * A database is a list of related data in which rows of related @@ -402,7 +413,8 @@ class Database * * @Deprecated 1.17.0 * - * @see Use the evaluate() method in the Database\DVarP class instead + * @see Database\DVarP::evaluate() + * Use the evaluate() method in the Database\DVarP class instead * * @param mixed[] $database The range of cells that makes up the list or database. * A database is a list of related data in which rows of related diff --git a/src/PhpSpreadsheet/Calculation/Web.php b/src/PhpSpreadsheet/Calculation/Web.php index 5cfd2ea8..3f3d945d 100644 --- a/src/PhpSpreadsheet/Calculation/Web.php +++ b/src/PhpSpreadsheet/Calculation/Web.php @@ -2,9 +2,9 @@ namespace PhpOffice\PhpSpreadsheet\Calculation; -use PhpOffice\PhpSpreadsheet\Settings; -use Psr\Http\Client\ClientExceptionInterface; - +/** + * @deprecated 1.18.0 + */ class Web { /** @@ -15,39 +15,13 @@ class Web * Excel Function: * Webservice(url) * + * @see Web\Service::webService() + * Use the webService() method in the Web\Service class instead + * * @return string the output resulting from a call to the webservice */ public static function WEBSERVICE(string $url) { - $url = trim($url); - if (strlen($url) > 2048) { - return Functions::VALUE(); // Invalid URL length - } - - if (!preg_match('/^http[s]?:\/\//', $url)) { - return Functions::VALUE(); // Invalid protocol - } - - // Get results from the the webservice - $client = Settings::getHttpClient(); - $requestFactory = Settings::getRequestFactory(); - $request = $requestFactory->createRequest('GET', $url); - - try { - $response = $client->sendRequest($request); - } catch (ClientExceptionInterface $e) { - return Functions::VALUE(); // cURL error - } - - if ($response->getStatusCode() != 200) { - return Functions::VALUE(); // cURL error - } - - $output = $response->getBody()->getContents(); - if (strlen($output) > 32767) { - return Functions::VALUE(); // Output not a string or too long - } - - return $output; + return Web\Service::webService($url); } } diff --git a/src/PhpSpreadsheet/Calculation/Web/Service.php b/src/PhpSpreadsheet/Calculation/Web/Service.php new file mode 100644 index 00000000..05e04bf9 --- /dev/null +++ b/src/PhpSpreadsheet/Calculation/Web/Service.php @@ -0,0 +1,75 @@ + 2048) { + return Functions::VALUE(); // Invalid URL length + } + + if (!preg_match('/^http[s]?:\/\//', $url)) { + return Functions::VALUE(); // Invalid protocol + } + + // Get results from the the webservice + $client = Settings::getHttpClient(); + $requestFactory = Settings::getRequestFactory(); + $request = $requestFactory->createRequest('GET', $url); + + try { + $response = $client->sendRequest($request); + } catch (ClientExceptionInterface $e) { + return Functions::VALUE(); // cURL error + } + + if ($response->getStatusCode() != 200) { + return Functions::VALUE(); // cURL error + } + + $output = $response->getBody()->getContents(); + if (strlen($output) > 32767) { + return Functions::VALUE(); // Output not a string or too long + } + + return $output; + } + + /** + * URLENCODE. + * + * Returns data from a web service on the Internet or Intranet. + * + * Excel Function: + * urlEncode(text) + * + * @param mixed $text + * + * @return string the url encoded output + */ + public static function urlEncode($text) + { + if (!is_string($text)) { + return Functions::VALUE(); + } + + return str_replace('+', '%20', urlencode($text)); + } +} diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/Web/UrlEncodeTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/Web/UrlEncodeTest.php new file mode 100644 index 00000000..3ce6098d --- /dev/null +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/Web/UrlEncodeTest.php @@ -0,0 +1,26 @@ + Date: Thu, 29 Apr 2021 14:15:45 +0200 Subject: [PATCH 14/57] Resolve problem where underscore placeholder in a number format masks (#2038) * Resolve problem where underscore placeholder in a number format mask was being replaced, but leaving the sizing character as part of the mask --- .../Style/NumberFormat/Formatter.php | 2 +- .../Style/NumberFormat/NumberFormatter.php | 7 ++++- tests/data/Style/NumberFormat.php | 30 +++++++++++++++---- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/PhpSpreadsheet/Style/NumberFormat/Formatter.php b/src/PhpSpreadsheet/Style/NumberFormat/Formatter.php index ef756d7b..01407e64 100644 --- a/src/PhpSpreadsheet/Style/NumberFormat/Formatter.php +++ b/src/PhpSpreadsheet/Style/NumberFormat/Formatter.php @@ -130,7 +130,7 @@ class Formatter // In Excel formats, "_" is used to add spacing, // The following character indicates the size of the spacing, which we can't do in HTML, so we just use a standard space - $format = preg_replace('/_/ui', ' ', $format); + $format = preg_replace('/_.?/ui', ' ', $format); // Let's begin inspecting the format and converting the value to a formatted string diff --git a/src/PhpSpreadsheet/Style/NumberFormat/NumberFormatter.php b/src/PhpSpreadsheet/Style/NumberFormat/NumberFormatter.php index 9a4f32ac..0a94c333 100644 --- a/src/PhpSpreadsheet/Style/NumberFormat/NumberFormatter.php +++ b/src/PhpSpreadsheet/Style/NumberFormat/NumberFormatter.php @@ -3,6 +3,7 @@ namespace PhpOffice\PhpSpreadsheet\Style\NumberFormat; use PhpOffice\PhpSpreadsheet\Shared\StringHelper; +use PhpOffice\PhpSpreadsheet\Style\NumberFormat; class NumberFormatter { @@ -145,7 +146,6 @@ class NumberFormatter $format = preg_replace('/0,+/', '0', $format); $format = preg_replace('/#,+/', '#', $format); } - if (preg_match('/#?.*\?\/\?/', $format, $m)) { if ($value != (int) $value) { $value = FractionFormatter::format($value, $format); @@ -163,7 +163,12 @@ class NumberFormatter $n = '/\\[[^\\]]+\\]/'; $m = preg_replace($n, '', $format); if (preg_match(self::NUMBER_REGEX, $m, $matches)) { + // There are placeholders for digits, so inject digits from the value into the mask $value = self::formatStraightNumericValue($value, $format, $matches, $useThousands); + } elseif ($format !== NumberFormat::FORMAT_GENERAL) { + // Yes, I know that this is basically just a hack; + // if there's no placeholders for digits, just return the format mask "as is" + $value = str_replace('?', '', $format ?? ''); } } diff --git a/tests/data/Style/NumberFormat.php b/tests/data/Style/NumberFormat.php index aee357a7..92a38cce 100644 --- a/tests/data/Style/NumberFormat.php +++ b/tests/data/Style/NumberFormat.php @@ -131,7 +131,7 @@ return [ 'Spacing Character' => [ '826.00 €', 826, - '#,##0.00 _€', + '#,##0.00 __€', ], [ '5.68', @@ -263,7 +263,7 @@ return [ [ '( 12.30% )', -0.123, - '_0.00%_;( 0.00% )', + '_(0.00%_;( 0.00% )', ], // Fraction [ @@ -374,15 +374,35 @@ return [ '[$-1010409]#,##0.00;-#,##0.00', ], [ - ' ($ 23.06 )', + ' $ 23.06 ', 23.0597, '_("$"* #,##0.00_);_("$"* \(#,##0.00\);_("$"* "-"??_);_(@_)', ], [ - ' (€ 13.03 )', - 13.0316, + ' € (13.03)', + -13.0316, '_("€"* #,##0.00_);_("€"* \(#,##0.00\);_("€"* "-"??_);_(@_)', ], + [ + ' € 11.70 ', + 11.7, + '_-€* #,##0.00_-;"-€"* #,##0.00_-;_-€* -??_-;_-@_-', + ], + [ + '-€ 12.14 ', + -12.14, + '_-€* #,##0.00_-;"-€"* #,##0.00_-;_-€* -??_-;_-@_-', + ], + [ + ' € - ', + 0, + '_-€* #,##0.00_-;"-€"* #,##0.00_-;_-€* -??_-;_-@_-', + ], + [ + 'test', + 'test', + '_-€* #,##0.00_-;"-€"* #,##0.00_-;_-€* -??_-;_-@_-', + ], // Named colours // Simple color [ From e4973fa0418effba52641b5989be0c0f75afcad5 Mon Sep 17 00:00:00 2001 From: Mark Baker Date: Thu, 29 Apr 2021 14:34:50 +0200 Subject: [PATCH 15/57] Start work on refactoring the last of the Excel Statistical functions (#2033) * Refactoring the last of the Excel Statistical functions --- phpstan-baseline.neon | 5 - .../Calculation/Calculation.php | 26 +- .../Calculation/Statistical.php | 299 ++++-------------- .../Calculation/Statistical/Averages/Mean.php | 131 ++++++++ .../Calculation/Statistical/Deviations.php | 141 +++++++++ .../Distributions/StandardNormal.php | 20 ++ .../Calculation/Statistical/Size.php | 96 ++++++ .../Calculation/Statistical/Standardize.php | 41 +++ .../Functions/Statistical/DevSqTest.php | 31 ++ tests/data/Calculation/Statistical/DEVSQ.php | 20 ++ .../data/Calculation/Statistical/HARMEAN.php | 4 + tests/data/Calculation/Statistical/KURT.php | 8 + tests/data/Calculation/Statistical/SKEW.php | 8 + .../Calculation/Statistical/STANDARDIZE.php | 2 + 14 files changed, 581 insertions(+), 251 deletions(-) create mode 100644 src/PhpSpreadsheet/Calculation/Statistical/Averages/Mean.php create mode 100644 src/PhpSpreadsheet/Calculation/Statistical/Deviations.php create mode 100644 src/PhpSpreadsheet/Calculation/Statistical/Size.php create mode 100644 src/PhpSpreadsheet/Calculation/Statistical/Standardize.php create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/DevSqTest.php create mode 100644 tests/data/Calculation/Statistical/DEVSQ.php diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index e07db455..89355400 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1085,11 +1085,6 @@ parameters: count: 1 path: src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php - - - message: "#^Binary operation \"\\-\" between float\\|int and float\\|string results in an error\\.$#" - count: 4 - path: src/PhpSpreadsheet/Calculation/Statistical.php - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Statistical\\:\\:MAXIFS\\(\\) should return float but returns float\\|string\\|null\\.$#" count: 1 diff --git a/src/PhpSpreadsheet/Calculation/Calculation.php b/src/PhpSpreadsheet/Calculation/Calculation.php index 1a0fb6d7..54d9a6e8 100644 --- a/src/PhpSpreadsheet/Calculation/Calculation.php +++ b/src/PhpSpreadsheet/Calculation/Calculation.php @@ -848,7 +848,7 @@ class Calculation ], 'DEVSQ' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical::class, 'DEVSQ'], + 'functionCall' => [Statistical\Deviations::class, 'sumSquares'], 'argumentCount' => '1+', ], 'DGET' => [ @@ -1185,7 +1185,7 @@ class Calculation ], 'GAUSS' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical::class, 'GAUSS'], + 'functionCall' => [Statistical\Distributions\StandardNormal::class, 'gauss'], 'argumentCount' => '1', ], 'GCD' => [ @@ -1195,7 +1195,7 @@ class Calculation ], 'GEOMEAN' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical::class, 'GEOMEAN'], + 'functionCall' => [Statistical\Averages\Mean::class, 'geometric'], 'argumentCount' => '1+', ], 'GESTEP' => [ @@ -1215,7 +1215,7 @@ class Calculation ], 'HARMEAN' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical::class, 'HARMEAN'], + 'functionCall' => [Statistical\Averages\Mean::class, 'harmonic'], 'argumentCount' => '1+', ], 'HEX2BIN' => [ @@ -1529,12 +1529,12 @@ class Calculation ], 'KURT' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical::class, 'KURT'], + 'functionCall' => [Statistical\Deviations::class, 'kurtosis'], 'argumentCount' => '1+', ], 'LARGE' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical::class, 'LARGE'], + 'functionCall' => [Statistical\Size::class, 'large'], 'argumentCount' => '2', ], 'LCM' => [ @@ -2071,7 +2071,7 @@ class Calculation ], 'RANK.EQ' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical::class, 'RANK'], + 'functionCall' => [Statistical\Percentiles::class, 'RANK'], 'argumentCount' => '2,3', ], 'RATE' => [ @@ -2218,7 +2218,7 @@ class Calculation ], 'SKEW' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical::class, 'SKEW'], + 'functionCall' => [Statistical\Deviations::class, 'skew'], 'argumentCount' => '1+', ], 'SKEW.P' => [ @@ -2238,7 +2238,7 @@ class Calculation ], 'SMALL' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical::class, 'SMALL'], + 'functionCall' => [Statistical\Size::class, 'small'], 'argumentCount' => '2', ], 'SORT' => [ @@ -2263,7 +2263,7 @@ class Calculation ], 'STANDARDIZE' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical::class, 'STANDARDIZE'], + 'functionCall' => [Statistical\Standardize::class, 'execute'], 'argumentCount' => '3', ], 'STDEV' => [ @@ -2288,12 +2288,12 @@ class Calculation ], 'STDEVP' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical::class, 'STDEVP'], + 'functionCall' => [Statistical\StandardDeviations::class, 'STDEVP'], 'argumentCount' => '1+', ], 'STDEVPA' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical::class, 'STDEVPA'], + 'functionCall' => [Statistical\StandardDeviations::class, 'STDEVPA'], 'argumentCount' => '1+', ], 'STEYX' => [ @@ -2469,7 +2469,7 @@ class Calculation ], 'TRIMMEAN' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical::class, 'TRIMMEAN'], + 'functionCall' => [Statistical\Averages\Mean::class, 'trim'], 'argumentCount' => '2', ], 'TRUE' => [ diff --git a/src/PhpSpreadsheet/Calculation/Statistical.php b/src/PhpSpreadsheet/Calculation/Statistical.php index 0f5355b9..e34c0c1e 100644 --- a/src/PhpSpreadsheet/Calculation/Statistical.php +++ b/src/PhpSpreadsheet/Calculation/Statistical.php @@ -13,12 +13,14 @@ use PhpOffice\PhpSpreadsheet\Calculation\Statistical\StandardDeviations; use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Trends; use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Variances; +/** + * @deprecated 1.18.0 + */ class Statistical { const LOG_GAMMA_X_MAX_VALUE = 2.55e305; const EPS = 2.22e-16; const MAX_VALUE = 1.2e308; - const MAX_ITERATIONS = 256; const SQRT2PI = 2.5066282746310005024157652848110452530069867406099; /** @@ -428,48 +430,18 @@ class Statistical * Excel Function: * DEVSQ(value1[,value2[, ...]]) * + * @Deprecated 1.18.0 + * + * @see Statistical\Deviations::sumSquares() + * Use the sumSquares() method in the Statistical\Deviations class instead + * * @param mixed ...$args Data values * * @return float|string */ public static function DEVSQ(...$args) { - $aArgs = Functions::flattenArrayIndexed($args); - - // Return value - $returnValue = null; - - $aMean = Averages::average($aArgs); - if ($aMean != Functions::DIV0()) { - $aCount = -1; - foreach ($aArgs as $k => $arg) { - // Is it a numeric value? - if ( - (is_bool($arg)) && - ((!Functions::isCellValue($k)) || - (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE)) - ) { - $arg = (int) $arg; - } - if ((is_numeric($arg)) && (!is_string($arg))) { - if ($returnValue === null) { - $returnValue = ($arg - $aMean) ** 2; - } else { - $returnValue += ($arg - $aMean) ** 2; - } - ++$aCount; - } - } - - // Return - if ($returnValue === null) { - return Functions::NAN(); - } - - return $returnValue; - } - - return Functions::NA(); + return Statistical\Deviations::sumSquares(...$args); } /** @@ -671,18 +643,18 @@ class Statistical * Calculates the probability that a member of a standard normal population will fall between * the mean and z standard deviations from the mean. * + * @Deprecated 1.18.0 + * + * @see Statistical\Distributions\StandardNormal::gauss() + * Use the gauss() method in the Statistical\Distributions\StandardNormal class instead + * * @param float $value * * @return float|string The result, or a string containing an error */ public static function GAUSS($value) { - $value = Functions::flattenSingleValue($value); - if (!is_numeric($value)) { - return Functions::VALUE(); - } - - return Statistical\Distributions\Normal::distribution($value, 0, 1, true) - 0.5; + return Statistical\Distributions\StandardNormal::gauss($value); } /** @@ -695,23 +667,18 @@ class Statistical * Excel Function: * GEOMEAN(value1[,value2[, ...]]) * + * @Deprecated 1.18.0 + * + * @see Statistical\Averages\Mean::geometric() + * Use the geometric() method in the Statistical\Averages\Mean class instead + * * @param mixed ...$args Data values * * @return float|string */ public static function GEOMEAN(...$args) { - $aArgs = Functions::flattenArray($args); - - $aMean = MathTrig\Product::evaluate($aArgs); - if (is_numeric($aMean) && ($aMean > 0)) { - $aCount = Counts::COUNT($aArgs); - if (Minimum::MIN($aArgs) > 0) { - return $aMean ** (1 / $aCount); - } - } - - return Functions::NAN(); + return Statistical\Averages\Mean::geometric(...$args); } /** @@ -745,38 +712,18 @@ class Statistical * Excel Function: * HARMEAN(value1[,value2[, ...]]) * + * @Deprecated 1.18.0 + * + * @see Statistical\Averages\Mean::harmonic() + * Use the harmonic() method in the Statistical\Averages\Mean class instead + * * @param mixed ...$args Data values * * @return float|string */ public static function HARMEAN(...$args) { - // Return value - $returnValue = 0; - - // Loop through arguments - $aArgs = Functions::flattenArray($args); - if (Minimum::MIN($aArgs) < 0) { - return Functions::NAN(); - } - $aCount = 0; - foreach ($aArgs as $arg) { - // Is it a numeric value? - if ((is_numeric($arg)) && (!is_string($arg))) { - if ($arg <= 0) { - return Functions::NAN(); - } - $returnValue += (1 / $arg); - ++$aCount; - } - } - - // Return - if ($aCount > 0) { - return 1 / ($returnValue / $aCount); - } - - return Functions::NA(); + return Statistical\Averages\Mean::harmonic(...$args); } /** @@ -835,40 +782,18 @@ class Statistical * kurtosis indicates a relatively peaked distribution. Negative kurtosis indicates a * relatively flat distribution. * + * @Deprecated 1.18.0 + * + * @see Statistical\Deviations::kurtosis() + * Use the kurtosis() method in the Statistical\Deviations class instead + * * @param array ...$args Data Series * * @return float|string */ public static function KURT(...$args) { - $aArgs = Functions::flattenArrayIndexed($args); - $mean = Averages::average($aArgs); - $stdDev = StandardDeviations::STDEV($aArgs); - - if ($stdDev > 0) { - $count = $summer = 0; - // Loop through arguments - foreach ($aArgs as $k => $arg) { - if ( - (is_bool($arg)) && - (!Functions::isMatrixValue($k)) - ) { - } else { - // Is it a numeric value? - if ((is_numeric($arg)) && (!is_string($arg))) { - $summer += (($arg - $mean) / $stdDev) ** 4; - ++$count; - } - } - } - - // Return - if ($count > 3) { - return $summer * ($count * ($count + 1) / (($count - 1) * ($count - 2) * ($count - 3))) - (3 * ($count - 1) ** 2 / (($count - 2) * ($count - 3))); - } - } - - return Functions::DIV0(); + return Statistical\Deviations::kurtosis(...$args); } /** @@ -880,37 +805,18 @@ class Statistical * Excel Function: * LARGE(value1[,value2[, ...]],entry) * + * @Deprecated 1.18.0 + * + * @see Statistical\Size::large() + * Use the large() method in the Statistical\Size class instead + * * @param mixed $args Data values * * @return float|string The result, or a string containing an error */ public static function LARGE(...$args) { - $aArgs = Functions::flattenArray($args); - $entry = array_pop($aArgs); - - if ((is_numeric($entry)) && (!is_string($entry))) { - $entry = (int) floor($entry); - - // Calculate - $mArgs = []; - foreach ($aArgs as $arg) { - // Is it a numeric value? - if ((is_numeric($arg)) && (!is_string($arg))) { - $mArgs[] = $arg; - } - } - $count = Counts::COUNT($mArgs); - --$entry; - if (($entry < 0) || ($entry >= $count) || ($count == 0)) { - return Functions::NAN(); - } - rsort($mArgs); - - return $mArgs[$entry]; - } - - return Functions::VALUE(); + return Statistical\Size::large(...$args); } /** @@ -1503,40 +1409,18 @@ class Statistical * asymmetric tail extending toward more positive values. Negative skewness indicates a * distribution with an asymmetric tail extending toward more negative values. * + * @Deprecated 1.18.0 + * + * @see Statistical\Deviations::skew() + * Use the skew() method in the Statistical\Deviations class instead + * * @param array ...$args Data Series * * @return float|string The result, or a string containing an error */ public static function SKEW(...$args) { - $aArgs = Functions::flattenArrayIndexed($args); - $mean = Averages::average($aArgs); - $stdDev = StandardDeviations::STDEV($aArgs); - - if ($stdDev === 0.0 || is_string($stdDev)) { - return Functions::DIV0(); - } - - $count = $summer = 0; - // Loop through arguments - foreach ($aArgs as $k => $arg) { - if ((is_bool($arg)) && (!Functions::isMatrixValue($k))) { - } elseif (!is_numeric($arg)) { - return Functions::VALUE(); - } else { - // Is it a numeric value? - if ((is_numeric($arg)) && (!is_string($arg))) { - $summer += (($arg - $mean) / $stdDev) ** 3; - ++$count; - } - } - } - - if ($count > 2) { - return $summer * ($count / (($count - 1) * ($count - 2))); - } - - return Functions::DIV0(); + return Statistical\Deviations::skew(...$args); } /** @@ -1568,38 +1452,18 @@ class Statistical * Excel Function: * SMALL(value1[,value2[, ...]],entry) * + * @Deprecated 1.18.0 + * + * @see Statistical\Size::small() + * Use the small() method in the Statistical\Size class instead + * * @param mixed $args Data values * * @return float|string The result, or a string containing an error */ public static function SMALL(...$args) { - $aArgs = Functions::flattenArray($args); - - // Calculate - $entry = array_pop($aArgs); - - if ((is_numeric($entry)) && (!is_string($entry))) { - $entry = (int) floor($entry); - - $mArgs = []; - foreach ($aArgs as $arg) { - // Is it a numeric value? - if ((is_numeric($arg)) && (!is_string($arg))) { - $mArgs[] = $arg; - } - } - $count = Counts::COUNT($mArgs); - --$entry; - if (($entry < 0) || ($entry >= $count) || ($count == 0)) { - return Functions::NAN(); - } - sort($mArgs); - - return $mArgs[$entry]; - } - - return Functions::VALUE(); + return Statistical\Size::small(...$args); } /** @@ -1607,6 +1471,11 @@ class Statistical * * Returns a normalized value from a distribution characterized by mean and standard_dev. * + * @Deprecated 1.18.0 + * + * @see Statistical\Standardize::execute() + * Use the execute() method in the Statistical\Standardize class instead + * * @param float $value Value to normalize * @param float $mean Mean Value * @param float $stdDev Standard Deviation @@ -1615,19 +1484,7 @@ class Statistical */ public static function STANDARDIZE($value, $mean, $stdDev) { - $value = Functions::flattenSingleValue($value); - $mean = Functions::flattenSingleValue($mean); - $stdDev = Functions::flattenSingleValue($stdDev); - - if ((is_numeric($value)) && (is_numeric($mean)) && (is_numeric($stdDev))) { - if ($stdDev <= 0) { - return Functions::NAN(); - } - - return ($value - $mean) / $stdDev; - } - - return Functions::VALUE(); + return Statistical\Standardize::execute($value, $mean, $stdDev); } /** @@ -1812,42 +1669,18 @@ class Statistical * Excel Function: * TRIMEAN(value1[,value2[, ...]], $discard) * + * @Deprecated 1.18.0 + * + *@see Statistical\Averages\Mean::trim() + * Use the trim() method in the Statistical\Averages\Mean class instead + * * @param mixed $args Data values * * @return float|string */ public static function TRIMMEAN(...$args) { - $aArgs = Functions::flattenArray($args); - - // Calculate - $percent = array_pop($aArgs); - - if ((is_numeric($percent)) && (!is_string($percent))) { - if (($percent < 0) || ($percent > 1)) { - return Functions::NAN(); - } - - $mArgs = []; - foreach ($aArgs as $arg) { - // Is it a numeric value? - if ((is_numeric($arg)) && (!is_string($arg))) { - $mArgs[] = $arg; - } - } - - $discard = floor(Counts::COUNT($mArgs) * $percent / 2); - sort($mArgs); - - for ($i = 0; $i < $discard; ++$i) { - array_pop($mArgs); - array_shift($mArgs); - } - - return Averages::average($mArgs); - } - - return Functions::VALUE(); + return Statistical\Averages\Mean::trim(...$args); } /** @@ -1860,12 +1693,12 @@ class Statistical * * @Deprecated 1.17.0 * + *@see Statistical\Variances::VAR() + * Use the VAR() method in the Statistical\Variances class instead + * * @param mixed ...$args Data values * * @return float|string (string if result is an error) - * - *@see Statistical\Variances::VAR() - * Use the VAR() method in the Statistical\Variances class instead */ public static function VARFunc(...$args) { diff --git a/src/PhpSpreadsheet/Calculation/Statistical/Averages/Mean.php b/src/PhpSpreadsheet/Calculation/Statistical/Averages/Mean.php new file mode 100644 index 00000000..e39a3ff2 --- /dev/null +++ b/src/PhpSpreadsheet/Calculation/Statistical/Averages/Mean.php @@ -0,0 +1,131 @@ + 0)) { + $aCount = Counts::COUNT($aArgs); + if (Minimum::MIN($aArgs) > 0) { + return $aMean ** (1 / $aCount); + } + } + + return Functions::NAN(); + } + + /** + * HARMEAN. + * + * Returns the harmonic mean of a data set. The harmonic mean is the reciprocal of the + * arithmetic mean of reciprocals. + * + * Excel Function: + * HARMEAN(value1[,value2[, ...]]) + * + * @param mixed ...$args Data values + * + * @return float|string + */ + public static function harmonic(...$args) + { + // Loop through arguments + $aArgs = Functions::flattenArray($args); + if (Minimum::MIN($aArgs) < 0) { + return Functions::NAN(); + } + + $returnValue = 0; + $aCount = 0; + foreach ($aArgs as $arg) { + // Is it a numeric value? + if ((is_numeric($arg)) && (!is_string($arg))) { + if ($arg <= 0) { + return Functions::NAN(); + } + $returnValue += (1 / $arg); + ++$aCount; + } + } + + // Return + if ($aCount > 0) { + return 1 / ($returnValue / $aCount); + } + + return Functions::NA(); + } + + /** + * TRIMMEAN. + * + * Returns the mean of the interior of a data set. TRIMMEAN calculates the mean + * taken by excluding a percentage of data points from the top and bottom tails + * of a data set. + * + * Excel Function: + * TRIMEAN(value1[,value2[, ...]], $discard) + * + * @param mixed $args Data values + * + * @return float|string + */ + public static function trim(...$args) + { + $aArgs = Functions::flattenArray($args); + + // Calculate + $percent = array_pop($aArgs); + + if ((is_numeric($percent)) && (!is_string($percent))) { + if (($percent < 0) || ($percent > 1)) { + return Functions::NAN(); + } + + $mArgs = []; + foreach ($aArgs as $arg) { + // Is it a numeric value? + if ((is_numeric($arg)) && (!is_string($arg))) { + $mArgs[] = $arg; + } + } + + $discard = floor(Counts::COUNT($mArgs) * $percent / 2); + sort($mArgs); + + for ($i = 0; $i < $discard; ++$i) { + array_pop($mArgs); + array_shift($mArgs); + } + + return Averages::average($mArgs); + } + + return Functions::VALUE(); + } +} diff --git a/src/PhpSpreadsheet/Calculation/Statistical/Deviations.php b/src/PhpSpreadsheet/Calculation/Statistical/Deviations.php new file mode 100644 index 00000000..1b64fe2c --- /dev/null +++ b/src/PhpSpreadsheet/Calculation/Statistical/Deviations.php @@ -0,0 +1,141 @@ + $arg) { + // Is it a numeric value? + if ( + (is_bool($arg)) && + ((!Functions::isCellValue($k)) || + (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE)) + ) { + $arg = (int) $arg; + } + if ((is_numeric($arg)) && (!is_string($arg))) { + $returnValue += ($arg - $aMean) ** 2; + ++$aCount; + } + } + + return $aCount === 0 ? Functions::VALUE() : $returnValue; + } + + /** + * KURT. + * + * Returns the kurtosis of a data set. Kurtosis characterizes the relative peakedness + * or flatness of a distribution compared with the normal distribution. Positive + * kurtosis indicates a relatively peaked distribution. Negative kurtosis indicates a + * relatively flat distribution. + * + * @param array ...$args Data Series + * + * @return float|string + */ + public static function kurtosis(...$args) + { + $aArgs = Functions::flattenArrayIndexed($args); + $mean = Averages::average($aArgs); + if (!is_numeric($mean)) { + return Functions::DIV0(); + } + $stdDev = StandardDeviations::STDEV($aArgs); + + if ($stdDev > 0) { + $count = $summer = 0; + + foreach ($aArgs as $k => $arg) { + if ((is_bool($arg)) && (!Functions::isMatrixValue($k))) { + } else { + // Is it a numeric value? + if ((is_numeric($arg)) && (!is_string($arg))) { + $summer += (($arg - $mean) / $stdDev) ** 4; + ++$count; + } + } + } + + if ($count > 3) { + return $summer * ($count * ($count + 1) / + (($count - 1) * ($count - 2) * ($count - 3))) - (3 * ($count - 1) ** 2 / + (($count - 2) * ($count - 3))); + } + } + + return Functions::DIV0(); + } + + /** + * SKEW. + * + * Returns the skewness of a distribution. Skewness characterizes the degree of asymmetry + * of a distribution around its mean. Positive skewness indicates a distribution with an + * asymmetric tail extending toward more positive values. Negative skewness indicates a + * distribution with an asymmetric tail extending toward more negative values. + * + * @param array ...$args Data Series + * + * @return float|string The result, or a string containing an error + */ + public static function skew(...$args) + { + $aArgs = Functions::flattenArrayIndexed($args); + $mean = Averages::average($aArgs); + if (!is_numeric($mean)) { + return Functions::DIV0(); + } + $stdDev = StandardDeviations::STDEV($aArgs); + if ($stdDev === 0.0 || is_string($stdDev)) { + return Functions::DIV0(); + } + + $count = $summer = 0; + // Loop through arguments + foreach ($aArgs as $k => $arg) { + if ((is_bool($arg)) && (!Functions::isMatrixValue($k))) { + } elseif (!is_numeric($arg)) { + return Functions::VALUE(); + } else { + // Is it a numeric value? + if ((is_numeric($arg)) && (!is_string($arg))) { + $summer += (($arg - $mean) / $stdDev) ** 3; + ++$count; + } + } + } + + if ($count > 2) { + return $summer * ($count / (($count - 1) * ($count - 2))); + } + + return Functions::DIV0(); + } +} diff --git a/src/PhpSpreadsheet/Calculation/Statistical/Distributions/StandardNormal.php b/src/PhpSpreadsheet/Calculation/Statistical/Distributions/StandardNormal.php index 0dde2006..d10f02a5 100644 --- a/src/PhpSpreadsheet/Calculation/Statistical/Distributions/StandardNormal.php +++ b/src/PhpSpreadsheet/Calculation/Statistical/Distributions/StandardNormal.php @@ -55,6 +55,26 @@ class StandardNormal return Normal::inverse($value, 0, 1); } + /** + * GAUSS. + * + * Calculates the probability that a member of a standard normal population will fall between + * the mean and z standard deviations from the mean. + * + * @param mixed $value + * + * @return float|string The result, or a string containing an error + */ + public static function gauss($value) + { + $value = Functions::flattenSingleValue($value); + if (!is_numeric($value)) { + return Functions::VALUE(); + } + + return self::distribution($value, true) - 0.5; + } + /** * ZTEST. * diff --git a/src/PhpSpreadsheet/Calculation/Statistical/Size.php b/src/PhpSpreadsheet/Calculation/Statistical/Size.php new file mode 100644 index 00000000..de4b6d6c --- /dev/null +++ b/src/PhpSpreadsheet/Calculation/Statistical/Size.php @@ -0,0 +1,96 @@ += $count) || ($count == 0)) { + return Functions::NAN(); + } + rsort($mArgs); + + return $mArgs[$entry]; + } + + return Functions::VALUE(); + } + + /** + * SMALL. + * + * Returns the nth smallest value in a data set. You can use this function to + * select a value based on its relative standing. + * + * Excel Function: + * SMALL(value1[,value2[, ...]],entry) + * + * @param mixed $args Data values + * + * @return float|string The result, or a string containing an error + */ + public static function small(...$args) + { + $aArgs = Functions::flattenArray($args); + + $entry = array_pop($aArgs); + + if ((is_numeric($entry)) && (!is_string($entry))) { + $entry = (int) floor($entry); + + $mArgs = self::filter($aArgs); + $count = Counts::COUNT($mArgs); + --$entry; + if (($entry < 0) || ($entry >= $count) || ($count == 0)) { + return Functions::NAN(); + } + sort($mArgs); + + return $mArgs[$entry]; + } + + return Functions::VALUE(); + } + + /** + * @param mixed[] $args Data values + */ + protected static function filter(array $args): array + { + $mArgs = []; + + foreach ($args as $arg) { + // Is it a numeric value? + if ((is_numeric($arg)) && (!is_string($arg))) { + $mArgs[] = $arg; + } + } + + return $mArgs; + } +} diff --git a/src/PhpSpreadsheet/Calculation/Statistical/Standardize.php b/src/PhpSpreadsheet/Calculation/Statistical/Standardize.php new file mode 100644 index 00000000..2f3c58e7 --- /dev/null +++ b/src/PhpSpreadsheet/Calculation/Statistical/Standardize.php @@ -0,0 +1,41 @@ +getMessage(); + } + + if ($stdDev <= 0) { + return Functions::NAN(); + } + + return ($value - $mean) / $stdDev; + } +} diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/DevSqTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/DevSqTest.php new file mode 100644 index 00000000..2db29300 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/Statistical/DevSqTest.php @@ -0,0 +1,31 @@ + Date: Thu, 29 Apr 2021 13:59:01 -0700 Subject: [PATCH 16/57] Fix for Issue 2029 (Invalid Cell Coordinate A-1) (#2032) * Fix for Issue 2029 (Invalid Cell Coordinate A-1) Fix for #2021. When Html Reader encounters an embedded table, it tries to shift it up a row. It obviously should not attempt to shift it above row 1. @danmodini reported the problem, and suggests the correct solution. This PR implements that and adds a test case. Performing some additional testing, I found that Html Reader cannot handle inline column width or row height set in points rather than pixels (and HTML writer with useInlineCss generates these values in points). It also doesn't handle border style when the border width (which it ignores) is omitted. Fixed and added tests. --- src/PhpSpreadsheet/Reader/Html.php | 16 ++- .../Reader/Html/Issue2029Test.php | 115 ++++++++++++++++++ 2 files changed, 127 insertions(+), 4 deletions(-) create mode 100644 tests/PhpSpreadsheetTests/Reader/Html/Issue2029Test.php diff --git a/src/PhpSpreadsheet/Reader/Html.php b/src/PhpSpreadsheet/Reader/Html.php index 1a0ef5d3..b7faac87 100644 --- a/src/PhpSpreadsheet/Reader/Html.php +++ b/src/PhpSpreadsheet/Reader/Html.php @@ -469,7 +469,7 @@ class Html extends BaseReader if ($child->nodeName === 'table') { $this->flushCell($sheet, $column, $row, $cellContent); $column = $this->setTableStartColumn($column); - if ($this->tableLevel > 1) { + if ($this->tableLevel > 1 && $row > 1) { --$row; } $this->processDomElement($child, $sheet, $row, $column, $cellContent); @@ -878,14 +878,14 @@ class Html extends BaseReader case 'width': $sheet->getColumnDimension($column)->setWidth( - (float) str_replace('px', '', $styleValue) + (float) str_replace(['px', 'pt'], '', $styleValue) ); break; case 'height': $sheet->getRowDimension($row)->setRowHeight( - (float) str_replace('px', '', $styleValue) + (float) str_replace(['px', 'pt'], '', $styleValue) ); break; @@ -1009,7 +1009,15 @@ class Html extends BaseReader $borderStyle = Border::BORDER_NONE; $color = null; } else { - [, $borderStyle, $color] = explode(' ', $styleValue); + $borderArray = explode(' ', $styleValue); + $borderCount = count($borderArray); + if ($borderCount >= 3) { + $borderStyle = $borderArray[1]; + $color = $borderArray[2]; + } else { + $borderStyle = $borderArray[0]; + $color = $borderArray[1] ?? null; + } } $cellStyle->applyFromArray([ diff --git a/tests/PhpSpreadsheetTests/Reader/Html/Issue2029Test.php b/tests/PhpSpreadsheetTests/Reader/Html/Issue2029Test.php new file mode 100644 index 00000000..08cdbc25 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Reader/Html/Issue2029Test.php @@ -0,0 +1,115 @@ + + + + + Declaracion en Linea + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + +
CUIT:
Período
Secuencia:
Contribuyente:
+
+
+ + + + + + + + + + + + + + + + + + + + + +
+ CUIL + + Apellido y Nombre + + Obra Social + + Corresponde Reducción? +
+ 12345678901 + + EMILIANO ZAPATA SALAZAR + + 101208 + + Yes +
+ 23456789012 + + FRANCISCO PANCHO VILLA + + 101208 + + No +
+ + + +EOF; + $reader = new Html(); + $spreadsheet = $reader->loadFromString($content); + $sheet = $spreadsheet->getActiveSheet(); + self::assertSame('CUIT:', $sheet->getCell('A1')->getValue()); + self::assertSame('30-53914190-9', $sheet->getCell('B1')->getValue()); + self::assertSame('Contribuyente:', $sheet->getCell('A4')->getValue()); + self::assertSame('Apellido y Nombre', $sheet->getCell('B9')->getValue()); + self::assertEquals('101208', $sheet->getCell('C10')->getValue()); + self::assertEquals('Yes', $sheet->getCell('D10')->getValue()); + self::assertEquals('23456789012', $sheet->getCell('A11')->getValue()); + self::assertEquals('No', $sheet->getCell('D11')->getValue()); + } +} From 815dabae897292f5d3c223669905c0e1d224d46f Mon Sep 17 00:00:00 2001 From: xandros15 Date: Fri, 30 Apr 2021 15:22:07 +0200 Subject: [PATCH 17/57] #984 add support notContainsText for conditional styles in xlsx --- src/PhpSpreadsheet/Style/Conditional.php | 1 + src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php | 12 +++- .../Writer/Xlsx/ConditionalTest.php | 60 +++++++++++++++++++ 3 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 tests/PhpSpreadsheetTests/Writer/Xlsx/ConditionalTest.php diff --git a/src/PhpSpreadsheet/Style/Conditional.php b/src/PhpSpreadsheet/Style/Conditional.php index b008c9f2..2fd415af 100644 --- a/src/PhpSpreadsheet/Style/Conditional.php +++ b/src/PhpSpreadsheet/Style/Conditional.php @@ -15,6 +15,7 @@ class Conditional implements IComparable const CONDITION_CONTAINSBLANKS = 'containsBlanks'; const CONDITION_NOTCONTAINSBLANKS = 'notContainsBlanks'; const CONDITION_DATABAR = 'dataBar'; + const CONDITION_NOTCONTAINSTEXT = 'notContainsText'; // Operator types const OPERATOR_NONE = ''; diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php index 3978eb6f..4c2aeb39 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php @@ -634,15 +634,21 @@ class Worksheet extends WriterPart self::writeAttributeif( $objWriter, - ($conditional->getConditionType() == Conditional::CONDITION_CELLIS || $conditional->getConditionType() == Conditional::CONDITION_CONTAINSTEXT) - && $conditional->getOperatorType() != Conditional::OPERATOR_NONE, + in_array($conditional->getConditionType(), [ + Conditional::CONDITION_CELLIS, + Conditional::CONDITION_CONTAINSTEXT, + Conditional::CONDITION_NOTCONTAINSTEXT, + ]) && $conditional->getOperatorType() != Conditional::OPERATOR_NONE, 'operator', $conditional->getOperatorType() ); self::writeAttributeIf($objWriter, $conditional->getStopIfTrue(), 'stopIfTrue', '1'); - if ($conditional->getConditionType() == Conditional::CONDITION_CONTAINSTEXT) { + if (in_array($conditional->getConditionType(), [ + Conditional::CONDITION_CONTAINSTEXT, + Conditional::CONDITION_NOTCONTAINSTEXT, + ])) { self::writeTextCondElements($objWriter, $conditional, $cellCoordinate); } else { self::writeOtherCondElements($objWriter, $conditional, $cellCoordinate); diff --git a/tests/PhpSpreadsheetTests/Writer/Xlsx/ConditionalTest.php b/tests/PhpSpreadsheetTests/Writer/Xlsx/ConditionalTest.php new file mode 100644 index 00000000..1bcad0d1 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Writer/Xlsx/ConditionalTest.php @@ -0,0 +1,60 @@ +prevValue = Settings::getLibXmlLoaderOptions(); + + // Disable validating XML with the DTD + Settings::setLibXmlLoaderOptions($this->prevValue & ~LIBXML_DTDVALID & ~LIBXML_DTDATTR & ~LIBXML_DTDLOAD); + } + + protected function tearDown(): void + { + Settings::setLibXmlLoaderOptions($this->prevValue); + } + + /** + * Test check if conditional style with type 'notContainsText' works on xlsx + */ + public function testConditionalNotContainsText(): void + { + $spreadsheet = new Spreadsheet(); + $worksheet = $spreadsheet->getActiveSheet(); + + $condition = new Conditional(); + $condition->setConditionType(Conditional::CONDITION_NOTCONTAINSTEXT); + $condition->setOperatorType(Conditional::OPERATOR_NOTCONTAINS); + $condition->setText("C"); + $condition->getStyle()->applyFromArray([ + 'fill' => [ + 'color' => ['argb' => 'FFFFC000'], + 'fillType' => Fill::FILL_SOLID, + ], + ]); + $worksheet->setConditionalStyles('A1:A5', [$condition]); + + $writer = new Xlsx($spreadsheet); + $writerWorksheet = new Xlsx\Worksheet($writer); + $data = $writerWorksheet->writeWorksheet($worksheet, []); + $needle = <<ISERROR(SEARCH("C",A1:A5)) +xml; + $this->assertStringContainsString($needle, $data); + } +} From 0b0215514ab0942c836dc23241b570856eaf117e Mon Sep 17 00:00:00 2001 From: xandros15 Date: Fri, 30 Apr 2021 15:30:43 +0200 Subject: [PATCH 18/57] #984 update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21762f89..5fcace6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Improved support for Row and Column ranges in formulae [Issue #1755](https://github.com/PHPOffice/PhpSpreadsheet/issues/1755) [PR #2028](https://github.com/PHPOffice/PhpSpreadsheet/pull/2028) - Implemented the CHITEST(), CHISQ.DIST() and CHISQ.INV() and equivalent Statistical functions, for both left- and right-tailed distributions. - Support for ActiveSheet and SelectedCells in the ODS Reader and Writer. [PR #1908](https://github.com/PHPOffice/PhpSpreadsheet/pull/1908) +- Support for notContainsText Conditional Style in xlsx [Issue #984](https://github.com/PHPOffice/PhpSpreadsheet/issues/984) ### Changed From d555b5d312adf95d6c1cbed123fff850d8f1eef7 Mon Sep 17 00:00:00 2001 From: Mark Baker Date: Fri, 30 Apr 2021 20:05:45 +0200 Subject: [PATCH 19/57] Pattern Fill style should default to 'solid' if there is a pattern fill with colour but no style (#2050) * Pattern Fill style should default to 'solid' if there is a pattern fill style for a conditional; though may need to check if there are defined fg/bg colours as well; and only set a fill style if there are defined colurs --- CHANGELOG.md | 2 +- src/PhpSpreadsheet/Reader/Xlsx/Styles.php | 14 +++++++++----- src/PhpSpreadsheet/Writer/Xls/Style/ColorMap.php | 10 ++++++++++ .../Reader/Xlsx/DefaultFillTest.php | 11 +++++++++++ tests/data/Reader/XLSX/pr2050cf-fill.xlsx | Bin 0 -> 8267 bytes 5 files changed, 31 insertions(+), 6 deletions(-) create mode 100644 tests/data/Reader/XLSX/pr2050cf-fill.xlsx diff --git a/CHANGELOG.md b/CHANGELOG.md index 21762f89..ed4f44c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,7 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Nothing. ### Fixed - +- Correct default fill style for conditional without a pattern defined [Issue #2035](https://github.com/PHPOffice/PhpSpreadsheet/issues/2035) [PR #2050](https://github.com/PHPOffice/PhpSpreadsheet/pull/2050) - Fixed issue where array key check for existince before accessing arrays in Xlsx.php. [PR #1970](https://github.com/PHPOffice/PhpSpreadsheet/pull/1970) - Fixed issue with quoted strings in number format mask rendered with toFormattedString() [Issue 1972#](https://github.com/PHPOffice/PhpSpreadsheet/issues/1972) [PR #1978](https://github.com/PHPOffice/PhpSpreadsheet/pull/1978) - Fixed issue with percentage formats in number format mask rendered with toFormattedString() [Issue 1929#](https://github.com/PHPOffice/PhpSpreadsheet/issues/1929) [PR #1928](https://github.com/PHPOffice/PhpSpreadsheet/pull/1928) diff --git a/src/PhpSpreadsheet/Reader/Xlsx/Styles.php b/src/PhpSpreadsheet/Reader/Xlsx/Styles.php index 2b0c7016..2968a3fe 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx/Styles.php +++ b/src/PhpSpreadsheet/Reader/Xlsx/Styles.php @@ -103,17 +103,21 @@ class Styles extends BaseParserClass self::readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=1]'))->color) ); } elseif ($fillStyleXml->patternFill) { - $patternType = (string) $fillStyleXml->patternFill['patternType'] != '' - ? (string) $fillStyleXml->patternFill['patternType'] - : Fill::FILL_NONE; - - $fillStyle->setFillType($patternType); + $defaultFillStyle = Fill::FILL_NONE; if ($fillStyleXml->patternFill->fgColor) { $fillStyle->getStartColor()->setARGB(self::readColor($fillStyleXml->patternFill->fgColor, true)); + $defaultFillStyle = Fill::FILL_SOLID; } if ($fillStyleXml->patternFill->bgColor) { $fillStyle->getEndColor()->setARGB(self::readColor($fillStyleXml->patternFill->bgColor, true)); + $defaultFillStyle = Fill::FILL_SOLID; } + + $patternType = (string) $fillStyleXml->patternFill['patternType'] != '' + ? (string) $fillStyleXml->patternFill['patternType'] + : $defaultFillStyle; + + $fillStyle->setFillType($patternType); } } diff --git a/src/PhpSpreadsheet/Writer/Xls/Style/ColorMap.php b/src/PhpSpreadsheet/Writer/Xls/Style/ColorMap.php index e3a6b206..caf85c04 100644 --- a/src/PhpSpreadsheet/Writer/Xls/Style/ColorMap.php +++ b/src/PhpSpreadsheet/Writer/Xls/Style/ColorMap.php @@ -75,6 +75,16 @@ class ColorMap return self::$colorMap["#{$colorRgb}"]; } +// TODO Try and map RGB value to nearest colour within the define pallette +// $red = Color::getRed($colorRgb, false); +// $green = Color::getGreen($colorRgb, false); +// $blue = Color::getBlue($colorRgb, false); + +// $paletteSpace = 3; +// $newColor = ($red * $paletteSpace / 256) * ($paletteSpace * $paletteSpace) + +// ($green * $paletteSpace / 256) * $paletteSpace + +// ($blue * $paletteSpace / 256); + return $defaultIndex; } } diff --git a/tests/PhpSpreadsheetTests/Reader/Xlsx/DefaultFillTest.php b/tests/PhpSpreadsheetTests/Reader/Xlsx/DefaultFillTest.php index 88d666b2..ccdad067 100644 --- a/tests/PhpSpreadsheetTests/Reader/Xlsx/DefaultFillTest.php +++ b/tests/PhpSpreadsheetTests/Reader/Xlsx/DefaultFillTest.php @@ -29,4 +29,15 @@ class DefaultFillTest extends TestCase self::assertSame('none', $sheet->getCell('J16')->getStyle()->getFill()->getFillType()); self::assertSame('solid', $sheet->getCell('C2')->getStyle()->getFill()->getFillType()); } + + public function testDefaultConditionalFill(): void + { + // default fill pattern for a conditional style where the filltype is not defined + $filename = 'tests/data/Reader/XLSX/pr2050cf-fill.xlsx'; + $reader = IOFactory::createReader('Xlsx'); + $spreadsheet = $reader->load($filename); + + $style = $spreadsheet->getActiveSheet()->getConditionalStyles('A1')[0]->getStyle(); + self::assertSame('solid', $style->getFill()->getFillType()); + } } diff --git a/tests/data/Reader/XLSX/pr2050cf-fill.xlsx b/tests/data/Reader/XLSX/pr2050cf-fill.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..e2183422d25a46b3513812937cb19a0c73c6a562 GIT binary patch literal 8267 zcmeHMg;$hY`yCn-kS;+$8l<~B1cafK8oGz>9sy~P8cIS^kQ|VPkuDLDk{m#g4(U?h zH~QWC-Fvo}T>&~OYaRd<`TKvb|KS;^NEy=T<{?l#Q~56c zg);_GD}yb#8`@9Q!b)ql{Lo^O+i!mCT9WvzMkb%sLAb_ZWZpxPKli=RClYW}DZG3- zBS9af>QuG0!Qy3X<6#nMMv=~#FBa(>YVFMNsZH%PW=Ci(&EpC+HVK>%FRaJ+&rqmy z1f&czH-sDR5BReg5u6o{hLytAmb5ed@q}{PWEh?>lUCoIT@Z~nOC%j>?a0;Rr3@Xd zSO210k#niYc80EzDT*mhv;W+YhgW4o^BQyKNUBsoi-UEI@0E}JV8k@V!S2f8&^IP& zPQIlv;rJ@q3xDg^sMPZ4uVH8^pZ43_IqJpFr8-ACSr7T%)+SvC8=S`_C|qGCwWk{m z@Yz1iXu#Z`wH_%b8JMW{J4Ug0XZZdym||F}6_*j6*IopF9hj1!@ZvJI`u0_;b{^*R z{q~j3J4goI+@Jw~f01Fm9uLDY@`*Z9Ww((sGR9k$`E+)_jhv$iTJGn z`qO2BidZ5NafSw;%82w!H!m!9X7@BDw~Eg_gx-@Ulc$-=s(!5A@C5eqrqX=1!9@n8 z@dJe#f)TDKR5--N)DdKonIVRKj|>(p&Z|%+<#cu{BVdifd0QzXS&*r;SDSd!QKITQ zW0|A_9u~GURe=MJ^d}codOCKZ_ArZFcd`3^rdEy}M+#Z(#8-iw8rcKd_XUW~`9_rc znet8`hIIl5Lpgpu!Z_XK1N(!av9zTNNJsj+NHRJdidRce#^t(n44uK4l9uZOHn2?^+)LVrzYS&P`jtQYh1!XruQ$5SX`>+=DQv@adY$69Q)-LzPusLicv#E3uL;*dA?Xrxn=X%<_h7 zC@r?rxIAkE-Esq?$W#*P+97(L7uJUA>C!|;OcEI27--?iH+$l&6_lmf( zzXR7PN3AOJ^f>hLr^QrM>Q|o8C8z451?lZ8o!IG}5;n!Wn`@}y*AfKOd|{eu#V0Y) zBYRl|rr)eZpD|iCGQxQRfQ06|eaoafb}oyt)n=_3QwHqrJZvA=gvk$-lQ=G}FIlSO zGR#N{3rfI}J6Q}cuqu`)^Mmc!yP24V;_1tVC=(j!B|8vmgaUy7VZ-J%EEJ7Lqk`{ zi|c2}#vcP%38v{FhV3Ol6uzd{{FEtKk$zO~3k9fWrSNSUgzw_+xDi}>^U!}zG^4%@ z+aT;2p!KRzOKmGvq?lB&}ty>;2KFc)MI z@#*x%RP!_=>tP^4{dS~0tw9!-=dcIpX;0L%NLy(N61?-4ynzbrO)3){e+muTO~I9M=*{ z{A|5TZXOg`V?GxKomr^B?otjrmP6PPCuTzU$5D~}^3;Apxp;wdw;87J;UQp6_(aZy z{F2ncBd_=N1UCtiHAjK-wEPj)vc+$q0?W-bb$l+(66Mh|*5hUq-s~9>k z1C?v&y#mlD(DtM~vq}C0G5s8hkWmqu!LGtYcrZHoNU!8+kTjoZ$S1Al4H%f7RZZ^e z1xdg|EqJSh!hDL%??-?x&S6(Yoho8IAX@!mvVJPDr2UA2hQd+THMC{{`#|dWA_^ni zzI*CwO@l||aW~?565G1p4CUviM)Jzo7!P-y7X{$OJ}wCiqRmfb((F~pfvM4)(Gl(0 zOv)WY=fjc*$+_W=FR9&3V!`;mk~D9FBwUpl%lm74MKsAMCBA(*B>yLVuof$F-9d6g z8d(5PAQ%3(P1VEB+S=2D`{%&>V|UF?(fYBwhA*=0O6v#T!BkCVMlEAxHKb*J1Zwo@ zH|~h3$=nr)esy&Ql6lBteMgH)rlCgqrRB>ksT@Pag$^-$Po+Y-BJ?_*;t4M$NSiZD z0#szgYcO>*ag<-=5(>G7-it@HVs;QS+NF>7eRCenAjYek$9RPn4`H`7+N%MB*TinO zQIplj^qPxUa;Cp{{;U`tX5^6nGGJ$h~%sVTJI0rmp1%SsgkweoCAn*n@s>%wDjR%b-UA zdM6cV950fPDnDNxYm==7rzRnQ?C~g}LjC*V(n=gil*BmVdnfR^YV01IVWt2rU;DzU z@uqyqf=FpdM!KP(7i@Ps?RMI!4(nC~16Lc;W(hGS&CE2SX=ox)8sMA$MjoE6{0mv9 zV_+B%#rheF3CJ2(l~Vjl)>OwOwvw%DUIvl^IdQyfoWlB#4!cIXq5w{u5Z}Udd$z4+;*23l}YqOo3*GoUCu=k zIwpgL2R}~0VXWJv9c3AwW~f|c%yLvU^u~)-zJ`~SoaF}lA9)^(!881oGj4Y!r}xP;ksoqkSkk$4=IZ z9g)=3VIvZ<$Q;+N(#7zSte=`DY$1b;qgcC=f+6|bB-8>J5X48%u!EPX3JG}KrumsS zNe(ZLb@54A4O4#2N_VYXRQQJW9u58Mp?NN^_`T(Tu`^a8qF}oVrmJ=GMsBjv$IJ?= zmSlrZqaKHwoepDk*kOhN#inSb#{DPTSBC8kVp|8m7gzQ=Jpd z@qm|;`K?dB>J5=Cd^82UX{ISl2%pN+g}@?caa)!YZ&%B*Nf~!X=Gc_jZcpWrm>P9Y zsqLBo8;`+2g@^7<5nSQ*4nEDZq*h%U&xsds!(`#fY$IBUc;!3VI_moOg;x#Z_V}a# zPf@iQA1)8};%a`xu5F5C6ez{6LR3Nnk< zzpH)fYtb{P-te6xZ&H|!J6AH`dVljMvRx8#ym8TOTH9RDuI7xzq@%MpnP7Tzbw$2l z+J3#ibs)Ss!E(^m)BgQpD82pWKa*on=2-I({BH*+(m6dmay`j1gd5(adqV@LCPs8yU0$%=t~qM`K8!YrcJNFK?Ljl+ z^UP|c8jN|%IByCcv8A#XL;L%#x$20Nqvq(==#NR@JucM>z}Y%~-jikRnRuA>8eR-h z^ed6hF`f1r`8b;LR{5yaDM`L%by)QA9zk_j)Hsv8yBU)uq_6^x_Y3*o>D)0)@yp6T`!sia@RA#Q?W>oNxVLQ6CeDF+d zO)T<+O1AwL?h5|5d!QbnWHtAVvF2C`{v=87>3E=o$E0{^< zx`Rfd^cYT@@+H1^B)XR6yU)9OJ}4fT7zsuP-=Jbytk&xaA#ZTbXdi(6?Q-WkT$3w2 zKKbc+m`tAEd@HjVH<7Bg{s7KAGVD7!J&|?DYzqu|$NcqhBUiJnW|f~Fx;KQ;+bZW1 zgx)q9hs9Gmr`5vJ?11v&{`m{Rj0f{j2F?{XW@#3_*@Ta{hVs^kj_erq6J1|^<~+-y zNG}f3mme#?X&m_`#E)y(HdnN~czqiq`NEgYI9@(o_0@)<uPwy%2Gwv(J6~yVx>td@n(>Jy z{gAdg5fqEo$;0{bJ>MgPHjVC>H*d7&>i8dE<7!R_ojZ9ciAWw)u}BZ(yf^fcd9baT zbAI`5zHQP5%O^y_-xVTKqP+o>b4h1ly&Stc02MNeolh}{$6jm`W#PODW^=?ss7`Z2 zsm$96bi=gs2^-48y(u0KUBQgEyx8w7nI&l%Lppx;TnY>cRq=Qn^YQo#q&qNMyVzC z;S6~Avy;xiZQ)z>?u|y+4oud;g$AWqsjA|ojd!o|uIM-{%~sihtlk3799kQ0{;@D7 zpoMCVB6GL_WMPc=AN%$2^mDZK_>s63{ZSU1UF1;N4J2cirrjx0MF_mkse6JJp?{)m zl^y3e3t1FQd{#OxRL^Z@T9q&YU!U&rsU+FxQ17l+b#+~_?9t6vtJ*?`1ifpcREjSy zE0W_#TVeq3gD$T@^V5Z92}XSnP_ZW&eZjlVvMM@`nMK3#3%nSn>`sjJJc5Gu-Vvj< zZm~sl`Cm1oi;O?tGpCQeK}njB`08Vlzxv!T*Q0Z{rn^gpEcTnBy}SpC?wpz2?FmdB z;(|}vLYoELr%7!*Eu*P$6yt*O81fw^glHzQp>ZPM!b?R~;ro8s>8Ca#dpTTjR~A~0 z6;+P8Jh`u_7`!2+;Lc@(&9CepFO6X%sw-<)VnCE6K#BlY-A7D=M0~L+MUvEt5q+&r zk8Y5mA`^MP6m^SC$!Q{s=zDKczM6yPVzL%T+0Zwg0>uW zO1gP;I#mPDt4km-f%wDKq4d^XQ^ z6R&&JX;w2^-45fNl$5=hW&9YM4epwG=7^B5jG|MRSKJ1QXI)Y`(#+?4*FCCl(?5%D z-T3wFK67F({Uzk`=SwP5Gs8wznwqg~s6MCQ*A5j|pVd~mon>b|R(GU*Qg zuok>g9UNbkBWx^&;LPlE%_fh3T0V%|i_auwVu5q-k^d#=PRMD-H;L>^+QN>rS|jOw zW*xC3oAJ4Up#nN%O_rfYP(_BlQ4VgFm*}#PVbR0UF84w7CoGj3x8e)wh}3Vi(M8#0 zx!UuNg^|qudwp8~>PVD9s=^e>e?nw^Yvp1Iba!#};I?#exBl(;;@`3sIdyT#T58=t z3OI}#`N4p!x>R2IxsZ3c)Yuv%i*oLb7+C_0qt4g<3Z4qLr0x%wR0cd< zqHqA_!Mp8~m)}f<>Ftd~m}H8i9%B)Ei}mHyV$k-C!hPHY8l1A~{Ad}zXdcXM9WNtm7^gXWDj$ONZOX9WHS$XDBx$9Oux|@&499OZ-cK00>0<9?d$bFVo z!FGr~w^nM>gZRNm*@&vaxV2|%M`zB5)z@4LKw$sH4a+O&7vg0cAnEzCny&Oe($4%L z4nZ`Mc7(`&B*C9)XYT6yFYAy~_S==6EbB7IgB$)8?UEK3aaR-p#0{yiPD;DxqO?V#a?f{!4)GrNTo5wV3H~;%F~0dbNNA1N| z#Yo1fKj4*Ndj51-$uv+qi<;QV{V9erdWC`-^s#nrr=!SdR+G*>!o!HM)u64qBedYZI&m4!6t`))q!xD}I)(6%=b=V_NEZ$vE|OV5L0 zOR3qhF`Er$rnQBIT&dZO4&eAmL9FdQUv#EL%&mLxVRi9tl=WNolVQafw>=@gPEQKX zod__qRgam)iKA*T=O*InHB}6xd%wqV0`ij`Ga@WS&pv?96uFhr$k+Sm%}~uhQ@gDX zpj|r%F^`M)42d2L@EmA}%!Rm!d3+Ey$<(}L37~1U>PiaNIq43!4OD6M1!J2``AO-{ zeX=UOK-!NK-h`wJ-#2XO2kHq!Lareogzg95U1<014Y4N)D`o9G`3Mwby z|2z2oeYt)g|KS)IsP@+Ye?7AL9r$C6L~7zs2Ux!Xe{JLc0j)shFTXVPe+B=wo%sh8 z0Qija6a4=)H-C-uYuoFONGQ1feTly{!G4YMYv1FKC`$N0qx{+h`8B|=ncE)$dP#o< z_#=<|75Z1>`vV97bRzqBf3v|~;eXx0{tOqn`zQEs+Zj*|1GzQ;01omag>>38njhc( E55p);2><{9 literal 0 HcmV?d00001 From 759c192a447b56f296ead23464c65eb9edfe2018 Mon Sep 17 00:00:00 2001 From: xandros15 Date: Sun, 2 May 2021 20:42:28 +0200 Subject: [PATCH 20/57] #984 fix phpcs warnings --- src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php index 4c2aeb39..83238d7a 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php @@ -634,21 +634,21 @@ class Worksheet extends WriterPart self::writeAttributeif( $objWriter, - in_array($conditional->getConditionType(), [ - Conditional::CONDITION_CELLIS, - Conditional::CONDITION_CONTAINSTEXT, - Conditional::CONDITION_NOTCONTAINSTEXT, - ]) && $conditional->getOperatorType() != Conditional::OPERATOR_NONE, + ( + $conditional->getConditionType() == Conditional::CONDITION_CELLIS + || $conditional->getConditionType() == Conditional::CONDITION_CONTAINSTEXT + || $conditional->getConditionType() == Conditional::CONDITION_NOTCONTAINSTEXT + ) && $conditional->getOperatorType() != Conditional::OPERATOR_NONE, 'operator', $conditional->getOperatorType() ); self::writeAttributeIf($objWriter, $conditional->getStopIfTrue(), 'stopIfTrue', '1'); - if (in_array($conditional->getConditionType(), [ - Conditional::CONDITION_CONTAINSTEXT, - Conditional::CONDITION_NOTCONTAINSTEXT, - ])) { + if ( + $conditional->getConditionType() == Conditional::CONDITION_CONTAINSTEXT + || $conditional->getConditionType() == Conditional::CONDITION_NOTCONTAINSTEXT + ) { self::writeTextCondElements($objWriter, $conditional, $cellCoordinate); } else { self::writeOtherCondElements($objWriter, $conditional, $cellCoordinate); From 83e55cffcc162fe19fa67b15866328793b52fe7b Mon Sep 17 00:00:00 2001 From: Mark Baker Date: Sun, 2 May 2021 22:00:48 +0200 Subject: [PATCH 21/57] First steps in the implementation of AutoFilters for ODS Reader and Writer (#2053) * First steps in the implementation of AutoFilters for ODS Reader and Writer, starting with reading a basic AutoFilter range (ignoring row visibility, filter types and active filters for the moment). And also some additional refactoring to extract the DefinedNames Reader into its own dedicated class as a part of overall code improvement... on the principle of "when working on a class, always try to leave the library codebase in a better state than you found it" * Provide a basic Ods Writer implementation for AutoFilters * AutoFilter Reader Test * AutoFilter Writer Test * Update Change Log --- CHANGELOG.md | 2 + docs/references/features-cross-reference.md | 4 +- phpstan-baseline.neon | 7 +- src/PhpSpreadsheet/Reader/Ods.php | 81 ++---------------- src/PhpSpreadsheet/Reader/Ods/AutoFilter.php | 45 ++++++++++ src/PhpSpreadsheet/Reader/Ods/BaseReader.php | 75 ++++++++++++++++ .../Reader/Ods/DefinedNames.php | 65 ++++++++++++++ src/PhpSpreadsheet/Writer/Ods/AutoFilters.php | 63 ++++++++++++++ src/PhpSpreadsheet/Writer/Ods/Content.php | 1 + .../Reader/Ods/AutoFilterTest.php | 31 +++++++ .../Writer/Ods/AutoFilterTest.php | 33 +++++++ tests/data/Reader/Ods/AutoFilter.ods | Bin 0 -> 10115 bytes 12 files changed, 325 insertions(+), 82 deletions(-) create mode 100644 src/PhpSpreadsheet/Reader/Ods/AutoFilter.php create mode 100644 src/PhpSpreadsheet/Reader/Ods/BaseReader.php create mode 100644 src/PhpSpreadsheet/Reader/Ods/DefinedNames.php create mode 100644 src/PhpSpreadsheet/Writer/Ods/AutoFilters.php create mode 100644 tests/PhpSpreadsheetTests/Reader/Ods/AutoFilterTest.php create mode 100644 tests/PhpSpreadsheetTests/Writer/Ods/AutoFilterTest.php create mode 100644 tests/data/Reader/Ods/AutoFilter.ods diff --git a/CHANGELOG.md b/CHANGELOG.md index ed4f44c3..7940a6a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org). ### Added +- Implemented basic AutoFiltering for Ods Reader and Writer [PR #2053](https://github.com/PHPOffice/PhpSpreadsheet/pull/2053) - Improved support for Row and Column ranges in formulae [Issue #1755](https://github.com/PHPOffice/PhpSpreadsheet/issues/1755) [PR #2028](https://github.com/PHPOffice/PhpSpreadsheet/pull/2028) +- Implemented URLENCODE() Web Function - Implemented the CHITEST(), CHISQ.DIST() and CHISQ.INV() and equivalent Statistical functions, for both left- and right-tailed distributions. - Support for ActiveSheet and SelectedCells in the ODS Reader and Writer. [PR #1908](https://github.com/PHPOffice/PhpSpreadsheet/pull/1908) diff --git a/docs/references/features-cross-reference.md b/docs/references/features-cross-reference.md index 716a3787..9dcf8d91 100644 --- a/docs/references/features-cross-reference.md +++ b/docs/references/features-cross-reference.md @@ -1313,13 +1313,13 @@ ● ● - + ● ● ● - + ● diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 89355400..89f50e3b 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -2812,12 +2812,7 @@ parameters: - message: "#^Parameter \\#3 \\$subject of function preg_replace expects array\\|string, string\\|null given\\.$#" - count: 7 - path: src/PhpSpreadsheet/Reader/Ods.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Ods\\:\\:convertToExcelAddressValue\\(\\) should return string but returns string\\|null\\.$#" - count: 1 + count: 3 path: src/PhpSpreadsheet/Reader/Ods.php - diff --git a/src/PhpSpreadsheet/Reader/Ods.php b/src/PhpSpreadsheet/Reader/Ods.php index 1a4d7ca3..d4163cf7 100644 --- a/src/PhpSpreadsheet/Reader/Ods.php +++ b/src/PhpSpreadsheet/Reader/Ods.php @@ -11,8 +11,9 @@ use DOMNode; use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Cell\Coordinate; use PhpOffice\PhpSpreadsheet\Cell\DataType; -use PhpOffice\PhpSpreadsheet\DefinedName; use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException; +use PhpOffice\PhpSpreadsheet\Reader\Ods\AutoFilter; +use PhpOffice\PhpSpreadsheet\Reader\Ods\DefinedNames; use PhpOffice\PhpSpreadsheet\Reader\Ods\PageSettings; use PhpOffice\PhpSpreadsheet\Reader\Ods\Properties as DocumentProperties; use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner; @@ -22,7 +23,6 @@ use PhpOffice\PhpSpreadsheet\Shared\Date; use PhpOffice\PhpSpreadsheet\Shared\File; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Style\NumberFormat; -use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; use Throwable; use XMLReader; use ZipArchive; @@ -304,8 +304,10 @@ class Ods extends BaseReader $pageSettings->readStyleCrossReferences($dom); - // Content + $autoFilterReader = new AutoFilter($spreadsheet, $tableNs); + $definedNameReader = new DefinedNames($spreadsheet, $tableNs); + // Content $spreadsheets = $dom->getElementsByTagNameNS($officeNs, 'body') ->item(0) ->getElementsByTagNameNS($officeNs, 'spreadsheet'); @@ -642,8 +644,8 @@ class Ods extends BaseReader ++$worksheetID; } - $this->readDefinedRanges($spreadsheet, $workbookData, $tableNs); - $this->readDefinedExpressions($spreadsheet, $workbookData, $tableNs); + $autoFilterReader->read($workbookData); + $definedNameReader->read($workbookData); } $spreadsheet->setActiveSheetIndex(0); @@ -771,26 +773,6 @@ class Ods extends BaseReader return $value; } - private function convertToExcelAddressValue(string $openOfficeAddress): string - { - $excelAddress = $openOfficeAddress; - - // Cell range 3-d reference - // As we don't support 3-d ranges, we're just going to take a quick and dirty approach - // and assume that the second worksheet reference is the same as the first - $excelAddress = preg_replace('/\$?([^\.]+)\.([^\.]+):\$?([^\.]+)\.([^\.]+)/miu', '$1!$2:$4', $excelAddress); - // Cell range reference in another sheet - $excelAddress = preg_replace('/\$?([^\.]+)\.([^\.]+):\.([^\.]+)/miu', '$1!$2:$3', $excelAddress); - // Cell reference in another sheet - $excelAddress = preg_replace('/\$?([^\.]+)\.([^\.]+)/miu', '$1!$2', $excelAddress); - // Cell range reference - $excelAddress = preg_replace('/\.([^\.]+):\.([^\.]+)/miu', '$1:$2', $excelAddress); - // Simple cell reference - $excelAddress = preg_replace('/\.([^\.]+)/miu', '$1', $excelAddress); - - return $excelAddress; - } - private function convertToExcelFormulaValue(string $openOfficeFormula): string { $temp = explode('"', $openOfficeFormula); @@ -816,53 +798,4 @@ class Ods extends BaseReader return $excelFormula; } - - /** - * Read any Named Ranges that are defined in this spreadsheet. - */ - private function readDefinedRanges(Spreadsheet $spreadsheet, DOMElement $workbookData, string $tableNs): void - { - $namedRanges = $workbookData->getElementsByTagNameNS($tableNs, 'named-range'); - foreach ($namedRanges as $definedNameElement) { - $definedName = $definedNameElement->getAttributeNS($tableNs, 'name'); - $baseAddress = $definedNameElement->getAttributeNS($tableNs, 'base-cell-address'); - $range = $definedNameElement->getAttributeNS($tableNs, 'cell-range-address'); - - $baseAddress = $this->convertToExcelAddressValue($baseAddress); - $range = $this->convertToExcelAddressValue($range); - - $this->addDefinedName($spreadsheet, $baseAddress, $definedName, $range); - } - } - - /** - * Read any Named Formulae that are defined in this spreadsheet. - */ - private function readDefinedExpressions(Spreadsheet $spreadsheet, DOMElement $workbookData, string $tableNs): void - { - $namedExpressions = $workbookData->getElementsByTagNameNS($tableNs, 'named-expression'); - foreach ($namedExpressions as $definedNameElement) { - $definedName = $definedNameElement->getAttributeNS($tableNs, 'name'); - $baseAddress = $definedNameElement->getAttributeNS($tableNs, 'base-cell-address'); - $expression = $definedNameElement->getAttributeNS($tableNs, 'expression'); - - $baseAddress = $this->convertToExcelAddressValue($baseAddress); - $expression = $this->convertToExcelFormulaValue($expression); - - $this->addDefinedName($spreadsheet, $baseAddress, $definedName, $expression); - } - } - - /** - * Assess scope and store the Defined Name. - */ - private function addDefinedName(Spreadsheet $spreadsheet, string $baseAddress, string $definedName, string $value): void - { - [$sheetReference] = Worksheet::extractSheetTitle($baseAddress, true); - $worksheet = $spreadsheet->getSheetByName($sheetReference); - // Worksheet might still be null if we're only loading selected sheets rather than the full spreadsheet - if ($worksheet !== null) { - $spreadsheet->addDefinedName(DefinedName::createInstance((string) $definedName, $worksheet, $value)); - } - } } diff --git a/src/PhpSpreadsheet/Reader/Ods/AutoFilter.php b/src/PhpSpreadsheet/Reader/Ods/AutoFilter.php new file mode 100644 index 00000000..bdc8b3ff --- /dev/null +++ b/src/PhpSpreadsheet/Reader/Ods/AutoFilter.php @@ -0,0 +1,45 @@ +readAutoFilters($workbookData); + } + + protected function readAutoFilters(DOMElement $workbookData): void + { + $databases = $workbookData->getElementsByTagNameNS($this->tableNs, 'database-ranges'); + + foreach ($databases as $autofilters) { + foreach ($autofilters->childNodes as $autofilter) { + $autofilterRange = $this->getAttributeValue($autofilter, 'target-range-address'); + if ($autofilterRange !== null) { + $baseAddress = $this->convertToExcelAddressValue($autofilterRange); + $this->spreadsheet->getActiveSheet()->setAutoFilter($baseAddress); + } + } + } + } + + protected function getAttributeValue(?DOMNode $node, string $attributeName): ?string + { + if ($node !== null && $node->attributes !== null) { + $attribute = $node->attributes->getNamedItemNS( + $this->tableNs, + $attributeName + ); + + if ($attribute !== null) { + return $attribute->nodeValue; + } + } + + return null; + } +} diff --git a/src/PhpSpreadsheet/Reader/Ods/BaseReader.php b/src/PhpSpreadsheet/Reader/Ods/BaseReader.php new file mode 100644 index 00000000..82d41710 --- /dev/null +++ b/src/PhpSpreadsheet/Reader/Ods/BaseReader.php @@ -0,0 +1,75 @@ +spreadsheet = $spreadsheet; + $this->tableNs = $tableNs; + } + + abstract public function read(DOMElement $workbookData): void; + + protected function convertToExcelAddressValue(string $openOfficeAddress): string + { + $excelAddress = $openOfficeAddress; + + // Cell range 3-d reference + // As we don't support 3-d ranges, we're just going to take a quick and dirty approach + // and assume that the second worksheet reference is the same as the first + $excelAddress = preg_replace('/\$?([^\.]+)\.([^\.]+):\$?([^\.]+)\.([^\.]+)/miu', '$1!$2:$4', $excelAddress); + // Cell range reference in another sheet + $excelAddress = preg_replace('/\$?([^\.]+)\.([^\.]+):\.([^\.]+)/miu', '$1!$2:$3', $excelAddress ?? ''); + // Cell reference in another sheet + $excelAddress = preg_replace('/\$?([^\.]+)\.([^\.]+)/miu', '$1!$2', $excelAddress ?? ''); + // Cell range reference + $excelAddress = preg_replace('/\.([^\.]+):\.([^\.]+)/miu', '$1:$2', $excelAddress ?? ''); + // Simple cell reference + $excelAddress = preg_replace('/\.([^\.]+)/miu', '$1', $excelAddress ?? ''); + + return $excelAddress ?? ''; + } + + protected function convertToExcelFormulaValue(string $openOfficeFormula): string + { + $temp = explode('"', $openOfficeFormula); + $tKey = false; + foreach ($temp as &$value) { + // @var string $value + // Only replace in alternate array entries (i.e. non-quoted blocks) + if ($tKey = !$tKey) { + // Cell range reference in another sheet + $value = preg_replace('/\[\$?([^\.]+)\.([^\.]+):\.([^\.]+)\]/miu', '$1!$2:$3', $value); + // Cell reference in another sheet + $value = preg_replace('/\[\$?([^\.]+)\.([^\.]+)\]/miu', '$1!$2', $value ?? ''); + // Cell range reference + $value = preg_replace('/\[\.([^\.]+):\.([^\.]+)\]/miu', '$1:$2', $value ?? ''); + // Simple cell reference + $value = preg_replace('/\[\.([^\.]+)\]/miu', '$1', $value ?? ''); + + $value = Calculation::translateSeparator(';', ',', $value ?? '', $inBraces); + } + } + + // Then rebuild the formula string + $excelFormula = implode('"', $temp); + + return $excelFormula; + } +} diff --git a/src/PhpSpreadsheet/Reader/Ods/DefinedNames.php b/src/PhpSpreadsheet/Reader/Ods/DefinedNames.php new file mode 100644 index 00000000..79f5c027 --- /dev/null +++ b/src/PhpSpreadsheet/Reader/Ods/DefinedNames.php @@ -0,0 +1,65 @@ +readDefinedRanges($workbookData); + $this->readDefinedExpressions($workbookData); + } + + /** + * Read any Named Ranges that are defined in this spreadsheet. + */ + protected function readDefinedRanges(DOMElement $workbookData): void + { + $namedRanges = $workbookData->getElementsByTagNameNS($this->tableNs, 'named-range'); + foreach ($namedRanges as $definedNameElement) { + $definedName = $definedNameElement->getAttributeNS($this->tableNs, 'name'); + $baseAddress = $definedNameElement->getAttributeNS($this->tableNs, 'base-cell-address'); + $range = $definedNameElement->getAttributeNS($this->tableNs, 'cell-range-address'); + + $baseAddress = $this->convertToExcelAddressValue($baseAddress); + $range = $this->convertToExcelAddressValue($range); + + $this->addDefinedName($baseAddress, $definedName, $range); + } + } + + /** + * Read any Named Formulae that are defined in this spreadsheet. + */ + protected function readDefinedExpressions(DOMElement $workbookData): void + { + $namedExpressions = $workbookData->getElementsByTagNameNS($this->tableNs, 'named-expression'); + foreach ($namedExpressions as $definedNameElement) { + $definedName = $definedNameElement->getAttributeNS($this->tableNs, 'name'); + $baseAddress = $definedNameElement->getAttributeNS($this->tableNs, 'base-cell-address'); + $expression = $definedNameElement->getAttributeNS($this->tableNs, 'expression'); + + $baseAddress = $this->convertToExcelAddressValue($baseAddress); + $expression = $this->convertToExcelFormulaValue($expression); + + $this->addDefinedName($baseAddress, $definedName, $expression); + } + } + + /** + * Assess scope and store the Defined Name. + */ + private function addDefinedName(string $baseAddress, string $definedName, string $value): void + { + [$sheetReference] = Worksheet::extractSheetTitle($baseAddress, true); + $worksheet = $this->spreadsheet->getSheetByName($sheetReference); + // Worksheet might still be null if we're only loading selected sheets rather than the full spreadsheet + if ($worksheet !== null) { + $this->spreadsheet->addDefinedName(DefinedName::createInstance((string) $definedName, $worksheet, $value)); + } + } +} diff --git a/src/PhpSpreadsheet/Writer/Ods/AutoFilters.php b/src/PhpSpreadsheet/Writer/Ods/AutoFilters.php new file mode 100644 index 00000000..cf0450f1 --- /dev/null +++ b/src/PhpSpreadsheet/Writer/Ods/AutoFilters.php @@ -0,0 +1,63 @@ +objWriter = $objWriter; + $this->spreadsheet = $spreadsheet; + } + + public function write(): void + { + $wrapperWritten = false; + $sheetCount = $this->spreadsheet->getSheetCount(); + for ($i = 0; $i < $sheetCount; ++$i) { + $worksheet = $this->spreadsheet->getSheet($i); + $autofilter = $worksheet->getAutoFilter(); + if ($autofilter !== null && !empty($autofilter->getRange())) { + if ($wrapperWritten === false) { + $this->objWriter->startElement('table:database-ranges'); + $wrapperWritten = true; + } + $this->objWriter->startElement('table:database-range'); + $this->objWriter->writeAttribute('table:orientation', 'column'); + $this->objWriter->writeAttribute('table:display-filter-buttons', 'true'); + $this->objWriter->writeAttribute( + 'table:target-range-address', + $this->formatRange($worksheet, $autofilter) + ); + $this->objWriter->endElement(); + } + } + + if ($wrapperWritten === true) { + $this->objWriter->endElement(); + } + } + + protected function formatRange(Worksheet $worksheet, Autofilter $autofilter): string + { + $title = $worksheet->getTitle(); + $range = $autofilter->getRange(); + + return "'{$title}'.{$range}"; + } +} diff --git a/src/PhpSpreadsheet/Writer/Ods/Content.php b/src/PhpSpreadsheet/Writer/Ods/Content.php index e4bd1793..a589e549 100644 --- a/src/PhpSpreadsheet/Writer/Ods/Content.php +++ b/src/PhpSpreadsheet/Writer/Ods/Content.php @@ -101,6 +101,7 @@ class Content extends WriterPart $this->writeSheets($objWriter); + (new AutoFilters($objWriter, $this->getParentWriter()->getSpreadsheet()))->write(); // Defined names (ranges and formulae) (new NamedExpressions($objWriter, $this->getParentWriter()->getSpreadsheet(), $this->formulaConvertor))->write(); diff --git a/tests/PhpSpreadsheetTests/Reader/Ods/AutoFilterTest.php b/tests/PhpSpreadsheetTests/Reader/Ods/AutoFilterTest.php new file mode 100644 index 00000000..47c7ee6a --- /dev/null +++ b/tests/PhpSpreadsheetTests/Reader/Ods/AutoFilterTest.php @@ -0,0 +1,31 @@ +spreadsheet = $reader->load($filename); + } + + public function testAutoFilterRange(): void + { + $worksheet = $this->spreadsheet->getActiveSheet(); + + $autoFilterRange = $worksheet->getAutoFilter()->getRange(); + + self::assertSame('A1:C9', $autoFilterRange); + } +} diff --git a/tests/PhpSpreadsheetTests/Writer/Ods/AutoFilterTest.php b/tests/PhpSpreadsheetTests/Writer/Ods/AutoFilterTest.php new file mode 100644 index 00000000..4368ef76 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Writer/Ods/AutoFilterTest.php @@ -0,0 +1,33 @@ +getActiveSheet(); + + $dataSet = [ + ['Year', 'Quarter', 'Sales'], + [2020, 'Q1', 100], + [2020, 'Q2', 120], + [2020, 'Q3', 140], + [2020, 'Q4', 160], + [2021, 'Q1', 180], + [2021, 'Q2', 75], + [2021, 'Q3', 0], + [2021, 'Q4', 0], + ]; + $worksheet->fromArray($dataSet, null, 'A1'); + $worksheet->getAutoFilter()->setRange('A1:C9'); + + $reloaded = $this->writeAndReload($spreadsheet, 'Ods'); + + self::assertSame('A1:C9', $reloaded->getActiveSheet()->getAutoFilter()->getRange()); + } +} diff --git a/tests/data/Reader/Ods/AutoFilter.ods b/tests/data/Reader/Ods/AutoFilter.ods new file mode 100644 index 0000000000000000000000000000000000000000..a8f53b8105f466d30330e4533ff8ad113fba0675 GIT binary patch literal 10115 zcmdUVby$>J*EfoEN~0((4bmafC7^V749yHMLw7gQ-6`EIAl+Rz>*7%)Qp$`?qGV9qT761^WaC1_luZhBhiw-q(UVh!F+`=Ha^k3dYjZ z5&&_u0_a&;S(qB=K}7&Kall z!O{X2Mpg>p!80(AAKvcepCKc@|Ml-+n_B7_1Heo|rVvX#EAYP{@H@PXEI@h?z`uFv zcep+D-%1Y%u=tp_2=0yTCaPoZFVX*Es1Wo-~muc7+SjM>Trp zo;B1?m*r_8BE2FsawIf5S3J&9Z)ecoMc~<5Pe68qUI}7FEOFmB0C}TJLFjNP`>*kR zVs>@i2oYE{U(r(nn$h__-@SEtHgDlpXQU>E^;uF)9=|R+BV0VF6V*qmoF!^EePca{ zRIsB!1P+fbp-9zNimu8mOT~$XH8bWt?(}j^RR;pMlB(WYHOL!R02)v#k7z#+nT{S> z#gJN_--aOA9^#8cM@#X}ztc1Fc$Eh#saBk6(z2v;P|8R+oONkaA97kmp3J4G1TK&> z#nKC!)F(^S86pp6C=*e*q_IenSHrK3m*GH*{Ioe4Wbgypk*`9x&63<)3lCc^K&o#S_7MU!S*ZJ;EPL4rz+R|E5dT$jdlNFkx!|;N2czJ{L`&mMRWno`#IfVeT@|e7g7tGNM!T_*e6`O-^jILSb zprxrSnt>jvki1ZMx!Gjo8H-K;9rs$$If$Gczd!+Q$Efnd?rW_l&z)_IP(KW!QgTUK z3w3VHD)MsjvJMBW@i@Foci9;nUd+Jq!s(0j#q)VN-3qJ7d{pGk>fJh^Q2b3VObW_Q z@eztm$}1vj_;?-Q&ox^9bV0EAgGA6lx^0_u1_TESU#D#w_Z=5DeN-j?C|Fv|(bh`G zckWFxEARC;{pe^o7hI0R(O6s)kKm+>vB|7*2T<4hRTH&bE@5`d*>{EY8QD@w@w$?* zD5aR<+~SiQMI<`RaZj9NS>a4Z+6rD%3T4VyXPtDeM`dy)h~${+$YQ^E*BDYvO?&ce z{dKU^+i(HBNTQ1$M^Yn7PumBc}WWi;N5n^a=AG#~6L?jv^kmwZhAz}Z5n zRSeweG~Z8plm#kzUpaMZkxCV!q}?ruz-FF`EWb0G;H@cXLk35n#l)6AzAa%kv=U@Z zq;B?--wk_CYmWfV_|vWC@iPQi)nqW&u^OJL(Fg5Y+9(Yp7J5Li;IYdJ! z5z*R4ncN%VcwTe-H%VY+l`E&5+DVo;P(nUDO4nBn1UkTozmB0As*YK)SY8`YsJ6oG z2DooNppf2|Bc6Z-E{Bq;ulj#{xpq7=3-_{sc2@Px>En}-(h$QX;g$qLlmkYfeKeH@ z*_FRmsUc_2^$RCur=s~eN0rlVl-MP@Q#?rB@Zqx$6N)2vo=|H_be`v#DtHxw4L4gPL96^zGO%v;1Ms?Tc=%3{P#L1Wcl+xC~u(l_n7} zbY*T$IA-}SA7n+ZOBNB)!U5DXYTe_gG23um+f{-?Y0c)&qg-&AvDIU?y#!yN{}R>XDtlrM z4l1BphOYw?FD)>H6aO!arZwbdGDqc)?*ut^*vO&gH!VC0>J{Cu{=9 z7pUNuIEHpzvWYbFh;LP1KXGM|8@|Q9sks2&&{gl|EA6za70s?_0SqHkT^0px(){QoqZ!-fu&aRc*%nZ|0)vzt3mc^|%U zXr(_qRbaMdfTP<4t50ntXEyeyN**>DhpLZW2Lql$uAQD8LHH-H-LSgK#<{{yWr#TG zajZ^O2e9DJtUr(Ohn>n^T-@B&#wUterZ-0dWBWNqLVk#sJDb5>I$#K9jXha#>5Dc2$KTBE1Y1 zGX1{h@{D`vE0U3l9Aq~=-0_^`ZSVq5etHG6UX6T{e+@~5r$8u@be|8zo2cP~R3~ff zi5z!p#{SoC-KKHxOkUk@bYa8L#Q@aYb?xP*vI9-;C6^czrvn2g+wsn$CGu$9WR?S& zoyIqgcbm72!*_&VORMZeEChzKjIY8stB#44#7iIGt2#FsgL?!6;|=?7@cprP1VHp2 zrt?sR{>bsyn4eE@85vYGF!(u?xfh>PQGX)C>1Gg3eBw!-L*e-1_T?pRMR@zwM(V+K zDq*8zLvq6!T=Mjk#nVAV$uAOxRouYQ+68}zcj~Nt=v$(yCnZIDGP*)eUL=v!?5V>! z3_B?+$`1SWPpQ7~N&ts-m62KiC%q>{6j44=) zu|Lq_ERXIOY28|89;DW;H)yN%dsbf?QB_GBgJQpuH_r=yt)10~i31z_L z^&!uq%eUdU_c8MmX~RQd`&M#M-V~RqG29C{xDg0yc%o`CRgYUG)ukkHarG1SOVzw} zUSKR;%gypXI96*SDj@6Lu||J6_Rn2`;;Nq)-2H%fxE|JpiYB&}`anHX3osMp&myB0 z&^S=`gE$)UvxmhT+B*pm`TNgi7??*ei1!v@cwX?ngn_wVze+2Lva&L;bMkV(k>C~- zH&uEoCMqEztsp6{Auk}TC?%>WEv5cJO7VlLioB$PtdhKfri!YfvbwsCg1nlthNil* zhKA-lMSV4OfR4VFmY#`*vAwaXl)kc}fu@4Krnb9M!Yd*AV|+q_ zXH=GNT%liLVQ6f2Xku^;KGcU`s}dE%&7A0_^RUM=Hlqaf~4k( zl)SvW^t|${;>zrjhQiW{PZbT7ML89vl?_#eHMP|VMQy2-J(-nlnKeB*b=`S&{RP!+ z#SLABp9gZAMyoz|6}C<^wDneZL+gfD+e;EV%9GnGGh3?*y6f{g8jJd?GyCgv`x*;7 zK39)7<&8EM_O+JwwAGKamX3E+PIc8y^?jc2t(qHXS{iLx?yFoKsqgOYX&;zuADrnM zoERP(=pBbn4YotaddDXwMkZ$`XBX$D#^z_HXJ_X+Ccbt-H@g=O`=?e0W>)(bcE%Uh z#um5d7rzcI?~Q%kpIcs?Tic!8Szp@Toml_2uyeAof3ZH@{dI19YiaD;V&DGC(Ax6Y z`pW#?*U8@V*`1Qi`-_vfr-qs|EttiG!! z07vA+4Xl=tM58OhkyhYbN6i3mZ$6^RXG@SO9XwODWRv_u3encUuBGU5D$qt`Ko!2)(-Osv-4i66ocS>Xq>bx7N`1uf8c!#fD zPx{#|THK_sb$((It@o(-5L1{;cv|pTq4PhNP_ux zG{x@Jb_SykPLv+|pe2T!l-w=1M$3G&g*9maglmBejgDD$+mF7zA!jn4;>}2&z7qfL zHd=Jal}n4}B~xq#c{kWnc6LI@lQLL))(O1JPkSu`eRATw9#YnxnS^B6M496h~d60vkO76vW|pr%SNvrnWRl~P5kf;7Tzp+ zcHKEXN|v%ybNA)A7<;mt2D(9i&PLo`#T>tqj`sZNj)$uv5RHIJb@t^1Q)YpnWy+6_ z+_m^;413i1v57j>rV8Bdv`bUTQI(Uf+<9@j0Jq&-B4#(?FhW1@tR7hl)^n=kG%`2v zi!XpKv{zkl%0?cBrpCVU#Te}&XavH(!NvoTC)IOO6AW}{NNFW3JqNwd08di7X$)LM zLq;=*&iI?Ft>55bsVagn)A`iel~X)0zTMQLusqWTL;K@MkGml43G3kGF z$uTQ!0tFmu2^Z-3zPZihsE<8C9cdU%IvH+Jg zsSZ$}Q~p$bz+00*MI0}^SECwVLn}M7VeE9N8f2((f5@K@0@(!Pm0G*BzpaJ} zintgA#1p#m757sP6jprnDXUYy8mw$KVxdPD5`OgMaRn?==3=6GD3tb161@9$3};vD z=6Cg;&*&&G4lQ60zfqNj##dWioi8E=@1nd{(TE-h@{7rp`fU1Y)DbfZloD`US(x;r z8Jz_lt&3>zQ$*rY_Vlz@;EJ@9E1XKs^c%#36Gf%1&d>czYI?B7mki5jViw6g?oJnY zJSEeW2|#0aY!w7y$;_Pd&V5XUsWW5&r6~@mQ1?=J;+=nzaDP8~WkuBYqBM;=gUGJc z0dK3Phm=PxC;mv>pVeA=_Cp83y3S6-9Z+2pIDywQAYE@!_FEVa%KBCempC|x)DRPc zNB5> z#G&wQer736g2!cbNxAlI+FDER5gg;IQHbA*tT*XkJWsMV^EU((3t`d#tj8V?SCQDI_6gIono|9;6 zBtCu=`u#I;DF(^zHPh1<7N|CktK-@w2%5&JGLP(Vle8I`G@)wq>yb&Ti zHAD2EQ_?!b)paYK<6yrr{h`TlJC()bS;HkN4P+PNr3lZq z+3PWE+1{UyQlygR+W~3Yx&mF+$cZm|#QBaRKK_4Dq;$MNLH6D-2#4B@-Y;oj-idw? zDG}23`pxd`6)#+ zqAFH#G82Y-xcO%LRbCFF@P;=&B7ZRHXa{~^R||RchiH|9l4R*usp&y9Oc~77GS{gR zr1?3YIcDiBV(27^>#H3e0j12jOf>Yo_j|^Qo7NxC$D&gVTKb~lrOe`C_!fT?w8la{ zbxM{W98x9Xl4>qaPMONzY;a8dcyg)Q)046|KVR1g6o!JVOkX*ABD>&o5g7EPgyjK4 zLj@Hy+~DVOqYaPnva8KM<$lael$1qmon{X-&HKbK-dpNwn4)!U@@4QgOmdf(?#qH= z0>W873&v~(^JJsvg?6KTotn%1Lh7+(S4|CS%_^-<#l#i66huQCaa5N5F`yhDVjR^Q zqrq1@aw+P-m;gWHb&bEmx9+xT{q&q0%~}Ef>65cp>q%rRVY0A~S!?HkUuX#we+)0| zH??@jr+(QpeXSQb#>VX^mPmryyxkTPj-3Cz+vjW$Ka1VGZXYt>ui-yaoDqTQrA%1CL|yg`VWlr}frVsnE3Tynb+<1~pVsPEhB@s3HplyV{EISU^&pEZ!t>zn4P z+UT|9pskJt@vYt9OJ)OX1YtnFt!6y;?v;h?+2eH%6w=hE5)!T?k1jNe;{YE)vEu8M zD@`^)LiCWfrYz?~-5fpgk__h6?wAvl_r6!(QB@duvck-~sh(17AzI+m8cW(@(v0(Q zOuUUL>ft*_GD{eKeD$OlyUTA(&NLvceLL(@p8Dy|sLB;pnV`0Doyv+GMuf*`TnqfW zNe>mh69M}+wpDS>#L!K3d;jAm^}GZz;l#`Ak#QWMqnVYRU{ix4eYI0@-iUAXAClE8 znk3|l%)=8spZT`UNv1FL;FWdAWiV@*7cFz644CS3JZCCdty^BoWF2^qZXHF&Pwq!& zsk|PmHvcHt4r)@;6-+qWJiw_SAX1sp?UFRl+Lr%j0+Sk0&}PN9Vc~7g|4m;;13xN}Z*BX=a*k2jkR4 z|2`=r<|q>h3vO1mNG%28M~{SJ1(hsr1nmc-TX8wudmt*gPXgITxDGg zqe@~)()$dp75tF7h-^c`#TRZ<^ZXx60CV~D?(E(^R=ZE7go@F|0;kNP z*<)Bg)$^BZ5-KGL4vc-xuWXfpctN9E0AxD=T2j8EbzijBn+1qQ8-NK8_(D>|M((P>5 z>`b_-602nYUE9`mvGQaAg|N1$p^#I})~>P(=FYzosHbKde1FT&1L22Ey)VVLO8_@H zU@Xif+S;+7Yg>aXhMJ_Ivvlm-*d-ocT5sZwN*t9=MCUb+K>0#yi6R@#nV@=baoLQE zw1`cJXf8)5Zu`bKC}}Xe8FoZbEM1CQ$oq?G(IHtEah~?~r9;jS=VQJ;AxRoZmbvU$ z77Ivdn8l@k}eC=5+PYbbl z6SkUlae>%5c95EyI=ek{BadI z6-{Rj<*rN<(uQmH^JNjf^}jMx@;hj8ws(2$`WRAAJ9v{l&YvF)LZGlvIB18x=#<5b z;Mlrzjn#M(QO zmf;SB*0;ud>Fm9RgnWfvPd0!?sN|l7(Ro}dOGILY8RV3^<-2$Cn4MeD{)F=jv13pP zX^sq%j&gKfEivzKt=@QF`8cf+!HM|0MEceJ#?vaUpXDVzz6Wr)+kC`+^dD=3U&4 z-2+Vy=tE!JJ)CP@VmnLgiiE*;q5->;lc=F6Zd(e*7h!6qyyh3sO>A*#O5gp~lFs70 z2*PU?`S8iWkQ&*`1UE9KV!04(d$-QEREk7rqz6@uje&34V*n_E{2{S9nS@ z)5c-2+ma!trQqiN{7OXdd%w@y>Dd-Q@wch#MW>~^#cS;#wji7^q!Y5-&4foT1QmnD zPmWTl$9QkWKnJ!7&0GBI*KUm*!WycIF8J(JSXWf|(b(nr-p@({NK*YmnGxZ7MDRjE z+gcTVjeC{-Dtt!N@1<`B?3v);SrLVVXLY4(+q19ItoUBd!y2Urk{spDu0&LZMu=Uw zU5gG*4lnlL?Fhd{xWlS1u#g-6LKjzZ7A0eAHz_`_((*9+aU4D0cif+q!vAY#0{4FO zzZX>$WO(;MjOniw#r-*Zv7!{Podk1X69;duZ^TF`gG)_u2Wbpeme(%)n@2op5J-oJ zCapjcr*>iRyonRFY?cUKXP-0z1sZ^VNlz>AT;1Q5oUi8adm*$4zpyoZ*F!Zeq~Me^ zWkGDshlju>dlXe5u^9bn85#$MM^Vf4F8TOOBUTyCuBh$GZnA&FY2K0i0^o4h#9kbjYGwdye#ed5n1Lv3g#1{FkaKSlx^b2}cxYJzpL<4?|2j9xo0j z1pq9G?ZEBxyzjQ_`*K~hbtFk;)Y&t=m)u8No(BVMR=Vst#;vLPEY%CJC(#9YmqcGS zmV80>ju#@Uyfl0XEc))=9cZ}-?CZwm(Y{AN=uY2!e=@LM&fhQ`b9IDU)ba;+B0hnf znC$+TTlC38h<)@J2j-vYFZW3QB7c%${;u`QBQOsHm!G0|U;H}*9b=JoA6s&)eS^`$?wxXDk2hJ+}Mq z{|>D`$u<9t*RN^Lhm6cmvADPMlWg-RB!A-kYku!xq4860?~{6e#hd$-=-)MeO(6Y2 zQTj{u?R^98iDZ9d1OL?er{vOKN-%i$)&Enf=}#+vw))qF)5AXVr^wy^ Date: Sun, 2 May 2021 22:09:38 +0200 Subject: [PATCH 22/57] #984 add support notContainsText for conditional styles in xlsx reader --- .../Reader/Xlsx/ConditionalStyles.php | 1 + .../Reader/Xlsx/ConditionalTest.php | 31 ++++++++++++++++++ .../XLSX/conditionalFormatting3Test.xlsx | Bin 0 -> 9486 bytes 3 files changed, 32 insertions(+) create mode 100644 tests/PhpSpreadsheetTests/Reader/Xlsx/ConditionalTest.php create mode 100644 tests/data/Reader/XLSX/conditionalFormatting3Test.xlsx diff --git a/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php b/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php index 5f6cb4dc..80361371 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php +++ b/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php @@ -45,6 +45,7 @@ class ConditionalStyles || (string) $cfRule['type'] == Conditional::CONDITION_CONTAINSTEXT || (string) $cfRule['type'] == Conditional::CONDITION_CONTAINSBLANKS || (string) $cfRule['type'] == Conditional::CONDITION_NOTCONTAINSBLANKS + || (string) $cfRule['type'] == Conditional::CONDITION_NOTCONTAINSTEXT || (string) $cfRule['type'] == Conditional::CONDITION_EXPRESSION) && isset($this->dxfs[(int) ($cfRule['dxfId'])]) ) { diff --git a/tests/PhpSpreadsheetTests/Reader/Xlsx/ConditionalTest.php b/tests/PhpSpreadsheetTests/Reader/Xlsx/ConditionalTest.php new file mode 100644 index 00000000..6ee7bc37 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Reader/Xlsx/ConditionalTest.php @@ -0,0 +1,31 @@ +load($filename); + $worksheet = $spreadsheet->getActiveSheet(); + $styles = $worksheet->getConditionalStyles('A1:A5'); + + $this->assertCount(1, $styles); + + /** @var $notContainsTextStyle Conditional */ + $notContainsTextStyle = $styles[0]; + $this->assertEquals('A', $notContainsTextStyle->getText()); + $this->assertEquals(Conditional::CONDITION_NOTCONTAINSTEXT, $notContainsTextStyle->getConditionType()); + $this->assertEquals(Conditional::OPERATOR_NOTCONTAINS, $notContainsTextStyle->getOperatorType()); + } +} diff --git a/tests/data/Reader/XLSX/conditionalFormatting3Test.xlsx b/tests/data/Reader/XLSX/conditionalFormatting3Test.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..ee23e21c8c5d9a6c1cf5b819de6a30954d0bad5c GIT binary patch literal 9486 zcmeHtby!qu*Z$BQ;?N?EpfpH#4k=QCbT>nHcS?6DARUs@NJ|f0(y4T}e53DqKhNR# z{;v1$?|1fGdp7&pvu^gY_Pw5UuV-xq88~<#01x6=ax<+{lB!jUaE zsw?`Hn})UB&M8C`Rg2rzL0y;%5t37{dM!6$D7CFvaJgf=qj9PpNe5OO>t6P^2P!@g z40?a^RHKmkvtFzB6E+hR`qP4T?3^_eJaa9Dlcx14cWQeB@sql{@@(nAqZZ~A3cRPB zs&`~x>$}x;I*X6|R+YbOW!hT?zC|u;<@~tOd03c+V>{|G+l$V(J)E`c_Lo&78X-FAM zHik+WGo&Yi>Xf@5J8&lWJYz>0%2{i7!~>}0Jm1PR0U;YMC96N=#Q$)lJ%wlFN$fOf z^E#5!O9K&u$NDbx&0gaX*j2X-c8l&%sGsV$N0I?28~)Ob@1rxDnud2Vl>%du zSx_?F-N6AA{t}TjD$JCp&?~agfI|Ze!RXi-S=uu({`e-#?)xVa|Ib8VMC`D92Qzy2 zKB!4_?fRQF2>IRnMp;lJx$@&rRC}Z~kvUXC=Vt+Me$obWBWIh#f*K2Z8)nL+kMx=3 zI~r((E7Qk7Q0egtvJkonLzhJ53k{h3QBjnN8+=L%9q`lbAFkJHB<^j=dKz-7|RYQ8wRtL3t#kO_r=mX7%r!|a8& zw|yD2ObaiXhxk_V1D53sFZ9~Fu#|Ov%k>46TUuCX_8Er*0Ehs{FfNu%|DwAy*v>*9 z47T`@fBv98{156wfBXOU))Y5v{*yHR&Hf{vwz1Ym@jT5D!sQz00GgL8kWP-ovnOD| zIMD{7UdDI${!}-ff{^^%<^26ld_?mMm1t@@DDlbcOsx zZM{EwcgWk{h~=G9WrtBC1kLQ{=8o!?)7d_@N*`xDNi`rO`s(O9iP6U=_mTBx!bd-k z?oEQ9O$i5?ynoctoke4VwTy@Dx=B=Gx!Pwi{*WOyci1-w*|X%=R6V^kp`B#q9c!FH zy-B*2#2tn*%yZB6K5zL<@CGM~G?bW6YWumq#tjN)4Dv!nk35aNT}?fE^a6!x&w#GS zc{VtGUjGp7ceCXIpGt-{lobxp*%Av%4Jd>D%$*2D;TQhQ1kGs|q^S!e3LLCSZP_nZ z8M`~a3?+C)7{8~siW$_|M>6EfL3(kk-P*I-jMP6-p|}QD5+{+*o*EGEGZY!^mNoiu zaBMIgXR{tplMsWB-#4dt=gNGB$lZl=Lr6iWF+Oo&DxA#56$RJ%p^|^MoStPDzeppN zrj;++E7%KZ1bXziw=0Tc!g_jlIeK9t8wa{ad9b@k575HgtM{eVc~cqC3Uzs@D4NQ2 zTmoC7!f?7Wyn&gwJ)(z8miRbGjd(9j4mdNkAhwUc?ttAqgnL|bdr0`X8vE+|DoXdZ ztUi3DZ`dW=B&E5AI{WX5>j(k1`j!9H#jIX-zFJ!nmnLKBc|3+*c1p445Ffjg&Cw?nAyDjp6qN~uI--8>>w17%V&jj+Ft{RVP7*ymDpX{ z``~RxDWtgU5JC7tiZ}Toq2`?1c}B5|QWq6D@|IG4=$vqga&3Jjg{WHh5$iArb)QC) zO06v?M70~16m~J7v}DqJ`x#1}nU7*RU{0%SBbc^BNqQW8K1J3PmTJak zWr0wD!Hfxv96(`-G1A@s3EFEoKIj)FL<=e(! zX0rO58f%P8msd%?TgdQybLeJJdGLJ7R%_ z@LF2}_I1okIX#+Sx+3z%sH-teTeIw$$R7?_E4On3T3+@qqX7*?Qw(2s2IPN>kC!=s zW4!Via|yzgi%B&x^b`HO%P245r_X={#@T7Aka?&1dXypt9br(J*F~^H;R%{oxsJ#6 zRTtaIRo7UK$*N9^#FY}{CIcPP@A^>oL&0*6m> z4E=`(Hi;4w?Og+1y9OQ#)Q#avOx4Zmi2T(c>EZMFWEb;2mh`8OV&Z9Y7Z2&kaVO{V z$B(xeF%XhGvLT*fJjtpJh9F683WgAC2>c3bc_pF9?xpeh<8L;qMyHtE2QdoR$ttvh zR0H;_j(kd7h5Fo`DE1^?XvKBXy=|HSjAc)7#ds(pau$+7>f-j2KF#tFu)e8Ag5ILh zai=7zc)kiBSZ_#c4KvHhAl*=98b;0l|2i{WBuu}$M+Mg-=^8gXiyV`SpXJNBuCp^j z`U3La=w~If7_zHD*)27Z*0U?H$e5r-cZ3LME%!k!;YWX~>C__WmF@~W51c)nX&2`M z`gzLe?SA6nxt8nNeZ;%V7I#I4teQDSBKi|;e!l6$Y4rYHv#O&2Rt^Pw32P}fMww~0 zZcnxGwT%-{AEh#0$8b}RdmTtu)F1v8sRAyRetKJh78ZLPXrqKOl$CKg*Cg?os|8=g zc32&YB6FX))o;_v$!ju8PFxROd(2Tz(?a6Q9a+! zQj|u9Td!}8Oy>MlKlg=azRO_D#qr?;Z4h;W-K@X=nL(RfR|5>>7c3UJu5X7uv5p zdv(jP3`L4a=nc|{HdHcU=_g`Kz8A~cY>N}>vfW}-tIhcsUi4H01h3vv$DyDk5L=pv zUk?dvl*SJOANad?gh*J@oUsJxPaL z=~G}Bx5GR7JUyLHH@a>}hG?2Y?G~V?b(S1=2;7i6t3mb0c_qsS`DbPU@PW6WF287g zQyH3Tc|FzPbH@JI^$1xB?r(hqMf?;<0T_;0np5uXKKdZXF}WiSU7MccVeg@5rogY{ zJ*AxG!SU|prM|81M-^S@5|^IS=*7VpRe_RK0Zs!ZGpp8>3afNQ-@&czlCyc?4Bm5HoWoa{l2lr8~3QXb~pi&8Ps zhEpVHZu)!n;9C-qT0gRnStWxaNk8; z+~Xz#(M6RwDe6=<;qsW5&>rH^2t28Q`0K zIB=h3XceyD`t}8g!oU};H^$hK3MVfw9oiGEhKp7a_>$_Dpu?+krs54sVYI-;j8v#A zbMIw1F{Gr(-0DaNshUZ?W}kk|^OZjjzoPp0oj0DOeAYRwL!hJK04eThU4d*6_h<3l zBOiU?ZRfL03r|e=Jgb5J<1>`sc_31fY7ZYe`UdDu@HY?q96RfeBp#vA_M>iTraZ)o z*=L{E^~tM!ILh7zgF?QPStC!(5L(=sb8wzp%0X_sykR0Jc-^djyk2tAiH&iIgQt!O z>nRs`Xoa#5ZY*}kj!6K=Q%6yI7zD^Uy1qmQVpfP6#sEF&-x=&pDRwuA=IcxFU$Kt> z(^ud{pBa!UJRgg#vP;?gI7+vwr>YiwXBs`3*Uc~?8JuYnw-5 z>2K-uUpSNPA7}oT&g{>2bRvXm&SPkq_#c@0Gb$tEDp=5B(T|Nzg&@I!Ei2Ph0sdmT7+L(N^!XgT$!i*O>+UGUnXF8S` zRW}XcHboN{Ha;fKjCU=$QEFevDj^IzMFox{W56uYv)6KLaNpGBDo7K0-jEvtpZAf_ z3l;i#_gW(lm8(&e6pFDUyLK-#J51$>xYWL?>Md*(;MhHk3RyzXGwL=9>fpNdjkFmrvEn#U+Dvuv;Un}%aWL)NSC9yA_z&S6HTPxLmK_i`h{ly;%{R!ApS#E9`wo@ zbO%CrXw7JEs%K|psN`U0W^H2sBQ7dlz7Q#Z7jy!{~x`9#CMZ9N^aMY7cxM_1)Rea6Sl8()45xZ6jkE|%F55$7n<(>qo zkJnMxLP8vfOY1F}-G&r{*|p0GqqMtsEC#irT2>HW>KEg?wv3J-!K&o>cDCb;3oUaj zeV-e$v61*PX=*Fcx(e6m&2`gph~#!N+Wgyb9f-3`tV1VnLwEoH6EP3WV(O6Zf#JrE8#-k8ca4F0SB3>dP z-0+-%+h^RYfte(7eN7d8Jg^~>jc=ny&YuZpV31JR*^r-yTX|KClLWY9AB$^fG zadU)!$mEV%p%iRvL2IG_i2UAI=OjcXsp)Wvv`yWR4k@G3w^ z^#mdJp%EBDPX#m5Y$9{Ij!8Gdn|IG_w>4kkR~L<*kT*-M4scjttJ1vT z3r+Sm+#R!NrhHb=G3q;U(T@@S!3mf@B;GH95PWJRJbdQdwq77u{!(y#L_`dDV(E>h zQQsJedwIJ5?gsj}|1N1n?dzWGL;0YI002Ds7awe(1a~m9Q!;XJ_`!6hd*>{S`exQU z%Kg@3LO^~g%JFvC&IYt`&j(gxs`R^DbcHx`l_a_rv1twgs}LhL*4Z_?S7k=T7%_Vr zSNg7QRpXYG!S>hl;g1!9#s`&q`xD^)3)1T;r=_d4{mL>q?IwrR44xgzn=Ox}aMvLX zFCTt$EJ+XX9)d&O+=<}f7Rmk`N7;Ir&Ib%xA)e@T{L<%p0+lOuB}CiBINAn|PXh-S zV)ck*X_6EAK)WS{^vao9%6@g9LnDu+Ws4TjXgcVLX+k1mM>kZ>5Ob(xnrwsK7NWWa zGouC*Y>A{5Y9r@0jDvC~#mCxn`U*WKHKi<-&6Fxs6P~4m=xCTU&0{o#*@>2wTTx6B zUIBeGIV}_?;j~`Ym^&!p<7;xjusrePPw{z{{EkcT1F)UZ zkBN~i`=5nasOJC?u{tsx%)qWigjUgB_q1FCgP>Zt1b%MpblR4{HS1>_-k|LHmS!_v z3nPJxWOsQl?#)bHS0H48~wu8s0Zw1>VHy~H{1#GV|Os%Yup zwzKLz;^Q%B3ncQuk(y=GlW{<9V~uF$@>vlG$0X^>unh}#ad5Y+ciD9R#7&M?LT1wo z*F(ZwL-`;hMlk|(y`1`(3?T>+xV6MGNc;T|(WdXt_1!BPd6nR}$c!P|d`mh+TL)54 zexkYB!C7@u%8mRB$K!+XS8^8fyV8InRJvrFPyV(OEM>!lFE$Y}Wg9U#z68r@o|PfG zx~CE`b%A!!(AA=)B07U$7yJ5m_PiP;ROIbaV_l_>7@GB}5GeaU?(OQFH->ZwWjwrz zN`5GToEC?2NUVT+v7+%GdI4#cl3#+L%wva&JnTO+PtV5YAL9L>+`qQ;xIy`CX6(T8 zA8jLk$Jf{*2?hZd55mK(-`DO$vY9bx@*c_NB<8oqiM^-8ulQ7)!kv>dyjsyPZ{;y5 zB!VwW-kqaqh;2!)rG~3t@6u5BfG`&2{UM7G`I*m(YF1XEdf0O+J0C&~c>2!Nf=ua9 zCk$eL%0#$lDze0)>3IneD(m!oVvX}cWZb>S7AU-a3L>sAy@|pWY-b^0xqed(0S~Pm z3&hE9Wo6A(K^KWvT{A!y)p-6rdKc>8Z2boAX)C+xsruLs<-yWJo8j&T*2luV2EzRQ zX2Be6jPF!2Da6`qDzr=gPx3p+(jN|F=&fU37;5JRZ3j=qGW;vk!oV^>)#LBqqxko;`uFiSZ&fJB{1xD@ZQp+z{y4@$1L99j z;QNMu?V7DTuX}k9tv`N) z^0S(~k8;0k`GsuBMa+GK`vt-;gaN1;fZm7w|CS5)P48>TU#4FOf12J?lD}1! z_d)+EHhw`u6Tl;A8FOEH+&BMg#`v>&*yBH$|B*iwWDub7003Y@Kb6psk|6(a_CGC9 BGW`Gm literal 0 HcmV?d00001 From fd14da167510ea1e980189bb8597982622250e43 Mon Sep 17 00:00:00 2001 From: Mark Baker Date: Mon, 3 May 2021 08:39:42 +0200 Subject: [PATCH 23/57] Ods defined names unit tests (#2054) * Defined names/formulae in ODS are prefixed by $$ when used in a formula; so we need to strip this out to fully convert them to an Excel formula * Test for ODS Writer for DefinedNames --- phpstan-baseline.neon | 10 ----- src/PhpSpreadsheet/Reader/Ods.php | 8 ++-- src/PhpSpreadsheet/Reader/Ods/BaseReader.php | 4 +- .../Reader/Ods/DefinedNames.php | 1 + .../Reader/Ods/DefinedNamesTest.php | 35 ++++++++++++++++++ .../Writer/Ods/DefinedNamesTest.php | 33 +++++++++++++++++ tests/data/Reader/Ods/DefinedNames.ods | Bin 0 -> 3140 bytes 7 files changed, 77 insertions(+), 14 deletions(-) create mode 100644 tests/PhpSpreadsheetTests/Reader/Ods/DefinedNamesTest.php create mode 100644 tests/PhpSpreadsheetTests/Writer/Ods/DefinedNamesTest.php create mode 100644 tests/data/Reader/Ods/DefinedNames.ods diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 89f50e3b..af995b7b 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -2810,16 +2810,6 @@ parameters: count: 1 path: src/PhpSpreadsheet/Reader/Ods.php - - - message: "#^Parameter \\#3 \\$subject of function preg_replace expects array\\|string, string\\|null given\\.$#" - count: 3 - path: src/PhpSpreadsheet/Reader/Ods.php - - - - message: "#^Parameter \\#3 \\$formula of static method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Calculation\\:\\:translateSeparator\\(\\) expects string, string\\|null given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Ods.php - - message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Ods\\\\PageSettings\\:\\:\\$officeNs has no typehint specified\\.$#" count: 1 diff --git a/src/PhpSpreadsheet/Reader/Ods.php b/src/PhpSpreadsheet/Reader/Ods.php index d4163cf7..26151cdc 100644 --- a/src/PhpSpreadsheet/Reader/Ods.php +++ b/src/PhpSpreadsheet/Reader/Ods.php @@ -783,11 +783,13 @@ class Ods extends BaseReader // Cell range reference in another sheet $value = preg_replace('/\[\$?([^\.]+)\.([^\.]+):\.([^\.]+)\]/miu', '$1!$2:$3', $value); // Cell reference in another sheet - $value = preg_replace('/\[\$?([^\.]+)\.([^\.]+)\]/miu', '$1!$2', $value); + $value = preg_replace('/\[\$?([^\.]+)\.([^\.]+)\]/miu', '$1!$2', $value ?? ''); // Cell range reference - $value = preg_replace('/\[\.([^\.]+):\.([^\.]+)\]/miu', '$1:$2', $value); + $value = preg_replace('/\[\.([^\.]+):\.([^\.]+)\]/miu', '$1:$2', $value ?? ''); // Simple cell reference - $value = preg_replace('/\[\.([^\.]+)\]/miu', '$1', $value); + $value = preg_replace('/\[\.([^\.]+)\]/miu', '$1', $value ?? ''); + // Convert references to defined names/formulae + $value = str_replace('$$', '', $value ?? ''); $value = Calculation::translateSeparator(';', ',', $value, $inBraces); } diff --git a/src/PhpSpreadsheet/Reader/Ods/BaseReader.php b/src/PhpSpreadsheet/Reader/Ods/BaseReader.php index 82d41710..17e2d4d5 100644 --- a/src/PhpSpreadsheet/Reader/Ods/BaseReader.php +++ b/src/PhpSpreadsheet/Reader/Ods/BaseReader.php @@ -62,8 +62,10 @@ abstract class BaseReader $value = preg_replace('/\[\.([^\.]+):\.([^\.]+)\]/miu', '$1:$2', $value ?? ''); // Simple cell reference $value = preg_replace('/\[\.([^\.]+)\]/miu', '$1', $value ?? ''); + // Convert references to defined names/formulae + $value = str_replace('$$', '', $value ?? ''); - $value = Calculation::translateSeparator(';', ',', $value ?? '', $inBraces); + $value = Calculation::translateSeparator(';', ',', $value, $inBraces); } } diff --git a/src/PhpSpreadsheet/Reader/Ods/DefinedNames.php b/src/PhpSpreadsheet/Reader/Ods/DefinedNames.php index 79f5c027..6810a3c7 100644 --- a/src/PhpSpreadsheet/Reader/Ods/DefinedNames.php +++ b/src/PhpSpreadsheet/Reader/Ods/DefinedNames.php @@ -44,6 +44,7 @@ class DefinedNames extends BaseReader $expression = $definedNameElement->getAttributeNS($this->tableNs, 'expression'); $baseAddress = $this->convertToExcelAddressValue($baseAddress); + $expression = substr($expression, strpos($expression, ':=') + 1); $expression = $this->convertToExcelFormulaValue($expression); $this->addDefinedName($baseAddress, $definedName, $expression); diff --git a/tests/PhpSpreadsheetTests/Reader/Ods/DefinedNamesTest.php b/tests/PhpSpreadsheetTests/Reader/Ods/DefinedNamesTest.php new file mode 100644 index 00000000..760421ce --- /dev/null +++ b/tests/PhpSpreadsheetTests/Reader/Ods/DefinedNamesTest.php @@ -0,0 +1,35 @@ +spreadsheet = $reader->load($filename); + } + + public function testDefinedNames(): void + { + $worksheet = $this->spreadsheet->getActiveSheet(); + + $firstDefinedNameValue = $worksheet->getCell('First')->getValue(); + $secondDefinedNameValue = $worksheet->getCell('Second')->getValue(); + $calculatedFormulaValue = $worksheet->getCell('B2')->getCalculatedValue(); + + self::assertSame(3, $firstDefinedNameValue); + self::assertSame(4, $secondDefinedNameValue); + self::assertSame(12, $calculatedFormulaValue); + } +} diff --git a/tests/PhpSpreadsheetTests/Writer/Ods/DefinedNamesTest.php b/tests/PhpSpreadsheetTests/Writer/Ods/DefinedNamesTest.php new file mode 100644 index 00000000..1b2e30b2 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Writer/Ods/DefinedNamesTest.php @@ -0,0 +1,33 @@ +getActiveSheet(); + + $dataSet = [ + [7, 'x', 5], + ['=', '=FORMULA'], + ]; + $worksheet->fromArray($dataSet, null, 'A1'); + + $spreadsheet->addDefinedName(new NamedRange('FIRST', $worksheet, '$A$1')); + $spreadsheet->addDefinedName(new NamedRange('SECOND', $worksheet, '$C$1')); + $spreadsheet->addDefinedName(new NamedFormula('FORMULA', $worksheet, '=FIRST*SECOND')); + + $reloaded = $this->writeAndReload($spreadsheet, 'Ods'); + + self::assertSame(7, $reloaded->getActiveSheet()->getCell('FIRST')->getValue()); + self::assertSame(5, $reloaded->getActiveSheet()->getCell('SECOND')->getValue()); + self::assertSame(35, $reloaded->getActiveSheet()->getCell('B2')->getCalculatedValue()); + } +} diff --git a/tests/data/Reader/Ods/DefinedNames.ods b/tests/data/Reader/Ods/DefinedNames.ods new file mode 100644 index 0000000000000000000000000000000000000000..4246435bad747543ec4d48887dde8f38cd3c6010 GIT binary patch literal 3140 zcmZ`+2UJsA7L7pYU7A#>QUvMJi*#wB1(0S4fzU!H(yJh4KoJB5rAseTLRXL`O#>n& z5Rgv`9VQeD$OQk)a>oDX-gVczuk3T*JL~Ly_X8OclTZTwY`lQ`UW&;Qgp-RwP8i-62CWiO3Y{$CNmiIV~=|6f`Wf3wW?JyFQj%xX1mz>)neAdHW$!s}4q(CF3Xm zSvlNE9nV*q;netC34`H&-*u z&rdR*F!hyyiL-{v9NDJTg6QMXc33DcRg~d};Hs8JhqYsGub|@r!#(iwN3fe4mGBMh zQ_N<#JfEBBwMw4)Rlu?IsIBkU$%5i$EeEEukM79!c@Zr0r=1<|rDkE;O2NliNspzP zJLVuwEXdewr;=}W_?0@StNAU9-6<&#p1K+{Kq+}vpxTcb08!taExvBGx^ zoEecU3^7cSJV6$28c-jSxrUM6#Bs2k7AS7_1z77nevy)GYcapbEVanQr<3N-L(d4* z*xF}cfoE;IQCQWcHhigBmtLQkNZagOqP9d*K<1dDLXq_o-61?I4Ryg^tf4q&OXn7=G7|qkP3C6C+ClJ2-(KZHfK#jR8xX zOngN0mfWN3(Td)O(aO)LPK$IqFtLSoLU2}j!+SBrs#9EE&#ge4wV{zrvd=Z&xry5%Ssp|MkAgC0Fa>v00<8NpgxGe3mRY0(z*w+ zEdP_1Y_X<5xX!B%W0H{+zBzzuAS)cjqmjaIYm>~RFN!*Ut!17mIA;L!N^#7~;q-T1 z$PLk|Q|Iyli8Kh?&g;m~x4C2qH{$B*KCJLQbJqeE+f%+P;>Y2Z*9Li}8iXr3 z&4utNQi#69{hl{6ou;@+w7A|DILXr7VZ##5?yG1DBj)40O*T-0-(fw)TBIW%QNQO< z$f+o+*+I9+4{khCUS4v4iHods^GuPXOK0`pT#8#OdH5g{3$oyy>Gh=TWL#{3yBR$m zIEZ}Xw4F^=F{}_09-fLtOfvNUbD`Fq8413g9uo(%8XV$FimQQdiI-O}NPFObYTuYg zJnmQ(Y;bH0Id5_`XxQu)+gkI*DZNRTFN~+Ewl_y}lE>w3jbZe#H=t)wgI3D-Mc0O_ zYAWkR97mdDD#RjHcKULI=D%*$k>5maHOV#TV*LN8-F0a9;YTV8pw6q$V?)(zoAqh) z5}zlC&c(b>ozWdBG6kWZeKEgcQ!+nB7{nw$Z+pr@(ZxS71>TGBkJRE>CDvhduU>D}Hvs zLyp;7yv$Q5@kBuQ?$`;ljI#1;?u<`oy&v8L=C{;WEKphuo!O?gN>if~eOQs7M}%#~ z)np^^&Fl}njI5wKbTz3SJ&-WLt|1*bE+~*#L(W4|cmmC+RTrox12ng!AoJ!(Ac$|RS+IAa!fgGun2B|)^)aPIzL^wlrx6u7(jinOT!2Q z$M06_sZ!aImyi}prCC$=AR}5TVk*P+{HYl&bquf`+ea^#^qd7|_XNnwp7LIcDlK(& z@_S;@f4KT2mJw=3Ld#Z;G8K?zZ&O{hwTxvjY3UB*5E$?9%XcVn%y;s7|1NJ42|ym& z+|pb3CC8|>IX|1rIJ)^|eEzA&A3#PnRA1t1uc(cbIx{DIvESXnQH48p71s6`3B?pz zGBb525ucbGD0Fry!4#Z1Cw>bBj_Uc_5?FX?@U(zGAzm=6xg*=0LGqH@R zD=CGy6jxn2wxLrTgMUpNmG zK9iKc5^N-Q3Z4!BF?cY0BmS(GZr`u8Qfra6z3^5mq7{*dgZsP7zVro^44rw0n0&7P zus>h%oTOLxULr;!BzsKb(k@tmp>ArG1FOP2pY`}hN^8QS1fr(c!30L71(0PXz2#_^ zLw?jbe%15p@aur2M&3-Rs<*Z){Q*+vK?_@pPl_^>=c>S6sOMxd{^4fE*RXWJb?5Ky zYEfTzHb&M>dEd=-I=QO6-s#HBM;Nq0msQ+O+ZYB8;ebRP^`hREeLJq)p|kGAQ<}dM ztHfctgBgNIF%ba(%!H${j-{r!fr;)7ZwSKO6&CPQwyGa$dJs!d>zv$Q=((pJ+$bjW zVmmbU81QyVmefw3-}v3Unh7K$*i64r*??s)A|y&7yV^t{pmOEteJA`rlZVma#?j#S zx&2CJDp0&VMVI5FBS?58hP6PJH)W3UlR;n0)D}$0I_9!+Z0xo`E9RjyjiDyvNx-55 zc9~1LG*!zf*Sh)2eA9}G?8D+|@Zum48ml=qh1c{{1U`&RQ#F%yx-^QcTaxG3R^v-X zrE}O$rFw;KI!(s9y_#NE*U%E&(EB0NT+kh-ANGB|hV2I7L;tGVXJAp3?j-;qo{);4 z4h^}J8scDv5D^jAWyQCh=`kKMAsTx=10 zAz1$PT#M3T9$!aWi>vI{go*g8EnS@o(s#rhi{FtU1g57nVj_ErjYFcc=ddOecPEnD zi7xvBWAtir4!mrb%YzeaceC6QtUue*k8<}IG0E#SE`;U;nxDgvGm6&9L=lO;F7ce6 zWzel-ZAv2c_RJUP97fqS6?Yh|+r5vnet7g{_!C&kViCME0Bn{#yz%XQ*XVcZFv-vN z>TAO--rD*gm%8d6pkaC1>q{dB(Lx{WQ|{o=s2_@%=^qCL9M)EDd(m6#0{i7yXi2Gd zX~!?CM{`h2N83{dQ>k#!4Xc(L#|YAaUK-r?Yv?v;FBL=CNVb1t=aPtd{zH7=*?2X~ zXf7}Hh(^b_ZiDPF|K%ycpZ=NaOGM&7T`D2@fWP|Ti>trdf7~(1@B;P!9q%uMC1Lu@ z|Nf5qeNX;{+a$c-f9=q}1AphoFJLyIM*fy9zlZp}u6~6mq9FVS{JY9PhCtGbJ3vCe MN~l8G3+n**7m@~ literal 0 HcmV?d00001 From 346bad1b1de6a47a1be19536f88947289ec7feca Mon Sep 17 00:00:00 2001 From: oleibman Date: Mon, 3 May 2021 09:31:01 -0700 Subject: [PATCH 24/57] Fix for Issue 2042 (SUM Partially Broken) (#2045) As issue #2042 documents, SUM behaves differently with invalid strings depending on whether they come from a cell or are used as literals in the formula. SUM is not alone in this regard; COUNTA is another function within this behavior, and the solution to this one is modeled on COUNTA. New tests are added for SUM, and the resulting tests are duplicated to confirm correct behavior for both cells and literals. Samples 16 (CSV), 17 (Html), and 21 (PDF) were adversely affected by this problem. 17 and 21 were immediately fixed, but 16 had another problem - Excel was not interpreting the UTF8 currency symbols correctly, even though the file was saved with a BOM. After some experimenting, it appears that the `sep=;` line generated by setExcelCompatibility(true) causes Excel to mis-handle the file. This seems like a bug - there is apparently no way to save a UTF-8 CSV with non-ASCII characters which specifies a non-standard separator which Excel will open correctly. I don't know if this is a recent change or if it is just the case that nobody noticed this problem till now. So, I changed Sample 16 to use setUseBom rather than setExcelCompatibility, which solved its problem. I then added new tests for setExcelCompatibility, with documentation of this problem. --- samples/Basic/16_Csv.php | 3 +- .../Calculation/MathTrig/Sum.php | 8 ++- .../Functions/MathTrig/SumTest.php | 18 +++++++ .../Writer/Csv/CsvExcelCompatibilityTest.php | 49 +++++++++++++++++++ tests/data/Calculation/MathTrig/SUM.php | 6 ++- .../data/Calculation/MathTrig/SUMLITERALS.php | 12 +++++ 6 files changed, 92 insertions(+), 4 deletions(-) create mode 100644 tests/PhpSpreadsheetTests/Writer/Csv/CsvExcelCompatibilityTest.php create mode 100644 tests/data/Calculation/MathTrig/SUMLITERALS.php diff --git a/samples/Basic/16_Csv.php b/samples/Basic/16_Csv.php index 15bbf0d4..137f6469 100644 --- a/samples/Basic/16_Csv.php +++ b/samples/Basic/16_Csv.php @@ -38,7 +38,8 @@ $helper->write($spreadsheetFromCSV, __FILE__, ['Xlsx']); $filenameCSV = $helper->getFilename(__FILE__, 'csv'); /** @var \PhpOffice\PhpSpreadsheet\Writer\Csv $writerCSV */ $writerCSV = new CsvWriter($spreadsheetFromCSV); -$writerCSV->setExcelCompatibility(true); +//$writerCSV->setExcelCompatibility(true); +$writerCSV->setUseBom(true); // because of non-ASCII chars $callStartTime = microtime(true); $writerCSV->save($filenameCSV); diff --git a/src/PhpSpreadsheet/Calculation/MathTrig/Sum.php b/src/PhpSpreadsheet/Calculation/MathTrig/Sum.php index ab3a9a07..8a3223b1 100644 --- a/src/PhpSpreadsheet/Calculation/MathTrig/Sum.php +++ b/src/PhpSpreadsheet/Calculation/MathTrig/Sum.php @@ -51,16 +51,20 @@ class Sum { $returnValue = 0; // Loop through the arguments - foreach (Functions::flattenArray($args) as $arg) { + $aArgs = Functions::flattenArrayIndexed($args); + foreach ($aArgs as $k => $arg) { // Is it a numeric value? if (is_numeric($arg) || empty($arg)) { if (is_string($arg)) { $arg = (int) $arg; } $returnValue += $arg; + } elseif (is_bool($arg)) { + $returnValue += (int) $arg; } elseif (Functions::isError($arg)) { return $arg; - } else { + // ignore non-numerics from cell, but fail as literals (except null) + } elseif ($arg !== null && !Functions::isCellValue($k)) { return Functions::VALUE(); } } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/SumTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/SumTest.php index a9ea7f29..b85f0c90 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/SumTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/SumTest.php @@ -26,4 +26,22 @@ class SumTest extends AllSetupTeardown { return require 'tests/data/Calculation/MathTrig/SUM.php'; } + + /** + * @dataProvider providerSUMLiterals + * + * @param mixed $expectedResult + */ + public function testSUMLiterals($expectedResult, string $args): void + { + $sheet = $this->sheet; + $sheet->getCell('B1')->setValue("=SUM($args)"); + $result = $sheet->getCell('B1')->getCalculatedValue(); + self::assertEqualsWithDelta($expectedResult, $result, 1E-12); + } + + public function providerSUMLiterals(): array + { + return require 'tests/data/Calculation/MathTrig/SUMLITERALS.php'; + } } diff --git a/tests/PhpSpreadsheetTests/Writer/Csv/CsvExcelCompatibilityTest.php b/tests/PhpSpreadsheetTests/Writer/Csv/CsvExcelCompatibilityTest.php new file mode 100644 index 00000000..9b7d16aa --- /dev/null +++ b/tests/PhpSpreadsheetTests/Writer/Csv/CsvExcelCompatibilityTest.php @@ -0,0 +1,49 @@ +getActiveSheet(); + $sheet->setCellValue('A1', '1'); + $sheet->setCellValue('B1', '2'); + $sheet->setCellValue('C1', '3'); + $sheet->setCellValue('A2', '4'); + $sheet->setCellValue('B2', '5'); + $sheet->setCellValue('C2', '6'); + $writer = new CsvWriter($spreadsheet); + $writer->setExcelCompatibility(true); + self::assertSame('', $writer->getOutputEncoding()); + $filename = File::temporaryFilename(); + $writer->save($filename); + $reader = new CsvReader(); + $spreadsheet2 = $reader->load($filename); + $contents = file_get_contents($filename); + unlink($filename); + self::assertEquals(1, $spreadsheet2->getActiveSheet()->getCell('A1')->getValue()); + self::assertEquals(6, $spreadsheet2->getActiveSheet()->getCell('C2')->getValue()); + self::assertStringContainsString(CsvReader::UTF8_BOM, $contents); + self::assertStringContainsString("\r\n", $contents); + self::assertStringContainsString('sep=;', $contents); + self::assertStringContainsString('"1";"2";"3"', $contents); + self::assertStringContainsString('"4";"5";"6"', $contents); + } +} diff --git a/tests/data/Calculation/MathTrig/SUM.php b/tests/data/Calculation/MathTrig/SUM.php index a8219076..0c54613e 100644 --- a/tests/data/Calculation/MathTrig/SUM.php +++ b/tests/data/Calculation/MathTrig/SUM.php @@ -4,5 +4,9 @@ return [ [50, 5, 15, 30], [52, 5, 15, 30, 2], [53.1, 5.7, 15, 30, 2.4], - ['#VALUE!', 5.7, 'X', 30, 2.4], // error here conflicts with SUMIF + [52.1, 5.7, '14', 30, 2.4], + [38.1, 5.7, 'X', 30, 2.4], // error if entered in formula, but not in cell + [38.1, 5.7, null, 30, 2.4], + [38.1, 5.7, false, 30, 2.4], + [39.1, 5.7, true, 30, 2.4], ]; diff --git a/tests/data/Calculation/MathTrig/SUMLITERALS.php b/tests/data/Calculation/MathTrig/SUMLITERALS.php new file mode 100644 index 00000000..fd184ebd --- /dev/null +++ b/tests/data/Calculation/MathTrig/SUMLITERALS.php @@ -0,0 +1,12 @@ + Date: Mon, 3 May 2021 18:52:15 +0200 Subject: [PATCH 25/57] #984 fix Scrutinizer warnings --- .../Reader/Xlsx/ConditionalStyles.php | 12 +--------- src/PhpSpreadsheet/Style/Conditional.php | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php b/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php index 80361371..dcd7ad12 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php +++ b/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php @@ -2,7 +2,6 @@ namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx; -use PhpOffice\PhpSpreadsheet\Style\Color; use PhpOffice\PhpSpreadsheet\Style\Conditional; use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\ConditionalDataBar; use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\ConditionalFormattingRuleExtension; @@ -39,16 +38,7 @@ class ConditionalStyles $conditionals = []; foreach ($xmlSheet->conditionalFormatting as $conditional) { foreach ($conditional->cfRule as $cfRule) { - if ( - ((string) $cfRule['type'] == Conditional::CONDITION_NONE - || (string) $cfRule['type'] == Conditional::CONDITION_CELLIS - || (string) $cfRule['type'] == Conditional::CONDITION_CONTAINSTEXT - || (string) $cfRule['type'] == Conditional::CONDITION_CONTAINSBLANKS - || (string) $cfRule['type'] == Conditional::CONDITION_NOTCONTAINSBLANKS - || (string) $cfRule['type'] == Conditional::CONDITION_NOTCONTAINSTEXT - || (string) $cfRule['type'] == Conditional::CONDITION_EXPRESSION) - && isset($this->dxfs[(int) ($cfRule['dxfId'])]) - ) { + if (Conditional::isValidConditionType((string) $cfRule['type']) && isset($this->dxfs[(int) ($cfRule['dxfId'])])) { $conditionals[(string) $conditional['sqref']][(int) ($cfRule['priority'])] = $cfRule; } elseif ((string) $cfRule['type'] == Conditional::CONDITION_DATABAR) { $conditionals[(string) $conditional['sqref']][(int) ($cfRule['priority'])] = $cfRule; diff --git a/src/PhpSpreadsheet/Style/Conditional.php b/src/PhpSpreadsheet/Style/Conditional.php index 2fd415af..83520722 100644 --- a/src/PhpSpreadsheet/Style/Conditional.php +++ b/src/PhpSpreadsheet/Style/Conditional.php @@ -17,6 +17,17 @@ class Conditional implements IComparable const CONDITION_DATABAR = 'dataBar'; const CONDITION_NOTCONTAINSTEXT = 'notContainsText'; + private const CONDITION_TYPES = [ + self::CONDITION_CELLIS, + self::CONDITION_CONTAINSBLANKS, + self::CONDITION_CONTAINSTEXT, + self::CONDITION_DATABAR, + self::CONDITION_EXPRESSION, + self::CONDITION_NONE, + self::CONDITION_NOTCONTAINSBLANKS, + self::CONDITION_NOTCONTAINSTEXT, + ]; + // Operator types const OPERATOR_NONE = ''; const OPERATOR_BEGINSWITH = 'beginsWith'; @@ -301,4 +312,16 @@ class Conditional implements IComparable } } } + + /** + * Verify if param is valid condition type + * + * @param string $type + * + * @return bool + */ + public static function isValidConditionType(string $type): bool + { + return in_array($type, self::CONDITION_TYPES); + } } From 2b268c8dd9afb401792da1f0f9e12e1d48aa4555 Mon Sep 17 00:00:00 2001 From: Mark Baker Date: Mon, 3 May 2021 22:21:57 +0200 Subject: [PATCH 26/57] Fix row visibility in XLS Writer (#2058) * Fix reversed visibility in Xls Writer --- docs/references/features-cross-reference.md | 10 ++--- src/PhpSpreadsheet/Reader/Gnumeric.php | 4 +- src/PhpSpreadsheet/Writer/Xls/Worksheet.php | 10 ++++- .../Writer/Xls/RowVisibilityTest.php | 37 +++++++++++++++++++ 4 files changed, 53 insertions(+), 8 deletions(-) create mode 100644 tests/PhpSpreadsheetTests/Writer/Xls/RowVisibilityTest.php diff --git a/docs/references/features-cross-reference.md b/docs/references/features-cross-reference.md index 9dcf8d91..05b8c117 100644 --- a/docs/references/features-cross-reference.md +++ b/docs/references/features-cross-reference.md @@ -1220,13 +1220,13 @@ Merged Cells - - - - + ✔ ✔ - + ✔ + ✔ + N/A + N/A diff --git a/src/PhpSpreadsheet/Reader/Gnumeric.php b/src/PhpSpreadsheet/Reader/Gnumeric.php index d3cdf1b0..80ca46cb 100644 --- a/src/PhpSpreadsheet/Reader/Gnumeric.php +++ b/src/PhpSpreadsheet/Reader/Gnumeric.php @@ -270,7 +270,9 @@ class Gnumeric extends BaseReader $commentAttributes = $comment->attributes(); // Only comment objects are handled at the moment if ($commentAttributes->Text) { - $this->spreadsheet->getActiveSheet()->getComment((string) $commentAttributes->ObjectBound)->setAuthor((string) $commentAttributes->Author)->setText($this->parseRichText((string) $commentAttributes->Text)); + $this->spreadsheet->getActiveSheet()->getComment((string) $commentAttributes->ObjectBound) + ->setAuthor((string) $commentAttributes->Author) + ->setText($this->parseRichText((string) $commentAttributes->Text)); } } } diff --git a/src/PhpSpreadsheet/Writer/Xls/Worksheet.php b/src/PhpSpreadsheet/Writer/Xls/Worksheet.php index 84844d3d..c3d5d8f4 100644 --- a/src/PhpSpreadsheet/Writer/Xls/Worksheet.php +++ b/src/PhpSpreadsheet/Writer/Xls/Worksheet.php @@ -390,7 +390,13 @@ class Worksheet extends BIFFwriter // Row dimensions foreach ($phpSheet->getRowDimensions() as $rowDimension) { $xfIndex = $rowDimension->getXfIndex() + 15; // there are 15 cellXfs - $this->writeRow($rowDimension->getRowIndex() - 1, (int) $rowDimension->getRowHeight(), $xfIndex, $rowDimension->getVisible(), $rowDimension->getOutlineLevel()); + $this->writeRow( + $rowDimension->getRowIndex() - 1, + (int) $rowDimension->getRowHeight(), + $xfIndex, + !$rowDimension->getVisible(), + $rowDimension->getOutlineLevel() + ); } // Write Cells @@ -1181,7 +1187,7 @@ class Worksheet extends BIFFwriter // collapsed. The zero height flag, 0x20, is used to collapse a row. $grbit |= $level; - if ($hidden) { + if ($hidden === true) { $grbit |= 0x0030; } if ($height !== null) { diff --git a/tests/PhpSpreadsheetTests/Writer/Xls/RowVisibilityTest.php b/tests/PhpSpreadsheetTests/Writer/Xls/RowVisibilityTest.php new file mode 100644 index 00000000..055ee1b9 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Writer/Xls/RowVisibilityTest.php @@ -0,0 +1,37 @@ +getActiveSheet(); + foreach ($visibleRows as $row => $visibility) { + $worksheet->setCellValue("A{$row}", $row); + $worksheet->getRowDimension($row)->setVisible($visibility); + } + + $reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xls'); + $reloadedWorksheet = $reloadedSpreadsheet->getActiveSheet(); + foreach ($visibleRows as $row => $visibility) { + self::assertSame($visibility, $reloadedWorksheet->getRowDimension($row)->getVisible()); + } + } + + public function dataProviderReoVisibility(): array + { + return [ + [ + [1 => true, 2 => false, 3 => false, 4 => true, 5 => true, 6 => false], + ], + ]; + } +} From 5873116488e07016500df9ece9c30975ab290a87 Mon Sep 17 00:00:00 2001 From: Mark Baker Date: Mon, 3 May 2021 23:46:40 +0200 Subject: [PATCH 27/57] Unit testing for row/column/worksheet visibility for Xls and Xlsx files (#2059) * Unit testing for row/column/worksheet visibility for Xls and Xlsx files * Include very hidden in worksheet visibility tests --- src/PhpSpreadsheet/Writer/Xls/Worksheet.php | 4 +- .../Writer/Xls/RowVisibilityTest.php | 37 ------- .../Writer/Xls/VisibilityTest.php | 99 +++++++++++++++++++ .../Writer/Xlsx/VisibilityTest.php | 99 +++++++++++++++++++ 4 files changed, 201 insertions(+), 38 deletions(-) delete mode 100644 tests/PhpSpreadsheetTests/Writer/Xls/RowVisibilityTest.php create mode 100644 tests/PhpSpreadsheetTests/Writer/Xls/VisibilityTest.php create mode 100644 tests/PhpSpreadsheetTests/Writer/Xlsx/VisibilityTest.php diff --git a/src/PhpSpreadsheet/Writer/Xls/Worksheet.php b/src/PhpSpreadsheet/Writer/Xls/Worksheet.php index c3d5d8f4..894ce03a 100644 --- a/src/PhpSpreadsheet/Writer/Xls/Worksheet.php +++ b/src/PhpSpreadsheet/Writer/Xls/Worksheet.php @@ -2166,7 +2166,9 @@ class Worksheet extends BIFFwriter */ public function insertBitmap($row, $col, $bitmap, $x = 0, $y = 0, $scale_x = 1, $scale_y = 1): void { - $bitmap_array = (is_resource($bitmap) || $bitmap instanceof GdImage ? $this->processBitmapGd($bitmap) : $this->processBitmap($bitmap)); + $bitmap_array = (is_resource($bitmap) || $bitmap instanceof GdImage + ? $this->processBitmapGd($bitmap) + : $this->processBitmap($bitmap)); [$width, $height, $size, $data] = $bitmap_array; // Scale the frame of the image. diff --git a/tests/PhpSpreadsheetTests/Writer/Xls/RowVisibilityTest.php b/tests/PhpSpreadsheetTests/Writer/Xls/RowVisibilityTest.php deleted file mode 100644 index 055ee1b9..00000000 --- a/tests/PhpSpreadsheetTests/Writer/Xls/RowVisibilityTest.php +++ /dev/null @@ -1,37 +0,0 @@ -getActiveSheet(); - foreach ($visibleRows as $row => $visibility) { - $worksheet->setCellValue("A{$row}", $row); - $worksheet->getRowDimension($row)->setVisible($visibility); - } - - $reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xls'); - $reloadedWorksheet = $reloadedSpreadsheet->getActiveSheet(); - foreach ($visibleRows as $row => $visibility) { - self::assertSame($visibility, $reloadedWorksheet->getRowDimension($row)->getVisible()); - } - } - - public function dataProviderReoVisibility(): array - { - return [ - [ - [1 => true, 2 => false, 3 => false, 4 => true, 5 => true, 6 => false], - ], - ]; - } -} diff --git a/tests/PhpSpreadsheetTests/Writer/Xls/VisibilityTest.php b/tests/PhpSpreadsheetTests/Writer/Xls/VisibilityTest.php new file mode 100644 index 00000000..7de39328 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Writer/Xls/VisibilityTest.php @@ -0,0 +1,99 @@ +getActiveSheet(); + foreach ($visibleRows as $row => $visibility) { + $worksheet->setCellValue("A{$row}", $row); + $worksheet->getRowDimension($row)->setVisible($visibility); + } + + $reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xls'); + $reloadedWorksheet = $reloadedSpreadsheet->getActiveSheet(); + foreach ($visibleRows as $row => $visibility) { + self::assertSame($visibility, $reloadedWorksheet->getRowDimension($row)->getVisible()); + } + } + + public function dataProviderRowVisibility(): array + { + return [ + [ + [1 => true, 2 => false, 3 => false, 4 => true, 5 => true, 6 => false], + ], + ]; + } + + /** + * @dataProvider dataProviderColumnVisibility + */ + public function testColumnVisibility(array $visibleColumns): void + { + $spreadsheet = new Spreadsheet(); + $worksheet = $spreadsheet->getActiveSheet(); + foreach ($visibleColumns as $column => $visibility) { + $worksheet->setCellValue("{$column}1", $column); + $worksheet->getColumnDimension($column)->setVisible($visibility); + } + + $reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xls'); + $reloadedWorksheet = $reloadedSpreadsheet->getActiveSheet(); + foreach ($visibleColumns as $column => $visibility) { + self::assertSame($visibility, $reloadedWorksheet->getColumnDimension($column)->getVisible()); + } + } + + public function dataProviderColumnVisibility(): array + { + return [ + [ + ['A' => true, 'B' => false, 'C' => false, 'D' => true, 'E' => true, 'F' => false], + ], + ]; + } + + /** + * @dataProvider dataProviderSheetVisibility + */ + public function testSheetVisibility(array $visibleSheets): void + { + $spreadsheet = new Spreadsheet(); + $spreadsheet->removeSheetByIndex(0); + foreach ($visibleSheets as $sheetName => $visibility) { + $worksheet = $spreadsheet->addSheet(new Worksheet($spreadsheet, $sheetName)); + $worksheet->setCellValue('A1', $sheetName); + $worksheet->setSheetState($visibility); + } + + $reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xls'); + foreach ($visibleSheets as $sheetName => $visibility) { + $reloadedWorksheet = $reloadedSpreadsheet->getSheetByName($sheetName) ?? new Worksheet(); + self::assertSame($visibility, $reloadedWorksheet->getSheetState()); + } + } + + public function dataProviderSheetVisibility(): array + { + return [ + [ + [ + 'Worksheet 1' => Worksheet::SHEETSTATE_HIDDEN, + 'Worksheet 2' => Worksheet::SHEETSTATE_VERYHIDDEN, + 'Worksheet 3' => Worksheet::SHEETSTATE_VISIBLE, + ], + ], + ]; + } +} diff --git a/tests/PhpSpreadsheetTests/Writer/Xlsx/VisibilityTest.php b/tests/PhpSpreadsheetTests/Writer/Xlsx/VisibilityTest.php new file mode 100644 index 00000000..7e1ca967 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Writer/Xlsx/VisibilityTest.php @@ -0,0 +1,99 @@ +getActiveSheet(); + foreach ($visibleRows as $row => $visibility) { + $worksheet->setCellValue("A{$row}", $row); + $worksheet->getRowDimension($row)->setVisible($visibility); + } + + $reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx'); + $reloadedWorksheet = $reloadedSpreadsheet->getActiveSheet(); + foreach ($visibleRows as $row => $visibility) { + self::assertSame($visibility, $reloadedWorksheet->getRowDimension($row)->getVisible()); + } + } + + public function dataProviderRowVisibility(): array + { + return [ + [ + [1 => false, 2 => false, 3 => true, 4 => false, 5 => true, 6 => false], + ], + ]; + } + + /** + * @dataProvider dataProviderColumnVisibility + */ + public function testColumnVisibility(array $visibleColumns): void + { + $spreadsheet = new Spreadsheet(); + $worksheet = $spreadsheet->getActiveSheet(); + foreach ($visibleColumns as $column => $visibility) { + $worksheet->setCellValue("{$column}1", $column); + $worksheet->getColumnDimension($column)->setVisible($visibility); + } + + $reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx'); + $reloadedWorksheet = $reloadedSpreadsheet->getActiveSheet(); + foreach ($visibleColumns as $column => $visibility) { + self::assertSame($visibility, $reloadedWorksheet->getColumnDimension($column)->getVisible()); + } + } + + public function dataProviderColumnVisibility(): array + { + return [ + [ + ['A' => false, 'B' => false, 'C' => true, 'D' => false, 'E' => true, 'F' => false], + ], + ]; + } + + /** + * @dataProvider dataProviderSheetVisibility + */ + public function testSheetVisibility(array $visibleSheets): void + { + $spreadsheet = new Spreadsheet(); + $spreadsheet->removeSheetByIndex(0); + foreach ($visibleSheets as $sheetName => $visibility) { + $worksheet = $spreadsheet->addSheet(new Worksheet($spreadsheet, $sheetName)); + $worksheet->setCellValue('A1', $sheetName); + $worksheet->setSheetState($visibility); + } + + $reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx'); + foreach ($visibleSheets as $sheetName => $visibility) { + $reloadedWorksheet = $reloadedSpreadsheet->getSheetByName($sheetName) ?? new Worksheet(); + self::assertSame($visibility, $reloadedWorksheet->getSheetState()); + } + } + + public function dataProviderSheetVisibility(): array + { + return [ + [ + [ + 'Worksheet 1' => Worksheet::SHEETSTATE_HIDDEN, + 'Worksheet 2' => Worksheet::SHEETSTATE_VERYHIDDEN, + 'Worksheet 3' => Worksheet::SHEETSTATE_VISIBLE, + ], + ], + ]; + } +} From d0f76fd3ba5095ec1971741fb4f43a5034731ec4 Mon Sep 17 00:00:00 2001 From: xandros15 Date: Tue, 4 May 2021 11:41:55 +0200 Subject: [PATCH 28/57] #984 change to force typing in condition --- src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php index 83238d7a..2c8e6f58 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php @@ -635,10 +635,10 @@ class Worksheet extends WriterPart self::writeAttributeif( $objWriter, ( - $conditional->getConditionType() == Conditional::CONDITION_CELLIS - || $conditional->getConditionType() == Conditional::CONDITION_CONTAINSTEXT - || $conditional->getConditionType() == Conditional::CONDITION_NOTCONTAINSTEXT - ) && $conditional->getOperatorType() != Conditional::OPERATOR_NONE, + $conditional->getConditionType() === Conditional::CONDITION_CELLIS + || $conditional->getConditionType() === Conditional::CONDITION_CONTAINSTEXT + || $conditional->getConditionType() === Conditional::CONDITION_NOTCONTAINSTEXT + ) && $conditional->getOperatorType() !== Conditional::OPERATOR_NONE, 'operator', $conditional->getOperatorType() ); @@ -646,8 +646,8 @@ class Worksheet extends WriterPart self::writeAttributeIf($objWriter, $conditional->getStopIfTrue(), 'stopIfTrue', '1'); if ( - $conditional->getConditionType() == Conditional::CONDITION_CONTAINSTEXT - || $conditional->getConditionType() == Conditional::CONDITION_NOTCONTAINSTEXT + $conditional->getConditionType() === Conditional::CONDITION_CONTAINSTEXT + || $conditional->getConditionType() === Conditional::CONDITION_NOTCONTAINSTEXT ) { self::writeTextCondElements($objWriter, $conditional, $cellCoordinate); } else { From 4be93667228ce81c4f7083d7443490d9316bf2c2 Mon Sep 17 00:00:00 2001 From: oleibman Date: Tue, 4 May 2021 12:41:11 -0700 Subject: [PATCH 29/57] Gnumeric Better Namespace Handling (#2022) * Gnumeric Better Namespace Handling There have been a number of issues concerning the handling of legitimate but unexpected namespace prefixes in Xlsx spreadsheets created by software other than Excel and PhpSpreadsheet/PhpExcel.I have studied them, but, till now, have not had a good idea on how to act on them. A recent comment https://github.com/PHPOffice/PhpSpreadsheet/issues/860#issuecomment-824926224 in issue #860 by @IMSoP has triggered an idea about how to proceed. Although the issues exclusively concern Xlsx format, I am starting out by dealing with Gnumeric. It is simpler and smaller than Xlsx, and, more important, already has a test for an unexpected prefix, since, at some point, it changed its generic prefix from gmr to gnm. I added support and a test for that some time ago, but almost certainly not in the best possible manner. The code as changed for this PR seems simpler and less kludgey, both for that exceptional case as well as for normal handling. My hope is that this change can be a template for similar Reader changes for Xml, Ods, and, especially, Xlsx. All grandfathered Phpstan issues with Gnumeric are fixed and eliminated from baseline as part of this change. * Namespace Handling using XMLReader Adopt a suggestion from @IMSoP affecting listWorkSheetInfo, which uses XMLReader rather than SimpleXML for its processing. * Update GnumericLoadTest.php PR #2024 was pushed last night, causing a Phpstan problem with this member. * Update Gnumeric.php Suggestions from Mark Baker - strict equality test, more descriptive variable names. --- phpstan-baseline.neon | 65 ------ src/PhpSpreadsheet/Reader/Gnumeric.php | 212 +++++++++++------- .../Reader/Gnumeric/PageSetup.php | 11 +- .../Reader/Gnumeric/Properties.php | 99 ++++---- .../Reader/Gnumeric/GnumericLoadTest.php | 3 +- 5 files changed, 187 insertions(+), 203 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index af995b7b..0a8369ee 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -2545,71 +2545,6 @@ parameters: count: 1 path: src/PhpSpreadsheet/Reader/Csv/Delimiter.php - - - message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Gnumeric\\:\\:\\$referenceHelper has no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Gnumeric.php - - - - message: "#^Parameter \\#1 \\$fp of function fread expects resource, resource\\|false given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Gnumeric.php - - - - message: "#^Parameter \\#1 \\$fp of function fclose expects resource, resource\\|false given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Gnumeric.php - - - - message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Gnumeric\\:\\:\\$mappings has no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Gnumeric.php - - - - message: "#^Offset 'No' does not exist on SimpleXMLElement\\|null\\.$#" - count: 2 - path: src/PhpSpreadsheet/Reader/Gnumeric.php - - - - message: "#^Offset 'Unit' does not exist on SimpleXMLElement\\|null\\.$#" - count: 2 - path: src/PhpSpreadsheet/Reader/Gnumeric.php - - - - message: "#^Offset 'DefaultSizePts' does not exist on SimpleXMLElement\\|null\\.$#" - count: 2 - path: src/PhpSpreadsheet/Reader/Gnumeric.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Gnumeric\\:\\:parseBorderAttributes\\(\\) has no return typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Gnumeric.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Gnumeric\\:\\:parseBorderAttributes\\(\\) has parameter \\$borderAttributes with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Gnumeric.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Gnumeric\\:\\:parseRichText\\(\\) has no return typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Gnumeric.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Gnumeric\\:\\:parseRichText\\(\\) has parameter \\$is with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Gnumeric.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Gnumeric\\:\\:parseGnumericColour\\(\\) has no return typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Gnumeric.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Gnumeric\\:\\:parseGnumericColour\\(\\) has parameter \\$gnmColour with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Reader/Gnumeric.php - - message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Html\\:\\:\\$rowspan has no typehint specified\\.$#" count: 1 diff --git a/src/PhpSpreadsheet/Reader/Gnumeric.php b/src/PhpSpreadsheet/Reader/Gnumeric.php index 80ca46cb..d7358293 100644 --- a/src/PhpSpreadsheet/Reader/Gnumeric.php +++ b/src/PhpSpreadsheet/Reader/Gnumeric.php @@ -25,7 +25,19 @@ use XMLReader; class Gnumeric extends BaseReader { - private const UOM_CONVERSION_POINTS_TO_CENTIMETERS = 0.03527777778; + const NAMESPACE_GNM = 'http://www.gnumeric.org/v10.dtd'; // gmr in old sheets + + const NAMESPACE_XSI = 'http://www.w3.org/2001/XMLSchema-instance'; + + const NAMESPACE_OFFICE = 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'; + + const NAMESPACE_XLINK = 'http://www.w3.org/1999/xlink'; + + const NAMESPACE_DC = 'http://purl.org/dc/elements/1.1/'; + + const NAMESPACE_META = 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0'; + + const NAMESPACE_OOO = 'http://openoffice.org/2004/office'; /** * Shared Expressions. @@ -41,16 +53,9 @@ class Gnumeric extends BaseReader */ private $spreadsheet; + /** @var ReferenceHelper */ private $referenceHelper; - /** - * Namespace shared across all functions. - * It is 'gnm', except for really old sheets which use 'gmr'. - * - * @var string - */ - private $gnm = 'gnm'; - /** * Create a new Gnumeric. */ @@ -77,16 +82,20 @@ class Gnumeric extends BaseReader if (function_exists('gzread')) { // Read signature data (first 3 bytes) $fh = fopen($pFilename, 'rb'); - $data = fread($fh, 2); - fclose($fh); + if ($fh !== false) { + $data = fread($fh, 2); + fclose($fh); + } } - return $data == chr(0x1F) . chr(0x8B); + return $data === chr(0x1F) . chr(0x8B); } - private static function matchXml(string $name, string $field): bool + private static function matchXml(XMLReader $xml, string $expectedLocalName): bool { - return 1 === preg_match("/^(gnm|gmr):$field$/", $name); + return $xml->namespaceURI === self::NAMESPACE_GNM + && $xml->localName === $expectedLocalName + && $xml->nodeType === XMLReader::ELEMENT; } /** @@ -106,10 +115,10 @@ class Gnumeric extends BaseReader $worksheetNames = []; while ($xml->read()) { - if (self::matchXml($xml->name, 'SheetName') && $xml->nodeType == XMLReader::ELEMENT) { + if (self::matchXml($xml, 'SheetName')) { $xml->read(); // Move onto the value node $worksheetNames[] = (string) $xml->value; - } elseif (self::matchXml($xml->name, 'Sheets')) { + } elseif (self::matchXml($xml, 'Sheets')) { // break out of the loop once we've got our sheet names rather than parse the entire file break; } @@ -135,7 +144,7 @@ class Gnumeric extends BaseReader $worksheetInfo = []; while ($xml->read()) { - if (self::matchXml($xml->name, 'Sheet') && $xml->nodeType == XMLReader::ELEMENT) { + if (self::matchXml($xml, 'Sheet')) { $tmpInfo = [ 'worksheetName' => '', 'lastColumnLetter' => 'A', @@ -145,20 +154,18 @@ class Gnumeric extends BaseReader ]; while ($xml->read()) { - if ($xml->nodeType == XMLReader::ELEMENT) { - if (self::matchXml($xml->name, 'Name')) { - $xml->read(); // Move onto the value node - $tmpInfo['worksheetName'] = (string) $xml->value; - } elseif (self::matchXml($xml->name, 'MaxCol')) { - $xml->read(); // Move onto the value node - $tmpInfo['lastColumnIndex'] = (int) $xml->value; - $tmpInfo['totalColumns'] = (int) $xml->value + 1; - } elseif (self::matchXml($xml->name, 'MaxRow')) { - $xml->read(); // Move onto the value node - $tmpInfo['totalRows'] = (int) $xml->value + 1; + if (self::matchXml($xml, 'Name')) { + $xml->read(); // Move onto the value node + $tmpInfo['worksheetName'] = (string) $xml->value; + } elseif (self::matchXml($xml, 'MaxCol')) { + $xml->read(); // Move onto the value node + $tmpInfo['lastColumnIndex'] = (int) $xml->value; + $tmpInfo['totalColumns'] = (int) $xml->value + 1; + } elseif (self::matchXml($xml, 'MaxRow')) { + $xml->read(); // Move onto the value node + $tmpInfo['totalRows'] = (int) $xml->value + 1; - break; - } + break; } } $tmpInfo['lastColumnLetter'] = Coordinate::stringFromColumnIndex($tmpInfo['lastColumnIndex'] + 1); @@ -188,6 +195,7 @@ class Gnumeric extends BaseReader return $data; } + /** @var array */ private static $mappings = [ 'borderStyle' => [ '0' => Border::BORDER_NONE, @@ -266,7 +274,7 @@ class Gnumeric extends BaseReader private function processComments(SimpleXMLElement $sheet): void { if ((!$this->readDataOnly) && (isset($sheet->Objects))) { - foreach ($sheet->Objects->children($this->gnm, true) as $key => $comment) { + foreach ($sheet->Objects->children(self::NAMESPACE_GNM) as $key => $comment) { $commentAttributes = $comment->attributes(); // Only comment objects are handled at the moment if ($commentAttributes->Text) { @@ -278,6 +286,14 @@ class Gnumeric extends BaseReader } } + /** + * @param mixed $value + */ + private static function testSimpleXml($value): SimpleXMLElement + { + return ($value instanceof SimpleXMLElement) ? $value : new SimpleXMLElement(''); + } + /** * Loads Spreadsheet from file. * @@ -306,12 +322,10 @@ class Gnumeric extends BaseReader $gFileData = $this->gzfileGetContents($pFilename); $xml2 = simplexml_load_string($this->securityScanner->scan($gFileData), 'SimpleXMLElement', Settings::getLibXmlLoaderOptions()); - $xml = ($xml2 !== false) ? $xml2 : new SimpleXMLElement(''); - $namespacesMeta = $xml->getNamespaces(true); - $this->gnm = array_key_exists('gmr', $namespacesMeta) ? 'gmr' : 'gnm'; + $xml = self::testSimpleXml($xml2); - $gnmXML = $xml->children($namespacesMeta[$this->gnm]); - (new Properties($this->spreadsheet))->readProperties($xml, $gnmXML, $namespacesMeta); + $gnmXML = $xml->children(self::NAMESPACE_GNM); + (new Properties($this->spreadsheet))->readProperties($xml, $gnmXML); $worksheetID = 0; foreach ($gnmXML->Sheets->Sheet as $sheet) { @@ -331,7 +345,7 @@ class Gnumeric extends BaseReader $this->spreadsheet->getActiveSheet()->setTitle($worksheetName, false, false); if (!$this->readDataOnly) { - (new PageSetup($this->spreadsheet, $this->gnm)) + (new PageSetup($this->spreadsheet)) ->printInformation($sheet) ->sheetMargins($sheet); } @@ -384,7 +398,7 @@ class Gnumeric extends BaseReader if (array_key_exists($vtype, self::$mappings['dataType'])) { $type = self::$mappings['dataType'][$vtype]; } - if ($vtype == '20') { // Boolean + if ($vtype === '20') { // Boolean $cell = $cell == 'TRUE'; } } @@ -512,84 +526,122 @@ class Gnumeric extends BaseReader } } - private function processColumnLoop(int $c, int $maxCol, SimpleXMLElement $columnOverride, float $defaultWidth): int + private function setColumnWidth(int $whichColumn, float $defaultWidth): void { - $columnAttributes = $columnOverride->attributes(); + $columnDimension = $this->spreadsheet->getActiveSheet()->getColumnDimension(Coordinate::stringFromColumnIndex($whichColumn + 1)); + if ($columnDimension !== null) { + $columnDimension->setWidth($defaultWidth); + } + } + + private function setColumnInvisible(int $whichColumn): void + { + $columnDimension = $this->spreadsheet->getActiveSheet()->getColumnDimension(Coordinate::stringFromColumnIndex($whichColumn + 1)); + if ($columnDimension !== null) { + $columnDimension->setVisible(false); + } + } + + private function processColumnLoop(int $whichColumn, int $maxCol, SimpleXMLElement $columnOverride, float $defaultWidth): int + { + $columnAttributes = self::testSimpleXml($columnOverride->attributes()); $column = $columnAttributes['No']; $columnWidth = ((float) $columnAttributes['Unit']) / 5.4; $hidden = (isset($columnAttributes['Hidden'])) && ((string) $columnAttributes['Hidden'] == '1'); $columnCount = (int) ($columnAttributes['Count'] ?? 1); - while ($c < $column) { - $this->spreadsheet->getActiveSheet()->getColumnDimension(Coordinate::stringFromColumnIndex($c + 1))->setWidth($defaultWidth); - ++$c; + while ($whichColumn < $column) { + $this->setColumnWidth($whichColumn, $defaultWidth); + ++$whichColumn; } - while (($c < ($column + $columnCount)) && ($c <= $maxCol)) { - $this->spreadsheet->getActiveSheet()->getColumnDimension(Coordinate::stringFromColumnIndex($c + 1))->setWidth($columnWidth); + while (($whichColumn < ($column + $columnCount)) && ($whichColumn <= $maxCol)) { + $this->setColumnWidth($whichColumn, $columnWidth); if ($hidden) { - $this->spreadsheet->getActiveSheet()->getColumnDimension(Coordinate::stringFromColumnIndex($c + 1))->setVisible(false); + $this->setColumnInvisible($whichColumn); } - ++$c; + ++$whichColumn; } - return $c; + return $whichColumn; } private function processColumnWidths(SimpleXMLElement $sheet, int $maxCol): void { if ((!$this->readDataOnly) && (isset($sheet->Cols))) { // Column Widths + $defaultWidth = 0; $columnAttributes = $sheet->Cols->attributes(); - $defaultWidth = $columnAttributes['DefaultSizePts'] / 5.4; - $c = 0; - foreach ($sheet->Cols->ColInfo as $columnOverride) { - $c = $this->processColumnLoop($c, $maxCol, $columnOverride, $defaultWidth); + if ($columnAttributes !== null) { + $defaultWidth = $columnAttributes['DefaultSizePts'] / 5.4; } - while ($c <= $maxCol) { - $this->spreadsheet->getActiveSheet()->getColumnDimension(Coordinate::stringFromColumnIndex($c + 1))->setWidth($defaultWidth); - ++$c; + $whichColumn = 0; + foreach ($sheet->Cols->ColInfo as $columnOverride) { + $whichColumn = $this->processColumnLoop($whichColumn, $maxCol, $columnOverride, $defaultWidth); + } + while ($whichColumn <= $maxCol) { + $this->setColumnWidth($whichColumn, $defaultWidth); + ++$whichColumn; } } } - private function processRowLoop(int $r, int $maxRow, SimpleXMLElement $rowOverride, float $defaultHeight): int + private function setRowHeight(int $whichRow, float $defaultHeight): void { - $rowAttributes = $rowOverride->attributes(); + $rowDimension = $this->spreadsheet->getActiveSheet()->getRowDimension($whichRow); + if ($rowDimension !== null) { + $rowDimension->setRowHeight($defaultHeight); + } + } + + private function setRowInvisible(int $whichRow): void + { + $rowDimension = $this->spreadsheet->getActiveSheet()->getRowDimension($whichRow); + if ($rowDimension !== null) { + $rowDimension->setVisible(false); + } + } + + private function processRowLoop(int $whichRow, int $maxRow, SimpleXMLElement $rowOverride, float $defaultHeight): int + { + $rowAttributes = self::testSimpleXml($rowOverride->attributes()); $row = $rowAttributes['No']; $rowHeight = (float) $rowAttributes['Unit']; $hidden = (isset($rowAttributes['Hidden'])) && ((string) $rowAttributes['Hidden'] == '1'); $rowCount = (int) ($rowAttributes['Count'] ?? 1); - while ($r < $row) { - ++$r; - $this->spreadsheet->getActiveSheet()->getRowDimension($r)->setRowHeight($defaultHeight); + while ($whichRow < $row) { + ++$whichRow; + $this->setRowHeight($whichRow, $defaultHeight); } - while (($r < ($row + $rowCount)) && ($r < $maxRow)) { - ++$r; - $this->spreadsheet->getActiveSheet()->getRowDimension($r)->setRowHeight($rowHeight); + while (($whichRow < ($row + $rowCount)) && ($whichRow < $maxRow)) { + ++$whichRow; + $this->setRowHeight($whichRow, $rowHeight); if ($hidden) { - $this->spreadsheet->getActiveSheet()->getRowDimension($r)->setVisible(false); + $this->setRowInvisible($whichRow); } } - return $r; + return $whichRow; } private function processRowHeights(SimpleXMLElement $sheet, int $maxRow): void { if ((!$this->readDataOnly) && (isset($sheet->Rows))) { // Row Heights + $defaultHeight = 0; $rowAttributes = $sheet->Rows->attributes(); - $defaultHeight = (float) $rowAttributes['DefaultSizePts']; - $r = 0; + if ($rowAttributes !== null) { + $defaultHeight = (float) $rowAttributes['DefaultSizePts']; + } + $whichRow = 0; foreach ($sheet->Rows->RowInfo as $rowOverride) { - $r = $this->processRowLoop($r, $maxRow, $rowOverride, $defaultHeight); + $whichRow = $this->processRowLoop($whichRow, $maxRow, $rowOverride, $defaultHeight); } // never executed, I can't figure out any circumstances // under which it would be executed, and, even if // such exist, I'm not convinced this is needed. - //while ($r < $maxRow) { - // ++$r; - // $this->spreadsheet->getActiveSheet()->getRowDimension($r)->setRowHeight($defaultHeight); + //while ($whichRow < $maxRow) { + // ++$whichRow; + // $this->spreadsheet->getActiveSheet()->getRowDimension($whichRow)->setRowHeight($defaultHeight); //} } } @@ -641,19 +693,21 @@ class Gnumeric extends BaseReader } } - private static function parseBorderAttributes($borderAttributes) + private static function parseBorderAttributes(?SimpleXMLElement $borderAttributes): array { $styleArray = []; - if (isset($borderAttributes['Color'])) { - $styleArray['color']['rgb'] = self::parseGnumericColour($borderAttributes['Color']); - } + if ($borderAttributes !== null) { + if (isset($borderAttributes['Color'])) { + $styleArray['color']['rgb'] = self::parseGnumericColour($borderAttributes['Color']); + } - self::addStyle($styleArray, 'borderStyle', $borderAttributes['Style']); + self::addStyle($styleArray, 'borderStyle', $borderAttributes['Style']); + } return $styleArray; } - private function parseRichText($is) + private function parseRichText(string $is): RichText { $value = new RichText(); $value->createText($is); @@ -661,7 +715,7 @@ class Gnumeric extends BaseReader return $value; } - private static function parseGnumericColour($gnmColour) + private static function parseGnumericColour(string $gnmColour): string { [$gnmR, $gnmG, $gnmB] = explode(':', $gnmColour); $gnmR = substr(str_pad($gnmR, 4, '0', STR_PAD_RIGHT), 0, 2); @@ -679,7 +733,7 @@ class Gnumeric extends BaseReader $shade = (string) $styleAttributes['Shade']; if (($RGB != '000000') || ($shade != '0')) { $RGB2 = self::parseGnumericColour($styleAttributes['PatternColor']); - if ($shade == '1') { + if ($shade === '1') { $styleArray['fill']['startColor']['rgb'] = $RGB; $styleArray['fill']['endColor']['rgb'] = $RGB2; } else { diff --git a/src/PhpSpreadsheet/Reader/Gnumeric/PageSetup.php b/src/PhpSpreadsheet/Reader/Gnumeric/PageSetup.php index 0fe73005..accc2716 100644 --- a/src/PhpSpreadsheet/Reader/Gnumeric/PageSetup.php +++ b/src/PhpSpreadsheet/Reader/Gnumeric/PageSetup.php @@ -2,6 +2,7 @@ namespace PhpOffice\PhpSpreadsheet\Reader\Gnumeric; +use PhpOffice\PhpSpreadsheet\Reader\Gnumeric; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Worksheet\PageMargins; use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup as WorksheetPageSetup; @@ -14,15 +15,9 @@ class PageSetup */ private $spreadsheet; - /** - * @var string - */ - private $gnm; - - public function __construct(Spreadsheet $spreadsheet, string $gnm) + public function __construct(Spreadsheet $spreadsheet) { $this->spreadsheet = $spreadsheet; - $this->gnm = $gnm; } public function printInformation(SimpleXMLElement $sheet): self @@ -68,7 +63,7 @@ class PageSetup private function buildMarginSet(SimpleXMLElement $sheet, array $marginSet): array { - foreach ($sheet->PrintInformation->Margins->children($this->gnm, true) as $key => $margin) { + foreach ($sheet->PrintInformation->Margins->children(Gnumeric::NAMESPACE_GNM) as $key => $margin) { $marginAttributes = $margin->attributes(); $marginSize = ($marginAttributes['Points']) ?? 72; // Default is 72pt // Convert value in points to inches diff --git a/src/PhpSpreadsheet/Reader/Gnumeric/Properties.php b/src/PhpSpreadsheet/Reader/Gnumeric/Properties.php index 16d9c2e0..c466a859 100644 --- a/src/PhpSpreadsheet/Reader/Gnumeric/Properties.php +++ b/src/PhpSpreadsheet/Reader/Gnumeric/Properties.php @@ -2,6 +2,7 @@ namespace PhpOffice\PhpSpreadsheet\Reader\Gnumeric; +use PhpOffice\PhpSpreadsheet\Reader\Gnumeric; use PhpOffice\PhpSpreadsheet\Spreadsheet; use SimpleXMLElement; @@ -91,74 +92,72 @@ class Properties } } - private function docPropertiesMeta(SimpleXMLElement $officePropertyMeta, array $namespacesMeta): void + private function docPropertiesMeta(SimpleXMLElement $officePropertyMeta): void { $docProps = $this->spreadsheet->getProperties(); foreach ($officePropertyMeta as $propertyName => $propertyValue) { - if ($propertyValue === null) { - continue; - } + if ($propertyValue !== null) { + $attributes = $propertyValue->attributes(Gnumeric::NAMESPACE_META); + $propertyValue = trim((string) $propertyValue); + switch ($propertyName) { + case 'keyword': + $docProps->setKeywords($propertyValue); - $attributes = $propertyValue->attributes($namespacesMeta['meta']); - $propertyValue = trim((string) $propertyValue); - switch ($propertyName) { - case 'keyword': - $docProps->setKeywords($propertyValue); + break; + case 'initial-creator': + $docProps->setCreator($propertyValue); + $docProps->setLastModifiedBy($propertyValue); - break; - case 'initial-creator': - $docProps->setCreator($propertyValue); - $docProps->setLastModifiedBy($propertyValue); + break; + case 'creation-date': + $creationDate = strtotime($propertyValue); + $creationDate = $creationDate === false ? time() : $creationDate; + $docProps->setCreated($creationDate); + $docProps->setModified($creationDate); - break; - case 'creation-date': - $creationDate = strtotime($propertyValue); - $creationDate = $creationDate === false ? time() : $creationDate; - $docProps->setCreated($creationDate); - $docProps->setModified($creationDate); + break; + case 'user-defined': + [, $attrName] = explode(':', $attributes['name']); + $this->userDefinedProperties($attrName, $propertyValue); - break; - case 'user-defined': - [, $attrName] = explode(':', $attributes['name']); - switch ($attrName) { - case 'publisher': - $docProps->setCompany($propertyValue); - - break; - case 'category': - $docProps->setCategory($propertyValue); - - break; - case 'manager': - $docProps->setManager($propertyValue); - - break; - } - - break; + break; + } } } } - public function readProperties(SimpleXMLElement $xml, SimpleXMLElement $gnmXML, array $namespacesMeta): void + private function userDefinedProperties(string $attrName, string $propertyValue): void { - if (isset($namespacesMeta['office'])) { - $officeXML = $xml->children($namespacesMeta['office']); + $docProps = $this->spreadsheet->getProperties(); + switch ($attrName) { + case 'publisher': + $docProps->setCompany($propertyValue); + + break; + case 'category': + $docProps->setCategory($propertyValue); + + break; + case 'manager': + $docProps->setManager($propertyValue); + + break; + } + } + + public function readProperties(SimpleXMLElement $xml, SimpleXMLElement $gnmXML): void + { + $officeXML = $xml->children(Gnumeric::NAMESPACE_OFFICE); + if (!empty($officeXML)) { $officeDocXML = $officeXML->{'document-meta'}; $officeDocMetaXML = $officeDocXML->meta; foreach ($officeDocMetaXML as $officePropertyData) { - $officePropertyDC = []; - if (isset($namespacesMeta['dc'])) { - $officePropertyDC = $officePropertyData->children($namespacesMeta['dc']); - } + $officePropertyDC = $officePropertyData->children(Gnumeric::NAMESPACE_DC); $this->docPropertiesDC($officePropertyDC); - $officePropertyMeta = []; - if (isset($namespacesMeta['meta'])) { - $officePropertyMeta = $officePropertyData->children($namespacesMeta['meta']); - } - $this->docPropertiesMeta($officePropertyMeta, $namespacesMeta); + $officePropertyMeta = $officePropertyData->children(Gnumeric::NAMESPACE_META); + $this->docPropertiesMeta($officePropertyMeta); } } elseif (isset($gnmXML->Summary)) { $this->docPropertiesOld($gnmXML); diff --git a/tests/PhpSpreadsheetTests/Reader/Gnumeric/GnumericLoadTest.php b/tests/PhpSpreadsheetTests/Reader/Gnumeric/GnumericLoadTest.php index e24178e5..9544fc3a 100644 --- a/tests/PhpSpreadsheetTests/Reader/Gnumeric/GnumericLoadTest.php +++ b/tests/PhpSpreadsheetTests/Reader/Gnumeric/GnumericLoadTest.php @@ -115,7 +115,8 @@ class GnumericLoadTest extends TestCase self::assertEquals(Font::UNDERLINE_DOUBLE, $sheet->getCell('A24')->getStyle()->getFont()->getUnderline()); self::assertTrue($sheet->getCell('B23')->getStyle()->getFont()->getSubScript()); self::assertTrue($sheet->getCell('B24')->getStyle()->getFont()->getSuperScript()); - self::assertFalse($sheet->getRowDimension(30)->getVisible()); + $rowDimension = $sheet->getRowDimension(30); + self::assertFalse($rowDimension->getVisible()); } public function testLoadFilter(): void From 5ee4fbf090cb0aee04b172ff617da94d4455f7df Mon Sep 17 00:00:00 2001 From: Mark Baker Date: Tue, 4 May 2021 22:32:12 +0200 Subject: [PATCH 30/57] Implement basic autofilter ranges with Gnumeric Reader (#2057) * Load basic autofilter ranges with Gnumeric Reader * Handle null values passed to row height/column with/merged cells/autofilters --- CHANGELOG.md | 1 + docs/references/features-cross-reference.md | 2 +- src/PhpSpreadsheet/Reader/Gnumeric.php | 31 +++++++++++++----- .../Reader/Gnumeric/AutoFilterTest.php | 31 ++++++++++++++++++ .../Reader/Gnumeric/Autofilter_Basic.gnumeric | Bin 0 -> 3070 bytes 5 files changed, 56 insertions(+), 9 deletions(-) create mode 100644 tests/PhpSpreadsheetTests/Reader/Gnumeric/AutoFilterTest.php create mode 100644 tests/data/Reader/Gnumeric/Autofilter_Basic.gnumeric diff --git a/CHANGELOG.md b/CHANGELOG.md index 7940a6a2..4599f877 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). ### Added - Implemented basic AutoFiltering for Ods Reader and Writer [PR #2053](https://github.com/PHPOffice/PhpSpreadsheet/pull/2053) +- Implemented basic AutoFiltering for Gnumeric Reader [PR #2055](https://github.com/PHPOffice/PhpSpreadsheet/pull/2055) - Improved support for Row and Column ranges in formulae [Issue #1755](https://github.com/PHPOffice/PhpSpreadsheet/issues/1755) [PR #2028](https://github.com/PHPOffice/PhpSpreadsheet/pull/2028) - Implemented URLENCODE() Web Function - Implemented the CHITEST(), CHISQ.DIST() and CHISQ.INV() and equivalent Statistical functions, for both left- and right-tailed distributions. diff --git a/docs/references/features-cross-reference.md b/docs/references/features-cross-reference.md index 05b8c117..c836a99a 100644 --- a/docs/references/features-cross-reference.md +++ b/docs/references/features-cross-reference.md @@ -1314,7 +1314,7 @@ ● ● - + ● ● diff --git a/src/PhpSpreadsheet/Reader/Gnumeric.php b/src/PhpSpreadsheet/Reader/Gnumeric.php index d7358293..d66dbb88 100644 --- a/src/PhpSpreadsheet/Reader/Gnumeric.php +++ b/src/PhpSpreadsheet/Reader/Gnumeric.php @@ -482,6 +482,7 @@ class Gnumeric extends BaseReader $this->processColumnWidths($sheet, $maxCol); $this->processRowHeights($sheet, $maxRow); $this->processMergedCells($sheet); + $this->processAutofilter($sheet); ++$worksheetID; } @@ -514,10 +515,10 @@ class Gnumeric extends BaseReader } } - private function processMergedCells(SimpleXMLElement $sheet): void + private function processMergedCells(?SimpleXMLElement $sheet): void { // Handle Merged Cells in this worksheet - if (isset($sheet->MergedRegions)) { + if ($sheet !== null && isset($sheet->MergedRegions)) { foreach ($sheet->MergedRegions->Merge as $mergeCells) { if (strpos($mergeCells, ':') !== false) { $this->spreadsheet->getActiveSheet()->mergeCells($mergeCells); @@ -526,6 +527,20 @@ class Gnumeric extends BaseReader } } + private function processAutofilter(?SimpleXMLElement $sheet): void + { + if ($sheet !== null && isset($sheet->Filters)) { + foreach ($sheet->Filters->Filter as $autofilter) { + if ($autofilter !== null) { + $attributes = $autofilter->attributes(); + if (isset($attributes['Area'])) { + $this->spreadsheet->getActiveSheet()->setAutoFilter((string) $attributes['Area']); + } + } + } + } + } + private function setColumnWidth(int $whichColumn, float $defaultWidth): void { $columnDimension = $this->spreadsheet->getActiveSheet()->getColumnDimension(Coordinate::stringFromColumnIndex($whichColumn + 1)); @@ -564,9 +579,9 @@ class Gnumeric extends BaseReader return $whichColumn; } - private function processColumnWidths(SimpleXMLElement $sheet, int $maxCol): void + private function processColumnWidths(?SimpleXMLElement $sheet, int $maxCol): void { - if ((!$this->readDataOnly) && (isset($sheet->Cols))) { + if ((!$this->readDataOnly) && $sheet !== null && (isset($sheet->Cols))) { // Column Widths $defaultWidth = 0; $columnAttributes = $sheet->Cols->attributes(); @@ -622,9 +637,9 @@ class Gnumeric extends BaseReader return $whichRow; } - private function processRowHeights(SimpleXMLElement $sheet, int $maxRow): void + private function processRowHeights(?SimpleXMLElement $sheet, int $maxRow): void { - if ((!$this->readDataOnly) && (isset($sheet->Rows))) { + if ((!$this->readDataOnly) && $sheet !== null && (isset($sheet->Rows))) { // Row Heights $defaultHeight = 0; $rowAttributes = $sheet->Rows->attributes(); @@ -646,10 +661,10 @@ class Gnumeric extends BaseReader } } - private function processDefinedNames(SimpleXMLElement $gnmXML): void + private function processDefinedNames(?SimpleXMLElement $gnmXML): void { // Loop through definedNames (global named ranges) - if (isset($gnmXML->Names)) { + if ($gnmXML !== null && isset($gnmXML->Names)) { foreach ($gnmXML->Names->Name as $definedName) { $name = (string) $definedName->name; $value = (string) $definedName->value; diff --git a/tests/PhpSpreadsheetTests/Reader/Gnumeric/AutoFilterTest.php b/tests/PhpSpreadsheetTests/Reader/Gnumeric/AutoFilterTest.php new file mode 100644 index 00000000..18dde473 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Reader/Gnumeric/AutoFilterTest.php @@ -0,0 +1,31 @@ +spreadsheet = $reader->load($filename); + } + + public function testAutoFilterRange(): void + { + $worksheet = $this->spreadsheet->getActiveSheet(); + + $autoFilterRange = $worksheet->getAutoFilter()->getRange(); + + self::assertSame('A1:D57', $autoFilterRange); + } +} diff --git a/tests/data/Reader/Gnumeric/Autofilter_Basic.gnumeric b/tests/data/Reader/Gnumeric/Autofilter_Basic.gnumeric new file mode 100644 index 0000000000000000000000000000000000000000..0009f438d088ae7f587a4379a1dcb2cc6de2fc54 GIT binary patch literal 3070 zcmVWiwFP!000001MQsObDKIA$KU%`u-=({+QbHglXx57aT_P`Y<{inrrW-F z2q?C0F!F-jB=@i1BY_FV5jjMixqZ0JOb3J{j=qwPK007N{`RzttRFN<8IO-!_I{^j z(Kz5Ci|5C!FVoY#!`5&841>sN7=fT+tpTTliwVktN|GAAANl<~3n(-?fEg2uy(JZXJ}0wF$E|e|_qd<3R9TSr zM9||a8gGj9%DH>WLF98fV)4Ukv)Vp7I%=z{o2+3_$LVdxL{-vKhSfBg{ z3UjEGT*?{x&>c?4vG+ROUdNr52o?azJ`tRq!dzJ%;Kq~mn+`@B@P zbq^LZ!Tf0NWq|mJpfQY*C}RHlF)v-smQUi!Xe$!7=4-Jsl`MocEnfT4n1Wx2$ptfD z82fh-9SLbcdsx%gMn1KH_MH~A$c z-BMm@If`KkS^AYavW`sVz}h=D2)?pzI24uh9j5F}LhrwT<2r6Fm)VTk%6`X%;L9c@ zB6=@g@^|*%E|JKo(X7ZqP06B+WAAt0EOXBR#js^Iv!Nnl=3KxZQ%gLrz^krk5`Yi) z=Rh1U$C(>$SIYg6SHS``IHWtNW>~zHVGa&Kf0h2A@r>QicpN>?Qt=$oRG)dtW7s++ z;A8c9!o-KZ)LvFAdd)3>!eJ`_2n>SjbE(TSSfu8KCDJS2j7L|~+2HeZbUVyk zIf|h=9;D^c!}0ifJedt9vy;)}=3?+|c=A$5W|GdyWb;4`vFhZIiDNpK`~o}uM8JZh zmQHmtmTRL5KZGjXRfl=%&Wu%cS?7a@&Ew4Q^TEaF>}qy4yc&)N7qFI=uH9zWK3z|z z*Ox8pYvQk_^hv1uO%~9Qhbcd0+4-1@oS@W2=xcQ(2_(*PT=3w5hHAkE41i>n3MfH0 zi!tC|0DGfT_}44`1CQbh7zpv!x-H9rfMBNr%xJ5r#Q=g?lDQ%qZu*Hw+1*FNk64C( zU%EaVmAbEa$J#F)D}k`o zxfu?6e|7P07k7U3E+(3}Tj}Dz=I<}sxupx+1Sz*})(O4$*O8d8Kj@9ZFMFT3rDb5V zrQ6A96X2)qd=nbcKksiso+0r86{{^hqF`1XTFYM58R**cc+dC&}xdP!G2W= z0yQ`tyVIb8SER5T6yH@r&MF$hgz-hAmbg1DIXJ>J#4%OE(JSFJC~mHVqgTSkG{iMk z!qqF`HYjecgsWBJu#0I3F;#--l^_j@n=3)|N_d!tcy&r(Cc0FerpE&qwN*rjFI6^6LXsX1anV&fPX-XXGl|cE4BU6ug zWacN1v`T!U(VVT94eHp{z&%7E(d&HK1gdHP6XPRnJzU~tuwSQ3r2~qnY;(A*p-YU1 zu=RM^KnFcp5(m`^W3;qu*b?I(Y&~2y&_U0Y1VvMJEm)#<3pHffsJxL5da5L-)xxgD zN>n>)xDw?b^kCUQ2R&1|sI|g2hDy7JDpCGHkChE{&=V!W>0peMb`4Xa{DU4U8|a|t zNe?$KYJsv*4e#|2HAwmICgpb}=;D^lT9Ry3!+Tvr4NIaV&=X`A*8pRLv}+g=P^cgb-Y`KT1;$I!+ZUK9ux60=qZulp3@jpqGK3$bo7*nmqAa8 zxXrXgu&ew4pRLQ#{R9tmo9=@~z~_6$Rjo}M1D zDtLN!B)B#hv!iDiiS+dBh*rV$%}39e9X-Q9q^D;`v8YgeT5fZQnr7qs=hvR;Xm`Wu%>*KeN19tSAJ%D$|6CN>Tg=JR7-wbN0#ZGcn8&A z$W{twx8%_eD0az18f8{@OyN?|NxC_3!cuvfP_*?e#zW MA2gjpLg7*X076OytN;K2 literal 0 HcmV?d00001 From 115e39ae0cc2b2bafe559635456375ae6db81b6b Mon Sep 17 00:00:00 2001 From: Mark Baker Date: Fri, 7 May 2021 11:20:38 +0200 Subject: [PATCH 31/57] Issue 2066, highlighting more validation needed for LookupRef Functions (#2069) * Issue 2066, highlighting more validation needed for LookupRef Functions * Additional test cases --- phpstan-baseline.neon | 5 -- .../LookupRef/LookupRefValidations.php | 39 ++++++++++++ .../Calculation/LookupRef/Matrix.php | 22 ++++--- tests/data/Calculation/LookupRef/INDEX.php | 63 +++++++++++++++---- 4 files changed, 103 insertions(+), 26 deletions(-) create mode 100644 src/PhpSpreadsheet/Calculation/LookupRef/LookupRefValidations.php diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 0a8369ee..037ee997 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -975,11 +975,6 @@ parameters: count: 1 path: src/PhpSpreadsheet/Calculation/LookupRef/LookupBase.php - - - message: "#^Parameter \\#3 \\$rowNum of static method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\Matrix\\:\\:extractRowValue\\(\\) expects int, float\\|int\\<0, max\\>\\|string given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/LookupRef/Matrix.php - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\LookupRef\\\\Matrix\\:\\:extractRowValue\\(\\) has no return typehint specified\\.$#" count: 1 diff --git a/src/PhpSpreadsheet/Calculation/LookupRef/LookupRefValidations.php b/src/PhpSpreadsheet/Calculation/LookupRef/LookupRefValidations.php new file mode 100644 index 00000000..b0739eb3 --- /dev/null +++ b/src/PhpSpreadsheet/Calculation/LookupRef/LookupRefValidations.php @@ -0,0 +1,39 @@ +getMessage(); } if (!is_array($matrix) || ($rowNum > count($matrix))) { @@ -69,12 +75,12 @@ class Matrix return Functions::REF(); } - if ($columnNum == 0) { + if ($columnNum === 0) { return self::extractRowValue($matrix, $rowKeys, $rowNum); } $columnNum = $columnKeys[--$columnNum]; - if ($rowNum == 0) { + if ($rowNum === 0) { return array_map( function ($value) { return [$value]; @@ -89,7 +95,7 @@ class Matrix private static function extractRowValue(array $matrix, array $rowKeys, int $rowNum) { - if ($rowNum == 0) { + if ($rowNum === 0) { return $matrix; } diff --git a/tests/data/Calculation/LookupRef/INDEX.php b/tests/data/Calculation/LookupRef/INDEX.php index 157794ab..a699534e 100644 --- a/tests/data/Calculation/LookupRef/INDEX.php +++ b/tests/data/Calculation/LookupRef/INDEX.php @@ -6,7 +6,7 @@ return [ // Input [20 => ['R' => 1]], ], - [ + 'Negative Row' => [ '#VALUE!', // Expected // Input [ @@ -15,7 +15,7 @@ return [ ], -1, ], - [ + 'Row > matrix rows' => [ '#REF!', // Expected // Input [ @@ -24,7 +24,25 @@ return [ ], 10, ], - [ + 'Row is not a number' => [ + '#VALUE!', // Expected + // Input + [ + 20 => ['R' => 1], + 21 => ['R' => 2], + ], + 'NaN', + ], + 'Row is Error' => [ + '#N/A', // Expected + // Input + [ + 20 => ['R' => 1], + 21 => ['R' => 2], + ], + '#N/A', + ], + 'Return row 2' => [ [21 => ['R' => 2]], // Expected // Input [ @@ -33,7 +51,7 @@ return [ ], 2, ], - [ + 'Return row 2 from larger matrix' => [ [21 => ['R' => 2, 'S' => 4]], // Expected // Input [ @@ -43,17 +61,17 @@ return [ 2, 0, ], - [ + 'Negative Column' => [ '#VALUE!', // Expected // Input [ '20' => ['R' => 1, 'S' => 3], '21' => ['R' => 2, 'S' => 4], ], - 2, + 0, -1, ], - [ + 'Column > matrix columns' => [ '#REF!', // Expected // Input [ @@ -63,15 +81,25 @@ return [ 2, 10, ], - [ - '#REF!', // Expected + 'Column is not a number' => [ + '#VALUE!', // Expected // Input [ - '20' => ['R' => 1, 'S' => 3], - '21' => ['R' => 2, 'S' => 4], + 20 => ['R' => 1], + 21 => ['R' => 2], ], - 10, - 2, + 1, + 'NaN', + ], + 'Column is Error' => [ + '#N/A', // Expected + // Input + [ + 20 => ['R' => 1], + 21 => ['R' => 2], + ], + 1, + '#N/A', ], [ 4, // Expected @@ -115,6 +143,15 @@ return [ 2, 1, ], + [ + [1 => ['Bananas', 'Pears']], + [ + ['Apples', 'Lemons'], + ['Bananas', 'Pears'], + ], + 2, + 0, + ], [ 3, [ From 72a36a5bb8d0ba6a807a7ff48f354ada1806dbe9 Mon Sep 17 00:00:00 2001 From: Mark Baker Date: Fri, 7 May 2021 12:53:59 +0200 Subject: [PATCH 32/57] Resolve issue with conditional font size set to zero in PHP8 (#2073) * Let's see if the tests now pass against PHP8; output file looks to be good * Font can't be both superscript and subscript at the same time, so we use if/else rather than if/if --- src/PhpSpreadsheet/Reader/Xlsx/Styles.php | 12 +++++----- .../Reader/Xlsx/DefaultFillTest.php | 2 +- .../Reader/Xlsx/DefaultFontTest.php | 22 +++++++++++++++++++ 3 files changed, 30 insertions(+), 6 deletions(-) create mode 100644 tests/PhpSpreadsheetTests/Reader/Xlsx/DefaultFontTest.php diff --git a/src/PhpSpreadsheet/Reader/Xlsx/Styles.php b/src/PhpSpreadsheet/Reader/Xlsx/Styles.php index 2968a3fe..80c32065 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx/Styles.php +++ b/src/PhpSpreadsheet/Reader/Xlsx/Styles.php @@ -42,9 +42,12 @@ class Styles extends BaseParserClass public static function readFontStyle(Font $fontStyle, SimpleXMLElement $fontStyleXml): void { - $fontStyle->setName((string) $fontStyleXml->name['val']); - $fontStyle->setSize((float) $fontStyleXml->sz['val']); - + if (isset($fontStyleXml->name, $fontStyleXml->name['val'])) { + $fontStyle->setName((string) $fontStyleXml->name['val']); + } + if (isset($fontStyleXml->sz, $fontStyleXml->sz['val'])) { + $fontStyle->setSize((float) $fontStyleXml->sz['val']); + } if (isset($fontStyleXml->b)) { $fontStyle->setBold(!isset($fontStyleXml->b['val']) || self::boolean((string) $fontStyleXml->b['val'])); } @@ -68,8 +71,7 @@ class Styles extends BaseParserClass $verticalAlign = strtolower((string) $fontStyleXml->vertAlign['val']); if ($verticalAlign === 'superscript') { $fontStyle->setSuperscript(true); - } - if ($verticalAlign === 'subscript') { + } elseif ($verticalAlign === 'subscript') { $fontStyle->setSubscript(true); } } diff --git a/tests/PhpSpreadsheetTests/Reader/Xlsx/DefaultFillTest.php b/tests/PhpSpreadsheetTests/Reader/Xlsx/DefaultFillTest.php index ccdad067..dc61b953 100644 --- a/tests/PhpSpreadsheetTests/Reader/Xlsx/DefaultFillTest.php +++ b/tests/PhpSpreadsheetTests/Reader/Xlsx/DefaultFillTest.php @@ -1,6 +1,6 @@ load($filename); + + $style = $spreadsheet->getActiveSheet()->getConditionalStyles('A1')[0]->getStyle(); + self::assertSame('9C0006', $style->getFont()->getColor()->getRGB()); + self::assertNull($style->getFont()->getName()); + self::assertNull($style->getFont()->getSize()); + } +} From 76ac0089110e959e810e4611490d42c62580892c Mon Sep 17 00:00:00 2001 From: Nathan Dench Date: Tue, 4 May 2021 14:45:04 +1000 Subject: [PATCH 33/57] R1C1 conversion should handle absolute A1 references --- src/PhpSpreadsheet/Cell/AddressHelper.php | 19 ++++++++++++++++--- .../data/Cell/A1ConversionToR1C1Relative.php | 15 +++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/PhpSpreadsheet/Cell/AddressHelper.php b/src/PhpSpreadsheet/Cell/AddressHelper.php index b0e34e25..e5f4e952 100644 --- a/src/PhpSpreadsheet/Cell/AddressHelper.php +++ b/src/PhpSpreadsheet/Cell/AddressHelper.php @@ -102,14 +102,27 @@ class AddressHelper ?int $currentRowNumber = null, ?int $currentColumnNumber = null ): string { - $validityCheck = preg_match('/^\$?([A-Z]{1,3})\$?(\d{1,7})$/i', $address, $cellReference); + $validityCheck = preg_match('/^(\$?[A-Z]{1,3})(\$?\d{1,7})$/i', $address, $cellReference); if ($validityCheck === 0) { throw new Exception('Invalid A1-format Cell Reference'); } - $columnId = Coordinate::columnIndexFromString($cellReference[1]); - $rowId = (int) $cellReference[2]; + if ($cellReference[1][0] === '$') { + $columnId = Coordinate::columnIndexFromString(substr($cellReference[1], 1)); + // Column must be absolute address + $currentColumnNumber = null; + } else { + $columnId = Coordinate::columnIndexFromString($cellReference[1]); + } + + if ($cellReference[2][0] === '$') { + $rowId = (int) substr($cellReference[2], 1); + // Row must be absolute address + $currentRowNumber = null; + } else { + $rowId = (int) $cellReference[2]; + } if ($currentRowNumber !== null) { if ($rowId === $currentRowNumber) { diff --git a/tests/data/Cell/A1ConversionToR1C1Relative.php b/tests/data/Cell/A1ConversionToR1C1Relative.php index 76a6aee8..dd9b2391 100644 --- a/tests/data/Cell/A1ConversionToR1C1Relative.php +++ b/tests/data/Cell/A1ConversionToR1C1Relative.php @@ -2,18 +2,33 @@ return [ ['R[2]C[2]', 'O18', 16, 13], + ['R18C15', '$O$18', 16, 13], ['R[-2]C[2]', 'O14', 16, 13], + ['R[-2]C15', '$O14', 16, 13], ['R[2]C[-2]', 'K18', 16, 13], + ['R18C[-2]', 'K$18', 16, 13], ['R[-2]C[-2]', 'K14', 16, 13], ['RC[3]', 'P16', 16, 13], + ['R16C[3]', 'P$16', 16, 13], ['RC[-3]', 'J16', 16, 13], + ['RC10', '$J16', 16, 13], ['R[4]C', 'M20', 16, 13], + ['R[4]C13', '$M20', 16, 13], ['R[-4]C', 'M12', 16, 13], + ['R12C', 'M$12', 16, 13], ['RC', 'E5', 5, 5], + ['R5C5', '$E$5', 5, 5], ['R5C', 'E5', null, 5], + ['R5C5', '$E5', null, 5], + ['R5C', 'E$5', null, 5], ['RC5', 'E5', 5, null], + ['RC5', '$E5', 5, null], + ['R5C5', 'E$5', 5, null], ['R5C[2]', 'E5', null, 3], + ['R5C5', '$E5', null, 3], + ['R5C[2]', 'E$5', null, 3], ['R[2]C5', 'E5', 3, null], + ['R5C5', '$E$5', 3, null], ['R5C[-2]', 'E5', null, 7], ['R[-2]C5', 'E5', 7, null], ]; From a96109d89b2474c4c465c70a843ea04989715119 Mon Sep 17 00:00:00 2001 From: Nathan Dench Date: Tue, 4 May 2021 15:21:44 +1000 Subject: [PATCH 34/57] Update CHANGELOG.md with R1C1 conversion change --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4599f877..0c1a549c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Nothing. ### Fixed +- Correctly handle absolute A1 references when converting to R1C1 format [PR #2060](https://github.com/PHPOffice/PhpSpreadsheet/pull/2060) - Correct default fill style for conditional without a pattern defined [Issue #2035](https://github.com/PHPOffice/PhpSpreadsheet/issues/2035) [PR #2050](https://github.com/PHPOffice/PhpSpreadsheet/pull/2050) - Fixed issue where array key check for existince before accessing arrays in Xlsx.php. [PR #1970](https://github.com/PHPOffice/PhpSpreadsheet/pull/1970) - Fixed issue with quoted strings in number format mask rendered with toFormattedString() [Issue 1972#](https://github.com/PHPOffice/PhpSpreadsheet/issues/1972) [PR #1978](https://github.com/PHPOffice/PhpSpreadsheet/pull/1978) From df01db58ad09e0f4912b5e53537457a88d43eb7c Mon Sep 17 00:00:00 2001 From: Nathan Dench Date: Thu, 6 May 2021 09:57:22 +1000 Subject: [PATCH 35/57] Remove complexity from AddressHelper::convertToR1C1 --- src/PhpSpreadsheet/Cell/AddressHelper.php | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/PhpSpreadsheet/Cell/AddressHelper.php b/src/PhpSpreadsheet/Cell/AddressHelper.php index e5f4e952..91f85bed 100644 --- a/src/PhpSpreadsheet/Cell/AddressHelper.php +++ b/src/PhpSpreadsheet/Cell/AddressHelper.php @@ -102,26 +102,22 @@ class AddressHelper ?int $currentRowNumber = null, ?int $currentColumnNumber = null ): string { - $validityCheck = preg_match('/^(\$?[A-Z]{1,3})(\$?\d{1,7})$/i', $address, $cellReference); + $validityCheck = preg_match('/^(\$?)([A-Z]{1,3})(\$?)(\d{1,7})$/i', $address, $cellReference); if ($validityCheck === 0) { throw new Exception('Invalid A1-format Cell Reference'); } - if ($cellReference[1][0] === '$') { - $columnId = Coordinate::columnIndexFromString(substr($cellReference[1], 1)); + $columnId = Coordinate::columnIndexFromString($cellReference[2]); + if ($cellReference[1] === '$') { // Column must be absolute address $currentColumnNumber = null; - } else { - $columnId = Coordinate::columnIndexFromString($cellReference[1]); } - if ($cellReference[2][0] === '$') { - $rowId = (int) substr($cellReference[2], 1); + $rowId = (int) $cellReference[4]; + if ($cellReference[3] === '$') { // Row must be absolute address $currentRowNumber = null; - } else { - $rowId = (int) $cellReference[2]; } if ($currentRowNumber !== null) { From f28eea7341b5569354e4433fd10eda83f3945755 Mon Sep 17 00:00:00 2001 From: Nathan Dench Date: Fri, 7 May 2021 13:00:43 +1000 Subject: [PATCH 36/57] Use named regex groups and constants for regex strings --- src/PhpSpreadsheet/Cell/AddressHelper.php | 10 +++++----- src/PhpSpreadsheet/Cell/Coordinate.php | 6 ++++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/PhpSpreadsheet/Cell/AddressHelper.php b/src/PhpSpreadsheet/Cell/AddressHelper.php index 91f85bed..632c046f 100644 --- a/src/PhpSpreadsheet/Cell/AddressHelper.php +++ b/src/PhpSpreadsheet/Cell/AddressHelper.php @@ -102,20 +102,20 @@ class AddressHelper ?int $currentRowNumber = null, ?int $currentColumnNumber = null ): string { - $validityCheck = preg_match('/^(\$?)([A-Z]{1,3})(\$?)(\d{1,7})$/i', $address, $cellReference); + $validityCheck = preg_match(Coordinate::A1_COORDINATE_REGEX, $address, $cellReference); if ($validityCheck === 0) { throw new Exception('Invalid A1-format Cell Reference'); } - $columnId = Coordinate::columnIndexFromString($cellReference[2]); - if ($cellReference[1] === '$') { + $columnId = Coordinate::columnIndexFromString($cellReference['col_ref']); + if ($cellReference['absolute_col'] === '$') { // Column must be absolute address $currentColumnNumber = null; } - $rowId = (int) $cellReference[4]; - if ($cellReference[3] === '$') { + $rowId = (int) $cellReference['row_ref']; + if ($cellReference['absolute_row'] === '$') { // Row must be absolute address $currentRowNumber = null; } diff --git a/src/PhpSpreadsheet/Cell/Coordinate.php b/src/PhpSpreadsheet/Cell/Coordinate.php index 0b3917f2..58d2573e 100644 --- a/src/PhpSpreadsheet/Cell/Coordinate.php +++ b/src/PhpSpreadsheet/Cell/Coordinate.php @@ -13,6 +13,8 @@ use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; */ abstract class Coordinate { + public const A1_COORDINATE_REGEX = '/^(?\$?)(?[A-Z]{1,3})(?\$?)(?\d{1,7})$/i'; + /** * Default range variable constant. * @@ -29,8 +31,8 @@ abstract class Coordinate */ public static function coordinateFromString($pCoordinateString) { - if (preg_match('/^([$]?[A-Z]{1,3})([$]?\\d{1,7})$/', $pCoordinateString, $matches)) { - return [$matches[1], $matches[2]]; + if (preg_match(self::A1_COORDINATE_REGEX, $pCoordinateString, $matches)) { + return [$matches['absolute_col'] . $matches['col_ref'], $matches['absolute_row'] . $matches['row_ref']]; } elseif (self::coordinateIsRange($pCoordinateString)) { throw new Exception('Cell coordinate string can not be a range of cells'); } elseif ($pCoordinateString == '') { From d2e6db71fa3605b56dd7e6162a6f3a101d9b71b1 Mon Sep 17 00:00:00 2001 From: Mark Baker Date: Fri, 7 May 2021 23:40:30 +0200 Subject: [PATCH 37/57] Lookup functions additional unit tests (#2074) * Additional unit tests for VLOOKUP() and HLOOKUP() * Additional unit tests for CHOOSE() * Unit tests for HYPERLINK() function * Fix CHOOSE() test for spillage --- .../Calculation/LookupRef/Hyperlink.php | 2 +- src/PhpSpreadsheet/Reader/Xml.php | 2 +- .../Functions/LookupRef/HyperlinkTest.php | 51 +++++++++++++++++++ tests/data/Calculation/LookupRef/CHOOSE.php | 8 +++ tests/data/Calculation/LookupRef/HLOOKUP.php | 24 +++++++++ .../data/Calculation/LookupRef/HYPERLINK.php | 26 ++++++++++ tests/data/Calculation/LookupRef/VLOOKUP.php | 31 +++++++++++ 7 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/HyperlinkTest.php create mode 100644 tests/data/Calculation/LookupRef/HYPERLINK.php diff --git a/src/PhpSpreadsheet/Calculation/LookupRef/Hyperlink.php b/src/PhpSpreadsheet/Calculation/LookupRef/Hyperlink.php index d0324964..823d70c6 100644 --- a/src/PhpSpreadsheet/Calculation/LookupRef/Hyperlink.php +++ b/src/PhpSpreadsheet/Calculation/LookupRef/Hyperlink.php @@ -11,7 +11,7 @@ class Hyperlink * HYPERLINK. * * Excel Function: - * =HYPERLINK(linkURL,displayName) + * =HYPERLINK(linkURL, [displayName]) * * @param mixed $linkURL Expect string. Value to check, is also the value returned when no error * @param mixed $displayName Expect string. Value to return when testValue is an error condition diff --git a/src/PhpSpreadsheet/Reader/Xml.php b/src/PhpSpreadsheet/Reader/Xml.php index 282cd528..4ef4efe7 100644 --- a/src/PhpSpreadsheet/Reader/Xml.php +++ b/src/PhpSpreadsheet/Reader/Xml.php @@ -284,7 +284,7 @@ class Xml extends BaseReader $worksheet_ss = self::getAttributes($worksheet, $namespaces['ss']); if ( - (isset($this->loadSheetsOnly)) && (isset($worksheet_ss['Name'])) && + isset($this->loadSheetsOnly, $worksheet_ss['Name']) && (!in_array($worksheet_ss['Name'], $this->loadSheetsOnly)) ) { continue; diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/HyperlinkTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/HyperlinkTest.php new file mode 100644 index 00000000..e71992ed --- /dev/null +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/HyperlinkTest.php @@ -0,0 +1,51 @@ +getMockBuilder(Cell::class) + ->onlyMethods(['getHyperlink']) + ->disableOriginalConstructor() + ->getMock(); + $cell->method('getHyperlink') + ->willReturn($hyperlink); + + $result = LookupRef::HYPERLINK($linkUrl, $description, $cell); + if (!is_array($expectedResult)) { + self::assertSame($expectedResult, $result); + } else { + self::assertSame($expectedResult[1], $result); + self::assertSame($expectedResult[0], $hyperlink->getUrl()); + self::assertSame($expectedResult[1], $hyperlink->getTooltip()); + } + } + + public function providerHYPERLINK(): array + { + return require 'tests/data/Calculation/LookupRef/HYPERLINK.php'; + } + + public function testHYPERLINKwithoutCell(): void + { + $result = LookupRef::HYPERLINK('https://phpspreadsheet.readthedocs.io/en/latest/', 'Read the Docs'); + self::assertSame(Functions::REF(), $result); + } +} diff --git a/tests/data/Calculation/LookupRef/CHOOSE.php b/tests/data/Calculation/LookupRef/CHOOSE.php index 06371c79..96c29780 100644 --- a/tests/data/Calculation/LookupRef/CHOOSE.php +++ b/tests/data/Calculation/LookupRef/CHOOSE.php @@ -25,4 +25,12 @@ return [ '#VALUE!', 0, 'red', 'blue', 'green', 'brown', ], + [ + '#VALUE!', + 'NaN', 'red', 'blue', 'green', 'brown', + ], + [ + ['blue', 'purple'], + 3, ['red', 'orange'], ['yellow', 'green'], ['blue', 'purple'], + ], ]; diff --git a/tests/data/Calculation/LookupRef/HLOOKUP.php b/tests/data/Calculation/LookupRef/HLOOKUP.php index d2a8a446..61cb7e06 100644 --- a/tests/data/Calculation/LookupRef/HLOOKUP.php +++ b/tests/data/Calculation/LookupRef/HLOOKUP.php @@ -328,4 +328,28 @@ return [ 2, false, ], + [ + 0.61, + 'Ed', + [ + [null, 'Ann', 'Cara', 'Colin', 'Ed', 'Frank'], + ['Math', 0.58, 0.90, 0.67, 0.76, 0.80], + ['French', 0.61, 0.71, 0.59, 0.59, 0.76], + ['Physics', 0.75, 0.45, 0.39, 0.52, 0.69], + ['Bioogy', 0.39, 0.55, 0.77, 0.61, 0.45], + ], + 5, + false, + ], + [ + 'Normal Weight', + 23.5, + [ + [null, 'Min', 0.0, 18.5, 25.0, 30.0], + ['BMI', 'Max', 18.4, 24.9, 29.9, null], + [null, 'Body Type', 'Underweight', 'Normal Weight', 'Overweight', 'Obese'], + ], + 3, + true, + ], ]; diff --git a/tests/data/Calculation/LookupRef/HYPERLINK.php b/tests/data/Calculation/LookupRef/HYPERLINK.php new file mode 100644 index 00000000..9a5e4c2e --- /dev/null +++ b/tests/data/Calculation/LookupRef/HYPERLINK.php @@ -0,0 +1,26 @@ + Date: Sat, 8 May 2021 19:36:35 +0200 Subject: [PATCH 38/57] Refactor Gnumeric Style Reader into a separate dedicated class --- src/PhpSpreadsheet/Reader/Gnumeric.php | 253 ++--------------- src/PhpSpreadsheet/Reader/Gnumeric/Styles.php | 256 ++++++++++++++++++ 2 files changed, 274 insertions(+), 235 deletions(-) create mode 100644 src/PhpSpreadsheet/Reader/Gnumeric/Styles.php diff --git a/src/PhpSpreadsheet/Reader/Gnumeric.php b/src/PhpSpreadsheet/Reader/Gnumeric.php index d66dbb88..049e1da1 100644 --- a/src/PhpSpreadsheet/Reader/Gnumeric.php +++ b/src/PhpSpreadsheet/Reader/Gnumeric.php @@ -7,6 +7,7 @@ use PhpOffice\PhpSpreadsheet\Cell\DataType; use PhpOffice\PhpSpreadsheet\DefinedName; use PhpOffice\PhpSpreadsheet\Reader\Gnumeric\PageSetup; use PhpOffice\PhpSpreadsheet\Reader\Gnumeric\Properties; +use PhpOffice\PhpSpreadsheet\Reader\Gnumeric\Styles; use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner; use PhpOffice\PhpSpreadsheet\ReferenceHelper; use PhpOffice\PhpSpreadsheet\RichText\RichText; @@ -56,6 +57,20 @@ class Gnumeric extends BaseReader /** @var ReferenceHelper */ private $referenceHelper; + /** @var array */ + public static $mappings = [ + 'dataType' => [ + '10' => DataType::TYPE_NULL, + '20' => DataType::TYPE_BOOL, + '30' => DataType::TYPE_NUMERIC, // Integer doesn't exist in Excel + '40' => DataType::TYPE_NUMERIC, // Float + '50' => DataType::TYPE_ERROR, + '60' => DataType::TYPE_STRING, + //'70': // Cell Range + //'80': // Array + ], + ]; + /** * Create a new Gnumeric. */ @@ -195,80 +210,9 @@ class Gnumeric extends BaseReader return $data; } - /** @var array */ - private static $mappings = [ - 'borderStyle' => [ - '0' => Border::BORDER_NONE, - '1' => Border::BORDER_THIN, - '2' => Border::BORDER_MEDIUM, - '3' => Border::BORDER_SLANTDASHDOT, - '4' => Border::BORDER_DASHED, - '5' => Border::BORDER_THICK, - '6' => Border::BORDER_DOUBLE, - '7' => Border::BORDER_DOTTED, - '8' => Border::BORDER_MEDIUMDASHED, - '9' => Border::BORDER_DASHDOT, - '10' => Border::BORDER_MEDIUMDASHDOT, - '11' => Border::BORDER_DASHDOTDOT, - '12' => Border::BORDER_MEDIUMDASHDOTDOT, - '13' => Border::BORDER_MEDIUMDASHDOTDOT, - ], - 'dataType' => [ - '10' => DataType::TYPE_NULL, - '20' => DataType::TYPE_BOOL, - '30' => DataType::TYPE_NUMERIC, // Integer doesn't exist in Excel - '40' => DataType::TYPE_NUMERIC, // Float - '50' => DataType::TYPE_ERROR, - '60' => DataType::TYPE_STRING, - //'70': // Cell Range - //'80': // Array - ], - 'fillType' => [ - '1' => Fill::FILL_SOLID, - '2' => Fill::FILL_PATTERN_DARKGRAY, - '3' => Fill::FILL_PATTERN_MEDIUMGRAY, - '4' => Fill::FILL_PATTERN_LIGHTGRAY, - '5' => Fill::FILL_PATTERN_GRAY125, - '6' => Fill::FILL_PATTERN_GRAY0625, - '7' => Fill::FILL_PATTERN_DARKHORIZONTAL, // horizontal stripe - '8' => Fill::FILL_PATTERN_DARKVERTICAL, // vertical stripe - '9' => Fill::FILL_PATTERN_DARKDOWN, // diagonal stripe - '10' => Fill::FILL_PATTERN_DARKUP, // reverse diagonal stripe - '11' => Fill::FILL_PATTERN_DARKGRID, // diagoanl crosshatch - '12' => Fill::FILL_PATTERN_DARKTRELLIS, // thick diagonal crosshatch - '13' => Fill::FILL_PATTERN_LIGHTHORIZONTAL, - '14' => Fill::FILL_PATTERN_LIGHTVERTICAL, - '15' => Fill::FILL_PATTERN_LIGHTUP, - '16' => Fill::FILL_PATTERN_LIGHTDOWN, - '17' => Fill::FILL_PATTERN_LIGHTGRID, // thin horizontal crosshatch - '18' => Fill::FILL_PATTERN_LIGHTTRELLIS, // thin diagonal crosshatch - ], - 'horizontal' => [ - '1' => Alignment::HORIZONTAL_GENERAL, - '2' => Alignment::HORIZONTAL_LEFT, - '4' => Alignment::HORIZONTAL_RIGHT, - '8' => Alignment::HORIZONTAL_CENTER, - '16' => Alignment::HORIZONTAL_CENTER_CONTINUOUS, - '32' => Alignment::HORIZONTAL_JUSTIFY, - '64' => Alignment::HORIZONTAL_CENTER_CONTINUOUS, - ], - 'underline' => [ - '1' => Font::UNDERLINE_SINGLE, - '2' => Font::UNDERLINE_DOUBLE, - '3' => Font::UNDERLINE_SINGLEACCOUNTING, - '4' => Font::UNDERLINE_DOUBLEACCOUNTING, - ], - 'vertical' => [ - '1' => Alignment::VERTICAL_TOP, - '2' => Alignment::VERTICAL_BOTTOM, - '4' => Alignment::VERTICAL_CENTER, - '8' => Alignment::VERTICAL_JUSTIFY, - ], - ]; - public static function gnumericMappings(): array { - return self::$mappings; + return array_merge(self::$mappings, Styles::$mappings); } private function processComments(SimpleXMLElement $sheet): void @@ -405,80 +349,9 @@ class Gnumeric extends BaseReader $this->spreadsheet->getActiveSheet()->getCell($column . $row)->setValueExplicit((string) $cell, $type); } + (new Styles($this->spreadsheet, $this->readDataOnly))->read($sheet, $maxRow, $maxCol); + $this->processComments($sheet); - - foreach ($sheet->Styles->StyleRegion as $styleRegion) { - $styleAttributes = $styleRegion->attributes(); - if ( - ($styleAttributes['startRow'] <= $maxRow) && - ($styleAttributes['startCol'] <= $maxCol) - ) { - $startColumn = Coordinate::stringFromColumnIndex((int) $styleAttributes['startCol'] + 1); - $startRow = $styleAttributes['startRow'] + 1; - - $endColumn = ($styleAttributes['endCol'] > $maxCol) ? $maxCol : (int) $styleAttributes['endCol']; - $endColumn = Coordinate::stringFromColumnIndex($endColumn + 1); - - $endRow = 1 + (($styleAttributes['endRow'] > $maxRow) ? $maxRow : (int) $styleAttributes['endRow']); - $cellRange = $startColumn . $startRow . ':' . $endColumn . $endRow; - - $styleAttributes = $styleRegion->Style->attributes(); - - $styleArray = []; - // We still set the number format mask for date/time values, even if readDataOnly is true - $formatCode = (string) $styleAttributes['Format']; - if (Date::isDateTimeFormatCode($formatCode)) { - $styleArray['numberFormat']['formatCode'] = $formatCode; - } - if (!$this->readDataOnly) { - // If readDataOnly is false, we set all formatting information - $styleArray['numberFormat']['formatCode'] = $formatCode; - - self::addStyle2($styleArray, 'alignment', 'horizontal', $styleAttributes['HAlign']); - self::addStyle2($styleArray, 'alignment', 'vertical', $styleAttributes['VAlign']); - $styleArray['alignment']['wrapText'] = $styleAttributes['WrapText'] == '1'; - $styleArray['alignment']['textRotation'] = $this->calcRotation($styleAttributes); - $styleArray['alignment']['shrinkToFit'] = $styleAttributes['ShrinkToFit'] == '1'; - $styleArray['alignment']['indent'] = ((int) ($styleAttributes['Indent']) > 0) ? $styleAttributes['indent'] : 0; - - $this->addColors($styleArray, $styleAttributes); - - $fontAttributes = $styleRegion->Style->Font->attributes(); - $styleArray['font']['name'] = (string) $styleRegion->Style->Font; - $styleArray['font']['size'] = (int) ($fontAttributes['Unit']); - $styleArray['font']['bold'] = $fontAttributes['Bold'] == '1'; - $styleArray['font']['italic'] = $fontAttributes['Italic'] == '1'; - $styleArray['font']['strikethrough'] = $fontAttributes['StrikeThrough'] == '1'; - self::addStyle2($styleArray, 'font', 'underline', $fontAttributes['Underline']); - - switch ($fontAttributes['Script']) { - case '1': - $styleArray['font']['superscript'] = true; - - break; - case '-1': - $styleArray['font']['subscript'] = true; - - break; - } - - if (isset($styleRegion->Style->StyleBorder)) { - $srssb = $styleRegion->Style->StyleBorder; - $this->addBorderStyle($srssb, $styleArray, 'top'); - $this->addBorderStyle($srssb, $styleArray, 'bottom'); - $this->addBorderStyle($srssb, $styleArray, 'left'); - $this->addBorderStyle($srssb, $styleArray, 'right'); - $this->addBorderDiagonal($srssb, $styleArray); - } - if (isset($styleRegion->Style->HyperLink)) { - // TO DO - $hyperlink = $styleRegion->Style->HyperLink->attributes(); - } - } - $this->spreadsheet->getActiveSheet()->getStyle($cellRange)->applyFromArray($styleArray); - } - } - $this->processColumnWidths($sheet, $maxCol); $this->processRowHeights($sheet, $maxRow); $this->processMergedCells($sheet); @@ -493,28 +366,6 @@ class Gnumeric extends BaseReader return $this->spreadsheet; } - private function addBorderDiagonal(SimpleXMLElement $srssb, array &$styleArray): void - { - if (isset($srssb->Diagonal, $srssb->{'Rev-Diagonal'})) { - $styleArray['borders']['diagonal'] = self::parseBorderAttributes($srssb->Diagonal->attributes()); - $styleArray['borders']['diagonalDirection'] = Borders::DIAGONAL_BOTH; - } elseif (isset($srssb->Diagonal)) { - $styleArray['borders']['diagonal'] = self::parseBorderAttributes($srssb->Diagonal->attributes()); - $styleArray['borders']['diagonalDirection'] = Borders::DIAGONAL_UP; - } elseif (isset($srssb->{'Rev-Diagonal'})) { - $styleArray['borders']['diagonal'] = self::parseBorderAttributes($srssb->{'Rev-Diagonal'}->attributes()); - $styleArray['borders']['diagonalDirection'] = Borders::DIAGONAL_DOWN; - } - } - - private function addBorderStyle(SimpleXMLElement $srssb, array &$styleArray, string $direction): void - { - $ucDirection = ucfirst($direction); - if (isset($srssb->$ucDirection)) { - $styleArray['borders'][$direction] = self::parseBorderAttributes($srssb->$ucDirection->attributes()); - } - } - private function processMergedCells(?SimpleXMLElement $sheet): void { // Handle Merged Cells in this worksheet @@ -683,45 +534,6 @@ class Gnumeric extends BaseReader } } - private function calcRotation(SimpleXMLElement $styleAttributes): int - { - $rotation = (int) $styleAttributes->Rotation; - if ($rotation >= 270 && $rotation <= 360) { - $rotation -= 360; - } - $rotation = (abs($rotation) > 90) ? 0 : $rotation; - - return $rotation; - } - - private static function addStyle(array &$styleArray, string $key, string $value): void - { - if (array_key_exists($value, self::$mappings[$key])) { - $styleArray[$key] = self::$mappings[$key][$value]; - } - } - - private static function addStyle2(array &$styleArray, string $key1, string $key, string $value): void - { - if (array_key_exists($value, self::$mappings[$key])) { - $styleArray[$key1][$key] = self::$mappings[$key][$value]; - } - } - - private static function parseBorderAttributes(?SimpleXMLElement $borderAttributes): array - { - $styleArray = []; - if ($borderAttributes !== null) { - if (isset($borderAttributes['Color'])) { - $styleArray['color']['rgb'] = self::parseGnumericColour($borderAttributes['Color']); - } - - self::addStyle($styleArray, 'borderStyle', $borderAttributes['Style']); - } - - return $styleArray; - } - private function parseRichText(string $is): RichText { $value = new RichText(); @@ -729,33 +541,4 @@ class Gnumeric extends BaseReader return $value; } - - private static function parseGnumericColour(string $gnmColour): string - { - [$gnmR, $gnmG, $gnmB] = explode(':', $gnmColour); - $gnmR = substr(str_pad($gnmR, 4, '0', STR_PAD_RIGHT), 0, 2); - $gnmG = substr(str_pad($gnmG, 4, '0', STR_PAD_RIGHT), 0, 2); - $gnmB = substr(str_pad($gnmB, 4, '0', STR_PAD_RIGHT), 0, 2); - - return $gnmR . $gnmG . $gnmB; - } - - private function addColors(array &$styleArray, SimpleXMLElement $styleAttributes): void - { - $RGB = self::parseGnumericColour($styleAttributes['Fore']); - $styleArray['font']['color']['rgb'] = $RGB; - $RGB = self::parseGnumericColour($styleAttributes['Back']); - $shade = (string) $styleAttributes['Shade']; - if (($RGB != '000000') || ($shade != '0')) { - $RGB2 = self::parseGnumericColour($styleAttributes['PatternColor']); - if ($shade === '1') { - $styleArray['fill']['startColor']['rgb'] = $RGB; - $styleArray['fill']['endColor']['rgb'] = $RGB2; - } else { - $styleArray['fill']['endColor']['rgb'] = $RGB; - $styleArray['fill']['startColor']['rgb'] = $RGB2; - } - self::addStyle2($styleArray, 'fill', 'fillType', $shade); - } - } } diff --git a/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php b/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php new file mode 100644 index 00000000..9c725b20 --- /dev/null +++ b/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php @@ -0,0 +1,256 @@ + [ + '0' => Border::BORDER_NONE, + '1' => Border::BORDER_THIN, + '2' => Border::BORDER_MEDIUM, + '3' => Border::BORDER_SLANTDASHDOT, + '4' => Border::BORDER_DASHED, + '5' => Border::BORDER_THICK, + '6' => Border::BORDER_DOUBLE, + '7' => Border::BORDER_DOTTED, + '8' => Border::BORDER_MEDIUMDASHED, + '9' => Border::BORDER_DASHDOT, + '10' => Border::BORDER_MEDIUMDASHDOT, + '11' => Border::BORDER_DASHDOTDOT, + '12' => Border::BORDER_MEDIUMDASHDOTDOT, + '13' => Border::BORDER_MEDIUMDASHDOTDOT, + ], + 'fillType' => [ + '1' => Fill::FILL_SOLID, + '2' => Fill::FILL_PATTERN_DARKGRAY, + '3' => Fill::FILL_PATTERN_MEDIUMGRAY, + '4' => Fill::FILL_PATTERN_LIGHTGRAY, + '5' => Fill::FILL_PATTERN_GRAY125, + '6' => Fill::FILL_PATTERN_GRAY0625, + '7' => Fill::FILL_PATTERN_DARKHORIZONTAL, // horizontal stripe + '8' => Fill::FILL_PATTERN_DARKVERTICAL, // vertical stripe + '9' => Fill::FILL_PATTERN_DARKDOWN, // diagonal stripe + '10' => Fill::FILL_PATTERN_DARKUP, // reverse diagonal stripe + '11' => Fill::FILL_PATTERN_DARKGRID, // diagoanl crosshatch + '12' => Fill::FILL_PATTERN_DARKTRELLIS, // thick diagonal crosshatch + '13' => Fill::FILL_PATTERN_LIGHTHORIZONTAL, + '14' => Fill::FILL_PATTERN_LIGHTVERTICAL, + '15' => Fill::FILL_PATTERN_LIGHTUP, + '16' => Fill::FILL_PATTERN_LIGHTDOWN, + '17' => Fill::FILL_PATTERN_LIGHTGRID, // thin horizontal crosshatch + '18' => Fill::FILL_PATTERN_LIGHTTRELLIS, // thin diagonal crosshatch + ], + 'horizontal' => [ + '1' => Alignment::HORIZONTAL_GENERAL, + '2' => Alignment::HORIZONTAL_LEFT, + '4' => Alignment::HORIZONTAL_RIGHT, + '8' => Alignment::HORIZONTAL_CENTER, + '16' => Alignment::HORIZONTAL_CENTER_CONTINUOUS, + '32' => Alignment::HORIZONTAL_JUSTIFY, + '64' => Alignment::HORIZONTAL_CENTER_CONTINUOUS, + ], + 'underline' => [ + '1' => Font::UNDERLINE_SINGLE, + '2' => Font::UNDERLINE_DOUBLE, + '3' => Font::UNDERLINE_SINGLEACCOUNTING, + '4' => Font::UNDERLINE_DOUBLEACCOUNTING, + ], + 'vertical' => [ + '1' => Alignment::VERTICAL_TOP, + '2' => Alignment::VERTICAL_BOTTOM, + '4' => Alignment::VERTICAL_CENTER, + '8' => Alignment::VERTICAL_JUSTIFY, + ], + ]; + + public function __construct(Spreadsheet $spreadsheet, bool $readDataOnly) + { + $this->spreadsheet = $spreadsheet; + $this->readDataOnly = $readDataOnly; + } + + public function read(?SimpleXMLElement $sheet, $maxRow, $maxCol) + { + foreach ($sheet->Styles->StyleRegion as $styleRegion) { + $styleAttributes = $styleRegion->attributes(); + if (($styleAttributes['startRow'] <= $maxRow) && ($styleAttributes['startCol'] <= $maxCol)) { + $startColumn = Coordinate::stringFromColumnIndex((int) $styleAttributes['startCol'] + 1); + $startRow = $styleAttributes['startRow'] + 1; + + $endColumn = ($styleAttributes['endCol'] > $maxCol) ? $maxCol : (int) $styleAttributes['endCol']; + $endColumn = Coordinate::stringFromColumnIndex($endColumn + 1); + + $endRow = 1 + (($styleAttributes['endRow'] > $maxRow) ? $maxRow : (int) $styleAttributes['endRow']); + $cellRange = $startColumn . $startRow . ':' . $endColumn . $endRow; + + $styleAttributes = $styleRegion->Style->attributes(); + + $styleArray = []; + // We still set the number format mask for date/time values, even if readDataOnly is true + $formatCode = (string) $styleAttributes['Format']; + if (Date::isDateTimeFormatCode($formatCode)) { + $styleArray['numberFormat']['formatCode'] = $formatCode; + } + if (!$this->readDataOnly) { + // If readDataOnly is false, we set all formatting information + $styleArray['numberFormat']['formatCode'] = $formatCode; + + self::addStyle2($styleArray, 'alignment', 'horizontal', $styleAttributes['HAlign']); + self::addStyle2($styleArray, 'alignment', 'vertical', $styleAttributes['VAlign']); + $styleArray['alignment']['wrapText'] = $styleAttributes['WrapText'] == '1'; + $styleArray['alignment']['textRotation'] = $this->calcRotation($styleAttributes); + $styleArray['alignment']['shrinkToFit'] = $styleAttributes['ShrinkToFit'] == '1'; + $styleArray['alignment']['indent'] = ((int) ($styleAttributes['Indent']) > 0) ? $styleAttributes['indent'] : 0; + + $this->addColors($styleArray, $styleAttributes); + + $fontAttributes = $styleRegion->Style->Font->attributes(); + $styleArray['font']['name'] = (string) $styleRegion->Style->Font; + $styleArray['font']['size'] = (int) ($fontAttributes['Unit']); + $styleArray['font']['bold'] = $fontAttributes['Bold'] == '1'; + $styleArray['font']['italic'] = $fontAttributes['Italic'] == '1'; + $styleArray['font']['strikethrough'] = $fontAttributes['StrikeThrough'] == '1'; + self::addStyle2($styleArray, 'font', 'underline', $fontAttributes['Underline']); + + switch ($fontAttributes['Script']) { + case '1': + $styleArray['font']['superscript'] = true; + + break; + case '-1': + $styleArray['font']['subscript'] = true; + + break; + } + + if (isset($styleRegion->Style->StyleBorder)) { + $srssb = $styleRegion->Style->StyleBorder; + $this->addBorderStyle($srssb, $styleArray, 'top'); + $this->addBorderStyle($srssb, $styleArray, 'bottom'); + $this->addBorderStyle($srssb, $styleArray, 'left'); + $this->addBorderStyle($srssb, $styleArray, 'right'); + $this->addBorderDiagonal($srssb, $styleArray); + } + if (isset($styleRegion->Style->HyperLink)) { + // TO DO + $hyperlink = $styleRegion->Style->HyperLink->attributes(); + } + } + $this->spreadsheet->getActiveSheet()->getStyle($cellRange)->applyFromArray($styleArray); + } + } + } + + private function addBorderDiagonal(SimpleXMLElement $srssb, array &$styleArray): void + { + if (isset($srssb->Diagonal, $srssb->{'Rev-Diagonal'})) { + $styleArray['borders']['diagonal'] = self::parseBorderAttributes($srssb->Diagonal->attributes()); + $styleArray['borders']['diagonalDirection'] = Borders::DIAGONAL_BOTH; + } elseif (isset($srssb->Diagonal)) { + $styleArray['borders']['diagonal'] = self::parseBorderAttributes($srssb->Diagonal->attributes()); + $styleArray['borders']['diagonalDirection'] = Borders::DIAGONAL_UP; + } elseif (isset($srssb->{'Rev-Diagonal'})) { + $styleArray['borders']['diagonal'] = self::parseBorderAttributes($srssb->{'Rev-Diagonal'}->attributes()); + $styleArray['borders']['diagonalDirection'] = Borders::DIAGONAL_DOWN; + } + } + + private function addBorderStyle(SimpleXMLElement $srssb, array &$styleArray, string $direction): void + { + $ucDirection = ucfirst($direction); + if (isset($srssb->$ucDirection)) { + $styleArray['borders'][$direction] = self::parseBorderAttributes($srssb->$ucDirection->attributes()); + } + } + + private function calcRotation(SimpleXMLElement $styleAttributes): int + { + $rotation = (int) $styleAttributes->Rotation; + if ($rotation >= 270 && $rotation <= 360) { + $rotation -= 360; + } + $rotation = (abs($rotation) > 90) ? 0 : $rotation; + + return $rotation; + } + + private static function addStyle(array &$styleArray, string $key, string $value): void + { + if (array_key_exists($value, self::$mappings[$key])) { + $styleArray[$key] = self::$mappings[$key][$value]; + } + } + + private static function addStyle2(array &$styleArray, string $key1, string $key, string $value): void + { + if (array_key_exists($value, self::$mappings[$key])) { + $styleArray[$key1][$key] = self::$mappings[$key][$value]; + } + } + + private static function parseBorderAttributes(?SimpleXMLElement $borderAttributes): array + { + $styleArray = []; + if ($borderAttributes !== null) { + if (isset($borderAttributes['Color'])) { + $styleArray['color']['rgb'] = self::parseGnumericColour($borderAttributes['Color']); + } + + self::addStyle($styleArray, 'borderStyle', $borderAttributes['Style']); + } + + return $styleArray; + } + + private static function parseGnumericColour(string $gnmColour): string + { + [$gnmR, $gnmG, $gnmB] = explode(':', $gnmColour); + $gnmR = substr(str_pad($gnmR, 4, '0', STR_PAD_RIGHT), 0, 2); + $gnmG = substr(str_pad($gnmG, 4, '0', STR_PAD_RIGHT), 0, 2); + $gnmB = substr(str_pad($gnmB, 4, '0', STR_PAD_RIGHT), 0, 2); + + return $gnmR . $gnmG . $gnmB; + } + + private function addColors(array &$styleArray, SimpleXMLElement $styleAttributes): void + { + $RGB = self::parseGnumericColour($styleAttributes['Fore']); + $styleArray['font']['color']['rgb'] = $RGB; + $RGB = self::parseGnumericColour($styleAttributes['Back']); + $shade = (string) $styleAttributes['Shade']; + if (($RGB != '000000') || ($shade != '0')) { + $RGB2 = self::parseGnumericColour($styleAttributes['PatternColor']); + if ($shade === '1') { + $styleArray['fill']['startColor']['rgb'] = $RGB; + $styleArray['fill']['endColor']['rgb'] = $RGB2; + } else { + $styleArray['fill']['endColor']['rgb'] = $RGB; + $styleArray['fill']['startColor']['rgb'] = $RGB2; + } + self::addStyle2($styleArray, 'fill', 'fillType', $shade); + } + } +} From e71c2e46d00b23b33b7fafc3c0b2aa7bd17139b9 Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Sat, 8 May 2021 19:57:08 +0200 Subject: [PATCH 39/57] Minor style tweaks --- src/PhpSpreadsheet/Reader/Gnumeric.php | 10 ++----- src/PhpSpreadsheet/Reader/Gnumeric/Styles.php | 28 +++++++++++-------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/PhpSpreadsheet/Reader/Gnumeric.php b/src/PhpSpreadsheet/Reader/Gnumeric.php index 049e1da1..85bae6f8 100644 --- a/src/PhpSpreadsheet/Reader/Gnumeric.php +++ b/src/PhpSpreadsheet/Reader/Gnumeric.php @@ -12,14 +12,8 @@ use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner; use PhpOffice\PhpSpreadsheet\ReferenceHelper; use PhpOffice\PhpSpreadsheet\RichText\RichText; use PhpOffice\PhpSpreadsheet\Settings; -use PhpOffice\PhpSpreadsheet\Shared\Date; use PhpOffice\PhpSpreadsheet\Shared\File; use PhpOffice\PhpSpreadsheet\Spreadsheet; -use PhpOffice\PhpSpreadsheet\Style\Alignment; -use PhpOffice\PhpSpreadsheet\Style\Border; -use PhpOffice\PhpSpreadsheet\Style\Borders; -use PhpOffice\PhpSpreadsheet\Style\Fill; -use PhpOffice\PhpSpreadsheet\Style\Font; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; use SimpleXMLElement; use XMLReader; @@ -349,7 +343,9 @@ class Gnumeric extends BaseReader $this->spreadsheet->getActiveSheet()->getCell($column . $row)->setValueExplicit((string) $cell, $type); } - (new Styles($this->spreadsheet, $this->readDataOnly))->read($sheet, $maxRow, $maxCol); + if ($sheet->Styles !== null) { + (new Styles($this->spreadsheet, $this->readDataOnly))->read($sheet, $maxRow, $maxCol); + } $this->processComments($sheet); $this->processColumnWidths($sheet, $maxCol); diff --git a/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php b/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php index 9c725b20..02166eb0 100644 --- a/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php +++ b/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php @@ -3,7 +3,6 @@ namespace PhpOffice\PhpSpreadsheet\Reader\Gnumeric; use PhpOffice\PhpSpreadsheet\Cell\Coordinate; -use PhpOffice\PhpSpreadsheet\Cell\DataType; use PhpOffice\PhpSpreadsheet\Shared\Date; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Style\Alignment; @@ -92,10 +91,17 @@ class Styles $this->readDataOnly = $readDataOnly; } - public function read(?SimpleXMLElement $sheet, $maxRow, $maxCol) + public function read(SimpleXMLElement $sheet, int $maxRow, int $maxCol): void { - foreach ($sheet->Styles->StyleRegion as $styleRegion) { - $styleAttributes = $styleRegion->attributes(); + if ($sheet->Styles->StyleRegion !== null) { + $this->readStyles($sheet->Styles->StyleRegion, $maxRow, $maxCol); + } + } + + private function readStyles(SimpleXMLElement $styleRegion, int $maxRow, int $maxCol): void + { + foreach ($styleRegion as $style) { + $styleAttributes = $style->attributes(); if (($styleAttributes['startRow'] <= $maxRow) && ($styleAttributes['startCol'] <= $maxCol)) { $startColumn = Coordinate::stringFromColumnIndex((int) $styleAttributes['startCol'] + 1); $startRow = $styleAttributes['startRow'] + 1; @@ -106,7 +112,7 @@ class Styles $endRow = 1 + (($styleAttributes['endRow'] > $maxRow) ? $maxRow : (int) $styleAttributes['endRow']); $cellRange = $startColumn . $startRow . ':' . $endColumn . $endRow; - $styleAttributes = $styleRegion->Style->attributes(); + $styleAttributes = $style->Style->attributes(); $styleArray = []; // We still set the number format mask for date/time values, even if readDataOnly is true @@ -127,8 +133,8 @@ class Styles $this->addColors($styleArray, $styleAttributes); - $fontAttributes = $styleRegion->Style->Font->attributes(); - $styleArray['font']['name'] = (string) $styleRegion->Style->Font; + $fontAttributes = $style->Style->Font->attributes(); + $styleArray['font']['name'] = (string) $style->Style->Font; $styleArray['font']['size'] = (int) ($fontAttributes['Unit']); $styleArray['font']['bold'] = $fontAttributes['Bold'] == '1'; $styleArray['font']['italic'] = $fontAttributes['Italic'] == '1'; @@ -146,17 +152,17 @@ class Styles break; } - if (isset($styleRegion->Style->StyleBorder)) { - $srssb = $styleRegion->Style->StyleBorder; + if (isset($style->Style->StyleBorder)) { + $srssb = $style->Style->StyleBorder; $this->addBorderStyle($srssb, $styleArray, 'top'); $this->addBorderStyle($srssb, $styleArray, 'bottom'); $this->addBorderStyle($srssb, $styleArray, 'left'); $this->addBorderStyle($srssb, $styleArray, 'right'); $this->addBorderDiagonal($srssb, $styleArray); } - if (isset($styleRegion->Style->HyperLink)) { + if (isset($style->Style->HyperLink)) { // TO DO - $hyperlink = $styleRegion->Style->HyperLink->attributes(); + $hyperlink = $style->Style->HyperLink->attributes(); } } $this->spreadsheet->getActiveSheet()->getStyle($cellRange)->applyFromArray($styleArray); From 5d6b072fb0557344d110cee8502822cccd6f2422 Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Sat, 8 May 2021 20:58:17 +0200 Subject: [PATCH 40/57] More Minor tweaks --- src/PhpSpreadsheet/Reader/Gnumeric/Styles.php | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php b/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php index 02166eb0..270f0728 100644 --- a/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php +++ b/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php @@ -101,21 +101,19 @@ class Styles private function readStyles(SimpleXMLElement $styleRegion, int $maxRow, int $maxCol): void { foreach ($styleRegion as $style) { + if ($style === null) { + continue; + } + $styleAttributes = $style->attributes(); if (($styleAttributes['startRow'] <= $maxRow) && ($styleAttributes['startCol'] <= $maxCol)) { - $startColumn = Coordinate::stringFromColumnIndex((int) $styleAttributes['startCol'] + 1); - $startRow = $styleAttributes['startRow'] + 1; - - $endColumn = ($styleAttributes['endCol'] > $maxCol) ? $maxCol : (int) $styleAttributes['endCol']; - $endColumn = Coordinate::stringFromColumnIndex($endColumn + 1); - - $endRow = 1 + (($styleAttributes['endRow'] > $maxRow) ? $maxRow : (int) $styleAttributes['endRow']); - $cellRange = $startColumn . $startRow . ':' . $endColumn . $endRow; + $cellRange = $this->readStyleRange($styleAttributes, $maxCol, $maxRow); $styleAttributes = $style->Style->attributes(); $styleArray = []; - // We still set the number format mask for date/time values, even if readDataOnly is true + // We still set the number format mask for date/time values, even if readDataOnly is true + // so that we can identify whether a float is a float or a date value $formatCode = (string) $styleAttributes['Format']; if (Date::isDateTimeFormatCode($formatCode)) { $styleArray['numberFormat']['formatCode'] = $formatCode; @@ -247,7 +245,7 @@ class Styles $styleArray['font']['color']['rgb'] = $RGB; $RGB = self::parseGnumericColour($styleAttributes['Back']); $shade = (string) $styleAttributes['Shade']; - if (($RGB != '000000') || ($shade != '0')) { + if (($RGB !== '000000') || ($shade !== '0')) { $RGB2 = self::parseGnumericColour($styleAttributes['PatternColor']); if ($shade === '1') { $styleArray['fill']['startColor']['rgb'] = $RGB; @@ -259,4 +257,18 @@ class Styles self::addStyle2($styleArray, 'fill', 'fillType', $shade); } } + + private function readStyleRange(?SimpleXMLElement $styleAttributes, int $maxCol, int $maxRow): string + { + $startColumn = Coordinate::stringFromColumnIndex((int) $styleAttributes['startCol'] + 1); + $startRow = $styleAttributes['startRow'] + 1; + + $endColumn = ($styleAttributes['endCol'] > $maxCol) ? $maxCol : (int) $styleAttributes['endCol']; + $endColumn = Coordinate::stringFromColumnIndex($endColumn + 1); + + $endRow = 1 + (($styleAttributes['endRow'] > $maxRow) ? $maxRow : (int) $styleAttributes['endRow']); + $cellRange = $startColumn . $startRow . ':' . $endColumn . $endRow; + + return $cellRange; + } } From 13ec1633337e657e80e1ca2063da90853e0cb1b5 Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Sat, 8 May 2021 21:04:13 +0200 Subject: [PATCH 41/57] phpstan appeasement --- src/PhpSpreadsheet/Reader/Gnumeric/Styles.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php b/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php index 270f0728..3ee57f41 100644 --- a/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php +++ b/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php @@ -106,7 +106,7 @@ class Styles } $styleAttributes = $style->attributes(); - if (($styleAttributes['startRow'] <= $maxRow) && ($styleAttributes['startCol'] <= $maxCol)) { + if ($styleAttributes !== null && ($styleAttributes['startRow'] <= $maxRow) && ($styleAttributes['startCol'] <= $maxCol)) { $cellRange = $this->readStyleRange($styleAttributes, $maxCol, $maxRow); $styleAttributes = $style->Style->attributes(); @@ -258,7 +258,7 @@ class Styles } } - private function readStyleRange(?SimpleXMLElement $styleAttributes, int $maxCol, int $maxRow): string + private function readStyleRange(SimpleXMLElement $styleAttributes, int $maxCol, int $maxRow): string { $startColumn = Coordinate::stringFromColumnIndex((int) $styleAttributes['startCol'] + 1); $startRow = $styleAttributes['startRow'] + 1; From 60e6a59ff2b132d77750d0f65db4296243ae2851 Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Sat, 8 May 2021 22:08:58 +0200 Subject: [PATCH 42/57] Additional refactoring --- src/PhpSpreadsheet/Reader/Gnumeric/Styles.php | 90 ++++++++++--------- 1 file changed, 48 insertions(+), 42 deletions(-) diff --git a/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php b/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php index 3ee57f41..301a8d9b 100644 --- a/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php +++ b/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php @@ -118,50 +118,10 @@ class Styles if (Date::isDateTimeFormatCode($formatCode)) { $styleArray['numberFormat']['formatCode'] = $formatCode; } - if (!$this->readDataOnly) { + if ($this->readDataOnly === false) { // If readDataOnly is false, we set all formatting information $styleArray['numberFormat']['formatCode'] = $formatCode; - - self::addStyle2($styleArray, 'alignment', 'horizontal', $styleAttributes['HAlign']); - self::addStyle2($styleArray, 'alignment', 'vertical', $styleAttributes['VAlign']); - $styleArray['alignment']['wrapText'] = $styleAttributes['WrapText'] == '1'; - $styleArray['alignment']['textRotation'] = $this->calcRotation($styleAttributes); - $styleArray['alignment']['shrinkToFit'] = $styleAttributes['ShrinkToFit'] == '1'; - $styleArray['alignment']['indent'] = ((int) ($styleAttributes['Indent']) > 0) ? $styleAttributes['indent'] : 0; - - $this->addColors($styleArray, $styleAttributes); - - $fontAttributes = $style->Style->Font->attributes(); - $styleArray['font']['name'] = (string) $style->Style->Font; - $styleArray['font']['size'] = (int) ($fontAttributes['Unit']); - $styleArray['font']['bold'] = $fontAttributes['Bold'] == '1'; - $styleArray['font']['italic'] = $fontAttributes['Italic'] == '1'; - $styleArray['font']['strikethrough'] = $fontAttributes['StrikeThrough'] == '1'; - self::addStyle2($styleArray, 'font', 'underline', $fontAttributes['Underline']); - - switch ($fontAttributes['Script']) { - case '1': - $styleArray['font']['superscript'] = true; - - break; - case '-1': - $styleArray['font']['subscript'] = true; - - break; - } - - if (isset($style->Style->StyleBorder)) { - $srssb = $style->Style->StyleBorder; - $this->addBorderStyle($srssb, $styleArray, 'top'); - $this->addBorderStyle($srssb, $styleArray, 'bottom'); - $this->addBorderStyle($srssb, $styleArray, 'left'); - $this->addBorderStyle($srssb, $styleArray, 'right'); - $this->addBorderDiagonal($srssb, $styleArray); - } - if (isset($style->Style->HyperLink)) { - // TO DO - $hyperlink = $style->Style->HyperLink->attributes(); - } + $styleArray = $this->readStyle($styleArray, $styleAttributes, $style); } $this->spreadsheet->getActiveSheet()->getStyle($cellRange)->applyFromArray($styleArray); } @@ -271,4 +231,50 @@ class Styles return $cellRange; } + + private function readStyle(array $styleArray, ?SimpleXMLElement $styleAttributes, SimpleXMLElement $style): array + { + self::addStyle2($styleArray, 'alignment', 'horizontal', $styleAttributes['HAlign']); + self::addStyle2($styleArray, 'alignment', 'vertical', $styleAttributes['VAlign']); + $styleArray['alignment']['wrapText'] = $styleAttributes['WrapText'] == '1'; + $styleArray['alignment']['textRotation'] = $this->calcRotation($styleAttributes); + $styleArray['alignment']['shrinkToFit'] = $styleAttributes['ShrinkToFit'] == '1'; + $styleArray['alignment']['indent'] = ((int) ($styleAttributes['Indent']) > 0) ? $styleAttributes['indent'] : 0; + + $this->addColors($styleArray, $styleAttributes); + + $fontAttributes = $style->Style->Font->attributes(); + $styleArray['font']['name'] = (string) $style->Style->Font; + $styleArray['font']['size'] = (int) ($fontAttributes['Unit']); + $styleArray['font']['bold'] = $fontAttributes['Bold'] == '1'; + $styleArray['font']['italic'] = $fontAttributes['Italic'] == '1'; + $styleArray['font']['strikethrough'] = $fontAttributes['StrikeThrough'] == '1'; + self::addStyle2($styleArray, 'font', 'underline', $fontAttributes['Underline']); + + switch ($fontAttributes['Script']) { + case '1': + $styleArray['font']['superscript'] = true; + + break; + case '-1': + $styleArray['font']['subscript'] = true; + + break; + } + + if (isset($style->Style->StyleBorder)) { + $srssb = $style->Style->StyleBorder; + $this->addBorderStyle($srssb, $styleArray, 'top'); + $this->addBorderStyle($srssb, $styleArray, 'bottom'); + $this->addBorderStyle($srssb, $styleArray, 'left'); + $this->addBorderStyle($srssb, $styleArray, 'right'); + $this->addBorderDiagonal($srssb, $styleArray); + } + if (isset($style->Style->HyperLink)) { + // TO DO + $hyperlink = $style->Style->HyperLink->attributes(); + } + + return $styleArray; + } } From bb572f757f8a786a8e12774e412a517b786a5b32 Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Sat, 8 May 2021 22:18:00 +0200 Subject: [PATCH 43/57] Should fix phpstan --- src/PhpSpreadsheet/Reader/Gnumeric/Styles.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php b/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php index 301a8d9b..5e9b5d83 100644 --- a/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php +++ b/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php @@ -118,7 +118,7 @@ class Styles if (Date::isDateTimeFormatCode($formatCode)) { $styleArray['numberFormat']['formatCode'] = $formatCode; } - if ($this->readDataOnly === false) { + if ($this->readDataOnly === false && $styleAttributes !== null) { // If readDataOnly is false, we set all formatting information $styleArray['numberFormat']['formatCode'] = $formatCode; $styleArray = $this->readStyle($styleArray, $styleAttributes, $style); @@ -232,7 +232,7 @@ class Styles return $cellRange; } - private function readStyle(array $styleArray, ?SimpleXMLElement $styleAttributes, SimpleXMLElement $style): array + private function readStyle(array $styleArray, SimpleXMLElement $styleAttributes, SimpleXMLElement $style): array { self::addStyle2($styleArray, 'alignment', 'horizontal', $styleAttributes['HAlign']); self::addStyle2($styleArray, 'alignment', 'vertical', $styleAttributes['VAlign']); From 9a5a630e3fd0f6c87444dbb5d188d63eb5194230 Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Sat, 8 May 2021 22:23:43 +0200 Subject: [PATCH 44/57] Check against font attributes --- src/PhpSpreadsheet/Reader/Gnumeric/Styles.php | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php b/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php index 5e9b5d83..9961632f 100644 --- a/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php +++ b/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php @@ -244,22 +244,24 @@ class Styles $this->addColors($styleArray, $styleAttributes); $fontAttributes = $style->Style->Font->attributes(); - $styleArray['font']['name'] = (string) $style->Style->Font; - $styleArray['font']['size'] = (int) ($fontAttributes['Unit']); - $styleArray['font']['bold'] = $fontAttributes['Bold'] == '1'; - $styleArray['font']['italic'] = $fontAttributes['Italic'] == '1'; - $styleArray['font']['strikethrough'] = $fontAttributes['StrikeThrough'] == '1'; - self::addStyle2($styleArray, 'font', 'underline', $fontAttributes['Underline']); + if ($fontAttributes !== null) { + $styleArray['font']['name'] = (string)$style->Style->Font; + $styleArray['font']['size'] = (int)($fontAttributes['Unit']); + $styleArray['font']['bold'] = $fontAttributes['Bold'] == '1'; + $styleArray['font']['italic'] = $fontAttributes['Italic'] == '1'; + $styleArray['font']['strikethrough'] = $fontAttributes['StrikeThrough'] == '1'; + self::addStyle2($styleArray, 'font', 'underline', $fontAttributes['Underline']); - switch ($fontAttributes['Script']) { - case '1': - $styleArray['font']['superscript'] = true; + switch ($fontAttributes['Script']) { + case '1': + $styleArray['font']['superscript'] = true; - break; - case '-1': - $styleArray['font']['subscript'] = true; + break; + case '-1': + $styleArray['font']['subscript'] = true; - break; + break; + } } if (isset($style->Style->StyleBorder)) { From 2ddb23574eb5961741ab24759225754fc3203785 Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Sat, 8 May 2021 22:39:22 +0200 Subject: [PATCH 45/57] PHPCS Fix --- src/PhpSpreadsheet/Reader/Gnumeric/Styles.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php b/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php index 9961632f..9998448e 100644 --- a/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php +++ b/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php @@ -245,8 +245,8 @@ class Styles $fontAttributes = $style->Style->Font->attributes(); if ($fontAttributes !== null) { - $styleArray['font']['name'] = (string)$style->Style->Font; - $styleArray['font']['size'] = (int)($fontAttributes['Unit']); + $styleArray['font']['name'] = (string) $style->Style->Font; + $styleArray['font']['size'] = (int) ($fontAttributes['Unit']); $styleArray['font']['bold'] = $fontAttributes['Bold'] == '1'; $styleArray['font']['italic'] = $fontAttributes['Italic'] == '1'; $styleArray['font']['strikethrough'] = $fontAttributes['StrikeThrough'] == '1'; From a0719d8dd4e1766c67085a3b24a6573d5d83c4e1 Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Sun, 9 May 2021 14:27:50 +0200 Subject: [PATCH 46/57] Use modification time from properties when saving Excel5 --- src/PhpSpreadsheet/Writer/Xls.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/PhpSpreadsheet/Writer/Xls.php b/src/PhpSpreadsheet/Writer/Xls.php index a1b477bf..4bece774 100644 --- a/src/PhpSpreadsheet/Writer/Xls.php +++ b/src/PhpSpreadsheet/Writer/Xls.php @@ -219,7 +219,8 @@ class Xls extends BaseWriter $arrRootData[] = $OLE_DocumentSummaryInformation; } - $root = new Root(time(), time(), $arrRootData); + $time = $this->spreadsheet->getProperties()->getModified(); + $root = new Root($time, $time, $arrRootData); // save the OLE file $this->openFileHandle($pFilename); $root->save($this->fileHandle); From e5bfc3c8992c332df800b7e2d72f77b30e8a2d1f Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Mon, 10 May 2021 22:44:07 +0200 Subject: [PATCH 47/57] Add phpcs version compatibility check to pipeline --- .github/workflows/main.yml | 31 +++++++++++++++ composer.json | 3 +- composer.lock | 79 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 109 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 87d933e2..754a463b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -124,6 +124,37 @@ jobs: - name: Code style with PHP_CodeSniffer run: ./vendor/bin/phpcs -q --report=checkstyle | cs2pr + versions: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup PHP, with composer and extensions + uses: shivammathur/setup-php@v2 + with: + php-version: 7.4 + extensions: ctype, dom, gd, iconv, fileinfo, libxml, mbstring, simplexml, xml, xmlreader, xmlwriter, zip, zlib + coverage: none + tools: cs2pr + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache composer dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer install --no-progress --prefer-dist --optimize-autoloader + + - name: Code Version Compatibility check with PHP_CodeSniffer + run: ./vendor/bin/phpcs -q --report-width=200 --report=summary,full src/ --standard=PHPCompatibility --runtime-set testVersion 7.2- + phpstan: runs-on: ubuntu-latest steps: diff --git a/composer.json b/composer.json index 6cd65021..314f21cd 100644 --- a/composer.json +++ b/composer.json @@ -53,7 +53,6 @@ }, "require": { "php": "^7.2 || ^8.0", - "ext-simplexml": "*", "ext-ctype": "*", "ext-dom": "*", "ext-fileinfo": "*", @@ -61,6 +60,7 @@ "ext-iconv": "*", "ext-libxml": "*", "ext-mbstring": "*", + "ext-simplexml": "*", "ext-xml": "*", "ext-xmlreader": "*", "ext-xmlwriter": "*", @@ -75,6 +75,7 @@ "psr/simple-cache": "^1.0" }, "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "dev-master", "dompdf/dompdf": "^1.0", "friendsofphp/php-cs-fixer": "^2.18", "jpgraph/jpgraph": "^4.0", diff --git a/composer.lock b/composer.lock index 3670f857..4921cc8f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "3be2673a6367d296c616bf9c34b77953", + "content-hash": "9158fcde13425499acaf0da201637737", "packages": [ { "name": "ezyang/htmlpurifier", @@ -802,6 +802,77 @@ ], "time": "2021-03-25T17:01:18+00:00" }, + { + "name": "dealerdirect/phpcodesniffer-composer-installer", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git", + "reference": "c960cf4629fab7155caca18c038ca7257b7595e3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/c960cf4629fab7155caca18c038ca7257b7595e3", + "reference": "c960cf4629fab7155caca18c038ca7257b7595e3", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0 || ^2.0", + "php": ">=5.3", + "squizlabs/php_codesniffer": "^2.0 || ^3.0 || ^4.0" + }, + "require-dev": { + "composer/composer": "*", + "enlightn/security-checker": "^1.2", + "phpcompatibility/php-compatibility": "^9.0" + }, + "default-branch": true, + "type": "composer-plugin", + "extra": { + "class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" + }, + "autoload": { + "psr-4": { + "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Franck Nijhof", + "email": "franck.nijhof@dealerdirect.com", + "homepage": "http://www.frenck.nl", + "role": "Developer / IT Manager" + } + ], + "description": "PHP_CodeSniffer Standards Composer Installer Plugin", + "homepage": "http://www.dealerdirect.com", + "keywords": [ + "PHPCodeSniffer", + "PHP_CodeSniffer", + "code quality", + "codesniffer", + "composer", + "installer", + "phpcs", + "plugin", + "qa", + "quality", + "standard", + "standards", + "style guide", + "stylecheck", + "tests" + ], + "support": { + "issues": "https://github.com/dealerdirect/phpcodesniffer-composer-installer/issues", + "source": "https://github.com/dealerdirect/phpcodesniffer-composer-installer" + }, + "time": "2021-03-14T13:49:41+00:00" + }, { "name": "doctrine/annotations", "version": "1.12.1", @@ -5065,12 +5136,13 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": { + "dealerdirect/phpcodesniffer-composer-installer": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { "php": "^7.2 || ^8.0", - "ext-simplexml": "*", "ext-ctype": "*", "ext-dom": "*", "ext-fileinfo": "*", @@ -5078,6 +5150,7 @@ "ext-iconv": "*", "ext-libxml": "*", "ext-mbstring": "*", + "ext-simplexml": "*", "ext-xml": "*", "ext-xmlreader": "*", "ext-xmlwriter": "*", From bb11378fca2e8026670ab35b8c6bb53e2c6aef9c Mon Sep 17 00:00:00 2001 From: xandros15 Date: Tue, 11 May 2021 12:44:40 +0200 Subject: [PATCH 48/57] #984 fix php-cs-fixer warnings --- src/PhpSpreadsheet/Style/Conditional.php | 6 +----- .../Reader/Xlsx/ConditionalTest.php | 13 ++++++------- .../Writer/Xlsx/ConditionalTest.php | 6 +++--- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/PhpSpreadsheet/Style/Conditional.php b/src/PhpSpreadsheet/Style/Conditional.php index 83520722..4cbc2746 100644 --- a/src/PhpSpreadsheet/Style/Conditional.php +++ b/src/PhpSpreadsheet/Style/Conditional.php @@ -314,11 +314,7 @@ class Conditional implements IComparable } /** - * Verify if param is valid condition type - * - * @param string $type - * - * @return bool + * Verify if param is valid condition type. */ public static function isValidConditionType(string $type): bool { diff --git a/tests/PhpSpreadsheetTests/Reader/Xlsx/ConditionalTest.php b/tests/PhpSpreadsheetTests/Reader/Xlsx/ConditionalTest.php index 6ee7bc37..8108d988 100644 --- a/tests/PhpSpreadsheetTests/Reader/Xlsx/ConditionalTest.php +++ b/tests/PhpSpreadsheetTests/Reader/Xlsx/ConditionalTest.php @@ -8,9 +8,8 @@ use PhpOffice\PhpSpreadsheetTests\Functional\AbstractFunctional; class ConditionalTest extends AbstractFunctional { - /** - * Test check if conditional style with type 'notContainsText' works on xlsx + * Test check if conditional style with type 'notContainsText' works on xlsx. */ public function testConditionalNotContainsText(): void { @@ -20,12 +19,12 @@ class ConditionalTest extends AbstractFunctional $worksheet = $spreadsheet->getActiveSheet(); $styles = $worksheet->getConditionalStyles('A1:A5'); - $this->assertCount(1, $styles); + self::assertCount(1, $styles); - /** @var $notContainsTextStyle Conditional */ + /** @var Conditional $notContainsTextStyle */ $notContainsTextStyle = $styles[0]; - $this->assertEquals('A', $notContainsTextStyle->getText()); - $this->assertEquals(Conditional::CONDITION_NOTCONTAINSTEXT, $notContainsTextStyle->getConditionType()); - $this->assertEquals(Conditional::OPERATOR_NOTCONTAINS, $notContainsTextStyle->getOperatorType()); + self::assertEquals('A', $notContainsTextStyle->getText()); + self::assertEquals(Conditional::CONDITION_NOTCONTAINSTEXT, $notContainsTextStyle->getConditionType()); + self::assertEquals(Conditional::OPERATOR_NOTCONTAINS, $notContainsTextStyle->getOperatorType()); } } diff --git a/tests/PhpSpreadsheetTests/Writer/Xlsx/ConditionalTest.php b/tests/PhpSpreadsheetTests/Writer/Xlsx/ConditionalTest.php index 1bcad0d1..21df6ed6 100644 --- a/tests/PhpSpreadsheetTests/Writer/Xlsx/ConditionalTest.php +++ b/tests/PhpSpreadsheetTests/Writer/Xlsx/ConditionalTest.php @@ -30,7 +30,7 @@ class ConditionalTest extends AbstractFunctional } /** - * Test check if conditional style with type 'notContainsText' works on xlsx + * Test check if conditional style with type 'notContainsText' works on xlsx. */ public function testConditionalNotContainsText(): void { @@ -40,7 +40,7 @@ class ConditionalTest extends AbstractFunctional $condition = new Conditional(); $condition->setConditionType(Conditional::CONDITION_NOTCONTAINSTEXT); $condition->setOperatorType(Conditional::OPERATOR_NOTCONTAINS); - $condition->setText("C"); + $condition->setText('C'); $condition->getStyle()->applyFromArray([ 'fill' => [ 'color' => ['argb' => 'FFFFC000'], @@ -55,6 +55,6 @@ class ConditionalTest extends AbstractFunctional $needle = <<ISERROR(SEARCH("C",A1:A5)) xml; - $this->assertStringContainsString($needle, $data); + self::assertStringContainsString($needle, $data); } } From 9c43d5f1b7426dbd38274ef27aeb6d50b00acfdd Mon Sep 17 00:00:00 2001 From: Owen Leibman Date: Mon, 10 May 2021 21:35:34 -0700 Subject: [PATCH 49/57] Xlsx Writer Formula with Bool Result of False Fix for #2082. Xlsx Writer was writing a cell which is a formula which evaluates to boolean false as an empty XML tag. This is okay for Excel 365, but not for Excel 2016-. Change to write the tag as a value of 0 instead, which works for all Excel releases. Add test. --- src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php | 1 + .../Writer/Xlsx/Issue2082Test.php | 40 +++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 tests/PhpSpreadsheetTests/Writer/Xlsx/Issue2082Test.php diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php index 3978eb6f..9b1d6730 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php @@ -1230,6 +1230,7 @@ class Worksheet extends WriterPart $objWriter->writeAttribute('t', 'str'); } elseif (is_bool($calculatedValue)) { $objWriter->writeAttribute('t', 'b'); + $calculatedValue = (int) $calculatedValue; } // array values are not yet supported //$attributes = $pCell->getFormulaAttributes(); diff --git a/tests/PhpSpreadsheetTests/Writer/Xlsx/Issue2082Test.php b/tests/PhpSpreadsheetTests/Writer/Xlsx/Issue2082Test.php new file mode 100644 index 00000000..1f72a382 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Writer/Xlsx/Issue2082Test.php @@ -0,0 +1,40 @@ +getActiveSheet(); + $worksheet->fromArray(['A', 'B', 'C', 'D']); + $worksheet->getCell('A2')->setValue('=A1<>"A"'); + $worksheet->getCell('A3')->setValue('=A1="A"'); + $worksheet->getCell('B2')->setValue('=LEFT(B1, 0)'); + $worksheet->getCell('B3')->setValue('=B2=""'); + + $writer = new Writer($spreadsheet); + $writer->save($outputFilename); + $zipfile = "zip://$outputFilename#xl/worksheets/sheet1.xml"; + $contents = file_get_contents($zipfile); + unlink($outputFilename); + if ($contents === false) { + self::fail('Unable to open file'); + } else { + self::assertStringContainsString('A1<>"A"0', $contents); + self::assertStringContainsString('A1="A"1', $contents); + self::assertStringContainsString('LEFT(B1, 0)', $contents); + self::assertStringContainsString('B2=""1', $contents); + } + } +} From d08653433c39feb5337b1c0182fee7475fd7c643 Mon Sep 17 00:00:00 2001 From: Tanguy De Taxis Date: Tue, 11 May 2021 10:09:55 +0200 Subject: [PATCH 50/57] fr locale - Add JOURS function (DAYS equivalent) --- src/PhpSpreadsheet/Calculation/locale/fr/functions | 1 + 1 file changed, 1 insertion(+) diff --git a/src/PhpSpreadsheet/Calculation/locale/fr/functions b/src/PhpSpreadsheet/Calculation/locale/fr/functions index 7f40d5fd..b28b4a70 100644 --- a/src/PhpSpreadsheet/Calculation/locale/fr/functions +++ b/src/PhpSpreadsheet/Calculation/locale/fr/functions @@ -47,6 +47,7 @@ DVARP = BDVARP ## Calcule la variance pour l’ensemble d’une population d DATE = DATE ## Renvoie le numéro de série d’une date précise. DATEVALUE = DATEVAL ## Convertit une date représentée sous forme de texte en numéro de série. DAY = JOUR ## Convertit un numéro de série en jour du mois. +DAYS = JOURS ## Calcule le nombre de jours qui séparent deux dates. DAYS360 = JOURS360 ## Calcule le nombre de jours qui séparent deux dates sur la base d’une année de 360 jours. EDATE = MOIS.DECALER ## Renvoie le numéro séquentiel de la date qui représente une date spécifiée (l’argument date_départ), corrigée en plus ou en moins du nombre de mois indiqué. EOMONTH = FIN.MOIS ## Renvoie le numéro séquentiel de la date du dernier jour du mois précédant ou suivant la date_départ du nombre de mois indiqué. From 790382747506113f2a47f6b155e3c83d703afecc Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Wed, 12 May 2021 09:55:01 +0200 Subject: [PATCH 51/57] Deprecation notice in ChangeLog for the old Excel funcion implementations --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aac3a32d..de7283bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,7 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). ### Deprecated -- Nothing. +- All Excel Function implementations in `Calculation\Database`, `Calculation\DateTime`, `Calculation\Engineering`, `Calculation\Financial`, `Calculation\Logical`, `Calculation\LookupRef`, `Calculation\MathTrig`, `Calculation\Statistical`, `Calculation\TextData` and `Calculation\Web` have been moved to dedicated classes for individual functions or groups of related functions. See the docblocks against all the deprecated methods for details of the new methods to call instead. At some point, these old classes will be deleted. ### Removed From cd667500e086b1e4fdcc4001f5cc1a4eb4a9fbd4 Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Wed, 12 May 2021 13:25:28 +0200 Subject: [PATCH 52/57] Group some of the newly extracted Excel DateTime function implementations into groups of related functions with appropriate and meaningful class names, and rename the public methods to be more descriptive of their purpose --- .../Calculation/Calculation.php | 22 +-- src/PhpSpreadsheet/Calculation/DateTime.php | 66 ++++----- .../DateTimeExcel/{Now.php => Current.php} | 29 +++- .../Calculation/DateTimeExcel/DateParts.php | 127 +++++++++++++++++ .../Calculation/DateTimeExcel/Day.php | 65 --------- .../Calculation/DateTimeExcel/Hour.php | 44 ------ .../Calculation/DateTimeExcel/IsoWeekNum.php | 59 -------- .../Calculation/DateTimeExcel/Minute.php | 44 ------ .../Calculation/DateTimeExcel/Month.php | 40 ------ .../Calculation/DateTimeExcel/NetworkDays.php | 6 +- .../Calculation/DateTimeExcel/Second.php | 44 ------ .../Calculation/DateTimeExcel/TimeParts.php | 112 +++++++++++++++ .../Calculation/DateTimeExcel/Today.php | 34 ----- .../DateTimeExcel/{WeekNum.php => Week.php} | 132 +++++++++++++++++- .../Calculation/DateTimeExcel/WeekDay.php | 83 ----------- .../Calculation/DateTimeExcel/WorkDay.php | 2 +- .../Calculation/DateTimeExcel/Year.php | 40 ------ .../Calculation/DateTimeExcel/YearFrac.php | 14 +- .../Calculation/Financial/Amortization.php | 2 +- .../Calculation/Financial/Coupons.php | 6 +- .../Financial/Securities/Price.php | 2 +- .../Financial/Securities/Yields.php | 4 +- .../Calculation/Financial/TreasuryBill.php | 6 +- .../Functions/DateTime/WeekDayTest.php | 8 +- 24 files changed, 463 insertions(+), 528 deletions(-) rename src/PhpSpreadsheet/Calculation/DateTimeExcel/{Now.php => Current.php} (51%) create mode 100644 src/PhpSpreadsheet/Calculation/DateTimeExcel/DateParts.php delete mode 100644 src/PhpSpreadsheet/Calculation/DateTimeExcel/Day.php delete mode 100644 src/PhpSpreadsheet/Calculation/DateTimeExcel/Hour.php delete mode 100644 src/PhpSpreadsheet/Calculation/DateTimeExcel/IsoWeekNum.php delete mode 100644 src/PhpSpreadsheet/Calculation/DateTimeExcel/Minute.php delete mode 100644 src/PhpSpreadsheet/Calculation/DateTimeExcel/Month.php delete mode 100644 src/PhpSpreadsheet/Calculation/DateTimeExcel/Second.php create mode 100644 src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeParts.php delete mode 100644 src/PhpSpreadsheet/Calculation/DateTimeExcel/Today.php rename src/PhpSpreadsheet/Calculation/DateTimeExcel/{WeekNum.php => Week.php} (55%) delete mode 100644 src/PhpSpreadsheet/Calculation/DateTimeExcel/WeekDay.php delete mode 100644 src/PhpSpreadsheet/Calculation/DateTimeExcel/Year.php diff --git a/src/PhpSpreadsheet/Calculation/Calculation.php b/src/PhpSpreadsheet/Calculation/Calculation.php index 54d9a6e8..52dd290c 100644 --- a/src/PhpSpreadsheet/Calculation/Calculation.php +++ b/src/PhpSpreadsheet/Calculation/Calculation.php @@ -778,7 +778,7 @@ class Calculation ], 'DAY' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\Day::class, 'evaluate'], + 'functionCall' => [DateTimeExcel\DateParts::class, 'day'], 'argumentCount' => '1', ], 'DAYS' => [ @@ -1240,7 +1240,7 @@ class Calculation ], 'HOUR' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\Hour::class, 'evaluate'], + 'functionCall' => [DateTimeExcel\TimeParts::class, 'hour'], 'argumentCount' => '1', ], 'HYPERLINK' => [ @@ -1504,7 +1504,7 @@ class Calculation ], 'ISOWEEKNUM' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\IsoWeekNum::class, 'evaluate'], + 'functionCall' => [DateTimeExcel\Week::class, 'isoWeekNumber'], 'argumentCount' => '1', ], 'ISPMT' => [ @@ -1684,7 +1684,7 @@ class Calculation ], 'MINUTE' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\Minute::class, 'evaluate'], + 'functionCall' => [DateTimeExcel\TimeParts::class, 'minute'], 'argumentCount' => '1', ], 'MINVERSE' => [ @@ -1724,7 +1724,7 @@ class Calculation ], 'MONTH' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\Month::class, 'evaluate'], + 'functionCall' => [DateTimeExcel\DateParts::class, 'month'], 'argumentCount' => '1', ], 'MROUND' => [ @@ -1824,7 +1824,7 @@ class Calculation ], 'NOW' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\Now::class, 'evaluate'], + 'functionCall' => [DateTimeExcel\Current::class, 'now'], 'argumentCount' => '0', ], 'NPER' => [ @@ -2178,7 +2178,7 @@ class Calculation ], 'SECOND' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\Second::class, 'evaluate'], + 'functionCall' => [DateTimeExcel\TimeParts::class, 'second'], 'argumentCount' => '1', ], 'SEQUENCE' => [ @@ -2449,7 +2449,7 @@ class Calculation ], 'TODAY' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\Today::class, 'evaluate'], + 'functionCall' => [DateTimeExcel\Current::class, 'today'], 'argumentCount' => '0', ], 'TRANSPOSE' => [ @@ -2574,12 +2574,12 @@ class Calculation ], 'WEEKDAY' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\WeekDay::class, 'evaluate'], + 'functionCall' => [DateTimeExcel\Week::class, 'day'], 'argumentCount' => '1,2', ], 'WEEKNUM' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\WeekNum::class, 'evaluate'], + 'functionCall' => [DateTimeExcel\Week::class, 'number'], 'argumentCount' => '1,2', ], 'WEIBULL' => [ @@ -2629,7 +2629,7 @@ class Calculation ], 'YEAR' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\Year::class, 'evaluate'], + 'functionCall' => [DateTimeExcel\DateParts::class, 'year'], 'argumentCount' => '1', ], 'YEARFRAC' => [ diff --git a/src/PhpSpreadsheet/Calculation/DateTime.php b/src/PhpSpreadsheet/Calculation/DateTime.php index 4959c957..a81190d4 100644 --- a/src/PhpSpreadsheet/Calculation/DateTime.php +++ b/src/PhpSpreadsheet/Calculation/DateTime.php @@ -63,15 +63,15 @@ class DateTime * * @Deprecated 1.18.0 * - * @See DateTimeExcel\Now::evaluate() - * Use the evaluate method in the DateTimeExcel\Now class instead + * @See DateTimeExcel\Current::now() + * Use the now method in the DateTimeExcel\Current class instead * * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object, * depending on the value of the ReturnDateType flag */ public static function DATETIMENOW() { - return DateTimeExcel\Now::evaluate(); + return DateTimeExcel\Current::now(); } /** @@ -90,15 +90,15 @@ class DateTime * * @Deprecated 1.18.0 * - * @See DateTimeExcel\Today::evaluate() - * Use the evaluate method in the DateTimeExcel\Today class instead + * @See DateTimeExcel\Current::today() + * Use the today method in the DateTimeExcel\Current class instead * * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object, * depending on the value of the ReturnDateType flag */ public static function DATENOW() { - return DateTimeExcel\Today::evaluate(); + return DateTimeExcel\Current::today(); } /** @@ -458,8 +458,8 @@ class DateTime * * @Deprecated 1.18.0 * - * @See DateTimeExcel\Day::evaluate() - * Use the evaluate method in the DateTimeExcel\Day class instead + * @See DateTimeExcel\DateParts::day() + * Use the day method in the DateTimeExcel\DateParts class instead * * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer), * PHP DateTime object, or a standard date string @@ -468,7 +468,7 @@ class DateTime */ public static function DAYOFMONTH($dateValue = 1) { - return DateTimeExcel\Day::evaluate($dateValue); + return DateTimeExcel\DateParts::day($dateValue); } /** @@ -482,8 +482,8 @@ class DateTime * * @Deprecated 1.18.0 * - * @See DateTimeExcel\WeekDay::evaluate() - * Use the evaluate method in the DateTimeExcel\WeekDay class instead + * @See DateTimeExcel\Week::day() + * Use the day method in the DateTimeExcel\Week class instead * * @param float|int|string $dateValue Excel date serial value (float), PHP date timestamp (integer), * PHP DateTime object, or a standard date string @@ -496,7 +496,7 @@ class DateTime */ public static function WEEKDAY($dateValue = 1, $style = 1) { - return DateTimeExcel\WeekDay::evaluate($dateValue, $style); + return DateTimeExcel\Week::day($dateValue, $style); } /** @@ -687,8 +687,8 @@ class DateTime * * @Deprecated 1.18.0 * - * @See DateTimeExcel\WeekNum::evaluate() - * Use the evaluate method in the DateTimeExcel\WeekNum class instead + * @See DateTimeExcel\Week::number(() + * Use the number method in the DateTimeExcel\Week class instead * * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer), * PHP DateTime object, or a standard date string @@ -708,7 +708,7 @@ class DateTime */ public static function WEEKNUM($dateValue = 1, $method = self::STARTWEEK_SUNDAY) { - return DateTimeExcel\WeekNum::evaluate($dateValue, $method); + return DateTimeExcel\Week::number($dateValue, $method); } /** @@ -721,8 +721,8 @@ class DateTime * * @Deprecated 1.18.0 * - * @See DateTimeExcel\IsoWeekNum::evaluate() - * Use the evaluate method in the DateTimeExcel\IsoWeekNum class instead + * @See DateTimeExcel\Week::isoWeekNumber() + * Use the isoWeekNumber method in the DateTimeExcel\Week class instead * * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer), * PHP DateTime object, or a standard date string @@ -731,7 +731,7 @@ class DateTime */ public static function ISOWEEKNUM($dateValue = 1) { - return DateTimeExcel\IsoWeekNum::evaluate($dateValue); + return DateTimeExcel\Week::isoWeekNumber($dateValue); } /** @@ -745,8 +745,8 @@ class DateTime * * @Deprecated 1.18.0 * - * @See DateTimeExcel\Month::evaluate() - * Use the evaluate method in the DateTimeExcel\Month class instead + * @See DateTimeExcel\DateParts::month() + * Use the month method in the DateTimeExcel\DateParts class instead * * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer), * PHP DateTime object, or a standard date string @@ -755,7 +755,7 @@ class DateTime */ public static function MONTHOFYEAR($dateValue = 1) { - return DateTimeExcel\Month::evaluate($dateValue); + return DateTimeExcel\DateParts::month($dateValue); } /** @@ -769,8 +769,8 @@ class DateTime * * @Deprecated 1.18.0 * - * @See DateTimeExcel\Year::evaluate() - * Use the evaluate method in the DateTimeExcel\Year class instead + * @See DateTimeExcel\DateParts::year() + * Use the ear method in the DateTimeExcel\DateParts class instead * * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer), * PHP DateTime object, or a standard date string @@ -779,7 +779,7 @@ class DateTime */ public static function YEAR($dateValue = 1) { - return DateTimeExcel\Year::evaluate($dateValue); + return DateTimeExcel\DateParts::year($dateValue); } /** @@ -793,8 +793,8 @@ class DateTime * * @Deprecated 1.18.0 * - * @See DateTimeExcel\Hour::evaluate() - * Use the evaluate method in the DateTimeExcel\Hour class instead + * @See DateTimeExcel\TimeParts::hour() + * Use the hour method in the DateTimeExcel\TimeParts class instead * * @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer), * PHP DateTime object, or a standard time string @@ -803,7 +803,7 @@ class DateTime */ public static function HOUROFDAY($timeValue = 0) { - return DateTimeExcel\Hour::evaluate($timeValue); + return DateTimeExcel\TimeParts::hour($timeValue); } /** @@ -817,8 +817,8 @@ class DateTime * * @Deprecated 1.18.0 * - * @See DateTimeExcel\Minute::evaluate() - * Use the evaluate method in the DateTimeExcel\Minute class instead + * @See DateTimeExcel\TimeParts::minute() + * Use the minute method in the DateTimeExcel\TimeParts class instead * * @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer), * PHP DateTime object, or a standard time string @@ -827,7 +827,7 @@ class DateTime */ public static function MINUTE($timeValue = 0) { - return DateTimeExcel\Minute::evaluate($timeValue); + return DateTimeExcel\TimeParts::minute($timeValue); } /** @@ -841,8 +841,8 @@ class DateTime * * @Deprecated 1.18.0 * - * @See DateTimeExcel\Second::evaluate() - * Use the evaluate method in the DateTimeExcel\Second class instead + * @See DateTimeExcel\TimeParts::second() + * Use the second method in the DateTimeExcel\TimeParts class instead * * @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer), * PHP DateTime object, or a standard time string @@ -851,7 +851,7 @@ class DateTime */ public static function SECOND($timeValue = 0) { - return DateTimeExcel\Second::evaluate($timeValue); + return DateTimeExcel\TimeParts::second($timeValue); } /** diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Now.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Current.php similarity index 51% rename from src/PhpSpreadsheet/Calculation/DateTimeExcel/Now.php rename to src/PhpSpreadsheet/Calculation/DateTimeExcel/Current.php index a0dea86f..7a70978e 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Now.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Current.php @@ -5,8 +5,33 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel; use DateTimeImmutable; use PhpOffice\PhpSpreadsheet\Calculation\Functions; -class Now +class Current { + /** + * DATENOW. + * + * Returns the current date. + * The NOW function is useful when you need to display the current date and time on a worksheet or + * calculate a value based on the current date and time, and have that value updated each time you + * open the worksheet. + * + * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date + * and time format of your regional settings. PhpSpreadsheet does not change cell formatting in this way. + * + * Excel Function: + * TODAY() + * + * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object, + * depending on the value of the ReturnDateType flag + */ + public static function today() + { + $dti = new DateTimeImmutable(); + $dateArray = date_parse($dti->format('c')); + + return is_array($dateArray) ? Helpers::returnIn3FormatsArray($dateArray, true) : Functions::VALUE(); + } + /** * DATETIMENOW. * @@ -24,7 +49,7 @@ class Now * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object, * depending on the value of the ReturnDateType flag */ - public static function evaluate() + public static function now() { $dti = new DateTimeImmutable(); $dateArray = date_parse($dti->format('c')); diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateParts.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateParts.php new file mode 100644 index 00000000..f1ada435 --- /dev/null +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateParts.php @@ -0,0 +1,127 @@ += 0) { + return $weirdResult; + } + + try { + $dateValue = Helpers::getDateValue($dateValue); + } catch (Exception $e) { + return $e->getMessage(); + } + + // Execute function + $PHPDateObject = Date::excelToDateTimeObject($dateValue); + + return (int) $PHPDateObject->format('j'); + } + + /** + * MONTHOFYEAR. + * + * Returns the month of a date represented by a serial number. + * The month is given as an integer, ranging from 1 (January) to 12 (December). + * + * Excel Function: + * MONTH(dateValue) + * + * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer), + * PHP DateTime object, or a standard date string + * + * @return int|string Month of the year + */ + public static function month($dateValue) + { + try { + $dateValue = Helpers::getDateValue($dateValue); + } catch (Exception $e) { + return $e->getMessage(); + } + if ($dateValue < 1 && Date::getExcelCalendar() === DATE::CALENDAR_WINDOWS_1900) { + return 1; + } + + // Execute function + $PHPDateObject = Date::excelToDateTimeObject($dateValue); + + return (int) $PHPDateObject->format('n'); + } + + /** + * YEAR. + * + * Returns the year corresponding to a date. + * The year is returned as an integer in the range 1900-9999. + * + * Excel Function: + * YEAR(dateValue) + * + * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer), + * PHP DateTime object, or a standard date string + * + * @return int|string Year + */ + public static function year($dateValue) + { + try { + $dateValue = Helpers::getDateValue($dateValue); + } catch (Exception $e) { + return $e->getMessage(); + } + + if ($dateValue < 1 && Date::getExcelCalendar() === DATE::CALENDAR_WINDOWS_1900) { + return 1900; + } + // Execute function + $PHPDateObject = Date::excelToDateTimeObject($dateValue); + + return (int) $PHPDateObject->format('Y'); + } + + /** + * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer), + * PHP DateTime object, or a standard date string + */ + private static function weirdCondition($dateValue): int + { + // Excel does not treat 0 consistently for DAY vs. (MONTH or YEAR) + if (Date::getExcelCalendar() === DATE::CALENDAR_WINDOWS_1900 && Functions::getCompatibilityMode() == Functions::COMPATIBILITY_EXCEL) { + if (is_bool($dateValue)) { + return (int) $dateValue; + } + if ($dateValue === null) { + return 0; + } + if (is_numeric($dateValue) && $dateValue < 1 && $dateValue >= 0) { + return 0; + } + } + + return -1; + } +} diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Day.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Day.php deleted file mode 100644 index bc57644d..00000000 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Day.php +++ /dev/null @@ -1,65 +0,0 @@ -= 0) { - return $weirdResult; - } - - try { - $dateValue = Helpers::getDateValue($dateValue); - } catch (Exception $e) { - return $e->getMessage(); - } - - // Execute function - $PHPDateObject = Date::excelToDateTimeObject($dateValue); - - return (int) $PHPDateObject->format('j'); - } - - /** - * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer), - * PHP DateTime object, or a standard date string - */ - private static function weirdCondition($dateValue): int - { - // Excel does not treat 0 consistently for DAY vs. (MONTH or YEAR) - if (Date::getExcelCalendar() === DATE::CALENDAR_WINDOWS_1900 && Functions::getCompatibilityMode() == Functions::COMPATIBILITY_EXCEL) { - if (is_bool($dateValue)) { - return (int) $dateValue; - } - if ($dateValue === null) { - return 0; - } - if (is_numeric($dateValue) && $dateValue < 1 && $dateValue >= 0) { - return 0; - } - } - - return -1; - } -} diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Hour.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Hour.php deleted file mode 100644 index 84869036..00000000 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Hour.php +++ /dev/null @@ -1,44 +0,0 @@ -getMessage(); - } - - // Execute function - $timeValue = fmod($timeValue, 1); - $timeValue = Date::excelToDateTimeObject($timeValue); - - return (int) $timeValue->format('H'); - } -} diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/IsoWeekNum.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/IsoWeekNum.php deleted file mode 100644 index 7d2d65dc..00000000 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/IsoWeekNum.php +++ /dev/null @@ -1,59 +0,0 @@ -getMessage(); - } - - // Execute function - $PHPDateObject = Date::excelToDateTimeObject($dateValue); - Helpers::silly1900($PHPDateObject); - - return (int) $PHPDateObject->format('W'); - } - - /** - * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer), - * PHP DateTime object, or a standard date string - */ - private static function apparentBug($dateValue): bool - { - if (Date::getExcelCalendar() !== DATE::CALENDAR_MAC_1904) { - if (is_bool($dateValue)) { - return true; - } - if (is_numeric($dateValue) && !((int) $dateValue)) { - return true; - } - } - - return false; - } -} diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Minute.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Minute.php deleted file mode 100644 index 1a9d5c21..00000000 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Minute.php +++ /dev/null @@ -1,44 +0,0 @@ -getMessage(); - } - - // Execute function - $timeValue = fmod($timeValue, 1); - $timeValue = Date::excelToDateTimeObject($timeValue); - - return (int) $timeValue->format('i'); - } -} diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Month.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Month.php deleted file mode 100644 index 092370ba..00000000 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Month.php +++ /dev/null @@ -1,40 +0,0 @@ -getMessage(); - } - if ($dateValue < 1 && Date::getExcelCalendar() === DATE::CALENDAR_WINDOWS_1900) { - return 1; - } - - // Execute function - $PHPDateObject = Date::excelToDateTimeObject($dateValue); - - return (int) $PHPDateObject->format('n'); - } -} diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/NetworkDays.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/NetworkDays.php index 5c586913..af6eda5c 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/NetworkDays.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/NetworkDays.php @@ -56,7 +56,7 @@ class NetworkDays $holidayCountedArray = []; foreach ($holidayArray as $holidayDate) { if (($holidayDate >= $startDate) && ($holidayDate <= $endDate)) { - if ((WeekDay::evaluate($holidayDate, 2) < 6) && (!in_array($holidayDate, $holidayCountedArray))) { + if ((Week::day($holidayDate, 2) < 6) && (!in_array($holidayDate, $holidayCountedArray))) { --$partWeekDays; $holidayCountedArray[] = $holidayDate; } @@ -68,7 +68,7 @@ class NetworkDays private static function calcStartDow(float $startDate): int { - $startDow = 6 - (int) WeekDay::evaluate($startDate, 2); + $startDow = 6 - (int) Week::day($startDate, 2); if ($startDow < 0) { $startDow = 5; } @@ -78,7 +78,7 @@ class NetworkDays private static function calcEndDow(float $endDate): int { - $endDow = (int) WeekDay::evaluate($endDate, 2); + $endDow = (int) Week::day($endDate, 2); if ($endDow >= 6) { $endDow = 0; } diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Second.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Second.php deleted file mode 100644 index c3e15d86..00000000 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Second.php +++ /dev/null @@ -1,44 +0,0 @@ -getMessage(); - } - - // Execute function - $timeValue = fmod($timeValue, 1); - $timeValue = Date::excelToDateTimeObject($timeValue); - - return (int) $timeValue->format('s'); - } -} diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeParts.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeParts.php new file mode 100644 index 00000000..3e07da08 --- /dev/null +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeParts.php @@ -0,0 +1,112 @@ +getMessage(); + } + + // Execute function + $timeValue = fmod($timeValue, 1); + $timeValue = Date::excelToDateTimeObject($timeValue); + + return (int) $timeValue->format('H'); + } + + /** + * MINUTE. + * + * Returns the minutes of a time value. + * The minute is given as an integer, ranging from 0 to 59. + * + * Excel Function: + * MINUTE(timeValue) + * + * @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer), + * PHP DateTime object, or a standard time string + * + * @return int|string Minute + */ + public static function minute($timeValue) + { + try { + $timeValue = Functions::flattenSingleValue($timeValue); + Helpers::nullFalseTrueToNumber($timeValue); + if (!is_numeric($timeValue)) { + $timeValue = Helpers::getTimeValue($timeValue); + } + Helpers::validateNotNegative($timeValue); + } catch (Exception $e) { + return $e->getMessage(); + } + + // Execute function + $timeValue = fmod($timeValue, 1); + $timeValue = Date::excelToDateTimeObject($timeValue); + + return (int) $timeValue->format('i'); + } + + /** + * SECOND. + * + * Returns the seconds of a time value. + * The minute is given as an integer, ranging from 0 to 59. + * + * Excel Function: + * SECOND(timeValue) + * + * @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer), + * PHP DateTime object, or a standard time string + * + * @return int|string Second + */ + public static function second($timeValue) + { + try { + $timeValue = Functions::flattenSingleValue($timeValue); + Helpers::nullFalseTrueToNumber($timeValue); + if (!is_numeric($timeValue)) { + $timeValue = Helpers::getTimeValue($timeValue); + } + Helpers::validateNotNegative($timeValue); + } catch (Exception $e) { + return $e->getMessage(); + } + + // Execute function + $timeValue = fmod($timeValue, 1); + $timeValue = Date::excelToDateTimeObject($timeValue); + + return (int) $timeValue->format('s'); + } +} diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Today.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Today.php deleted file mode 100644 index 545f0efb..00000000 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Today.php +++ /dev/null @@ -1,34 +0,0 @@ -format('c')); - - return is_array($dateArray) ? Helpers::returnIn3FormatsArray($dateArray, true) : Functions::VALUE(); - } -} diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/WeekNum.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Week.php similarity index 55% rename from src/PhpSpreadsheet/Calculation/DateTimeExcel/WeekNum.php rename to src/PhpSpreadsheet/Calculation/DateTimeExcel/Week.php index 35634e4e..928bfc14 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/WeekNum.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Week.php @@ -3,11 +3,11 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel; use DateTime; -use Exception; +use PhpOffice\PhpSpreadsheet\Calculation\Exception; use PhpOffice\PhpSpreadsheet\Calculation\Functions; use PhpOffice\PhpSpreadsheet\Shared\Date; -class WeekNum +class Week { /** * WEEKNUM. @@ -38,7 +38,7 @@ class WeekNum * * @return int|string Week Number */ - public static function evaluate($dateValue, $method = Constants::STARTWEEK_SUNDAY) + public static function number($dateValue, $method = Constants::STARTWEEK_SUNDAY) { $origDateValueNull = empty($dateValue); @@ -78,6 +78,129 @@ class WeekNum return (int) $weekOfYear; } + /** + * ISOWEEKNUM. + * + * Returns the ISO 8601 week number of the year for a specified date. + * + * Excel Function: + * ISOWEEKNUM(dateValue) + * + * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer), + * PHP DateTime object, or a standard date string + * + * @return int|string Week Number + */ + public static function isoWeekNumber($dateValue) + { + if (self::apparentBug($dateValue)) { + return 52; + } + + try { + $dateValue = Helpers::getDateValue($dateValue); + } catch (Exception $e) { + return $e->getMessage(); + } + + // Execute function + $PHPDateObject = Date::excelToDateTimeObject($dateValue); + Helpers::silly1900($PHPDateObject); + + return (int) $PHPDateObject->format('W'); + } + + /** + * WEEKDAY. + * + * Returns the day of the week for a specified date. The day is given as an integer + * ranging from 0 to 7 (dependent on the requested style). + * + * Excel Function: + * WEEKDAY(dateValue[,style]) + * + * @param null|float|int|string $dateValue Excel date serial value (float), PHP date timestamp (integer), + * PHP DateTime object, or a standard date string + * @param mixed $style A number that determines the type of return value + * 1 or omitted Numbers 1 (Sunday) through 7 (Saturday). + * 2 Numbers 1 (Monday) through 7 (Sunday). + * 3 Numbers 0 (Monday) through 6 (Sunday). + * + * @return int|string Day of the week value + */ + public static function day($dateValue, $style = 1) + { + try { + $dateValue = Helpers::getDateValue($dateValue); + $style = self::validateStyle($style); + } catch (Exception $e) { + return $e->getMessage(); + } + + // Execute function + $PHPDateObject = Date::excelToDateTimeObject($dateValue); + Helpers::silly1900($PHPDateObject); + $DoW = (int) $PHPDateObject->format('w'); + + switch ($style) { + case 1: + ++$DoW; + + break; + case 2: + $DoW = self::dow0Becomes7($DoW); + + break; + case 3: + $DoW = self::dow0Becomes7($DoW) - 1; + + break; + } + + return $DoW; + } + + /** + * @param mixed $style expect int + */ + private static function validateStyle($style): int + { + $style = Functions::flattenSingleValue($style); + + if (!is_numeric($style)) { + throw new Exception(Functions::VALUE()); + } + $style = (int) $style; + if (($style < 1) || ($style > 3)) { + throw new Exception(Functions::NAN()); + } + + return $style; + } + + private static function dow0Becomes7(int $DoW): int + { + return ($DoW === 0) ? 7 : $DoW; + } + + /** + * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer), + * PHP DateTime object, or a standard date string + */ + private static function apparentBug($dateValue): bool + { + if (Date::getExcelCalendar() !== DATE::CALENDAR_MAC_1904) { + if (is_bool($dateValue)) { + return true; + } + if (is_numeric($dateValue) && !((int) $dateValue)) { + return true; + } + } + + return false; + } + /** * Validate dateValue parameter. * @@ -125,6 +248,7 @@ class WeekNum { // This appears to be another Excel bug. - return $method === Constants::DOW_SUNDAY && Date::getExcelCalendar() === Date::CALENDAR_MAC_1904 && !$origNull && $dateObject->format('Y-m-d') === '1904-01-01'; + return $method === Constants::DOW_SUNDAY && Date::getExcelCalendar() === Date::CALENDAR_MAC_1904 && + !$origNull && $dateObject->format('Y-m-d') === '1904-01-01'; } } diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/WeekDay.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/WeekDay.php deleted file mode 100644 index 6745557d..00000000 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/WeekDay.php +++ /dev/null @@ -1,83 +0,0 @@ -getMessage(); - } - - // Execute function - $PHPDateObject = Date::excelToDateTimeObject($dateValue); - Helpers::silly1900($PHPDateObject); - $DoW = (int) $PHPDateObject->format('w'); - - switch ($style) { - case 1: - ++$DoW; - - break; - case 2: - $DoW = self::dow0Becomes7($DoW); - - break; - case 3: - $DoW = self::dow0Becomes7($DoW) - 1; - - break; - } - - return $DoW; - } - - /** - * @param mixed $style expect int - */ - private static function validateStyle($style): int - { - $style = Functions::flattenSingleValue($style); - - if (!is_numeric($style)) { - throw new Exception(Functions::VALUE()); - } - $style = (int) $style; - if (($style < 1) || ($style > 3)) { - throw new Exception(Functions::NAN()); - } - - return $style; - } - - private static function dow0Becomes7(int $DoW): int - { - return ($DoW === 0) ? 7 : $DoW; - } -} diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php index 502cc0d6..da7a81c5 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php @@ -183,7 +183,7 @@ class WorkDay private static function getWeekDay(float $date, int $wd): int { - $result = WeekDay::evaluate($date, $wd); + $result = Week::day($date, $wd); return is_string($result) ? -1 : $result; } diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Year.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Year.php deleted file mode 100644 index 2b7a10c6..00000000 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Year.php +++ /dev/null @@ -1,40 +0,0 @@ -getMessage(); - } - - if ($dateValue < 1 && Date::getExcelCalendar() === DATE::CALENDAR_WINDOWS_1900) { - return 1900; - } - // Execute function - $PHPDateObject = Date::excelToDateTimeObject($dateValue); - - return (int) $PHPDateObject->format('Y'); - } -} diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php index 3d49be47..ad300818 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php @@ -74,7 +74,7 @@ class YearFrac { if (Functions::getCompatibilityMode() !== Functions::COMPATIBILITY_OPENOFFICE && Date::getExcelCalendar() !== Date::CALENDAR_MAC_1904) { if ($endDate === null && $startDate !== null) { - if (Month::evaluate($sDate) == 12 && Day::evaluate($sDate) === 31 && $method === 0) { + if (DateParts::month($sDate) == 12 && DateParts::day($sDate) === 31 && $method === 0) { $sDate += 2; } else { ++$sDate; @@ -88,13 +88,13 @@ class YearFrac private static function method1(float $startDate, float $endDate): float { $days = DateDif::evaluate($startDate, $endDate); - $startYear = (int) Year::evaluate($startDate); - $endYear = (int) Year::evaluate($endDate); + $startYear = (int) DateParts::year($startDate); + $endYear = (int) DateParts::year($endDate); $years = $endYear - $startYear + 1; - $startMonth = (int) Month::evaluate($startDate); - $startDay = (int) Day::evaluate($startDate); - $endMonth = (int) Month::evaluate($endDate); - $endDay = (int) Day::evaluate($endDate); + $startMonth = (int) DateParts::month($startDate); + $startDay = (int) DateParts::day($startDate); + $endMonth = (int) DateParts::month($endDate); + $endDay = (int) DateParts::day($endDate); $startMonthDay = 100 * $startMonth + $startDay; $endMonthDay = 100 * $endMonth + $endDay; if ($years == 1) { diff --git a/src/PhpSpreadsheet/Calculation/Financial/Amortization.php b/src/PhpSpreadsheet/Calculation/Financial/Amortization.php index c10e3304..048684d5 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/Amortization.php +++ b/src/PhpSpreadsheet/Calculation/Financial/Amortization.php @@ -160,7 +160,7 @@ class Amortization $fOneRate = $cost * $rate; $fCostDelta = $cost - $salvage; // Note, quirky variation for leap years on the YEARFRAC for this function - $purchasedYear = DateTimeExcel\Year::evaluate($purchased); + $purchasedYear = DateTimeExcel\DateParts::year($purchased); $yearFrac = DateTimeExcel\YearFrac::evaluate($purchased, $firstPeriod, $basis); if (is_string($yearFrac)) { return $yearFrac; diff --git a/src/PhpSpreadsheet/Calculation/Financial/Coupons.php b/src/PhpSpreadsheet/Calculation/Financial/Coupons.php index 7f8f4b7c..a39688d5 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/Coupons.php +++ b/src/PhpSpreadsheet/Calculation/Financial/Coupons.php @@ -63,7 +63,7 @@ class Coupons return $e->getMessage(); } - $daysPerYear = Helpers::daysPerYear(DateTimeExcel\Year::evaluate($settlement), $basis); + $daysPerYear = Helpers::daysPerYear(DateTimeExcel\DateParts::year($settlement), $basis); if (is_string($daysPerYear)) { return Functions::VALUE(); } @@ -133,7 +133,7 @@ class Coupons case FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL: // Actual/actual if ($frequency == FinancialConstants::FREQUENCY_ANNUAL) { - $daysPerYear = Helpers::daysPerYear(DateTimeExcel\Year::evaluate($settlement), $basis); + $daysPerYear = Helpers::daysPerYear(DateTimeExcel\DateParts::year($settlement), $basis); return $daysPerYear / $frequency; } @@ -197,7 +197,7 @@ class Coupons return $e->getMessage(); } - $daysPerYear = Helpers::daysPerYear(DateTimeExcel\Year::evaluate($settlement), $basis); + $daysPerYear = Helpers::daysPerYear(DateTimeExcel\DateParts::year($settlement), $basis); $next = self::couponFirstPeriodDate($settlement, $maturity, $frequency, self::PERIOD_DATE_NEXT); if ($basis === FinancialConstants::BASIS_DAYS_PER_YEAR_NASD) { diff --git a/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php b/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php index 2c43254c..d1d0c8ff 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php +++ b/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php @@ -194,7 +194,7 @@ class Price return $e->getMessage(); } - $daysPerYear = Helpers::daysPerYear(DateTimeExcel\Year::evaluate($settlement), $basis); + $daysPerYear = Helpers::daysPerYear(DateTimeExcel\DateParts::year($settlement), $basis); if (!is_numeric($daysPerYear)) { return $daysPerYear; } diff --git a/src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php b/src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php index 416a9c50..4a0d0873 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php +++ b/src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php @@ -57,7 +57,7 @@ class Yields return $e->getMessage(); } - $daysPerYear = Helpers::daysPerYear(DateTimeExcel\Year::evaluate($settlement), $basis); + $daysPerYear = Helpers::daysPerYear(DateTimeExcel\DateParts::year($settlement), $basis); if (!is_numeric($daysPerYear)) { return $daysPerYear; } @@ -122,7 +122,7 @@ class Yields return $e->getMessage(); } - $daysPerYear = Helpers::daysPerYear(DateTimeExcel\Year::evaluate($settlement), $basis); + $daysPerYear = Helpers::daysPerYear(DateTimeExcel\DateParts::year($settlement), $basis); if (!is_numeric($daysPerYear)) { return $daysPerYear; } diff --git a/src/PhpSpreadsheet/Calculation/Financial/TreasuryBill.php b/src/PhpSpreadsheet/Calculation/Financial/TreasuryBill.php index 2e186d52..c60af0b0 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/TreasuryBill.php +++ b/src/PhpSpreadsheet/Calculation/Financial/TreasuryBill.php @@ -43,7 +43,7 @@ class TreasuryBill $daysBetweenSettlementAndMaturity = $maturity - $settlement; $daysPerYear = Helpers::daysPerYear( - DateTimeExcel\Year::evaluate($maturity), + DateTimeExcel\DateParts::year($maturity), FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL ); @@ -88,7 +88,7 @@ class TreasuryBill $daysBetweenSettlementAndMaturity = $maturity - $settlement; $daysPerYear = Helpers::daysPerYear( - DateTimeExcel\Year::evaluate($maturity), + DateTimeExcel\DateParts::year($maturity), FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL ); @@ -134,7 +134,7 @@ class TreasuryBill $daysBetweenSettlementAndMaturity = $maturity - $settlement; $daysPerYear = Helpers::daysPerYear( - DateTimeExcel\Year::evaluate($maturity), + DateTimeExcel\DateParts::year($maturity), FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL ); diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/WeekDayTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/WeekDayTest.php index 30898988..625e4414 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/WeekDayTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/WeekDayTest.php @@ -2,7 +2,7 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\DateTime; -use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel\WeekDay; +use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel\Week; class WeekDayTest extends AllSetupTeardown { @@ -28,8 +28,8 @@ class WeekDayTest extends AllSetupTeardown public function testWEEKDAYwith1904Calendar(): void { self::setMac1904(); - self::assertEquals(7, WeekDay::evaluate('1904-01-02')); - self::assertEquals(6, WeekDay::evaluate('1904-01-01')); - self::assertEquals(6, WeekDay::evaluate(null)); + self::assertEquals(7, Week::day('1904-01-02')); + self::assertEquals(6, Week::day('1904-01-01')); + self::assertEquals(6, Week::day(null)); } } From 97472ae383e37830c29bccd20ee01e9ca56ffc94 Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Wed, 12 May 2021 13:37:31 +0200 Subject: [PATCH 53/57] Fix AutoFilter dat break referencing the old Now::evaluate() class/method, rather than the new Current::now() --- src/PhpSpreadsheet/Worksheet/AutoFilter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PhpSpreadsheet/Worksheet/AutoFilter.php b/src/PhpSpreadsheet/Worksheet/AutoFilter.php index 394a3896..b846f7b6 100644 --- a/src/PhpSpreadsheet/Worksheet/AutoFilter.php +++ b/src/PhpSpreadsheet/Worksheet/AutoFilter.php @@ -472,7 +472,7 @@ class AutoFilter $val = $maxVal = null; $ruleValues = []; - $baseDate = DateTimeExcel\Now::evaluate(); + $baseDate = DateTimeExcel\Current::now(); // Calculate start/end dates for the required date range based on current date switch ($dynamicRuleType) { case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTWEEK: From aa3269a8632ec3016a13ac26eaeb4fa1d7132f6e Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Wed, 12 May 2021 14:32:39 +0200 Subject: [PATCH 54/57] Some method renaming --- .../Calculation/Calculation.php | 8 ++++---- src/PhpSpreadsheet/Calculation/DateTime.php | 20 +++++++++---------- .../Calculation/DateTimeExcel/DateValue.php | 2 +- .../Calculation/DateTimeExcel/Datefunc.php | 2 +- .../Calculation/DateTimeExcel/Helpers.php | 4 ++-- .../Calculation/DateTimeExcel/Time.php | 2 +- .../Calculation/DateTimeExcel/TimeValue.php | 2 +- .../Calculation/TextData/Format.php | 6 +++--- src/PhpSpreadsheet/Shared/Date.php | 4 ++-- .../Functions/DateTime/DateTest.php | 8 ++++---- .../Functions/DateTime/DateValueTest.php | 14 ++++++------- .../Functions/DateTime/TimeTest.php | 8 ++++---- .../Functions/DateTime/TimeValueTest.php | 4 ++-- 13 files changed, 42 insertions(+), 42 deletions(-) diff --git a/src/PhpSpreadsheet/Calculation/Calculation.php b/src/PhpSpreadsheet/Calculation/Calculation.php index 52dd290c..f53987f2 100644 --- a/src/PhpSpreadsheet/Calculation/Calculation.php +++ b/src/PhpSpreadsheet/Calculation/Calculation.php @@ -758,7 +758,7 @@ class Calculation ], 'DATE' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\Datefunc::class, 'evaluate'], + 'functionCall' => [DateTimeExcel\Datefunc::class, 'fromYMD'], 'argumentCount' => '3', ], 'DATEDIF' => [ @@ -768,7 +768,7 @@ class Calculation ], 'DATEVALUE' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\DateValue::class, 'evaluate'], + 'functionCall' => [DateTimeExcel\DateValue::class, 'fromString'], 'argumentCount' => '1', ], 'DAVERAGE' => [ @@ -2424,12 +2424,12 @@ class Calculation ], 'TIME' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\Time::class, 'evaluate'], + 'functionCall' => [DateTimeExcel\Time::class, 'fromHMS'], 'argumentCount' => '3', ], 'TIMEVALUE' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\TimeValue::class, 'evaluate'], + 'functionCall' => [DateTimeExcel\TimeValue::class, 'fromString'], 'argumentCount' => '1', ], 'TINV' => [ diff --git a/src/PhpSpreadsheet/Calculation/DateTime.php b/src/PhpSpreadsheet/Calculation/DateTime.php index a81190d4..dc2ec985 100644 --- a/src/PhpSpreadsheet/Calculation/DateTime.php +++ b/src/PhpSpreadsheet/Calculation/DateTime.php @@ -115,7 +115,7 @@ class DateTime * * @Deprecated 1.18.0 * - * @See DateTimeExcel\Datefunc::evaluate() + * @See DateTimeExcel\Datefunc::fromYMD() * Use the evaluate method in the DateTimeExcel\Datefunc class instead * * PhpSpreadsheet is a lot more forgiving than MS Excel when passing non numeric values to this function. @@ -158,7 +158,7 @@ class DateTime */ public static function DATE($year = 0, $month = 1, $day = 1) { - return DateTimeExcel\Datefunc::evaluate($year, $month, $day); + return DateTimeExcel\Datefunc::fromYMD($year, $month, $day); } /** @@ -174,7 +174,7 @@ class DateTime * * @Deprecated 1.18.0 * - * @See DateTimeExcel\Time::evaluate() + * @See DateTimeExcel\Time::fromHMS() * Use the evaluate method in the DateTimeExcel\Time class instead * * @param int $hour A number from 0 (zero) to 32767 representing the hour. @@ -194,7 +194,7 @@ class DateTime */ public static function TIME($hour = 0, $minute = 0, $second = 0) { - return DateTimeExcel\Time::evaluate($hour, $minute, $second); + return DateTimeExcel\Time::fromHMS($hour, $minute, $second); } /** @@ -212,8 +212,8 @@ class DateTime * * @Deprecated 1.18.0 * - * @See DateTimeExcel\DateValue::evaluate() - * Use the evaluate method in the DateTimeExcel\DateValue class instead + * @See DateTimeExcel\DateValue::fromString() + * Use the fromString method in the DateTimeExcel\DateValue class instead * * @param string $dateValue Text that represents a date in a Microsoft Excel date format. * For example, "1/30/2008" or "30-Jan-2008" are text strings within @@ -229,7 +229,7 @@ class DateTime */ public static function DATEVALUE($dateValue) { - return DateTimeExcel\DateValue::evaluate($dateValue); + return DateTimeExcel\DateValue::fromString($dateValue); } /** @@ -247,8 +247,8 @@ class DateTime * * @Deprecated 1.18.0 * - * @See DateTimeExcel\TimeValue::evaluate() - * Use the evaluate method in the DateTimeExcel\TimeValue class instead + * @See DateTimeExcel\TimeValue::fromString() + * Use the fromString method in the DateTimeExcel\TimeValue class instead * * @param string $timeValue A text string that represents a time in any one of the Microsoft * Excel time formats; for example, "6:45 PM" and "18:45" text strings @@ -260,7 +260,7 @@ class DateTime */ public static function TIMEVALUE($timeValue) { - return DateTimeExcel\TimeValue::evaluate($timeValue); + return DateTimeExcel\TimeValue::fromString($timeValue); } /** diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php index ee872f31..4c754456 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php @@ -33,7 +33,7 @@ class DateValue * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object, * depending on the value of the ReturnDateType flag */ - public static function evaluate($dateValue) + public static function fromString($dateValue) { $dti = new DateTimeImmutable(); $baseYear = Date::getExcelCalendar(); diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Datefunc.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Datefunc.php index e8aa02cb..3a0aaeaa 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Datefunc.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Datefunc.php @@ -58,7 +58,7 @@ class Datefunc * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object, * depending on the value of the ReturnDateType flag */ - public static function evaluate($year, $month, $day) + public static function fromYMD($year, $month, $day) { $baseYear = Date::getExcelCalendar(); diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php index 00f12ddc..2d1b6af1 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php @@ -43,7 +43,7 @@ class Helpers if (!is_numeric($dateValue)) { $saveReturnDateType = Functions::getReturnDateType(); Functions::setReturnDateType(Functions::RETURNDATE_EXCEL); - $dateValue = DateValue::evaluate($dateValue); + $dateValue = DateValue::fromString($dateValue); Functions::setReturnDateType($saveReturnDateType); if (!is_numeric($dateValue)) { throw new Exception(Functions::VALUE()); @@ -67,7 +67,7 @@ class Helpers { $saveReturnDateType = Functions::getReturnDateType(); Functions::setReturnDateType(Functions::RETURNDATE_EXCEL); - $timeValue = TimeValue::evaluate($timeValue); + $timeValue = TimeValue::fromString($timeValue); Functions::setReturnDateType($saveReturnDateType); return $timeValue; diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Time.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Time.php index 7f29ba2c..c72bcf60 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Time.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Time.php @@ -35,7 +35,7 @@ class Time * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object, * depending on the value of the ReturnDateType flag */ - public static function evaluate($hour, $minute, $second) + public static function fromHMS($hour, $minute, $second) { try { $hour = self::toIntWithNullBool($hour); diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeValue.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeValue.php index 46fa4136..67600761 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeValue.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeValue.php @@ -29,7 +29,7 @@ class TimeValue * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object, * depending on the value of the ReturnDateType flag */ - public static function evaluate($timeValue) + public static function fromString($timeValue) { $timeValue = trim(Functions::flattenSingleValue($timeValue), '"'); $timeValue = str_replace(['/', '.'], '-', $timeValue); diff --git a/src/PhpSpreadsheet/Calculation/TextData/Format.php b/src/PhpSpreadsheet/Calculation/TextData/Format.php index 3b7f0486..b360a0f6 100644 --- a/src/PhpSpreadsheet/Calculation/TextData/Format.php +++ b/src/PhpSpreadsheet/Calculation/TextData/Format.php @@ -98,7 +98,7 @@ class Format $format = Functions::flattenSingleValue($format); if ((is_string($value)) && (!is_numeric($value)) && Date::isDateTimeFormatCode($format)) { - $value = DateTimeExcel\DateValue::evaluate($value); + $value = DateTimeExcel\DateValue::fromString($value); } return (string) NumberFormat::toFormattedString($value, $format); @@ -129,14 +129,14 @@ class Format Functions::setReturnDateType(Functions::RETURNDATE_EXCEL); if (strpos($value, ':') !== false) { - $timeValue = DateTimeExcel\TimeValue::evaluate($value); + $timeValue = DateTimeExcel\TimeValue::fromString($value); if ($timeValue !== Functions::VALUE()) { Functions::setReturnDateType($dateSetting); return $timeValue; } } - $dateValue = DateTimeExcel\DateValue::evaluate($value); + $dateValue = DateTimeExcel\DateValue::fromString($value); if ($dateValue !== Functions::VALUE()) { Functions::setReturnDateType($dateSetting); diff --git a/src/PhpSpreadsheet/Shared/Date.php b/src/PhpSpreadsheet/Shared/Date.php index a56ca3df..3b4c635d 100644 --- a/src/PhpSpreadsheet/Shared/Date.php +++ b/src/PhpSpreadsheet/Shared/Date.php @@ -437,14 +437,14 @@ class Date return false; } - $dateValueNew = DateTimeExcel\DateValue::evaluate($dateValue); + $dateValueNew = DateTimeExcel\DateValue::fromString($dateValue); if ($dateValueNew === Functions::VALUE()) { return false; } if (strpos($dateValue, ':') !== false) { - $timeValue = DateTimeExcel\TimeValue::evaluate($dateValue); + $timeValue = DateTimeExcel\TimeValue::fromString($dateValue); if ($timeValue === Functions::VALUE()) { return false; } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/DateTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/DateTest.php index 28988ded..01e8d4a0 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/DateTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/DateTest.php @@ -29,7 +29,7 @@ class DateTest extends AllSetupTeardown { self::setUnixReturn(); - $result = Datefunc::evaluate(2012, 1, 31); // 32-bit safe + $result = Datefunc::fromYMD(2012, 1, 31); // 32-bit safe self::assertEquals(1327968000, $result); } @@ -37,7 +37,7 @@ class DateTest extends AllSetupTeardown { self::setObjectReturn(); - $result = Datefunc::evaluate(2012, 1, 31); + $result = Datefunc::fromYMD(2012, 1, 31); // Must return an object... self::assertIsObject($result); // ... of the correct type @@ -50,10 +50,10 @@ class DateTest extends AllSetupTeardown { self::setMac1904(); - $result = Datefunc::evaluate(1918, 11, 11); + $result = Datefunc::fromYMD(1918, 11, 11); self::assertEquals($result, 5428); - $result = Datefunc::evaluate(1901, 1, 31); + $result = Datefunc::fromYMD(1901, 1, 31); self::assertEquals($result, '#NUM!'); } } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/DateValueTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/DateValueTest.php index 13e56671..cf37162a 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/DateValueTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/DateValueTest.php @@ -26,7 +26,7 @@ class DateValueTest extends AllSetupTeardown if (is_string($expectedResult)) { $replYMD = str_replace('Y', date('Y'), $expectedResult); if ($replYMD !== $expectedResult) { - $expectedResult = DateValue::evaluate($replYMD); + $expectedResult = DateValue::fromString($replYMD); } } $this->sheet->getCell("A$row")->setValue("=DATEVALUE($dateValue)"); @@ -46,7 +46,7 @@ class DateValueTest extends AllSetupTeardown { self::setUnixReturn(); - $result = DateValue::evaluate('2012-1-31'); + $result = DateValue::fromString('2012-1-31'); self::assertEquals(1327968000, $result); self::assertEqualsWithDelta(1327968000, $result, 1E-8); } @@ -55,7 +55,7 @@ class DateValueTest extends AllSetupTeardown { self::setObjectReturn(); - $result = DateValue::evaluate('2012-1-31'); + $result = DateValue::fromString('2012-1-31'); // Must return an object... self::assertIsObject($result); // ... of the correct type @@ -67,9 +67,9 @@ class DateValueTest extends AllSetupTeardown public function testDATEVALUEwith1904Calendar(): void { self::setMac1904(); - self::assertEquals(5428, DateValue::evaluate('1918-11-11')); - self::assertEquals(0, DateValue::evaluate('1904-01-01')); - self::assertEquals('#VALUE!', DateValue::evaluate('1903-12-31')); - self::assertEquals('#VALUE!', DateValue::evaluate('1900-02-29')); + self::assertEquals(5428, DateValue::fromString('1918-11-11')); + self::assertEquals(0, DateValue::fromString('1904-01-01')); + self::assertEquals('#VALUE!', DateValue::fromString('1903-12-31')); + self::assertEquals('#VALUE!', DateValue::fromString('1900-02-29')); } } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/TimeTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/TimeTest.php index 053385e7..ae83f05a 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/TimeTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/TimeTest.php @@ -31,7 +31,7 @@ class TimeTest extends AllSetupTeardown { self::setUnixReturn(); - $result = Time::evaluate(7, 30, 20); + $result = Time::fromHMS(7, 30, 20); self::assertEqualsWithDelta(27020, $result, 1E-8); } @@ -39,7 +39,7 @@ class TimeTest extends AllSetupTeardown { self::setObjectReturn(); - $result = Time::evaluate(7, 30, 20); + $result = Time::fromHMS(7, 30, 20); // Must return an object... self::assertIsObject($result); // ... of the correct type @@ -51,13 +51,13 @@ class TimeTest extends AllSetupTeardown public function testTIME1904(): void { self::setMac1904(); - $result = Time::evaluate(0, 0, 0); + $result = Time::fromHMS(0, 0, 0); self::assertEquals(0, $result); } public function testTIME1900(): void { - $result = Time::evaluate(0, 0, 0); + $result = Time::fromHMS(0, 0, 0); self::assertEquals(0, $result); } } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/TimeValueTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/TimeValueTest.php index 1eea77cb..23eb4f17 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/TimeValueTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/TimeValueTest.php @@ -31,7 +31,7 @@ class TimeValueTest extends AllSetupTeardown { self::setUnixReturn(); - $result = TimeValue::evaluate('7:30:20'); + $result = TimeValue::fromString('7:30:20'); self::assertEquals(23420, $result); self::assertEqualsWithDelta(23420, $result, 1E-8); } @@ -40,7 +40,7 @@ class TimeValueTest extends AllSetupTeardown { self::setObjectReturn(); - $result = TimeValue::evaluate('7:30:20'); + $result = TimeValue::fromString('7:30:20'); // Must return an object... self::assertIsObject($result); // ... of the correct type From f7a07747fd990fb8df2e414f59d72384f695cf13 Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Wed, 12 May 2021 15:22:46 +0200 Subject: [PATCH 55/57] More method renaming --- .../Calculation/Calculation.php | 12 ++--- src/PhpSpreadsheet/Calculation/DateTime.php | 40 ++++++++--------- .../Calculation/DateTimeExcel/DateDif.php | 2 +- .../Calculation/DateTimeExcel/Days.php | 2 +- .../Calculation/DateTimeExcel/Days360.php | 2 +- .../Calculation/DateTimeExcel/EDate.php | 45 ------------------- .../DateTimeExcel/{EoMonth.php => Month.php} | 40 ++++++++++++++++- .../Calculation/DateTimeExcel/YearFrac.php | 12 ++--- .../Calculation/Financial/Amortization.php | 4 +- .../CashFlow/Variable/NonPeriodic.php | 4 +- .../Calculation/Financial/Coupons.php | 8 ++-- .../Financial/Securities/AccruedInterest.php | 6 +-- .../Financial/Securities/Price.php | 10 ++--- .../Financial/Securities/Rates.php | 4 +- .../Financial/Securities/Yields.php | 8 ++-- .../Functions/DateTime/DaysTest.php | 4 +- .../Functions/DateTime/EDateTest.php | 6 +-- .../Functions/DateTime/EoMonthTest.php | 6 +-- 18 files changed, 103 insertions(+), 112 deletions(-) delete mode 100644 src/PhpSpreadsheet/Calculation/DateTimeExcel/EDate.php rename src/PhpSpreadsheet/Calculation/DateTimeExcel/{EoMonth.php => Month.php} (51%) diff --git a/src/PhpSpreadsheet/Calculation/Calculation.php b/src/PhpSpreadsheet/Calculation/Calculation.php index f53987f2..3e9e5bf3 100644 --- a/src/PhpSpreadsheet/Calculation/Calculation.php +++ b/src/PhpSpreadsheet/Calculation/Calculation.php @@ -763,7 +763,7 @@ class Calculation ], 'DATEDIF' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\DateDif::class, 'evaluate'], + 'functionCall' => [DateTimeExcel\DateDif::class, 'interval'], 'argumentCount' => '2,3', ], 'DATEVALUE' => [ @@ -783,12 +783,12 @@ class Calculation ], 'DAYS' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\Days::class, 'evaluate'], + 'functionCall' => [DateTimeExcel\Days::class, 'between'], 'argumentCount' => '2', ], 'DAYS360' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\Days360::class, 'evaluate'], + 'functionCall' => [DateTimeExcel\Days360::class, 'between'], 'argumentCount' => '2,3', ], 'DB' => [ @@ -923,7 +923,7 @@ class Calculation ], 'EDATE' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\EDate::class, 'evaluate'], + 'functionCall' => [DateTimeExcel\Month::class, 'adjust'], 'argumentCount' => '2', ], 'EFFECT' => [ @@ -938,7 +938,7 @@ class Calculation ], 'EOMONTH' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\EoMonth::class, 'evaluate'], + 'functionCall' => [DateTimeExcel\Month::class, 'lastDay'], 'argumentCount' => '2', ], 'ERF' => [ @@ -2634,7 +2634,7 @@ class Calculation ], 'YEARFRAC' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\YearFrac::class, 'evaluate'], + 'functionCall' => [DateTimeExcel\YearFrac::class, 'fraction'], 'argumentCount' => '2,3', ], 'YIELD' => [ diff --git a/src/PhpSpreadsheet/Calculation/DateTime.php b/src/PhpSpreadsheet/Calculation/DateTime.php index dc2ec985..1baacd17 100644 --- a/src/PhpSpreadsheet/Calculation/DateTime.php +++ b/src/PhpSpreadsheet/Calculation/DateTime.php @@ -116,7 +116,7 @@ class DateTime * @Deprecated 1.18.0 * * @See DateTimeExcel\Datefunc::fromYMD() - * Use the evaluate method in the DateTimeExcel\Datefunc class instead + * Use the fromYMD method in the DateTimeExcel\Datefunc class instead * * PhpSpreadsheet is a lot more forgiving than MS Excel when passing non numeric values to this function. * A Month name or abbreviation (English only at this point) such as 'January' or 'Jan' will still be accepted, @@ -175,7 +175,7 @@ class DateTime * @Deprecated 1.18.0 * * @See DateTimeExcel\Time::fromHMS() - * Use the evaluate method in the DateTimeExcel\Time class instead + * Use the fromHMS method in the DateTimeExcel\Time class instead * * @param int $hour A number from 0 (zero) to 32767 representing the hour. * Any value greater than 23 will be divided by 24 and the remainder @@ -271,8 +271,8 @@ class DateTime * * @Deprecated 1.18.0 * - * @See DateTimeExcel\DateDif::evaluate() - * Use the evaluate method in the DateTimeExcel\DateDif class instead + * @See DateTimeExcel\DateDif::interval() + * Use the interval method in the DateTimeExcel\DateDif class instead * * @param mixed $startDate Excel date serial value, PHP date/time stamp, PHP DateTime object * or a standard date string @@ -284,7 +284,7 @@ class DateTime */ public static function DATEDIF($startDate = 0, $endDate = 0, $unit = 'D') { - return DateTimeExcel\DateDif::evaluate($startDate, $endDate, $unit); + return DateTimeExcel\DateDif::interval($startDate, $endDate, $unit); } /** @@ -297,8 +297,8 @@ class DateTime * * @Deprecated 1.18.0 * - * @See DateTimeExcel\Days::evaluate() - * Use the evaluate method in the DateTimeExcel\Days class instead + * @See DateTimeExcel\Days::between() + * Use the between method in the DateTimeExcel\Days class instead * * @param DateTimeInterface|float|int|string $endDate Excel date serial value (float), * PHP date timestamp (integer), PHP DateTime object, or a standard date string @@ -309,7 +309,7 @@ class DateTime */ public static function DAYS($endDate = 0, $startDate = 0) { - return DateTimeExcel\Days::evaluate($endDate, $startDate); + return DateTimeExcel\Days::between($endDate, $startDate); } /** @@ -324,8 +324,8 @@ class DateTime * * @Deprecated 1.18.0 * - * @See DateTimeExcel\Days360::evaluate() - * Use the evaluate method in the DateTimeExcel\Days360 class instead + * @See DateTimeExcel\Days360::between() + * Use the between method in the DateTimeExcel\Days360 class instead * * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer), * PHP DateTime object, or a standard date string @@ -347,7 +347,7 @@ class DateTime */ public static function DAYS360($startDate = 0, $endDate = 0, $method = false) { - return DateTimeExcel\Days360::evaluate($startDate, $endDate, $method); + return DateTimeExcel\Days360::between($startDate, $endDate, $method); } /** @@ -363,8 +363,8 @@ class DateTime * * @Deprecated 1.18.0 * - * @See DateTimeExcel\YearFrac::evaluate() - * Use the evaluate method in the DateTimeExcel\YearFrac class instead + * @See DateTimeExcel\YearFrac::fraction() + * Use the fraction method in the DateTimeExcel\YearFrac class instead * * See https://lists.oasis-open.org/archives/office-formula/200806/msg00039.html * for description of algorithm used in Excel @@ -384,7 +384,7 @@ class DateTime */ public static function YEARFRAC($startDate = 0, $endDate = 0, $method = 0) { - return DateTimeExcel\YearFrac::evaluate($startDate, $endDate, $method); + return DateTimeExcel\YearFrac::fraction($startDate, $endDate, $method); } /** @@ -867,8 +867,8 @@ class DateTime * * @Deprecated 1.18.0 * - * @See DateTimeExcel\Edate::evaluate() - * Use the evaluate method in the DateTimeExcel\Edate class instead + * @See DateTimeExcel\Month::adjust() + * Use the adjust method in the DateTimeExcel\Edate class instead * * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer), * PHP DateTime object, or a standard date string @@ -881,7 +881,7 @@ class DateTime */ public static function EDATE($dateValue = 1, $adjustmentMonths = 0) { - return DateTimeExcel\EDate::evaluate($dateValue, $adjustmentMonths); + return DateTimeExcel\Month::adjust($dateValue, $adjustmentMonths); } /** @@ -896,8 +896,8 @@ class DateTime * * @Deprecated 1.18.0 * - * @See DateTimeExcel\EoMonth::evaluate() - * Use the evaluate method in the DateTimeExcel\EoMonth class instead + * @See DateTimeExcel\Month::lastDay() + * Use the lastDay method in the DateTimeExcel\EoMonth class instead * * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer), * PHP DateTime object, or a standard date string @@ -910,6 +910,6 @@ class DateTime */ public static function EOMONTH($dateValue = 1, $adjustmentMonths = 0) { - return DateTimeExcel\EoMonth::evaluate($dateValue, $adjustmentMonths); + return DateTimeExcel\Month::lastDay($dateValue, $adjustmentMonths); } } diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateDif.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateDif.php index 26eb1dbf..6891715b 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateDif.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateDif.php @@ -21,7 +21,7 @@ class DateDif * * @return int|string Interval between the dates */ - public static function evaluate($startDate, $endDate, $unit = 'D') + public static function interval($startDate, $endDate, $unit = 'D') { try { $startDate = Helpers::getDateValue($startDate); diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days.php index bd37e481..73465ccf 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days.php @@ -24,7 +24,7 @@ class Days * * @return int|string Number of days between start date and end date or an error */ - public static function evaluate($endDate, $startDate) + public static function between($endDate, $startDate) { try { $startDate = Helpers::getDateValue($startDate); diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days360.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days360.php index 74de95ad..d11cdd16 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days360.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days360.php @@ -36,7 +36,7 @@ class Days360 * * @return int|string Number of days between start date and end date */ - public static function evaluate($startDate = 0, $endDate = 0, $method = false) + public static function between($startDate = 0, $endDate = 0, $method = false) { try { $startDate = Helpers::getDateValue($startDate); diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/EDate.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/EDate.php deleted file mode 100644 index bb234097..00000000 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/EDate.php +++ /dev/null @@ -1,45 +0,0 @@ -getMessage(); - } - $adjustmentMonths = floor($adjustmentMonths); - - // Execute function - $PHPDateObject = Helpers::adjustDateByMonths($dateValue, $adjustmentMonths); - - return Helpers::returnIn3FormatsObject($PHPDateObject); - } -} diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/EoMonth.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Month.php similarity index 51% rename from src/PhpSpreadsheet/Calculation/DateTimeExcel/EoMonth.php rename to src/PhpSpreadsheet/Calculation/DateTimeExcel/Month.php index 54449c5d..10cb27fd 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/EoMonth.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Month.php @@ -5,8 +5,44 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel; use Exception; use PhpOffice\PhpSpreadsheet\Shared\Date; -class EoMonth +class Month { + /** + * EDATE. + * + * Returns the serial number that represents the date that is the indicated number of months + * before or after a specified date (the start_date). + * Use EDATE to calculate maturity dates or due dates that fall on the same day of the month + * as the date of issue. + * + * Excel Function: + * EDATE(dateValue,adjustmentMonths) + * + * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer), + * PHP DateTime object, or a standard date string + * @param int $adjustmentMonths The number of months before or after start_date. + * A positive value for months yields a future date; + * a negative value yields a past date. + * + * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object, + * depending on the value of the ReturnDateType flag + */ + public static function adjust($dateValue, $adjustmentMonths) + { + try { + $dateValue = Helpers::getDateValue($dateValue, false); + $adjustmentMonths = Helpers::validateNumericNull($adjustmentMonths); + } catch (Exception $e) { + return $e->getMessage(); + } + $adjustmentMonths = floor($adjustmentMonths); + + // Execute function + $PHPDateObject = Helpers::adjustDateByMonths($dateValue, $adjustmentMonths); + + return Helpers::returnIn3FormatsObject($PHPDateObject); + } + /** * EOMONTH. * @@ -26,7 +62,7 @@ class EoMonth * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object, * depending on the value of the ReturnDateType flag */ - public static function evaluate($dateValue, $adjustmentMonths) + public static function lastDay($dateValue, $adjustmentMonths) { try { $dateValue = Helpers::getDateValue($dateValue, false); diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php index ad300818..fd10221c 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php @@ -34,7 +34,7 @@ class YearFrac * * @return float|string fraction of the year, or a string containing an error */ - public static function evaluate($startDate, $endDate, $method = 0) + public static function fraction($startDate, $endDate, $method = 0) { try { $method = (int) Helpers::validateNumericNull($method); @@ -50,15 +50,15 @@ class YearFrac switch ($method) { case 0: - return Days360::evaluate($startDate, $endDate) / 360; + return Days360::between($startDate, $endDate) / 360; case 1: return self::method1($startDate, $endDate); case 2: - return DateDif::evaluate($startDate, $endDate) / 360; + return DateDif::interval($startDate, $endDate) / 360; case 3: - return DateDif::evaluate($startDate, $endDate) / 365; + return DateDif::interval($startDate, $endDate) / 365; case 4: - return Days360::evaluate($startDate, $endDate, true) / 360; + return Days360::between($startDate, $endDate, true) / 360; } return Functions::NAN(); @@ -87,7 +87,7 @@ class YearFrac private static function method1(float $startDate, float $endDate): float { - $days = DateDif::evaluate($startDate, $endDate); + $days = DateDif::interval($startDate, $endDate); $startYear = (int) DateParts::year($startDate); $endYear = (int) DateParts::year($endDate); $years = $endYear - $startYear + 1; diff --git a/src/PhpSpreadsheet/Calculation/Financial/Amortization.php b/src/PhpSpreadsheet/Calculation/Financial/Amortization.php index 048684d5..ba7fb521 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/Amortization.php +++ b/src/PhpSpreadsheet/Calculation/Financial/Amortization.php @@ -70,7 +70,7 @@ class Amortization return $e->getMessage(); } - $yearFrac = DateTimeExcel\YearFrac::evaluate($purchased, $firstPeriod, $basis); + $yearFrac = DateTimeExcel\YearFrac::fraction($purchased, $firstPeriod, $basis); if (is_string($yearFrac)) { return $yearFrac; } @@ -161,7 +161,7 @@ class Amortization $fCostDelta = $cost - $salvage; // Note, quirky variation for leap years on the YEARFRAC for this function $purchasedYear = DateTimeExcel\DateParts::year($purchased); - $yearFrac = DateTimeExcel\YearFrac::evaluate($purchased, $firstPeriod, $basis); + $yearFrac = DateTimeExcel\YearFrac::fraction($purchased, $firstPeriod, $basis); if (is_string($yearFrac)) { return $yearFrac; } diff --git a/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php b/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php index 251ae46d..938c5e44 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php +++ b/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php @@ -196,9 +196,9 @@ class NonPeriodic return $e->getMessage(); } if ($date0 > $datei) { - $dif = $ordered ? Functions::NAN() : -((int) DateTimeExcel\DateDif::evaluate($datei, $date0, 'd')); + $dif = $ordered ? Functions::NAN() : -((int) DateTimeExcel\DateDif::interval($datei, $date0, 'd')); } else { - $dif = DateTimeExcel\DateDif::evaluate($date0, $datei, 'd'); + $dif = DateTimeExcel\DateDif::interval($date0, $datei, 'd'); } if (!is_numeric($dif)) { return $dif; diff --git a/src/PhpSpreadsheet/Calculation/Financial/Coupons.php b/src/PhpSpreadsheet/Calculation/Financial/Coupons.php index a39688d5..5620c327 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/Coupons.php +++ b/src/PhpSpreadsheet/Calculation/Financial/Coupons.php @@ -70,10 +70,10 @@ class Coupons $prev = self::couponFirstPeriodDate($settlement, $maturity, $frequency, self::PERIOD_DATE_PREVIOUS); if ($basis === FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL) { - return abs((float) DateTimeExcel\Days::evaluate($prev, $settlement)); + return abs((float) DateTimeExcel\Days::between($prev, $settlement)); } - return DateTimeExcel\YearFrac::evaluate($prev, $settlement, $basis) * $daysPerYear; + return DateTimeExcel\YearFrac::fraction($prev, $settlement, $basis) * $daysPerYear; } /** @@ -208,7 +208,7 @@ class Coupons } } - return DateTimeExcel\YearFrac::evaluate($settlement, $next, $basis) * $daysPerYear; + return DateTimeExcel\YearFrac::fraction($settlement, $next, $basis) * $daysPerYear; } /** @@ -316,7 +316,7 @@ class Coupons return $e->getMessage(); } - $yearsBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::evaluate( + $yearsBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::fraction( $settlement, $maturity, FinancialConstants::BASIS_DAYS_PER_YEAR_NASD diff --git a/src/PhpSpreadsheet/Calculation/Financial/Securities/AccruedInterest.php b/src/PhpSpreadsheet/Calculation/Financial/Securities/AccruedInterest.php index 707423b5..e167429b 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/Securities/AccruedInterest.php +++ b/src/PhpSpreadsheet/Calculation/Financial/Securities/AccruedInterest.php @@ -78,12 +78,12 @@ class AccruedInterest return $e->getMessage(); } - $daysBetweenIssueAndSettlement = YearFrac::evaluate($issue, $settlement, $basis); + $daysBetweenIssueAndSettlement = YearFrac::fraction($issue, $settlement, $basis); if (!is_numeric($daysBetweenIssueAndSettlement)) { // return date error return $daysBetweenIssueAndSettlement; } - $daysBetweenFirstInterestAndSettlement = YearFrac::evaluate($firstInterest, $settlement, $basis); + $daysBetweenFirstInterestAndSettlement = YearFrac::fraction($firstInterest, $settlement, $basis); if (!is_numeric($daysBetweenFirstInterestAndSettlement)) { // return date error return $daysBetweenFirstInterestAndSettlement; @@ -140,7 +140,7 @@ class AccruedInterest return $e->getMessage(); } - $daysBetweenIssueAndSettlement = YearFrac::evaluate($issue, $settlement, $basis); + $daysBetweenIssueAndSettlement = YearFrac::fraction($issue, $settlement, $basis); if (!is_numeric($daysBetweenIssueAndSettlement)) { // return date error return $daysBetweenIssueAndSettlement; diff --git a/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php b/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php index d1d0c8ff..7d8d5a32 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php +++ b/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php @@ -134,7 +134,7 @@ class Price return $e->getMessage(); } - $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::evaluate($settlement, $maturity, $basis); + $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis); if (!is_numeric($daysBetweenSettlementAndMaturity)) { // return date error return $daysBetweenSettlementAndMaturity; @@ -198,19 +198,19 @@ class Price if (!is_numeric($daysPerYear)) { return $daysPerYear; } - $daysBetweenIssueAndSettlement = DateTimeExcel\YearFrac::evaluate($issue, $settlement, $basis); + $daysBetweenIssueAndSettlement = DateTimeExcel\YearFrac::fraction($issue, $settlement, $basis); if (!is_numeric($daysBetweenIssueAndSettlement)) { // return date error return $daysBetweenIssueAndSettlement; } $daysBetweenIssueAndSettlement *= $daysPerYear; - $daysBetweenIssueAndMaturity = DateTimeExcel\YearFrac::evaluate($issue, $maturity, $basis); + $daysBetweenIssueAndMaturity = DateTimeExcel\YearFrac::fraction($issue, $maturity, $basis); if (!is_numeric($daysBetweenIssueAndMaturity)) { // return date error return $daysBetweenIssueAndMaturity; } $daysBetweenIssueAndMaturity *= $daysPerYear; - $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::evaluate($settlement, $maturity, $basis); + $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis); if (!is_numeric($daysBetweenSettlementAndMaturity)) { // return date error return $daysBetweenSettlementAndMaturity; @@ -272,7 +272,7 @@ class Price if ($investment <= 0) { return Functions::NAN(); } - $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::evaluate($settlement, $maturity, $basis); + $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis); if (!is_numeric($daysBetweenSettlementAndMaturity)) { // return date error return $daysBetweenSettlementAndMaturity; diff --git a/src/PhpSpreadsheet/Calculation/Financial/Securities/Rates.php b/src/PhpSpreadsheet/Calculation/Financial/Securities/Rates.php index 3a46b40b..c5c5211b 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/Securities/Rates.php +++ b/src/PhpSpreadsheet/Calculation/Financial/Securities/Rates.php @@ -63,7 +63,7 @@ class Rates return Functions::NAN(); } - $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::evaluate($settlement, $maturity, $basis); + $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis); if (!is_numeric($daysBetweenSettlementAndMaturity)) { // return date error return $daysBetweenSettlementAndMaturity; @@ -126,7 +126,7 @@ class Rates return Functions::NAN(); } - $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::evaluate($settlement, $maturity, $basis); + $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis); if (!is_numeric($daysBetweenSettlementAndMaturity)) { // return date error return $daysBetweenSettlementAndMaturity; diff --git a/src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php b/src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php index 4a0d0873..aa626935 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php +++ b/src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php @@ -61,7 +61,7 @@ class Yields if (!is_numeric($daysPerYear)) { return $daysPerYear; } - $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::evaluate($settlement, $maturity, $basis); + $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis); if (!is_numeric($daysBetweenSettlementAndMaturity)) { // return date error return $daysBetweenSettlementAndMaturity; @@ -126,19 +126,19 @@ class Yields if (!is_numeric($daysPerYear)) { return $daysPerYear; } - $daysBetweenIssueAndSettlement = DateTimeExcel\YearFrac::evaluate($issue, $settlement, $basis); + $daysBetweenIssueAndSettlement = DateTimeExcel\YearFrac::fraction($issue, $settlement, $basis); if (!is_numeric($daysBetweenIssueAndSettlement)) { // return date error return $daysBetweenIssueAndSettlement; } $daysBetweenIssueAndSettlement *= $daysPerYear; - $daysBetweenIssueAndMaturity = DateTimeExcel\YearFrac::evaluate($issue, $maturity, $basis); + $daysBetweenIssueAndMaturity = DateTimeExcel\YearFrac::fraction($issue, $maturity, $basis); if (!is_numeric($daysBetweenIssueAndMaturity)) { // return date error return $daysBetweenIssueAndMaturity; } $daysBetweenIssueAndMaturity *= $daysPerYear; - $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::evaluate($settlement, $maturity, $basis); + $daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis); if (!is_numeric($daysBetweenSettlementAndMaturity)) { // return date error return $daysBetweenSettlementAndMaturity; diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/DaysTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/DaysTest.php index b88ba391..e4db4f26 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/DaysTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/DaysTest.php @@ -33,13 +33,13 @@ class DaysTest extends AllSetupTeardown { $obj1 = new DateTime('2000-3-31'); $obj2 = new DateTimeImmutable('2000-2-29'); - self::assertSame(31, Days::evaluate($obj1, $obj2)); + self::assertSame(31, Days::between($obj1, $obj2)); } public function testNonDateObject(): void { $obj1 = new Exception(); $obj2 = new DateTimeImmutable('2000-2-29'); - self::assertSame('#VALUE!', Days::evaluate($obj1, $obj2)); + self::assertSame('#VALUE!', Days::between($obj1, $obj2)); } } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/EDateTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/EDateTest.php index 8943911b..b3b2bd25 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/EDateTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/EDateTest.php @@ -2,7 +2,7 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\DateTime; -use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel\EDate; +use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel\Month; class EDateTest extends AllSetupTeardown { @@ -29,7 +29,7 @@ class EDateTest extends AllSetupTeardown { self::setUnixReturn(); - $result = EDate::evaluate('2012-1-26', -1); + $result = Month::adjust('2012-1-26', -1); self::assertEquals(1324857600, $result); self::assertEqualsWithDelta(1324857600, $result, 1E-8); } @@ -38,7 +38,7 @@ class EDateTest extends AllSetupTeardown { self::setObjectReturn(); - $result = EDate::evaluate('2012-1-26', -1); + $result = Month::adjust('2012-1-26', -1); // Must return an object... self::assertIsObject($result); // ... of the correct type diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/EoMonthTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/EoMonthTest.php index eabaf494..45e25e3b 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/EoMonthTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/EoMonthTest.php @@ -2,7 +2,7 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\DateTime; -use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel\EoMonth; +use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel\Month; class EoMonthTest extends AllSetupTeardown { @@ -29,7 +29,7 @@ class EoMonthTest extends AllSetupTeardown { self::setUnixReturn(); - $result = EoMonth::evaluate('2012-1-26', -1); + $result = Month::lastDay('2012-1-26', -1); self::assertEquals(1325289600, $result); } @@ -37,7 +37,7 @@ class EoMonthTest extends AllSetupTeardown { self::setObjectReturn(); - $result = EoMonth::evaluate('2012-1-26', -1); + $result = Month::lastDay('2012-1-26', -1); // Must return an object... self::assertIsObject($result); // ... of the correct type From 765d4586ae0ae8075d40ab631921903b963f20be Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Wed, 12 May 2021 16:12:11 +0200 Subject: [PATCH 56/57] Renaming the last of the DateTime implementation methods --- .../Calculation/Calculation.php | 8 +++---- src/PhpSpreadsheet/Calculation/DateTime.php | 24 +++++++++---------- .../DateTimeExcel/{Datefunc.php => Date.php} | 12 +++++----- .../{DateDif.php => Difference.php} | 2 +- .../Calculation/DateTimeExcel/NetworkDays.php | 2 +- .../Calculation/DateTimeExcel/WorkDay.php | 2 +- .../Calculation/DateTimeExcel/YearFrac.php | 6 ++--- .../CashFlow/Variable/NonPeriodic.php | 4 ++-- .../Functions/DateTime/DateTest.php | 10 ++++---- 9 files changed, 35 insertions(+), 35 deletions(-) rename src/PhpSpreadsheet/Calculation/DateTimeExcel/{Datefunc.php => Date.php} (95%) rename src/PhpSpreadsheet/Calculation/DateTimeExcel/{DateDif.php => Difference.php} (99%) diff --git a/src/PhpSpreadsheet/Calculation/Calculation.php b/src/PhpSpreadsheet/Calculation/Calculation.php index 3e9e5bf3..e32a7c6f 100644 --- a/src/PhpSpreadsheet/Calculation/Calculation.php +++ b/src/PhpSpreadsheet/Calculation/Calculation.php @@ -758,12 +758,12 @@ class Calculation ], 'DATE' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\Datefunc::class, 'fromYMD'], + 'functionCall' => [DateTimeExcel\Date::class, 'fromYMD'], 'argumentCount' => '3', ], 'DATEDIF' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\DateDif::class, 'interval'], + 'functionCall' => [DateTimeExcel\Difference::class, 'interval'], 'argumentCount' => '2,3', ], 'DATEVALUE' => [ @@ -1764,7 +1764,7 @@ class Calculation ], 'NETWORKDAYS' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\NetworkDays::class, 'evaluate'], + 'functionCall' => [DateTimeExcel\NetworkDays::class, 'count'], 'argumentCount' => '2-3', ], 'NETWORKDAYS.INTL' => [ @@ -2594,7 +2594,7 @@ class Calculation ], 'WORKDAY' => [ 'category' => Category::CATEGORY_DATE_AND_TIME, - 'functionCall' => [DateTimeExcel\WorkDay::class, 'evaluate'], + 'functionCall' => [DateTimeExcel\WorkDay::class, 'date'], 'argumentCount' => '2-3', ], 'WORKDAY.INTL' => [ diff --git a/src/PhpSpreadsheet/Calculation/DateTime.php b/src/PhpSpreadsheet/Calculation/DateTime.php index 1baacd17..44a38c19 100644 --- a/src/PhpSpreadsheet/Calculation/DateTime.php +++ b/src/PhpSpreadsheet/Calculation/DateTime.php @@ -115,8 +115,8 @@ class DateTime * * @Deprecated 1.18.0 * - * @See DateTimeExcel\Datefunc::fromYMD() - * Use the fromYMD method in the DateTimeExcel\Datefunc class instead + * @See DateTimeExcel\Date::fromYMD() + * Use the fromYMD method in the DateTimeExcel\Date class instead * * PhpSpreadsheet is a lot more forgiving than MS Excel when passing non numeric values to this function. * A Month name or abbreviation (English only at this point) such as 'January' or 'Jan' will still be accepted, @@ -158,7 +158,7 @@ class DateTime */ public static function DATE($year = 0, $month = 1, $day = 1) { - return DateTimeExcel\Datefunc::fromYMD($year, $month, $day); + return DateTimeExcel\Date::fromYMD($year, $month, $day); } /** @@ -271,8 +271,8 @@ class DateTime * * @Deprecated 1.18.0 * - * @See DateTimeExcel\DateDif::interval() - * Use the interval method in the DateTimeExcel\DateDif class instead + * @See DateTimeExcel\Difference::interval() + * Use the interval method in the DateTimeExcel\Difference class instead * * @param mixed $startDate Excel date serial value, PHP date/time stamp, PHP DateTime object * or a standard date string @@ -284,7 +284,7 @@ class DateTime */ public static function DATEDIF($startDate = 0, $endDate = 0, $unit = 'D') { - return DateTimeExcel\DateDif::interval($startDate, $endDate, $unit); + return DateTimeExcel\Difference::interval($startDate, $endDate, $unit); } /** @@ -400,8 +400,8 @@ class DateTime * * @Deprecated 1.18.0 * - * @See DateTimeExcel\NetworkDays::evaluate() - * Use the evaluate method in the DateTimeExcel\NetworkDays class instead + * @See DateTimeExcel\NetworkDays::count() + * Use the count method in the DateTimeExcel\NetworkDays class instead * * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer), * PHP DateTime object, or a standard date string @@ -413,7 +413,7 @@ class DateTime */ public static function NETWORKDAYS($startDate, $endDate, ...$dateArgs) { - return DateTimeExcel\NetworkDays::evaluate($startDate, $endDate, ...$dateArgs); + return DateTimeExcel\NetworkDays::count($startDate, $endDate, ...$dateArgs); } /** @@ -429,8 +429,8 @@ class DateTime * * @Deprecated 1.18.0 * - * @See DateTimeExcel\WorkDay::evaluate() - * Use the evaluate method in the DateTimeExcel\WorkDay class instead + * @See DateTimeExcel\WorkDay::date() + * Use the date method in the DateTimeExcel\WorkDay class instead * * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer), * PHP DateTime object, or a standard date string @@ -444,7 +444,7 @@ class DateTime */ public static function WORKDAY($startDate, $endDays, ...$dateArgs) { - return DateTimeExcel\WorkDay::evaluate($startDate, $endDays, ...$dateArgs); + return DateTimeExcel\WorkDay::date($startDate, $endDays, ...$dateArgs); } /** diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Datefunc.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php similarity index 95% rename from src/PhpSpreadsheet/Calculation/DateTimeExcel/Datefunc.php rename to src/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php index 3a0aaeaa..3ec6cfbb 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Datefunc.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php @@ -4,10 +4,10 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel; use Exception; use PhpOffice\PhpSpreadsheet\Calculation\Functions; -use PhpOffice\PhpSpreadsheet\Shared\Date; +use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper; use PhpOffice\PhpSpreadsheet\Shared\StringHelper; -class Datefunc +class Date { /** * DATE. @@ -60,7 +60,7 @@ class Datefunc */ public static function fromYMD($year, $month, $day) { - $baseYear = Date::getExcelCalendar(); + $baseYear = SharedDateHelper::getExcelCalendar(); try { $year = self::getYear($year, $baseYear); @@ -72,7 +72,7 @@ class Datefunc } // Execute function - $excelDateValue = Date::formattedPHPToExcel($year, $month, $day); + $excelDateValue = SharedDateHelper::formattedPHPToExcel($year, $month, $day); return Helpers::returnIn3FormatsFloat($excelDateValue); } @@ -115,7 +115,7 @@ class Datefunc $month = Functions::flattenSingleValue($month); if (($month !== null) && (!is_numeric($month))) { - $month = Date::monthStringToNumber($month); + $month = SharedDateHelper::monthStringToNumber($month); } $month = ($month !== null) ? StringHelper::testStringAsNumeric((string) $month) : 0; @@ -136,7 +136,7 @@ class Datefunc $day = Functions::flattenSingleValue($day); if (($day !== null) && (!is_numeric($day))) { - $day = Date::dayStringToNumber($day); + $day = SharedDateHelper::dayStringToNumber($day); } $day = ($day !== null) ? StringHelper::testStringAsNumeric((string) $day) : 0; diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateDif.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Difference.php similarity index 99% rename from src/PhpSpreadsheet/Calculation/DateTimeExcel/DateDif.php rename to src/PhpSpreadsheet/Calculation/DateTimeExcel/Difference.php index 6891715b..4e061d7d 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateDif.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Difference.php @@ -8,7 +8,7 @@ use Exception; use PhpOffice\PhpSpreadsheet\Calculation\Functions; use PhpOffice\PhpSpreadsheet\Shared\Date; -class DateDif +class Difference { /** * DATEDIF. diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/NetworkDays.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/NetworkDays.php index af6eda5c..ded4f647 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/NetworkDays.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/NetworkDays.php @@ -27,7 +27,7 @@ class NetworkDays * * @return int|string Interval between the dates */ - public static function evaluate($startDate, $endDate, ...$dateArgs) + public static function count($startDate, $endDate, ...$dateArgs) { try { // Retrieve the mandatory start and end date that are referenced in the function definition diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php index da7a81c5..ca8bd6b8 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php @@ -29,7 +29,7 @@ class WorkDay * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object, * depending on the value of the ReturnDateType flag */ - public static function evaluate($startDate, $endDays, ...$dateArgs) + public static function date($startDate, $endDays, ...$dateArgs) { // Retrieve the mandatory start date and days that are referenced in the function definition try { diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php index fd10221c..18b259ed 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php @@ -54,9 +54,9 @@ class YearFrac case 1: return self::method1($startDate, $endDate); case 2: - return DateDif::interval($startDate, $endDate) / 360; + return Difference::interval($startDate, $endDate) / 360; case 3: - return DateDif::interval($startDate, $endDate) / 365; + return Difference::interval($startDate, $endDate) / 365; case 4: return Days360::between($startDate, $endDate, true) / 360; } @@ -87,7 +87,7 @@ class YearFrac private static function method1(float $startDate, float $endDate): float { - $days = DateDif::interval($startDate, $endDate); + $days = Difference::interval($startDate, $endDate); $startYear = (int) DateParts::year($startDate); $endYear = (int) DateParts::year($endDate); $years = $endYear - $startYear + 1; diff --git a/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php b/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php index 938c5e44..6a32bdfc 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php +++ b/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php @@ -196,9 +196,9 @@ class NonPeriodic return $e->getMessage(); } if ($date0 > $datei) { - $dif = $ordered ? Functions::NAN() : -((int) DateTimeExcel\DateDif::interval($datei, $date0, 'd')); + $dif = $ordered ? Functions::NAN() : -((int) DateTimeExcel\Difference::interval($datei, $date0, 'd')); } else { - $dif = DateTimeExcel\DateDif::interval($date0, $datei, 'd'); + $dif = DateTimeExcel\Difference::interval($date0, $datei, 'd'); } if (!is_numeric($dif)) { return $dif; diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/DateTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/DateTest.php index 01e8d4a0..2ba9d41d 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/DateTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/DateTime/DateTest.php @@ -2,7 +2,7 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\DateTime; -use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel\Datefunc; +use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel\Date; class DateTest extends AllSetupTeardown { @@ -29,7 +29,7 @@ class DateTest extends AllSetupTeardown { self::setUnixReturn(); - $result = Datefunc::fromYMD(2012, 1, 31); // 32-bit safe + $result = Date::fromYMD(2012, 1, 31); // 32-bit safe self::assertEquals(1327968000, $result); } @@ -37,7 +37,7 @@ class DateTest extends AllSetupTeardown { self::setObjectReturn(); - $result = Datefunc::fromYMD(2012, 1, 31); + $result = Date::fromYMD(2012, 1, 31); // Must return an object... self::assertIsObject($result); // ... of the correct type @@ -50,10 +50,10 @@ class DateTest extends AllSetupTeardown { self::setMac1904(); - $result = Datefunc::fromYMD(1918, 11, 11); + $result = Date::fromYMD(1918, 11, 11); self::assertEquals($result, 5428); - $result = Datefunc::fromYMD(1901, 1, 31); + $result = Date::fromYMD(1901, 1, 31); self::assertEquals($result, '#NUM!'); } } From 9b34f8746bc931d0a2e6785625050841171a23a5 Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Wed, 12 May 2021 17:03:06 +0200 Subject: [PATCH 57/57] Use correct Exception, ensure that Shared\Date is referenced by a synonym to ensure clarity, and ensure case-sensitivity for class references --- .../Calculation/DateTimeExcel/Constants.php | 1 + .../Calculation/DateTimeExcel/Date.php | 4 ++-- .../Calculation/DateTimeExcel/DateParts.php | 14 ++++++------ .../Calculation/DateTimeExcel/DateValue.php | 4 ++-- .../Calculation/DateTimeExcel/Days.php | 8 +++---- .../Calculation/DateTimeExcel/Days360.php | 6 ++--- .../Calculation/DateTimeExcel/Difference.php | 8 +++---- .../Calculation/DateTimeExcel/Helpers.php | 22 +++++++++---------- .../Calculation/DateTimeExcel/Month.php | 3 +-- .../Calculation/DateTimeExcel/NetworkDays.php | 3 +-- .../Calculation/DateTimeExcel/Time.php | 10 ++++----- .../Calculation/DateTimeExcel/TimeParts.php | 8 +++---- .../Calculation/DateTimeExcel/TimeValue.php | 6 ++--- .../Calculation/DateTimeExcel/Week.php | 16 +++++++------- .../Calculation/DateTimeExcel/WorkDay.php | 3 +-- .../Calculation/DateTimeExcel/YearFrac.php | 6 ++--- 16 files changed, 60 insertions(+), 62 deletions(-) diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Constants.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Constants.php index da1b81c1..1165eb1f 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Constants.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Constants.php @@ -22,6 +22,7 @@ class Constants const DOW_FRIDAY = 6; const DOW_SATURDAY = 7; const STARTWEEK_MONDAY_ISO = 21; + const METHODARR = [ self::STARTWEEK_SUNDAY => self::DOW_SUNDAY, self::DOW_MONDAY, diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php index 3ec6cfbb..d18e2371 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php @@ -2,7 +2,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel; -use Exception; +use PhpOffice\PhpSpreadsheet\Calculation\Exception; use PhpOffice\PhpSpreadsheet\Calculation\Functions; use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper; use PhpOffice\PhpSpreadsheet\Shared\StringHelper; @@ -102,7 +102,7 @@ class Date $year += 1900; } - return $year; + return (int) $year; } /** diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateParts.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateParts.php index f1ada435..37ea0315 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateParts.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateParts.php @@ -4,7 +4,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel; use PhpOffice\PhpSpreadsheet\Calculation\Exception; use PhpOffice\PhpSpreadsheet\Calculation\Functions; -use PhpOffice\PhpSpreadsheet\Shared\Date; +use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper; class DateParts { @@ -36,7 +36,7 @@ class DateParts } // Execute function - $PHPDateObject = Date::excelToDateTimeObject($dateValue); + $PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue); return (int) $PHPDateObject->format('j'); } @@ -62,12 +62,12 @@ class DateParts } catch (Exception $e) { return $e->getMessage(); } - if ($dateValue < 1 && Date::getExcelCalendar() === DATE::CALENDAR_WINDOWS_1900) { + if ($dateValue < 1 && SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_WINDOWS_1900) { return 1; } // Execute function - $PHPDateObject = Date::excelToDateTimeObject($dateValue); + $PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue); return (int) $PHPDateObject->format('n'); } @@ -94,11 +94,11 @@ class DateParts return $e->getMessage(); } - if ($dateValue < 1 && Date::getExcelCalendar() === DATE::CALENDAR_WINDOWS_1900) { + if ($dateValue < 1 && SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_WINDOWS_1900) { return 1900; } // Execute function - $PHPDateObject = Date::excelToDateTimeObject($dateValue); + $PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue); return (int) $PHPDateObject->format('Y'); } @@ -110,7 +110,7 @@ class DateParts private static function weirdCondition($dateValue): int { // Excel does not treat 0 consistently for DAY vs. (MONTH or YEAR) - if (Date::getExcelCalendar() === DATE::CALENDAR_WINDOWS_1900 && Functions::getCompatibilityMode() == Functions::COMPATIBILITY_EXCEL) { + if (SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_WINDOWS_1900 && Functions::getCompatibilityMode() == Functions::COMPATIBILITY_EXCEL) { if (is_bool($dateValue)) { return (int) $dateValue; } diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php index 4c754456..de767fca 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php @@ -4,7 +4,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel; use DateTimeImmutable; use PhpOffice\PhpSpreadsheet\Calculation\Functions; -use PhpOffice\PhpSpreadsheet\Shared\Date; +use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper; class DateValue { @@ -36,7 +36,7 @@ class DateValue public static function fromString($dateValue) { $dti = new DateTimeImmutable(); - $baseYear = Date::getExcelCalendar(); + $baseYear = SharedDateHelper::getExcelCalendar(); $dateValue = trim(Functions::flattenSingleValue($dateValue), '"'); // Strip any ordinals because they're allowed in Excel (English only) $dateValue = preg_replace('/(\d)(st|nd|rd|th)([ -\/])/Ui', '$1$3', $dateValue) ?? ''; diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days.php index 73465ccf..5a97d8df 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days.php @@ -3,9 +3,9 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel; use DateTimeInterface; -use Exception; +use PhpOffice\PhpSpreadsheet\Calculation\Exception; use PhpOffice\PhpSpreadsheet\Calculation\Functions; -use PhpOffice\PhpSpreadsheet\Shared\Date; +use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper; class Days { @@ -34,8 +34,8 @@ class Days } // Execute function - $PHPStartDateObject = Date::excelToDateTimeObject($startDate); - $PHPEndDateObject = Date::excelToDateTimeObject($endDate); + $PHPStartDateObject = SharedDateHelper::excelToDateTimeObject($startDate); + $PHPEndDateObject = SharedDateHelper::excelToDateTimeObject($endDate); $days = Functions::VALUE(); $diff = $PHPStartDateObject->diff($PHPEndDateObject); diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days360.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days360.php index d11cdd16..bbc5d16a 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days360.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days360.php @@ -4,7 +4,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel; use PhpOffice\PhpSpreadsheet\Calculation\Exception; use PhpOffice\PhpSpreadsheet\Calculation\Functions; -use PhpOffice\PhpSpreadsheet\Shared\Date; +use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper; class Days360 { @@ -50,12 +50,12 @@ class Days360 } // Execute function - $PHPStartDateObject = Date::excelToDateTimeObject($startDate); + $PHPStartDateObject = SharedDateHelper::excelToDateTimeObject($startDate); $startDay = $PHPStartDateObject->format('j'); $startMonth = $PHPStartDateObject->format('n'); $startYear = $PHPStartDateObject->format('Y'); - $PHPEndDateObject = Date::excelToDateTimeObject($endDate); + $PHPEndDateObject = SharedDateHelper::excelToDateTimeObject($endDate); $endDay = $PHPEndDateObject->format('j'); $endMonth = $PHPEndDateObject->format('n'); $endYear = $PHPEndDateObject->format('Y'); diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Difference.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Difference.php index 4e061d7d..6adeca17 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Difference.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Difference.php @@ -4,9 +4,9 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel; use DateInterval; use DateTime; -use Exception; +use PhpOffice\PhpSpreadsheet\Calculation\Exception; use PhpOffice\PhpSpreadsheet\Calculation\Functions; -use PhpOffice\PhpSpreadsheet\Shared\Date; +use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper; class Difference { @@ -33,12 +33,12 @@ class Difference } // Execute function - $PHPStartDateObject = Date::excelToDateTimeObject($startDate); + $PHPStartDateObject = SharedDateHelper::excelToDateTimeObject($startDate); $startDays = (int) $PHPStartDateObject->format('j'); $startMonths = (int) $PHPStartDateObject->format('n'); $startYears = (int) $PHPStartDateObject->format('Y'); - $PHPEndDateObject = Date::excelToDateTimeObject($endDate); + $PHPEndDateObject = SharedDateHelper::excelToDateTimeObject($endDate); $endDays = (int) $PHPEndDateObject->format('j'); $endMonths = (int) $PHPEndDateObject->format('n'); $endYears = (int) $PHPEndDateObject->format('Y'); diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php index 2d1b6af1..9503401c 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php @@ -5,7 +5,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel; use DateTime; use PhpOffice\PhpSpreadsheet\Calculation\Exception; use PhpOffice\PhpSpreadsheet\Calculation\Functions; -use PhpOffice\PhpSpreadsheet\Shared\Date; +use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper; class Helpers { @@ -31,7 +31,7 @@ class Helpers public static function getDateValue($dateValue, bool $allowBool = true): float { if (is_object($dateValue)) { - $retval = Date::PHPToExcel($dateValue); + $retval = SharedDateHelper::PHPToExcel($dateValue); if (is_bool($retval)) { throw new Exception(Functions::VALUE()); } @@ -81,7 +81,7 @@ class Helpers public static function adjustDateByMonths($dateValue = 0, float $adjustmentMonths = 0): DateTime { // Execute function - $PHPDateObject = Date::excelToDateTimeObject($dateValue); + $PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue); $oMonth = (int) $PHPDateObject->format('m'); $oYear = (int) $PHPDateObject->format('Y'); @@ -149,7 +149,7 @@ class Helpers ); } $excelDateValue = - Date::formattedPHPToExcel( + SharedDateHelper::formattedPHPToExcel( $dateArray['year'], $dateArray['month'], $dateArray['day'], @@ -162,7 +162,7 @@ class Helpers } // RETURNDATE_UNIX_TIMESTAMP) - return (int) Date::excelToTimestamp($excelDateValue); + return (int) SharedDateHelper::excelToTimestamp($excelDateValue); } /** @@ -177,11 +177,11 @@ class Helpers return $excelDateValue; } if ($retType === Functions::RETURNDATE_UNIX_TIMESTAMP) { - return (int) Date::excelToTimestamp($excelDateValue); + return (int) SharedDateHelper::excelToTimestamp($excelDateValue); } // RETURNDATE_PHP_DATETIME_OBJECT - return Date::excelToDateTimeObject($excelDateValue); + return SharedDateHelper::excelToDateTimeObject($excelDateValue); } /** @@ -196,13 +196,13 @@ class Helpers return $PHPDateObject; } if ($retType === Functions::RETURNDATE_EXCEL) { - return (float) Date::PHPToExcel($PHPDateObject); + return (float) SharedDateHelper::PHPToExcel($PHPDateObject); } // RETURNDATE_UNIX_TIMESTAMP - $stamp = Date::PHPToExcel($PHPDateObject); + $stamp = SharedDateHelper::PHPToExcel($PHPDateObject); $stamp = is_bool($stamp) ? ((int) $stamp) : $stamp; - return (int) Date::excelToTimestamp($stamp); + return (int) SharedDateHelper::excelToTimestamp($stamp); } private static function baseDate(): int @@ -210,7 +210,7 @@ class Helpers if (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE) { return 0; } - if (Date::getExcelCalendar() === Date::CALENDAR_MAC_1904) { + if (SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_MAC_1904) { return 0; } diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Month.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Month.php index 10cb27fd..560b7a80 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Month.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Month.php @@ -2,8 +2,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel; -use Exception; -use PhpOffice\PhpSpreadsheet\Shared\Date; +use PhpOffice\PhpSpreadsheet\Calculation\Exception; class Month { diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/NetworkDays.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/NetworkDays.php index ded4f647..d0f53cf3 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/NetworkDays.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/NetworkDays.php @@ -2,9 +2,8 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel; -use Exception; +use PhpOffice\PhpSpreadsheet\Calculation\Exception; use PhpOffice\PhpSpreadsheet\Calculation\Functions; -use PhpOffice\PhpSpreadsheet\Shared\Date; class NetworkDays { diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Time.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Time.php index c72bcf60..fb5e4965 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Time.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Time.php @@ -5,7 +5,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel; use DateTime; use PhpOffice\PhpSpreadsheet\Calculation\Exception; use PhpOffice\PhpSpreadsheet\Calculation\Functions; -use PhpOffice\PhpSpreadsheet\Shared\Date; +use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper; class Time { @@ -57,13 +57,13 @@ class Time // Execute function $retType = Functions::getReturnDateType(); if ($retType === Functions::RETURNDATE_EXCEL) { - $calendar = Date::getExcelCalendar(); - $date = (int) ($calendar !== Date::CALENDAR_WINDOWS_1900); + $calendar = SharedDateHelper::getExcelCalendar(); + $date = (int) ($calendar !== SharedDateHelper::CALENDAR_WINDOWS_1900); - return (float) Date::formattedPHPToExcel($calendar, 1, $date, $hour, $minute, $second); + return (float) SharedDateHelper::formattedPHPToExcel($calendar, 1, $date, $hour, $minute, $second); } if ($retType === Functions::RETURNDATE_UNIX_TIMESTAMP) { - return (int) Date::excelToTimestamp(Date::formattedPHPToExcel(1970, 1, 1, $hour, $minute, $second)); // -2147468400; // -2147472000 + 3600 + return (int) SharedDateHelper::excelToTimestamp(SharedDateHelper::formattedPHPToExcel(1970, 1, 1, $hour, $minute, $second)); // -2147468400; // -2147472000 + 3600 } // RETURNDATE_PHP_DATETIME_OBJECT // Hour has already been normalized (0-23) above diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeParts.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeParts.php index 3e07da08..49cd983d 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeParts.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeParts.php @@ -4,7 +4,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel; use PhpOffice\PhpSpreadsheet\Calculation\Exception; use PhpOffice\PhpSpreadsheet\Calculation\Functions; -use PhpOffice\PhpSpreadsheet\Shared\Date; +use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper; class TimeParts { @@ -37,7 +37,7 @@ class TimeParts // Execute function $timeValue = fmod($timeValue, 1); - $timeValue = Date::excelToDateTimeObject($timeValue); + $timeValue = SharedDateHelper::excelToDateTimeObject($timeValue); return (int) $timeValue->format('H'); } @@ -71,7 +71,7 @@ class TimeParts // Execute function $timeValue = fmod($timeValue, 1); - $timeValue = Date::excelToDateTimeObject($timeValue); + $timeValue = SharedDateHelper::excelToDateTimeObject($timeValue); return (int) $timeValue->format('i'); } @@ -105,7 +105,7 @@ class TimeParts // Execute function $timeValue = fmod($timeValue, 1); - $timeValue = Date::excelToDateTimeObject($timeValue); + $timeValue = SharedDateHelper::excelToDateTimeObject($timeValue); return (int) $timeValue->format('s'); } diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeValue.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeValue.php index 67600761..73b7ba91 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeValue.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeValue.php @@ -4,7 +4,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel; use Datetime; use PhpOffice\PhpSpreadsheet\Calculation\Functions; -use PhpOffice\PhpSpreadsheet\Shared\Date; +use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper; class TimeValue { @@ -44,13 +44,13 @@ class TimeValue $retValue = Functions::VALUE(); if (($PHPDateArray !== false) && ($PHPDateArray['error_count'] == 0)) { // OpenOffice-specific code removed - it works just like Excel - $excelDateValue = Date::formattedPHPToExcel(1900, 1, 1, $PHPDateArray['hour'], $PHPDateArray['minute'], $PHPDateArray['second']) - 1; + $excelDateValue = SharedDateHelper::formattedPHPToExcel(1900, 1, 1, $PHPDateArray['hour'], $PHPDateArray['minute'], $PHPDateArray['second']) - 1; $retType = Functions::getReturnDateType(); if ($retType === Functions::RETURNDATE_EXCEL) { $retValue = (float) $excelDateValue; } elseif ($retType === Functions::RETURNDATE_UNIX_TIMESTAMP) { - $retValue = (int) $phpDateValue = Date::excelToTimestamp($excelDateValue + 25569) - 3600; + $retValue = (int) $phpDateValue = SharedDateHelper::excelToTimestamp($excelDateValue + 25569) - 3600; } else { $retValue = new DateTime('1900-01-01 ' . $PHPDateArray['hour'] . ':' . $PHPDateArray['minute'] . ':' . $PHPDateArray['second']); } diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Week.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Week.php index 928bfc14..66362221 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Week.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Week.php @@ -5,7 +5,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel; use DateTime; use PhpOffice\PhpSpreadsheet\Calculation\Exception; use PhpOffice\PhpSpreadsheet\Calculation\Functions; -use PhpOffice\PhpSpreadsheet\Shared\Date; +use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper; class Week { @@ -45,7 +45,7 @@ class Week try { $method = self::validateMethod($method); if ($dateValue === null) { // boolean not allowed - $dateValue = (Date::getExcelCalendar() === DATE::CALENDAR_MAC_1904 || $method === Constants::DOW_SUNDAY) ? 0 : 1; + $dateValue = (SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_MAC_1904 || $method === Constants::DOW_SUNDAY) ? 0 : 1; } $dateValue = self::validateDateValue($dateValue); if (!$dateValue && self::buggyWeekNum1900($method)) { @@ -57,7 +57,7 @@ class Week } // Execute function - $PHPDateObject = Date::excelToDateTimeObject($dateValue); + $PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue); if ($method == Constants::STARTWEEK_MONDAY_ISO) { Helpers::silly1900($PHPDateObject); @@ -104,7 +104,7 @@ class Week } // Execute function - $PHPDateObject = Date::excelToDateTimeObject($dateValue); + $PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue); Helpers::silly1900($PHPDateObject); return (int) $PHPDateObject->format('W'); @@ -138,7 +138,7 @@ class Week } // Execute function - $PHPDateObject = Date::excelToDateTimeObject($dateValue); + $PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue); Helpers::silly1900($PHPDateObject); $DoW = (int) $PHPDateObject->format('w'); @@ -189,7 +189,7 @@ class Week */ private static function apparentBug($dateValue): bool { - if (Date::getExcelCalendar() !== DATE::CALENDAR_MAC_1904) { + if (SharedDateHelper::getExcelCalendar() !== SharedDateHelper::CALENDAR_MAC_1904) { if (is_bool($dateValue)) { return true; } @@ -241,14 +241,14 @@ class Week private static function buggyWeekNum1900(int $method): bool { - return $method === Constants::DOW_SUNDAY && Date::getExcelCalendar() === Date::CALENDAR_WINDOWS_1900; + return $method === Constants::DOW_SUNDAY && SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_WINDOWS_1900; } private static function buggyWeekNum1904(int $method, bool $origNull, DateTime $dateObject): bool { // This appears to be another Excel bug. - return $method === Constants::DOW_SUNDAY && Date::getExcelCalendar() === Date::CALENDAR_MAC_1904 && + return $method === Constants::DOW_SUNDAY && SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_MAC_1904 && !$origNull && $dateObject->format('Y-m-d') === '1904-01-01'; } } diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php index ca8bd6b8..89e47b96 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php @@ -2,9 +2,8 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel; -use Exception; +use PhpOffice\PhpSpreadsheet\Calculation\Exception; use PhpOffice\PhpSpreadsheet\Calculation\Functions; -use PhpOffice\PhpSpreadsheet\Shared\Date; class WorkDay { diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php index 18b259ed..da2ac12f 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php @@ -2,9 +2,9 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel; -use Exception; +use PhpOffice\PhpSpreadsheet\Calculation\Exception; use PhpOffice\PhpSpreadsheet\Calculation\Functions; -use PhpOffice\PhpSpreadsheet\Shared\Date; +use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper; class YearFrac { @@ -72,7 +72,7 @@ class YearFrac */ private static function excelBug(float $sDate, $startDate, $endDate, int $method): float { - if (Functions::getCompatibilityMode() !== Functions::COMPATIBILITY_OPENOFFICE && Date::getExcelCalendar() !== Date::CALENDAR_MAC_1904) { + if (Functions::getCompatibilityMode() !== Functions::COMPATIBILITY_OPENOFFICE && SharedDateHelper::getExcelCalendar() !== SharedDateHelper::CALENDAR_MAC_1904) { if ($endDate === null && $startDate !== null) { if (DateParts::month($sDate) == 12 && DateParts::day($sDate) === 31 && $method === 0) { $sDate += 2;