Passed
Pull Request — master (#24)
by Viacheslav
03:13
created

RefResolver::resolveReference()   C

Complexity

Conditions 20
Paths 104

Size

Total Lines 74
Code Lines 47

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 40
CRAP Score 20.3004

Importance

Changes 0
Metric Value
dl 0
loc 74
ccs 40
cts 44
cp 0.9091
rs 5.2374
c 0
b 0
f 0
cc 20
eloc 47
nc 104
nop 1
crap 20.3004

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\JsonSchema;
4
5
use PhpLang\ScopeExit;
6
use Swaggest\JsonDiff\JsonPointer;
7
use Swaggest\JsonSchema\Constraint\Ref;
8
use Swaggest\JsonSchema\RemoteRef\BasicFetcher;
9
10
class RefResolver
11
{
12
    public $resolutionScope = '';
13
    public $url;
14
    /** @var RefResolver */
15
    private $rootResolver;
16
17
    /**
18
     * @param mixed $resolutionScope
19
     * @return string previous value
20
     */
21 573
    public function setResolutionScope($resolutionScope)
22
    {
23 573
        $rootResolver = $this->rootResolver ? $this->rootResolver : $this;
24 573
        if ($resolutionScope === $rootResolver->resolutionScope) {
25 573
            return $resolutionScope;
26
        }
27 443
        $prev = $rootResolver->resolutionScope;
28 443
        $rootResolver->resolutionScope = $resolutionScope;
29 443
        return $prev;
30
    }
31
32
    /**
33
     * @return string
34
     */
35 572
    public function getResolutionScope()
36
    {
37 572
        $rootResolver = $this->rootResolver ? $this->rootResolver : $this;
38 572
        return $rootResolver->resolutionScope;
39
    }
40
41
42 401
    public function updateResolutionScope($id)
43
    {
44 401
        $id = rtrim($id, '#');
45 401
        $rootResolver = $this->rootResolver ? $this->rootResolver : $this;
46 401
        if (strpos($id, '://') !== false) {
47 401
            $prev = $rootResolver->setResolutionScope($id);
48
        } else {
49 360
            $prev = $rootResolver->setResolutionScope(Helper::resolveURI($rootResolver->resolutionScope, $id));
50
        }
51
52 401
        return $prev;
53
    }
54
55 395
    public function setupResolutionScope($id, $data)
56
    {
57 395
        $rootResolver = $this->rootResolver ? $this->rootResolver : $this;
58
59 395
        $prev = $rootResolver->updateResolutionScope($id);
60
61 395
        $refParts = explode('#', $rootResolver->resolutionScope, 2);
62
63 395
        if ($refParts[0]) { // external uri
64 395
            $resolver = &$rootResolver->remoteRefResolvers[$refParts[0]];
65 395
            if ($resolver === null) {
66 395
                $resolver = new RefResolver();
67 395
                $resolver->rootResolver = $rootResolver;
68 395
                $resolver->url = $refParts[0];
69 395
                $this->remoteRefResolvers[$refParts[0]] = $resolver;
70
            }
71
        } else { // local uri
72 12
            $resolver = $this;
73
        }
74
75 395
        if (empty($refParts[1])) {
76 395
            $resolver->rootData = $data;
77
        } else {
78 12
            $refPath = '#' . $refParts[1];
79 12
            $resolver->refs[$refPath] = new Ref($refPath, $data);
80
        }
81
82 395
        return $prev;
83
    }
84
85
    private $rootData;
86
87
    /** @var Ref[] */
88
    private $refs = array();
89
90
    /** @var RefResolver[]|null[] */
91
    private $remoteRefResolvers = array();
92
93
    /** @var RemoteRefProvider */
94
    private $refProvider;
95
96
    /**
97
     * RefResolver constructor.
98
     * @param JsonSchema $rootData
99
     */
100 3140
    public function __construct($rootData = null)
101
    {
102 3140
        $this->rootData = $rootData;
103 3140
    }
104
105 1694
    public function setRootData($rootData)
106
    {
107 1694
        $this->rootData = $rootData;
108 1694
        return $this;
109
    }
110
111
112 3080
    public function setRemoteRefProvider(RemoteRefProvider $provider)
113
    {
114 3080
        $this->refProvider = $provider;
115 3080
        return $this;
116
    }
117
118 108
    private function getRefProvider()
119
    {
120 108
        if (null === $this->refProvider) {
121
            $this->refProvider = new BasicFetcher();
122
        }
123 108
        return $this->refProvider;
124
    }
125
126
    /**
127
     * @param string $referencePath
128
     * @return Ref
129
     * @throws InvalidValue
130
     */
131 384
    public function resolveReference($referencePath)
132
    {
133 384
        if ($this->resolutionScope) {
134 174
            $referencePath = Helper::resolveURI($this->resolutionScope, $referencePath);
135
        }
136
137 384
        $refParts = explode('#', $referencePath, 2);
138 384
        $url = rtrim($refParts[0], '#');
139 384
        $refLocalPath = isset($refParts[1]) ? '#' . $refParts[1] : '#';
140
141 384
        if ($url === $this->url) {
142
            $referencePath = $refLocalPath;
143
        }
144
145
        /** @var null|Ref $ref */
146 384
        $ref = &$this->refs[$referencePath];
147
148 384
        $refResolver = $this;
149
150 384
        if (null === $ref) {
151 378
            if ($referencePath[0] === '#') {
152 372
                if ($referencePath === '#') {
153 144
                    $ref = new Ref($referencePath, $refResolver->rootData);
154
                } else {
155 296
                    $ref = new Ref($referencePath);
156
                    try {
157 296
                        $path = JsonPointer::splitPath($referencePath);
158
                    } catch (\Swaggest\JsonDiff\Exception $e) {
159
                        throw new InvalidValue('Invalid reference: ' . $referencePath . ', ' . $e->getMessage());
160
                    }
161
162
                    /** @var JsonSchema $branch */
163 296
                    $branch = &$refResolver->rootData;
164 296
                    while (!empty($path)) {
165 296
                        if (isset($branch->{Schema::PROP_ID_D4}) && is_string($branch->{Schema::PROP_ID_D4})) {
166 20
                            $refResolver->updateResolutionScope($branch->{Schema::PROP_ID_D4});
167
                        }
168 296
                        if (isset($branch->{Schema::PROP_ID}) && is_string($branch->{Schema::PROP_ID})) {
169 75
                            $refResolver->updateResolutionScope($branch->{Schema::PROP_ID});
170
                        }
171
172 296
                        $folder = array_shift($path);
173
174 296
                        if ($branch instanceof \stdClass && isset($branch->$folder)) {
175 296
                            $branch = &$branch->$folder;
176 12
                        } elseif (is_array($branch) && isset($branch[$folder])) {
177 12
                            $branch = &$branch[$folder];
178
                        } else {
179
                            throw new InvalidValue('Could not resolve ' . $referencePath . '@' . $this->getResolutionScope() . ': ' . $folder);
180
                        }
181
                    }
182 372
                    $ref->setData($branch);
183
                }
184
            } else {
185 200
                if ($url !== $this->url) {
186 200
                    $rootResolver = $this->rootResolver ? $this->rootResolver : $this;
187
                    /** @var null|RefResolver $refResolver */
188 200
                    $refResolver = &$rootResolver->remoteRefResolvers[$url];
189 200
                    $this->setResolutionScope($url);
190 200
                    if (null === $refResolver) {
191 108
                        $rootData = $rootResolver->getRefProvider()->getSchemaData($url);
192 108
                        $refResolver = new RefResolver($rootData);
193 108
                        $refResolver->rootResolver = $rootResolver;
194 108
                        $refResolver->refProvider = $this->refProvider;
195 108
                        $refResolver->url = $url;
196 108
                        $rootResolver->setResolutionScope($url);
197
                    }
198
                }
199
200 200
                $ref = $refResolver->resolveReference($refLocalPath);
201
            }
202
        }
203
204 384
        return $ref;
205
    }
206
207
208
    /**
209
     * @param mixed $data
210
     * @param Context $options
211
     * @param int $nestingLevel
212
     * @throws Exception
213
     */
214 3139
    public function preProcessReferences($data, Context $options, $nestingLevel = 0)
215
    {
216 3139
        if ($nestingLevel > 200) {
217
            throw new Exception('Too deep nesting level', Exception::DEEP_NESTING);
218
        }
219 3139
        if (is_array($data)) {
220 1325
            foreach ($data as $key => $item) {
221 1325
                $this->preProcessReferences($item, $options, $nestingLevel + 1);
222
            }
223 3137
        } elseif ($data instanceof \stdClass) {
224
            /** @var JsonSchema $data */
225
            if (
226 3121
                isset($data->{Schema::PROP_ID_D4})
227 3121
                && is_string($data->{Schema::PROP_ID_D4})
228 3121
                && (($options->version === Schema::VERSION_AUTO) || $options->version === Schema::VERSION_DRAFT_04)
229
            ) {
230 349
                $prev = $this->setupResolutionScope($data->{Schema::PROP_ID_D4}, $data);
231
                /** @noinspection PhpUnusedLocalVariableInspection */
232
                $_ = new ScopeExit(function () use ($prev) {
0 ignored issues
show
Unused Code introduced by
The assignment to $_ is dead and can be removed.
Loading history...
233 349
                    $this->setResolutionScope($prev);
234 349
                });
235
            }
236
237 3121
            if (isset($data->{Schema::PROP_ID})
238 3121
                && is_string($data->{Schema::PROP_ID})
239 3121
                && (($options->version === Schema::VERSION_AUTO) || $options->version >= Schema::VERSION_DRAFT_06)
240
            ) {
241 365
                $prev = $this->setupResolutionScope($data->{Schema::PROP_ID}, $data);
242
                /** @noinspection PhpUnusedLocalVariableInspection */
243 365
                $_ = new ScopeExit(function () use ($prev) {
244 365
                    $this->setResolutionScope($prev);
245 365
                });
246
            }
247
248
249 3121
            foreach ((array)$data as $key => $value) {
250 3119
                $this->preProcessReferences($value, $options, $nestingLevel + 1);
251
            }
252
        }
253 3139
    }
254
255
256
}