Passed
Push — master ( f81cc4...5281ab )
by stéphane
04:50
created

NodeGeneric::getParent()   B

Complexity

Conditions 7
Paths 5

Size

Total Lines 20
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 7.0222

Importance

Changes 0
Metric Value
cc 7
eloc 14
nc 5
nop 1
dl 0
loc 20
ccs 12
cts 13
cp 0.9231
crap 7.0222
rs 8.8333
c 0
b 0
f 0
1
<?php
2
3
namespace Dallgoot\Yaml\Nodes;
4
5
use Dallgoot\Yaml\NodeList;
6
7
/**
8
 * An abstract type for all Nodes that defines generic behaviour
9
 * Node deriving from this MUST implement the "build" method
10
 *
11
 * Note: custom var_dump output is defined by method "__debugInfo"
12
 *
13
 * @author  Stéphane Rebai <[email protected]>
14
 * @license Apache 2.0
15
 * @link    https://github.com/dallgoot/yaml
16
 */
17
abstract class NodeGeneric
18
{
19
    /** @var null|string|boolean */
20
    protected $identifier;
21
    /** @var null|NodeGeneric */
22
    protected $_parent;
23
24
    /** @var int|null */
25
    public $indent = -1;
26
    /** @var int */
27
    public $line;
28
    /** @var null|string */
29
    public $raw;
30
    /** @var null|NodeGeneric|NodeList */
31
    public $value;
32
    /** @var string|null */
33
    public $anchor;
34
    /** @var string|null */
35
    public $tag;
36
37
    /**
38
     * Create the Node object and parses $nodeString
39
     *
40
     * @param string $nodeString The node string
41
     * @param int|null    $line       The line
42
     * @todo make it more permissive to tabs but replacement
43
     */
44 15
    public function __construct(string $nodeString, $line = 0)
45
    {
46 15
        $this->raw    = $nodeString;
47 15
        $this->line   = (int) $line;
48 15
        $nodeValue    = preg_replace("/^\t+/m", " ", $nodeString);
49 15
        $this->indent = strspn($nodeValue, ' ');
50 15
    }
51
52
    /**
53
     * Sets the parent of the current Node
54
     *
55
     * @param NodeGeneric $node The node
56
     *
57
     * @return NodeGeneric The currentNode
58
     */
59 8
    protected function setParent($node):NodeGeneric
60
    {
61 8
        $this->_parent = $node;
62 8
        return $this;
63
    }
64
65
    /**
66
     * Gets the ancestor with specified $indent or the direct $_parent
67
     *
68
     * @param int|null $indent The indent
69
     *
70
     * @return NodeGeneric   The parent.
71
     */
72 4
    public function getParent(int $indent = null):NodeGeneric
73
    {
74 4
        if (!is_int($indent)){
75 4
            if ($this->_parent instanceof NodeGeneric) {
76 3
                return $this->_parent;
77
            } else {
78 1
                throw new \Exception("Cannnot find a parent for ".get_class($this), 1);
79
            }
80
        }
81 2
        $cursor = $this->getParent();
82 2
        while (!($cursor instanceof Root)
83 2
                && (is_null($cursor->indent)
84 2
                || $cursor->indent >= $indent)) {
85 1
            if ($cursor->_parent) {
86 1
                $cursor = $cursor->_parent;
87
            } else {
88
                break;
89
            }
90
        }
91 2
        return $cursor;
92
    }
93
94
    /**
95
     * Gets the root of the structure map (or current Yaml document)
96
     *
97
     * @throws     \Exception  (description)
98
     *
99
     * @return     Root   The root.
100
     */
101 2
    protected function getRoot():Root
102
    {
103 2
        if (is_null($this->_parent)) {
104 1
            throw new \Exception(__METHOD__.": can only be used when Node has a parent set", 1);
105
        }
106 1
        $cursor = $this;
107 1
        while (!($cursor instanceof Root) && $cursor->_parent instanceof NodeGeneric) {
108 1
            $cursor = $cursor->_parent;
109
        }
110 1
        return $cursor;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $cursor returns the type Dallgoot\Yaml\Nodes\NodeGeneric which includes types incompatible with the type-hinted return Dallgoot\Yaml\Nodes\Root.
Loading history...
111
    }
112
113
    /**
114
     * Set the value for the current Node :
115
     * - if value is null , then value = $child (Node)
116
     * - if value is Node, then value is a NodeList with (previous value AND $child)
117
     * - if value is a NodeList, push $child into
118
     *
119
     * @param NodeGeneric $child The child
120
     *
121
     * @return NodeGeneric
122
     */
123 7
    public function add(NodeGeneric $child):NodeGeneric
124
    {
125 7
        $child->setParent($this);
126 7
        if (is_null($this->value)) {
127 6
            $this->value = $child;
128
        } else {
129 5
            if ($this->value instanceof NodeGeneric) {
130 3
                $this->value = new NodeList($this->value);
131
            }
132 5
            $this->value->push($child);
133
        }
134 7
        return $child;
135
    }
136
137
    /**
138
     * Gets the deepest node.
139
     *
140
     * @return NodeGeneric  The deepest node.
141
     */
142 1
    public function getDeepestNode():NodeGeneric
143
    {
144 1
        $cursor = $this;
145 1
        while ($cursor->value instanceof NodeGeneric) {
146 1
            $cursor = $cursor->value;
147
        }
148 1
        return $cursor;
149
    }
150
151 1
    public function specialProcess(NodeGeneric &$previous, array &$emptyLines):bool
0 ignored issues
show
Unused Code introduced by
The parameter $previous is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

151
    public function specialProcess(/** @scrutinizer ignore-unused */ NodeGeneric &$previous, array &$emptyLines):bool

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $emptyLines is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

151
    public function specialProcess(NodeGeneric &$previous, /** @scrutinizer ignore-unused */ array &$emptyLines):bool

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
152
    {
153 1
        return false;
154
    }
155
156
   /**
157
     * Find parent target when current Node indentation is lesser than previous node indentation
158
     *
159
     * @param NodeGeneric $node
160
     *
161
     * @return NodeGeneric|Key
162
     */
163 1
    public function getTargetOnLessIndent(NodeGeneric &$node):NodeGeneric
164
    {
165 1
        $supposedParent = $this->getParent($node->indent);
166 1
        if ($node instanceof Item && $supposedParent instanceof Root) {
167
            if ($supposedParent->value->has('Key')) {
168
                // $lastKey = null;
169
                foreach ($supposedParent->value as $child) {
170
                    if ($child instanceof Key) {
171
                        $lastKey = $child;
172
                    }
173
                }
174
                return $lastKey;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $lastKey does not seem to be defined for all execution paths leading up to this point.
Loading history...
175
            }
176
        }
177 1
        return $supposedParent;
178
    }
179
180
    /**
181
     * Find parent target when current Node indentation is equal to previous node indentation
182
     *
183
     * @param NodeGeneric $node
184
     *
185
     * @return NodeGeneric
186
     */
187 1
    public function getTargetOnEqualIndent(NodeGeneric &$node):NodeGeneric
188
    {
189 1
        return $this->getParent();
190
    }
191
192
   /**
193
     * Find parent target when current Node indentation is superior than previous node indentation
194
     *
195
     * @param NodeGeneric $node
196
     *
197
     * @return NodeGeneric
198
     */
199
    public function getTargetOnMoreIndent(NodeGeneric &$node):NodeGeneric
200
    {
201
        return $this->isAwaitingChild($node) ? $this : $this->getParent();
202
    }
203
204 1
    protected function isAwaitingChild(NodeGeneric $node):bool
205
    {
206 1
        return false;
207
    }
208
209
    /**
210
     *
211
     * @param Array|Object|null         $parent The parent collector or NULL otherwise
212
     *
213
     * @return mixed  whatever the build process returns
214
     */
215
    abstract function build(&$parent = null);
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
216
217
    /**
218
     * Determines if $subject is one of the Node types provided (as strings) in $comparison array
219
     * A node type is one of the class found in "nodes" folder.
220
     *
221
     * @param  string    ...$classNameList  A list of string whre each is a Node type e.g. 'Key', 'Blank', etc.
222
     *
223
     * @return     boolean  True if $subject is one of $comparison, False otherwise.
224
     */
225 3
    public function isOneOf(...$classNameList):bool
226
    {
227 3
        foreach ($classNameList as $className) {
228 3
            $fqn = __NAMESPACE__."\\$className";
229 3
            if ($this instanceof $fqn) return true;
230
        }
231 3
        return false;
232
    }
233
234
    /**
235
     * PHP internal function for debugging purpose : simplify output provided by 'var_dump'
236
     *
237
     * @return array  the Node properties and respective values displayed by 'var_dump'
238
     */
239 1
    public function __debugInfo():array
240
    {
241 1
        $props = [];
242 1
        $props['line->indent'] = "$this->line -> $this->indent";
243 1
        if ($this->identifier) $props['identifier'] = "($this->identifier)";
244 1
        if ($this->anchor)     $props['anchor']     = "($this->anchor)";
245 1
        if ($this->tag)        $props['tag']        = "($this->tag)";
246 1
        if ($this->value)      $props['value']      = $this->value;
247
        // $props['value'] = $this->value;
248 1
        $props['raw']   = $this->raw;
249 1
        if (!$this->_parent)  $props['parent'] = 'NO PARENT!!!';
250 1
        return $props;
251
    }
252
}
253