diff --git a/docs/reference/filters.md b/docs/reference/filters.md
index 6deb49372..7dd276c1d 100644
--- a/docs/reference/filters.md
+++ b/docs/reference/filters.md
@@ -24,7 +24,7 @@ survives regeneration.
| `bytes` | `Filters::bytes` | | |
| `capitalize` | `Filters::capitalize` | | needs ext-mbstring |
| `ceil` | `Filters::ceil` | | |
-| `checkUrl` | `Filters::checkUrl` | | appended automatically to URL attributes by the `checkUrls` pass; opt out with `nocheck` |
+| `checkUrl` | `Filters::checkUrl` | | appended automatically to URL attributes by the `checkUrls` pass as nullsafe `?\|checkUrl`, so null still drops the attribute; opt out with `nocheck` |
| `clamp` | `Filters::clamp` | | |
| `column` | `Filters::column` | | |
| `commas` | `Filters::commas` | | |
diff --git a/src/Latte/Essential/Passes.php b/src/Latte/Essential/Passes.php
index 70406e9c3..62b791cf6 100644
--- a/src/Latte/Essential/Passes.php
+++ b/src/Latte/Essential/Passes.php
@@ -127,7 +127,7 @@ public function checkUrlsPass(TemplateNode $node): void
&& !$node->modifier->removeFilter('nocheck') && !$node->modifier->removeFilter('noCheck')
&& !$node->modifier->hasFilter('datastream') && !$node->modifier->hasFilter('dataStream')
) {
- $node->modifier->filters[] = new Php\FilterNode(new Php\IdentifierNode('checkUrl'));
+ $node->modifier->filters[] = new Php\FilterNode(new Php\IdentifierNode('checkUrl'), nullsafe: true); // null drops the attribute
}
});
}
diff --git a/tests/common/Engine.migrationWarnings.phpt b/tests/common/Engine.migrationWarnings.phpt
index 28b096480..a7b46379b 100644
--- a/tests/common/Engine.migrationWarnings.phpt
+++ b/tests/common/Engine.migrationWarnings.phpt
@@ -15,6 +15,12 @@ Assert::error(
'Behavior change for attribute \'title\' with value null: previously it rendered as title="", now the attribute is omitted (on line 1 at column 17)',
);
+Assert::error(
+ fn() => $latte->renderToString(''),
+ E_USER_WARNING,
+ 'Behavior change for attribute \'href\' with value null: previously it rendered as href="", now the attribute is omitted (on line 1 at column 12)',
+);
+
Assert::error(
fn() => $latte->renderToString(''),
E_USER_WARNING,
diff --git a/tests/common/Safe.url.phpt b/tests/common/Safe.url.phpt
index a50ad881a..ea9cb011c 100644
--- a/tests/common/Safe.url.phpt
+++ b/tests/common/Safe.url.phpt
@@ -71,11 +71,18 @@ Assert::match(
Assert::contains(
- 'LR\HtmlHelpers::formatAttribute(\' href\', ($this->filters->checkUrl)(($this->filters->upper)($url1)))',
+ 'LR\HtmlHelpers::formatAttribute(\' href\', (($ʟ_tmp = ($this->filters->upper)($url1)) === null ? null : ($this->filters->checkUrl)($ʟ_tmp)))',
$latte->compile(''),
);
+// null drops the attribute like in other attributes
+Assert::match(
+ '
',
+ $latte->renderToString('
', ['url' => null]),
+);
+
+
// xlink:href in inline SVG
Assert::match(
'',