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

nette / latte / 22836865504

09 Mar 2026 03:15AM UTC coverage: 94.972% (+0.2%) from 94.76%
22836865504

push

github

dg
github actions: code coverage job is non-blocking

5610 of 5907 relevant lines covered (94.97%)

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
         * Finds all nodes matching the filter and returns them in traversal order.
24
         * @param callable(Node): bool  $filter
25
         * @return list<Node>
26
         */
27
        public static function find(Node $node, callable $filter): array
1✔
28
        {
29
                $found = [];
1✔
30
                (new NodeTraverser)
1✔
31
                        ->traverse($node, enter: function (Node $node) use ($filter, &$found) {
1✔
32
                                if ($filter($node)) {
1✔
33
                                        $found[] = $node;
1✔
34
                                }
35
                        });
1✔
36
                return $found;
1✔
37
        }
38

39

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

57

58
        /**
59
         * Creates a deep clone of the node tree.
60
         */
61
        public static function clone(Node $node): Node
1✔
62
        {
63
                return (new NodeTraverser)
1✔
64
                        ->traverse($node, enter: fn(Node $node) => clone $node) ?? throw new \LogicException;
1✔
65
        }
66

67

68
        /**
69
         * Evaluates a scalar expression node to a PHP value. Resolves constants when $constants is true.
70
         * Throws InvalidArgumentException if the expression cannot be reduced to a value.
71
         */
72
        public static function toValue(ExpressionNode $node, bool $constants = false): mixed
1✔
73
        {
74
                if ($node instanceof Scalar\BooleanNode
1✔
75
                        || $node instanceof Scalar\FloatNode
1✔
76
                        || $node instanceof Scalar\IntegerNode
1✔
77
                        || $node instanceof Scalar\StringNode
1✔
78
                ) {
79
                        return $node->value;
1✔
80

81
                } elseif ($node instanceof Scalar\NullNode) {
1✔
82
                        return null;
1✔
83

84
                } elseif ($node instanceof Expression\ArrayNode) {
1✔
85
                        $res = [];
1✔
86
                        foreach ($node->items as $item) {
1✔
87
                                $value = self::toValue($item->value, $constants);
1✔
88
                                if ($item->key) {
1✔
89
                                        $key = $item->key instanceof Php\IdentifierNode
1✔
90
                                                ? $item->key->name
1✔
91
                                                : self::toValue($item->key, $constants);
1✔
92
                                        $res[$key] = $value;
1✔
93

94
                                } elseif ($item->unpack) {
1✔
95
                                        $res = array_merge($res, $value);
1✔
96

97
                                } else {
98
                                        $res[] = $value;
1✔
99
                                }
100
                        }
101

102
                        return $res;
1✔
103

104
                } elseif ($node instanceof Expression\ConstantFetchNode && $constants) {
1✔
105
                        $name = $node->name->toCodeString();
1✔
106
                        return defined($name)
1✔
107
                                ? constant($name)
1✔
108
                                : throw new \InvalidArgumentException("The constant '$name' is not defined.");
1✔
109

110
                } elseif (
111
                        $node instanceof Expression\ClassConstantFetchNode
1✔
112
                        && $constants
1✔
113
                        && $node->name instanceof Php\IdentifierNode
1✔
114
                ) {
115
                        $class = $node->class instanceof Php\NameNode
1✔
116
                                ? $node->class->toCodeString()
1✔
117
                                : self::toValue($node->class, $constants);
×
118
                        $name = $class . '::' . $node->name->name;
1✔
119
                        return defined($name)
1✔
120
                                ? constant($name)
1✔
121
                                : throw new \InvalidArgumentException("The constant '$name' is not defined.");
1✔
122

123
                } else {
124
                        throw new \InvalidArgumentException('The expression cannot be converted to PHP value.');
1✔
125
                }
126
        }
127

128

129
        /**
130
         * Extracts plain text from a node if it contains only static text, or returns null.
131
         */
132
        public static function toText(?Node $node): ?string
1✔
133
        {
134
                if ($node instanceof Nodes\FragmentNode) {
1✔
135
                        $res = '';
1✔
136
                        foreach ($node->children as $child) {
1✔
137
                                if (($s = self::toText($child)) === null) {
1✔
138
                                        return null;
1✔
139
                                }
140
                                $res .= $s;
1✔
141
                        }
142

143
                        return $res;
1✔
144
                }
145

146
                return match (true) {
147
                        $node instanceof Nodes\TextNode => $node->content,
1✔
148
                        $node instanceof Nodes\NopNode => '',
1✔
149
                        default => null,
1✔
150
                };
151
        }
152
}
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