|
1
|
|
|
<?php |
|
2
|
|
|
/* |
|
3
|
|
|
* This file is part of the KleijnWeb\SwaggerBundle package. |
|
4
|
|
|
* |
|
5
|
|
|
* For the full copyright and license information, please view the LICENSE |
|
6
|
|
|
* file that was distributed with this source code. |
|
7
|
|
|
*/ |
|
8
|
|
|
|
|
9
|
|
|
namespace KleijnWeb\SwaggerBundle\Document; |
|
10
|
|
|
|
|
11
|
|
|
use KleijnWeb\SwaggerBundle\Document\Exception\ResourceNotReadableException; |
|
12
|
|
|
use KleijnWeb\SwaggerBundle\Document\Exception\InvalidReferenceException; |
|
13
|
|
|
|
|
14
|
|
|
/** |
|
15
|
|
|
* @author John Kleijn <[email protected]> |
|
16
|
|
|
*/ |
|
17
|
|
|
class RefResolver |
|
18
|
|
|
{ |
|
19
|
|
|
/** |
|
20
|
|
|
* @var object |
|
21
|
|
|
*/ |
|
22
|
|
|
private $definition; |
|
23
|
|
|
|
|
24
|
|
|
/** |
|
25
|
|
|
* @var string |
|
26
|
|
|
*/ |
|
27
|
|
|
private $uri; |
|
28
|
|
|
|
|
29
|
|
|
/** |
|
30
|
|
|
* @var string |
|
31
|
|
|
*/ |
|
32
|
|
|
private $directory; |
|
33
|
|
|
|
|
34
|
|
|
/** |
|
35
|
|
|
* @var YamlParser |
|
36
|
|
|
*/ |
|
37
|
|
|
private $loader; |
|
38
|
|
|
|
|
39
|
|
|
/** |
|
40
|
|
|
* @param object $definition |
|
41
|
|
|
* @param string $uri |
|
42
|
|
|
* @param Loader $loader |
|
43
|
|
|
*/ |
|
44
|
|
|
public function __construct($definition, $uri, Loader $loader = null) |
|
45
|
|
|
{ |
|
46
|
|
|
$this->definition = $definition; |
|
47
|
|
|
$uriSegs = $this->parseUri($uri); |
|
48
|
|
|
if (!$uriSegs['scheme']) { |
|
|
|
|
|
|
49
|
|
|
//$uri = realpath($uri); |
|
|
|
|
|
|
50
|
|
|
} |
|
51
|
|
|
$this->uri = $uri; |
|
52
|
|
|
$this->directory = dirname($this->uri); |
|
53
|
|
|
$this->loader = $loader ?: new Loader(); |
|
|
|
|
|
|
54
|
|
|
} |
|
55
|
|
|
|
|
56
|
|
|
/** |
|
57
|
|
|
* @return object |
|
58
|
|
|
*/ |
|
59
|
|
|
public function getDefinition() |
|
60
|
|
|
{ |
|
61
|
|
|
return $this->definition; |
|
62
|
|
|
} |
|
63
|
|
|
|
|
64
|
|
|
/** |
|
65
|
|
|
* Resolve all references |
|
66
|
|
|
* |
|
67
|
|
|
* @return object |
|
68
|
|
|
*/ |
|
69
|
|
|
public function resolve() |
|
70
|
|
|
{ |
|
71
|
|
|
$this->resolveRecursively($this->definition); |
|
72
|
|
|
|
|
73
|
|
|
return $this->definition; |
|
74
|
|
|
} |
|
75
|
|
|
|
|
76
|
|
|
/** |
|
77
|
|
|
* Revert to original state |
|
78
|
|
|
* |
|
79
|
|
|
* @return object |
|
80
|
|
|
*/ |
|
81
|
|
|
public function unresolve() |
|
82
|
|
|
{ |
|
83
|
|
|
$this->unresolveRecursively($this->definition); |
|
84
|
|
|
|
|
85
|
|
|
return $this->definition; |
|
86
|
|
|
} |
|
87
|
|
|
|
|
88
|
|
|
/** |
|
89
|
|
|
* @param object|array $current |
|
90
|
|
|
* @param object $document |
|
91
|
|
|
* @param string $uri |
|
92
|
|
|
* |
|
93
|
|
|
* @throws InvalidReferenceException |
|
94
|
|
|
* @throws ResourceNotReadableException |
|
95
|
|
|
*/ |
|
96
|
|
|
private function resolveRecursively(&$current, $document = null, $uri = null) |
|
97
|
|
|
{ |
|
98
|
|
|
$document = $document ?: $this->definition; |
|
99
|
|
|
$uri = $uri ?: $this->uri; |
|
100
|
|
|
|
|
101
|
|
|
if (is_array($current)) { |
|
102
|
|
|
foreach ($current as &$value) { |
|
103
|
|
|
$this->resolveRecursively($value, $document, $uri); |
|
104
|
|
|
} |
|
105
|
|
|
} elseif (is_object($current)) { |
|
106
|
|
|
if (property_exists($current, '$ref')) { |
|
107
|
|
|
$uri = $current->{'$ref'}; |
|
108
|
|
|
if ('#' === $uri[0]) { |
|
109
|
|
|
$current = $this->lookup($uri, $document); |
|
110
|
|
|
$this->resolveRecursively($current, $document, $uri); |
|
111
|
|
|
} else { |
|
112
|
|
|
$uriSegs = $this->parseUri($uri); |
|
113
|
|
|
$normalizedUri = $this->normalizeFileUri($uriSegs); |
|
114
|
|
|
$externalDocument = $this->loadExternal($normalizedUri); |
|
115
|
|
|
$current = $this->lookup($uriSegs['fragment'], $externalDocument, $normalizedUri); |
|
116
|
|
|
$this->resolveRecursively($current, $externalDocument, $normalizedUri); |
|
117
|
|
|
} |
|
118
|
|
|
if (is_object($current)) { |
|
119
|
|
|
$current->{'x-ref-id'} = $uri; |
|
120
|
|
|
} |
|
121
|
|
|
|
|
122
|
|
|
return; |
|
123
|
|
|
} |
|
124
|
|
|
foreach ($current as $propertyName => &$propertyValue) { |
|
125
|
|
|
$this->resolveRecursively($propertyValue, $document, $uri); |
|
126
|
|
|
} |
|
127
|
|
|
} |
|
128
|
|
|
} |
|
129
|
|
|
|
|
130
|
|
|
/** |
|
131
|
|
|
* @param object|array $current |
|
132
|
|
|
* @param object|array $parent |
|
133
|
|
|
* |
|
134
|
|
|
* @return void |
|
135
|
|
|
*/ |
|
136
|
|
|
private function unresolveRecursively(&$current, &$parent = null) |
|
137
|
|
|
{ |
|
138
|
|
|
foreach ($current as $key => &$value) { |
|
139
|
|
|
if ($value !== null && !is_scalar($value)) { |
|
140
|
|
|
$this->unresolveRecursively($value, $current); |
|
141
|
|
|
} |
|
142
|
|
|
if ($key === 'x-ref-id') { |
|
143
|
|
|
$parent = (object)['$ref' => $value]; |
|
144
|
|
|
} |
|
145
|
|
|
} |
|
146
|
|
|
} |
|
147
|
|
|
|
|
148
|
|
|
/** |
|
149
|
|
|
* @param string $path |
|
150
|
|
|
* @param object $document |
|
151
|
|
|
* @param string $uri |
|
152
|
|
|
* |
|
153
|
|
|
* @return mixed |
|
154
|
|
|
* @throws InvalidReferenceException |
|
155
|
|
|
*/ |
|
156
|
|
|
private function lookup($path, $document, $uri = null) |
|
157
|
|
|
{ |
|
158
|
|
|
$target = $this->lookupRecursively( |
|
159
|
|
|
explode('/', trim($path, '/#')), |
|
160
|
|
|
$document |
|
161
|
|
|
); |
|
162
|
|
|
if (!$target) { |
|
163
|
|
|
throw new InvalidReferenceException( |
|
164
|
|
|
"Target '$path' does not exist'" . ($uri ? " at '$uri''" : '') |
|
165
|
|
|
); |
|
166
|
|
|
} |
|
167
|
|
|
|
|
168
|
|
|
return $target; |
|
169
|
|
|
} |
|
170
|
|
|
|
|
171
|
|
|
/** |
|
172
|
|
|
* @param array $segments |
|
173
|
|
|
* @param object $context |
|
174
|
|
|
* |
|
175
|
|
|
* @return mixed |
|
176
|
|
|
*/ |
|
177
|
|
|
private function lookupRecursively(array $segments, $context) |
|
178
|
|
|
{ |
|
179
|
|
|
$segment = str_replace(['~0', '~1'], ['~', '/'], array_shift($segments)); |
|
180
|
|
|
if (property_exists($context, $segment)) { |
|
181
|
|
|
if (!count($segments)) { |
|
182
|
|
|
return $context->$segment; |
|
183
|
|
|
} |
|
184
|
|
|
|
|
185
|
|
|
return $this->lookupRecursively($segments, $context->$segment); |
|
186
|
|
|
} |
|
187
|
|
|
|
|
188
|
|
|
return null; |
|
189
|
|
|
} |
|
190
|
|
|
|
|
191
|
|
|
/** |
|
192
|
|
|
* @param string $fileUrl |
|
193
|
|
|
* |
|
194
|
|
|
* @return object |
|
195
|
|
|
*/ |
|
196
|
|
|
private function loadExternal($fileUrl) |
|
197
|
|
|
{ |
|
198
|
|
|
return $this->loader->load($fileUrl); |
|
|
|
|
|
|
199
|
|
|
} |
|
200
|
|
|
|
|
201
|
|
|
/** |
|
202
|
|
|
* @param array $uriSegs |
|
203
|
|
|
* |
|
204
|
|
|
* @return string |
|
205
|
|
|
*/ |
|
206
|
|
|
private function normalizeFileUri(array $uriSegs) |
|
207
|
|
|
{ |
|
208
|
|
|
$path = $uriSegs['path']; |
|
209
|
|
|
$auth = !$uriSegs['user'] ? '' : "{$uriSegs['user']}:{$uriSegs['pass']}@"; |
|
210
|
|
|
$query = !$uriSegs['query'] ? '' : "?{$uriSegs['query']}"; |
|
211
|
|
|
$port = !$uriSegs['port'] ? '' : ":{$uriSegs['port']}"; |
|
212
|
|
|
$host = !$uriSegs['host'] ? '' : "{$uriSegs['scheme']}://$auth{$uriSegs['host']}{$port}"; |
|
213
|
|
|
|
|
214
|
|
|
if (substr($path, 0, 1) !== '/') { |
|
215
|
|
|
$path = "$this->directory/$path"; |
|
216
|
|
|
if (substr($this->directory, 0, 1) === '/') { |
|
217
|
|
|
//Assume working directory is web root |
|
218
|
|
|
$path = ltrim($path, '/'); |
|
219
|
|
|
} |
|
220
|
|
|
} |
|
221
|
|
|
|
|
222
|
|
|
return "{$host}{$path}{$query}"; |
|
223
|
|
|
} |
|
224
|
|
|
|
|
225
|
|
|
/** |
|
226
|
|
|
* @param string $uri |
|
227
|
|
|
* |
|
228
|
|
|
* @return array |
|
229
|
|
|
*/ |
|
230
|
|
|
private function parseUri($uri) |
|
231
|
|
|
{ |
|
232
|
|
|
$defaults = [ |
|
233
|
|
|
'scheme' => '', |
|
234
|
|
|
'host' => '', |
|
235
|
|
|
'port' => '', |
|
236
|
|
|
'user' => '', |
|
237
|
|
|
'pass' => '', |
|
238
|
|
|
'path' => '', |
|
239
|
|
|
'query' => '', |
|
240
|
|
|
'fragment' => '' |
|
241
|
|
|
]; |
|
242
|
|
|
|
|
243
|
|
|
if (0 === strpos($uri, 'file://')) { |
|
244
|
|
|
// parse_url botches this up |
|
245
|
|
|
preg_match('@file://(?P<path>[^#]*)(?P<fragment>#.*)?@', $uri, $matches); |
|
246
|
|
|
|
|
247
|
|
|
return array_merge($defaults, array_intersect_key($matches, $defaults)); |
|
248
|
|
|
} |
|
249
|
|
|
|
|
250
|
|
|
return array_merge($defaults, array_intersect_key(parse_url($uri), $defaults)); |
|
251
|
|
|
} |
|
252
|
|
|
} |
|
253
|
|
|
|
This check looks for the bodies of
ifstatements 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
ifbodies can be removed. If you have an empty if but statements in theelsebranch, consider inverting the condition.could be turned into
This is much more concise to read.