Test Failed
Push — master ( 8368bb...ec8827 )
by Edward
04:04
created

Patch::getOperationValue()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 2
nop 1
dl 0
loc 11
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Remorhaz\JSON\Patch;
4
5
use Remorhaz\JSON\Data\Value\ArrayValueInterface;
6
use Remorhaz\JSON\Data\Value\DecodedJson\NodeValueFactory as DecodedJsonNodeValueFactory;
7
use Remorhaz\JSON\Data\Value\NodeValueInterface;
8
use Remorhaz\JSON\Pointer\Processor\Processor;
9
use Remorhaz\JSON\Pointer\Processor\Result\ResultInterface;
10
use Remorhaz\JSON\Pointer\Query\QueryFactory;
11
use Remorhaz\JSON\Pointer\Query\QueryInterface;
12
use RuntimeException;
13
use function is_string;
14
15
class Patch
16
{
17
18
    private $queryFactory;
19
20
    private $queryProcessor;
21
22
    private $decodedJsonNodeValueFactory;
23
24
    private $outputData;
25
26
    public function __construct(NodeValueInterface $inputData)
27
    {
28
        $this->outputData = $inputData;
29
        $this->queryFactory = QueryFactory::create();
30
        $this->queryProcessor = Processor::create();
31
        $this->decodedJsonNodeValueFactory = DecodedJsonNodeValueFactory::create();
32
    }
33
34
    public function apply(NodeValueInterface $patch)
35
    {
36
        if (!$patch instanceof ArrayValueInterface) {
37
            throw new RuntimeException("Patch must be an array");
38
        }
39
        foreach ($patch->createChildIterator() as $element) {
40
            $this->performOperation($element);
41
        }
42
43
        return $this;
44
    }
45
46
    protected function performOperation(NodeValueInterface $element)
47
    {
48
        $operation = $this
49
            ->queryProcessor
50
            ->select($this->queryFactory->createQuery('/op'), $element)
51
            ->decode();
52
        if (!is_string($operation)) {
53
            throw new RuntimeException("Invalid patch operation");
54
        }
55
        $pathQuery = $this->getOperationPath($element);
56
        switch ($operation) {
57
            case 'add':
58
                $result = $this
59
                    ->queryProcessor
60
                    ->add(
61
                        $pathQuery,
62
                        $this->outputData,
63
                        $this->getOperationValue($element)
64
                    );
65
                $this->outputData = $this->getResultValue($result);
66
                break;
67
68
            case 'remove':
69
                $result = $this
70
                    ->queryProcessor
71
                    ->delete($pathQuery, $this->outputData);
72
                $this->outputData = $this->getResultValue($result);
73
                break;
74
75
            case 'replace':
76
                $result = $this
77
                    ->queryProcessor
78
                    ->replace(
79
                        $pathQuery,
80
                        $this->outputData,
81
                        $this->getOperationValue($element)
82
                    );
83
                $this->outputData = $this->getResultValue($result);
84
                break;
85
86
            case 'test':
87
                $result = $this
88
                    ->queryProcessor
89
                    ->select($pathQuery, $this->outputData);
90
                if (!$result->exists()) {
91
                    throw new RuntimeException("Test operation failed");
92
                }
93
                break;
94
95
            case 'copy':
96
                $fromResult = $this
97
                    ->queryProcessor
98
                    ->select(
99
                        $this->getOperationFrom($element),
100
                        $this->outputData
101
                    );
102
                $result = $this
103
                    ->queryProcessor
104
                    ->add(
105
                        $pathQuery,
106
                        $this->outputData,
107
                        $this->getResultValue($fromResult)
108
                    );
109
                $this->outputData = $this->getResultValue($result);
110
                break;
111
112
            case 'move':
113
                $fromPathQuery = $this->getOperationFrom($element);
114
                $fromResult = $this
115
                    ->queryProcessor
116
                    ->select($fromPathQuery, $this->outputData);
117
                $removeResult = $this
118
                    ->queryProcessor
119
                    ->delete($fromPathQuery, $this->outputData);
120
                $result = $this
121
                    ->queryProcessor
122
                    ->add(
123
                        $pathQuery,
124
                        $this->getResultValue($removeResult),
125
                        $this->getResultValue($fromResult)
126
                    );
127
                $this->outputData = $this->getResultValue($result);
128
                break;
129
130
            default:
131
                throw new RuntimeException("Unknown operation '{$operation}'");
132
        }
133
134
        return $this;
135
    }
136
137
    private function getOperationPath(NodeValueInterface $operation): QueryInterface
138
    {
139
        $path = $this
140
            ->queryProcessor
141
            ->select($this->queryFactory->createQuery('/path'), $operation)
142
            ->decode();
143
        if (!is_string($path)) {
144
            throw new RuntimeException("Invalid operation path");
145
        }
146
147
        return $this
148
            ->queryFactory
149
            ->createQuery($path);
150
    }
151
152
    private function getOperationFrom(NodeValueInterface $operation): QueryInterface
153
    {
154
        $path = $this
155
            ->queryProcessor
156
            ->select($this->queryFactory->createQuery('/from'), $operation)
157
            ->decode();
158
        if (!is_string($path)) {
159
            throw new RuntimeException("Invalid operation from");
160
        }
161
162
        return $this
163
            ->queryFactory
164
            ->createQuery($path);
165
    }
166
167
    private function getOperationValue(NodeValueInterface $operation): NodeValueInterface
168
    {
169
        $result = $this
170
            ->queryProcessor
171
            ->select($this->queryFactory->createQuery('/value'), $operation);
172
173
        if (!$result->exists()) {
174
            throw new RuntimeException("Patch result not found");
175
        }
176
177
        return $this->getResultValue($result);
178
    }
179
180
    private function getResultValue(ResultInterface $result): NodeValueInterface
181
    {
182
        return $this
183
            ->decodedJsonNodeValueFactory
184
            ->createValue($result->decode());
185
    }
186
187
    public function getOutputData(): NodeValueInterface
188
    {
189
        return $this->outputData;
190
    }
191
}
192