Skip to content

Commit a735afc

Browse files
oleibmanMarkBaker
authored andcommitted
Autofilter Part 2
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.
1 parent 5769885 commit a735afc

24 files changed

+1448
-654
lines changed

docs/topics/autofilters.md

+9-9
Original file line numberDiff line numberDiff line change
@@ -251,16 +251,16 @@ $columnFilter->createRule()
251251
);
252252
```
253253

254-
MS Excel uses \* as a wildcard to match any number of characters, and ?
255-
as a wildcard to match a single character. 'U\*' equates to "begins with
256-
a 'U'"; '\*U' equates to "ends with a 'U'"; and '\*U\*' equates to
257-
"contains a 'U'"
258-
259-
If you want to match explicitly against a \* or a ? character, you can
260-
escape it with a tilde (\~), so ?\~\*\* would explicitly match for a \*
261-
character as the second character in the cell value, followed by any
254+
MS Excel uses `*` as a wildcard to match any number of characters, and `?`
255+
as a wildcard to match a single character. `U*` equates to "begins with
256+
a 'U'"; `*U` equates to "ends with a 'U'"; and `*U*` equates to
257+
"contains a 'U'".
258+
259+
If you want to match explicitly against `*` or `?`, you can
260+
escape it with a tilde `~`, so `?~**` would explicitly match for `*`
261+
as the second character in the cell value, followed by any
262262
number of other characters. The only other character that needs escaping
263-
is the \~ itself.
263+
is the `~` itself.
264264

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

phpstan-baseline.neon

-165
Original file line numberDiff line numberDiff line change
@@ -5290,156 +5290,6 @@ parameters:
52905290
count: 1
52915291
path: src/PhpSpreadsheet/Style/Style.php
52925292

5293-
-
5294-
message: "#^Result of && is always true\\.$#"
5295-
count: 1
5296-
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
5297-
5298-
-
5299-
message: "#^Parameter \\#1 \\$excelTimestamp of static method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Date\\:\\:excelToTimestamp\\(\\) expects float\\|int, float\\|int\\|string given\\.$#"
5300-
count: 1
5301-
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
5302-
5303-
-
5304-
message: "#^Parameter \\#1 \\$number of function floor expects float, float\\|int\\<1, max\\>\\|string given\\.$#"
5305-
count: 1
5306-
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
5307-
5308-
-
5309-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\:\\:\\$toReplace has no typehint specified\\.$#"
5310-
count: 1
5311-
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
5312-
5313-
-
5314-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\:\\:calculateTopTenValue\\(\\) has no return typehint specified\\.$#"
5315-
count: 1
5316-
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
5317-
5318-
-
5319-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\:\\:calculateTopTenValue\\(\\) has parameter \\$columnID with no typehint specified\\.$#"
5320-
count: 1
5321-
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
5322-
5323-
-
5324-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\:\\:calculateTopTenValue\\(\\) has parameter \\$endRow with no typehint specified\\.$#"
5325-
count: 1
5326-
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
5327-
5328-
-
5329-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\:\\:calculateTopTenValue\\(\\) has parameter \\$ruleType with no typehint specified\\.$#"
5330-
count: 1
5331-
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
5332-
5333-
-
5334-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\:\\:calculateTopTenValue\\(\\) has parameter \\$ruleValue with no typehint specified\\.$#"
5335-
count: 1
5336-
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
5337-
5338-
-
5339-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\:\\:calculateTopTenValue\\(\\) has parameter \\$startRow with no typehint specified\\.$#"
5340-
count: 1
5341-
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
5342-
5343-
-
5344-
message: "#^Cannot call method rangeToArray\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\|null\\.$#"
5345-
count: 1
5346-
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
5347-
5348-
-
5349-
message: "#^Cannot call method getRowDimension\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\|null\\.$#"
5350-
count: 2
5351-
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
5352-
5353-
-
5354-
message: "#^Cannot assign offset 'year' to array\\<string\\>\\|string\\.$#"
5355-
count: 1
5356-
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
5357-
5358-
-
5359-
message: "#^Cannot assign offset 'month' to array\\<string\\>\\|string\\.$#"
5360-
count: 1
5361-
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
5362-
5363-
-
5364-
message: "#^Cannot assign offset 'day' to array\\<string\\>\\|string\\.$#"
5365-
count: 1
5366-
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
5367-
5368-
-
5369-
message: "#^Cannot assign offset 'hour' to array\\<string\\>\\|string\\.$#"
5370-
count: 1
5371-
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
5372-
5373-
-
5374-
message: "#^Cannot assign offset 'minute' to array\\<string\\>\\|string\\.$#"
5375-
count: 1
5376-
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
5377-
5378-
-
5379-
message: "#^Cannot assign offset 'second' to array\\<string\\>\\|string\\.$#"
5380-
count: 1
5381-
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
5382-
5383-
-
5384-
message: "#^Parameter \\#1 \\$str of function preg_quote expects string, array\\<string\\>\\|string given\\.$#"
5385-
count: 1
5386-
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
5387-
5388-
-
5389-
message: "#^Cannot call method getCell\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\|null\\.$#"
5390-
count: 2
5391-
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
5392-
5393-
-
5394-
message: "#^Binary operation \"\\*\" between 0\\|array\\<string\\>\\|string and float\\|int results in an error\\.$#"
5395-
count: 1
5396-
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
5397-
5398-
-
5399-
message: "#^Left side of && is always true\\.$#"
5400-
count: 1
5401-
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
5402-
5403-
-
5404-
message: "#^Parameter \\#1 \\$function of function call_user_func_array expects callable\\(\\)\\: mixed, array\\('PhpOffice\\\\\\\\PhpSpreadsheet\\\\\\\\Worksheet\\\\\\\\AutoFilter', mixed\\) given\\.$#"
5405-
count: 1
5406-
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
5407-
5408-
-
5409-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\\\Column\\\\Rule\\:\\:\\$ruleTypes has no typehint specified\\.$#"
5410-
count: 1
5411-
path: src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php
5412-
5413-
-
5414-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\\\Column\\\\Rule\\:\\:\\$dateTimeGroups has no typehint specified\\.$#"
5415-
count: 1
5416-
path: src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php
5417-
5418-
-
5419-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\\\Column\\\\Rule\\:\\:\\$dynamicTypes has no typehint specified\\.$#"
5420-
count: 1
5421-
path: src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php
5422-
5423-
-
5424-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\\\Column\\\\Rule\\:\\:\\$operators has no typehint specified\\.$#"
5425-
count: 1
5426-
path: src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php
5427-
5428-
-
5429-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\\\Column\\\\Rule\\:\\:\\$topTenValue has no typehint specified\\.$#"
5430-
count: 1
5431-
path: src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php
5432-
5433-
-
5434-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\\\Column\\\\Rule\\:\\:\\$topTenType has no typehint specified\\.$#"
5435-
count: 1
5436-
path: src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php
5437-
5438-
-
5439-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\\\Column\\\\Rule\\:\\:\\$parent \\(PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\\\Column\\) does not accept PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\\\Column\\|null\\.$#"
5440-
count: 2
5441-
path: src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php
5442-
54435293
-
54445294
message: "#^Cannot call method getCell\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\|null\\.$#"
54455295
count: 1
@@ -7030,16 +6880,6 @@ parameters:
70306880
count: 2
70316881
path: src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php
70326882

7033-
-
7034-
message: "#^Parameter \\#2 \\$value of method XMLWriter\\:\\:writeAttribute\\(\\) expects string, array\\<string\\>\\|string given\\.$#"
7035-
count: 2
7036-
path: src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php
7037-
7038-
-
7039-
message: "#^Argument of an invalid type array\\<string\\>\\|string supplied for foreach, only iterables are supported\\.$#"
7040-
count: 1
7041-
path: src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php
7042-
70436883
-
70446884
message: "#^Parameter \\#2 \\$content of method XMLWriter\\:\\:writeElement\\(\\) expects string\\|null, int\\|string given\\.$#"
70456885
count: 1
@@ -7270,11 +7110,6 @@ parameters:
72707110
count: 1
72717111
path: tests/PhpSpreadsheetTests/Style/ConditionalTest.php
72727112

7273-
-
7274-
message: "#^Parameter \\#1 \\$pValue of method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\\\Column\\\\Rule\\:\\:setValue\\(\\) expects array\\<string\\>\\|string, int given\\.$#"
7275-
count: 1
7276-
path: tests/PhpSpreadsheetTests/Worksheet/AutoFilter/Column/RuleTest.php
7277-
72787113
-
72797114
message: "#^Parameter \\#2 \\$pValue of method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\\\Column\\:\\:setAttribute\\(\\) expects string, int given\\.$#"
72807115
count: 1

samples/Autofilter/10_Autofilter_selection_1.php

+9-4
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131
->setCellValue('D1', 'Date')
3232
->setCellValue('E1', 'Sales Value')
3333
->setCellValue('F1', 'Expenditure');
34-
$startYear = $endYear = $currentYear = date('Y');
34+
$dateTime = new DateTime();
35+
$startYear = $endYear = $currentYear = (int) $dateTime->format('Y');
3536
--$startYear;
3637
++$endYear;
3738

@@ -52,7 +53,9 @@
5253
foreach ($years as $year) {
5354
foreach ($periods as $period) {
5455
foreach ($countries as $country) {
55-
$endDays = date('t', mktime(0, 0, 0, $period, 1, (int) $year));
56+
$dateString = sprintf('%04d-%02d-01T00:00:00', $year, $period);
57+
$dateTime = new DateTime($dateString);
58+
$endDays = (int) $dateTime->format('t');
5659
for ($i = 1; $i <= $endDays; ++$i) {
5760
$eDate = Date::formattedPHPToExcel(
5861
$year,
@@ -124,10 +127,12 @@
124127
'japan'
125128
)
126129
->setRuleType(Rule::AUTOFILTER_RULETYPE_CUSTOMFILTER);
127-
// Filter the Date column on a filter value of the first day of every period of the current year
130+
// Filter the Date column on a filter value of the last day of every period of the current year
128131
// We us a dateGroup ruletype for this, although it is still a standard filter
129132
foreach ($periods as $period) {
130-
$endDate = date('t', mktime(0, 0, 0, $period, 1, (int) $currentYear));
133+
$dateString = sprintf('%04d-%02d-01T00:00:00', $currentYear, $period);
134+
$dateTime = new DateTime($dateString);
135+
$endDate = (int) $dateTime->format('t');
131136

132137
$autoFilter->getColumn('D')
133138
->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER)

samples/Autofilter/10_Autofilter_selection_2.php

+5-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131
->setCellValue('D1', 'Date')
3232
->setCellValue('E1', 'Sales Value')
3333
->setCellValue('F1', 'Expenditure');
34-
$startYear = $endYear = $currentYear = date('Y');
34+
$dateTime = new DateTime();
35+
$startYear = $endYear = $currentYear = (int) $dateTime->format('Y');
3536
--$startYear;
3637
++$endYear;
3738

@@ -52,7 +53,9 @@
5253
foreach ($years as $year) {
5354
foreach ($periods as $period) {
5455
foreach ($countries as $country) {
55-
$endDays = date('t', mktime(0, 0, 0, $period, 1, (int) $year));
56+
$dateString = sprintf('%04d-%02d-01T00:00:00', $year, $period);
57+
$dateTime = new DateTime($dateString);
58+
$endDays = (int) $dateTime->format('t');
5659
for ($i = 1; $i <= $endDays; ++$i) {
5760
$eDate = Date::formattedPHPToExcel(
5861
$year,

samples/Autofilter/10_Autofilter_selection_display.php

+9-4
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131
->setCellValue('D1', 'Date')
3232
->setCellValue('E1', 'Sales Value')
3333
->setCellValue('F1', 'Expenditure');
34-
$startYear = $endYear = $currentYear = date('Y');
34+
$dateTime = new DateTime();
35+
$startYear = $endYear = $currentYear = (int) $dateTime->format('Y');
3536
--$startYear;
3637
++$endYear;
3738

@@ -52,7 +53,9 @@
5253
foreach ($years as $year) {
5354
foreach ($periods as $period) {
5455
foreach ($countries as $country) {
55-
$endDays = date('t', mktime(0, 0, 0, $period, 1, (int) $year));
56+
$dateString = sprintf('%04d-%02d-01T00:00:00', $year, $period);
57+
$dateTime = new DateTime($dateString);
58+
$endDays = (int) $dateTime->format('t');
5659
for ($i = 1; $i <= $endDays; ++$i) {
5760
$eDate = Date::formattedPHPToExcel(
5861
$year,
@@ -124,10 +127,12 @@
124127
'japan'
125128
)
126129
->setRuleType(Rule::AUTOFILTER_RULETYPE_CUSTOMFILTER);
127-
// Filter the Date column on a filter value of the first day of every period of the current year
130+
// Filter the Date column on a filter value of the last day of every period of the current year
128131
// We us a dateGroup ruletype for this, although it is still a standard filter
129132
foreach ($periods as $period) {
130-
$endDate = date('t', mktime(0, 0, 0, $period, 1, (int) $currentYear));
133+
$dateString = sprintf('%04d-%02d-01T00:00:00', $currentYear, $period);
134+
$dateTime = new DateTime($dateString);
135+
$endDate = (int) $dateTime->format('t');
131136

132137
$autoFilter->getColumn('D')
133138
->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER)

0 commit comments

Comments
 (0)