Completed
Push — master ( 9afc27...035c9e )
by Chris
04:26
created

Document::evaluatePointer()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
dl 0
loc 15
c 0
b 0
f 0
ccs 0
cts 9
cp 0
rs 9.2
cc 4
eloc 8
nc 6
nop 2
crap 20
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 (!($node instanceof ArrayNode || $node instanceof ObjectNode)) {
26
            throw new InvalidSubjectNodeException('Source node is of unknown type ' . \get_class($node));
27
        }
28
29
        $newNode = new $node(null, $this);
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 (!($node instanceof BooleanNode || $node instanceof NumberNode || $node instanceof StringNode)) {
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
            throw unexpected($e);
52
        }
53
        //@codeCoverageIgnoreEnd
54
    }
55
56
    private function __construct() { }
57
58
    public function __clone()
59
    {
60
        try {
61
            $this->rootNode = $this->import($this->rootNode);
62
        //@codeCoverageIgnoreStart
63
        } catch (\Exception $e) {
64
            throw unexpected($e);
65
        }
66
        //@codeCoverageIgnoreEnd
67
    }
68
69
    /**
70
     * @throws ParseFailureException
71
     */
72 64
    public static function parse(string $json, ?int $depthLimit = 512, ?int $options = 0): Document
73
    {
74 64
        static $nodeFactory;
75
76 64
        $depthLimit = $depthLimit ?? 512;
77 64
        $options = ($options ?? 0) & ~\JSON_OBJECT_AS_ARRAY;
78
79
        try {
80 64
            $data = \ExceptionalJSON\decode($json, false, $depthLimit, $options);
81
82 64
            $doc = new self();
83 64
            $doc->rootNode = ($nodeFactory ?? $nodeFactory = new SafeNodeFactory)
84 64
                ->createNodeFromValue($data, $doc, 0);
85
86 64
            return $doc;
87
        } catch (DecodeErrorException $e) {
88
            throw new ParseFailureException("Decoding JSON string failed: {$e->getMessage()}", $e);
89
        //@codeCoverageIgnoreStart
90
        } catch (\Exception $e) {
91
            throw unexpected($e);
92
        }
93
        //@codeCoverageIgnoreEnd
94
    }
95
96
    /**
97
     * @throws InvalidNodeValueException
98
     */
99 21
    public static function createFromValue($value, ?int $flags = 0): Document
100
    {
101
        try {
102 21
            $doc = new self();
103 21
            $doc->rootNode = Node::createFromValue($value, $doc, $flags);
104
105 21
            return $doc;
106
        } catch (InvalidNodeValueException $e) {
107
            throw $e;
108
        //@codeCoverageIgnoreStart
109
        } catch (\Exception $e) {
110
            throw unexpected($e);
111
        }
112
        //@codeCoverageIgnoreEnd
113
    }
114
115
    public static function createFromNode(Node $node): Document
116
    {
117
        try {
118
            $doc = new self();
119
            $doc->rootNode = $doc->import($node);
120
121
            return $doc;
122
        //@codeCoverageIgnoreStart
123
        } catch (\Exception $e) {
124
            throw unexpected($e);
125
        }
126
        //@codeCoverageIgnoreEnd
127
    }
128
129 84
    public function getRootNode(): Node
130
    {
131 84
        return $this->rootNode;
132
    }
133
134
    /**
135
     * @throws InvalidSubjectNodeException
136
     * @throws WriteOperationForbiddenException
137
     * @throws InvalidNodeValueException
138
     * @throws InvalidSubjectNodeException
139
     */
140
    public function import(Node $node): Node
141
    {
142
        if ($node->getOwnerDocument() === $this) {
143
            throw new InvalidSubjectNodeException('Cannot import tne supplied node, already owned by this document');
144
        }
145
146
        if ($node instanceof NullNode) {
147
            return new NullNode($this);
148
        }
149
150
        return $node instanceof VectorNode
151
            ? $this->importVectorNode($node)
152
            : $this->importScalarNode($node);
153
    }
154
155
    public function jsonSerialize()
156
    {
157
        return $this->rootNode !== null
158
            ? $this->rootNode->jsonSerialize()
159
            : null;
160
    }
161
}
162