aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Alexandre Alapetite <alexandre@alapetite.fr> 2025-12-24 01:19:06 +0100
committerGravatar GitHub <noreply@github.com> 2025-12-24 01:19:06 +0100
commit00c61cf34c3f9a59b4dc16a4accd7cf85945a5a6 (patch)
tree78cb9940bc1ea860cc261a503f0baf686def7660
parent3cd716861259c6d6a984041618c06c146aba4a2d (diff)
Fix serialisation of dates in searches (#8341)
Dates should not be expanded/resolved in string serialisations of search expressions, as it otherwise break relative user queries such as `P30D`. Fix of https://github.com/FreshRSS/FreshRSS/pull/8293
-rw-r--r--app/Models/Search.php125
-rw-r--r--tests/app/Models/SearchTest.php8
2 files changed, 73 insertions, 60 deletions
diff --git a/app/Models/Search.php b/app/Models/Search.php
index 2b207134c..80db5a58c 100644
--- a/app/Models/Search.php
+++ b/app/Models/Search.php
@@ -35,18 +35,15 @@ class FreshRSS_Search implements \Stringable {
private ?array $intext = null;
/** @var list<string>|null */
private ?array $intext_regex = null;
- /** @var int|false|null */
- private $min_date = null;
- /** @var int|false|null */
- private $max_date = null;
- /** @var int|false|null */
- private $min_pubdate = null;
- /** @var int|false|null */
- private $max_pubdate = null;
- /** @var int|false|null */
- private $min_userdate = null;
- /** @var int|false|null */
- private $max_userdate = null;
+ private ?string $input_date = null;
+ private int|false|null $min_date = null;
+ private int|false|null $max_date = null;
+ private ?string $input_pubdate = null;
+ private int|false|null $min_pubdate = null;
+ private int|false|null $max_pubdate = null;
+ private ?string $input_userdate = null;
+ private int|false|null $min_userdate = null;
+ private int|false|null $max_userdate = null;
/** @var list<string>|null */
private ?array $inurl = null;
/** @var list<string>|null */
@@ -82,18 +79,15 @@ class FreshRSS_Search implements \Stringable {
private ?array $not_intext = null;
/** @var list<string>|null */
private ?array $not_intext_regex = null;
- /** @var int|false|null */
- private $not_min_date = null;
- /** @var int|false|null */
- private $not_max_date = null;
- /** @var int|false|null */
- private $not_min_pubdate = null;
- /** @var int|false|null */
- private $not_max_pubdate = null;
- /** @var int|false|null */
- private $not_min_userdate = null;
- /** @var int|false|null */
- private $not_max_userdate = null;
+ private ?string $input_not_date = null;
+ private int|false|null $not_min_date = null;
+ private int|false|null $not_max_date = null;
+ private ?string $input_not_pubdate = null;
+ private int|false|null $not_min_pubdate = null;
+ private int|false|null $not_max_pubdate = null;
+ private ?string $input_not_userdate = null;
+ private int|false|null $not_min_userdate = null;
+ private int|false|null $not_max_userdate = null;
/** @var list<string>|null */
private ?array $not_inurl = null;
/** @var list<string>|null */
@@ -161,26 +155,27 @@ class FreshRSS_Search implements \Stringable {
return $s;
}
- private static function dateIntervalToString(int|false|null $min, int|false|null $max): string {
- if ($min === false) {
- $min = null;
- }
- if ($max === false) {
- $max = null;
- }
- if ($min === null && $max === null) {
- return '';
- }
- $s = '';
- if ($min !== null) {
- $s .= date('Y-m-d\\TH:i:s', $min);
- }
- $s .= '/';
- if ($max !== null) {
- $s .= date('Y-m-d\\TH:i:s', $max);
- }
- return $s;
- }
+ // TODO: Reuse as option for a string representation resolving and expanding date intervals
+ // private static function dateIntervalToString(int|false|null $min, int|false|null $max): string {
+ // if ($min === false) {
+ // $min = null;
+ // }
+ // if ($max === false) {
+ // $max = null;
+ // }
+ // if ($min === null && $max === null) {
+ // return '';
+ // }
+ // $s = '';
+ // if ($min !== null) {
+ // $s .= date('Y-m-d\\TH:i:s', $min);
+ // }
+ // $s .= '/';
+ // if ($max !== null) {
+ // $s .= date('Y-m-d\\TH:i:s', $max);
+ // }
+ // return $s;
+ // }
/**
* Return true if both searches have the same constraint parameters (even if the values differ), false otherwise.
@@ -287,14 +282,14 @@ class FreshRSS_Search implements \Stringable {
}
}
- if ($this->min_userdate !== null || $this->max_userdate !== null) {
- $result .= ' userdate:' . self::dateIntervalToString($this->min_userdate, $this->max_userdate);
+ if ($this->input_userdate !== null) {
+ $result .= ' userdate:' . $this->input_userdate;
}
- if ($this->min_pubdate !== null || $this->max_pubdate !== null) {
- $result .= ' pubdate:' . self::dateIntervalToString($this->min_pubdate, $this->max_pubdate);
+ if ($this->input_pubdate !== null) {
+ $result .= ' pubdate:' . $this->input_pubdate;
}
- if ($this->min_date !== null || $this->max_date !== null) {
- $result .= ' date:' . self::dateIntervalToString($this->min_date, $this->max_date);
+ if ($this->input_date !== null) {
+ $result .= ' date:' . $this->input_date;
}
if ($this->intitle_regex !== null) {
@@ -378,14 +373,14 @@ class FreshRSS_Search implements \Stringable {
}
}
- if ($this->not_min_userdate !== null || $this->not_max_userdate !== null) {
- $result .= ' -userdate:' . self::dateIntervalToString($this->not_min_userdate, $this->not_max_userdate);
+ if ($this->input_not_userdate !== null) {
+ $result .= ' -userdate:' . $this->input_not_userdate;
}
- if ($this->not_min_pubdate !== null || $this->not_max_pubdate !== null) {
- $result .= ' -pubdate:' . self::dateIntervalToString($this->not_min_pubdate, $this->not_max_pubdate);
+ if ($this->input_not_pubdate !== null) {
+ $result .= ' -pubdate:' . $this->input_not_pubdate;
}
- if ($this->not_min_date !== null || $this->not_max_date !== null) {
- $result .= ' -date:' . self::dateIntervalToString($this->not_min_date, $this->not_max_date);
+ if ($this->input_not_date !== null) {
+ $result .= ' -date:' . $this->input_not_date;
}
if ($this->not_intitle_regex !== null) {
@@ -1069,6 +1064,9 @@ class FreshRSS_Search implements \Stringable {
$dates = self::removeEmptyValues($matches['search']);
if (!empty($dates[0])) {
[$this->min_date, $this->max_date] = parseDateInterval($dates[0]);
+ if (is_int($this->min_date) || is_int($this->max_date)) {
+ $this->input_date = $dates[0];
+ }
}
}
return $input;
@@ -1080,6 +1078,9 @@ class FreshRSS_Search implements \Stringable {
$dates = self::removeEmptyValues($matches['search']);
if (!empty($dates[0])) {
[$this->not_min_date, $this->not_max_date] = parseDateInterval($dates[0]);
+ if (is_int($this->not_min_date) || is_int($this->not_max_date)) {
+ $this->input_not_date = $dates[0];
+ }
}
}
return $input;
@@ -1096,6 +1097,9 @@ class FreshRSS_Search implements \Stringable {
$dates = self::removeEmptyValues($matches['search']);
if (!empty($dates[0])) {
[$this->min_pubdate, $this->max_pubdate] = parseDateInterval($dates[0]);
+ if (is_int($this->min_pubdate) || is_int($this->max_pubdate)) {
+ $this->input_pubdate = $dates[0];
+ }
}
}
return $input;
@@ -1107,6 +1111,9 @@ class FreshRSS_Search implements \Stringable {
$dates = self::removeEmptyValues($matches['search']);
if (!empty($dates[0])) {
[$this->not_min_pubdate, $this->not_max_pubdate] = parseDateInterval($dates[0]);
+ if (is_int($this->not_min_pubdate) || is_int($this->not_max_pubdate)) {
+ $this->input_not_pubdate = $dates[0];
+ }
}
}
return $input;
@@ -1122,6 +1129,9 @@ class FreshRSS_Search implements \Stringable {
$dates = self::removeEmptyValues($matches['search']);
if (!empty($dates[0])) {
[$this->min_userdate, $this->max_userdate] = parseDateInterval($dates[0]);
+ if (is_int($this->min_userdate) || is_int($this->max_userdate)) {
+ $this->input_userdate = $dates[0];
+ }
}
}
return $input;
@@ -1133,6 +1143,9 @@ class FreshRSS_Search implements \Stringable {
$dates = self::removeEmptyValues($matches['search']);
if (!empty($dates[0])) {
[$this->not_min_userdate, $this->not_max_userdate] = parseDateInterval($dates[0]);
+ if (is_int($this->not_min_userdate) || is_int($this->not_max_userdate)) {
+ $this->input_not_userdate = $dates[0];
+ }
}
}
return $input;
diff --git a/tests/app/Models/SearchTest.php b/tests/app/Models/SearchTest.php
index 213fdde45..9e107a79d 100644
--- a/tests/app/Models/SearchTest.php
+++ b/tests/app/Models/SearchTest.php
@@ -956,8 +956,8 @@ final class SearchTest extends \PHPUnit\Framework\TestCase {
/search_regex/i "quoted search" search
-e:3,4 -f:12,13 -c:22,23 -L:32,33 -labels:"Not label,Not other label"
-userdate:2025-06-01T00:00:00/2025-09-01T00:00:00
- -pubdate:2025-06-01T00:00:00/2025-09-01T00:00:00
- -date:2025-06-01T00:00:00/2025-09-01T00:00:00
+ -pubdate:2025
+ -date:P30D
-intitle:/Spam/i -intitle:"'bad"
-intext:/Spam/i -intext:"'bad"
-author:/Dave/i -author:Charlie
@@ -1070,8 +1070,8 @@ final class SearchTest extends \PHPUnit\Framework\TestCase {
['(a b) OR (c d)', 'e', 'e ((a b) OR (c d))'],
['(a b) (c d)', 'e', 'e ((a b) (c d))'],
['(a b)', 'e', 'e (a b)'],
- ['date:2024/', 'date:/2025', 'date:/2025-12-31T23:59:59'],
- ['a', 'date:/2025', 'date:/2025-12-31T23:59:59 a'],
+ ['date:2024/', 'date:/2025', 'date:/2025'],
+ ['a', 'date:/2025', 'date:/2025 a'],
];
}