Skip to content

Commit e8bb091

Browse files
authored
Merge branch 'master' into pr1415
2 parents 1ef0633 + aae4992 commit e8bb091

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

+777
-99
lines changed

.github/workflows/github-pages.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ jobs:
1818
- name: Setup PHP, with composer and extensions
1919
uses: shivammathur/setup-php@v2
2020
with:
21-
php-version: 8.1
21+
php-version: 8.3
2222
coverage: none # remove xdebug
2323

2424
- name: Build API documentation
2525
run: |
26-
curl -LO https://github.com/phpDocumentor/phpDocumentor/releases/download/v3.4.1/phpDocumentor.phar
26+
curl -LO https://github.com/phpDocumentor/phpDocumentor/releases/download/v3.5.0/phpDocumentor.phar
2727
php phpDocumentor.phar --directory src/ --target docs/api
2828
2929
- name: Deploy to GithHub Pages

.github/workflows/main.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ jobs:
7474
- name: Setup PHP, with composer and extensions
7575
uses: shivammathur/setup-php@v2
7676
with:
77-
php-version: 8.1
77+
php-version: 8.3
7878
extensions: ctype, dom, gd, iconv, fileinfo, libxml, mbstring, simplexml, xml, xmlreader, xmlwriter, zip, zlib
7979
coverage: none
8080

@@ -91,7 +91,7 @@ jobs:
9191
- name: Setup PHP, with composer and extensions
9292
uses: shivammathur/setup-php@v2
9393
with:
94-
php-version: 8.1
94+
php-version: 8.3
9595
extensions: ctype, dom, gd, iconv, fileinfo, libxml, mbstring, simplexml, xml, xmlreader, xmlwriter, zip, zlib
9696
coverage: none
9797
tools: cs2pr
@@ -122,7 +122,7 @@ jobs:
122122
- name: Setup PHP, with composer and extensions
123123
uses: shivammathur/setup-php@v2
124124
with:
125-
php-version: 8.1
125+
php-version: 8.3
126126
extensions: ctype, dom, gd, iconv, fileinfo, libxml, mbstring, simplexml, xml, xmlreader, xmlwriter, zip, zlib
127127
coverage: none
128128
tools: cs2pr
@@ -153,7 +153,7 @@ jobs:
153153
- name: Setup PHP, with composer and extensions
154154
uses: shivammathur/setup-php@v2
155155
with:
156-
php-version: 8.1
156+
php-version: 8.3
157157
extensions: ctype, dom, gd, iconv, fileinfo, libxml, mbstring, simplexml, xml, xmlreader, xmlwriter, zip, zlib
158158
coverage: none
159159
tools: cs2pr
@@ -184,7 +184,7 @@ jobs:
184184
- name: Setup PHP, with composer and extensions
185185
uses: shivammathur/setup-php@v2
186186
with:
187-
php-version: 8.1
187+
php-version: 8.3
188188
extensions: ctype, dom, gd, iconv, fileinfo, libxml, mbstring, simplexml, xml, xmlreader, xmlwriter, zip, zlib
189189
coverage: none
190190
tools: cs2pr
@@ -217,7 +217,7 @@ jobs:
217217
- name: Setup PHP, with composer and extensions
218218
uses: shivammathur/setup-php@v2
219219
with:
220-
php-version: 8.1
220+
php-version: 8.3
221221
extensions: ctype, dom, gd, iconv, fileinfo, libxml, mbstring, simplexml, xml, xmlreader, xmlwriter, zip, zlib
222222
coverage: pcov
223223

.scrutinizer.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ build:
1414
analysis:
1515
image: default-bionic
1616
environment:
17-
php: 8.1
17+
php: 8.2
1818
tests:
1919
override:
2020
- php-scrutinizer-run

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
1515
### Changed
1616

1717
- On read, Xlsx Reader had been breaking up union ranges into separate individual ranges. It will now try to preserve range as it was read in. [PR #4042](https://github.com/PHPOffice/PhpSpreadsheet/pull/4042)
18+
- Xlsx/Xls spreadsheet calculation and formatting of dates will use base date of spreadsheet even when spreadsheets with different base dates are simultaneously open. [Issue #1036](https://github.com/PHPOffice/PhpSpreadsheet/issues/1036) [Issue #1635](https://github.com/PHPOffice/PhpSpreadsheet/issues/1635) [PR #4071](https://github.com/PHPOffice/PhpSpreadsheet/pull/4071)
1819

1920
### Deprecated
2021

@@ -35,6 +36,8 @@ and this project adheres to [Semantic Versioning](https://semver.org).
3536
- Conditional Range Unions and Intersections [Issue #4039](https://github.com/PHPOffice/PhpSpreadsheet/issues/4039) [PR #4042](https://github.com/PHPOffice/PhpSpreadsheet/pull/4042)
3637
- Csv Reader allow use of html mimetype. [Issue #4036](https://github.com/PHPOffice/PhpSpreadsheet/issues/4036) [PR #4040](https://github.com/PHPOffice/PhpSpreadsheet/pull/4040)
3738
- Problem rendering line chart with missing plot label. [PR #4074](https://github.com/PHPOffice/PhpSpreadsheet/pull/4074)
39+
- More RTL in Xlsx/Html Comments [Issue #4004](https://github.com/PHPOffice/PhpSpreadsheet/issues/4004) [PR #4065](https://github.com/PHPOffice/PhpSpreadsheet/pull/4065)
40+
- Empty String in sharedStrings. [Issue #4063](https://github.com/PHPOffice/PhpSpreadsheet/issues/4063) [PR #4064](https://github.com/PHPOffice/PhpSpreadsheet/pull/4064)
3841

3942
## 2024-05-11 - 2.1.0
4043

docs/topics/Looping the Loop.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,28 @@ But a peak memory usage of 49,152KB compared with the 57,344KB used by `toArray(
308308
Like `toArray()`, `rangeToArray()` is easy to use, but it has the same limitations for flexibility. It provides the same limited control over how the data from each cell is returned in the array as `toArray()`.
309309
The same additional arguments that can be provided for the `toArray()` method can also be provided to `rangeToArray()`.
310310

311+
312+
## Using `rangeToArrayYieldRows()`
313+
314+
Since v2.1.0 the worksheet method `rangeToArrayYieldRows()` is available.
315+
It allows you to iterate over all sheet's rows with little memory consumption,
316+
while obtaining each row as an array:
317+
318+
```php
319+
$rowGenerator = $sheet->rangeToArrayYieldRows(
320+
'A1:' . $sheet->getHighestDataColumn() . $sheet->getHighestDataRow(),
321+
null,
322+
false,
323+
false
324+
);
325+
foreach ($rowGenerator as $row) {
326+
echo $row[0] . ' | ' . $row[1] . "\n";
327+
}
328+
```
329+
330+
See `samples/Reader2/23_iterateRowsYield.php`.
331+
332+
311333
## Using Iterators
312334

313335
You don't need to build an array from the worksheet to loop through the rows and columns and do whatever processing you need; you can loop through the rows and columns in the Worksheet directly and more efficiently using PhpSpreadsheet's built-in iterators.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
/**
4+
* Use rangeToArrayYieldRows() to efficiently iterate over all rows.
5+
*/
6+
7+
require __DIR__ . '/../Header.php';
8+
9+
$inputFileName = __DIR__ . '/../Reader/sampleData/example1.xls';
10+
11+
$spreadsheet = PhpOffice\PhpSpreadsheet\IOFactory::load(
12+
$inputFileName,
13+
PhpOffice\PhpSpreadsheet\Reader\IReader::READ_DATA_ONLY
14+
);
15+
$sheet = $spreadsheet->getSheet(0);
16+
17+
$rowGenerator = $sheet->rangeToArrayYieldRows(
18+
$spreadsheet->getActiveSheet()->calculateWorksheetDataDimension(),
19+
null,
20+
false,
21+
false
22+
);
23+
foreach ($rowGenerator as $row) {
24+
echo '| ' . $row[0] . ' | ' . $row[1] . "|\n";
25+
}

src/PhpSpreadsheet/Calculation/Calculation.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ class Calculation
120120
*/
121121
private Logger $debugLog;
122122

123-
private bool $suppressFormulaErrorsNew = false;
123+
private bool $suppressFormulaErrors = false;
124124

125125
/**
126126
* Error message for any error that was raised/thrown by the calculation engine.
@@ -5350,7 +5350,7 @@ protected function raiseFormulaError(string $errorMessage, int $code = 0, ?Throw
53505350
{
53515351
$this->formulaError = $errorMessage;
53525352
$this->cyclicReferenceStack->clear();
5353-
$suppress = $this->suppressFormulaErrors ?? $this->suppressFormulaErrorsNew;
5353+
$suppress = $this->suppressFormulaErrors;
53545354
if (!$suppress) {
53555355
throw new Exception($errorMessage, $code, $exception);
53565356
}
@@ -5634,12 +5634,12 @@ private function evaluateDefinedName(Cell $cell, DefinedName $namedRange, Worksh
56345634

56355635
public function setSuppressFormulaErrors(bool $suppressFormulaErrors): void
56365636
{
5637-
$this->suppressFormulaErrorsNew = $suppressFormulaErrors;
5637+
$this->suppressFormulaErrors = $suppressFormulaErrors;
56385638
}
56395639

56405640
public function getSuppressFormulaErrors(): bool
56415641
{
5642-
return $this->suppressFormulaErrorsNew;
5642+
return $this->suppressFormulaErrors;
56435643
}
56445644

56455645
private static function boolToString(mixed $operand1): mixed

src/PhpSpreadsheet/Cell/AdvancedValueBinder.php

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
66
use PhpOffice\PhpSpreadsheet\Calculation\Engine\FormattedNumber;
7-
use PhpOffice\PhpSpreadsheet\RichText\RichText;
87
use PhpOffice\PhpSpreadsheet\Shared\Date;
98
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
109
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
@@ -30,7 +29,7 @@ public function bindValue(Cell $cell, mixed $value = null): bool
3029
$dataType = parent::dataTypeForValue($value);
3130

3231
// Style logic - strings
33-
if ($dataType === DataType::TYPE_STRING && !$value instanceof RichText) {
32+
if ($dataType === DataType::TYPE_STRING && is_string($value)) {
3433
// Test for booleans using locale-setting
3534
if (StringHelper::strToUpper($value) === Calculation::getTRUE()) {
3635
$cell->setValueExplicit(true, DataType::TYPE_BOOL);
@@ -54,17 +53,17 @@ public function bindValue(Cell $cell, mixed $value = null): bool
5453
$thousandsSeparator = preg_quote(StringHelper::getThousandsSeparator(), '/');
5554

5655
// Check for percentage
57-
if (preg_match('/^\-?\d*' . $decimalSeparator . '?\d*\s?\%$/', preg_replace('/(\d)' . $thousandsSeparator . '(\d)/u', '$1$2', $value))) {
58-
return $this->setPercentage(preg_replace('/(\d)' . $thousandsSeparator . '(\d)/u', '$1$2', $value), $cell);
56+
if (preg_match('/^\-?\d*' . $decimalSeparator . '?\d*\s?\%$/', (string) preg_replace('/(\d)' . $thousandsSeparator . '(\d)/u', '$1$2', $value))) {
57+
return $this->setPercentage((string) preg_replace('/(\d)' . $thousandsSeparator . '(\d)/u', '$1$2', $value), $cell);
5958
}
6059

6160
// Check for currency
62-
if (preg_match(FormattedNumber::currencyMatcherRegexp(), preg_replace('/(\d)' . $thousandsSeparator . '(\d)/u', '$1$2', $value), $matches, PREG_UNMATCHED_AS_NULL)) {
61+
if (preg_match(FormattedNumber::currencyMatcherRegexp(), (string) preg_replace('/(\d)' . $thousandsSeparator . '(\d)/u', '$1$2', $value), $matches, PREG_UNMATCHED_AS_NULL)) {
6362
// Convert value to number
6463
$sign = ($matches['PrefixedSign'] ?? $matches['PrefixedSign2'] ?? $matches['PostfixedSign']) ?? null;
6564
$currencyCode = $matches['PrefixedCurrency'] ?? $matches['PostfixedCurrency'];
6665
/** @var string */
67-
$temp = str_replace([$decimalSeparatorNoPreg, $currencyCode, ' ', '-'], ['.', '', '', ''], preg_replace('/(\d)' . $thousandsSeparator . '(\d)/u', '$1$2', $value));
66+
$temp = str_replace([$decimalSeparatorNoPreg, $currencyCode, ' ', '-'], ['.', '', '', ''], (string) preg_replace('/(\d)' . $thousandsSeparator . '(\d)/u', '$1$2', $value));
6867
$value = (float) ($sign . trim($temp));
6968

7069
return $this->setCurrency($value, $cell, $currencyCode ?? '');

src/PhpSpreadsheet/Cell/Cell.php

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -187,14 +187,21 @@ public function getValueString(): string
187187
*/
188188
public function getFormattedValue(): string
189189
{
190-
return (string) NumberFormat::toFormattedString(
190+
$currentCalendar = SharedDate::getExcelCalendar();
191+
SharedDate::setExcelCalendar($this->getWorksheet()->getParent()?->getExcelCalendar());
192+
$formattedValue = (string) NumberFormat::toFormattedString(
191193
$this->getCalculatedValue(),
192194
(string) $this->getStyle()->getNumberFormat()->getFormatCode(true)
193195
);
196+
SharedDate::setExcelCalendar($currentCalendar);
197+
198+
return $formattedValue;
194199
}
195200

196201
protected static function updateIfCellIsTableHeader(?Worksheet $workSheet, self $cell, mixed $oldValue, mixed $newValue): void
197202
{
203+
$oldValue = (is_scalar($oldValue) || $oldValue instanceof Stringable) ? ((string) $oldValue) : null;
204+
$newValue = (is_scalar($newValue) || $newValue instanceof Stringable) ? ((string) $newValue) : null;
198205
if (StringHelper::strToLower($oldValue ?? '') === StringHelper::strToLower($newValue ?? '') || $workSheet === null) {
199206
return;
200207
}
@@ -262,7 +269,10 @@ public function setValueExplicit(mixed $value, string $dataType = DataType::TYPE
262269
// Synonym for string
263270
case DataType::TYPE_INLINE:
264271
// Rich text
265-
$this->value = DataType::checkString($value);
272+
if ($value !== null && !is_scalar($value) && !($value instanceof Stringable)) {
273+
throw new SpreadsheetException('Invalid unstringable value for datatype Inline/String/String2');
274+
}
275+
$this->value = DataType::checkString(($value instanceof RichText) ? $value : ((string) $value));
266276

267277
break;
268278
case DataType::TYPE_NUMERIC:
@@ -273,6 +283,9 @@ public function setValueExplicit(mixed $value, string $dataType = DataType::TYPE
273283

274284
break;
275285
case DataType::TYPE_FORMULA:
286+
if ($value !== null && !is_scalar($value) && !($value instanceof Stringable)) {
287+
throw new SpreadsheetException('Invalid unstringable value for datatype Formula');
288+
}
276289
$this->value = (string) $value;
277290

278291
break;
@@ -364,6 +377,8 @@ public function getCalculatedValue(bool $resetLog = true): mixed
364377
{
365378
if ($this->dataType === DataType::TYPE_FORMULA) {
366379
try {
380+
$currentCalendar = SharedDate::getExcelCalendar();
381+
SharedDate::setExcelCalendar($this->getWorksheet()->getParent()?->getExcelCalendar());
367382
$index = $this->getWorksheet()->getParentOrThrow()->getActiveSheetIndex();
368383
$selected = $this->getWorksheet()->getSelectedCells();
369384
$result = Calculation::getInstance(
@@ -379,6 +394,7 @@ public function getCalculatedValue(bool $resetLog = true): mixed
379394
}
380395
}
381396
} catch (SpreadsheetException $ex) {
397+
SharedDate::setExcelCalendar($currentCalendar);
382398
if (($ex->getMessage() === 'Unable to access External Workbook') && ($this->calculatedValue !== null)) {
383399
return $this->calculatedValue; // Fallback for calculations referencing external files.
384400
} elseif (preg_match('/[Uu]ndefined (name|offset: 2|array key 2)/', $ex->getMessage()) === 1) {
@@ -391,6 +407,7 @@ public function getCalculatedValue(bool $resetLog = true): mixed
391407
$ex
392408
);
393409
}
410+
SharedDate::setExcelCalendar($currentCalendar);
394411

395412
if ($result === '#Not Yet Implemented') {
396413
return $this->calculatedValue; // Fallback if calculation engine does not support the formula.
@@ -790,7 +807,9 @@ public function getFormulaAttributes(): mixed
790807
*/
791808
public function __toString(): string
792809
{
793-
return (string) $this->getValue();
810+
$retVal = $this->value;
811+
812+
return ($retVal === null || is_scalar($retVal) || $retVal instanceof Stringable) ? ((string) $retVal) : '';
794813
}
795814

796815
public function getIgnoredErrors(): IgnoredErrors

src/PhpSpreadsheet/Cell/DataType.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use PhpOffice\PhpSpreadsheet\RichText\RichText;
66
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
7+
use Stringable;
78

89
class DataType
910
{
@@ -78,7 +79,7 @@ public static function checkString(null|RichText|string $textValue): RichText|st
7879
*/
7980
public static function checkErrorCode(mixed $value): string
8081
{
81-
$value = (string) $value;
82+
$value = (is_scalar($value) || $value instanceof Stringable) ? ((string) $value) : '#NULL!';
8283

8384
if (!isset(self::$errorCodes[$value])) {
8485
$value = '#NULL!';

0 commit comments

Comments
 (0)