• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nette / latte / 28710508693

04 Jul 2026 03:15PM UTC coverage: 94.108% (-0.8%) from 94.946%
28710508693

push

github

dg
added |json filter with context-aware encoding

New |json filter (Helpers::encodeJson(), refactored out of escapeJs()
without the HtmlStringable unwrap branch). As the last filter in a chain
it carries a special, context-dependent meaning:

- HTML attribute as a whole value (via ExpressionAttributeNode), e.g.
  `data-x={$arr|json}`, routes to HtmlHelpers::formatJsonAttribute(),
  the same JSON + smart-quoting mechanism used by formatDataAttribute().
- JavaScript context (a <script> element, an on* attribute, or a JS
  content-type template, detected via Escaper::isJavaScript()) drops
  |json in PrintNode: escapeJs() already encodes to JSON there, so the
  filter is redundant and would otherwise double-encode. Dropped only
  when auto-escaping applies, so |json|noescape keeps it.
- Everywhere else (HTML text, XML, ...) |json runs as a regular filter
  and its output passes through the normal escaper, so '<', '>', '&' and
  quotes stay escaped.

Not being last, |json is a plain filter with no special handling. Both
nodes special-case it without mutating the AST: they clone the modifier
before removing the filter.

29 of 29 new or added lines in 6 files covered. (100.0%)

11 existing lines in 5 files now uncovered.

6021 of 6398 relevant lines covered (94.11%)

0.94 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.77
/src/Latte/Compiler/Nodes/Php/ModifierNode.php
1
<?php declare(strict_types=1);
2

3
/**
4
 * This file is part of the Latte (https://latte.nette.org)
5
 * Copyright (c) 2008 David Grudl (https://davidgrudl.com)
6
 */
7

8
namespace Latte\Compiler\Nodes\Php;
9

10
use Latte\Compiler\Node;
11
use Latte\Compiler\Position;
12
use Latte\Compiler\PrintContext;
13
use Latte\Helpers;
14

15

16
/**
17
 * Chain of filters with auto-escape flag.
18
 */
19
class ModifierNode extends Node
20
{
21
        /** @deprecated */
22
        public bool $check = true;
23

24

25
        public function __construct(
1✔
26
                /** @var FilterNode[] */
27
                public array $filters,
28
                public bool $escape = false,
29
                public ?Position $position = null,
30
        ) {
31
                (function (FilterNode ...$args) {})(...$filters);
1✔
32
        }
1✔
33

34

35
        public function hasFilter(string $name): bool
1✔
36
        {
37
                foreach ($this->filters as $filter) {
1✔
38
                        if ($filter->name->name === $name) {
1✔
39
                                return true;
1✔
40
                        }
41
                }
42

43
                return false;
1✔
44
        }
45

46

47
        /**
48
         * Removes and returns the first filter with the given name, or null if not found.
49
         */
50
        public function removeFilter(string $name): ?FilterNode
1✔
51
        {
52
                foreach ($this->filters as $i => $filter) {
1✔
53
                        if ($filter->name->name === $name) {
1✔
54
                                return array_splice($this->filters, $i, 1)[0];
1✔
55
                        }
56
                }
57

58
                return null;
1✔
59
        }
60

61

62
        public function print(PrintContext $context): string
63
        {
UNCOV
64
                throw new \LogicException('Cannot directly print ModifierNode');
×
65
        }
66

67

68
        public function printSimple(PrintContext $context, string $expr): string
1✔
69
        {
70
                $expr = FilterNode::printSimple($context, $this->filters, $expr);
1✔
71

72
                $escaper = $context->getEscaper();
1✔
73
                return $this->escape
1✔
74
                        ? $escaper->escape($expr)
1✔
75
                        : $escaper->escapeMandatory($expr, $this->position);
1✔
76
        }
77

78

79
        public function printContentAware(PrintContext $context, string $expr): string
1✔
80
        {
81
                foreach ($this->filters as $filter) {
1✔
82
                        $expr = $filter->printContentAware($context, $expr);
1✔
83
                }
84

85
                return $this->escape
1✔
86
                        ? $context->getEscaper()->escapeContent($expr)
1✔
87
                        : $expr;
1✔
88
        }
89

90

91
        public function &getIterator(): \Generator
1✔
92
        {
93
                foreach ($this->filters as &$filter) {
1✔
94
                        yield $filter;
1✔
95
                }
96
                Helpers::removeNulls($this->filters);
1✔
97
        }
1✔
98
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc