Pointer::unescape()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
dl 0
loc 13
c 3
b 0
f 0
rs 9.4285
cc 2
eloc 7
nc 2
nop 1
1
<?php
2
declare(strict_types=1);
3
4
namespace gamringer\JSONPointer;
5
6
use gamringer\JSONPointer\Access\Accesses;
7
use gamringer\JSONPointer\Access\ArrayAccessor;
8
use gamringer\JSONPointer\Access\ObjectAccessor;
9
use gamringer\JSONPointer\Exception;
10
11
class Pointer
12
{
13
    private $target;
14
15
    private $accessorCollection;
16
17
    public function __construct(&...$targets)
18
    {
19
        $target = new VoidValue();
20
        if (!empty($targets)) {
21
            $target = &$targets[0];
22
        }
23
        $this->setTarget($target);
24
25
        $this->accessorCollection = new AccessorCollection();
26
    }
27
28
    public function getAccessorCollection(): AccessorCollection
29
    {
30
        return $this->accessorCollection;
31
    }
32
33
    public function setTarget(&$target)
34
    {
35
        $this->target = &$target;
36
    }
37
38
    public function getTarget()
39
    {
40
        return $this->target;
41
    }
42
43
    public function has(string $path)
44
    {
45
        try {
46
            $this->reference($path)->hasValue();
47
        } catch (Exception $e) {
48
            return false;
49
        }
50
51
        return true;
52
    }
53
54
    public function get(string $path)
55
    {
56
        return $this->reference($path)->getValue();
57
    }
58
59
    public function set(string $path, &$value)
60
    {
61
        return $this->reference($path)->setValue($value);
62
    }
63
64
    public function insert(string $path, &$value)
65
    {
66
        return $this->reference($path)->insertValue($value);
67
    }
68
69
    public function remove(string $path)
70
    {
71
        return $this->reference($path)->unsetValue();
72
    }
73
74
    private function reference(string $path): ReferencedValue
75
    {
76
        $path = $this->getCleanPath($path);
77
        if (empty($path)) {
78
            return new ReferencedValue($this->target);
79
        }
80
81
        $this->assertTarget();
82
83
        return $this->walk($path);
84
    }
85
86
    private function unescape(string $token): string
87
    {
88
        $token = (string) $token;
89
90
        if (preg_match('/~[^01]/', $token)) {
91
            throw new Exception('Invalid pointer syntax');
92
        }
93
94
        $token = str_replace('~1', '/', $token);
95
        $token = str_replace('~0', '~', $token);
96
97
        return $token;
98
    }
99
100
    private function getCleanPath(string $path): string
101
    {
102
        $path = (string) $path;
103
104
        $path = $this->getRepresentedPath($path);
105
106
        if (!empty($path) && $path[0] !== '/') {
107
            throw new Exception('Invalid pointer syntax');
108
        }
109
110
        return $path;
111
    }
112
113
    private function getRepresentedPath(string $path): string
114
    {
115
        if (substr($path, 0, 1) === '#') {
116
            return urldecode(substr($path, 1));
117
        }
118
119
        return stripslashes($path);
120
    }
121
122
    private function walk(string $path): ReferencedValue
123
    {
124
        $target = &$this->target;
125
        $tokens = explode('/', substr($path, 1));
126
127
        $accessor = null;
128
129
        while (($token = array_shift($tokens)) !== null) {
130
            $accessor = $this->accessorCollection->getAccessorFor($target);
131
            $token = $this->unescape($token);
132
133
            if (empty($tokens)) {
134
                break;
135
            }
136
137
            $target = &$this->fetchTokenTargetFrom($target, $token, $accessor);
138
        }
139
140
        return new ReferencedValue($target, $token, $accessor);
141
    }
142
143
    private function &fetchTokenTargetFrom(&$target, string $token, Accesses $accessor)
144
    {
145
        $result = &$accessor->getValue($target, $token);
146
147
        return $result;
148
    }
149
150
    private function assertTarget()
151
    {
152
        if ($this->target instanceof VoidValue) {
153
            throw new Exception('No target defined');
154
        }
155
    }
156
}
157