Skip to content

Commit c307c99

Browse files
committed
references mayhem
1 parent 2e6a9c9 commit c307c99

File tree

8 files changed

+254
-48
lines changed

8 files changed

+254
-48
lines changed

src/Constraint/Ref.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,6 @@ public function __construct($ref, $data = null)
1616
/** @var mixed */
1717
private $data;
1818

19-
/** @var RefResolver */
20-
public $resolver;
21-
2219
/** @var mixed */
2320
private $imported;
2421
/** @var boolean */

src/Helper.php

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -28,31 +28,40 @@ public static function toPregPattern($jsonPattern)
2828
return $pattern;
2929
}
3030

31+
/**
32+
* @param $parent
33+
* @param $current
34+
* @return string
35+
* @todo getaway from zeroes
36+
*/
3137
public static function resolveURI($parent, $current)
3238
{
33-
if (false !== $pos = strpos($current, '://')) {
34-
if (strpos($current, '/') > $pos) {
35-
return $current;
36-
}
37-
}
38-
3939
if ($current === '') {
4040
return $parent;
4141
}
4242

43-
$result = $parent;
44-
if ($current[0] === '#') {
45-
if (false !== $pos = strpos($parent, '#')) {
46-
$result = substr($parent, 0, $pos) . $current;
47-
}
48-
} else {
49-
if (false !== $pos = strrpos($parent, '/')) {
50-
$result = substr($parent, 0, $pos + 1) . $current;
51-
}
43+
$parentParts = explode('#', $parent, 2);
44+
$currentParts = explode('#', $current, 2);
45+
46+
$resultParts = array($parentParts[0], '');
47+
if (isset($currentParts[1])) {
48+
$resultParts[1] = $currentParts[1];
5249
}
53-
if (false === strpos($result, '#')) {
54-
$result .= '#';
50+
51+
if (isset($currentParts[0]) && $currentParts[0]) {
52+
if (strpos($currentParts[0], '://')) {
53+
$resultParts[0] = $currentParts[0];
54+
} elseif ('/' === substr($currentParts[0], 0, 1)) {
55+
$resultParts[0] = $currentParts[0];
56+
if ($pos = strpos($parentParts[0], '://')) {
57+
$resultParts[0] = substr($parentParts[0], 0, strpos($parentParts[0], '/', $pos)) . $resultParts[0];
58+
}
59+
} elseif (false !== $pos = strrpos($parentParts[0], '/')) {
60+
$resultParts[0] = substr($parentParts[0], 0, $pos + 1) . $currentParts[0];
61+
}
5562
}
63+
64+
$result = $resultParts[0] . '#' . $resultParts[1];
5665
return $result;
5766
}
5867

src/ProcessingOptions.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class ProcessingOptions extends MagicMap
1010
/** @var DataPreProcessor */
1111
public $dataPreProcessor;
1212
/** @var RefResolver */
13-
protected $refResolver;
13+
public $refResolver;
1414

1515
/** @var RemoteRefProvider */
1616
public $remoteRefProvider;

src/RefResolver.php

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,50 @@ class RefResolver
99
{
1010
private $resolutionScope = '';
1111
private $url;
12+
/** @var RefResolver */
13+
private $rootResolver;
1214

1315
/**
1416
* @param mixed $resolutionScope
17+
* @return string previous value
1518
*/
1619
public function setResolutionScope($resolutionScope)
1720
{
18-
$this->resolutionScope = $resolutionScope;
21+
$rootResolver = $this->rootResolver ? $this->rootResolver : $this;
22+
$prev = $rootResolver->resolutionScope;
23+
$rootResolver->resolutionScope = $resolutionScope;
24+
return $prev;
1925
}
2026

2127
public function updateResolutionScope($id)
2228
{
23-
$prev = $this->resolutionScope;
24-
$this->resolutionScope = Helper::resolveURI($this->resolutionScope, $id);
29+
$rootResolver = $this->rootResolver ? $this->rootResolver : $this;
30+
$prev = $rootResolver->resolutionScope;
31+
if (strpos($id, '://') !== false) {
32+
$rootResolver->resolutionScope = $id;
33+
} else {
34+
$rootResolver->resolutionScope = Helper::resolveURI($rootResolver->resolutionScope, $id);
35+
}
36+
37+
return $prev;
38+
}
39+
40+
public function setupResolutionScope($id, $data)
41+
{
42+
$rootResolver = $this->rootResolver ? $this->rootResolver : $this;
43+
44+
$prev = $this->updateResolutionScope($id);
45+
46+
$refParts = explode('#', $this->resolutionScope, 2);
47+
if ($refParts[0] && empty($refParts[1])) {
48+
if (!isset($rootResolver->remoteRefResolvers[$refParts[0]])) {
49+
$resolver = new RefResolver($data);
50+
$resolver->rootResolver = $rootResolver;
51+
$resolver->url = $refParts[0];
52+
$this->remoteRefResolvers[$refParts[0]] = $resolver;
53+
}
54+
}
55+
2556
return $prev;
2657
}
2758

@@ -63,7 +94,6 @@ private function getRefProvider()
6394

6495
/**
6596
* @param $referencePath
66-
* @param string $resolutionScope
6797
* @return Ref
6898
* @throws \Exception
6999
*/
@@ -73,9 +103,8 @@ public function resolveReference($referencePath)
73103
$referencePath = Helper::resolveURI($this->resolutionScope, $referencePath);
74104
}
75105

76-
$refParts = explode('#', $referencePath);
77-
$url = Helper::resolveURI($this->resolutionScope, $refParts[0]);
78-
$url = rtrim($url, '#');
106+
$refParts = explode('#', $referencePath, 2);
107+
$url = rtrim($refParts[0], '#');
79108
$refLocalPath = isset($refParts[1]) ? '#' . $refParts[1] : '#';
80109

81110
if ($url === $this->url) {
@@ -90,10 +119,8 @@ public function resolveReference($referencePath)
90119
if ($referencePath[0] === '#') {
91120
if ($referencePath === '#') {
92121
$ref = new Ref($referencePath, $this->rootData);
93-
$ref->resolver = $this;
94122
} else {
95123
$ref = new Ref($referencePath);
96-
$ref->resolver = $this;
97124
$path = explode('/', trim($referencePath, '#/'));
98125
$branch = &$this->rootData;
99126
while (!empty($path)) {
@@ -116,10 +143,12 @@ public function resolveReference($referencePath)
116143
}
117144
} else {
118145
if ($url !== $this->url) {
119-
$refResolver = &$this->remoteRefResolvers[$url];
146+
$rootResolver = $this->rootResolver ? $this->rootResolver : $this;
147+
$refResolver = &$rootResolver->remoteRefResolvers[$url];
120148
if (null === $refResolver) {
121-
$rootData = $this->getRefProvider()->getSchemaData($url);
149+
$rootData = $rootResolver->getRefProvider()->getSchemaData($url);
122150
$refResolver = new RefResolver($rootData);
151+
$refResolver->rootResolver = $rootResolver;
123152
$refResolver->refProvider = $this->refProvider;
124153
$refResolver->url = $url;
125154
}

src/Schema.php

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
*/
2525
class Schema extends ObjectItem
2626
{
27+
const SCHEMA_DRAFT_04_URL = 'http://json-schema.org/draft-04/schema';
28+
2729
public $fromRef;
2830
public $originPath;
2931

@@ -126,6 +128,28 @@ public function addPropertyMapping($dataName, $propertyName)
126128
return $this;
127129
}
128130

131+
private function preProcessReferences($data, ProcessingOptions $options = null)
132+
{
133+
if (is_array($data)) {
134+
foreach ($data as $key => $item) {
135+
$this->preProcessReferences($item, $options);
136+
}
137+
} elseif ($data instanceof \stdClass) {
138+
/** @var JsonSchema $data */
139+
if (isset($data->id) && is_string($data->id)) {
140+
$prev = $options->refResolver->setupResolutionScope($data->id, $data);
141+
/** @noinspection PhpUnusedLocalVariableInspection */
142+
$_ = new ScopeExit(function () use ($prev, $options) {
143+
$options->refResolver->setResolutionScope($prev);
144+
});
145+
}
146+
147+
foreach ((array)$data as $key => $value) {
148+
$this->preProcessReferences($value, $options);
149+
}
150+
}
151+
}
152+
129153
public function import($data, ProcessingOptions $options = null)
130154
{
131155
if ($options === null) {
@@ -138,6 +162,11 @@ public function import($data, ProcessingOptions $options = null)
138162
if ($options->remoteRefProvider) {
139163
$options->refResolver->setRemoteRefProvider($options->remoteRefProvider);
140164
}
165+
166+
if ($options->import) {
167+
$this->preProcessReferences($data, $options);
168+
}
169+
141170
return $this->process($data, $options, '#');
142171
}
143172

@@ -357,16 +386,6 @@ public function process($data, ProcessingOptions $options, $path = '#')
357386
}
358387
$ref->setImported($result);
359388
$path .= '->$ref:' . $refString;
360-
361-
if ($ref->resolver !== $options->refResolver) {
362-
$prevResolver = $options->refResolver;
363-
$options->refResolver = $ref->resolver;
364-
/** @noinspection PhpUnusedLocalVariableInspection */
365-
$deferResolver = new ScopeExit(function () use ($prevResolver, $options) {
366-
$options->refResolver = $prevResolver;
367-
});
368-
369-
}
370389
}
371390
} catch (InvalidValue $exception) {
372391
$this->fail($exception, $path);
@@ -376,8 +395,9 @@ public function process($data, ProcessingOptions $options, $path = '#')
376395
// @todo better check for schema id
377396

378397
if ($import && isset($data->id) && is_string($data->id) /*&& (!isset($this->properties['id']))/* && $this->isMetaSchema($data)*/) {
398+
$id = $data->id;
379399
$refResolver = $options->refResolver;
380-
$parentScope = $refResolver->updateResolutionScope($data->id);
400+
$parentScope = $refResolver->updateResolutionScope($id);
381401
/** @noinspection PhpUnusedLocalVariableInspection */
382402
$defer = new ScopeExit(function () use ($parentScope, $refResolver) {
383403
$refResolver->setResolutionScope($parentScope);
@@ -558,7 +578,6 @@ public function process($data, ProcessingOptions $options, $path = '#')
558578
}
559579
}
560580

561-
562581
return $result;
563582
}
564583

tests/src/PHPUnit/Misc/ResolveURITest.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,17 @@ public function testResolve()
1919
$this->assertSame("some://where.else/completely#", Helper::resolveURI($root, "some://where.else/completely#"));
2020
}
2121

22+
public function testResolveInner()
23+
{
24+
$root = 'http://x.y.z/rootschema.json';
25+
$resolved = Helper::resolveURI($root, '#/def/deep');
26+
$this->assertSame('http://x.y.z/rootschema.json#/def/deep', $resolved);
27+
}
28+
29+
public function testResolveInnerDoc()
30+
{
31+
$root = 'http://x.y.z/rootschema.json#';
32+
$resolved = Helper::resolveURI($root, '#/def/deep');
33+
$this->assertSame('http://x.y.z/rootschema.json#/def/deep', $resolved);
34+
}
2235
}

0 commit comments

Comments
 (0)