mirror of
https://github.com/PHPOffice/PhpSpreadsheet.git
synced 2026-08-31 12:40:00 +00:00
f8f78a7ed3
Fix #4600. String incrementation through the `++` operator is deprecated in Php 8.5. Because we make use of that operator to iterate through columns, we are particularly hard hit by that change - unaddressed, it causes over 2,000 errors in our test suite! It is, fortunately, not as difficult as I feared to correct. Replacing the `++` operator with a call to new method `StringHelper::stringIncrement` in 79 statements scattered over 31 source modules (in src, samples, test, and infra) eliminates all the messages in the test suite. It is possible that others are lurking, but I don't know a systematic way of determining if there are others. We'll stick with this for now, and deal with any others as they show up. This PR will be applied to the master, release390, and release222 branches. It will not be applied to the release210 or release1291 branches, which will now accept security changes only.
239 lines
8.1 KiB
PHP
239 lines
8.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Statistical;
|
|
|
|
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
|
|
use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalcException;
|
|
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
|
use PhpOffice\PhpSpreadsheet\Cell\DataType;
|
|
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
|
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
|
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
|
use PHPUnit\Framework\TestCase;
|
|
use Stringable;
|
|
|
|
class AllSetupTeardown extends TestCase
|
|
{
|
|
private string $compatibilityMode;
|
|
|
|
private ?Spreadsheet $spreadsheet = null;
|
|
|
|
private ?Worksheet $sheet = null;
|
|
|
|
protected string $returnArrayAs;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
$this->compatibilityMode = Functions::getCompatibilityMode();
|
|
$this->returnArrayAs = '';
|
|
}
|
|
|
|
protected function tearDown(): void
|
|
{
|
|
Functions::setCompatibilityMode($this->compatibilityMode);
|
|
$this->sheet = null;
|
|
if ($this->spreadsheet !== null) {
|
|
$this->spreadsheet->disconnectWorksheets();
|
|
$this->spreadsheet = null;
|
|
}
|
|
}
|
|
|
|
protected static function setOpenOffice(): void
|
|
{
|
|
Functions::setCompatibilityMode(Functions::COMPATIBILITY_OPENOFFICE);
|
|
}
|
|
|
|
protected static function setGnumeric(): void
|
|
{
|
|
Functions::setCompatibilityMode(Functions::COMPATIBILITY_GNUMERIC);
|
|
}
|
|
|
|
protected function mightHaveException(mixed $expectedResult): void
|
|
{
|
|
if ($expectedResult === 'exception') {
|
|
$this->expectException(CalcException::class);
|
|
}
|
|
}
|
|
|
|
protected function setCell(string $cell, mixed $value): void
|
|
{
|
|
if ($value !== null) {
|
|
if (is_string($value) && is_numeric($value)) {
|
|
$this->getSheet()->getCell($cell)->setValueExplicit($value, DataType::TYPE_STRING);
|
|
} else {
|
|
$this->getSheet()->getCell($cell)->setValue($value);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected function getSpreadsheet(): Spreadsheet
|
|
{
|
|
if ($this->spreadsheet !== null) {
|
|
return $this->spreadsheet;
|
|
}
|
|
$this->spreadsheet = new Spreadsheet();
|
|
|
|
return $this->spreadsheet;
|
|
}
|
|
|
|
protected function getSheet(): Worksheet
|
|
{
|
|
if ($this->sheet !== null) {
|
|
return $this->sheet;
|
|
}
|
|
$this->sheet = $this->getSpreadsheet()->getActiveSheet();
|
|
|
|
return $this->sheet;
|
|
}
|
|
|
|
/**
|
|
* Excel handles text/logical/empty cells differently when
|
|
* passed directly as arguments as opposed to cell references or arrays.
|
|
* This function will test both approaches.
|
|
*/
|
|
protected function runTestCases(string $functionName, mixed $expectedResult, mixed ...$args): void
|
|
{
|
|
if (is_array($expectedResult)) {
|
|
$this->runTestCaseReference($functionName, $expectedResult[0], ...$args);
|
|
$this->runTestCaseDirect($functionName, $expectedResult[1], ...$args);
|
|
} else {
|
|
$this->runTestCaseReference($functionName, $expectedResult, ...$args);
|
|
$this->runTestCaseDirect($functionName, $expectedResult, ...$args);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Excel handles text/logical/empty cells differently when
|
|
* passed directly as arguments as opposed to cell references or arrays.
|
|
* This functions tests passing as arrays.
|
|
*/
|
|
protected function runTestCaseReference(string $functionName, mixed $expectedResult, mixed ...$args): void
|
|
{
|
|
$this->mightHaveException($expectedResult);
|
|
$sheet = $this->getSheet();
|
|
if ($this->returnArrayAs !== '') {
|
|
$calculation = Calculation::getInstance($this->spreadsheet);
|
|
$calculation->setInstanceArrayReturnType(
|
|
$this->returnArrayAs
|
|
);
|
|
}
|
|
$formula = "=$functionName(";
|
|
$comma = '';
|
|
$row = 0;
|
|
foreach ($args as $arg) {
|
|
++$row;
|
|
if (is_array($arg)) {
|
|
$arrayArg = '{';
|
|
$arrayComma = '';
|
|
foreach ($arg as $arrayItem) {
|
|
$arrayArg .= $arrayComma;
|
|
if ($arrayItem !== null && !is_scalar($arrayItem) && !($arrayItem instanceof Stringable)) {
|
|
self::fail('non-stringable item');
|
|
}
|
|
$arrayArg .= $this->convertToString($arrayItem);
|
|
$arrayComma = ';';
|
|
}
|
|
$arrayArg .= '}';
|
|
$formula .= "$comma$arrayArg";
|
|
$comma = ',';
|
|
} else {
|
|
$cellId = "A$row";
|
|
$formula .= "$comma$cellId";
|
|
$comma = ',';
|
|
$this->setCell($cellId, $arg);
|
|
}
|
|
}
|
|
$formula .= ')';
|
|
$this->setCell('B1', $formula);
|
|
self::assertEqualsWithDelta($expectedResult, $sheet->getCell('B1')->getCalculatedValue(), 1.0e-8, 'arguments supplied as references');
|
|
}
|
|
|
|
/**
|
|
* Excel handles text/logical/empty cells differently when
|
|
* passed directly as arguments as opposed to cell references or arrays.
|
|
* This functions tests passing as direct arguments.
|
|
*/
|
|
protected function runTestCaseDirect(string $functionName, mixed $expectedResult, mixed ...$args): void
|
|
{
|
|
$this->mightHaveException($expectedResult);
|
|
$sheet = $this->getSheet();
|
|
$formula = "=$functionName(";
|
|
$comma = '';
|
|
foreach ($args as $arg) {
|
|
if (is_array($arg)) {
|
|
foreach ($arg as $arrayItem) {
|
|
$formula .= $comma;
|
|
$comma = ',';
|
|
if ($arrayItem !== null && !is_scalar($arrayItem) && !($arrayItem instanceof Stringable)) {
|
|
self::fail('non-stringable item');
|
|
}
|
|
$formula .= $this->convertToString($arrayItem);
|
|
}
|
|
} else {
|
|
$formula .= $comma;
|
|
$comma = ',';
|
|
/** @var string */
|
|
$argx = $arg;
|
|
$formula .= $this->convertToString($argx);
|
|
}
|
|
}
|
|
$formula .= ')';
|
|
$this->setCell('B2', $formula);
|
|
self::assertEqualsWithDelta($expectedResult, $sheet->getCell('B2')->getCalculatedValue(), 1.0e-8, 'arguments supplied directly');
|
|
}
|
|
|
|
/**
|
|
* Excel seems to reject bracket notation for literal arrays
|
|
* for some functions.
|
|
*/
|
|
protected function runTestCaseNoBracket(string $functionName, mixed $expectedResult, mixed ...$args): void
|
|
{
|
|
$this->mightHaveException($expectedResult);
|
|
$sheet = $this->getSheet();
|
|
$formula = "=$functionName(";
|
|
$comma = '';
|
|
$row = 0;
|
|
foreach ($args as $arg) {
|
|
++$row;
|
|
if (is_array($arg)) {
|
|
$col = 'A';
|
|
$arrayRange = '';
|
|
foreach ($arg as $arrayItem) {
|
|
$cellId = "$col$row";
|
|
$arrayRange = "A$row:$cellId";
|
|
$this->setCell($cellId, $arrayItem);
|
|
StringHelper::stringIncrement($col);
|
|
}
|
|
$formula .= "$comma$arrayRange";
|
|
$comma = ',';
|
|
} else {
|
|
$cellId = "A$row";
|
|
$formula .= "$comma$cellId";
|
|
$comma = ',';
|
|
if (is_string($arg) && str_starts_with($arg, '=')) {
|
|
$sheet->getCell($cellId)->setValueExplicit($arg, DataType::TYPE_STRING);
|
|
} else {
|
|
$this->setCell($cellId, $arg);
|
|
}
|
|
}
|
|
}
|
|
$formula .= ')';
|
|
$this->setCell('Z99', $formula);
|
|
self::assertEqualsWithDelta($expectedResult, $sheet->getCell('Z99')->getCalculatedValue(), 1.0e-8, 'arguments supplied as ranges');
|
|
}
|
|
|
|
private function convertToString(null|bool|float|int|string|Stringable $arg): string
|
|
{
|
|
if (is_string($arg)) {
|
|
return '"' . $arg . '"';
|
|
}
|
|
if (is_bool($arg)) {
|
|
return $arg ? 'TRUE' : 'FALSE';
|
|
}
|
|
|
|
return (string) $arg;
|
|
}
|
|
}
|