Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Autofilter Part 2 #2162

Merged
merged 5 commits into from
Jun 24, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions docs/topics/autofilters.md
Original file line number Diff line number Diff line change
Expand Up @@ -251,16 +251,16 @@ $columnFilter->createRule()
);
```

MS Excel uses \* as a wildcard to match any number of characters, and ?
as a wildcard to match a single character. 'U\*' equates to "begins with
a 'U'"; '\*U' equates to "ends with a 'U'"; and '\*U\*' equates to
"contains a 'U'"

If you want to match explicitly against a \* or a ? character, you can
escape it with a tilde (\~), so ?\~\*\* would explicitly match for a \*
character as the second character in the cell value, followed by any
MS Excel uses `*` as a wildcard to match any number of characters, and `?`
as a wildcard to match a single character. `U*` equates to "begins with
a 'U'"; `*U` equates to "ends with a 'U'"; and `*U*` equates to
"contains a 'U'".

If you want to match explicitly against `*` or `?`, you can
escape it with a tilde `~`, so `?~**` would explicitly match for `*`
as the second character in the cell value, followed by any
number of other characters. The only other character that needs escaping
is the \~ itself.
is the `~` itself.

To create a "between" condition, we need to define two rules:

Expand Down
175 changes: 0 additions & 175 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -755,16 +755,6 @@ parameters:
count: 1
path: src/PhpSpreadsheet/Calculation/Internal/MakeMatrix.php

-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Internal\\\\WildcardMatch\\:\\:wildcard\\(\\) should return string but returns string\\|null\\.$#"
count: 1
path: src/PhpSpreadsheet/Calculation/Internal/WildcardMatch.php

-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Internal\\\\WildcardMatch\\:\\:compare\\(\\) has parameter \\$value with no typehint specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Calculation/Internal/WildcardMatch.php

-
message: "#^Call to function is_string\\(\\) with null will always evaluate to false\\.$#"
count: 3
Expand Down Expand Up @@ -5290,156 +5280,6 @@ parameters:
count: 1
path: src/PhpSpreadsheet/Style/Style.php

-
message: "#^Result of && is always true\\.$#"
count: 1
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php

-
message: "#^Parameter \\#1 \\$excelTimestamp of static method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Date\\:\\:excelToTimestamp\\(\\) expects float\\|int, float\\|int\\|string given\\.$#"
count: 1
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php

-
message: "#^Parameter \\#1 \\$number of function floor expects float, float\\|int\\<1, max\\>\\|string given\\.$#"
count: 1
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php

-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\:\\:\\$toReplace has no typehint specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php

-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\:\\:calculateTopTenValue\\(\\) has no return typehint specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php

-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\:\\:calculateTopTenValue\\(\\) has parameter \\$columnID with no typehint specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php

-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\:\\:calculateTopTenValue\\(\\) has parameter \\$endRow with no typehint specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php

-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\:\\:calculateTopTenValue\\(\\) has parameter \\$ruleType with no typehint specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php

-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\:\\:calculateTopTenValue\\(\\) has parameter \\$ruleValue with no typehint specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php

-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\:\\:calculateTopTenValue\\(\\) has parameter \\$startRow with no typehint specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php

-
message: "#^Cannot call method rangeToArray\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\|null\\.$#"
count: 1
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php

-
message: "#^Cannot call method getRowDimension\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\|null\\.$#"
count: 2
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php

-
message: "#^Cannot assign offset 'year' to array\\<string\\>\\|string\\.$#"
count: 1
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php

-
message: "#^Cannot assign offset 'month' to array\\<string\\>\\|string\\.$#"
count: 1
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php

-
message: "#^Cannot assign offset 'day' to array\\<string\\>\\|string\\.$#"
count: 1
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php

-
message: "#^Cannot assign offset 'hour' to array\\<string\\>\\|string\\.$#"
count: 1
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php

-
message: "#^Cannot assign offset 'minute' to array\\<string\\>\\|string\\.$#"
count: 1
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php

-
message: "#^Cannot assign offset 'second' to array\\<string\\>\\|string\\.$#"
count: 1
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php

-
message: "#^Parameter \\#1 \\$str of function preg_quote expects string, array\\<string\\>\\|string given\\.$#"
count: 1
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php

-
message: "#^Cannot call method getCell\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\|null\\.$#"
count: 2
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php

-
message: "#^Binary operation \"\\*\" between 0\\|array\\<string\\>\\|string and float\\|int results in an error\\.$#"
count: 1
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php

-
message: "#^Left side of && is always true\\.$#"
count: 1
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php

-
message: "#^Parameter \\#1 \\$function of function call_user_func_array expects callable\\(\\)\\: mixed, array\\('PhpOffice\\\\\\\\PhpSpreadsheet\\\\\\\\Worksheet\\\\\\\\AutoFilter', mixed\\) given\\.$#"
count: 1
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php

-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\\\Column\\\\Rule\\:\\:\\$ruleTypes has no typehint specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php

-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\\\Column\\\\Rule\\:\\:\\$dateTimeGroups has no typehint specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php

-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\\\Column\\\\Rule\\:\\:\\$dynamicTypes has no typehint specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php

-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\\\Column\\\\Rule\\:\\:\\$operators has no typehint specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php

-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\\\Column\\\\Rule\\:\\:\\$topTenValue has no typehint specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php

-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\\\Column\\\\Rule\\:\\:\\$topTenType has no typehint specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php

-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\\\Column\\\\Rule\\:\\:\\$parent \\(PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\\\Column\\) does not accept PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\\\Column\\|null\\.$#"
count: 2
path: src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php

-
message: "#^Cannot call method getCell\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\|null\\.$#"
count: 1
Expand Down Expand Up @@ -7030,16 +6870,6 @@ parameters:
count: 2
path: src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php

-
message: "#^Parameter \\#2 \\$value of method XMLWriter\\:\\:writeAttribute\\(\\) expects string, array\\<string\\>\\|string given\\.$#"
count: 2
path: src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php

-
message: "#^Argument of an invalid type array\\<string\\>\\|string supplied for foreach, only iterables are supported\\.$#"
count: 1
path: src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php

-
message: "#^Parameter \\#2 \\$content of method XMLWriter\\:\\:writeElement\\(\\) expects string\\|null, int\\|string given\\.$#"
count: 1
Expand Down Expand Up @@ -7285,11 +7115,6 @@ parameters:
count: 1
path: tests/PhpSpreadsheetTests/Style/ConditionalTest.php

-
message: "#^Parameter \\#1 \\$pValue of method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\\\Column\\\\Rule\\:\\:setValue\\(\\) expects array\\<string\\>\\|string, int given\\.$#"
count: 1
path: tests/PhpSpreadsheetTests/Worksheet/AutoFilter/Column/RuleTest.php

-
message: "#^Parameter \\#2 \\$pValue of method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\\\Column\\:\\:setAttribute\\(\\) expects string, int given\\.$#"
count: 1
Expand Down
13 changes: 9 additions & 4 deletions samples/Autofilter/10_Autofilter_selection_1.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@
->setCellValue('D1', 'Date')
->setCellValue('E1', 'Sales Value')
->setCellValue('F1', 'Expenditure');
$startYear = $endYear = $currentYear = date('Y');
$dateTime = new DateTime();
$startYear = $endYear = $currentYear = (int) $dateTime->format('Y');
--$startYear;
++$endYear;

Expand All @@ -52,7 +53,9 @@
foreach ($years as $year) {
foreach ($periods as $period) {
foreach ($countries as $country) {
$endDays = date('t', mktime(0, 0, 0, $period, 1, (int) $year));
$dateString = sprintf('%04d-%02d-01T00:00:00', $year, $period);
$dateTime = new DateTime($dateString);
$endDays = (int) $dateTime->format('t');
for ($i = 1; $i <= $endDays; ++$i) {
$eDate = Date::formattedPHPToExcel(
$year,
Expand Down Expand Up @@ -124,10 +127,12 @@
'japan'
)
->setRuleType(Rule::AUTOFILTER_RULETYPE_CUSTOMFILTER);
// Filter the Date column on a filter value of the first day of every period of the current year
// Filter the Date column on a filter value of the last day of every period of the current year
// We us a dateGroup ruletype for this, although it is still a standard filter
foreach ($periods as $period) {
$endDate = date('t', mktime(0, 0, 0, $period, 1, (int) $currentYear));
$dateString = sprintf('%04d-%02d-01T00:00:00', $currentYear, $period);
$dateTime = new DateTime($dateString);
$endDate = (int) $dateTime->format('t');

$autoFilter->getColumn('D')
->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER)
Expand Down
7 changes: 5 additions & 2 deletions samples/Autofilter/10_Autofilter_selection_2.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@
->setCellValue('D1', 'Date')
->setCellValue('E1', 'Sales Value')
->setCellValue('F1', 'Expenditure');
$startYear = $endYear = $currentYear = date('Y');
$dateTime = new DateTime();
$startYear = $endYear = $currentYear = (int) $dateTime->format('Y');
--$startYear;
++$endYear;

Expand All @@ -52,7 +53,9 @@
foreach ($years as $year) {
foreach ($periods as $period) {
foreach ($countries as $country) {
$endDays = date('t', mktime(0, 0, 0, $period, 1, (int) $year));
$dateString = sprintf('%04d-%02d-01T00:00:00', $year, $period);
$dateTime = new DateTime($dateString);
$endDays = (int) $dateTime->format('t');
for ($i = 1; $i <= $endDays; ++$i) {
$eDate = Date::formattedPHPToExcel(
$year,
Expand Down
13 changes: 9 additions & 4 deletions samples/Autofilter/10_Autofilter_selection_display.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@
->setCellValue('D1', 'Date')
->setCellValue('E1', 'Sales Value')
->setCellValue('F1', 'Expenditure');
$startYear = $endYear = $currentYear = date('Y');
$dateTime = new DateTime();
$startYear = $endYear = $currentYear = (int) $dateTime->format('Y');
--$startYear;
++$endYear;

Expand All @@ -52,7 +53,9 @@
foreach ($years as $year) {
foreach ($periods as $period) {
foreach ($countries as $country) {
$endDays = date('t', mktime(0, 0, 0, $period, 1, (int) $year));
$dateString = sprintf('%04d-%02d-01T00:00:00', $year, $period);
$dateTime = new DateTime($dateString);
$endDays = (int) $dateTime->format('t');
for ($i = 1; $i <= $endDays; ++$i) {
$eDate = Date::formattedPHPToExcel(
$year,
Expand Down Expand Up @@ -124,10 +127,12 @@
'japan'
)
->setRuleType(Rule::AUTOFILTER_RULETYPE_CUSTOMFILTER);
// Filter the Date column on a filter value of the first day of every period of the current year
// Filter the Date column on a filter value of the last day of every period of the current year
// We us a dateGroup ruletype for this, although it is still a standard filter
foreach ($periods as $period) {
$endDate = date('t', mktime(0, 0, 0, $period, 1, (int) $currentYear));
$dateString = sprintf('%04d-%02d-01T00:00:00', $currentYear, $period);
$dateTime = new DateTime($dateString);
$endDate = (int) $dateTime->format('t');

$autoFilter->getColumn('D')
->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER)
Expand Down
28 changes: 14 additions & 14 deletions src/PhpSpreadsheet/Calculation/Internal/WildcardMatch.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,30 @@
class WildcardMatch
{
private const SEARCH_SET = [
'/(?<!~)\*/ui',
'/~\*/ui',
'/(?<!~)\?/ui',
'/~\?/ui',
'~~', // convert double tilde to unprintable value
'~\\*', // convert tilde backslash asterisk to [*] (matches literal asterisk in regexp)
'\\*', // convert backslash asterisk to .* (matches string of any length in regexp)
'~\\?', // convert tilde backslash question to [?] (matches literal question mark in regexp)
'\\?', // convert backslash question to . (matches one character in regexp)
"\x1c", // convert original double tilde to single tilde
];

private const REPLACEMENT_SET = [
'${1}.*',
'\*',
'${1}.',
'\?',
"\x1c",
'[*]',
'.*',
'[?]',
'.',
'~',
];

public static function wildcard(string $wildcard): string
{
// Preg Escape the wildcard, but protecting the Excel * and ? search characters
$wildcard = str_replace(['*', '?'], [0x1A, 0x1B], $wildcard);
$wildcard = preg_quote($wildcard);
$wildcard = str_replace([0x1A, 0x1B], ['*', '?'], $wildcard);

return preg_replace(self::SEARCH_SET, self::REPLACEMENT_SET, $wildcard);
return str_replace(self::SEARCH_SET, self::REPLACEMENT_SET, preg_quote($wildcard));
}

public static function compare($value, string $wildcard): bool
public static function compare(string $value, string $wildcard): bool
{
if ($value === '') {
return true;
Expand Down
Loading