Skip to content

Commit cff4015

Browse files
authored
Merge branch 'master' into parseutf8
2 parents d0a0080 + 18cfe5b commit cff4015

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+356
-136
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
1919
### Added
2020

2121
- Pdf Charts and Drawings. [Discussion #4129](https://github.com/PHPOffice/PhpSpreadsheet/discussions/4129) [Discussion #4168](https://github.com/PHPOffice/PhpSpreadsheet/discussions/4168) [PR #4327](https://github.com/PHPOffice/PhpSpreadsheet/pull/4327)
22+
- Allow spreadsheet serialization. [Discussion #4324](https://github.com/PHPOffice/PhpSpreadsheet/discussions/4324) [Issue #1741](https://github.com/PHPOffice/PhpSpreadsheet/issues/1741) [Issue #1757](https://github.com/PHPOffice/PhpSpreadsheet/issues/1757) [PR #4326](https://github.com/PHPOffice/PhpSpreadsheet/pull/4326)
2223

2324
### Removed
2425

@@ -47,6 +48,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
4748

4849
- Xls writer Parser Mishandling True/False Argument. [Issue #4331](https://github.com/PHPOffice/PhpSpreadsheet/issues/4331) [PR #4333](https://github.com/PHPOffice/PhpSpreadsheet/pull/4333)
4950
- Xls writer Parser Parse By Character Not Byte. [PR #4344](https://github.com/PHPOffice/PhpSpreadsheet/pull/4344)
51+
- Minor changes to dynamic array calculations exposed by using explicit array return types in some tests. [PR #4328](https://github.com/PHPOffice/PhpSpreadsheet/pull/4328)
5052

5153
## 2025-01-26 - 3.9.0
5254

samples/LookupRef/COLUMN.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<?php
22

3+
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
34
use PhpOffice\PhpSpreadsheet\Spreadsheet;
45

56
require __DIR__ . '/../Header.php';
@@ -8,6 +9,10 @@
89

910
// Create new PhpSpreadsheet object
1011
$spreadsheet = new Spreadsheet();
12+
$calculation = Calculation::getInstance($spreadsheet);
13+
$calculation->setInstanceArrayReturnType(
14+
Calculation::RETURN_ARRAY_AS_VALUE
15+
);
1116
$worksheet = $spreadsheet->getActiveSheet();
1217

1318
$worksheet->getCell('A1')->setValue('=COLUMN(C13)');

src/PhpSpreadsheet/Calculation/Calculation.php

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -133,11 +133,6 @@ class Calculation
133133
*/
134134
public ?string $formulaError = null;
135135

136-
/**
137-
* Reference Helper.
138-
*/
139-
private static ReferenceHelper $referenceHelper;
140-
141136
/**
142137
* An array of the nested cell references accessed by the calculation engine, used for the debug log.
143138
*/
@@ -2895,7 +2890,6 @@ public function __construct(?Spreadsheet $spreadsheet = null)
28952890
$this->cyclicReferenceStack = new CyclicReferenceStack();
28962891
$this->debugLog = new Logger($this->cyclicReferenceStack);
28972892
$this->branchPruner = new BranchPruner($this->branchPruningEnabled);
2898-
self::$referenceHelper = ReferenceHelper::getInstance();
28992893
}
29002894

29012895
private static function loadLocales(): void
@@ -4887,17 +4881,18 @@ private function processTokenStack(mixed $tokens, ?string $cellID = null, ?Cell
48874881
}
48884882
$result = $operand1;
48894883
} else {
4890-
// In theory, we should truncate here.
4891-
// But I can't figure out a formula
4892-
// using the concatenation operator
4893-
// with literals that fits in 32K,
4894-
// so I don't think we can overflow here.
48954884
if (Information\ErrorValue::isError($operand1)) {
48964885
$result = $operand1;
48974886
} elseif (Information\ErrorValue::isError($operand2)) {
48984887
$result = $operand2;
48994888
} else {
4900-
$result = self::FORMULA_STRING_QUOTE . str_replace('""', self::FORMULA_STRING_QUOTE, self::unwrapResult($operand1) . self::unwrapResult($operand2)) . self::FORMULA_STRING_QUOTE;
4889+
$result = str_replace('""', self::FORMULA_STRING_QUOTE, self::unwrapResult($operand1) . self::unwrapResult($operand2));
4890+
$result = Shared\StringHelper::substring(
4891+
$result,
4892+
0,
4893+
DataType::MAX_STRING_LENGTH
4894+
);
4895+
$result = self::FORMULA_STRING_QUOTE . $result . self::FORMULA_STRING_QUOTE;
49014896
}
49024897
}
49034898
$this->debugLog->writeDebugLog('Evaluation Result is %s', $this->showTypeDetails($result));
@@ -5046,6 +5041,9 @@ private function processTokenStack(mixed $tokens, ?string $cellID = null, ?Cell
50465041
while (is_array($cellValue)) {
50475042
$cellValue = array_shift($cellValue);
50485043
}
5044+
if (is_string($cellValue)) {
5045+
$cellValue = preg_replace('/"/', '""', $cellValue);
5046+
}
50495047
$this->debugLog->writeDebugLog('Scalar Result for cell %s is %s', $cellRef, $this->showTypeDetails($cellValue));
50505048
}
50515049
$this->processingAnchorArray = false;
@@ -5741,11 +5739,14 @@ private function evaluateDefinedName(Cell $cell, DefinedName $namedRange, Worksh
57415739
$recursiveCalculationCellAddress = $recursiveCalculationCell->getCoordinate();
57425740

57435741
// Adjust relative references in ranges and formulae so that we execute the calculation for the correct rows and columns
5744-
$definedNameValue = self::$referenceHelper->updateFormulaReferencesAnyWorksheet(
5745-
$definedNameValue,
5746-
Coordinate::columnIndexFromString($cell->getColumn()) - 1,
5747-
$cell->getRow() - 1
5748-
);
5742+
$definedNameValue = ReferenceHelper::getInstance()
5743+
->updateFormulaReferencesAnyWorksheet(
5744+
$definedNameValue,
5745+
Coordinate::columnIndexFromString(
5746+
$cell->getColumn()
5747+
) - 1,
5748+
$cell->getRow() - 1
5749+
);
57495750

57505751
$this->debugLog->writeDebugLog('Value adjusted for relative references is %s', $definedNameValue);
57515752

src/PhpSpreadsheet/Calculation/Financial/CashFlow/Single.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,13 @@ public static function periods(mixed $rate, mixed $presentValue, mixed $futureVa
7777
*
7878
* Calculates the interest rate required for an investment to grow to a specified future value .
7979
*
80-
* @param array|float $periods The number of periods over which the investment is made
81-
* @param array|float $presentValue Present Value
82-
* @param array|float $futureValue Future Value
80+
* @param mixed $periods The number of periods over which the investment is made, expect array|float
81+
* @param mixed $presentValue Present Value, expect array|float
82+
* @param mixed $futureValue Future Value, expect array|float
8383
*
8484
* @return float|string Result, or a string containing an error
8585
*/
86-
public static function interestRate(array|float $periods = 0.0, array|float $presentValue = 0.0, array|float $futureValue = 0.0): string|float
86+
public static function interestRate(mixed $periods = 0.0, mixed $presentValue = 0.0, mixed $futureValue = 0.0): string|float
8787
{
8888
$periods = Functions::flattenSingleValue($periods);
8989
$presentValue = Functions::flattenSingleValue($presentValue);

src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/NonPeriodic.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@ class NonPeriodic
2323
* Excel Function:
2424
* =XIRR(values,dates,guess)
2525
*
26-
* @param float[] $values A series of cash flow payments
26+
* @param mixed $values A series of cash flow payments, expecting float[]
2727
* The series of values must contain at least one positive value & one negative value
2828
* @param mixed[] $dates A series of payment dates
2929
* The first payment date indicates the beginning of the schedule of payments
3030
* All other dates must be later than this date, but they may occur in any order
3131
* @param mixed $guess An optional guess at the expected answer
3232
*/
33-
public static function rate(array $values, array $dates, mixed $guess = self::DEFAULT_GUESS): float|string
33+
public static function rate(mixed $values, mixed $dates, mixed $guess = self::DEFAULT_GUESS): float|string
3434
{
3535
$rslt = self::xirrPart1($values, $dates);
3636
if ($rslt !== '') {
@@ -106,18 +106,18 @@ public static function rate(array $values, array $dates, mixed $guess = self::DE
106106
* Excel Function:
107107
* =XNPV(rate,values,dates)
108108
*
109-
* @param array|float $rate the discount rate to apply to the cash flows
110-
* @param float[] $values A series of cash flows that corresponds to a schedule of payments in dates.
109+
* @param mixed $rate the discount rate to apply to the cash flows, expect array|float
110+
* @param mixed $values A series of cash flows that corresponds to a schedule of payments in dates, expecting floag[].
111111
* The first payment is optional and corresponds to a cost or payment that occurs
112112
* at the beginning of the investment.
113113
* If the first value is a cost or payment, it must be a negative value.
114114
* All succeeding payments are discounted based on a 365-day year.
115115
* The series of values must contain at least one positive value and one negative value.
116-
* @param mixed[] $dates A schedule of payment dates that corresponds to the cash flow payments.
116+
* @param mixed $dates A schedule of payment dates that corresponds to the cash flow payments, expecting mixed[].
117117
* The first payment date indicates the beginning of the schedule of payments.
118118
* All other dates must be later than this date, but they may occur in any order.
119119
*/
120-
public static function presentValue(array|float $rate, array $values, array $dates): float|string
120+
public static function presentValue(mixed $rate, mixed $values, mixed $dates): float|string
121121
{
122122
return self::xnpvOrdered($rate, $values, $dates, true);
123123
}

src/PhpSpreadsheet/Calculation/LookupRef/Matrix.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,15 @@ public static function index(mixed $matrix, mixed $rowNum = 0, mixed $columnNum
8282

8383
$rowNum = $rowNum ?? 0;
8484
$columnNum = $columnNum ?? 0;
85+
if (is_scalar($matrix)) {
86+
if ($rowNum === 0 || $rowNum === 1) {
87+
if ($columnNum === 0 || $columnNum === 1) {
88+
if ($columnNum === 1 || $rowNum === 1) {
89+
return $matrix;
90+
}
91+
}
92+
}
93+
}
8594

8695
try {
8796
$rowNum = LookupRefValidations::validatePositiveInt($rowNum);

src/PhpSpreadsheet/Cell/Cell.php

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ class Cell implements Stringable
6363
*
6464
* @var null|array<string, string>
6565
*/
66-
private mixed $formulaAttributes = null;
66+
private ?array $formulaAttributes = null;
6767

6868
private IgnoredErrors $ignoredErrors;
6969

@@ -408,9 +408,6 @@ public function getCalculatedValue(bool $resetLog = true): mixed
408408
$oldAttributesT = $oldAttributes['t'] ?? '';
409409
$coordinate = $this->getCoordinate();
410410
$oldAttributesRef = $oldAttributes['ref'] ?? $coordinate;
411-
if (!str_contains($oldAttributesRef, ':')) {
412-
$oldAttributesRef .= ":$oldAttributesRef";
413-
}
414411
$originalValue = $this->value;
415412
$originalDataType = $this->dataType;
416413
$this->formulaAttributes = [];
@@ -434,6 +431,14 @@ public function getCalculatedValue(bool $resetLog = true): mixed
434431
$result = array_shift($result);
435432
}
436433
}
434+
if (
435+
!is_array($result)
436+
&& $calculation->getInstanceArrayReturnType() === Calculation::RETURN_ARRAY_AS_ARRAY
437+
&& $oldAttributesT === 'array'
438+
&& ($oldAttributesRef === $coordinate || $oldAttributesRef === "$coordinate:$coordinate")
439+
) {
440+
$result = [$result];
441+
}
437442
// if return_as_array for formula like '=sheet!cell'
438443
if (is_array($result) && count($result) === 1) {
439444
$resultKey = array_keys($result)[0];
@@ -560,6 +565,8 @@ public function getCalculatedValue(bool $resetLog = true): mixed
560565
SharedDate::setExcelCalendar($currentCalendar);
561566

562567
if ($result === Functions::NOT_YET_IMPLEMENTED) {
568+
$this->formulaAttributes = $oldAttributes;
569+
563570
return $this->calculatedValue; // Fallback if calculation engine does not support the formula.
564571
}
565572

src/PhpSpreadsheet/Spreadsheet.php

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,12 @@
77
use PhpOffice\PhpSpreadsheet\Cell\IValueBinder;
88
use PhpOffice\PhpSpreadsheet\Document\Properties;
99
use PhpOffice\PhpSpreadsheet\Document\Security;
10-
use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader;
1110
use PhpOffice\PhpSpreadsheet\Shared\Date;
12-
use PhpOffice\PhpSpreadsheet\Shared\File;
1311
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
1412
use PhpOffice\PhpSpreadsheet\Style\Style;
1513
use PhpOffice\PhpSpreadsheet\Worksheet\Iterator;
1614
use PhpOffice\PhpSpreadsheet\Worksheet\Table;
1715
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
18-
use PhpOffice\PhpSpreadsheet\Writer\Xlsx as XlsxWriter;
1916

2017
class Spreadsheet implements JsonSerializable
2118
{
@@ -1063,17 +1060,7 @@ public function getWorksheetIterator(): Iterator
10631060
*/
10641061
public function copy(): self
10651062
{
1066-
$filename = File::temporaryFilename();
1067-
$writer = new XlsxWriter($this);
1068-
$writer->setIncludeCharts(true);
1069-
$writer->save($filename);
1070-
1071-
$reader = new XlsxReader();
1072-
$reader->setIncludeCharts(true);
1073-
$reloadedSpreadsheet = $reader->load($filename);
1074-
unlink($filename);
1075-
1076-
return $reloadedSpreadsheet;
1063+
return unserialize(serialize($this));
10771064
}
10781065

10791066
public function __clone()
@@ -1542,14 +1529,6 @@ public function reevaluateAutoFilters(bool $resetToMax): void
15421529
}
15431530
}
15441531

1545-
/**
1546-
* @throws Exception
1547-
*/
1548-
public function __serialize(): array
1549-
{
1550-
throw new Exception('Spreadsheet objects cannot be serialized');
1551-
}
1552-
15531532
/**
15541533
* @throws Exception
15551534
*/

src/PhpSpreadsheet/Worksheet/Worksheet.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,6 @@ public function __destruct()
377377
public function __wakeup(): void
378378
{
379379
$this->hash = spl_object_id($this);
380-
$this->parent = null;
381380
}
382381

383382
/**

src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1513,6 +1513,9 @@ private function writeCellFormula(XMLWriter $objWriter, string $cellValue, Cell
15131513

15141514
if (isset($attributes['ref'])) {
15151515
$ref = $this->parseRef($coordinate, $attributes['ref']);
1516+
if ($ref === "$coordinate:$coordinate") {
1517+
$ref = $coordinate;
1518+
}
15161519
} else {
15171520
$ref = $coordinate;
15181521
}

0 commit comments

Comments
 (0)