Passed
Push — master ( 9463bf...25e428 )
by Chris
03:12
created

Document::importScalarNode()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 0
cts 4
cp 0
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 6
nc 3
nop 1
crap 12
1
<?php declare(strict_types=1);
2
3
namespace DaveRandom\Jom;
4
5
use DaveRandom\Jom\Exceptions\InvalidNodeValueException;
6
use DaveRandom\Jom\Exceptions\InvalidSubjectNodeException;
7
use DaveRandom\Jom\Exceptions\ParseFailureException;
8
use DaveRandom\Jom\Exceptions\WriteOperationForbiddenException;
9
use ExceptionalJSON\DecodeErrorException;
10
11
final class Document implements \JsonSerializable
12
{
13
    public const IGNORE_INVALID_VALUES = Node::IGNORE_INVALID_VALUES;
14
15
    /** @var Node */
16
    private $rootNode;
17
18
    /**
19
     * @throws InvalidNodeValueException
20
     * @throws InvalidSubjectNodeException
21
     * @throws WriteOperationForbiddenException
22
     */
23
    private function importVectorNode(VectorNode $node): VectorNode
24
    {
25
        if (!\in_array(\get_class($node), [ArrayNode::class, ObjectNode::class])) {
26
            throw new InvalidSubjectNodeException('Source node is of unknown type: ' . \get_class($node));
27
        }
28
29
        $newNode = new $node(null, $this);
1 ignored issue
show
Unused Code introduced by
The call to DaveRandom\Jom\VectorNode::__construct() has too many arguments starting with $this. ( Ignorable by Annotation )

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

29
        $newNode = /** @scrutinizer ignore-call */ new $node(null, $this);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
30
31
        foreach ($node as $key => $value) {
32
            $newNode[$key] = $this->import($value);
33
        }
34
35
        return $newNode;
36
    }
37
38
    /**
39
     * @throws InvalidSubjectNodeException
40
     */
41
    private function importScalarNode(Node $node): Node
42
    {
43
        if (!\in_array(\get_class($node), [BooleanNode::class, NumberNode::class, StringNode::class])) {
44
            throw new InvalidSubjectNodeException('Source node is of unknown type: ' . \get_class($node));
45
        }
46
47
        try {
48
            return Node::createFromValue($node->getValue(), $this);
49
        //@codeCoverageIgnoreStart
50
        } catch (\Exception $e) {
51
            /** @noinspection PhpInternalEntityUsedInspection */
52
            throw unexpected($e);
53
        }
54
        //@codeCoverageIgnoreEnd
55
    }
56
57
    private function __construct() { }
58
59
    public function __clone()
60
    {
61
        try {
62
            $this->rootNode = $this->import($this->rootNode);
63
        //@codeCoverageIgnoreStart
64
        } catch (\Exception $e) {
65
            /** @noinspection PhpInternalEntityUsedInspection */
66
            throw unexpected($e);
67
        }
68
        //@codeCoverageIgnoreEnd
69
    }
70
71
    /**
72
     * @throws ParseFailureException
73
     */
74 64
    public static function parse(string $json, ?int $depthLimit = 512, ?int $options = 0): Document
75
    {
76 64
        static $nodeFactory;
77
78 64
        $depthLimit = $depthLimit ?? 512;
79 64
        $options = ($options ?? 0) & ~\JSON_OBJECT_AS_ARRAY;
80
81
        try {
82 64
            $data = \ExceptionalJSON\decode($json, false, $depthLimit, $options);
83
84 64
            $doc = new self();
85 64
            $doc->rootNode = ($nodeFactory ?? $nodeFactory = new SafeNodeFactory)
86 64
                ->createNodeFromValue($data, $doc, 0);
87
88 64
            return $doc;
89
        } catch (DecodeErrorException $e) {
90
            throw new ParseFailureException("Decoding JSON string failed: {$e->getMessage()}", $e);
91
        //@codeCoverageIgnoreStart
92
        } catch (\Exception $e) {
93
            /** @noinspection PhpInternalEntityUsedInspection */
94
            throw unexpected($e);
95
        }
96
        //@codeCoverageIgnoreEnd
97
    }
98
99
    /**
100
     * @throws InvalidNodeValueException
101
     */
102 21
    public static function createFromValue($value, ?int $flags = 0): Document
103
    {
104
        try {
105 21
            $doc = new self();
106 21
            $doc->rootNode = Node::createFromValue($value, $doc, $flags);
107
108 21
            return $doc;
109
        } catch (InvalidNodeValueException $e) {
110
            throw $e;
111
        //@codeCoverageIgnoreStart
112
        } catch (\Exception $e) {
113
            /** @noinspection PhpInternalEntityUsedInspection */
114
            throw unexpected($e);
115
        }
116
        //@codeCoverageIgnoreEnd
117
    }
118
119
    public static function createFromNode(Node $node): Document
120
    {
121
        try {
122
            $doc = new self();
123
            $doc->rootNode = $doc->import($node);
124
125
            return $doc;
126
        //@codeCoverageIgnoreStart
127
        } catch (\Exception $e) {
128
            /** @noinspection PhpInternalEntityUsedInspection */
129
            throw unexpected($e);
130
        }
131
        //@codeCoverageIgnoreEnd
132
    }
133
134 84
    public function getRootNode(): Node
135
    {
136 84
        return $this->rootNode;
137
    }
138
139
    /**
140
     * @throws InvalidSubjectNodeException
141
     * @throws WriteOperationForbiddenException
142
     * @throws InvalidNodeValueException
143
     * @throws InvalidSubjectNodeException
144
     */
145
    public function import(Node $node): Node
146
    {
147
        if ($node->getOwnerDocument() === $this) {
148
            throw new InvalidSubjectNodeException('Cannot import tne supplied node, already owned by this document');
149
        }
150
151
        if ($node instanceof NullNode) {
152
            return new NullNode($this);
153
        }
154
155
        return $node instanceof VectorNode
156
            ? $this->importVectorNode($node)
157
            : $this->importScalarNode($node);
158
    }
159
160
    public function jsonSerialize()
161
    {
162
        return $this->rootNode !== null
163
            ? $this->rootNode->jsonSerialize()
164
            : null;
165
    }
166
}
167