diff --git a/CHANGELOG.md b/CHANGELOG.md index b6010729..225ea2af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Implementation of the `ARRAYTOTEXT()` and `VALUETOTEXT()` Excel Functions - Support for [mitoteam/jpgraph](https://packagist.org/packages/mitoteam/jpgraph) implementation of JpGraph library to render charts added. -- Charts: Add Gradients, Transparency, Hidden Axes, Rounded Corners, Trendlines. +- Charts: Add Gradients, Transparency, Hidden Axes, Rounded Corners, Trendlines, Date Axes. ### Changed @@ -50,6 +50,11 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Add setName Method for Chart [Issue #2991](https://github.com/PHPOffice/PhpSpreadsheet/issues/2991) [PR #3001](https://github.com/PHPOffice/PhpSpreadsheet/pull/3001) - Eliminate partial dependency on php-intl in StringHelper [Issue #2982](https://github.com/PHPOffice/PhpSpreadsheet/issues/2982) [PR #2994](https://github.com/PHPOffice/PhpSpreadsheet/pull/2994) - Minor changes for Pdf [Issue #2999](https://github.com/PHPOffice/PhpSpreadsheet/issues/2999) [PR #3002](https://github.com/PHPOffice/PhpSpreadsheet/pull/3002) [PR #3006](https://github.com/PHPOffice/PhpSpreadsheet/pull/3006) +- Html/Pdf Do net set background color for cells using (default) nofill [PR #3016](https://github.com/PHPOffice/PhpSpreadsheet/pull/3016) +- Add support for Date Axis to Chart [Issue #2967](https://github.com/PHPOffice/PhpSpreadsheet/issues/2967) [PR #3018](https://github.com/PHPOffice/PhpSpreadsheet/pull/3018) +- Reconcile Differences Between Css and Excel for Cell Alignment [PR #3048](https://github.com/PHPOffice/PhpSpreadsheet/pull/3048) +- R1C1 Format Internationalization and Better Support for Relative Offsets [Issue #1704](https://github.com/PHPOffice/PhpSpreadsheet/issues/1704) [PR #3052](https://github.com/PHPOffice/PhpSpreadsheet/pull/3052) +- Minor Fix for Percentage Formatting [Issue #1929](https://github.com/PHPOffice/PhpSpreadsheet/issues/1929) [PR #3053](https://github.com/PHPOffice/PhpSpreadsheet/pull/3053) ## 1.24.1 - 2022-07-18 diff --git a/composer.json b/composer.json index a4b033cd..ea7f57b1 100644 --- a/composer.json +++ b/composer.json @@ -81,7 +81,7 @@ "dealerdirect/phpcodesniffer-composer-installer": "dev-master", "dompdf/dompdf": "^1.0 || ^2.0", "friendsofphp/php-cs-fixer": "^3.2", - "mitoteam/jpgraph": "10.2.3", + "mitoteam/jpgraph": "10.2.4", "mpdf/mpdf": "8.1.1", "phpcompatibility/php-compatibility": "^9.3", "phpstan/phpstan": "^1.1", diff --git a/composer.lock b/composer.lock index 508733ee..af246273 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": "b5bdb9f96d18ce59557436521053fdd9", + "content-hash": "6d946e91cbe5d38e1cfb0208512ab981", "packages": [ { "name": "ezyang/htmlpurifier", @@ -1342,16 +1342,16 @@ }, { "name": "mitoteam/jpgraph", - "version": "10.2.3", + "version": "10.2.4", "source": { "type": "git", "url": "https://github.com/mitoteam/jpgraph.git", - "reference": "21121535537e05c32e7964327b80746462a6057d" + "reference": "9ce4d106a89f120c7e220ea22205ef7956a7027b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mitoteam/jpgraph/zipball/21121535537e05c32e7964327b80746462a6057d", - "reference": "21121535537e05c32e7964327b80746462a6057d", + "url": "https://api.github.com/repos/mitoteam/jpgraph/zipball/9ce4d106a89f120c7e220ea22205ef7956a7027b", + "reference": "9ce4d106a89f120c7e220ea22205ef7956a7027b", "shasum": "" }, "require": { @@ -1382,9 +1382,9 @@ ], "support": { "issues": "https://github.com/mitoteam/jpgraph/issues", - "source": "https://github.com/mitoteam/jpgraph/tree/10.2.3" + "source": "https://github.com/mitoteam/jpgraph/tree/10.2.4" }, - "time": "2022-09-14T04:02:09+00:00" + "time": "2022-09-15T05:57:43+00:00" }, { "name": "mpdf/mpdf", diff --git a/infra/LocaleGenerator.php b/infra/LocaleGenerator.php index bb97754d..992bde17 100644 --- a/infra/LocaleGenerator.php +++ b/infra/LocaleGenerator.php @@ -146,7 +146,7 @@ class LocaleGenerator $translationValue = $translationCell->getValue(); if ($this->isFunctionCategoryEntry($translationCell)) { $this->writeFileSectionHeader($functionFile, "{$translationValue} ({$functionName})"); - } elseif (!array_key_exists($functionName, $this->phpSpreadsheetFunctions)) { + } elseif (!array_key_exists($functionName, $this->phpSpreadsheetFunctions) && substr($functionName, 0, 1) !== '*') { $this->log("Function {$functionName} is not defined in PhpSpreadsheet"); } elseif (!empty($translationValue)) { $functionTranslation = "{$functionName} = {$translationValue}" . self::EOL; diff --git a/src/PhpSpreadsheet/Calculation/Calculation.php b/src/PhpSpreadsheet/Calculation/Calculation.php index b5a26f62..94eadd85 100644 --- a/src/PhpSpreadsheet/Calculation/Calculation.php +++ b/src/PhpSpreadsheet/Calculation/Calculation.php @@ -3110,7 +3110,7 @@ class Calculation [$localeFunction] = explode('##', $localeFunction); // Strip out comments if (strpos($localeFunction, '=') !== false) { [$fName, $lfName] = array_map('trim', explode('=', $localeFunction)); - if ((isset(self::$phpSpreadsheetFunctions[$fName])) && ($lfName != '') && ($fName != $lfName)) { + if ((substr($fName, 0, 1) === '*' || isset(self::$phpSpreadsheetFunctions[$fName])) && ($lfName != '') && ($fName != $lfName)) { self::$localeFunctions[$fName] = $lfName; } } diff --git a/src/PhpSpreadsheet/Calculation/LookupRef/Address.php b/src/PhpSpreadsheet/Calculation/LookupRef/Address.php index c8cdf2dd..0d2db8b2 100644 --- a/src/PhpSpreadsheet/Calculation/LookupRef/Address.php +++ b/src/PhpSpreadsheet/Calculation/LookupRef/Address.php @@ -4,6 +4,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef; use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled; use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError; +use PhpOffice\PhpSpreadsheet\Cell\AddressHelper; use PhpOffice\PhpSpreadsheet\Cell\Coordinate; class Address @@ -72,6 +73,9 @@ class Address $sheetName = self::sheetName($sheetName); + if (is_int($referenceStyle)) { + $referenceStyle = (bool) $referenceStyle; + } if ((!is_bool($referenceStyle)) || $referenceStyle === self::REFERENCE_STYLE_A1) { return self::formatAsA1($row, $column, $relativity, $sheetName); } @@ -113,7 +117,8 @@ class Address if (($relativity == self::ADDRESS_ROW_RELATIVE) || ($relativity == self::ADDRESS_RELATIVE)) { $row = "[{$row}]"; } + [$rowChar, $colChar] = AddressHelper::getRowAndColumnChars(); - return "{$sheetName}R{$row}C{$column}"; + return "{$sheetName}$rowChar{$row}$colChar{$column}"; } } diff --git a/src/PhpSpreadsheet/Calculation/LookupRef/Helpers.php b/src/PhpSpreadsheet/Calculation/LookupRef/Helpers.php index 7408a66e..76a194b3 100644 --- a/src/PhpSpreadsheet/Calculation/LookupRef/Helpers.php +++ b/src/PhpSpreadsheet/Calculation/LookupRef/Helpers.php @@ -13,12 +13,12 @@ class Helpers public const CELLADDRESS_USE_R1C1 = false; - private static function convertR1C1(string &$cellAddress1, ?string &$cellAddress2, bool $a1): string + private static function convertR1C1(string &$cellAddress1, ?string &$cellAddress2, bool $a1, ?int $baseRow = null, ?int $baseCol = null): string { if ($a1 === self::CELLADDRESS_USE_R1C1) { - $cellAddress1 = AddressHelper::convertToA1($cellAddress1); + $cellAddress1 = AddressHelper::convertToA1($cellAddress1, $baseRow ?? 1, $baseCol ?? 1); if ($cellAddress2) { - $cellAddress2 = AddressHelper::convertToA1($cellAddress2); + $cellAddress2 = AddressHelper::convertToA1($cellAddress2, $baseRow ?? 1, $baseCol ?? 1); } } @@ -35,7 +35,7 @@ class Helpers } } - public static function extractCellAddresses(string $cellAddress, bool $a1, Worksheet $sheet, string $sheetName = ''): array + public static function extractCellAddresses(string $cellAddress, bool $a1, Worksheet $sheet, string $sheetName = '', ?int $baseRow = null, ?int $baseCol = null): array { $cellAddress1 = $cellAddress; $cellAddress2 = null; @@ -52,7 +52,7 @@ class Helpers if (strpos($cellAddress, ':') !== false) { [$cellAddress1, $cellAddress2] = explode(':', $cellAddress); } - $cellAddress = self::convertR1C1($cellAddress1, $cellAddress2, $a1); + $cellAddress = self::convertR1C1($cellAddress1, $cellAddress2, $a1, $baseRow, $baseCol); return [$cellAddress1, $cellAddress2, $cellAddress]; } diff --git a/src/PhpSpreadsheet/Calculation/LookupRef/Indirect.php b/src/PhpSpreadsheet/Calculation/LookupRef/Indirect.php index 417a1f79..91a14491 100644 --- a/src/PhpSpreadsheet/Calculation/LookupRef/Indirect.php +++ b/src/PhpSpreadsheet/Calculation/LookupRef/Indirect.php @@ -7,6 +7,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Calculation\Functions; use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError; use PhpOffice\PhpSpreadsheet\Cell\Cell; +use PhpOffice\PhpSpreadsheet\Cell\Coordinate; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; class Indirect @@ -63,6 +64,8 @@ class Indirect */ public static function INDIRECT($cellAddress, $a1fmt, Cell $cell) { + [$baseCol, $baseRow] = Coordinate::indexesFromString($cell->getCoordinate()); + try { $a1 = self::a1Format($a1fmt); $cellAddress = self::validateAddress($cellAddress); @@ -78,7 +81,11 @@ class Indirect $cellAddress = self::handleRowColumnRanges($worksheet, ...explode(':', $cellAddress)); } - [$cellAddress1, $cellAddress2, $cellAddress] = Helpers::extractCellAddresses($cellAddress, $a1, $cell->getWorkSheet(), $sheetName); + try { + [$cellAddress1, $cellAddress2, $cellAddress] = Helpers::extractCellAddresses($cellAddress, $a1, $cell->getWorkSheet(), $sheetName, $baseRow, $baseCol); + } catch (Exception $e) { + return ExcelError::REF(); + } if ( (!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/miu', $cellAddress1, $matches)) || diff --git a/src/PhpSpreadsheet/Calculation/locale/Translations.xlsx b/src/PhpSpreadsheet/Calculation/locale/Translations.xlsx index d013aee0..7b9fb0dd 100644 Binary files a/src/PhpSpreadsheet/Calculation/locale/Translations.xlsx and b/src/PhpSpreadsheet/Calculation/locale/Translations.xlsx differ diff --git a/src/PhpSpreadsheet/Calculation/locale/da/functions b/src/PhpSpreadsheet/Calculation/locale/da/functions index 6260760b..03b68c9b 100644 --- a/src/PhpSpreadsheet/Calculation/locale/da/functions +++ b/src/PhpSpreadsheet/Calculation/locale/da/functions @@ -245,6 +245,7 @@ ROWS = RÆKKER RTD = RTD TRANSPOSE = TRANSPONER VLOOKUP = LOPSLAG +*RC = RK ## ## Matematiske og trigonometriske funktioner (Math & Trig Functions) diff --git a/src/PhpSpreadsheet/Calculation/locale/de/functions b/src/PhpSpreadsheet/Calculation/locale/de/functions index 331232f7..d49fc5f1 100644 --- a/src/PhpSpreadsheet/Calculation/locale/de/functions +++ b/src/PhpSpreadsheet/Calculation/locale/de/functions @@ -243,6 +243,7 @@ ROWS = ZEILEN RTD = RTD TRANSPOSE = MTRANS VLOOKUP = SVERWEIS +*RC = ZS ## ## Mathematische und trigonometrische Funktionen (Math & Trig Functions) diff --git a/src/PhpSpreadsheet/Calculation/locale/es/functions b/src/PhpSpreadsheet/Calculation/locale/es/functions index 1f9f2891..88012aa1 100644 --- a/src/PhpSpreadsheet/Calculation/locale/es/functions +++ b/src/PhpSpreadsheet/Calculation/locale/es/functions @@ -245,6 +245,7 @@ ROWS = FILAS RTD = RDTR TRANSPOSE = TRANSPONER VLOOKUP = BUSCARV +*RC = FC ## ## Funciones matemáticas y trigonométricas (Math & Trig Functions) diff --git a/src/PhpSpreadsheet/Calculation/locale/fi/functions b/src/PhpSpreadsheet/Calculation/locale/fi/functions index 33068d93..18f7c8c8 100644 --- a/src/PhpSpreadsheet/Calculation/locale/fi/functions +++ b/src/PhpSpreadsheet/Calculation/locale/fi/functions @@ -245,6 +245,7 @@ ROWS = RIVIT RTD = RTD TRANSPOSE = TRANSPONOI VLOOKUP = PHAKU +*RC = RS ## ## Matemaattiset ja trigonometriset funktiot (Math & Trig Functions) diff --git a/src/PhpSpreadsheet/Calculation/locale/fr/functions b/src/PhpSpreadsheet/Calculation/locale/fr/functions index 78b603e9..621cb0db 100644 --- a/src/PhpSpreadsheet/Calculation/locale/fr/functions +++ b/src/PhpSpreadsheet/Calculation/locale/fr/functions @@ -240,6 +240,7 @@ ROWS = LIGNES RTD = RTD TRANSPOSE = TRANSPOSE VLOOKUP = RECHERCHEV +*RC = LC ## ## Fonctions mathématiques et trigonométriques (Math & Trig Functions) diff --git a/src/PhpSpreadsheet/Calculation/locale/hu/functions b/src/PhpSpreadsheet/Calculation/locale/hu/functions index 46b30127..4a375ea2 100644 --- a/src/PhpSpreadsheet/Calculation/locale/hu/functions +++ b/src/PhpSpreadsheet/Calculation/locale/hu/functions @@ -245,6 +245,7 @@ ROWS = SOROK RTD = VIA TRANSPOSE = TRANSZPONÁLÁS VLOOKUP = FKERES +*RC = SO ## ## Matematikai és trigonometrikus függvények (Math & Trig Functions) diff --git a/src/PhpSpreadsheet/Calculation/locale/nb/functions b/src/PhpSpreadsheet/Calculation/locale/nb/functions index b0a0f949..d352e1f4 100644 --- a/src/PhpSpreadsheet/Calculation/locale/nb/functions +++ b/src/PhpSpreadsheet/Calculation/locale/nb/functions @@ -245,6 +245,7 @@ ROWS = RADER RTD = RTD TRANSPOSE = TRANSPONER VLOOKUP = FINN.RAD +*RC = RK ## ## Matematikk- og trigonometrifunksjoner (Math & Trig Functions) diff --git a/src/PhpSpreadsheet/Calculation/locale/nl/functions b/src/PhpSpreadsheet/Calculation/locale/nl/functions index 0e4f1597..ce0b30cc 100644 --- a/src/PhpSpreadsheet/Calculation/locale/nl/functions +++ b/src/PhpSpreadsheet/Calculation/locale/nl/functions @@ -244,6 +244,7 @@ ROWS = RIJEN RTD = RTG TRANSPOSE = TRANSPONEREN VLOOKUP = VERT.ZOEKEN +*RC = RK ## ## Wiskundige en trigonometrische functies (Math & Trig Functions) diff --git a/src/PhpSpreadsheet/Calculation/locale/pt/br/functions b/src/PhpSpreadsheet/Calculation/locale/pt/br/functions index feba30d9..5781b0c7 100644 --- a/src/PhpSpreadsheet/Calculation/locale/pt/br/functions +++ b/src/PhpSpreadsheet/Calculation/locale/pt/br/functions @@ -242,6 +242,7 @@ ROWS = LINS RTD = RTD TRANSPOSE = TRANSPOR VLOOKUP = PROCV +*RC = LC ## ## Funções matemáticas e trigonométricas (Math & Trig Functions) diff --git a/src/PhpSpreadsheet/Calculation/locale/pt/functions b/src/PhpSpreadsheet/Calculation/locale/pt/functions index 8a94d826..70a3bb0c 100644 --- a/src/PhpSpreadsheet/Calculation/locale/pt/functions +++ b/src/PhpSpreadsheet/Calculation/locale/pt/functions @@ -245,6 +245,7 @@ ROWS = LINS RTD = RTD TRANSPOSE = TRANSPOR VLOOKUP = PROCV +*RC = LC ## ## Funções matemáticas e trigonométricas (Math & Trig Functions) diff --git a/src/PhpSpreadsheet/Calculation/locale/sv/functions b/src/PhpSpreadsheet/Calculation/locale/sv/functions index 2531b4c1..491ecfb9 100644 --- a/src/PhpSpreadsheet/Calculation/locale/sv/functions +++ b/src/PhpSpreadsheet/Calculation/locale/sv/functions @@ -243,6 +243,7 @@ ROWS = RADER RTD = RTD TRANSPOSE = TRANSPONERA VLOOKUP = LETARAD +*RC = RK ## ## Matematiska och trigonometriska funktioner (Math & Trig Functions) diff --git a/src/PhpSpreadsheet/Cell/AddressHelper.php b/src/PhpSpreadsheet/Cell/AddressHelper.php index 535bdee0..a23a78b6 100644 --- a/src/PhpSpreadsheet/Cell/AddressHelper.php +++ b/src/PhpSpreadsheet/Cell/AddressHelper.php @@ -2,23 +2,44 @@ namespace PhpOffice\PhpSpreadsheet\Cell; +use PhpOffice\PhpSpreadsheet\Calculation\Calculation; +use PhpOffice\PhpSpreadsheet\Calculation\Functions; use PhpOffice\PhpSpreadsheet\Exception; class AddressHelper { public const R1C1_COORDINATE_REGEX = '/(R((?:\[-?\d*\])|(?:\d*))?)(C((?:\[-?\d*\])|(?:\d*))?)/i'; + /** @return string[] */ + public static function getRowAndColumnChars() + { + $rowChar = 'R'; + $colChar = 'C'; + if (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_EXCEL) { + $rowColChars = Calculation::localeFunc('*RC'); + if (mb_strlen($rowColChars) === 2) { + $rowChar = mb_substr($rowColChars, 0, 1); + $colChar = mb_substr($rowColChars, 1, 1); + } + } + + return [$rowChar, $colChar]; + } + /** * Converts an R1C1 format cell address to an A1 format cell address. */ public static function convertToA1( string $address, int $currentRowNumber = 1, - int $currentColumnNumber = 1 + int $currentColumnNumber = 1, + bool $useLocale = true ): string { - $validityCheck = preg_match('/^(R(\[?-?\d*\]?))(C(\[?-?\d*\]?))$/i', $address, $cellReference); + [$rowChar, $colChar] = $useLocale ? self::getRowAndColumnChars() : ['R', 'C']; + $regex = '/^(' . $rowChar . '(\[?[-+]?\d*\]?))(' . $colChar . '(\[?[-+]?\d*\]?))$/i'; + $validityCheck = preg_match($regex, $address, $cellReference); - if ($validityCheck === 0) { + if (empty($validityCheck)) { throw new Exception('Invalid R1C1-format Cell Reference'); } @@ -92,7 +113,7 @@ class AddressHelper // Loop through each R1C1 style reference in turn, converting it to its A1 style equivalent, // then modify the formula to use that new reference foreach ($cellReferences as $cellReference) { - $A1CellReference = self::convertToA1($cellReference[0][0], $currentRowNumber, $currentColumnNumber); + $A1CellReference = self::convertToA1($cellReference[0][0], $currentRowNumber, $currentColumnNumber, false); $value = substr_replace($value, $A1CellReference, $cellReference[0][1], strlen($cellReference[0][0])); } } diff --git a/src/PhpSpreadsheet/Reader/Xlsx/Chart.php b/src/PhpSpreadsheet/Reader/Xlsx/Chart.php index 6bc6d7c5..c22334ca 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx/Chart.php +++ b/src/PhpSpreadsheet/Reader/Xlsx/Chart.php @@ -14,6 +14,7 @@ use PhpOffice\PhpSpreadsheet\Chart\PlotArea; use PhpOffice\PhpSpreadsheet\Chart\Properties as ChartProperties; use PhpOffice\PhpSpreadsheet\Chart\Title; use PhpOffice\PhpSpreadsheet\Chart\TrendLine; +use PhpOffice\PhpSpreadsheet\Reader\Xlsx; use PhpOffice\PhpSpreadsheet\RichText\RichText; use PhpOffice\PhpSpreadsheet\Style\Font; use SimpleXMLElement; @@ -94,7 +95,7 @@ class Chart break; case 'chart': foreach ($chartElement as $chartDetailsKey => $chartDetails) { - $chartDetailsC = $chartDetails->children($this->cNamespace); + $chartDetails = Xlsx::testSimpleXml($chartDetails); switch ($chartDetailsKey) { case 'autoTitleDeleted': /** @var bool */ @@ -113,8 +114,8 @@ class Chart $plotSeries = $plotAttributes = []; $catAxRead = false; $plotNoFill = false; - /** @var SimpleXMLElement $chartDetail */ foreach ($chartDetails as $chartDetailKey => $chartDetail) { + $chartDetail = Xlsx::testSimpleXml($chartDetail); switch ($chartDetailKey) { case 'spPr': $possibleNoFill = $chartDetails->spPr->children($this->aNamespace); @@ -122,8 +123,8 @@ class Chart $plotNoFill = true; } if (isset($possibleNoFill->gradFill->gsLst)) { - /** @var SimpleXMLElement $gradient */ foreach ($possibleNoFill->gradFill->gsLst->gs as $gradient) { + $gradient = Xlsx::testSimpleXml($gradient); /** @var float */ $pos = self::getAttribute($gradient, 'pos', 'float'); $gradientArray[] = [ @@ -348,6 +349,7 @@ class Chart $legendLayout = null; $legendOverlay = false; foreach ($chartDetails as $chartDetailKey => $chartDetail) { + $chartDetail = Xlsx::testSimpleXml($chartDetail); switch ($chartDetailKey) { case 'legendPos': $legendPos = self::getAttribute($chartDetail, 'val', 'string'); @@ -399,11 +401,13 @@ class Chart $caption = []; $titleLayout = null; foreach ($titleDetails as $titleDetailKey => $chartDetail) { + $chartDetail = Xlsx::testSimpleXml($chartDetail); switch ($titleDetailKey) { case 'tx': if (isset($chartDetail->rich)) { $titleDetails = $chartDetail->rich->children($this->aNamespace); foreach ($titleDetails as $titleKey => $titleDetail) { + $titleDetail = Xlsx::testSimpleXml($titleDetail); switch ($titleKey) { case 'p': $titleDetailPart = $titleDetail->children($this->aNamespace); @@ -440,6 +444,7 @@ class Chart } $layout = []; foreach ($details as $detailKey => $detail) { + $detail = Xlsx::testSimpleXml($detail); $layout[$detailKey] = self::getAttribute($detail, 'val', 'string'); } @@ -472,8 +477,8 @@ class Chart $lineStyle = null; $labelLayout = null; $trendLines = []; - /** @var SimpleXMLElement $seriesDetail */ foreach ($seriesDetails as $seriesKey => $seriesDetail) { + $seriesDetail = Xlsx::testSimpleXml($seriesDetail); switch ($seriesKey) { case 'idx': $seriesIndex = self::getAttribute($seriesDetail, 'val', 'integer'); @@ -786,6 +791,7 @@ class Chart $pointCount = 0; foreach ($seriesValueSet as $seriesValueIdx => $seriesValue) { + $seriesValue = Xlsx::testSimpleXml($seriesValue); switch ($seriesValueIdx) { case 'ptCount': $pointCount = self::getAttribute($seriesValue, 'val', 'integer'); @@ -858,7 +864,6 @@ class Chart private function parseRichText(SimpleXMLElement $titleDetailPart): RichText { $value = new RichText(); - $objText = null; $defaultFontSize = null; $defaultBold = null; $defaultItalic = null; diff --git a/src/PhpSpreadsheet/Writer/Html.php b/src/PhpSpreadsheet/Writer/Html.php index fca5ee89..0fef0f60 100644 --- a/src/PhpSpreadsheet/Writer/Html.php +++ b/src/PhpSpreadsheet/Writer/Html.php @@ -1461,9 +1461,6 @@ class Html extends BaseWriter foreach ($values as $cellAddress) { [$cell, $cssClass, $coordinate] = $this->generateRowCellCss($worksheet, $cellAddress, $row, $colNum); - $colSpan = 1; - $rowSpan = 1; - // Cell Data $cellData = $this->generateRowCellData($worksheet, $cell, $cssClass, $cellType); diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/AddressInternationalTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/AddressInternationalTest.php new file mode 100644 index 00000000..87e0bb4e --- /dev/null +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/AddressInternationalTest.php @@ -0,0 +1,79 @@ +locale = Settings::getLocale(); + } + + protected function tearDown(): void + { + Settings::setLocale($this->locale); + // CompatibilityMode is restored in parent + parent::tearDown(); + } + + /** + * @dataProvider providerInternational + */ + public function testR1C1International(string $locale, string $r, string $c): void + { + if ($locale !== '') { + Settings::setLocale($locale); + } + $sheet = $this->getSheet(); + $sheet->getCell('A1')->setValue('=LEFT(ADDRESS(1,1,1,0),1)'); + $sheet->getCell('A2')->setValue('=MID(ADDRESS(1,1,1,0),3,1)'); + self::assertSame($r, $sheet->getCell('A1')->getCalculatedValue()); + self::assertSame($c, $sheet->getCell('A2')->getCalculatedValue()); + } + + public function providerInternational(): array + { + return [ + 'Default' => ['', 'R', 'C'], + 'English' => ['en', 'R', 'C'], + 'French' => ['fr', 'L', 'C'], + 'German' => ['de', 'Z', 'S'], + 'Made-up' => ['xx', 'R', 'C'], + 'Spanish' => ['es', 'F', 'C'], + 'Bulgarian' => ['bg', 'R', 'C'], + 'Czech' => ['cs', 'R', 'C'], // maybe should be R/S + 'Polish' => ['pl', 'R', 'C'], // maybe should be W/K + 'Turkish' => ['tr', 'R', 'C'], + ]; + } + + /** + * @dataProvider providerCompatibility + */ + public function testCompatibilityInternational(string $compatibilityMode, string $r, string $c): void + { + Functions::setCompatibilityMode($compatibilityMode); + Settings::setLocale('de'); + $sheet = $this->getSheet(); + $sheet->getCell('A1')->setValue('=LEFT(ADDRESS(1,1,1,0),1)'); + $sheet->getCell('A2')->setValue('=MID(ADDRESS(1,1,1,0),3,1)'); + self::assertSame($r, $sheet->getCell('A1')->getCalculatedValue()); + self::assertSame($c, $sheet->getCell('A2')->getCalculatedValue()); + } + + public function providerCompatibility(): array + { + return [ + [Functions::COMPATIBILITY_EXCEL, 'Z', 'S'], + [Functions::COMPATIBILITY_OPENOFFICE, 'R', 'C'], + [Functions::COMPATIBILITY_GNUMERIC, 'R', 'C'], + ]; + } +} diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/AddressTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/AddressTest.php index 2b92030e..2a6ae883 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/AddressTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/AddressTest.php @@ -3,17 +3,11 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\LookupRef; use PhpOffice\PhpSpreadsheet\Calculation\Calculation; -use PhpOffice\PhpSpreadsheet\Calculation\Functions; use PhpOffice\PhpSpreadsheet\Calculation\LookupRef; use PHPUnit\Framework\TestCase; class AddressTest extends TestCase { - protected function setUp(): void - { - Functions::setCompatibilityMode(Functions::COMPATIBILITY_EXCEL); - } - /** * @dataProvider providerADDRESS * diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/IndirectInternationalTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/IndirectInternationalTest.php new file mode 100644 index 00000000..7d6d0a65 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/IndirectInternationalTest.php @@ -0,0 +1,132 @@ +locale = Settings::getLocale(); + } + + protected function tearDown(): void + { + Settings::setLocale($this->locale); + // CompatibilityMode is restored in parent + parent::tearDown(); + } + + /** + * @dataProvider providerInternational + */ + public function testR1C1International(string $locale): void + { + Settings::setLocale($locale); + $sameAsEnglish = ['en', 'xx', 'ru', 'tr', 'cs', 'pl']; + $sheet = $this->getSheet(); + $sheet->getCell('C1')->setValue('text'); + $sheet->getCell('A2')->setValue('en'); + $sheet->getCell('B2')->setValue('=INDIRECT("R1C3", false)'); + $sheet->getCell('A3')->setValue('fr'); + $sheet->getCell('B3')->setValue('=INDIRECT("L1C3", false)'); + $sheet->getCell('A4')->setValue('de'); + $sheet->getCell('B4')->setValue('=INDIRECT("Z1S3", false)'); + $sheet->getCell('A5')->setValue('es'); + $sheet->getCell('B5')->setValue('=INDIRECT("F1C3", false)'); + $sheet->getCell('A6')->setValue('xx'); + $sheet->getCell('B6')->setValue('=INDIRECT("R1C3", false)'); + $sheet->getCell('A7')->setValue('ru'); + $sheet->getCell('B7')->setValue('=INDIRECT("R1C3", false)'); + $sheet->getCell('A8')->setValue('cs'); + $sheet->getCell('B8')->setValue('=INDIRECT("R1C3", false)'); + $sheet->getCell('A9')->setValue('tr'); + $sheet->getCell('B9')->setValue('=INDIRECT("R1C3", false)'); + $sheet->getCell('A10')->setValue('pl'); + $sheet->getCell('B10')->setValue('=INDIRECT("R1C3", false)'); + $maxRow = $sheet->getHighestRow(); + for ($row = 2; $row <= $maxRow; ++$row) { + $rowLocale = $sheet->getCell("A$row")->getValue(); + if (in_array($rowLocale, $sameAsEnglish, true) && in_array($locale, $sameAsEnglish, true)) { + $expectedResult = 'text'; + } else { + $expectedResult = ($locale === $sheet->getCell("A$row")->getValue()) ? 'text' : '#REF!'; + } + self::assertSame($expectedResult, $sheet->getCell("B$row")->getCalculatedValue(), "Locale $locale error in cell B$row $rowLocale"); + } + } + + public function providerInternational(): array + { + return [ + 'English' => ['en'], + 'French' => ['fr'], + 'German' => ['de'], + 'Made-up' => ['xx'], + 'Spanish' => ['es'], + 'Russian' => ['ru'], + 'Czech' => ['cs'], + 'Polish' => ['pl'], + 'Turkish' => ['tr'], + ]; + } + + /** + * @dataProvider providerRelativeInternational + */ + public function testRelativeInternational(string $locale, string $cell, string $relative): void + { + Settings::setLocale($locale); + $sheet = $this->getSheet(); + $sheet->getCell('C3')->setValue('text'); + $sheet->getCell($cell)->setValue("=INDIRECT(\"$relative\", false)"); + self::assertSame('text', $sheet->getCell($cell)->getCalculatedValue()); + } + + public function providerRelativeInternational(): array + { + return [ + 'English A3' => ['en', 'A3', 'R[]C[+2]'], + 'French B4' => ['fr', 'B4', 'L[-1]C[+1]'], + 'German C5' => ['de', 'C5', 'Z[-2]S[]'], + 'Spanish E1' => ['es', 'E1', 'F[+2]C[-2]'], + ]; + } + + /** + * @dataProvider providerCompatibility + */ + public function testCompatibilityInternational(string $compatibilityMode): void + { + Functions::setCompatibilityMode($compatibilityMode); + if ($compatibilityMode === Functions::COMPATIBILITY_EXCEL) { + $expected1 = '#REF!'; + $expected2 = 'text'; + } else { + $expected2 = '#REF!'; + $expected1 = 'text'; + } + Settings::setLocale('fr'); + $sheet = $this->getSheet(); + $sheet->getCell('C3')->setValue('text'); + $sheet->getCell('A1')->setValue('=INDIRECT("R3C3", false)'); + $sheet->getCell('A2')->setValue('=INDIRECT("L3C3", false)'); + self::assertSame($expected1, $sheet->getCell('A1')->getCalculatedValue()); + self::assertSame($expected2, $sheet->getCell('A2')->getCalculatedValue()); + } + + public function providerCompatibility(): array + { + return [ + [Functions::COMPATIBILITY_EXCEL], + [Functions::COMPATIBILITY_OPENOFFICE], + [Functions::COMPATIBILITY_GNUMERIC], + ]; + } +} diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/IndirectTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/IndirectTest.php index 7601e336..accfc058 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/IndirectTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/LookupRef/IndirectTest.php @@ -132,4 +132,48 @@ class IndirectTest extends AllSetupTeardown $result = \PhpOffice\PhpSpreadsheet\Calculation\Functions::flattenSingleValue($result); self::assertSame('This is it', $result); } + + /** + * @param null|int|string $expectedResult + * + * @dataProvider providerRelative + */ + public function testR1C1Relative($expectedResult, string $address): void + { + $sheet = $this->getSheet(); + $sheet->fromArray([ + ['a1', 'b1', 'c1'], + ['a2', 'b2', 'c2'], + ['a3', 'b3', 'c3'], + ['a4', 'b4', 'c4'], + ]); + $sheet->getCell('B2')->setValue('=INDIRECT("' . $address . '", false)'); + self::assertSame($expectedResult, $sheet->getCell('B2')->getCalculatedValue()); + } + + public function providerRelative(): array + { + return [ + 'same row with bracket next column' => ['c2', 'R[]C[+1]'], + 'same row without bracket next column' => ['c2', 'RC[+1]'], + 'same row without bracket next column no plus sign' => ['c2', 'RC[1]'], + 'same row previous column' => ['a2', 'RC[-1]'], + 'previous row previous column' => ['a1', 'R[-1]C[-1]'], + 'previous row same column with bracket' => ['b1', 'R[-1]C[]'], + 'previous row same column without bracket' => ['b1', 'R[-1]C'], + 'previous row next column' => ['c1', 'R[-1]C[+1]'], + 'next row no plus sign previous column' => ['a3', 'R[1]C[-1]'], + 'next row previous column' => ['a3', 'R[+1]C[-1]'], + 'next row same column' => ['b3', 'R[+1]C'], + 'next row next column' => ['c3', 'R[+1]C[+1]'], + 'two rows down same column' => ['b4', 'R[+2]C'], + 'invalid row' => ['#REF!', 'R[-2]C'], + 'invalid column' => ['#REF!', 'RC[-2]'], + 'circular reference' => [0, 'RC'], // matches Excel's treatment + 'absolute row absolute column' => ['c2', 'R2C3'], + 'absolute row relative column' => ['a2', 'R2C[-1]'], + 'relative row absolute column lowercase' => ['a2', 'rc1'], + 'uninitialized cell' => [null, 'RC[+2]'], // Excel result is 0 + ]; + } } diff --git a/tests/PhpSpreadsheetTests/Calculation/TranslationTest.php b/tests/PhpSpreadsheetTests/Calculation/TranslationTest.php index e2384460..ac1f15a1 100644 --- a/tests/PhpSpreadsheetTests/Calculation/TranslationTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/TranslationTest.php @@ -19,18 +19,23 @@ class TranslationTest extends TestCase */ private $returnDate; + /** @var string */ + private $locale; + protected function setUp(): void { $this->compatibilityMode = Functions::getCompatibilityMode(); $this->returnDate = Functions::getReturnDateType(); Functions::setCompatibilityMode(Functions::COMPATIBILITY_EXCEL); Functions::setReturnDateType(Functions::RETURNDATE_EXCEL); + $this->locale = Settings::getLocale(); } protected function tearDown(): void { Functions::setCompatibilityMode($this->compatibilityMode); Functions::setReturnDateType($this->returnDate); + Settings::setLocale($this->locale); } /** diff --git a/tests/PhpSpreadsheetTests/LocaleGeneratorTest.php b/tests/PhpSpreadsheetTests/LocaleGeneratorTest.php index e7429a25..ca9d5183 100644 --- a/tests/PhpSpreadsheetTests/LocaleGeneratorTest.php +++ b/tests/PhpSpreadsheetTests/LocaleGeneratorTest.php @@ -11,31 +11,59 @@ class LocaleGeneratorTest extends TestCase { public function testLocaleGenerator(): void { + $directory = realpath(__DIR__ . '/../../src/PhpSpreadsheet/Calculation/locale/') ?: ''; + self::assertNotEquals('', $directory); $phpSpreadsheetFunctionsProperty = (new ReflectionClass(Calculation::class)) ->getProperty('phpSpreadsheetFunctions'); $phpSpreadsheetFunctionsProperty->setAccessible(true); $phpSpreadsheetFunctions = $phpSpreadsheetFunctionsProperty->getValue(); $localeGenerator = new LocaleGenerator( - (string) realpath(__DIR__ . '/../../src/PhpSpreadsheet/Calculation/locale/'), + $directory . DIRECTORY_SEPARATOR, 'Translations.xlsx', $phpSpreadsheetFunctions ); $localeGenerator->generateLocales(); $testLocales = [ + 'bg', + 'cs', + 'da', + 'de', + 'en', + 'es', + 'fi', 'fr', + 'hu', + 'it', + 'nb', 'nl', + 'pl', 'pt', - 'pt_br', 'ru', + 'sv', + 'tr', ]; - foreach ($testLocales as $locale) { - $locale = str_replace('_', '/', $locale); - $path = realpath(__DIR__ . "/../../src/PhpSpreadsheet/Calculation/locale/{$locale}"); - self::assertFileExists("{$path}/config"); - self::assertFileExists("{$path}/functions"); + $count = count(glob($directory . DIRECTORY_SEPARATOR . '*') ?: []) - 1; // exclude Translations.xlsx + self::assertCount($count, $testLocales); + $testLocales[] = 'pt_br'; + $testLocales[] = 'en_uk'; + $noconfig = ['en']; + $nofunctions = ['en', 'en_uk']; + foreach ($testLocales as $originalLocale) { + $locale = str_replace('_', DIRECTORY_SEPARATOR, $originalLocale); + $path = $directory . DIRECTORY_SEPARATOR . $locale; + if (in_array($originalLocale, $noconfig, true)) { + self::assertFileDoesNotExist($path . DIRECTORY_SEPARATOR . 'config'); + } else { + self::assertFileExists($path . DIRECTORY_SEPARATOR . 'config'); + } + if (in_array($originalLocale, $nofunctions, true)) { + self::assertFileDoesNotExist($path . DIRECTORY_SEPARATOR . 'functions'); + } else { + self::assertFileExists($path . DIRECTORY_SEPARATOR . 'functions'); + } } } } diff --git a/tests/PhpSpreadsheetTests/Reader/Csv/CsvIssue2232Test.php b/tests/PhpSpreadsheetTests/Reader/Csv/CsvIssue2232Test.php index f9321102..429874dc 100644 --- a/tests/PhpSpreadsheetTests/Reader/Csv/CsvIssue2232Test.php +++ b/tests/PhpSpreadsheetTests/Reader/Csv/CsvIssue2232Test.php @@ -2,11 +2,11 @@ namespace PhpOffice\PhpSpreadsheetTests\Reader\Csv; -use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Cell\Cell; use PhpOffice\PhpSpreadsheet\Cell\IValueBinder; use PhpOffice\PhpSpreadsheet\Cell\StringValueBinder; use PhpOffice\PhpSpreadsheet\Reader\Csv; +use PhpOffice\PhpSpreadsheet\Settings; use PHPUnit\Framework\TestCase; class CsvIssue2232Test extends TestCase @@ -16,14 +16,19 @@ class CsvIssue2232Test extends TestCase */ private $valueBinder; + /** @var string */ + private $locale; + protected function setUp(): void { $this->valueBinder = Cell::getValueBinder(); + $this->locale = Settings::getLocale(); } protected function tearDown(): void { Cell::setValueBinder($this->valueBinder); + Settings::setLocale($this->locale); } /** @@ -78,7 +83,7 @@ class CsvIssue2232Test extends TestCase Cell::setValueBinder($binder); } - Calculation::getInstance()->setLocale('fr'); + Settings::setLocale('fr'); $reader = new Csv(); $filename = 'tests/data/Reader/CSV/issue.2232.csv'; diff --git a/tests/PhpSpreadsheetTests/Reader/Xlsx/AutoFilter2Test.php b/tests/PhpSpreadsheetTests/Reader/Xlsx/AutoFilter2Test.php index 06d6b562..7a264394 100644 --- a/tests/PhpSpreadsheetTests/Reader/Xlsx/AutoFilter2Test.php +++ b/tests/PhpSpreadsheetTests/Reader/Xlsx/AutoFilter2Test.php @@ -93,6 +93,7 @@ class AutoFilter2Test extends TestCase self::assertCount(1, $columns); $column = $columns['A'] ?? null; self::assertNotNull($column); + /** @scrutinizer ignore-call */ $ruleset = $column->getRules(); self::assertCount(1, $ruleset); $rule = $ruleset[0]; diff --git a/tests/PhpSpreadsheetTests/Reader/Xml/PageSetupTest.php b/tests/PhpSpreadsheetTests/Reader/Xml/PageSetupTest.php index 97476ed5..ae79dd0e 100644 --- a/tests/PhpSpreadsheetTests/Reader/Xml/PageSetupTest.php +++ b/tests/PhpSpreadsheetTests/Reader/Xml/PageSetupTest.php @@ -14,19 +14,25 @@ class PageSetupTest extends TestCase private const MARGIN_UNIT_CONVERSION = 2.54; // Inches to cm /** - * @var Spreadsheet + * @var ?Spreadsheet */ private $spreadsheet; - protected function setup(): void + /** @var string */ + private $filename = 'tests/data/Reader/Xml/PageSetup.xml'; + + protected function tearDown(): void { - $filename = 'tests/data/Reader/Xml/PageSetup.xml'; - $reader = new Xml(); - $this->spreadsheet = $reader->load($filename); + if ($this->spreadsheet !== null) { + $this->spreadsheet->disconnectWorksheets(); + $this->spreadsheet = null; + } } public function testPageSetup(): void { + $reader = new Xml(); + $this->spreadsheet = $reader->load($this->filename); $assertions = $this->pageSetupAssertions(); foreach ($this->spreadsheet->getAllSheets() as $worksheet) { @@ -49,6 +55,8 @@ class PageSetupTest extends TestCase public function testPageMargins(): void { + $reader = new Xml(); + $this->spreadsheet = $reader->load($this->filename); $assertions = $this->pageMarginAssertions(); foreach ($this->spreadsheet->getAllSheets() as $worksheet) { diff --git a/tests/PhpSpreadsheetTests/Reader/Xml/XmlLoadTest.php b/tests/PhpSpreadsheetTests/Reader/Xml/XmlLoadTest.php index 29d81299..9846b861 100644 --- a/tests/PhpSpreadsheetTests/Reader/Xml/XmlLoadTest.php +++ b/tests/PhpSpreadsheetTests/Reader/Xml/XmlLoadTest.php @@ -4,18 +4,51 @@ namespace PhpOffice\PhpSpreadsheetTests\Reader\Xml; use DateTimeZone; use PhpOffice\PhpSpreadsheet\Reader\Xml; +use PhpOffice\PhpSpreadsheet\Settings; use PhpOffice\PhpSpreadsheet\Shared\Date; +use PhpOffice\PhpSpreadsheet\Spreadsheet; use PHPUnit\Framework\TestCase; class XmlLoadTest extends TestCase { - public function testLoad(): void + /** @var ?Spreadsheet */ + private $spreadsheet; + + /** @var string */ + private $locale; + + protected function setUp(): void + { + $this->locale = Settings::getLocale(); + } + + protected function tearDown(): void + { + if ($this->spreadsheet !== null) { + $this->spreadsheet->disconnectWorksheets(); + $this->spreadsheet = null; + } + Settings::setLocale($this->locale); + } + + public function testLoadEnglish(): void + { + $this->xtestLoad(); + } + + public function testLoadFrench(): void + { + Settings::setLocale('fr'); + $this->xtestLoad(); + } + + public function xtestLoad(): void { $filename = __DIR__ . '/../../../..' . '/samples/templates/excel2003.xml'; $reader = new Xml(); - $spreadsheet = $reader->load($filename); + $this->spreadsheet = $spreadsheet = $reader->load($filename); self::assertEquals(2, $spreadsheet->getSheetCount()); $sheet = $spreadsheet->getSheet(1); @@ -71,7 +104,7 @@ class XmlLoadTest extends TestCase $reader = new Xml(); $filter = new XmlFilter(); $reader->setReadFilter($filter); - $spreadsheet = $reader->load($filename); + $this->spreadsheet = $spreadsheet = $reader->load($filename); self::assertEquals(2, $spreadsheet->getSheetCount()); $sheet = $spreadsheet->getSheet(1); self::assertEquals('Report Data', $sheet->getTitle()); @@ -87,7 +120,7 @@ class XmlLoadTest extends TestCase . '/samples/templates/excel2003.xml'; $reader = new Xml(); $reader->setLoadSheetsOnly(['Unknown Sheet', 'Report Data']); - $spreadsheet = $reader->load($filename); + $this->spreadsheet = $spreadsheet = $reader->load($filename); self::assertEquals(1, $spreadsheet->getSheetCount()); $sheet = $spreadsheet->getSheet(0); self::assertEquals('Report Data', $sheet->getTitle()); @@ -102,7 +135,7 @@ class XmlLoadTest extends TestCase . '/../../../..' . '/samples/templates/excel2003.short.bad.xml'; $reader = new Xml(); - $spreadsheet = $reader->load($filename); + $this->spreadsheet = $spreadsheet = $reader->load($filename); self::assertEquals(1, $spreadsheet->getSheetCount()); $sheet = $spreadsheet->getSheet(0); self::assertEquals('Sample Data', $sheet->getTitle()); diff --git a/tests/PhpSpreadsheetTests/Shared/FileTest.php b/tests/PhpSpreadsheetTests/Shared/FileTest.php index ddc54b5e..e65ec810 100644 --- a/tests/PhpSpreadsheetTests/Shared/FileTest.php +++ b/tests/PhpSpreadsheetTests/Shared/FileTest.php @@ -87,12 +87,14 @@ class FileTest extends TestCase public function testNotReadable(): void { - if (PHP_OS_FAMILY === 'Windows') { + if (PHP_OS_FAMILY === 'Windows' || stristr(PHP_OS, 'CYGWIN') !== false) { self::markTestSkipped('chmod does not work reliably on Windows'); } $this->tempfile = $temp = File::temporaryFileName(); file_put_contents($temp, ''); - chmod($temp, 0070); + if (chmod($temp, 0070) === false) { + self::markTestSkipped('chmod failed'); + } self::assertFalse(File::testFileNoThrow($temp)); $this->expectException(ReaderException::class); $this->expectExceptionMessage('for reading'); diff --git a/tests/data/Calculation/LookupRef/ADDRESS.php b/tests/data/Calculation/LookupRef/ADDRESS.php index b9e170d5..14a3cf7d 100644 --- a/tests/data/Calculation/LookupRef/ADDRESS.php +++ b/tests/data/Calculation/LookupRef/ADDRESS.php @@ -48,6 +48,22 @@ return [ false, 'EXCEL SHEET', ], + '0 instead of bool for 4th arg' => [ + "'EXCEL SHEET'!R2C3", + 2, + 3, + null, + 0, + 'EXCEL SHEET', + ], + '1 instead of bool for 4th arg' => [ + "'EXCEL SHEET'!\$C\$2", + 2, + 3, + null, + 1, + 'EXCEL SHEET', + ], [ "'EXCEL SHEET'!\$C\$2", 2, diff --git a/tests/data/Calculation/Translations.php b/tests/data/Calculation/Translations.php index 604c6721..0965bac1 100644 --- a/tests/data/Calculation/Translations.php +++ b/tests/data/Calculation/Translations.php @@ -80,4 +80,14 @@ return [ 'nb', '=MAX(ABS({2,-3;-4,5}), ABS{-2,3;4,-5})', ], + 'not fooled by *RC' => [ + '=3*RC(B1)', + 'fr', + '=3*RC(B1)', + ], + 'handle * for ROW' => [ + '=3*LIGNE(B1)', + 'fr', + '=3*ROW(B1)', + ], ];