More AutoFilter Tweaks

This commit is contained in:
oleibman
2026-06-25 17:08:15 -07:00
parent 7f331bca4c
commit 13edf0d8a2
3 changed files with 93 additions and 51 deletions
+23 -51
View File
@@ -2,6 +2,7 @@
namespace PhpOffice\PhpSpreadsheet\Worksheet;
use Composer\Pcre\Preg;
use DateTime;
use DateTimeZone;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
@@ -146,7 +147,7 @@ class AutoFilter implements Stringable
$this->evaluated = false;
if ($this->workSheet !== null) {
$thisrange = $this->range;
$range = (string) preg_replace('/\d+$/', (string) $this->workSheet->getHighestRow(), $thisrange);
$range = Preg::replace('/\d+$/', (string) $this->workSheet->getHighestRow(), $thisrange);
if ($range !== $thisrange) {
$this->setRange($range);
}
@@ -239,12 +240,10 @@ class AutoFilter implements Stringable
public function setColumn(AutoFilter\Column|string $columnObjectOrString): static
{
$this->evaluated = false;
if ((is_string($columnObjectOrString)) && (!empty($columnObjectOrString))) {
if (is_string($columnObjectOrString)) {
$column = $columnObjectOrString;
} elseif ($columnObjectOrString instanceof AutoFilter\Column) {
$column = $columnObjectOrString->getColumnIndex();
} else {
throw new Exception('Column is not within the autofilter range.');
$column = $columnObjectOrString->getColumnIndex();
}
$this->testColumnInRange($column);
@@ -434,32 +433,15 @@ class AutoFilter implements Stringable
};
} else {
// String values are always tested for equality, factoring in for wildcards (hence a regexp test)
switch ($ruleOperator) {
case Rule::AUTOFILTER_COLUMN_RULE_EQUAL:
$retVal = (bool) preg_match('/^' . $ruleValue . '$/i', $cellValueString);
break;
case Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL:
$retVal = !((bool) preg_match('/^' . $ruleValue . '$/i', $cellValueString));
break;
case Rule::AUTOFILTER_COLUMN_RULE_GREATERTHAN:
$retVal = strcasecmp($cellValueString, $ruleValue) > 0;
break;
case Rule::AUTOFILTER_COLUMN_RULE_GREATERTHANOREQUAL:
$retVal = strcasecmp($cellValueString, $ruleValue) >= 0;
break;
case Rule::AUTOFILTER_COLUMN_RULE_LESSTHAN:
$retVal = strcasecmp($cellValueString, $ruleValue) < 0;
break;
case Rule::AUTOFILTER_COLUMN_RULE_LESSTHANOREQUAL:
$retVal = strcasecmp($cellValueString, $ruleValue) <= 0;
break;
}
$retVal = match ($ruleOperator) {
Rule::AUTOFILTER_COLUMN_RULE_EQUAL => Preg::isMatch('/^' . $ruleValue . '$/i', $cellValueString),
Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL => !(Preg::isMatch('/^' . $ruleValue . '$/i', $cellValueString)),
Rule::AUTOFILTER_COLUMN_RULE_GREATERTHAN => strcasecmp($cellValueString, $ruleValue) > 0,
Rule::AUTOFILTER_COLUMN_RULE_GREATERTHANOREQUAL => strcasecmp($cellValueString, $ruleValue) >= 0,
Rule::AUTOFILTER_COLUMN_RULE_LESSTHAN => strcasecmp($cellValueString, $ruleValue) < 0,
// Rule::AUTOFILTER_COLUMN_RULE_LESSTHANOREQUAL
default => strcasecmp($cellValueString, $ruleValue) <= 0,
};
}
// If there are multiple conditions, then we need to test both using the appropriate join operator
switch ($join) {
@@ -1084,26 +1066,16 @@ class AutoFilter implements Stringable
*/
public function __clone()
{
$vars = get_object_vars($this);
foreach ($vars as $key => $value) {
if (is_object($value)) {
if ($key === 'workSheet') {
// Detach from worksheet
$this->{$key} = null;
} else {
$this->{$key} = clone $value;
}
} elseif ((is_array($value)) && ($key == 'columns')) {
// The columns array of \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\AutoFilter objects
$this->{$key} = [];
foreach ($value as $k => $v) {
$this->{$key}[$k] = clone $v; //* @phpstan-ignore-line
// attach the new cloned Column to this new cloned Autofilter object
$this->{$key}[$k]->setParent($this); //* @phpstan-ignore-line
}
} else {
$this->{$key} = $value;
}
// workSheet is only property which is an object
// detach from worksheet
$this->workSheet = null;
// columns is the only property which is an array
$columns = $this->columns;
$this->columns = [];
foreach ($columns as $k => $v) {
$this->columns[$k] = clone $v;
// attach the new cloned Column to this new cloned Autofilter object
$this->columns[$k]->setParent($this);
}
}
@@ -51,6 +51,14 @@ class ColumnTest extends SetupTeardown
$sheet->getAutoFilter()->getColumn('B');
}
public function testSetBadColumnIndex(): void
{
$this->expectException(PhpSpreadsheetException::class);
$this->expectExceptionMessage('Column string index can not be empty');
$sheet = $this->initSheet();
$sheet->getAutoFilter()->setColumn('');
}
public function testSetColumnIndex(): void
{
$sheet = $this->initSheet();
@@ -0,0 +1,62 @@
<?php
declare(strict_types=1);
namespace PhpOffice\PhpSpreadsheetTests\Worksheet\AutoFilter;
use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column;
use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PHPUnit\Framework\Attributes\DataProvider;
class RuleCustom2Test extends SetupTeardown
{
protected function initSheet(): Worksheet
{
$sheet = $this->getSheet();
$sheet->getCell('A1')->setValue('Heading');
$sheet->getCell('A2')->setValue(2);
//$sheet->getCell('A3')->setValue(3);
$sheet->getCell('A4')->setValue(4);
$sheet->getCell('B1')->setValue('Heading2');
$sheet->getCell('B2')->setValue(1);
$sheet->getCell('B3')->setValue(2);
$sheet->getCell('B4')->setValue(3);
$this->maxRow = $maxRow = 4;
$autoFilter = $sheet->getAutoFilter();
$autoFilter->setRange("A1:B$maxRow");
return $sheet;
}
/** @param int[] $expectedResult */
#[DataProvider('providerCondition')]
public function testComparisonToNullString(array $expectedResult, string $condition): void
{
$sheet = $this->initSheet();
$columnFilter = $sheet->getAutoFilter()->getColumn('A');
$columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_CUSTOMFILTER);
$columnFilter->createRule()
->setRule(
$condition,
''
)
->setRuleType(Rule::AUTOFILTER_RULETYPE_CUSTOMFILTER);
self::assertEquals($expectedResult, $this->getVisible());
}
/** @return array<array{int[], string}> */
public static function providerCondition(): array
{
// Comparing to null-string, equal and notequal work as expected.
// Other conditions filter everything.
return [
[[3], Rule::AUTOFILTER_COLUMN_RULE_EQUAL],
[[2, 4], Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL],
[[2, 3, 4], Rule::AUTOFILTER_COLUMN_RULE_GREATERTHAN],
[[2, 3, 4], Rule::AUTOFILTER_COLUMN_RULE_GREATERTHANOREQUAL],
[[2, 3, 4], Rule::AUTOFILTER_COLUMN_RULE_LESSTHAN],
[[2, 3, 4], Rule::AUTOFILTER_COLUMN_RULE_LESSTHANOREQUAL],
];
}
}