Completed
Pull Request — master (#15)
by Matt
02:49
created

Pointer::traverse()   D

Complexity

Conditions 9
Paths 8

Size

Total Lines 33
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 9.1317

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 9
eloc 18
c 1
b 0
f 0
nc 8
nop 2
dl 0
loc 33
ccs 15
cts 17
cp 0.8824
crap 9.1317
rs 4.909
1
<?php
2
3
namespace League\JsonGuard;
4
5
use League\JsonGuard\Pointer\InvalidPointerException;
6
use League\JsonGuard\Pointer\Parser;
7
8
/**
9
 * A simple JSON Pointer implementation that can traverse
10
 * an object resulting from a json_decode() call.
11
 *
12
 * @see https://tools.ietf.org/html/rfc6901
13
 */
14
class Pointer
15
{
16
    /**
17
     * @var object
18
     */
19
    private $json;
20
21
    /**
22
     * Pointer constructor.
23
     * @param object $json
24
     */
25 46
    public function __construct($json)
26
    {
27 46
        $this->json = $json;
28 46
    }
29
30
    /**
31
     * @param string $pointer
32
     * @return mixed
33
     * @throws InvalidPointerException
34
     */
35 38
    public function get($pointer)
36
    {
37 38
        $pointer = (new Parser($pointer))->get();
38
39 34
        return $this->traverse($this->json, $pointer);
40
    }
41
42
    /**
43
     * @param string $pointer
44
     * @return bool
45
     */
46 32
    public function has($pointer)
47
    {
48
        try {
49 32
            $this->get($pointer);
50
51 32
            return true;
52 2
        } catch (InvalidPointerException $e) {
53 2
            return false;
54
        }
55
    }
56
57
    /**
58
     * @param string $pointer
59
     * @param mixed  $data
60
     * @return null
61
     * @throws InvalidPointerException
62
     * @throws \InvalidArgumentException
63
     *
64
     */
65 40
    public function set($pointer, $data)
66
    {
67 40
        if ($pointer === '') {
68
            throw new \InvalidArgumentException('Cannot replace the object with set.');
69
        }
70
71 40
        $pointer = (new Parser($pointer))->get();
72
73
        // Simple way to check if the path exists.
74
        // It will throw an exception if it isn't valid.
75 40
        $this->traverse($this->json, $pointer);
76
77 40
        $replace = array_pop($pointer);
78 40
        $target  = $this->json;
79 40
        foreach ($pointer as $segment) {
80 34
            if (is_array($target)) {
81 8
                $target =& $target[$segment];
82 8
            } else {
83 34
                $target =& $target->$segment;
84
            }
85 40
        }
86
87 40
        if (is_array($target)) {
88 12
            if ($replace === '-') {
89 2
                $target[] = $data;
90 2
            } else {
91 10
                $target[$replace] = $data;
92
            }
93 40
        } elseif (is_object($target)) {
94 34
            $target->$replace = $data;
95 34
        } else {
96
            throw new \InvalidArgumentException('Cannot set data because pointer target is not an object or array.');
97
        }
98
99 40
        return null;
100
    }
101
102
    /**
103
     * @param mixed $json    The result of a json_decode call or a portion of it.
104
     * @param array $pointer The parsed pointer
105
     * @return mixed
106
     */
107 42
    private function traverse($json, $pointer)
108
    {
109
        // If we are out of pointers to process we are done.
110 42
        if (empty($pointer)) {
111 40
            return $json;
112
        }
113
114 42
        $reference = array_shift($pointer);
115
116
        // who does this?
117 42
        if ($reference === '' && property_exists($json, '_empty_')) {
118 2
            return $this->traverse($json->_empty_, $pointer);
119
        }
120
121 42
        if (is_object($json)) {
122 42
            if (!property_exists($json, $reference)) {
123 2
                throw InvalidPointerException::nonexistentValue($reference);
124
            }
125
126 42
            return $this->traverse($json->$reference, $pointer);
127 16
        } elseif (is_array($json)) {
128 16
            if ($reference === '-') {
129 2
                return $json;
130
            }
131 14
            if (!array_key_exists($reference, $json)) {
132
                throw InvalidPointerException::nonexistentValue($reference);
133
            }
134
135 14
            return $this->traverse($json[$reference], $pointer);
136
        } else {
137
            throw InvalidPointerException::nonexistentValue($reference);
138
        }
139
    }
140
}
141