From 05fa51581c22f08f6b7c95d4753f83fe9ca58268 Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Sun, 10 Aug 2025 15:05:51 -0700 Subject: [PATCH] splitRange and ProtectedRange Fix #1457, which had gone stale but is now re-opened. The `Coordinate::splitRange` method expects a string of cell ranges, but it is a bit limited. Excel sometimes uses comma for union and space for intersection, and sometimes vice versa. `splitRange` uses comma for union, and doesn't do anything with spaces. This PR adds a new method `Coordinate::allRanges` which handles both union and intersection, and adds a parameter to indicate whether comma means union or intersection (with space meaning the other). Also, since the issue specifically mentioned this as a problem for `ProtectedRange`, an `allRanges` method is added to that class. --- src/PhpSpreadsheet/Cell/Coordinate.php | 25 ++++++++++++++++- .../Worksheet/ProtectedRange.php | 14 ++++++++++ .../Worksheet/Issue1457Test.php | 28 +++++++++++++++++++ 3 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 tests/PhpSpreadsheetTests/Worksheet/Issue1457Test.php diff --git a/src/PhpSpreadsheet/Cell/Coordinate.php b/src/PhpSpreadsheet/Cell/Coordinate.php index 03c475fb66..227f2c1170 100644 --- a/src/PhpSpreadsheet/Cell/Coordinate.php +++ b/src/PhpSpreadsheet/Cell/Coordinate.php @@ -136,7 +136,8 @@ public static function absoluteCoordinate(string $cellAddress): string } /** - * Split range into coordinate strings. + * Split range into coordinate strings, using comma for union + * and ignoring intersection (space). * * @param string $range e.g. 'B4:D9' or 'B4:D9,H2:O11' or 'B4' * @@ -160,6 +161,28 @@ public static function splitRange(string $range): array return $outArray; } + /** + * Split range into coordinate strings, resolving unions and intersections. + * + * @param string $range e.g. 'B4:D9' or 'B4:D9,H2:O11' or 'B4' + * @param bool $unionIsComma true=comma is union, space is intersection + * false=space is union, comma is intersection + * + * @return array> Array containing one or more arrays containing one or two coordinate strings + * e.g. ['B4','D9'] or [['B4','D9'], ['H2','O11']] + * or ['B4'] + */ + public static function allRanges(string $range, bool $unionIsComma = true): array + { + if (!$unionIsComma) { + $range = str_replace([',', ' ', "\0"], ["\0", ',', ' '], $range); + } + + return self::splitRange( + self::resolveUnionAndIntersection($range) + ); + } + /** * Build range from coordinate strings. * diff --git a/src/PhpSpreadsheet/Worksheet/ProtectedRange.php b/src/PhpSpreadsheet/Worksheet/ProtectedRange.php index bd4197628f..13c0d50bf1 100644 --- a/src/PhpSpreadsheet/Worksheet/ProtectedRange.php +++ b/src/PhpSpreadsheet/Worksheet/ProtectedRange.php @@ -2,6 +2,8 @@ namespace PhpOffice\PhpSpreadsheet\Worksheet; +use PhpOffice\PhpSpreadsheet\Cell\Coordinate; + class ProtectedRange { private string $name = ''; @@ -42,4 +44,16 @@ public function getSecurityDescriptor(): string { return $this->securityDescriptor; } + + /** + * Split range into coordinate strings. + * + * @return array> Array containing one or more arrays containing one or two coordinate strings + * e.g. ['B4','D9'] or [['B4','D9'], ['H2','O11']] + * or ['B4'] + */ + public function allRanges(): array + { + return Coordinate::allRanges($this->sqref, false); + } } diff --git a/tests/PhpSpreadsheetTests/Worksheet/Issue1457Test.php b/tests/PhpSpreadsheetTests/Worksheet/Issue1457Test.php new file mode 100644 index 0000000000..dbdead3745 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Worksheet/Issue1457Test.php @@ -0,0 +1,28 @@ +protectCells('C14:O15 C161:O1081 C16:H160 J16:O160 Q5'); + $protectedRanges = $sheet->getProtectedCellRanges(); + self::assertCount(1, $protectedRanges); + $range0 = reset($protectedRanges); + $expected = [ + ['C14', 'O15'], + ['C161', 'O1081'], + ['C16', 'H160'], + ['J16', 'O160'], + ['Q5'], + ]; + self::assertSame($expected, $range0->allRanges()); + } +}