Passed
Push — master ( 42571c...f8295f )
by stéphane
09:20
created

NodeFactory::onCompact()   B

Complexity

Conditions 7
Paths 5

Size

Total Lines 23
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 7.0119

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 16
c 1
b 0
f 0
nc 5
nop 3
dl 0
loc 23
ccs 15
cts 16
cp 0.9375
crap 7.0119
rs 8.8333
1
<?php
2
3
namespace Dallgoot\Yaml;
4
5
use Dallgoot\Yaml\Nodes;
6
use Dallgoot\Yaml\Regex;
7
use Dallgoot\Yaml\Nodes\NodeGeneric;
8
9
/**
10
 * Analyzes $nodeString
11
 * determines the appropriate NodeType
12
 * constructs it
13
 * and returns it
14
 *
15
 * @author  Stéphane Rebai <[email protected]>
16
 * @license Apache 2.0
17
 * @link    https://github.com/dallgoot/yaml
18
 */
19
final class NodeFactory
20
{
21
    private const JSON_OPTIONS = \JSON_PARTIAL_OUTPUT_ON_ERROR|\JSON_UNESCAPED_SLASHES;
22
23 7
    final public static function get($nodeString = null, $line = 0):NodeGeneric
24
    {
25 7
        $trimmed = ltrim($nodeString);
26 7
        if ($trimmed === '')                                return new Nodes\Blank($nodeString, $line);
27 7
        elseif (substr($trimmed, 0, 3) === '...')           return new Nodes\DocEnd($nodeString, $line);
28 7
        elseif ((bool) preg_match(Regex::KEY, $trimmed, $matches)) return new Nodes\Key($nodeString, $line, $matches);
29
        else {
30 7
            $first = $trimmed[0];
31 7
            $stringGroups = ['-',         '>|' ,   '"\'',    "#%" ,    "{[" ,       ":?" ,       '*&!'];
32 7
            $methodGroups = ['Hyphen','Literal','Quoted','Special','Compact','SetElement','NodeAction'];
33 7
            foreach ($stringGroups as $groupIndex => $stringRef) {
34 7
                if (is_int(strpos($stringRef, $first))) {
35 2
                    $methodName = 'on'.$methodGroups[$groupIndex];
36
                    try {
37 2
                        return self::$methodName($first, $nodeString, $line);
38 1
                    } catch (\Throwable $e) {
39 1
                        throw new \Exception(" could not create a Node ($methodName) for '$nodeString'", 1, $e);
40
                    }
41
                }
42
            }
43
        }
44 6
        return new Nodes\Scalar($nodeString, $line);
45
    }
46
47
    /**
48
     * Return the correct Node Object between NodeComment OR NodeDirective
49
     *
50
     * @param      string   $nodeString  The node string
51
     * @param      integer  $line         The line
52
     *
53
     * @return     NodeGeneric
54
     */
55 3
    final private static function onSpecial(string $first, string $nodeString, int $line):NodeGeneric
56
    {
57 3
        if ($first === "#") {
58 1
            return new Nodes\Comment(ltrim($nodeString), $line);
59
        } else {
60 3
            if (preg_match(Regex::DIRECTIVE_TAG, $nodeString)
61 3
                || preg_match(Regex::DIRECTIVE_VERSION, $nodeString)) {
62 1
                return new Nodes\Directive(ltrim($nodeString), $line);
63
            } else {
64 2
                throw new \ParseError("Invalid/Unknown Directive", 1);
65
            }
66
        }
67
    }
68
69
    /**
70
     * Set $node type and value when $nodevalue starts with a quote (simple or double)
71
     *
72
     * @param string $nodeString The node value
73
     * @param int    $line       The line
74
     *
75
     * @return     NodeGeneric
76
     */
77 1
    final private static function onQuoted(string $first, string $nodeString, int $line):NodeGeneric
78
    {
79 1
        return Regex::isProperlyQuoted(trim($nodeString)) ? new Nodes\Quoted($nodeString, $line)
80 1
                                                          : new Nodes\Partial($nodeString, $line);
81
    }
82
83
    /**
84
     * Set $node type and value when NodeValue starts with a Set characters "?:"
85
     *
86
     * @param string $nodeString The node value
87
     * @param int    $line       The line
88
     *
89
     * @return     NodeGeneric
90
     */
91 1
    final private static function onSetElement(string $first, string $nodeString, int $line):NodeGeneric
92
    {
93 1
        return $first === '?' ? new Nodes\SetKey($nodeString, $line)
94 1
                              : new Nodes\SetValue($nodeString, $line);
95
    }
96
97
    /**
98
     * Determines the Node type and value when a compact object/array syntax is found
99
     *
100
     * @param string $nodeString The value assumed to start with { or [ or characters
101
     * @param int    $line       The line
102
     *
103
     * @return     NodeGeneric
104
     */
105 7
    final private static function onCompact(string $first, string $nodeString, int $line):NodeGeneric
106
    {
107 7
        json_decode($nodeString, false, 512, self::JSON_OPTIONS);
108 7
        if (json_last_error() === \JSON_ERROR_NONE) {
109 3
            return new Nodes\JSON($nodeString, $line);
110
        } else {
111 4
            $backtrack_setting = "pcre.backtrack_limit";
112 4
            ini_set($backtrack_setting, "-1");
113 4
            $isMapping  = preg_match(Regex::MAPPING, trim($nodeString));
114 4
            $isSequence = preg_match(Regex::SEQUENCE, trim($nodeString));
115 4
            ini_restore($backtrack_setting);
116
117 4
            if (is_bool($isMapping) || is_bool($isSequence)) {
0 ignored issues
show
introduced by
The condition is_bool($isSequence) is always false.
Loading history...
118
                throw new \Exception("Regex Error for ".(is_bool($isMapping) ? 'mapping:' :'sequence:').preg_last_error());
119
            }
120 4
            if ($isMapping) {
121
                // var_dump(Regex::MAPPING, trim($nodeString));
122 1
                return new Nodes\CompactMapping($nodeString, $line);
123 3
            } elseif ($isSequence) {
124 1
                return new Nodes\CompactSequence($nodeString, $line);
125
            }
126
        }
127 2
        return new Nodes\Partial($nodeString, $line);
128
    }
129
130
    /**
131
     * Determines Node type and value when an hyphen "-" is found
132
     *
133
     * @param string $nodeString The node string value
134
     * @param int    $line       The line
135
     *
136
     * @return     NodeGeneric
137
     */
138 1
    final private static function onHyphen(string $first, string $nodeString, int $line):NodeGeneric
139
    {
140 1
        if (substr($nodeString, 0, 3) === '---')              return new Nodes\DocStart($nodeString, $line);
141 1
        elseif ((bool) preg_match(Regex::ITEM, ltrim($nodeString)))  return new Nodes\Item($nodeString, $line);
142
        else {
143 1
            return new Nodes\Scalar($nodeString, $line);
144
        }
145
    }
146
147
    /**
148
     * Sets Node type and value according to $nodeString when one of these characters is found : !,&,*
149
     *
150
     * @param string $nodeString The node value
151
     * @param int    $line       The line
152
     *
153
     *@todo replace $action[0] with $first if applicable
154
     */
155 1
    final private static function onNodeAction(string $first, string $nodeString, int $line):NodeGeneric
156
    {
157 1
        if (!((bool) preg_match(Regex::NODE_ACTIONS, trim($nodeString), $matches))) {
158
            // var_dump("ACTION is scalar: '$nodeString'");
159 1
            return new Nodes\Scalar($nodeString, $line);
160
        }
161 1
        if ($first === "!") {
162 1
            return new Nodes\Tag($nodeString, $line);
163
        } else {
164 1
            return new Nodes\Anchor($nodeString, $line);
165
        }
166
    }
167
168 2
    final private static function onLiteral(string $first, string $nodeString, int $line):NodeGeneric
169
    {
170 2
        switch ($first) {
171 2
            case '>': return new Nodes\LiteralFolded($nodeString, $line);
172 2
            case '|': return new Nodes\Literal($nodeString, $line);
173
            default:
174 1
                throw new \ParseError("Not a literal node !! '$first' on line:$line");
175
        }
176
    }
177
178
}