Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/PhpSpreadsheet/Calculation/Calculation.php
Original file line number Diff line number Diff line change
Expand Up @@ -888,7 +888,7 @@ private static function resizeMatricesExtend(array &$matrix1, array &$matrix2, i
}
}
if ($matrix1Rows < $matrix2Rows) {
$x = ($matrix1Rows === 1) ? $matrix1[0] : array_fill(0, $matrix1Columns, null);
$x = ($matrix1Rows === 1) ? $matrix1[0] : array_fill(0, $matrix2Columns, null);
for ($i = $matrix1Rows; $i < $matrix2Rows; ++$i) {
$matrix1[$i] = $x;
}
Expand Down
5 changes: 5 additions & 0 deletions src/PhpSpreadsheet/Calculation/CalculationLocale.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ protected static function loadLocales(): void
$filename = substr($filename, strlen($localeFileDirectory));
if ($filename != 'en') {
self::$validLocaleLanguages[] = $filename;
$subdirs = glob("$localeFileDirectory$filename/*", GLOB_ONLYDIR) ?: [];
foreach ($subdirs as $subdir) {
$subdirx = basename($subdir);
self::$validLocaleLanguages[] = "{$filename}_{$subdirx}";
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/PhpSpreadsheet/Shared/StringHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ public static function UTF8toBIFF8UnicodeLong(string $textValue): string
*/
public static function convertEncoding(string $textValue, string $to, string $from): string
{
if (self::getIsIconvEnabled()) {
if (static::getIsIconvEnabled()) {
$result = iconv($from, $to . self::$iconvOptions, $textValue);
if (false !== $result) {
return $result;
Expand Down
150 changes: 150 additions & 0 deletions tests/PhpSpreadsheetTests/Calculation/CalculationCoverageTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
<?php

declare(strict_types=1);

namespace PhpOffice\PhpSpreadsheetTests\Calculation;

use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalcException;
use PhpOffice\PhpSpreadsheet\Calculation\ExceptionHandler;
use PhpOffice\PhpSpreadsheet\NamedRange;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PHPUnit\Framework\Attributes;
use PHPUnit\Framework\TestCase;

class CalculationCoverageTest extends TestCase
{
public function testClone(): void
{
$this->expectException(CalcException::class);
$this->expectExceptionMessage('Cloning the calculation engine is not allowed!');
$calc = Calculation::getInstance();
$clone = clone $calc;
$clone->flushInstance();
}

public function testBadInstanceArray(): void
{
$spreadsheet = new Spreadsheet();
$calc = Calculation::getInstance($spreadsheet);
$type = $calc->getInstanceArrayReturnType();
self::assertFalse($calc->setInstanceArrayReturnType('bad'));
self::assertSame($type, $calc->getInstanceArrayReturnType());
$spreadsheet->disconnectWorksheets();
}

public function testCalculate(): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$calc = Calculation::getInstance($spreadsheet);
$sheet->getCell('A1')->setValue('=2+3');
$result = $calc->calculate($sheet->getCell('A1'));
self::assertSame(5, $result);
self::assertSame('', Calculation::boolToString(null));
$spreadsheet->disconnectWorksheets();
}

public function testCalculateBad(): void
{
$this->expectException(CalcException::class);
$this->expectExceptionMessage('Formula Error');
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$calc = Calculation::getInstance($spreadsheet);
$sheet->getCell('A1')->setValue('=SUM(');
$result = $calc->calculate($sheet->getCell('A1'));
self::assertSame(5, $result);
$spreadsheet->disconnectWorksheets();
}

public function testParse(): void
{
$calc = Calculation::getInstance();
self::assertSame([], $calc->parseFormula('2+3'), 'no leading =');
self::assertSame([], $calc->parseFormula('='), 'leading = but no other text');
}

public function testExtractNamedRange(): void
{
$spreadsheet = new Spreadsheet();
$calc = Calculation::getInstance($spreadsheet);
$sheet = $spreadsheet->getActiveSheet();
$sheet->setTitle('mysheet');
$sheet->setCellValue('A1', 1);
$sheet->setCellValue('B1', 2);
$sheet->setCellValue('A2', 3);
$sheet->setCellValue('B2', 4);
$spreadsheet->addNamedRange(
new NamedRange('Whatever', $sheet, '$A$1:$B$2')
);
$range = 'Whatever';
$result = $calc->extractNamedRange($range, $sheet);
self::assertSame('$A$1:$B$2', $range);
self::assertSame([1 => ['A' => 1, 'B' => 2], 2 => ['A' => 3, 'B' => 4]], $result);
$range = 'mysheet!Whatever';
$result = $calc->extractNamedRange($range, $sheet);
self::assertSame('$A$1:$B$2', $range);
self::assertSame([1 => ['A' => 1, 'B' => 2], 2 => ['A' => 3, 'B' => 4]], $result);

$range = 'mysheet!Whateverx';
$result = $calc->extractNamedRange($range, $sheet);
self::assertSame('Whateverx', $range);
self::assertSame('#REF!', $result);

$range = 'Why';
$result = $calc->extractNamedRange($range, $sheet);
self::assertSame('Why', $range);
self::assertSame('#REF!', $result);

$spreadsheet->addNamedRange(
new NamedRange('OneCell', $sheet, '$A$1')
);
$range = 'OneCell';
$result = $calc->extractNamedRange($range, $sheet);
self::assertSame('$A$1', $range);
self::assertSame([1 => ['A' => 1]], $result);

$spreadsheet->addNamedRange(
new NamedRange('NoSuchCell', $sheet, '$Z$1')
);
$range = 'NoSuchCell';
$result = $calc->extractNamedRange($range, $sheet);
self::assertSame('$Z$1', $range);
self::assertSame([1 => ['Z' => null]], $result);

$spreadsheet->addNamedRange(
new NamedRange('SomeCells', $sheet, '$B$1:$C$2')
);
$range = 'SomeCells';
$result = $calc->extractNamedRange($range, $sheet);
self::assertSame('$B$1:$C$2', $range);
self::assertSame([1 => ['B' => 2, 'C' => null], 2 => ['B' => 4, 'C' => null]], $result);

$spreadsheet->disconnectWorksheets();
}

protected static int $winMinPhpToSkip = 80300;

protected static int $winMaxPhpToSkip = 80499;

protected static string $winIndicator = 'WIN';

// separate process because it sets its own handler
#[Attributes\RunInSeparateProcess]
public function testExceptionHandler(): void
{
if (
strtoupper(substr(PHP_OS, 0, 3)) === self::$winIndicator
&& PHP_VERSION_ID >= self::$winMinPhpToSkip
&& PHP_VERSION_ID <= self::$winMaxPhpToSkip
) {
self::markTestSkipped('Mysterious problem on Windows with Php8.3/4 only');
}
$this->expectException(CalcException::class);
$this->expectExceptionMessage('hello');
$handler = new ExceptionHandler();
trigger_error('hello');
self::assertNotNull($handler); // @phpstan-ignore-line
}
}
16 changes: 16 additions & 0 deletions tests/PhpSpreadsheetTests/Calculation/CustomFunctionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,22 @@ public static function testCustomFunction(): void
self::assertTrue(Calculation::removeFunction($key));
self::assertSame('#NAME?', $calculation->calculateFormula('=FOURTHPOWER(3)'));
self::assertFalse(Calculation::removeFunction('WHATEVER'));
$key = 'NATIVECOS';
$value = [
'category' => 'custom',
'functionCall' => 'cos',
'argumentCount' => '1',
];
self::assertTrue(Calculation::addFunction($key, $value));
self::assertSame(1.0, $calculation->calculateFormula('=NATIVECOS(0)'));
self::assertTrue(Calculation::removeFunction($key));
$key = 'PI';
$value = [
'category' => 'custom',
'functionCall' => 'pi',
'argumentCount' => '0',
];
self::assertFalse(Calculation::addFunction($key, $value));
}

public static function testReplaceDummyFunction(): void
Expand Down
52 changes: 51 additions & 1 deletion tests/PhpSpreadsheetTests/Calculation/Issue4451Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

class Issue4451Test extends TestCase
{
public static function testReflect(): void
public static function testReflectExtend1(): void
{
// Sample matrices to test with
$matrix1 = [[1], [3]];
Expand All @@ -27,6 +27,56 @@ public static function testReflect(): void
self::assertSame([[1], [3], [null]], $matrix1); //* @phpstan-ignore-line
}

public static function testReflectExtend2(): void
{
// Sample matrices to test with
$matrix1 = [[1], [3]];
$matrix2 = [[5, 6], [8, 9], [11, 12]];

// Use reflection to make the protected method accessible
$calculation = new Calculation();
$reflectionMethod = new ReflectionMethod(Calculation::class, 'resizeMatricesExtend');

// Call the method using reflection
$reflectionMethod->invokeArgs($calculation, [&$matrix1, &$matrix2, count($matrix1), 1, count($matrix2), 2]);

self::assertSame([[1, 1], [3, 3], [null, null]], $matrix1); //* @phpstan-ignore-line
}

public static function testReflectShrink1(): void
{
// Sample matrices to test with
$matrix1 = [[10, 20], [30, 40]];
$matrix2 = [[50, 60, 70], [80, 90, 100], [110, 120, 130]];

// Use reflection to make the protected method accessible
$calculation = new Calculation();
$reflectionMethod = new ReflectionMethod(Calculation::class, 'resizeMatricesShrink');

// Call the method using reflection
$reflectionMethod->invokeArgs($calculation, [&$matrix1, &$matrix2, count($matrix1), count($matrix1), count($matrix2), count($matrix2)]);

self::assertSame([[10, 20], [30, 40]], $matrix1); //* @phpstan-ignore-line
self::assertSame([[50, 60], [80, 90]], $matrix2); //* @phpstan-ignore-line
}

public static function testReflectShrink2(): void
{
// Sample matrices to test with
$matrix2 = [[10, 20], [30, 40]];
$matrix1 = [[50, 60, 70], [80, 90, 100], [110, 120, 130]];

// Use reflection to make the protected method accessible
$calculation = new Calculation();
$reflectionMethod = new ReflectionMethod(Calculation::class, 'resizeMatricesShrink');

// Call the method using reflection
$reflectionMethod->invokeArgs($calculation, [&$matrix1, &$matrix2, count($matrix1), count($matrix1), count($matrix2), count($matrix2)]);

self::assertSame([[10, 20], [30, 40]], $matrix2); //* @phpstan-ignore-line
self::assertSame([[50, 60], [80, 90]], $matrix1); //* @phpstan-ignore-line
}

/**
* These 2 tests are contrived. They prove that method
* works as desired, but Excel will actually return
Expand Down
27 changes: 27 additions & 0 deletions tests/PhpSpreadsheetTests/Collection/Cells2Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace PhpOffice\PhpSpreadsheetTests\Collection;

use PhpOffice\PhpSpreadsheet\Collection\Cells;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PHPUnit\Framework\TestCase;

class Cells2Test extends TestCase
{
public function testThrowsWhenCellCannotBeStoredInClonedCache(): void
{
$this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class);
$this->expectExceptionMessage('Failed to copy cells in cache');

$cache = new SimpleCache3xxx();

$worksheet = new Worksheet();
$collection = new Cells($worksheet, $cache);

$collection->add('A1', $worksheet->getCell('A1'));
$collection->add('A2', $worksheet->getCell('A2'));
$collection->cloneCellCollection($worksheet);
}
}
23 changes: 23 additions & 0 deletions tests/PhpSpreadsheetTests/Collection/SimpleCache3xxx.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace PhpOffice\PhpSpreadsheetTests\Collection;

use DateInterval;
use PhpOffice\PhpSpreadsheet\Collection\Memory\SimpleCache3;

class SimpleCache3xxx extends SimpleCache3
{
public static int $useCount = 0;

public function set(string $key, mixed $value, null|int|DateInterval $ttl = null): bool
{
++self::$useCount;
if (self::$useCount >= 4) {
return false;
}

return parent::set($key, $value, $ttl);
}
}
18 changes: 18 additions & 0 deletions tests/PhpSpreadsheetTests/Shared/StringHelperNoIconv.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace PhpOffice\PhpSpreadsheetTests\Shared;

use PhpOffice\PhpSpreadsheet\Shared\StringHelper;

class StringHelperNoIconv extends StringHelper
{
/**
* Simulate that iconv is not available.
*/
public static function getIsIconvEnabled(): bool
{
return false;
}
}
10 changes: 10 additions & 0 deletions tests/PhpSpreadsheetTests/Shared/StringHelperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -131,4 +131,14 @@ public function testNonStringable(): void
$this->expectExceptionMessage('Unable to convert to string');
StringHelper::convertToString($dt);
}

public function testNoIconv(): void
{
$string = "\xBF"; // inverted question mark in ISO-8859-1
$expected = '¿';
$from = 'ISO-8859-1';
$to = 'UTF-8';
self::assertSame($expected, StringHelper::convertEncoding($string, $to, $from));
self::assertSame($expected, StringHelperNoIconv::convertEncoding($string, $to, $from));
}
}
12 changes: 12 additions & 0 deletions tests/PhpSpreadsheetTests/Writer/Html/BetterBooleanTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,18 @@ public function testBetterBoolean(): void
$sheet->getCell('G1')->setValue('=1+2');

$reloaded = $this->writeAndReload($spreadsheet, 'Html');
$falseTrue = Calculation::getInstance($spreadsheet)->getFalseTrueArray();
$countPortTrue = 0;
$portTrue = 'VERDADEIRO';
$portFalse = 'FALSO';
$countArray = count($falseTrue[1]);
for ($i = 0; $i < $countArray; ++$i) {
if ($falseTrue[1][$i] === $portTrue) {
++$countPortTrue;
self::assertSame($portFalse, $falseTrue[0][$i]);
}
}
self::assertSame(2, $countPortTrue, '1 for pt, 1 for pt-br');
$spreadsheet->disconnectWorksheets();

$rsheet = $reloaded->getActiveSheet();
Expand Down
15 changes: 15 additions & 0 deletions tests/data/Calculation/Translations.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,21 @@
'pt_br',
'=DAYS360(DATE(2010,2,5),DATE(2010,12,31),TRUE)',
],
'Portuguese No Country' => [
'=DIA.SEMANA(10000)',
'pt',
'=WEEKDAY(10000)',
],
'Portuguese Unknown Country' => [
'=DIA.SEMANA(10000)',
'pt_xx',
'=WEEKDAY(10000)',
],
'Portuguese Brazil' => [
'=DIA.DA.SEMANA(10000)',
'pt_br',
'=WEEKDAY(10000)',
],
[
'=ДНЕЙ360(ДАТА(2010;2;5);ДАТА(2010;12;31);ИСТИНА)',
'ru',
Expand Down