Completed
Pull Request — master (#457)
by Claus
02:40
created

AbstractNode::flatten()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
nc 6
nop 1
dl 0
loc 17
rs 8.8333
c 0
b 0
f 0
1
<?php
2
namespace TYPO3Fluid\Fluid\Core\Parser\SyntaxTree;
3
4
/*
5
 * This file belongs to the package "TYPO3 Fluid".
6
 * See LICENSE.txt that was shipped with this package.
7
 */
8
9
use TYPO3Fluid\Fluid\Core\Parser;
10
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
11
12
/**
13
 * Abstract node in the syntax tree which has been built.
14
 */
15
abstract class AbstractNode implements NodeInterface
16
{
17
18
    /**
19
     * List of Child Nodes.
20
     *
21
     * @var NodeInterface[]
22
     */
23
    protected $childNodes = [];
24
25
    /**
26
     * @param NodeInterface[] $childNodes
27
     * @return NodeInterface
28
     */
29
    public function setChildNodes(array $childNodes)
30
    {
31
        $this->childNodes = $childNodes;
32
        return $this;
33
    }
34
35
    /**
36
     * Evaluate all child nodes and return the evaluated results.
37
     *
38
     * @param RenderingContextInterface $renderingContext
39
     * @return mixed Normally, an object is returned - in case it is concatenated with a string, a string is returned.
40
     * @throws Parser\Exception
41
     */
42
    public function evaluateChildNodes(RenderingContextInterface $renderingContext)
43
    {
44
        $evaluatedNodes = [];
45
        foreach ($this->getChildNodes() as $childNode) {
46
            $evaluatedNodes[] = $this->evaluateChildNode($childNode, $renderingContext, false);
47
        }
48
        // Make decisions about what to actually return
49
        if (empty($evaluatedNodes)) {
50
            return null;
51
        }
52
        if (count($evaluatedNodes) === 1) {
53
            return $evaluatedNodes[0];
54
        }
55
        return implode('', array_map([$this, 'castToString'], $evaluatedNodes));
56
    }
57
58
    /**
59
     * @param NodeInterface $node
60
     * @param RenderingContextInterface $renderingContext
61
     * @param boolean $cast
62
     * @return mixed
63
     */
64
    protected function evaluateChildNode(NodeInterface $node, RenderingContextInterface $renderingContext, $cast)
65
    {
66
        $output = $node->evaluate($renderingContext);
67
        if ($cast) {
68
            $output = $this->castToString($output);
69
        }
70
        return $output;
71
    }
72
73
    /**
74
     * @param mixed $value
75
     * @return string
76
     */
77
    protected function castToString($value)
78
    {
79
        if (is_object($value) && !method_exists($value, '__toString')) {
80
            throw new Parser\Exception('Cannot cast object of type "' . get_class($value) . '" to string.', 1273753083);
81
        }
82
        $output = (string) $value;
83
        return $output;
84
    }
85
86
    /**
87
     * Returns one of the following:
88
     *
89
     * - Itself, if there is more than one child node and one or more nodes are not TextNode or NumericNode
90
     * - A plain value if there is a single child node of type TextNode or NumericNode
91
     * - The one child node if there is only a single child node not of type TextNode or NumericNode
92
     * - Null if there are no child nodes at all.
93
     *
94
     * @param bool $extractNode If TRUE, will extract the value of a single node if the node type contains a scalar value
95
     * @return NodeInterface|string|int|float|null
96
     */
97
    public function flatten(bool $extractNode = false)
98
    {
99
        if (empty($this->childNodes) && $extractNode) {
100
            return null;
101
        }
102
        $nodesCounted = count($this->childNodes);
103
        if ($nodesCounted === 1) {
104
            if ($extractNode) {
105
                if ($this->childNodes[0] instanceof TextNode) {
106
                    $text = $this->childNodes[0]->getText();
107
                    return is_numeric($text) ? $text + 0 : $text;
108
                }
109
            }
110
            return $this->childNodes[0];
111
        }
112
        return $this;
113
    }
114
115
    /**
116
     * Returns all child nodes for a given node.
117
     * This is especially needed to implement the boolean expression language.
118
     *
119
     * @return NodeInterface[] A list of nodes
120
     */
121
    public function getChildNodes()
122
    {
123
        return $this->childNodes;
124
    }
125
126
    /**
127
     * Appends a sub node to this node. Is used inside the parser to append children
128
     *
129
     * @param NodeInterface $childNode The sub node to add
130
     * @return self
131
     */
132
    public function addChildNode(NodeInterface $childNode)
133
    {
134
        if ($childNode instanceof RootNode) {
135
            // Assimilate child nodes instead of allowing a root node inside a root node.
136
            foreach ($childNode->getChildNodes() as $node) {
137
                $this->addChildNode($node);
138
            }
139
        } elseif ($childNode instanceof TextNode && ($last = end($this->childNodes)) && $last instanceof TextNode) {
140
            $last->appendText($childNode->getText());
141
        } else {
142
            $this->childNodes[] = $childNode;
143
        }
144
        return $this;
145
    }
146
}
147