Completed
Pull Request — master (#9)
by Viacheslav
39:48
created

RefResolver::getRefProvider()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 4
nc 2
nop 0
1
<?php
2
3
namespace Swaggest\JsonSchema;
4
5
use PhpLang\ScopeExit;
6
use Swaggest\JsonSchema\Constraint\Ref;
7
use Swaggest\JsonSchema\RemoteRef\BasicFetcher;
8
9
class RefResolver
10
{
11
    public $resolutionScope = '';
12
    public $url;
13
    /** @var RefResolver */
14
    private $rootResolver;
15
16
    /**
17
     * @param mixed $resolutionScope
18
     * @return string previous value
19
     */
20
    public function setResolutionScope($resolutionScope)
21
    {
22
        $rootResolver = $this->rootResolver ? $this->rootResolver : $this;
23
        if ($resolutionScope === $rootResolver->resolutionScope) {
24
            return $resolutionScope;
25
        }
26
        $prev = $rootResolver->resolutionScope;
27
        $rootResolver->resolutionScope = $resolutionScope;
28
        return $prev;
29
    }
30
31
    /**
32
     * @return string
33
     */
34
    public function getResolutionScope()
35
    {
36
        $rootResolver = $this->rootResolver ? $this->rootResolver : $this;
37
        return $rootResolver->resolutionScope;
38
    }
39
40
41
    public function updateResolutionScope($id)
42
    {
43
        $id = rtrim($id, '#');
44
        $rootResolver = $this->rootResolver ? $this->rootResolver : $this;
45
        if (strpos($id, '://') !== false) {
46
            $prev = $rootResolver->setResolutionScope($id);
47
        } else {
48
            $prev = $rootResolver->setResolutionScope(Helper::resolveURI($rootResolver->resolutionScope, $id));
49
        }
50
51
        return $prev;
52
    }
53
54
    public function setupResolutionScope($id, $data)
55
    {
56
        $rootResolver = $this->rootResolver ? $this->rootResolver : $this;
57
58
        $prev = $this->updateResolutionScope($id);
59
60
        $refParts = explode('#', $rootResolver->resolutionScope, 2);
61
        if ($refParts[0] && empty($refParts[1])) {
62
            if (!isset($rootResolver->remoteRefResolvers[$refParts[0]])) {
63
                $resolver = new RefResolver($data);
64
                $resolver->rootResolver = $rootResolver;
65
                $resolver->url = $refParts[0];
66
                $this->remoteRefResolvers[$refParts[0]] = $resolver;
67
            }
68
        }
69
70
        return $prev;
71
    }
72
73
    private $rootData;
74
75
    /** @var Ref[] */
76
    private $refs = array();
77
78
    /** @var RefResolver[] */
79
    private $remoteRefResolvers = array();
80
81
    /** @var RemoteRefProvider */
82
    private $refProvider;
83
84
    /**
85
     * RefResolver constructor.
86
     * @param $rootData
87
     */
88
    public function __construct($rootData)
89
    {
90
        $this->rootData = $rootData;
91
    }
92
93
94
    public function setRemoteRefProvider(RemoteRefProvider $provider)
95
    {
96
        $this->refProvider = $provider;
97
        return $this;
98
    }
99
100
    private function getRefProvider()
101
    {
102
        if (null === $this->refProvider) {
103
            $this->refProvider = new BasicFetcher();
104
        }
105
        return $this->refProvider;
106
    }
107
108
109
    /**
110
     * @param $referencePath
111
     * @return Ref
112
     * @throws \Exception
113
     */
114
    public function resolveReference($referencePath)
115
    {
116
        if ($this->resolutionScope) {
117
            $referencePath = Helper::resolveURI($this->resolutionScope, $referencePath);
118
        }
119
120
        $refParts = explode('#', $referencePath, 2);
121
        $url = rtrim($refParts[0], '#');
122
        $refLocalPath = isset($refParts[1]) ? '#' . $refParts[1] : '#';
123
124
        if ($url === $this->url) {
125
            $referencePath = $refLocalPath;
126
        }
127
128
        $ref = &$this->refs[$referencePath];
129
130
        $refResolver = $this;
131
132
        if (null === $ref) {
133
            if ($referencePath[0] === '#') {
134
                if ($referencePath === '#') {
135
                    $ref = new Ref($referencePath, $refResolver->rootData);
136
                } else {
137
                    $ref = new Ref($referencePath);
138
                    $path = explode('/', trim($referencePath, '#/'));
139
140
                    /** @var JsonSchema $branch */
141
                    $branch = &$refResolver->rootData;
142
                    while (!empty($path)) {
143
                        if (isset($branch->id) && is_string($branch->id)) {
144
                            $refResolver->updateResolutionScope($branch->id);
145
                        }
146
147
                        $folder = array_shift($path);
148
149
                        // unescaping special characters
150
                        // https://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-07#section-4
151
                        // https://github.com/json-schema-org/JSON-Schema-Test-Suite/issues/130
152
                        $folder = str_replace(array('~0', '~1', '%25'), array('~', '/', '%'), $folder);
153
154
                        if ($branch instanceof \stdClass && isset($branch->$folder)) {
155
                            $branch = &$branch->$folder;
156
                        } elseif (is_array($branch) && isset($branch[$folder])) {
157
                            $branch = &$branch[$folder];
158
                        } else {
159
                            throw new InvalidValue('Could not resolve ' . $referencePath . '@' . $this->getResolutionScope() . ': ' . $folder);
160
                        }
161
                    }
162
                    $ref->setData($branch);
163
                }
164
            } else {
165
                if ($url !== $this->url) {
166
                    $rootResolver = $this->rootResolver ? $this->rootResolver : $this;
167
                    $refResolver = &$rootResolver->remoteRefResolvers[$url];
168
                    if (null === $refResolver) {
169
                        $rootData = $rootResolver->getRefProvider()->getSchemaData($url);
170
                        $refResolver = new RefResolver($rootData);
171
                        $refResolver->rootResolver = $rootResolver;
172
                        $refResolver->refProvider = $this->refProvider;
173
                        $refResolver->url = $url;
174
                        $rootResolver->setResolutionScope($url);
175
                    }
176
                }
177
178
                $ref = $refResolver->resolveReference($refLocalPath);
179
            }
180
        }
181
182
        return $ref;
183
    }
184
185
186
}