Test Failed
Branch master (bee4a6)
by stéphane
14:37
created

NodeGeneric::getParent()   B

Complexity

Conditions 7
Paths 5

Size

Total Lines 22
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 15
dl 0
loc 22
rs 8.8333
c 0
b 0
f 0
cc 7
nc 5
nop 1
1
<?php
2
3
namespace Dallgoot\Yaml\Nodes\Generic;
4
5
use Dallgoot\Yaml\NodeList;
6
use Dallgoot\Yaml\Nodes\Root;
7
use Dallgoot\Yaml\Nodes\Item;
8
use Dallgoot\Yaml\Nodes\Key;
9
10
/**
11
 * An abstract type for all Nodes that defines generic behaviour
12
 * Node deriving from this MUST implement the "build" method
13
 *
14
 * Note: custom var_dump output is defined by method "__debugInfo"
15
 *
16
 * @author  Stéphane Rebai <[email protected]>
17
 * @license Apache 2.0
18
 * @link    https://github.com/dallgoot/yaml
19
 */
20
abstract class NodeGeneric
21
{
22
    /** @var null|string|boolean */
23
    public $identifier;
24
25
    protected ?self $_parent = null;
26
27
28
    public ?int $indent = -1;
29
30
    public int $line = 0;
31
32
    public string $raw = '';
33
    /** @var null|self|NodeList */
34
    public $value;
35
36
    public ?string $anchor = null;
37
38
    public ?string $tag = null;
39
40
41
    /**
42
     *
43
     * @param array|object|null         $parent The parent collector or NULL otherwise
44
     *
45
     * @return mixed  whatever the build process returns
46
     */
47
    public abstract function build(&$parent = null);
48
49
    /**
50
     * Create the Node object and parses $nodeString
51
     *
52
     * @todo make it more permissive to tabs but replacement
53
     */
54
    public function __construct(string $nodeString, ?int $line = 0)
55
    {
56
        $this->raw    = $nodeString;
57
        $this->line   = (int) $line;
58
        $nodeValue    = preg_replace("/^\t+/m", " ", $nodeString);
59
        $this->indent = strspn($nodeValue, ' ');
60
    }
61
62
    /**
63
     * Sets the parent of the current Node
64
     *
65
     */
66
    protected function setParent(NodeGeneric $node): NodeGeneric
67
    {
68
        $this->_parent = $node;
69
        return $this;
70
    }
71
72
    /**
73
     * Gets the ancestor with specified $indent or the direct $_parent
74
     *
75
     */
76
    public function getParent(?int $indent = null): ?NodeGeneric
77
    {
78
        if (!is_int($indent)) {
79
            if ($this->_parent instanceof NodeGeneric) {
80
                return $this->_parent;
81
            } else {
82
                throw new \Exception("Cannnot find a parent for " . get_class($this), 1);
83
            }
84
        }
85
        $cursor = $this->getParent();
86
        while (
87
            !($cursor instanceof Root)
88
            && (is_null($cursor->indent)
89
                || $cursor->indent >= $indent)
90
        ) {
91
            if ($cursor->_parent) {
92
                $cursor = $cursor->_parent;
93
            } else {
94
                break;
95
            }
96
        }
97
        return $cursor;
98
    }
99
100
    /**
101
     * Gets the root of the structure map (or current Yaml document)
102
     *
103
     * @throws     \Exception  (description)
104
     */
105
    protected function getRoot(): Root
106
    {
107
        if (is_null($this->_parent)) {
108
            throw new \Exception(__METHOD__ . ": can only be used when Node has a parent set", 1);
109
        }
110
        $pointer = $this;
111
        do {
112
            if ($pointer->_parent instanceof NodeGeneric) {
113
                $pointer = $pointer->_parent;
114
            } else {
115
                throw new \Exception("Node has no _parent set : " . get_class($pointer), 1);
116
            }
117
        } while (!($pointer instanceof Root));
118
        return $pointer;
119
    }
120
121
    /**
122
     * Set the value for the current Node :
123
     * - if value is null , then value = $child (Node)
124
     * - if value is Node, then value is a NodeList with (previous value AND $child)
125
     * - if value is a NodeList, push $child into
126
     *
127
     */
128
    public function add(NodeGeneric $child): NodeGeneric
129
    {
130
        $child->setParent($this);
131
        if (is_null($this->value)) {
132
            $this->value = $child;
133
        } else {
134
            if ($this->value instanceof NodeGeneric) {
135
                $this->value = new NodeList($this->value);
136
            }
137
            $this->value->push($child);
138
        }
139
        return $child;
140
    }
141
142
    /**
143
     * Gets the deepest node.
144
     */
145
    public function getDeepestNode(): NodeGeneric
146
    {
147
        $cursor = $this;
148
        while ($cursor->value instanceof NodeGeneric) {
149
            $cursor = $cursor->value;
150
        }
151
        return $cursor;
152
    }
153
154
    public function specialProcess(
155
        /** @scrutinizer ignore-unused */
156
        NodeGeneric &$previous,
157
        /** @scrutinizer ignore-unused */
158
        array &$emptyLines
159
    ): bool {
160
        return false;
161
    }
162
163
    /**
164
     * Find parent target when current Node indentation is lesser than previous node indentation
165
     *
166
     */
167
    public function getTargetOnLessIndent(NodeGeneric &$node): ?NodeGeneric
168
    {
169
        $supposedParent = $this->getParent($node->indent);
170
        if ($node instanceof Item && $supposedParent instanceof Root) {
171
            if ($supposedParent->value->has('Key')) {
172
                $supposedParent->value->setIteratorMode(\SplDoublyLinkedList::IT_MODE_LIFO);
173
                foreach ($supposedParent->value as $child) {
174
                    if ($child instanceof Key) {
175
                        $supposedParent->value->setIteratorMode(\SplDoublyLinkedList::IT_MODE_FIFO);
176
                        // $supposedParent->value->rewind();
177
                        return $child;
178
                    }
179
                }
180
            }
181
        }
182
        return $supposedParent;
183
    }
184
185
    /**
186
     * Find parent target when current Node indentation is equal to previous node indentation
187
     *
188
     */
189
    public function getTargetOnEqualIndent(NodeGeneric &$node): ?NodeGeneric
190
    {
191
        return $this->getParent();
192
    }
193
194
    /**
195
     * Find parent target when current Node indentation is superior than previous node indentation
196
     *
197
     */
198
    public function getTargetOnMoreIndent(NodeGeneric &$node): ?NodeGeneric
199
    {
200
        return $this->isAwaitingChild($node) ? $this : $this->getParent();
201
    }
202
203
    protected function isAwaitingChild(NodeGeneric $node): bool
204
    {
205
        return false;
206
    }
207
208
    /**
209
     * Determines if $subject is one of the Node types provided (as strings) in $comparison array
210
     * A node type is one of the class found in "nodes" folder.
211
     *
212
     * @param  string    ...$classNameList  A list of string where each is a Node type e.g. 'Key', 'Blank', etc.
213
     *
214
     * @return     boolean  True if $subject is one of $comparison, False otherwise.
215
     */
216
    public function isOneOf(...$classNameList): bool
217
    {
218
        foreach ($classNameList as $className) {
219
            $fqn =  "Dallgoot\\Yaml\\Nodes\\$className";
220
            if ($this instanceof $fqn) return true;
221
        }
222
        return false;
223
    }
224
225
    /**
226
     * PHP internal function for debugging purpose : simplify output provided by 'var_dump'
227
     *
228
     * @return array  the Node properties and respective values displayed by 'var_dump'
229
     */
230
    public function __debugInfo(): array
231
    {
232
        $props = [];
233
        $props['line->indent'] = "$this->line -> $this->indent";
234
        if ($this->identifier) $props['identifier'] = "($this->identifier)";
235
        if ($this->anchor)     $props['anchor']     = "($this->anchor)";
236
        if ($this->tag)        $props['tag']        = "($this->tag)";
237
        if ($this->value)      $props['value']      = $this->value;
238
        // $props['value'] = $this->value;
239
        $props['raw']   = $this->raw;
240
        if (!$this->_parent)  $props['parent'] = 'NO PARENT!!!';
241
        return $props;
242
    }
243
}
244