aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorAlexandre Alapetite <alexandre@alapetite.fr>2025-03-13 22:40:41 +0100
committerGitHub <noreply@github.com>2025-03-13 22:40:41 +0100
commita7361a3e7cd335c8932deba88fe60e8f673c2d40 (patch)
treebb4c007188d54897210f2a0539c2df0f438fb069
parentdf545b513b1c43e54da5b023bf3fe01bf36ad013 (diff)
downloadfreshrss-a7361a3e7cd335c8932deba88fe60e8f673c2d40.tar.gz
freshrss-a7361a3e7cd335c8932deba88fe60e8f673c2d40.zip
Implement JSON string concatenation with & operator (#7414)
Inspired by [JSONata syntax](https://docs.jsonata.org/expressions). fix https://github.com/FreshRSS/FreshRSS/issues/6565
-rw-r--r--app/Utils/dotNotationUtil.php32
-rw-r--r--docs/en/users/11_website_scraping.md2
-rw-r--r--tests/app/Utils/dotNotationUtilTest.php3
3 files changed, 36 insertions, 1 deletions
diff --git a/app/Utils/dotNotationUtil.php b/app/Utils/dotNotationUtil.php
index 77ae96c30..89b91bc5b 100644
--- a/app/Utils/dotNotationUtil.php
+++ b/app/Utils/dotNotationUtil.php
@@ -18,9 +18,39 @@ final class FreshRSS_dotNotation_Util
return static::value($default);
}
/** @var \ArrayAccess<string,mixed>|array<string,mixed> $array */
- if (in_array($key, [null, '', '.', '$'], true)) {
+ if ($key === null) {
return $array;
}
+ $key = trim($key);
+
+ if (in_array($key, ['', '.', '$'], true)) {
+ return $array;
+ }
+
+ // If the key is a simple string, return the text
+ if (preg_match('/^(?P<delim>[\'"])(?P<text>[^&]*)(?P=delim)$/', $key, $matches)) {
+ $text = $matches['text'];
+ $text = str_replace('&', '&', $text); // Unescape `&`
+ return $text;
+ }
+
+ // Escape `&` operator
+ $key = preg_replace_callback('/(?P<delim>[\'"])(?P<text>.*?)(?P=delim)/',
+ fn(array $matches): string => str_replace('&', '&', $matches[0]),
+ $key) ?? $key;
+
+ // If the key contains string concatenations with `&`, process them
+ $concats = explode('&', $key);
+ if (count($concats) > 1) {
+ $text = '';
+ foreach ($concats as $concat) {
+ $result = static::get($array, $concat, $default);
+ if (is_scalar($result)) {
+ $text .= (string)$result;
+ }
+ }
+ return $text;
+ }
// Compatibility with brackets path such as `items[0].value`
$key = preg_replace('/\[(\d+)\]/', '.$1', $key);
diff --git a/docs/en/users/11_website_scraping.md b/docs/en/users/11_website_scraping.md
index f725280ea..a5704b81b 100644
--- a/docs/en/users/11_website_scraping.md
+++ b/docs/en/users/11_website_scraping.md
@@ -50,6 +50,8 @@ and the link would be `links[1]`.
It is a similar syntax to the JavaScript way to access JSON: `object.object.array[2].property`.
+Support string concatenation with a syntax like: `meta.title & " some text"` using single-quotes or double-quotes.
+
## Tips & tricks
- [Timezone of date](https://github.com/FreshRSS/FreshRSS/discussions/5483)
diff --git a/tests/app/Utils/dotNotationUtilTest.php b/tests/app/Utils/dotNotationUtilTest.php
index 20d4726af..787d3ddf3 100644
--- a/tests/app/Utils/dotNotationUtilTest.php
+++ b/tests/app/Utils/dotNotationUtilTest.php
@@ -33,6 +33,9 @@ class dotNotationUtilTest extends PHPUnit\Framework\TestCase {
yield [$array, 'items[0].meta.title', 'first'];
yield [$array, 'items.1.meta.title', 'second'];
yield [$array, 'items[1].meta.title', 'second'];
+ yield [$array, '"Hello " & hello & \'!\'', 'Hello world!'];
+ yield [$array, '"Hello & goodbye " & hello & \'!\'', 'Hello & goodbye world!'];
+ yield [$array, '"Hello " & hello & deeper.hello & "!"', 'Hello worldagain!'];
}
/**