Completed
Push — master ( 809c98...158972 )
by stéphane
02:21
created

Builder::buildSetKey()   A

Complexity

Conditions 5
Paths 16

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 5
nc 16
nop 2
dl 0
loc 7
rs 9.6111
c 0
b 0
f 0
1
<?php
2
3
namespace Dallgoot\Yaml;
4
5
use Dallgoot\Yaml\{Yaml as Y, Regex as R, TypesBuilder as TB};
6
7
/**
8
 * Constructs the result (YamlObject or array) according to every Node and respecting value
9
 *
10
 * @author  Stéphane Rebai <[email protected]>
11
 * @license Apache 2.0
12
 * @link    TODO : url to specific online doc
13
 */
14
final class Builder
15
{
16
    public static $_root;
17
    private static $_debug;
18
19
    const ERROR_NO_KEYNAME = self::class.": key has NO IDENTIFIER on line %d";
20
    const INVALID_DOCUMENT = self::class.": DOCUMENT %d can NOT be a mapping AND a sequence";
21
22
    /**
23
     * Builds a file.  check multiple documents & split if more than one documents
24
     *
25
     * @param   Node   $_root      The root node : Node with Node->type === YAML::ROOT
26
     * @param   int   $_debug      the level of debugging requested
27
     *
28
     * @return array|YamlObject      list of documents or just one.
29
     * @todo  implement splitting on YAML::DOC_END also
30
     */
31
    public static function buildContent(Node $_root, int $_debug)
32
    {
33
        self::$_debug = $_debug;
34
        $totalDocStart = 0;
35
        $documents = [];
36
        $buffer = new NodeList();
37
        $_root->value->setIteratorMode(NodeList::IT_MODE_DELETE);
0 ignored issues
show
Bug introduced by
The method setIteratorMode() 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

37
        $_root->value->/** @scrutinizer ignore-call */ 
38
                       setIteratorMode(NodeList::IT_MODE_DELETE);

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 setIteratorMode() 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

37
        $_root->value->/** @scrutinizer ignore-call */ 
38
                       setIteratorMode(NodeList::IT_MODE_DELETE);

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...
38
        foreach ($_root->value as $child) {
39
            if ($child->type & Y::DOC_START) {
40
                if(++$totalDocStart > 1){
41
                    $documents[] = self::buildDocument($buffer, count($documents));
42
                    $buffer = new NodeList($child);
43
                }
44
            } else {
45
                $buffer->push($child);
46
            }
47
        }
48
        $documents[] = self::buildDocument($buffer, count($documents));
49
        return count($documents) === 1 ? $documents[0] : $documents;
50
    }
51
52
    private static function buildDocument(NodeList $list, int $docNum):YamlObject
53
    {
54
        self::$_root = new YamlObject;
55
        try {
56
            $out = self::buildNodeList($list, self::$_root);
57
        } catch (\Exception $e) {
58
            throw new \ParseError(sprintf(self::INVALID_DOCUMENT, $docNum));
59
        }
60
        return $out;
61
    }
62
63
    /**
64
     * Generic function to distinguish between Node and NodeList
65
     *
66
     * @param Node|NodeList $node   The node.
67
     * @param mixed         $parent The parent
68
     *
69
     * @return mixed  ( description_of_the_return_value )
70
     */
71
    public static function build(object $node, &$parent = null)
72
    {
73
        if ($node instanceof NodeList) return self::buildNodeList($node, $parent);
74
        return self::buildNode($node, $parent);
75
    }
76
77
    /**
78
     * Builds a node list.
79
     *
80
     * @param NodeList $node   The node
81
     * @param mixed    $parent The parent
82
     *
83
     * @return mixed    The parent (object|array) or a string representing the NodeList.
84
     */
85
    public static function buildNodeList(NodeList $node, &$parent=null)
86
    {
87
        $node->forceType();
88
        if ($node->type & (Y::RAW | Y::LITTERALS)) {
89
            return self::buildLitteral($node, (int) $node->type);
90
        }
91
        $action = function ($child, &$parent, &$out) {
92
            self::build($child, $out);
93
        };
94
        if ($node->type & (Y::COMPACT_MAPPING|Y::MAPPING|Y::SET)) {
95
            $out = $parent ?? new \StdClass;
96
        } elseif ($node->type & (Y::COMPACT_SEQUENCE|Y::SEQUENCE)) {
97
            $out = $parent ?? [];
98
        } else {
99
            $out = '';
100
            $action = function ($child, &$parent, &$out) {
101
                if ($child->type & (Y::SCALAR|Y::QUOTED)) {
102
                    if ($parent) {
103
                        $parent->setText(self::build($child));
104
                    } else {
105
                        $out .= self::build($child);
106
                    }
107
                }
108
            };
109
        }
110
        foreach ($node as $child) {
111
            $action($child, $parent, $out);
112
        }
113
        if ($node->type & (Y::COMPACT_SEQUENCE|Y::COMPACT_MAPPING)) {
114
            $out = new Compact($out);
0 ignored issues
show
Bug introduced by
It seems like $out can also be of type string; however, parameter $candidate of Dallgoot\Yaml\Compact::__construct() does only seem to accept array|object, 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

114
            $out = new Compact(/** @scrutinizer ignore-type */ $out);
Loading history...
115
        }
116
        return is_null($out) ? $parent : $out;
117
    }
118
119
    /**
120
     * Builds a node.
121
     *
122
     * @param Node    $node    The node of any Node->type
123
     * @param mixed  $parent  The parent
124
     *
125
     * @return mixed  The node value as Scalar, Array, Object or Null otherwise.
126
     */
127
    private static function buildNode(Node $node, &$parent)
128
    {
129
        extract((array) $node, EXTR_REFS);
130
        $actions = [Y::DIRECTIVE => 'buildDirective',
131
                    Y::ITEM      => 'buildItem',
132
                    Y::KEY       => 'buildKey',
133
                    Y::SET_KEY   => 'buildSetKey',
134
                    Y::SET_VALUE => 'buildSetValue',
135
                    Y::TAG       => 'buildTag',
136
        ];
137
        if (isset($actions[$type])) {
138
            return TB::{$actions[$type]}($node, $parent);
139
        } elseif ($type & Y::COMMENT) {
140
            self::$_root->addComment($line, $value);
141
        } elseif ($type & (Y::COMPACT_MAPPING|Y::COMPACT_SEQUENCE)) {
142
            return self::buildNodeList($value, $parent);
143
        } elseif ($type & (Y::REF_DEF | Y::REF_CALL)) {
144
            return TB::buildReference($node, $parent);
145
        } elseif ($value instanceof Node) {
146
            return self::buildNode($value, $parent);
147
        } else {
148
            return Node2PHP::get($node);
149
        }
150
    }
151
152
153
    /**
154
     * Builds a litteral (folded or not) or any NodeList that has YAML::RAW type (like a multiline value)
155
     *
156
     * @param      NodeList  $children  The children
157
     * @param      integer   $type      The type
158
     *
159
     * @return     string    The litteral.
160
     * @todo : Example 6.1. Indentation Spaces  spaces must be considered as content
161
     */
162
    private static function buildLitteral(NodeList $list, int $type = Y::RAW):string
163
    {
164
        $list->rewind();
165
        $refIndent = $list->current()->indent;
166
        //remove trailing blank
167
        while ($list->top()->type & Y::BLANK) $list->pop();
168
        $result = '';
169
        $separator = [ Y::RAW => '', Y::LITT => "\n", Y::LITT_FOLDED => ' '][$type];
170
        foreach ($list as $child) {
171
            if ($child->value instanceof NodeList) {
172
                $result .= self::buildLitteral($child->value, $type).$separator;
173
            } else {
174
                self::setLiteralValue($child, $result, $refIndent, $separator, $type);
175
            }
176
        }
177
        return rtrim($result);
178
    }
179
180
    private static function setLiteralValue(Node $child, string &$result, int $refIndent, string $separator, int $type)
181
    {
182
        $val = $child->type & (Y::SCALAR) ? $child->value : substr($child->raw, $refIndent);
183
        if ($type & Y::LITT_FOLDED && ($child->indent > $refIndent || ($child->type & Y::BLANK))) {
184
            if ($result[-1] === $separator)
185
                $result[-1] = "\n";
186
            if ($result[-1] === "\n")
187
                $result .= $val;
188
            return;
189
        }
190
        $result .= $val.$separator;
191
    }
192
193
}
194