Passed
Push — master ( ccfc71...4eb344 )
by Viacheslav
15:41 queued 05:46
created

JsonPatch::apply()   C

Complexity

Conditions 13
Paths 76

Size

Total Lines 65
Code Lines 55

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 35
CRAP Score 13

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 13
eloc 55
c 2
b 0
f 0
nc 76
nop 2
dl 0
loc 65
rs 6.6166
ccs 35
cts 35
cp 1
crap 13

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Swaggest\JsonDiff;
4
5
use Swaggest\JsonDiff\JsonPatch\Add;
6
use Swaggest\JsonDiff\JsonPatch\Copy;
7
use Swaggest\JsonDiff\JsonPatch\Move;
8
use Swaggest\JsonDiff\JsonPatch\OpPath;
9
use Swaggest\JsonDiff\JsonPatch\OpPathFrom;
10
use Swaggest\JsonDiff\JsonPatch\OpPathValue;
11
use Swaggest\JsonDiff\JsonPatch\Remove;
12
use Swaggest\JsonDiff\JsonPatch\Replace;
13
use Swaggest\JsonDiff\JsonPatch\Test;
14
15
/**
16
 * JSON Patch is specified in [RFC 6902](http://tools.ietf.org/html/rfc6902) from the IETF.
17
 *
18
 * Class JsonPatch
19
 */
20
class JsonPatch implements \JsonSerializable
21
{
22
    /**
23
     * Disallow converting empty array to object for key creation
24
     * @see JsonPointer::STRICT_MODE
25
     */
26
    const STRICT_MODE = 2;
27
28
    /**
29
     * Allow associative arrays to mimic JSON objects (not recommended)
30
     */
31
    const TOLERATE_ASSOCIATIVE_ARRAYS = 8;
32
33
34
    private $flags = 0;
35
36
    /**
37
     * @param int $options
38
     * @return $this
39
     */
40 2
    public function setFlags($options)
41
    {
42 2
        $this->flags = $options;
43 2
        return $this;
44
    }
45
46
    /** @var OpPath[] */
47
    private $operations = array();
48
49
    /**
50
     * @param array $data
51
     * @return JsonPatch
52
     * @throws Exception
53
     */
54 108
    public static function import(array $data)
55
    {
56 108
        $result = new JsonPatch();
57 108
        foreach ($data as $operation) {
58
            /** @var OpPath|OpPathValue|OpPathFrom|array $operation */
59 102
            if (is_array($operation)) {
60 4
                $operation = (object)$operation;
61
            }
62
63 102
            if (!is_object($operation)) {
64 1
                throw new Exception('Invalid patch operation - should be a JSON object');
65
            }
66 101
67 1
            if (!isset($operation->op)) {
68
                throw new MissingFieldException('op', $operation);
69
            }
70 100
            if (!isset($operation->path)) {
71 100
                throw new MissingFieldException('path', $operation);
72 100
            }
73 40
74 40
            $op = null;
75 64
            switch ($operation->op) {
76 6
                case Add::OP:
77 6
                    $op = new Add();
78 58
                    break;
79 8
                case Copy::OP:
80 8
                    $op = new Copy();
81 50
                    break;
82 18
                case Move::OP:
83 18
                    $op = new Move();
84 36
                    break;
85 19
                case Remove::OP:
86 19
                    $op = new Remove();
87 20
                    break;
88 18
                case Replace::OP:
89 18
                    $op = new Replace();
90
                    break;
91 2
                case Test::OP:
92
                    $op = new Test();
93 98
                    break;
94 98
                default:
95 71
                    throw new UnknownOperationException($operation);
96 5
            }
97
            $op->path = $operation->path;
98 66
            if ($op instanceof OpPathValue) {
99 32
                if (property_exists($operation, 'value')) {
100 14
                    $op->value = $operation->value;
101 3
                } else {
102
                    throw new MissingFieldException('value', $operation);
103 11
                }
104
            } elseif ($op instanceof OpPathFrom) {
105 90
                if (!isset($operation->from)) {
106
                    throw new MissingFieldException('from', $operation);
107 96
                }
108
                $op->from = $operation->from;
109
            }
110 7
            $result->operations[] = $op;
111
        }
112 7
        return $result;
113 7
    }
114 7
115
    public static function export(JsonPatch $patch)
116
    {
117 7
        $result = array();
118
        foreach ($patch->operations as $operation) {
119
            $result[] = (object)(array)$operation;
120 33
        }
121
122 33
        return $result;
123 33
    }
124
125
    public function op(OpPath $op)
126 6
    {
127
        $this->operations[] = $op;
128 6
        return $this;
129
    }
130
131
    #[\ReturnTypeWillChange]
132
    public function jsonSerialize()
133
    {
134
        return self::export($this);
135
    }
136
137 100
    /**
138
     * @param mixed $original
139 100
     * @param bool $stopOnError
140 100
     * @return Exception[] array of errors
141
     * @throws Exception
142 94
     */
143
    public function apply(&$original, $stopOnError = true)
144 94
    {
145 40
        $errors = array();
146 32
        foreach ($this->operations as $operation) {
147 57
            try {
148 5
                // track the current pointer field so we can use it for a potential PathException
149 5
                $pointerField = 'path';
150 4
                $pathItems = JsonPointer::splitPath($operation->path);
151 4
                switch (true) {
152 53
                    case $operation instanceof Add:
153 7
                        JsonPointer::add($original, $pathItems, $operation->value, $this->flags);
154 7
                        break;
155 6
                    case $operation instanceof Copy:
156 6
                        $pointerField = 'from';
157 6
                        $fromItems = JsonPointer::splitPath($operation->from);
158 46
                        $value = JsonPointer::get($original, $fromItems);
159 18
                        $pointerField = 'path';
160 14
                        JsonPointer::add($original, $pathItems, $value, $this->flags);
161 32
                        break;
162 18
                    case $operation instanceof Move:
163 16
                        $pointerField = 'from';
164 16
                        $fromItems = JsonPointer::splitPath($operation->from);
165 16
                        $value = JsonPointer::get($original, $fromItems);
166 18
                        JsonPointer::remove($original, $fromItems, $this->flags);
167 18
                        $pointerField = 'path';
168 15
                        JsonPointer::add($original, $pathItems, $value, $this->flags);
169 15
                        break;
170 15
                    case $operation instanceof Remove:
171 3
                        JsonPointer::remove($original, $pathItems, $this->flags);
172 3
                        break;
173
                    case $operation instanceof Replace:
174 73
                        JsonPointer::get($original, $pathItems);
175
                        JsonPointer::remove($original, $pathItems, $this->flags);
176 23
                        JsonPointer::add($original, $pathItems, $operation->value, $this->flags);
177 23
                        break;
178 21
                    case $operation instanceof Test:
179
                        $value = JsonPointer::get($original, $pathItems);
180 2
                        $diff = new JsonDiff($operation->value, $value,
181
                            JsonDiff::STOP_ON_DIFF);
182
                        if ($diff->getDiffCnt() !== 0) {
183
                            throw new PatchTestOperationFailedException($operation, $value);
184 80
                        }
185
                        break;
186
                }
187
            } catch (JsonPointerException $jsonPointerException) {
188
                $pathException = new PathException(
189
                    $jsonPointerException->getMessage(),
190
                    $operation,
191
                    $pointerField,
192
                    $jsonPointerException->getCode()
193
                );
194
                if ($stopOnError) {
195
                    throw $pathException;
196
                } else {
197
                    $errors[] = $pathException;
198
                }
199
            } catch (Exception $exception) {
200
                if ($stopOnError) {
201
                    throw $exception;
202
                } else {
203
                    $errors[] = $exception;
204
                }
205
            }
206
        }
207
        return $errors;
208
    }
209
}
210