Skip to content

Commit

Permalink
Merge branch 'master' into comppcre
Browse files Browse the repository at this point in the history
  • Loading branch information
oleibman authored Jan 21, 2025
2 parents 3f8fea0 + 8871095 commit dfd8f3c
Show file tree
Hide file tree
Showing 35 changed files with 1,171 additions and 54 deletions.
20 changes: 18 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ 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)
- Methods to get style for row or column. [PR #4317](https://github.com/PHPOffice/PhpSpreadsheet/pull/4317)
- Method for duplicating worksheet in spreadsheet. [PR #4315](https://github.com/PHPOffice/PhpSpreadsheet/pull/4315)

### Changed

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

### Fixed

- Ods Reader Sheet Names with Period. [Issue #4311](https://github.com/PHPOffice/PhpSpreadsheet/issues/4311) [PR #4313](https://github.com/PHPOffice/PhpSpreadsheet/pull/4313)
- Mpdf and Tcpdf Hidden Columns and Merged Cells. [Issue #4319](https://github.com/PHPOffice/PhpSpreadsheet/issues/4319) [PR #4320](https://github.com/PHPOffice/PhpSpreadsheet/pull/4320)
- Html Writer Allow mailto. [Issue #4316](https://github.com/PHPOffice/PhpSpreadsheet/issues/4316) [PR #4322](https://github.com/PHPOffice/PhpSpreadsheet/pull/4322)

## 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
3 changes: 3 additions & 0 deletions docs/topics/recipes.md
Original file line number Diff line number Diff line change
Expand Up @@ -925,6 +925,9 @@ 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.
Also, starting with release 3.9.0, you should use getRowStyle or getColumnStyle to get the style for an entire row or column.

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
27 changes: 23 additions & 4 deletions docs/topics/worksheets.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,19 +95,38 @@ insert the clone into the workbook.

```php
$clonedWorksheet = clone $spreadsheet->getSheetByName('Worksheet 1');
$clonedWorksheet->setTitle('Copy of Worksheet 1');
$clonedWorksheet->setTitle('Copy of Worksheet 1'); // must be unique
$spreadsheet->addSheet($clonedWorksheet);
```
Starting with PhpSpreadsheet 3.9.0, this can be done more simply (copied sheet's title will be set to something unique):
```php
$copiedWorksheet = $spreadsheet->duplicateWorksheetByTitle('sheetname');
```

You can also copy worksheets from one workbook to another, though this
is more complex as PhpSpreadsheet also has to replicate the styling
between the two workbooks. The `addExternalSheet()` method is provided for
this purpose.

$clonedWorksheet = clone $spreadsheet1->getSheetByName('Worksheet 1');
$spreadsheet->addExternalSheet($clonedWorksheet);
```php
$clonedWorksheet = clone $spreadsheet1->getSheetByName('Worksheet 1');
$clonedWorksheet->setTitle('Copy of Worksheet 1'); // must be unique
$spreadsheet1->addSheet($clonedWorksheet);
$spreadsheet->addExternalSheet($clonedWorksheet);
```
Starting with PhpSpreadsheet 3.8.0, this can be simplified:
```php
$clonedWorksheet = clone $spreadsheet1->getSheetByName('Worksheet 1');
$spreadsheet1->addSheet($clonedWorksheet, null, true);
$spreadsheet->addExternalSheet($clonedWorksheet);
```
Starting with PhpSpreadsheet 3.9.0, this can be simplified even further:
```php
$clonedWorksheet = $spreadsheet1->duplicateWorksheetByTitle('sheetname');
$spreadsheet->addExternalSheet($clonedWorksheet);
```

In both cases, it is the developer's responsibility to ensure that
In the cases commented "must be unique", it is the developer's responsibility to ensure that
worksheet names are not duplicated. PhpSpreadsheet will throw an
exception if you attempt to copy worksheets that will result in a
duplicate name.
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
26 changes: 22 additions & 4 deletions src/PhpSpreadsheet/Reader/Ods/FormulaTranslator.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,24 @@

class FormulaTranslator
{
public static function convertToExcelAddressValue(string $openOfficeAddress): string
private static function replaceQuotedPeriod(string $value): string
{
$excelAddress = $openOfficeAddress;
$value2 = '';
$quoted = false;
foreach (mb_str_split($value, 1, 'UTF-8') as $char) {
if ($char === "'") {
$quoted = !$quoted;
} elseif ($char === '.' && $quoted) {
$char = "\u{fffe}";
}
$value2 .= $char;
}

return $value2;
}

public static function convertToExcelAddressValue(string $openOfficeAddress): string
{
// Cell range 3-d reference
// As we don't support 3-d ranges, we're just going to take a quick and dirty approach
// and assume that the second worksheet reference is the same as the first
Expand All @@ -20,15 +34,17 @@ public static function convertToExcelAddressValue(string $openOfficeAddress): st
'/\$?([^\.]+)\.([^\.]+)/miu', // Cell reference in another sheet
'/\.([^\.]+):\.([^\.]+)/miu', // Cell range reference
'/\.([^\.]+)/miu', // Simple cell reference
'/\\x{FFFE}/miu', // restore quoted periods
],
[
'$1!$2:$4',
'$1!$2:$3',
'$1!$2',
'$1:$2',
'$1',
'.',
],
$excelAddress
self::replaceQuotedPeriod($openOfficeAddress)
);

return $excelAddress;
Expand All @@ -52,14 +68,16 @@ public static function convertToExcelFormulaValue(string $openOfficeFormula): st
'/\[\$?([^\.]+)\.([^\.]+)\]/miu', // Cell reference in another sheet
'/\[\.([^\.]+):\.([^\.]+)\]/miu', // Cell range reference
'/\[\.([^\.]+)\]/miu', // Simple cell reference
'/\\x{FFFE}/miu', // restore quoted periods
],
[
'$1!$2:$3',
'$1!$2',
'$1:$2',
'$1',
'.',
],
$value
self::replaceQuotedPeriod($value)
);
// Convert references to defined names/formulae
$value = str_replace('$$', '', $value);
Expand Down
30 changes: 28 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 @@ -529,14 +529,35 @@ public function sheetNameExists(string $worksheetName): bool
return $this->getSheetByName($worksheetName) !== null;
}

public function duplicateWorksheetByTitle(string $title): Worksheet
{
$original = $this->getSheetByNameOrThrow($title);
$index = $this->getIndex($original) + 1;
$clone = clone $original;

return $this->addSheet($clone, $index, true);
}

/**
* Add sheet.
*
* @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 Expand Up @@ -1080,6 +1101,11 @@ public function getCellXfByIndex(int $cellStyleIndex): Style
return $this->cellXfCollection[$cellStyleIndex];
}

public function getCellXfByIndexOrNull(?int $cellStyleIndex): ?Style
{
return ($cellStyleIndex === null) ? null : ($this->cellXfCollection[$cellStyleIndex] ?? null);
}

/**
* Get cellXf by hash code.
*
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
Loading

0 comments on commit dfd8f3c

Please sign in to comment.