4 * This file is part of the JsonSchema package.
6 * For the full copyright and license information, please view the LICENSE
7 * file that was distributed with this source code.
10 namespace JsonSchema\Uri;
12 use JsonSchema\Exception\UriResolverException;
13 use JsonSchema\UriResolverInterface;
16 * Resolves JSON Schema URIs
18 * @author Sander Coolen <sander@jibber.nl>
20 class UriResolver implements UriResolverInterface
23 * Parses a URI into five main components
29 public function parse($uri)
31 preg_match('|^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?|', $uri, $match);
33 $components = array();
34 if (5 < count($match)) {
36 'scheme' => $match[2],
37 'authority' => $match[4],
41 if (7 < count($match)) {
42 $components['query'] = $match[7];
44 if (9 < count($match)) {
45 $components['fragment'] = $match[9];
52 * Builds a URI based on n array with the main components
54 * @param array $components
58 public function generate(array $components)
60 $uri = $components['scheme'] . '://'
61 . $components['authority']
62 . $components['path'];
64 if (array_key_exists('query', $components)) {
65 $uri .= $components['query'];
67 if (array_key_exists('fragment', $components)) {
68 $uri .= '#' . $components['fragment'];
77 public function resolve($uri, $baseUri = null)
83 $components = $this->parse($uri);
84 $path = $components['path'];
86 if (!empty($components['scheme'])) {
89 $baseComponents = $this->parse($baseUri);
90 $basePath = $baseComponents['path'];
92 $baseComponents['path'] = self::combineRelativePathWithBasePath($path, $basePath);
93 if (isset($components['fragment'])) {
94 $baseComponents['fragment'] = $components['fragment'];
97 return $this->generate($baseComponents);
101 * Tries to glue a relative path onto an absolute one
103 * @param string $relativePath
104 * @param string $basePath
106 * @throws UriResolverException
108 * @return string Merged path
110 public static function combineRelativePathWithBasePath($relativePath, $basePath)
112 $relativePath = self::normalizePath($relativePath);
113 if ($relativePath == '') {
116 if ($relativePath[0] == '/') {
117 return $relativePath;
120 $basePathSegments = explode('/', $basePath);
122 preg_match('|^/?(\.\./(?:\./)*)*|', $relativePath, $match);
123 $numLevelUp = strlen($match[0]) /3 + 1;
124 if ($numLevelUp >= count($basePathSegments)) {
125 throw new UriResolverException(sprintf("Unable to resolve URI '%s' from base '%s'", $relativePath, $basePath));
128 $basePathSegments = array_slice($basePathSegments, 0, -$numLevelUp);
129 $path = preg_replace('|^/?(\.\./(\./)*)*|', '', $relativePath);
131 return implode('/', $basePathSegments) . '/' . $path;
135 * Normalizes a URI path component by removing dot-slash and double slashes
137 * @param string $path
141 private static function normalizePath($path)
143 $path = preg_replace('|((?<!\.)\./)*|', '', $path);
144 $path = preg_replace('|//|', '/', $path);
154 public function isValid($uri)
156 $components = $this->parse($uri);
158 return !empty($components);