Completed
Push — master ( 5927eb...1e13db )
by
unknown
04:04
created

Pointer::traverse()   D

Complexity

Conditions 9
Paths 8

Size

Total Lines 34
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 9.4453
Metric Value
cc 9
eloc 18
nc 8
nop 2
dl 0
loc 34
ccs 14
cts 17
cp 0.8235
crap 9.4453
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 40
    public function __construct($json)
26
    {
27 40
        $this->json = $json;
28 40
    }
29
30
    /**
31
     * @param string $pointer
32
     * @return mixed
33
     * @throws InvalidPointerException
34
     */
35 34
    public function get($pointer)
36
    {
37 34
        $pointer = (new Parser($pointer))->get();
38
39 30
        return $this->traverse($this->json, $pointer);
40
    }
41
42
    /**
43
     * @param string $pointer
44
     * @return bool
45
     */
46 28
    public function has($pointer)
47
    {
48
        try {
49 28
            $this->get($pointer);
50
51 28
            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 34
    public function set($pointer, $data)
66
    {
67 34
        if ($pointer === '') {
68
            throw new \InvalidArgumentException('Cannot replace the object with set.');
69
        }
70
71 34
        $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 34
        $this->traverse($this->json, $pointer);
76
77 34
        $replace = array_pop($pointer);
78 34
        $target  = $this->json;
79 34
        foreach ($pointer as $segment) {
80 32
            if (is_array($target)) {
81 8
                $target =& $target[$segment];
82 8
            } else {
83 32
                $target =& $target->$segment;
84
            }
85 34
        }
86
87 34
        if (is_array($target)) {
88 10
            $target[$replace] = $data;
89 34
        } elseif (is_object($target)) {
90 30
            $target->$replace = $data;
91 30
        } else {
92
            throw new \InvalidArgumentException('Cannot set data because pointer target is not an object or array.');
93
        }
94
95 34
        return null;
96
    }
97
98
    /**
99
     * @param mixed $json    The result of a json_decode call or a portion of it.
100
     * @param array $pointer The parsed pointer
101
     * @return mixed
102
     */
103 36
    private function traverse($json, $pointer)
104
    {
105
        // If we are out of pointers to process we are done.
106 36
        if (empty($pointer)) {
107 36
            return $json;
108
        }
109
110 36
        $reference = array_shift($pointer);
111
112
        // who does this?
113 36
        if ($reference === '' && property_exists($json, '_empty_')) {
114 2
            return $this->traverse($json->_empty_, $pointer);
115
        }
116
117 36
        if (is_object($json)) {
118 36
            if (!property_exists($json, $reference)) {
119 2
                throw InvalidPointerException::nonexistentValue($reference);
120
            }
121
122 36
            return $this->traverse($json->$reference, $pointer);
123 14
        } elseif (is_array($json)) {
124 14
            if ($reference === '-') {
125
                // I guess we are done.
126
                return end($json);
127
            }
128 14
            if (!array_key_exists($reference, $json)) {
129
                throw InvalidPointerException::nonexistentValue($reference);
130
            }
131
132 14
            return $this->traverse($json[$reference], $pointer);
133
        } else {
134
            throw InvalidPointerException::nonexistentValue($reference);
135
        }
136
    }
137
}
138