Passed
Pull Request — master (#61)
by
unknown
03:16
created

RefResolver::resolveReference()   D

Complexity

Conditions 21
Paths 104

Size

Total Lines 75
Code Lines 48

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 41
CRAP Score 21.3098

Importance

Changes 0
Metric Value
eloc 48
dl 0
loc 75
ccs 41
cts 45
cp 0.9111
rs 4.1333
c 0
b 0
f 0
cc 21
nc 104
nop 1
crap 21.3098

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 614
    public function setResolutionScope($resolutionScope)
22
    {
23 614
        $rootResolver = $this->rootResolver ? $this->rootResolver : $this;
24 614
        if ($resolutionScope === $rootResolver->resolutionScope) {
25 614
            return $resolutionScope;
26
        }
27 476
        $prev = $rootResolver->resolutionScope;
28 476
        $rootResolver->resolutionScope = $resolutionScope;
29 476
        return $prev;
30
    }
31
32
    /**
33
     * @return string
34
     */
35 613
    public function getResolutionScope()
36
    {
37 613
        $rootResolver = $this->rootResolver ? $this->rootResolver : $this;
38 613
        return $rootResolver->resolutionScope;
39
    }
40
41
42
    /**
43
     * @param string $id
44
     * @return string
45
     */
46 430
    public function updateResolutionScope($id)
47
    {
48 430
        $id = rtrim($id, '#'); // safe to trim because # in hashCode must be urlencoded to %23
49 430
        $rootResolver = $this->rootResolver ? $this->rootResolver : $this;
50 430
        if (strpos($id, '://') !== false) {
51 430
            $prev = $rootResolver->setResolutionScope($id);
52
        } else {
53 378
            $prev = $rootResolver->setResolutionScope(Helper::resolveURI($rootResolver->resolutionScope, $id));
54
        }
55
56 430
        return $prev;
57
    }
58
59 421
    public function setupResolutionScope($id, $data)
60
    {
61 421
        $rootResolver = $this->rootResolver ? $this->rootResolver : $this;
62
63 421
        $prev = $rootResolver->updateResolutionScope($id);
64
65 421
        $refParts = explode('#', $rootResolver->resolutionScope, 2);
66
67 421
        if ($refParts[0]) { // external uri
68 421
            $resolver = &$rootResolver->remoteRefResolvers[$refParts[0]];
69 421
            if ($resolver === null) {
70 421
                $resolver = new RefResolver();
71 421
                $resolver->rootResolver = $rootResolver;
72 421
                $resolver->url = $refParts[0];
73 421
                $this->remoteRefResolvers[$refParts[0]] = $resolver;
74
            }
75
        } else { // local uri
76 6
            $resolver = $this;
77
        }
78
79 421
        if (empty($refParts[1])) {
80 421
            $resolver->rootData = $data;
81
        } else {
82 12
            $refPath = '#' . $refParts[1];
83 12
            $resolver->refs[$refPath] = new Ref($refPath, $data);
84
        }
85
86 421
        return $prev;
87
    }
88
89
    private $rootData;
90
91
    /** @var Ref[] */
92
    private $refs = array();
93
94
    /** @var RefResolver[]|null[] */
95
    private $remoteRefResolvers = array();
96
97
    /** @var RemoteRefProvider */
98
    private $refProvider;
99
100
    /**
101
     * RefResolver constructor.
102
     * @param JsonSchema $rootData
103
     */
104 3265
    public function __construct($rootData = null)
105
    {
106 3265
        $this->rootData = $rootData;
107 3265
    }
108
109 1766
    public function setRootData($rootData)
110
    {
111 1766
        $this->rootData = $rootData;
112 1766
        return $this;
113
    }
114
115
116 3192
    public function setRemoteRefProvider(RemoteRefProvider $provider)
117
    {
118 3192
        $this->refProvider = $provider;
119 3192
        return $this;
120
    }
121
122 127
    private function getRefProvider()
123
    {
124 127
        if (null === $this->refProvider) {
125 5
            $this->refProvider = new BasicFetcher();
126
        }
127 127
        return $this->refProvider;
128
    }
129
130
    /**
131
     * @param string $referencePath
132
     * @return Ref
133
     * @throws InvalidValue
134
     */
135 411
    public function resolveReference($referencePath)
136
    {
137 411
        if ($this->resolutionScope) {
138 186
            $referencePath = Helper::resolveURI($this->resolutionScope, $referencePath);
139
        }
140
141 411
        $refParts = explode('#', $referencePath, 2);
142 411
        $url = rtrim($refParts[0], '#');
143 411
        $refLocalPath = isset($refParts[1]) ? '#' . $refParts[1] : '#';
144
145 411
        if ($url === $this->url) {
146
            $referencePath = $refLocalPath;
147
        }
148
149
        /** @var null|Ref $ref */
150 411
        $ref = &$this->refs[$referencePath];
151
152 411
        $refResolver = $this;
153
154 411
        if (null === $ref) {
155 405
            if ($referencePath[0] === '#') {
156 399
                if ($referencePath === '#') {
157 156
                    $ref = new Ref($referencePath, $refResolver->rootData);
158
                } else {
159 318
                    $ref = new Ref($referencePath);
160
                    try {
161 318
                        $path = JsonPointer::splitPath($referencePath);
162
                    } catch (\Swaggest\JsonDiff\Exception $e) {
163
                        throw new InvalidValue('Invalid reference: ' . $referencePath . ', ' . $e->getMessage());
164
                    }
165
166
                    /** @var JsonSchema $branch */
167 318
                    $branch = &$refResolver->rootData;
168 318
                    while (!empty($path)) {
169 318
                        if (isset($branch->{Schema::PROP_ID_D4}) && is_string($branch->{Schema::PROP_ID_D4})) {
170 27
                            $refResolver->updateResolutionScope($branch->{Schema::PROP_ID_D4});
171
                        }
172 318
                        if (isset($branch->{Schema::PROP_ID}) && is_string($branch->{Schema::PROP_ID})) {
173 75
                            $refResolver->updateResolutionScope($branch->{Schema::PROP_ID});
174
                        }
175
176 318
                        $folder = array_shift($path);
177 318
                        if ($folder === ''){
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
178
                            // root element
179 318
                        } elseif ($branch instanceof \stdClass && isset($branch->$folder)) {
180 318
                            $branch = &$branch->$folder;
181 12
                        } elseif (is_array($branch) && isset($branch[$folder])) {
182 12
                            $branch = &$branch[$folder];
183
                        } else {
184
                            throw new InvalidValue('Could not resolve ' . $referencePath . '@' . $this->getResolutionScope() . ': ' . $folder);
185
                        }
186
                    }
187 399
                    $ref->setData($branch);
188
                }
189
            } else {
190 225
                if ($url !== $this->url) {
191 225
                    $rootResolver = $this->rootResolver ? $this->rootResolver : $this;
192
                    /** @var null|RefResolver $refResolver */
193 225
                    $refResolver = &$rootResolver->remoteRefResolvers[$url];
194 225
                    $this->setResolutionScope($url);
195 225
                    if (null === $refResolver) {
196 127
                        $rootData = $rootResolver->getRefProvider()->getSchemaData($url);
197 127
                        $refResolver = new RefResolver($rootData);
198 127
                        $refResolver->rootResolver = $rootResolver;
199 127
                        $refResolver->refProvider = $this->refProvider;
200 127
                        $refResolver->url = $url;
201 127
                        $rootResolver->setResolutionScope($url);
202
                    }
203
                }
204
205 225
                $ref = $refResolver->resolveReference($refLocalPath);
206
            }
207
        }
208
209 411
        return $ref;
210
    }
211
212
213
    /**
214
     * @param mixed $data
215
     * @param Context $options
216
     * @param int $nestingLevel
217
     * @throws Exception
218
     */
219 3264
    public function preProcessReferences($data, Context $options, $nestingLevel = 0)
220
    {
221 3264
        if ($nestingLevel > 200) {
222
            throw new Exception('Too deep nesting level', Exception::DEEP_NESTING);
223
        }
224 3264
        if (is_array($data)) {
225 1361
            foreach ($data as $key => $item) {
226 1361
                $this->preProcessReferences($item, $options, $nestingLevel + 1);
227
            }
228 3262
        } elseif ($data instanceof \stdClass) {
229
            /** @var JsonSchema $data */
230
            if (
231 3246
                isset($data->{Schema::PROP_ID_D4})
232 3246
                && is_string($data->{Schema::PROP_ID_D4})
233 3246
                && (($options->version === Schema::VERSION_AUTO) || $options->version === Schema::VERSION_DRAFT_04)
234
            ) {
235 375
                $prev = $this->setupResolutionScope($data->{Schema::PROP_ID_D4}, $data);
236
                /** @noinspection PhpUnusedLocalVariableInspection */
237
                $_ = new ScopeExit(function () use ($prev) {
0 ignored issues
show
Unused Code introduced by
The assignment to $_ is dead and can be removed.
Loading history...
238 375
                    $this->setResolutionScope($prev);
239 375
                });
240
            }
241
242 3246
            if (isset($data->{Schema::PROP_ID})
243 3246
                && is_string($data->{Schema::PROP_ID})
244 3246
                && (($options->version === Schema::VERSION_AUTO) || $options->version >= Schema::VERSION_DRAFT_06)
245
            ) {
246 379
                $prev = $this->setupResolutionScope($data->{Schema::PROP_ID}, $data);
247
                /** @noinspection PhpUnusedLocalVariableInspection */
248 379
                $_ = new ScopeExit(function () use ($prev) {
249 379
                    $this->setResolutionScope($prev);
250 379
                });
251
            }
252
253
254 3246
            foreach ((array)$data as $key => $value) {
255 3244
                $this->preProcessReferences($value, $options, $nestingLevel + 1);
256
            }
257
        }
258 3264
    }
259
260
261
}