Passed
Branch master (f496ba)
by stéphane
02:11
created

Node::onKey()   A

Complexity

Conditions 5
Paths 8

Size

Total Lines 19
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 14
nc 8
nop 1
dl 0
loc 19
rs 9.4888
c 0
b 0
f 0
1
<?php
2
namespace Dallgoot\Yaml;
0 ignored issues
show
Coding Style introduced by
Missing file doc comment
Loading history...
3
4
use Dallgoot\Yaml\{Types as T, Regex as R};
5
use \SplDoublyLinkedList as DLL;
6
7
class Node
0 ignored issues
show
Coding Style introduced by
Missing class doc comment
Loading history...
8
{
9
    /** @var int */
2 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
10
    public $indent = -1;
11
    /** @var int */
2 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
12
    public $line;
13
    /** @var int */
2 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
14
    public $type;
15
    /** @var null|string|boolean */
2 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
16
    public $identifier;
17
    /** @var Node|DLL|null|string */
2 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
18
    public $value;
19
20
    /** @var null|Node */
2 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
21
    private $parent;
0 ignored issues
show
Coding Style introduced by
Private member variable "parent" must be prefixed with an underscore
Loading history...
22
23
    public function __construct($nodeString = null, $line = null)
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
24
    {
25
        $this->line = $line;
26
        if (is_null($nodeString)) {
27
            $this->type = T::ROOT;
28
        } else {
29
            $this->parse($nodeString);
30
        }
31
    }
32
33
    public function setParent(Node $node):Node
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
34
    {
35
        $this->parent = $node;
36
        return $this;
37
    }
38
39
    public function getParent($indent = null):Node
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
40
    {
41
        if (is_null($indent)) return $this->parent ?? $this;
1 ignored issue
show
Coding Style introduced by
Inline control structures are discouraged
Loading history...
42
        $cursor = $this;
43
        while ($cursor->indent >= $indent) {
44
            $cursor = $cursor->parent;
45
        }
46
        return $cursor;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $cursor could return the type null which is incompatible with the type-hinted return Dallgoot\Yaml\Node. Consider adding an additional type-check to rule them out.
Loading history...
47
    }
48
49
    public function add(Node $child):void
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
50
    {
51
        $child->setParent($this);
52
        $current = $this->value;
53
        if (in_array($this->type, T::$LITTERALS)) $child->type = T::SCALAR;
1 ignored issue
show
Coding Style introduced by
Inline control structures are discouraged
Loading history...
54
        if (is_null($current)) {
55
            $this->value = $child;
56
            return;
57
        } elseif ($current instanceof Node) {
58
            $this->value = new DLL();
59
            $this->value->setIteratorMode(DLL::IT_MODE_KEEP);
60
            $this->value->push($current);
61
        }
62
        $this->value->push($child);
0 ignored issues
show
Bug introduced by
The method push() does not exist on Dallgoot\Yaml\Node. ( Ignorable by Annotation )

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

62
        $this->value->/** @scrutinizer ignore-call */ 
63
                      push($child);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method push() does not exist on null. ( Ignorable by Annotation )

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

62
        $this->value->/** @scrutinizer ignore-call */ 
63
                      push($child);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
63
        //modify type according to child
64
        if ($this->value instanceof DLL && !property_exists($this->value, "type")) {
65
            switch ($child->type) {
66
                case T::KEY:    $this->value->type = T::MAPPING;break;
1 ignored issue
show
Bug introduced by
The property type does not seem to exist on SplDoublyLinkedList.
Loading history...
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
Coding Style introduced by
Closing brace must be on a line by itself
Loading history...
67
                case T::ITEM:   $this->value->type = T::SEQUENCE;break;
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
Coding Style introduced by
Closing brace must be on a line by itself
Loading history...
68
                case T::SCALAR: $this->value->type = $this->type;break;
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
Coding Style introduced by
Closing brace must be on a line by itself
Loading history...
69
            }
70
        }
71
    }
72
73
    public function getDeepestNode():Node
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
74
    {
75
        $cursor = $this;
76
        while ($cursor->value instanceof Node) {
77
            $cursor = $cursor->value;
78
        }
79
        return $cursor;
80
    }
81
82
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $nodeString should have a doc-comment as per coding-style.
Loading history...
83
    *  CAUTION : the types assumed here are NOT FINAL : they CAN be adjusted according to parent
84
    */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
85
    public function parse(String $nodeString):Node
86
    {
87
        $nodeValue = preg_replace("/^\t+/m", " ", $nodeString);//permissive to tabs but replacement
88
        $this->indent = strspn($nodeValue, ' ');
89
        $nodeValue = ltrim($nodeValue);
90
        if ($nodeValue === '') {
91
            $this->type = T::EMPTY;
92
            // $this->indent = 0; // remove if no bugs
93
        } elseif (substr($nodeValue, 0, 3) === '...') {//TODO: can have something on same line ?
94
            $this->type = T::DOC_END;
95
        } elseif (preg_match(R::KEY, $nodeValue, $matches)) {
96
            $this->onKey($matches);
97
        } else {//NOTE: can be of another type according to parent
98
            list($this->type, $value) = $this->define($nodeValue);
99
            is_object($value) ? $this->add($value) : $this->value = $value;
100
        }
101
        return $this;
102
    }
103
104
    /**
105
     *  Set the type and value according to first character
106
     *
107
     * @param      string  $nodeValue  The node value
3 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter type; 2 found
Loading history...
Coding Style introduced by
Expected 1 spaces after parameter name; 2 found
Loading history...
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 6
Loading history...
108
     * @return     array   contains [node->type, node->value]
1 ignored issue
show
Coding Style introduced by
Tag cannot be grouped with parameter tags in a doc comment
Loading history...
Coding Style introduced by
Tag value indented incorrectly; expected 1 spaces but found 5
Loading history...
109
     */
110
    private function define($nodeValue):array
0 ignored issues
show
Coding Style introduced by
Private method name "Node::define" must be prefixed with an underscore
Loading history...
111
    {
112
        $v = substr($nodeValue, 1);
113
        if (in_array($nodeValue[0], ['"', "'"])) {
114
            $type = R::isProperlyQuoted($nodeValue) ? T::QUOTED : T::PARTIAL;
115
            return [$type, $nodeValue];
116
        }
117
        if (in_array($nodeValue[0], ['{', '[']))      return $this->onObject($nodeValue);
1 ignored issue
show
Coding Style introduced by
Inline control structures are discouraged
Loading history...
118
        if (in_array($nodeValue[0], ['!', '&', '*'])) return $this->onNodeAction($nodeValue);
1 ignored issue
show
Coding Style introduced by
Inline control structures are discouraged
Loading history...
119
        switch ($nodeValue[0]) {
120
            case '#': return [T::COMMENT, ltrim($v)];
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
Coding Style introduced by
Closing brace must be on a line by itself
Loading history...
121
            case "-": return $this->onHyphen($nodeValue);
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
Coding Style introduced by
Closing brace must be on a line by itself
Loading history...
122
            case '%': return [T::DIRECTIVE, ltrim($v)];
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
Coding Style introduced by
Closing brace must be on a line by itself
Loading history...
123
            case '?': return [T::SET_KEY,   empty($v) ? null : new Node(ltrim($v), $this->line)];
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
Coding Style introduced by
Closing brace must be on a line by itself
Loading history...
124
            case ':': return [T::SET_VALUE, empty($v) ? null : new Node(ltrim($v), $this->line)];
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
Coding Style introduced by
Closing brace must be on a line by itself
Loading history...
125
            case '>': return [T::LITTERAL_FOLDED, null];
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
Coding Style introduced by
Closing brace must be on a line by itself
Loading history...
126
            case '|': return [T::LITTERAL, null];
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
Coding Style introduced by
Closing brace must be on a line by itself
Loading history...
127
            default:
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
128
                return [T::SCALAR, $nodeValue];
129
        }
130
    }
131
132
    private function onKey($matches):void
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
Coding Style introduced by
Private method name "Node::onKey" must be prefixed with an underscore
Loading history...
133
    {
134
        $this->type = T::KEY;
135
        $this->identifier = trim($matches[1]);
136
        $keyValue = isset($matches[2]) ? trim($matches[2]) : null;
137
        if (!empty($keyValue)) {
138
            $n = new Node($keyValue, $this->line);
139
            $hasComment = strpos($keyValue, ' #');
140
            if (!is_bool($hasComment)) {
0 ignored issues
show
introduced by
The condition is_bool($hasComment) is always false.
Loading history...
141
                $tmpNode = new Node(trim(substr($keyValue, 0, $hasComment)), $this->line);
142
                if ($tmpNode->type !== T::PARTIAL) {
143
                    $comment = new Node(trim(substr($keyValue, $hasComment+1)), $this->line);
144
                    //TODO: modify "identifier" to specify if fullline comment or not
145
                    $this->add($comment);
146
                    $n = $tmpNode;
147
                }
148
            }
149
            $n->indent = $this->indent + strlen($this->identifier);
150
            $this->add($n);
151
        }
152
    }
153
154
    private function onObject($value):array
1 ignored issue
show
Coding Style introduced by
Missing function doc comment
Loading history...
Coding Style introduced by
Private method name "Node::onObject" must be prefixed with an underscore
Loading history...
155
    {
156
        json_decode($value, false, 512, JSON_PARTIAL_OUTPUT_ON_ERROR|JSON_UNESCAPED_SLASHES);
157
        if (json_last_error() === JSON_ERROR_NONE)  return [T::JSON, $value];
1 ignored issue
show
Coding Style introduced by
Inline control structures are discouraged
Loading history...
158
        if (preg_match(R::MAPPING, $value))         return [T::MAPPING_SHORT, $value];
1 ignored issue
show
Coding Style introduced by
Inline control structures are discouraged
Loading history...
159
        if (preg_match(R::SEQUENCE, $value))        return [T::SEQUENCE_SHORT, $value];
1 ignored issue
show
Coding Style introduced by
Inline control structures are discouraged
Loading history...
160
        return [T::PARTIAL, $value];
161
    }
162
163
    private function onHyphen($nodeValue):array
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
Coding Style introduced by
Private method name "Node::onHyphen" must be prefixed with an underscore
Loading history...
164
    {
165
        if (substr($nodeValue, 0, 3) === '---') {
166
            $rest = trim(substr($nodeValue, 3));
167
            if (empty($rest)) return [T::DOC_START, null];
1 ignored issue
show
Coding Style introduced by
Inline control structures are discouraged
Loading history...
168
            $n = new Node($rest, $this->line);
169
            $n->indent = $this->indent + 4;
170
            return [T::DOC_START, $n->setParent($this)];
171
        }
172
        if (preg_match(R::ITEM, $nodeValue, $matches)) {
173
            if (isset($matches[1]) && !empty(trim($matches[1]))) {
174
                $n = new Node(trim($matches[1]), $this->line);
175
                return [T::ITEM, $n->setParent($this)];
176
            }
177
            return [T::ITEM, null];
178
        }
179
        return [T::SCALAR, $nodeValue];
180
    }
181
182
    private function onNodeAction($nodeValue):array
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
Coding Style introduced by
Private method name "Node::onNodeAction" must be prefixed with an underscore
Loading history...
183
    {
184
        // TODO: handle tags like  <tag:clarkevans.com,2002:invoice>
185
        $v = substr($nodeValue, 1);
186
        $type = ['!' => T::TAG, '&' => T::REF_DEF, '*' => T::REF_CALL][$nodeValue[0]];
187
        $pos = strpos($v, ' ');
188
        $this->identifier = is_bool($pos) ? $v : strstr($v, ' ', true);
0 ignored issues
show
introduced by
The condition is_bool($pos) is always false.
Loading history...
189
        $n = is_bool($pos) ? null : (new Node(trim(substr($nodeValue, $pos+1)), $this->line))->setParent($this);
0 ignored issues
show
introduced by
The condition is_bool($pos) is always false.
Loading history...
190
        return [$type, $n];
191
    }
192
193
    public function getPhpValue()
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
194
    {
195
        $v = $this->value;
196
        if (is_null($v)) return null;
1 ignored issue
show
Coding Style introduced by
Inline control structures are discouraged
Loading history...
197
        switch ($this->type) {
198
            case T::JSON:   return json_decode($v, false, 512, JSON_PARTIAL_OUTPUT_ON_ERROR);
1 ignored issue
show
Bug introduced by
It seems like $v can also be of type SplDoublyLinkedList and Dallgoot\Yaml\Node; however, parameter $json of json_decode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

198
            case T::JSON:   return json_decode(/** @scrutinizer ignore-type */ $v, false, 512, JSON_PARTIAL_OUTPUT_ON_ERROR);
Loading history...
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
Coding Style introduced by
Closing brace must be on a line by itself
Loading history...
199
            case T::QUOTED: return substr($v, 1, -1);
1 ignored issue
show
Bug introduced by
It seems like $v can also be of type SplDoublyLinkedList and Dallgoot\Yaml\Node; however, parameter $string of substr() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

199
            case T::QUOTED: return substr(/** @scrutinizer ignore-type */ $v, 1, -1);
Loading history...
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
Coding Style introduced by
Closing brace must be on a line by itself
Loading history...
200
            case T::RAW:    return strval($v);
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
Coding Style introduced by
Closing brace must be on a line by itself
Loading history...
201
            case T::REF_CALL://fall through
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
202
            case T::SCALAR: return self::getScalar($v);
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
Coding Style introduced by
Closing brace must be on a line by itself
Loading history...
203
            case T::MAPPING_SHORT:  return self::getShortMapping(substr($v, 1, -1));
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
Coding Style introduced by
Closing brace must be on a line by itself
Loading history...
204
            //TODO : that's not robust enough, improve it
205
            case T::SEQUENCE_SHORT:
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
206
                $f = function ($e) { return self::getScalar(trim($e));};
0 ignored issues
show
Coding Style introduced by
Opening brace must be the last content on the line
Loading history...
Coding Style introduced by
Closing brace must be on a line by itself
Loading history...
207
                return array_map($f, explode(",", substr($v, 1, -1)));
208
            default:
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 8 spaces, found 12
Loading history...
209
                trigger_error("Error can not get PHP type for ".T::getName($this->type), E_USER_WARNING);
210
                return null;
211
        }
212
    }
213
214
    private static function getScalar($v)
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
Coding Style introduced by
Private method name "Node::getScalar" must be prefixed with an underscore
Loading history...
215
    {
216
        $types = ['yes'   => true,
217
                  'no'    => false,
218
                  'true'  => true,
219
                  'false' => false,
220
                  'null'  => null,
221
                  '.inf'  => INF,
222
                  '-.inf' => -INF,
223
                  '.nan'  => NAN
224
        ];
225
        if (in_array(strtolower($v), array_keys($types))) return $types[strtolower($v)];
1 ignored issue
show
Coding Style introduced by
Inline control structures are discouraged
Loading history...
226
        if (R::isDate($v))   return date_create($v);
1 ignored issue
show
Coding Style introduced by
Inline control structures are discouraged
Loading history...
227
        if (R::isNumber($v)) return self::getNumber($v);
1 ignored issue
show
Coding Style introduced by
Inline control structures are discouraged
Loading history...
228
        return strval($v);
229
    }
230
231
    private static function getNumber($v)
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
Coding Style introduced by
Private method name "Node::getNumber" must be prefixed with an underscore
Loading history...
232
    {
233
        if (preg_match("/^(0o\d+)$/i", $v))      return intval(base_convert($v, 8, 10));
1 ignored issue
show
Coding Style introduced by
Inline control structures are discouraged
Loading history...
234
        if (preg_match("/^(0x[\da-f]+)$/i", $v)) return intval(base_convert($v, 16, 10));
1 ignored issue
show
Coding Style introduced by
Inline control structures are discouraged
Loading history...
235
        // if preg_match("/^([\d.]+e[-+]\d{1,2})$/", $v)://fall through
236
        // if preg_match("/^([-+]?(?:\d+|\d*.\d+))$/", $v):
237
            return is_bool(strpos($v, '.')) ? intval($v) : floatval($v);
0 ignored issues
show
introduced by
The condition is_bool(strpos($v, '.')) is always false.
Loading history...
238
    }
239
240
    //TODO : that's not robust enough, improve it
241
    private static function getShortMapping($mappingString):object
0 ignored issues
show
Coding Style introduced by
You must use "/**" style comments for a function comment
Loading history...
Coding Style introduced by
Private method name "Node::getShortMapping" must be prefixed with an underscore
Loading history...
242
    {
243
        $out = new \StdClass();
244
        foreach (explode(',', $mappingString) as $value) {
245
            list($keyName, $keyValue) = explode(':', $value);
246
            $out->{trim($keyName)} = self::getScalar(trim($keyValue));
247
        }
248
        return $out;
249
    }
250
251
    public function __debugInfo():array
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
252
    {
253
        $out = ['line'  => $this->line,
254
                'indent'=> $this->indent,
255
                'type'  => T::getName($this->type),
256
                'value' => $this->value];
257
        if (!is_null($this->identifier)) $out['type'] .= "($this->identifier)";
1 ignored issue
show
Coding Style introduced by
Inline control structures are discouraged
Loading history...
258
        return $out;
259
    }
260
}
261