Skip to content

Commit

Permalink
Handle Overlapping Ranges
Browse files Browse the repository at this point in the history
Fix PHPOffice#4318. Also make getConditionalStyles more useful, by adding a non-default parameter so that all rules pertaining to a single-cell coordinate can be returned, and in priority order. By default, just the first matching rule will be returned.
  • Loading branch information
oleibman committed Jan 15, 2025
1 parent 56e7422 commit 41ca105
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 22 deletions.
73 changes: 51 additions & 22 deletions src/PhpSpreadsheet/Worksheet/Worksheet.php
Original file line number Diff line number Diff line change
Expand Up @@ -1415,27 +1415,68 @@ public function getStyle(AddressRange|CellAddress|int|string|array $cellCoordina
* included in a conditional style range.
* If a range of cells is specified, then the styles will only be returned if the range matches the entire
* range of the conditional.
* @param bool $firstOnly default true, return all matching
* conditionals ordered by priority if false, first only if true
*
* @return Conditional[]
*/
public function getConditionalStyles(string $coordinate): array
public function getConditionalStyles(string $coordinate, bool $firstOnly = true): array
{
$coordinate = strtoupper($coordinate);
if (str_contains($coordinate, ':')) {
if (preg_match('/[: ,]/', $coordinate) === 1) {
return $this->conditionalStylesCollection[$coordinate] ?? [];
}

$cell = $this->getCell($coordinate);
foreach (array_keys($this->conditionalStylesCollection) as $conditionalRange) {
$cellBlocks = explode(',', Coordinate::resolveUnionAndIntersection($conditionalRange));
foreach ($cellBlocks as $cellBlock) {
if ($cell->isInRange($cellBlock)) {
return $this->conditionalStylesCollection[$conditionalRange];
$conditionalStyles = [];
foreach ($this->conditionalStylesCollection as $keyStylesOrig => $conditionalRange) {
$keyStyles = Coordinate::resolveUnionAndIntersection($keyStylesOrig);
$keyParts = explode(',', $keyStyles);
foreach ($keyParts as $keyPart) {
if ($keyPart === $coordinate) {
if ($firstOnly) {
return $conditionalRange;
}
$conditionalStyles[$keyStylesOrig] = $conditionalRange;

break;
} elseif (str_contains($keyPart, ':')) {
if (Coordinate::coordinateIsInsideRange($keyPart, $coordinate)) {
if ($firstOnly) {
return $conditionalRange;
}
$conditionalStyles[$keyStylesOrig] = $conditionalRange;

break;
}
}
}
}
$outArray = [];
foreach ($conditionalStyles as $conditionalArray) {
foreach ($conditionalArray as $conditional) {
$outArray[] = $conditional;
}
}
usort($outArray, [self::class, 'comparePriority']);

return [];
return $outArray;
}

private static function comparePriority(Conditional $condA, Conditional $condB): int
{
$a = $condA->getPriority();
$b = $condB->getPriority();
if ($a === $b) {
return 0;
}
if ($a === 0) {
return 1;
}
if ($b === 0) {
return -1;
}

return ($a < $b) ? -1 : 1;
}

public function getConditionalRange(string $coordinate): ?string
Expand Down Expand Up @@ -1465,19 +1506,7 @@ public function getConditionalRange(string $coordinate): ?string
*/
public function conditionalStylesExists(string $coordinate): bool
{
$coordinate = strtoupper($coordinate);
if (str_contains($coordinate, ':')) {
return isset($this->conditionalStylesCollection[$coordinate]);
}

$cell = $this->getCell($coordinate);
foreach (array_keys($this->conditionalStylesCollection) as $conditionalRange) {
if ($cell->isInRange($conditionalRange)) {
return true;
}
}

return false;
return !empty($this->getConditionalStyles($coordinate));
}

/**
Expand Down
32 changes: 32 additions & 0 deletions tests/PhpSpreadsheetTests/Reader/Xlsx/ConditionalPriority2Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace PhpOffice\PhpSpreadsheetTests\Reader\Xlsx;

use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheetTests\Functional\AbstractFunctional;

class ConditionalPriority2Test extends AbstractFunctional
{
public function testConditionalPriority(): void
{
$filename = 'tests/data/Reader/XLSX/issue.4318.xlsx';
$reader = IOFactory::createReader('Xlsx');
$spreadsheet = $reader->load($filename);
$reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx');
$spreadsheet->disconnectWorksheets();
$sheet = $reloadedSpreadsheet->getActiveSheet();
$rules = $sheet->getConditionalStyles('E5', false);
self::assertCount(4, $rules);
self::assertSame(2, $rules[0]->getPriority());
self::assertSame(['$B$2<2'], $rules[0]->getConditions());
self::assertSame(3, $rules[1]->getPriority());
self::assertSame(['$B$2=""'], $rules[1]->getConditions());
self::assertSame(4, $rules[2]->getPriority());
self::assertSame(['$B$2=3'], $rules[2]->getConditions());
self::assertSame(5, $rules[3]->getPriority());
self::assertSame(['$B$2=2'], $rules[3]->getConditions());
$reloadedSpreadsheet->disconnectWorksheets();
}
}
Binary file added tests/data/Reader/XLSX/issue.4318.xlsx
Binary file not shown.

0 comments on commit 41ca105

Please sign in to comment.