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

nette / latte / 22368407632

24 Feb 2026 08:17PM UTC coverage: 94.825% (-0.2%) from 95.054%
22368407632

push

github

dg
phpstan

135 of 147 new or added lines in 38 files covered. (91.84%)

186 existing lines in 70 files now uncovered.

5534 of 5836 relevant lines covered (94.83%)

0.95 hits per line

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

98.53
/src/Latte/Compiler/NodeHelpers.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;
9

10
use Latte\Compiler\Nodes\Php;
11
use Latte\Compiler\Nodes\Php\Expression;
12
use Latte\Compiler\Nodes\Php\ExpressionNode;
13
use Latte\Compiler\Nodes\Php\Scalar;
14
use function array_merge, constant, defined;
15

16

17
/**
18
 * Utility functions for working with AST nodes.
19
 */
20
final class NodeHelpers
21
{
22
        /**
23
         * @param callable(Node): bool  $filter
24
         * @return Node[]
25
         */
26
        public static function find(Node $node, callable $filter): array
1✔
27
        {
28
                $found = [];
1✔
29
                (new NodeTraverser)
1✔
30
                        ->traverse($node, enter: function (Node $node) use ($filter, &$found) {
1✔
31
                                if ($filter($node)) {
1✔
32
                                        $found[] = $node;
1✔
33
                                }
34
                        });
1✔
35
                return $found;
1✔
36
        }
37

38

39
        /** @param callable(Node): bool  $filter */
40
        public static function findFirst(Node $node, callable $filter): ?Node
1✔
41
        {
42
                $found = null;
1✔
43
                (new NodeTraverser)
1✔
44
                        ->traverse($node, enter: function (Node $node) use ($filter, &$found) {
1✔
45
                                if ($filter($node)) {
1✔
46
                                        $found = $node;
1✔
47
                                        return NodeTraverser::StopTraversal;
1✔
48
                                }
49
                        });
1✔
50
                return $found;
1✔
51
        }
52

53

54
        public static function clone(Node $node): Node
1✔
55
        {
56
                return (new NodeTraverser)
1✔
57
                        ->traverse($node, enter: fn(Node $node) => clone $node) ?? throw new \LogicException;
1✔
58
        }
59

60

61
        public static function toValue(ExpressionNode $node, bool $constants = false): mixed
1✔
62
        {
63
                if ($node instanceof Scalar\BooleanNode
1✔
64
                        || $node instanceof Scalar\FloatNode
1✔
65
                        || $node instanceof Scalar\IntegerNode
1✔
66
                        || $node instanceof Scalar\StringNode
1✔
67
                ) {
68
                        return $node->value;
1✔
69

70
                } elseif ($node instanceof Scalar\NullNode) {
1✔
71
                        return null;
1✔
72

73
                } elseif ($node instanceof Expression\ArrayNode) {
1✔
74
                        $res = [];
1✔
75
                        foreach ($node->items as $item) {
1✔
76
                                $value = self::toValue($item->value, $constants);
1✔
77
                                if ($item->key) {
1✔
78
                                        $key = $item->key instanceof Php\IdentifierNode
1✔
79
                                                ? $item->key->name
1✔
80
                                                : self::toValue($item->key, $constants);
1✔
81
                                        $res[$key] = $value;
1✔
82

83
                                } elseif ($item->unpack) {
1✔
84
                                        $res = array_merge($res, $value);
1✔
85

86
                                } else {
87
                                        $res[] = $value;
1✔
88
                                }
89
                        }
90

91
                        return $res;
1✔
92

93
                } elseif ($node instanceof Expression\ConstantFetchNode && $constants) {
1✔
94
                        $name = $node->name->toCodeString();
1✔
95
                        return defined($name)
1✔
96
                                ? constant($name)
1✔
97
                                : throw new \InvalidArgumentException("The constant '$name' is not defined.");
1✔
98

99
                } elseif (
100
                        $node instanceof Expression\ClassConstantFetchNode
1✔
101
                        && $constants
1✔
102
                        && $node->name instanceof Php\IdentifierNode
1✔
103
                ) {
104
                        $class = $node->class instanceof Php\NameNode
1✔
105
                                ? $node->class->toCodeString()
1✔
UNCOV
106
                                : self::toValue($node->class, $constants);
×
107
                        $name = $class . '::' . $node->name->name;
1✔
108
                        return defined($name)
1✔
109
                                ? constant($name)
1✔
110
                                : throw new \InvalidArgumentException("The constant '$name' is not defined.");
1✔
111

112
                } else {
113
                        throw new \InvalidArgumentException('The expression cannot be converted to PHP value.');
1✔
114
                }
115
        }
116

117

118
        public static function toText(?Node $node): ?string
1✔
119
        {
120
                if ($node instanceof Nodes\FragmentNode) {
1✔
121
                        $res = '';
1✔
122
                        foreach ($node->children as $child) {
1✔
123
                                if (($s = self::toText($child)) === null) {
1✔
124
                                        return null;
1✔
125
                                }
126
                                $res .= $s;
1✔
127
                        }
128

129
                        return $res;
1✔
130
                }
131

132
                return match (true) {
133
                        $node instanceof Nodes\TextNode => $node->content,
1✔
134
                        $node instanceof Nodes\NopNode => '',
1✔
135
                        default => null,
1✔
136
                };
137
        }
138
}
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