]> scripts.mit.edu Git - autoinstalls/mediawiki.git/blob - vendor/justinrainbow/json-schema/src/JsonSchema/Uri/UriRetriever.php
MediaWiki 1.30.2
[autoinstalls/mediawiki.git] / vendor / justinrainbow / json-schema / src / JsonSchema / Uri / UriRetriever.php
1 <?php
2
3 /*
4  * This file is part of the JsonSchema package.
5  *
6  * For the full copyright and license information, please view the LICENSE
7  * file that was distributed with this source code.
8  */
9
10 namespace JsonSchema\Uri;
11
12 use JsonSchema\Exception\InvalidSchemaMediaTypeException;
13 use JsonSchema\Exception\JsonDecodingException;
14 use JsonSchema\Exception\ResourceNotFoundException;
15 use JsonSchema\Uri\Retrievers\FileGetContents;
16 use JsonSchema\Uri\Retrievers\UriRetrieverInterface;
17 use JsonSchema\UriRetrieverInterface as BaseUriRetrieverInterface;
18 use JsonSchema\Validator;
19
20 /**
21  * Retrieves JSON Schema URIs
22  *
23  * @author Tyler Akins <fidian@rumkin.com>
24  */
25 class UriRetriever implements BaseUriRetrieverInterface
26 {
27     /**
28      * @var array Map of URL translations
29      */
30     protected $translationMap = array(
31         // use local copies of the spec schemas
32         '|^https?://json-schema.org/draft-(0[34])/schema#?|' => 'package://dist/schema/json-schema-draft-$1.json'
33     );
34
35     /**
36      * @var null|UriRetrieverInterface
37      */
38     protected $uriRetriever = null;
39
40     /**
41      * @var array|object[]
42      *
43      * @see loadSchema
44      */
45     private $schemaCache = array();
46
47     /**
48      * Guarantee the correct media type was encountered
49      *
50      * @param UriRetrieverInterface $uriRetriever
51      * @param string                $uri
52      *
53      * @return bool|void
54      */
55     public function confirmMediaType($uriRetriever, $uri)
56     {
57         $contentType = $uriRetriever->getContentType();
58
59         if (is_null($contentType)) {
60             // Well, we didn't get an invalid one
61             return;
62         }
63
64         if (in_array($contentType, array(Validator::SCHEMA_MEDIA_TYPE, 'application/json'))) {
65             return;
66         }
67
68         if (substr($uri, 0, 23) == 'http://json-schema.org/') {
69             //HACK; they deliver broken content types
70             return true;
71         }
72
73         throw new InvalidSchemaMediaTypeException(sprintf('Media type %s expected', Validator::SCHEMA_MEDIA_TYPE));
74     }
75
76     /**
77      * Get a URI Retriever
78      *
79      * If none is specified, sets a default FileGetContents retriever and
80      * returns that object.
81      *
82      * @return UriRetrieverInterface
83      */
84     public function getUriRetriever()
85     {
86         if (is_null($this->uriRetriever)) {
87             $this->setUriRetriever(new FileGetContents());
88         }
89
90         return $this->uriRetriever;
91     }
92
93     /**
94      * Resolve a schema based on pointer
95      *
96      * URIs can have a fragment at the end in the format of
97      * #/path/to/object and we are to look up the 'path' property of
98      * the first object then the 'to' and 'object' properties.
99      *
100      * @param object $jsonSchema JSON Schema contents
101      * @param string $uri        JSON Schema URI
102      *
103      * @throws ResourceNotFoundException
104      *
105      * @return object JSON Schema after walking down the fragment pieces
106      */
107     public function resolvePointer($jsonSchema, $uri)
108     {
109         $resolver = new UriResolver();
110         $parsed = $resolver->parse($uri);
111         if (empty($parsed['fragment'])) {
112             return $jsonSchema;
113         }
114
115         $path = explode('/', $parsed['fragment']);
116         while ($path) {
117             $pathElement = array_shift($path);
118             if (!empty($pathElement)) {
119                 $pathElement = str_replace('~1', '/', $pathElement);
120                 $pathElement = str_replace('~0', '~', $pathElement);
121                 if (!empty($jsonSchema->$pathElement)) {
122                     $jsonSchema = $jsonSchema->$pathElement;
123                 } else {
124                     throw new ResourceNotFoundException(
125                         'Fragment "' . $parsed['fragment'] . '" not found'
126                         . ' in ' . $uri
127                     );
128                 }
129
130                 if (!is_object($jsonSchema)) {
131                     throw new ResourceNotFoundException(
132                         'Fragment part "' . $pathElement . '" is no object '
133                         . ' in ' . $uri
134                     );
135                 }
136             }
137         }
138
139         return $jsonSchema;
140     }
141
142     /**
143      * {@inheritdoc}
144      */
145     public function retrieve($uri, $baseUri = null, $translate = true)
146     {
147         $resolver = new UriResolver();
148         $resolvedUri = $fetchUri = $resolver->resolve($uri, $baseUri);
149
150         //fetch URL without #fragment
151         $arParts = $resolver->parse($resolvedUri);
152         if (isset($arParts['fragment'])) {
153             unset($arParts['fragment']);
154             $fetchUri = $resolver->generate($arParts);
155         }
156
157         // apply URI translations
158         if ($translate) {
159             $fetchUri = $this->translate($fetchUri);
160         }
161
162         $jsonSchema = $this->loadSchema($fetchUri);
163
164         // Use the JSON pointer if specified
165         $jsonSchema = $this->resolvePointer($jsonSchema, $resolvedUri);
166
167         if ($jsonSchema instanceof \stdClass) {
168             $jsonSchema->id = $resolvedUri;
169         }
170
171         return $jsonSchema;
172     }
173
174     /**
175      * Fetch a schema from the given URI, json-decode it and return it.
176      * Caches schema objects.
177      *
178      * @param string $fetchUri Absolute URI
179      *
180      * @return object JSON schema object
181      */
182     protected function loadSchema($fetchUri)
183     {
184         if (isset($this->schemaCache[$fetchUri])) {
185             return $this->schemaCache[$fetchUri];
186         }
187
188         $uriRetriever = $this->getUriRetriever();
189         $contents = $this->uriRetriever->retrieve($fetchUri);
190         $this->confirmMediaType($uriRetriever, $fetchUri);
191         $jsonSchema = json_decode($contents);
192
193         if (JSON_ERROR_NONE < $error = json_last_error()) {
194             throw new JsonDecodingException($error);
195         }
196
197         $this->schemaCache[$fetchUri] = $jsonSchema;
198
199         return $jsonSchema;
200     }
201
202     /**
203      * Set the URI Retriever
204      *
205      * @param UriRetrieverInterface $uriRetriever
206      *
207      * @return $this for chaining
208      */
209     public function setUriRetriever(UriRetrieverInterface $uriRetriever)
210     {
211         $this->uriRetriever = $uriRetriever;
212
213         return $this;
214     }
215
216     /**
217      * Parses a URI into five main components
218      *
219      * @param string $uri
220      *
221      * @return array
222      */
223     public function parse($uri)
224     {
225         preg_match('|^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?|', $uri, $match);
226
227         $components = array();
228         if (5 < count($match)) {
229             $components =  array(
230                 'scheme'    => $match[2],
231                 'authority' => $match[4],
232                 'path'      => $match[5]
233             );
234         }
235
236         if (7 < count($match)) {
237             $components['query'] = $match[7];
238         }
239
240         if (9 < count($match)) {
241             $components['fragment'] = $match[9];
242         }
243
244         return $components;
245     }
246
247     /**
248      * Builds a URI based on n array with the main components
249      *
250      * @param array $components
251      *
252      * @return string
253      */
254     public function generate(array $components)
255     {
256         $uri = $components['scheme'] . '://'
257              . $components['authority']
258              . $components['path'];
259
260         if (array_key_exists('query', $components)) {
261             $uri .= $components['query'];
262         }
263
264         if (array_key_exists('fragment', $components)) {
265             $uri .= $components['fragment'];
266         }
267
268         return $uri;
269     }
270
271     /**
272      * Resolves a URI
273      *
274      * @param string $uri     Absolute or relative
275      * @param string $baseUri Optional base URI
276      *
277      * @return string
278      */
279     public function resolve($uri, $baseUri = null)
280     {
281         $components = $this->parse($uri);
282         $path = $components['path'];
283
284         if ((array_key_exists('scheme', $components)) && ('http' === $components['scheme'])) {
285             return $uri;
286         }
287
288         $baseComponents = $this->parse($baseUri);
289         $basePath = $baseComponents['path'];
290
291         $baseComponents['path'] = UriResolver::combineRelativePathWithBasePath($path, $basePath);
292
293         return $this->generate($baseComponents);
294     }
295
296     /**
297      * @param string $uri
298      *
299      * @return bool
300      */
301     public function isValid($uri)
302     {
303         $components = $this->parse($uri);
304
305         return !empty($components);
306     }
307
308     /**
309      * Set a URL translation rule
310      */
311     public function setTranslation($from, $to)
312     {
313         $this->translationMap[$from] = $to;
314     }
315
316     /**
317      * Apply URI translation rules
318      */
319     public function translate($uri)
320     {
321         foreach ($this->translationMap as $from => $to) {
322             $uri = preg_replace($from, $to, $uri);
323         }
324
325         // translate references to local files within the json-schema package
326         $uri = preg_replace('|^package://|', sprintf('file://%s/', realpath(__DIR__ . '/../../..')), $uri);
327
328         return $uri;
329     }
330 }