Passed
Push — master ( 012476...a245eb )
by stéphane
03:02
created

NodeFactory   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 150
Duplicated Lines 0 %

Test Coverage

Coverage 98.48%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 15
eloc 58
dl 0
loc 150
ccs 65
cts 66
cp 0.9848
rs 10
c 1
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A get() 0 12 2
A getKeyValue() 0 9 2
A onCharacter() 0 15 1
A onHyphen() 0 6 1
A onCompact() 0 16 2
A onQuoted() 0 4 2
A onNodeAction() 0 6 2
A onDirective() 0 9 3
1
<?php
2
3
namespace Dallgoot\Yaml;
4
5
use Dallgoot\Yaml\Nodes as Nodes;
6
use Dallgoot\Yaml\Nodes\Blank;
7
use Dallgoot\Yaml\Regex;
8
use Dallgoot\Yaml\Nodes\Generic\NodeGeneric;
9
10
/**
11
 * Analyzes $nodeString
12
 * determines the appropriate NodeType
13
 * constructs it
14
 * and returns it
15
 *
16
 * @author  Stéphane Rebai <[email protected]>
17
 * @license Apache 2.0
18
 * @link    https://github.com/dallgoot/yaml
19
 */
20
class NodeFactory
21
{
22
    private const JSON_OPTIONS = \JSON_PARTIAL_OUTPUT_ON_ERROR | \JSON_UNESCAPED_SLASHES;
23
24 8
    final public static function get(string $nodeString, int $line = 0, bool $debug = false): NodeGeneric
25
    {
26 8
        $trimmed = ltrim($nodeString);
27 8
        $match = (bool) preg_match(Regex::KEY, $trimmed, $matches);
28 8
        $node = match(true) {
29 8
            $trimmed === '' => new Blank($nodeString, $line),
30 8
            str_starts_with($trimmed, '...') => new Nodes\DocEnd($nodeString, $line),
31 8
            $match => new Nodes\Key($nodeString, $line, $matches),
32 8
            default => self::onCharacter($trimmed[0], $nodeString, $line)
33 8
        };
34 7
        if ($debug) echo $line . ":" . get_class($node) . "\n";
35 7
        return $node;
36
    }
37
38
    /**
39
     * This is a slimmer version og NodeFactory::get
40
     * in the case of a Key value we can ignore certain cases and remove ambiguity and errors
41
     * 
42
     * @param string $nodeString
43
     * @param int $line
44
     * @param bool $debug
45
     * @return \Dallgoot\Yaml\Nodes\Generic\NodeGeneric
46
     */
47 2
    public static function getKeyValue(string $nodeString, int $line = 0, bool $debug = false): NodeGeneric
48
    {
49 2
        $trimmed = ltrim($nodeString);
50 2
        $node = match(true) {
51 2
            $trimmed === '' => new Blank($nodeString, $line),
52 2
            default => self::onCharacter($trimmed[0], $nodeString, $line)
53 2
        };
54 2
        if ($debug) echo $line . ":" . get_class($node) . "\n";
55 2
        return $node;
56
    }
57
58 9
    private static function onCharacter(string $first, string $nodeString, int $line): NodeGeneric
59
    {
60 9
        return match ($first) {
61 9
            '-' => self::onHyphen($nodeString, $line),
62 9
            '>' => new Nodes\LiteralFolded($nodeString, $line),
63 9
            '|' => new Nodes\Literal($nodeString, $line),
64 9
            '"', "'" => self::onQuoted($first, $nodeString, $line),
65 9
            '#' => new Nodes\Comment(ltrim($nodeString), $line),
66 9
            '%' => self::onDirective($nodeString, $line),
67 9
            '{', '[' => self::onCompact($nodeString, $line),
68 9
            ':' => new Nodes\SetValue($nodeString, $line),
69 9
            '?' => new Nodes\SetKey($nodeString, $line),
70 9
            '*', '&' => self::onNodeAction($nodeString, $line),
71 9
            '!' => new Nodes\Tag($nodeString, $line),
72 9
            default => new Nodes\Scalar($nodeString, $line),
73 9
        };
74
    }
75
76
77
    /**
78
     * Return the correct Node Object between NodeComment OR NodeDirective
79
     *
80
     * @param      string   $nodeString  The node string
81
     * @param      integer  $line         The line
82
     *
83
     * @return     NodeGeneric
84
     */
85 2
    private static function onDirective(string $nodeString, int $line): NodeGeneric
86
    {
87
            if (
88 2
                (bool) preg_match(Regex::DIRECTIVE_TAG, $nodeString)
89 2
                || (bool) preg_match(Regex::DIRECTIVE_VERSION, $nodeString)
90
            ) {
91
                return new Nodes\Directive(ltrim($nodeString), $line);
92
            } else {
93 2
                throw new \ParseError("Invalid/Unknown Directive", 1);
94
            }
95
    }
96
97
    /**
98
     * Set $node type and value when $nodevalue starts with a quote (simple or double)
99
     *
100
     * @param string $nodeString The node value
101
     * @param int    $line       The line
102
     *
103
     * @return     NodeGeneric
104
     */
105 1
    private static function onQuoted(string $first, string $nodeString, int $line): NodeGeneric
106
    {
107 1
        return Regex::isProperlyQuoted(trim($nodeString)) ? new Nodes\Quoted($nodeString, $line)
108 1
            : new Nodes\Partial($nodeString, $line);
109
    }
110
111
    /**
112
     * Determines the Node type and value when a compact object/array syntax is found
113
     *
114
     * @param string $nodeString The value assumed to start with { or [ or characters
115
     * @param int    $line       The line
116
     *
117
     * @return     NodeGeneric
118
     */
119 7
    private static function onCompact(string $nodeString, int $line): NodeGeneric
120
    {
121 7
        json_decode($nodeString, false, 512, self::JSON_OPTIONS);
122 7
        if (json_last_error() === \JSON_ERROR_NONE) {
123 3
            return new Nodes\JSON($nodeString, $line);
124
        } else {
125 4
            $backtrack_setting = "pcre.backtrack_limit";
126 4
            ini_set($backtrack_setting, "-1");
127 4
            $isMapping  = (bool) preg_match(Regex::MAPPING, trim($nodeString));
128 4
            $isSequence = (bool) preg_match(Regex::SEQUENCE, trim($nodeString));
129 4
            ini_restore($backtrack_setting);
130
131 4
            return match(true) {
132 4
                $isMapping => new Nodes\CompactMapping($nodeString, $line),
133 4
                $isSequence => new Nodes\CompactSequence($nodeString, $line),
134 4
                default => new Nodes\Partial($nodeString, $line),
135 4
            };
136
        }
137
    }
138
139
    /**
140
     * Determines Node type and value when an hyphen "-" is found
141
     *
142
     * @param string $nodeString The node string value
143
     * @param int    $line       The line
144
     *
145
     * @return     NodeGeneric
146
     */
147 1
    private static function onHyphen(string $nodeString, int $line): NodeGeneric
148
    {
149 1
        return match(true) {
150 1
            str_starts_with($nodeString, '---') => new Nodes\DocStart($nodeString, $line),
151 1
            (bool) preg_match(Regex::ITEM, ltrim($nodeString)) => new Nodes\Item($nodeString, $line),
152 1
            default => new Nodes\Scalar($nodeString, $line),
153 1
        };
154
    }
155
156
    /**
157
     * Sets Node type and value according to $nodeString when one of these characters is found : !,&,*
158
     *
159
     * @param string $nodeString The node value
160
     * @param int    $line       The line
161
     *
162
     *@todo replace $action[0] with $first if applicable
163
     */
164 1
    private static function onNodeAction(string $nodeString, int $line): NodeGeneric
165
    {
166 1
        if (!((bool) preg_match(Regex::NODE_ACTIONS, trim($nodeString), $matches))) {
167 1
            return new Nodes\Scalar($nodeString, $line);
168
        }
169 1
        return new Nodes\Anchor($nodeString, $line);
170
    }
171
172
}
173