Skip to content

Commit 6a8eda9

Browse files
authored
Merge pull request #3962 from oleibman/atsign
Excel Dynamic Arrays (Avoid Adding At-Signs to Formulas)
2 parents ffbcee6 + fdbf333 commit 6a8eda9

File tree

81 files changed

+3498
-156
lines changed

Some content is hidden

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

81 files changed

+3498
-156
lines changed

CHANGELOG.md

+26
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,32 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com)
66
and this project adheres to [Semantic Versioning](https://semver.org).
77

8+
## TBD - 3.0.0
9+
10+
### Dynamic Arrays
11+
12+
- Support for Excel dynamic arrays is added. It is an opt-in feature, so our hope is that there will be no BC breaks, but it is a very large change. Full support is added for Xlsx. It is emulated as Ctrl-Shift-Enter arrays for Ods read and write and Excel2003 and Gnumeric read. Html/Pdf and Csv writers will populate cells on output if they are the result of array formulas. No support is added for Xls or Slk.
13+
14+
### Added
15+
16+
- Excel Dynamic Arrays. [Issue #3901](https://github.com/PHPOffice/PhpSpreadsheet/issues/3901) [Issue #3659](https://github.com/PHPOffice/PhpSpreadsheet/issues/3659) [Issue #1834](https://github.com/PHPOffice/PhpSpreadsheet/issues/1834) [PR #3962](https://github.com/PHPOffice/PhpSpreadsheet/pull/3962)
17+
18+
### Changed
19+
20+
- Nothing yet.
21+
22+
### Deprecated
23+
24+
- Nothing yet.
25+
26+
### Moved
27+
28+
- Nothing yet.
29+
30+
### Fixed
31+
32+
- Nothing yet.
33+
834
## 2024-08-07 - 2.2.2
935

1036
### Added

docs/references/features-cross-reference.md

+17-7
Original file line numberDiff line numberDiff line change
@@ -365,15 +365,15 @@
365365
<td style="text-align: center; color: red;">✖</td>
366366
</tr>
367367
<tr>
368-
<td style="padding-left: 1em;">Array</td>
369-
<td style="text-align: center; color: red;">✖</td>
370-
<td style="text-align: center; color: red;">✖</td>
371-
<td style="text-align: center; color: red;">✖</td>
372-
<td style="text-align: center; color: red;">✖</td>
373-
<td style="text-align: center; color: red;">✖</td>
374-
<td style="text-align: center; color: red;">✖</td>
368+
<td style="padding-left: 1em;">Array Formula</td>
375369
<td style="text-align: center; color: red;">✖</td>
370+
<td style="text-align: center; color: green;">✔</td>
371+
<td style="text-align: center; color: green;">✔</td>
372+
<td style="text-align: center; color: green;">✔</td>
373+
<td style="text-align: center; color: green;">✔</td>
374+
<td style="text-align: center;">N/A</td>
376375
<td style="text-align: center; color: red;">✖</td>
376+
<td style="text-align: center;">N/A</td>
377377
</tr>
378378
<tr>
379379
<td style="padding-left: 1em;">Rich Text</td>
@@ -1005,6 +1005,7 @@
10051005
5. <span id="footnote5">Xlsx macros can be read and written; their values can be retrieved and changed, but only in a binary form which is unlikely to be useful</span>
10061006
6. <span id="footnote6">There is very limited support for reading styles from an Ods spreadsheet. Writing styles has better support, although Number Format is incomplete.</span>
10071007
7. <span id="footnote7">In most cases, Html reader processes only inline styles; styles provided by Css classes may be ignored.</span>
1008+
8. <span id="footnote8">Code must [opt in](../topics/recipes.md#array-formulas) to array output.</span>
10081009

10091010
## Writers
10101011

@@ -1175,6 +1176,15 @@
11751176
<td style="text-align: center; color: red;">✖</td>
11761177
<td style="text-align: center; color: red;">✖</td>
11771178
</tr>
1179+
<tr>
1180+
<td style="padding-left: 0.5em;"><strong>Array Formula<a href="#footnote8"><sup>8</sup></a></strong></td>
1181+
<td style="text-align: center; color: red;">✖</td>
1182+
<td style="text-align: center; color: green;">✔</td>
1183+
<td style="text-align: center; color: green;">✔</td>
1184+
<td style="text-align: center; color: green;">✔</td>
1185+
<td style="text-align: center; color: green;">✔</td>
1186+
<td style="text-align: center; color: green;">✔</td>
1187+
</tr>
11781188
<tr>
11791189
<td style="padding-left: 0.5em;"><strong>Rows and Column Properties</strong></td>
11801190
<td></td>

docs/references/function-list-by-category.md

+8-3
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,7 @@ CHAR | \PhpOffice\PhpSpreadsheet\Calculation\TextData\Charac
529529
CLEAN | \PhpOffice\PhpSpreadsheet\Calculation\TextData\Trim::nonPrintable
530530
CODE | \PhpOffice\PhpSpreadsheet\Calculation\TextData\CharacterConvert::code
531531
CONCAT | \PhpOffice\PhpSpreadsheet\Calculation\TextData\Concatenate::CONCATENATE
532-
CONCATENATE | \PhpOffice\PhpSpreadsheet\Calculation\TextData\Concatenate::CONCATENATE
532+
CONCATENATE | \PhpOffice\PhpSpreadsheet\Calculation\TextData\Concatenate::actualCONCATENATE
533533
DBCS | **Not yet Implemented**
534534
DOLLAR | \PhpOffice\PhpSpreadsheet\Calculation\TextData\Format::DOLLAR
535535
EXACT | \PhpOffice\PhpSpreadsheet\Calculation\TextData\Text::exact
@@ -586,5 +586,10 @@ WEBSERVICE | \PhpOffice\PhpSpreadsheet\Calculation\Web\Service::we
586586

587587
Excel Function | PhpSpreadsheet Function
588588
-------------------------|--------------------------------------
589-
ANCHORARRAY | **Not yet Implemented**
590-
SINGLE | **Not yet Implemented**
589+
590+
## CATEGORY_MICROSOFT_INTERNAL
591+
592+
Excel Function | PhpSpreadsheet Function
593+
-------------------------|--------------------------------------
594+
ANCHORARRAY | \PhpOffice\PhpSpreadsheet\Calculation\Internal\ExcelArrayPseudoFunctions::anchorArray
595+
SINGLE | \PhpOffice\PhpSpreadsheet\Calculation\Internal\ExcelArrayPseudoFunctions::single

docs/references/function-list-by-name.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ ADDRESS | CATEGORY_LOOKUP_AND_REFERENCE | \PhpOffice\PhpSpread
1515
AGGREGATE | CATEGORY_MATH_AND_TRIG | **Not yet Implemented**
1616
AMORDEGRC | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial\Amortization::AMORDEGRC
1717
AMORLINC | CATEGORY_FINANCIAL | \PhpOffice\PhpSpreadsheet\Calculation\Financial\Amortization::AMORLINC
18-
ANCHORARRAY | CATEGORY_UNCATEGORISED | **Not yet Implemented**
18+
ANCHORARRAY | CATEGORY_MICROSOFT_INTERNAL | \PhpOffice\PhpSpreadsheet\Calculation\Internal\ExcelArrayPseudoFunctions::anchorArray
1919
AND | CATEGORY_LOGICAL | \PhpOffice\PhpSpreadsheet\Calculation\Logical\Operations::logicalAnd
2020
ARABIC | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig\Arabic::evaluate
2121
AREAS | CATEGORY_LOOKUP_AND_REFERENCE | **Not yet Implemented**
@@ -89,7 +89,7 @@ COMBIN | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpread
8989
COMBINA | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig\Combinations::withRepetition
9090
COMPLEX | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering\Complex::COMPLEX
9191
CONCAT | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData\Concatenate::CONCATENATE
92-
CONCATENATE | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData\Concatenate::CONCATENATE
92+
CONCATENATE | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData\Concatenate::actualCONCATENATE
9393
CONFIDENCE | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical\Confidence::CONFIDENCE
9494
CONFIDENCE.NORM | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical\Confidence::CONFIDENCE
9595
CONFIDENCE.T | CATEGORY_STATISTICAL | **Not yet Implemented**
@@ -510,7 +510,7 @@ SHEET | CATEGORY_INFORMATION | **Not yet Implemente
510510
SHEETS | CATEGORY_INFORMATION | **Not yet Implemented**
511511
SIGN | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig\Sign::evaluate
512512
SIN | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig\Trig\Sine::sin
513-
SINGLE | CATEGORY_UNCATEGORISED | **Not yet Implemented**
513+
SINGLE | CATEGORY_MICROSOFT_INTERNAL | \PhpOffice\PhpSpreadsheet\Calculation\Internal\ExcelArrayPseudoFunctions::single
514514
SINH | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig\Trig\Sine::sinh
515515
SKEW | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical\Deviations::skew
516516
SKEW.P | CATEGORY_STATISTICAL | **Not yet Implemented**

docs/topics/calculation-engine.md

+20-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ formula calculation capabilities. A cell can be of a value type
1010
which can be evaluated). For example, the formula `=SUM(A1:A10)`
1111
evaluates to the sum of values in A1, A2, ..., A10.
1212

13+
Calling `getValue()` on a cell that contains a formula will return the formula itself.
14+
1315
To calculate a formula, you can call the cell containing the formula’s
1416
method `getCalculatedValue()`, for example:
1517

@@ -22,7 +24,18 @@ with PhpSpreadsheet, it evaluates to the value "64":
2224

2325
![09-command-line-calculation.png](./images/09-command-line-calculation.png)
2426

25-
When writing a formula to a cell, formulae should always be set as they would appear in an English version of Microsoft Office Excel, and PhpSpreadsheet handles all formulae internally in this format. This means that the following rules hold:
27+
Calling `getCalculatedValue()` on a cell that doesn't contain a formula will simply return the value of that cell; but if the cell does contain a formula, then PhpSpreadsheet will evaluate that formula to calculate the result.
28+
29+
There are a few useful mehods to help identify whether a cell contains a formula or a simple value; and if a formula, to provide further information about it:
30+
31+
```php
32+
$spreadsheet->getActiveSheet()->getCell('E11')->isFormula();
33+
```
34+
will return a boolean true/false, telling you whether that cell contains a formula or not, so you can determine if a call to `getCalculatedVaue()` will need to perform an evaluation.
35+
36+
For more details on working with array formulas, see the [the recipes documentationn](./recipes.md/#array-formulas).
37+
38+
When writing a formula to a cell, formulas should always be set as they would appear in an English version of Microsoft Office Excel, and PhpSpreadsheet handles all formulas internally in this format. This means that the following rules hold:
2639

2740
- Decimal separator is `.` (period)
2841
- Function argument separator is `,` (comma)
@@ -91,6 +104,11 @@ formula calculation is subject to PHP's language characteristics.
91104
Not all functions are supported, for a comprehensive list, read the
92105
[function list by name](../references/function-list-by-name.md).
93106

107+
#### Array arguments for Function Calls in Formulas
108+
109+
While most of the Excel function implementations now support array arguments, there are a few that should accept arrays as arguments but don't do so.
110+
In these cases, the result may be a single value rather than an array; or it may be a `#VALUE!` error.
111+
94112
#### Operator precedence
95113

96114
In Excel `+` wins over `&`, just like `*` wins over `+` in ordinary
@@ -161,7 +179,7 @@ number of seconds from the PHP/Unix base date. The PHP/Unix base date
161179
(0) is 00:00 UST on 1st January 1970. This value can be positive or
162180
negative: so a value of -3600 would be 23:00 hrs on 31st December 1969;
163181
while a value of +3600 would be 01:00 hrs on 1st January 1970. This
164-
gives PHP a date range of between 14th December 1901 and 19th January
182+
gives 32-bit PHP a date range of between 14th December 1901 and 19th January
165183
2038.
166184

167185
#### PHP `DateTime` Objects
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading

docs/topics/reading-files.md

+87-1
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,87 @@ Once you have created a reader object for the workbook that you want to
168168
load, you have the opportunity to set additional options before
169169
executing the `load()` method.
170170

171+
All of these options can be set by calling the appropriate methods against the Reader (as described below), but some options (those with only two possible values) can also be set through flags, either by calling the Reader's `setFlags()` method, or passing the flags as an argument in the call to `load()`.
172+
Those options that can be set through flags are:
173+
174+
Option | Flag | Default
175+
-------------------|-------------------------------------|------------------------
176+
Empty Cells | IReader::IGNORE_EMPTY_CELLS | Load empty cells
177+
Rows with no Cells | IReader::IGNORE_ROWS_WITH_NO_CELLS | Load rows with no cells
178+
Data Only | IReader::READ_DATA_ONLY | Read data, structure and style
179+
Charts | IReader::LOAD_WITH_CHARTS | Don't read charts
180+
181+
Several flags can be combined in a single call:
182+
```php
183+
$inputFileType = 'Xlsx';
184+
$inputFileName = './sampleData/example1.xlsx';
185+
186+
/** Create a new Reader of the type defined in $inputFileType **/
187+
$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader($inputFileType);
188+
/** Set additional flags before the call to load() */
189+
$reader->setFlags(IReader::IGNORE_EMPTY_CELLS | IReader::LOAD_WITH_CHARTS);
190+
$reader->load($inputFileName);
191+
```
192+
or
193+
```php
194+
$inputFileType = 'Xlsx';
195+
$inputFileName = './sampleData/example1.xlsx';
196+
197+
/** Create a new Reader of the type defined in $inputFileType **/
198+
$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader($inputFileType);
199+
/** Set additional flags in the call to load() */
200+
$reader->load($inputFileName, IReader::IGNORE_EMPTY_CELLS | IReader::LOAD_WITH_CHARTS);
201+
```
202+
203+
### Ignoring Empty Cells
204+
205+
Many Excel files have empty rows or columns at the end of a worksheet, which can't easily be seen when looking at the file in Excel (Try using Ctrl-End to see the last cell in a worksheet).
206+
By default, PhpSpreadsheet will load these cells, because they are valid Excel values; but you may find that an apparently small spreadsheet requires a lot of memory for all those empty cells.
207+
If you are running into memory issues with seemingly small files, you can tell PhpSpreadsheet not to load those empty cells using the `setReadEmptyCells()` method.
208+
209+
```php
210+
$inputFileType = 'Xls';
211+
$inputFileName = './sampleData/example1.xls';
212+
213+
/** Create a new Reader of the type defined in $inputFileType **/
214+
$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader($inputFileType);
215+
/** Advise the Reader that we only want to load cell's that contain actual content **/
216+
$reader->setReadEmptyCells(false);
217+
/** Load $inputFileName to a Spreadsheet Object **/
218+
$spreadsheet = $reader->load($inputFileName);
219+
```
220+
221+
Note that cells containing formulae will still be loaded, even if that formula evaluates to a NULL or an empty string.
222+
Similarly, Conditional Styling might also hide the value of a cell; but cells that contain Conditional Styling or Data Validation will always be loaded regardless of their value.
223+
224+
This option is available for the following formats:
225+
226+
Reader | Y/N |Reader | Y/N |Reader | Y/N |
227+
----------|:---:|--------|:---:|--------------|:---:|
228+
Xlsx | YES | Xls | YES | Xml | NO |
229+
Ods | NO | SYLK | NO | Gnumeric | NO |
230+
CSV | NO | HTML | NO
231+
232+
This option is also available through flags.
233+
234+
### Ignoring Rows With No Cells
235+
236+
Similar to the previous item, you can choose to ignore rows which contain no cells.
237+
This can also help with memory issues.
238+
```php
239+
$inputFileType = 'Xlsx';
240+
$inputFileName = './sampleData/example1.xlsx';
241+
242+
/** Create a new Reader of the type defined in $inputFileType **/
243+
$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader($inputFileType);
244+
/** Advise the Reader that we do not want rows with no cells **/
245+
$reader->setIgnoreRowsWithNoCells(true);
246+
/** Load $inputFileName to a Spreadsheet Object **/
247+
$spreadsheet = $reader->load($inputFileName);
248+
```
249+
250+
This option is available only for Xlsx. It is also available through flags.
251+
171252
### Reading Only Data from a Spreadsheet File
172253

173254
If you're only interested in the cell values in a workbook, but don't
@@ -210,6 +291,8 @@ Xlsx | YES | Xls | YES | Xml | YES |
210291
Ods | YES | SYLK | NO | Gnumeric | YES |
211292
CSV | NO | HTML | NO
212293

294+
This option is also available through flags.
295+
213296
### Reading Only Named WorkSheets from a File
214297

215298
If your workbook contains a number of worksheets, but you are only
@@ -642,7 +725,7 @@ Xlsx | NO | Xls | NO | Xml | NO |
642725
Ods | NO | SYLK | NO | Gnumeric | NO |
643726
CSV | YES | HTML | NO
644727

645-
### A Brief Word about the Advanced Value Binder
728+
## A Brief Word about the Advanced Value Binder
646729

647730
When loading data from a file that contains no formatting information,
648731
such as a CSV file, then data is read either as strings or numbers
@@ -694,6 +777,9 @@ Xlsx | NO | Xls | NO | Xml | NO
694777
Ods | NO | SYLK | NO | Gnumeric | NO
695778
CSV | YES | HTML | YES
696779

780+
Note that you can also use the Binder to determine how PhpSpreadsheet identified datatypes for values when you set a cell value without explicitly setting a datatype.
781+
Value Binders can also be used to set formatting for a cell appropriate to the value.
782+
697783
## Error Handling
698784

699785
Of course, you should always apply some error handling to your scripts

0 commit comments

Comments
 (0)