Skip to content

Commit

Permalink
Merge branch 'master' into issue4311b
Browse files Browse the repository at this point in the history
  • Loading branch information
oleibman authored Jan 16, 2025
2 parents 3d98d34 + eacaa25 commit f8bf508
Show file tree
Hide file tree
Showing 23 changed files with 289 additions and 41 deletions.
17 changes: 15 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com)
and this project adheres to [Semantic Versioning](https://semver.org).

## TBD - 3.8.0
## TBD - 3.9.0

### Added

- CHOOSECOLS, CHOOSEROWS, DROP, TAKE, and EXPAND. [PR #4286](https://github.com/PHPOffice/PhpSpreadsheet/pull/4286)
- Nothing yet.

### Changed

Expand All @@ -25,7 +25,20 @@ and this project adheres to [Semantic Versioning](https://semver.org).

### Fixed

- Nothing yet.

## 2025-01-11 - 3.8.0

### Added

- CHOOSECOLS, CHOOSEROWS, DROP, TAKE, and EXPAND. [PR #4286](https://github.com/PHPOffice/PhpSpreadsheet/pull/4286)

### Fixed

- Security patch for Html navigation.
- Xlsx Reader Shared Formula with Boolean Result. Partial solution for [Issue #4280](https://github.com/PHPOffice/PhpSpreadsheet/issues/4280) [PR #4281](https://github.com/PHPOffice/PhpSpreadsheet/pull/4281)
- Retitling cloned Worksheets. [Issue #641](https://github.com/PHPOffice/PhpSpreadsheet/issues/641) [PR #4302](https://github.com/PHPOffice/PhpSpreadsheet/pull/4302)
- Extremely limited support for GROUPBY function. Partial response to [Issue #4282](https://github.com/PHPOffice/PhpSpreadsheet/issues/4282) [PR #4283](https://github.com/PHPOffice/PhpSpreadsheet/pull/4283)

## 2024-12-26 - 3.7.0

Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2019 PhpSpreadsheet Authors
Copyright (c) 2019-2025 PhpSpreadsheet Authors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
12 changes: 6 additions & 6 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions docs/references/function-list-by-category.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ EXPAND | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Choos
FILTER | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Filter::filter
FORMULATEXT | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Formula::text
GETPIVOTDATA | **Not yet Implemented**
GROUPBY | **Not yet Implemented**
HLOOKUP | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef\HLookup::lookup
HYPERLINK | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Hyperlink::set
INDEX | \PhpOffice\PhpSpreadsheet\Calculation\LookupRef\Matrix::index
Expand Down
1 change: 1 addition & 0 deletions docs/references/function-list-by-name.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ GCD | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpread
GEOMEAN | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical\Averages\Mean::geometric
GESTEP | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering\Compare::GESTEP
GETPIVOTDATA | CATEGORY_LOOKUP_AND_REFERENCE | **Not yet Implemented**
GROUPBY | CATEGORY_LOOKUP_AND_REFERENCE | **Not yet Implemented**
GROWTH | CATEGORY_STATISTICAL | \PhpOffice\PhpSpreadsheet\Calculation\Statistical\Trends::GROWTH

## H
Expand Down
2 changes: 2 additions & 0 deletions docs/topics/recipes.md
Original file line number Diff line number Diff line change
Expand Up @@ -925,6 +925,8 @@ getStyle('A1:M500'), rather than styling the cells individually in a
loop. This is much faster compared to looping through cells and styling
them individually.

**Tip** If you are styling entire row(s) or column(s), e.g. getStyle('A:A'), it is recommended to use applyFromArray as described below rather than setting the styles individually as described above.

There is also an alternative manner to set styles. The following code
sets a cell's style to font bold, alignment right, top border thin and a
gradient fill:
Expand Down
11 changes: 10 additions & 1 deletion src/PhpSpreadsheet/Calculation/Calculation.php
Original file line number Diff line number Diff line change
Expand Up @@ -1256,6 +1256,11 @@ public static function getExcelConstants(string $key): bool|null
'functionCall' => [Functions::class, 'DUMMY'],
'argumentCount' => '2+',
],
'GROUPBY' => [
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
'functionCall' => [Functions::class, 'DUMMY'],
'argumentCount' => '3-7',
],
'GROWTH' => [
'category' => Category::CATEGORY_STATISTICAL,
'functionCall' => [Statistical\Trends::class, 'GROWTH'],
Expand Down Expand Up @@ -4601,7 +4606,7 @@ private static function dataTestReference(array &$operandData): mixed
private static int $matchIndex10 = 10;

/**
* @return array<int, mixed>|false
* @return array<int, mixed>|false|string
*/
private function processTokenStack(mixed $tokens, ?string $cellID = null, ?Cell $cell = null)
{
Expand Down Expand Up @@ -5182,6 +5187,9 @@ private function processTokenStack(mixed $tokens, ?string $cellID = null, ?Cell
} elseif (preg_match('/^' . self::CALCULATION_REGEXP_DEFINEDNAME . '$/miu', $token, $matches)) {
// if the token is a named range or formula, evaluate it and push the result onto the stack
$definedName = $matches[6];
if (str_starts_with($definedName, '_xleta')) {
return Functions::NOT_YET_IMPLEMENTED;
}
if ($cell === null || $pCellWorksheet === null) {
return $this->raiseFormulaError("undefined name '$token'");
}
Expand Down Expand Up @@ -5214,6 +5222,7 @@ private function processTokenStack(mixed $tokens, ?string $cellID = null, ?Cell
}

$result = $this->evaluateDefinedName($cell, $namedRange, $pCellWorksheet, $stack, $specifiedWorksheet !== '');

if (isset($storeKey)) {
$branchStore[$storeKey] = $result;
}
Expand Down
16 changes: 14 additions & 2 deletions src/PhpSpreadsheet/Spreadsheet.php
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ public function getActiveSheet(): Worksheet
public function createSheet(?int $sheetIndex = null): Worksheet
{
$newSheet = new Worksheet($this);
$this->addSheet($newSheet, $sheetIndex);
$this->addSheet($newSheet, $sheetIndex, true);

return $newSheet;
}
Expand All @@ -535,8 +535,20 @@ public function sheetNameExists(string $worksheetName): bool
* @param Worksheet $worksheet The worksheet to add
* @param null|int $sheetIndex Index where sheet should go (0,1,..., or null for last)
*/
public function addSheet(Worksheet $worksheet, ?int $sheetIndex = null): Worksheet
public function addSheet(Worksheet $worksheet, ?int $sheetIndex = null, bool $retitleIfNeeded = false): Worksheet
{
if ($retitleIfNeeded) {
$title = $worksheet->getTitle();
if ($this->sheetNameExists($title)) {
$i = 1;
$newTitle = "$title $i";
while ($this->sheetNameExists($newTitle)) {
++$i;
$newTitle = "$title $i";
}
$worksheet->setTitle($newTitle);
}
}
if ($this->sheetNameExists($worksheet->getTitle())) {
throw new Exception(
"Workbook already contains a worksheet named '{$worksheet->getTitle()}'. Rename this worksheet first."
Expand Down
2 changes: 1 addition & 1 deletion src/PhpSpreadsheet/Worksheet/AutoFilter.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public function setEvaluated(bool $value): void
/**
* Create a new AutoFilter.
*
* @param AddressRange<CellAddress>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|string $range
* @param AddressRange<CellAddress>|AddressRange<int>|AddressRange<string>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|string $range
* A simple string containing a Cell range like 'A1:E10' is permitted
* or passing in an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 8]),
* or an AddressRange object.
Expand Down
4 changes: 2 additions & 2 deletions src/PhpSpreadsheet/Worksheet/Table.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class Table implements Stringable
/**
* Create a new Table.
*
* @param AddressRange<CellAddress>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|string $range
* @param AddressRange<CellAddress>|AddressRange<int>|AddressRange<string>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|string $range
* A simple string containing a Cell range like 'A1:E10' is permitted
* or passing in an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 8]),
* or an AddressRange object.
Expand Down Expand Up @@ -268,7 +268,7 @@ public function getRange(): string
/**
* Set Table Cell Range.
*
* @param AddressRange<CellAddress>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|string $range
* @param AddressRange<CellAddress>|AddressRange<int>|AddressRange<string>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|string $range
* A simple string containing a Cell range like 'A1:E10' is permitted
* or passing in an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 8]),
* or an AddressRange object.
Expand Down
4 changes: 2 additions & 2 deletions src/PhpSpreadsheet/Worksheet/Validations.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public static function validateCellAddress(null|CellAddress|string|array $cellAd
/**
* Validate a cell address or cell range.
*
* @param AddressRange<CellAddress>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|CellAddress|int|string $cellRange Coordinate of the cells as a string, eg: 'C5:F12';
* @param AddressRange<CellAddress>|AddressRange<int>|AddressRange<string>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|CellAddress|int|string $cellRange Coordinate of the cells as a string, eg: 'C5:F12';
* or as an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 12]),
* or as a CellAddress or AddressRange object.
*/
Expand All @@ -59,7 +59,7 @@ public static function validateCellOrCellRange(AddressRange|CellAddress|int|stri
/**
* Validate a cell range.
*
* @param AddressRange<CellAddress>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|string $cellRange Coordinate of the cells as a string, eg: 'C5:F12';
* @param AddressRange<CellAddress>|AddressRange<int>|AddressRange<string>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|string $cellRange Coordinate of the cells as a string, eg: 'C5:F12';
* or as an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 12]),
* or as an AddressRange object.
*/
Expand Down
26 changes: 15 additions & 11 deletions src/PhpSpreadsheet/Worksheet/Worksheet.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ class Worksheet
public const MERGE_CELL_CONTENT_HIDE = 'hide';
public const MERGE_CELL_CONTENT_MERGE = 'merge';

public const FUNCTION_LIKE_GROUPBY = '/\\b(groupby|_xleta)\\b/i'; // weird new syntax

protected const SHEET_NAME_REQUIRES_NO_QUOTES = '/^[_\p{L}][_\p{L}\p{N}]*$/mui';

/**
Expand Down Expand Up @@ -321,6 +323,7 @@ public function __construct(?Spreadsheet $parent = null, string $title = 'Worksh
{
// Set parent and title
$this->parent = $parent;
$this->hash = spl_object_id($this);
$this->setTitle($title, false);
// setTitle can change $pTitle
$this->setCodeName($this->getTitle());
Expand Down Expand Up @@ -349,7 +352,6 @@ public function __construct(?Spreadsheet $parent = null, string $title = 'Worksh
$this->autoFilter = new AutoFilter('', $this);
// Table collection
$this->tableCollection = new ArrayObject();
$this->hash = spl_object_id($this);
}

/**
Expand Down Expand Up @@ -869,7 +871,7 @@ public function setTitle(string $title, bool $updateFormulaCellReferences = true
// Syntax check
self::checkSheetTitle($title);

if ($this->parent) {
if ($this->parent && $this->parent->getIndex($this, true) >= 0) {
// Is there already such sheet name?
if ($this->parent->sheetNameExists($title)) {
// Use name, but append with lowest possible integer
Expand Down Expand Up @@ -899,7 +901,7 @@ public function setTitle(string $title, bool $updateFormulaCellReferences = true
// Set title
$this->title = $title;

if ($this->parent && $this->parent->getCalculationEngine()) {
if ($this->parent && $this->parent->getIndex($this, true) >= 0 && $this->parent->getCalculationEngine()) {
// New title
$newTitle = $this->getTitle();
$this->parent->getCalculationEngine()
Expand Down Expand Up @@ -1389,7 +1391,7 @@ public function getStyles(): array
/**
* Get style for cell.
*
* @param AddressRange<CellAddress>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|CellAddress|int|string $cellCoordinate
* @param AddressRange<CellAddress>|AddressRange<int>|AddressRange<string>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|CellAddress|int|string $cellCoordinate
* A simple string containing a cell address like 'A1' or a cell range like 'A1:E10'
* or passing in an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 8]),
* or a CellAddress or AddressRange object.
Expand Down Expand Up @@ -1693,7 +1695,7 @@ public function getColumnBreaks(): array
/**
* Set merge on a cell range.
*
* @param AddressRange<CellAddress>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|string $range A simple string containing a Cell range like 'A1:E10'
* @param AddressRange<CellAddress>|AddressRange<int>|AddressRange<string>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|string $range A simple string containing a Cell range like 'A1:E10'
* or passing in an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 8]),
* or an AddressRange.
* @param string $behaviour How the merged cells should behave.
Expand Down Expand Up @@ -1818,7 +1820,7 @@ public function mergeCellBehaviour(Cell $cell, string $upperLeft, string $behavi
/**
* Remove merge on a cell range.
*
* @param AddressRange<CellAddress>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|string $range A simple string containing a Cell range like 'A1:E10'
* @param AddressRange<CellAddress>|AddressRange<int>|AddressRange<string>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|string $range A simple string containing a Cell range like 'A1:E10'
* or passing in an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 8]),
* or an AddressRange.
*
Expand Down Expand Up @@ -1869,7 +1871,7 @@ public function setMergeCells(array $mergeCells): static
/**
* Set protection on a cell or cell range.
*
* @param AddressRange<CellAddress>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|CellAddress|int|string $range A simple string containing a Cell range like 'A1:E10'
* @param AddressRange<CellAddress>|AddressRange<int>|AddressRange<string>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|CellAddress|int|string $range A simple string containing a Cell range like 'A1:E10'
* or passing in an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 8]),
* or a CellAddress or AddressRange object.
* @param string $password Password to unlock the protection
Expand All @@ -1892,7 +1894,7 @@ public function protectCells(AddressRange|CellAddress|int|string|array $range, s
/**
* Remove protection on a cell or cell range.
*
* @param AddressRange<CellAddress>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|CellAddress|int|string $range A simple string containing a Cell range like 'A1:E10'
* @param AddressRange<CellAddress>|AddressRange<int>|AddressRange<string>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|CellAddress|int|string $range A simple string containing a Cell range like 'A1:E10'
* or passing in an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 8]),
* or a CellAddress or AddressRange object.
*
Expand Down Expand Up @@ -1950,7 +1952,7 @@ public function getAutoFilter(): AutoFilter
/**
* Set AutoFilter.
*
* @param AddressRange<CellAddress>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|AutoFilter|string $autoFilterOrRange
* @param AddressRange<CellAddress>|AddressRange<int>|AddressRange<string>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|AutoFilter|string $autoFilterOrRange
* A simple string containing a Cell range like 'A1:E10' is permitted for backward compatibility
* or passing in an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 8]),
* or an AddressRange.
Expand Down Expand Up @@ -2696,7 +2698,7 @@ public function setSelectedCell(string $coordinate): static
/**
* Select a range of cells.
*
* @param AddressRange<CellAddress>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|CellAddress|int|string $coordinate A simple string containing a Cell range like 'A1:E10'
* @param AddressRange<CellAddress>|AddressRange<int>|AddressRange<string>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|CellAddress|int|string $coordinate A simple string containing a Cell range like 'A1:E10'
* or passing in an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 8]),
* or a CellAddress or AddressRange object.
*
Expand Down Expand Up @@ -3701,7 +3703,9 @@ public function calculateArrays(bool $preCalculateFormulas = true): void
$keys = $this->cellCollection->getCoordinates();
foreach ($keys as $key) {
if ($this->getCell($key)->getDataType() === DataType::TYPE_FORMULA) {
$this->getCell($key)->getCalculatedValue();
if (preg_match(self::FUNCTION_LIKE_GROUPBY, $this->getCell($key)->getValue()) !== 1) {
$this->getCell($key)->getCalculatedValue();
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/PhpSpreadsheet/Writer/Html.php
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,7 @@ public function generateNavigation(): string
$html .= '<ul class="navigation">' . PHP_EOL;

foreach ($sheets as $sheet) {
$html .= ' <li class="sheet' . $sheetId . '"><a href="#sheet' . $sheetId . '">' . $sheet->getTitle() . '</a></li>' . PHP_EOL;
$html .= ' <li class="sheet' . $sheetId . '"><a href="#sheet' . $sheetId . '">' . htmlspecialchars($sheet->getTitle()) . '</a></li>' . PHP_EOL;
++$sheetId;
}

Expand Down
1 change: 1 addition & 0 deletions src/PhpSpreadsheet/Writer/Xlsx/FunctionPrefix.php
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ class FunctionPrefix
. '|drop'
. '|expand'
. '|filter'
. '|groupby'
. '|hstack'
. '|isomitted'
. '|lambda'
Expand Down
Loading

0 comments on commit f8bf508

Please sign in to comment.