Completed
Push — master ( c91a90...e32b70 )
by Chris
02:54
created

Document::__init()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
ccs 0
cts 2
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 2
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
\DaveRandom\Jom\initialize(Document::class);
12
13
final class Document implements \JsonSerializable, Taggable
14
{
15
    use TagData;
16
17
    public const IGNORE_INVALID_VALUES = Node::IGNORE_INVALID_VALUES;
18
19
    /** @var NodeFactory */
20
    private static $nodeFactory;
21
22
    /** @var Node */
23
    private $rootNode;
24
25
    /** @uses __init() */
26
    private static function __init(): void
27
    {
28
        self::$nodeFactory = new SafeNodeFactory();
29
    }
30
31
    /**
32
     * @throws InvalidNodeValueException
33
     * @throws InvalidSubjectNodeException
34
     * @throws WriteOperationForbiddenException
35
     */
36
    private function importVectorNode(VectorNode $node): VectorNode
37
    {
38
        if (!\in_array(\get_class($node), [ArrayNode::class, ObjectNode::class])) {
39
            throw new InvalidSubjectNodeException('Source node is of unknown type: ' . \get_class($node));
40
        }
41
42
        $newNode = new $node(null, $this);
43
44
        foreach ($node as $key => $value) {
45
            $newNode[$key] = $this->import($value);
46
        }
47
48
        return $newNode;
49
    }
50
51
    /**
52
     * @throws InvalidSubjectNodeException
53
     */
54
    private function importScalarNode(Node $node): Node
55
    {
56
        if (!\in_array(\get_class($node), [BooleanNode::class, NumberNode::class, StringNode::class])) {
57
            throw new InvalidSubjectNodeException('Source node is of unknown type: ' . \get_class($node));
58
        }
59
60
        try {
61
            return Node::createFromValue($node->getValue(), $this);
62
        //@codeCoverageIgnoreStart
63
        } catch (\Exception $e) {
64
            throw unexpected($e);
65
        }
66
        //@codeCoverageIgnoreEnd
67
    }
68
69
    private function __construct() { }
70
71
    public function __clone()
72
    {
73
        try {
74
            $this->rootNode = $this->import($this->rootNode);
75
        //@codeCoverageIgnoreStart
76 64
        } catch (\Exception $e) {
77
            throw unexpected($e);
78 64
        }
79
        //@codeCoverageIgnoreEnd
80 64
    }
81 64
82
    /**
83
     * @throws ParseFailureException
84 64
     */
85
    public static function parse(string $json, ?int $depthLimit = 512, ?int $options = 0): Document
86 64
    {
87 64
        $depthLimit = $depthLimit ?? 512;
88 64
        $options = ($options ?? 0) & ~\JSON_OBJECT_AS_ARRAY;
89
90 64
        try {
91
            $data = \ExceptionalJSON\decode($json, false, $depthLimit, $options);
92
93
            $doc = new self();
94
            $doc->rootNode = self::$nodeFactory->createNodeFromValue($data, $doc, 0);
95
96
            return $doc;
97
        } catch (DecodeErrorException $e) {
98
            throw new ParseFailureException("Decoding JSON string failed: {$e->getMessage()}", $e);
99
        //@codeCoverageIgnoreStart
100
        } catch (\Exception $e) {
101
            throw unexpected($e);
102
        }
103
        //@codeCoverageIgnoreEnd
104
    }
105 21
106
    /**
107
     * @param bool|int|float|string|array|object|null A value that can be encoded as JSON
108 21
     * @throws InvalidNodeValueException
109 21
     */
110
    public static function createFromValue($value, ?int $flags = 0): Document
111 21
    {
112
        try {
113
            $doc = new self();
114
            $doc->rootNode = Node::createFromValue($value, $doc, $flags);
115
116
            return $doc;
117
        } catch (InvalidNodeValueException $e) {
118
            throw $e;
119
        //@codeCoverageIgnoreStart
120
        } catch (\Exception $e) {
121
            throw unexpected($e);
122
        }
123
        //@codeCoverageIgnoreEnd
124
    }
125
126
    public static function createFromNode(Node $node): Document
127
    {
128
        try {
129
            $doc = new self();
130
            $doc->rootNode = $doc->import($node);
131
132
            return $doc;
133
        //@codeCoverageIgnoreStart
134
        } catch (\Exception $e) {
135
            throw unexpected($e);
136
        }
137 84
        //@codeCoverageIgnoreEnd
138
    }
139 84
140
    public function getRootNode(): Node
141
    {
142
        return $this->rootNode;
143
    }
144
145
    /**
146
     * @throws InvalidSubjectNodeException
147
     * @throws WriteOperationForbiddenException
148
     * @throws InvalidNodeValueException
149
     * @throws InvalidSubjectNodeException
150
     */
151
    public function import(Node $node): Node
152
    {
153
        if ($node->getOwnerDocument() === $this) {
154
            throw new InvalidSubjectNodeException('Cannot import tne supplied node, already owned by this document');
155
        }
156
157
        if ($node instanceof NullNode) {
158
            return new NullNode($this);
159
        }
160
161
        return $node instanceof VectorNode
162
            ? $this->importVectorNode($node)
163
            : $this->importScalarNode($node);
164
    }
165
166
    public function jsonSerialize()
167
    {
168
        return $this->rootNode !== null
169
            ? $this->rootNode->jsonSerialize()
170
            : null;
171
    }
172
}
173