Skip to content

Commit

Permalink
Autofilter Part 2
Browse files Browse the repository at this point in the history
Most of the remaining 32-bit-unsafe date handling that remains in PhpSpreadsheet is in AutoFilter. Cleaning this up demonstrated that there are a lot of problems with AutoFilter, and I will do it in two pieces. Part 1 was PR #2141 which I have just merged.

In this PR:
- Fix remaining 32-bit dates in filterTestInDateGroupSet.
- Also in some of the existing AutoFilter samples. Note that the comments in two of those said the filter was being set for the first day of each month, but the code specifies the last day - I have corrected the comments.
- Remove mocking in unit tests for AutoFilter in favor of 'real' tests.
- Code coverage is now 100% in all of AutoFilter, AutoFilter/Column, and AutoFilter/Common/Rule.
- No remaining AutoFilter(/Column(/Rule)) exceptions in Phpstan baseline.
- Documentation for escaping of asterisk, question mark, and tilde in text filters included spurious backslashes which are now removed.
- Text filter escaping of question mark did not work. There had been no unit tests for any text filtering.
- Likewise there had been no testing for TopTen.
- Above- and below- average filters were not working because they acquired their Calculation instance incorrectly. There had been no tests.
- Several unchanging private static arrays in Rule were changed to private const arrays.
- Clones are now tested.
- RuleTest is moved to same directory as other tests.
  • Loading branch information
oleibman authored and MarkBaker committed Jun 24, 2021
1 parent 5769885 commit a735afc
Show file tree
Hide file tree
Showing 24 changed files with 1,448 additions and 654 deletions.
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
165 changes: 0 additions & 165 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -5290,156 +5290,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 +6880,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 @@ -7270,11 +7110,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
Loading

0 comments on commit a735afc

Please sign in to comment.