Passed
Push — master ( affae1...bf1da0 )
by stéphane
10:14
created

Builder::build()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 2
nc 2
nop 2
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Dallgoot\Yaml;
4
5
use Dallgoot\Yaml\{NodeList, Yaml as Y, Regex as R, TypesBuilder as TB};
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Dallgoot\Yaml\NodeList. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
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 INVALID_DOCUMENT = "DOCUMENT %d is invalid,";
20
21
    /**
22
     * Builds a file.  check multiple documents & split if more than one documents
23
     *
24
     * @param   Node   $_root      The root node : Node with Node->type === YAML::ROOT
25
     * @param   int   $_debug      the level of debugging requested
26
     *
27
     * @return array|YamlObject      list of documents or just one.
28
     * @todo  implement splitting on YAML::DOC_END also
29
     */
30
    public static function buildContent(Node $_root, int $_debug)
31
    {
32
        self::$_debug = $_debug;
33
        $docStarted = 0;
0 ignored issues
show
Unused Code introduced by
The assignment to $docStarted is dead and can be removed.
Loading history...
34
        $documents = [];
35
        $buffer = new NodeList();
36
        if ($_root->value instanceof NodeList) {
37
            $_root->value->setIteratorMode(NodeList::IT_MODE_DELETE);
38
        } else {
39
            die("NOT A LIST");
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
40
        }
41
        foreach ($_root->value as $child) {
42
            if ($child->type & Y::DOC_END && $child !== $_root->value->top()) {
43
                $buffer->push($child);
44
                $documents[] = self::buildDocument($buffer, count($documents));
45
                $buffer = new NodeList();
46
            } elseif ($child->type & Y::DOC_START) {
47
                if ($buffer->count() === 0) {
48
                    $buffer->push($child);
49
                } else {
50
                    if (in_array($buffer->getTypes(), [Y::COMMENT, Y::DIRECTIVE])) {
51
                        $buffer->push($child);
52
                    } else {
53
                        $documents[] = self::buildDocument($buffer, count($documents));
54
                        $buffer = new NodeList($child);
55
                    }
56
                }
57
            } else {
58
                $buffer->push($child);
59
            }
60
        }
61
        $documents[] = self::buildDocument($buffer, count($documents));
62
        return count($documents) === 1 ? $documents[0] : $documents;
63
    }
64
65
    private static function buildDocument(NodeList $list, int $docNum):YamlObject
66
    {var_dump(__METHOD__);
0 ignored issues
show
Security Debugging Code introduced by
var_dump(__METHOD__) looks like debug code. Are you sure you do not want to remove it?
Loading history...
67
        self::$_root = new YamlObject;
68
        try {
69
            $out = self::buildNodeList($list, self::$_root);
70
            if (is_string($out)) {
71
                $out = self::$_root->setText($out);
72
            }
73
        } catch (\Exception $e) {
74
            throw new \ParseError(sprintf(self::INVALID_DOCUMENT, $docNum)." ".$e->getMessage()."\n ".$e->getFile().':'.$e->getLine());
75
        }
76
        return $out;
77
    }
78
79
    /**
80
     * Generic function to distinguish between Node and NodeList
81
     *
82
     * @param Node|NodeList $node   The node.
83
     * @param mixed         $parent The parent
84
     *
85
     * @return mixed  ( description_of_the_return_value )
86
     */
87
    public static function build(object $node, &$parent = null)
88
    {
89
        if ($node instanceof NodeList) return self::buildNodeList($node, $parent);
90
        return self::buildNode($node, $parent);
91
    }
92
93
    /**
94
     * Builds a node list.
95
     *
96
     * @param NodeList $node   The node
97
     * @param mixed    $parent The parent
98
     *
99
     * @return mixed    The parent (object|array) or a string representing the NodeList.
100
     */
101
    public static function buildNodeList(NodeList $list, &$parent=null)
102
    {
103
        $list->forceType();
104
        $action = function ($child, &$parent, &$out) {
105
            self::build($child, $out);
106
        };
107
        $list->type = $list->type ?? Y::RAW;
108
        if ($list->type & (Y::RAW|Y::LITTERALS)) {
109
            $tmp = self::buildLitteral($list, $list->type);//var_dump("ICI1",$list, $tmp);
110
            return $tmp;
111
        } elseif ($list->type & (Y::COMPACT_MAPPING|Y::MAPPING|Y::SET)) {//var_dump("ICI2");
112
            $out = $parent ?? new \StdClass;
113
        } elseif ($list->type & (Y::COMPACT_SEQUENCE|Y::SEQUENCE)) {//var_dump("ICI3");
114
            $out = $parent ?? [];
115
        } else {
116
            $out = null;//var_dump("ICI");
117
            $action = function ($child, &$parent, &$out) {
118
                if ($child->type & (Y::SCALAR|Y::QUOTED|Y::DOC_START)) {
119
                    $out .= Node2PHP::get($child);
120
                } else {
121
                    self::build($child);//var_dump("HERE");
122
                }
123
                // var_dump("out:".$out);
124
            };
125
        }
126
        foreach ($list as $child) {
127
            $action($child, $parent, $out);
128
        }
129
        if ($list->type & (Y::COMPACT_SEQUENCE|Y::COMPACT_MAPPING) && !empty($out)) {
130
            $out = new Compact($out);
131
        }
132
        return is_null($out) ? $parent : $out;
133
    }
134
135
    /**
136
     * Builds a node.
137
     *
138
     * @param Node    $node    The node of any Node->type
139
     * @param mixed  $parent  The parent
140
     *
141
     * @return mixed  The node value as Scalar, Array, Object or Null otherwise.
142
     */
143
    private static function buildNode(Node $node, &$parent)
144
    {
145
        extract((array) $node, EXTR_REFS);
146
        $actions = [Y::DIRECTIVE => 'buildDirective',
147
                    Y::ITEM      => 'buildItem',
148
                    Y::KEY       => 'buildKey',
149
                    Y::SET_KEY   => 'buildSetKey',
150
                    Y::SET_VALUE => 'buildSetValue',
151
                    Y::TAG       => 'buildTag',
152
        ];
153
        if (isset($actions[$type])) {
154
            return TB::{$actions[$type]}($node, $parent);
155
        } elseif ($type & Y::COMMENT) {
156
            self::$_root->addComment($line, $value);
157
        // } elseif ($type & Y::DOC_START) {
158
        //     if (!is_null($value)) {
159
        //         return self::build($value->value, $self::$_root);
160
        //     }
161
        } elseif ($type & (Y::COMPACT_MAPPING|Y::COMPACT_SEQUENCE)) {
162
            return self::buildNodeList($value, $parent);
163
        } elseif ($type & (Y::REF_DEF | Y::REF_CALL)) {
164
            return TB::buildReference($node, $parent);
165
        } elseif ($value instanceof Node) {
166
            return self::buildNode($value, $parent);
167
        } else {
168
            return Node2PHP::get($node);
169
        }
170
    }
171
172
173
    /**
174
     * Builds a litteral (folded or not) or any NodeList that has YAML::RAW type (like a multiline value)
175
     *
176
     * @param      NodeList  $children  The children
177
     * @param      integer   $type      The type
178
     *
179
     * @return     string    The litteral.
180
     * @todo : Example 6.1. Indentation Spaces  spaces must be considered as content
181
     */
182
    private static function buildLitteral(NodeList &$list, int $type = Y::RAW):string
183
    {//var_dump(__METHOD__,$list);
184
        $result = '';
185
        $list->rewind();
186
        $refIndent = $list->count() > 0 ? $list->current()->indent : 0;
187
        if($list->count()) {
188
            //remove trailing blank
189
            while ($list->top()->type & Y::BLANK) $list->pop();
190
            $separator = [ Y::RAW => '', Y::LITT => "\n", Y::LITT_FOLDED => ' '][$type];
191
            foreach ($list as $child) {
192
                if ($child->value instanceof NodeList) {
193
                    $result .= self::buildLitteral($child->value, $type).$separator;
194
                } else {
195
                    self::setLiteralValue($child, $result, $refIndent, $separator, $type);
196
                }
197
            }
198
        }
199
        return rtrim($result);
200
    }
201
202
    private static function setLiteralValue(Node $node, string &$result, int $refIndent, string $separator, int $type)
203
    {
204
        if ($node->type & Y::SCALAR) {
205
            $val = $node->value;
206
        } else {
207
            if ($node->value instanceof Node && !($node->value->type & Y::SCALAR) && preg_match('/(([^:]+:)|( *-)) *$/', $node->raw)) {
208
                $childValue = '';
209
                self::setLiteralValue($node->value, $childValue, $refIndent, $separator, $type);
210
                // $val = substr($node->raw.$separator.$childValue, $refIndent);
211
                // $val = substr($node->raw, $refIndent).$separator.self::buildLitteral(new NodeList($node->value), $type);
212
                $val = substr($node->raw, $refIndent).$separator.$childValue;
213
            } else {
214
                $val = substr($node->raw, $refIndent);
215
            }
216
        }
217
        if ($type & Y::LITT_FOLDED && ($node->indent > $refIndent || ($node->type & Y::BLANK))) {
218
            if ($result[-1] === $separator)
219
                $result[-1] = "\n";
220
            if ($result[-1] === "\n")
221
                $result .= $val;
222
            return;
223
        }
224
        $result .= $val.$separator;
225
    }
226
227
}
228