Skip to content

Commit 51a89b6

Browse files
committed
Prevent adjustRelativeReferences() to run twice for the same document
1 parent 895f919 commit 51a89b6

File tree

1 file changed

+66
-31
lines changed

1 file changed

+66
-31
lines changed

src/spec/Reference.php

+66-31
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ class Reference implements SpecObjectInterface, DocumentContextInterface
5858
*/
5959
private $_errors = [];
6060

61+
private $_recursingInsideFile = false;
62+
6163
/**
6264
* Create an object from spec data.
6365
* @param array $data spec data read from YAML or JSON
@@ -330,50 +332,83 @@ private function resolveTransitiveReference(Reference $referencedObject, Referen
330332
private $_recursingInsideFile = false;
331333

332334
/**
333-
* Adjust relative references inside of the file to match the context of the base file
334-
*/
335-
private function adjustRelativeReferences($referencedDocument, $basePath, $baseDocument = null, $oContext = null)
335+
* Adjust relative references inside the file to match the context of the base file
336+
*
337+
* @noinspection PhpConditionAlreadyCheckedInspection*/
338+
private function adjustRelativeReferences($referencedDocument, $basePath, $baseDocument = null, ?ReferenceContext $oContext = null)
336339
{
340+
337341
$context = new ReferenceContext(null, $basePath);
342+
338343
if ($baseDocument === null) {
339344
$baseDocument = $referencedDocument;
340345
}
341346

342347
foreach ($referencedDocument as $key => $value) {
343-
// adjust reference URLs
344-
if ($key === '$ref' && is_string($value)) {
345-
if (isset($value[0]) && $value[0] === '#') {
346-
// direcly inline references in the same document,
347-
// these are not going to be valid in the new context anymore
348-
$inlineDocument = (new JsonPointer(substr($value, 1)))->evaluate($baseDocument);
349-
if ($this->_recursingInsideFile) {
350-
// keep reference when it is a recursive reference
351-
return ['$ref' => $basePath . $value];
352-
}
353-
$this->_recursingInsideFile = true;
354-
$return = $this->adjustRelativeReferences($inlineDocument, $basePath, $baseDocument, $oContext);
355-
$this->_recursingInsideFile = false;
356-
return $return;
357-
}
358-
$referencedDocument[$key] = $context->resolveRelativeUri($value);
359-
$parts = explode('#', $referencedDocument[$key], 2);
360-
if ($parts[0] === $oContext->getUri()) {
361-
$referencedDocument[$key] = '#' . ($parts[1] ?? '');
362-
} else {
363-
$referencedDocument[$key] = $this->makeRelativePath($oContext->getUri(), $referencedDocument[$key]);
364-
}
348+
349+
if (is_array($value) === true) {
350+
$referencedDocument[$key] = $this->adjustRelativeReferences($value, $basePath, $baseDocument, $oContext);
365351
continue;
366352
}
367-
// adjust URLs for 'externalValue' references in Example Objects
368-
// https://spec.openapis.org/oas/v3.0.3#example-object
369-
if ($key === 'externalValue' && is_string($value)) {
370-
$referencedDocument[$key] = $this->makeRelativePath($oContext->getUri(), $context->resolveRelativeUri($value));
353+
354+
// non strings can't be references
355+
if (is_string($value) === false) {
371356
continue;
372357
}
373-
if (is_array($value)) {
374-
$referencedDocument[$key] = $this->adjustRelativeReferences($value, $basePath, $baseDocument, $oContext);
358+
359+
// $this->_to does not apply here
360+
$fullPath = $basePath . $value;
361+
$cachePointer = $fullPath;
362+
$cacheType = 'relativeReference';
363+
364+
if ($context->getCache()->has($cachePointer, $cacheType)) {
365+
return $context->getCache()->get($cachePointer, $cacheType);
366+
}
367+
368+
// directly inline references in the same document,
369+
// these are not going to be valid in the new context anymore
370+
if ($key === '$ref' && str_starts_with($value, '#')) {
371+
372+
$inlineDocument = (new JsonPointer(substr($value, 1)))->evaluate($baseDocument);
373+
374+
// keep reference when it is a recursive reference
375+
if ($this->_recursingInsideFile) {
376+
return ['$ref' => $fullPath];
377+
}
378+
379+
$this->_recursingInsideFile = true;
380+
$return = $this->adjustRelativeReferences($inlineDocument, $basePath, $baseDocument, $oContext);
381+
$this->_recursingInsideFile = false;
382+
383+
$context->getCache()->set($cachePointer, $cacheType, $return);
384+
385+
return $return;
375386
}
387+
388+
$oContextUri = $oContext->getUri();
389+
$resolvedUri = $context->resolveRelativeUri($value);
390+
391+
// adjust reference URLs
392+
if ($key === '$ref') {
393+
394+
if (str_starts_with($resolvedUri, $oContextUri)) {
395+
$fragment = str_replace($oContextUri, '', $resolvedUri);
396+
$referencedDocument[$key] = $fragment ?: '#';
397+
} else {
398+
$referencedDocument[$key] = $this->makeRelativePath($oContextUri, $resolvedUri);
399+
}
400+
}
401+
402+
// adjust externalValue fields https://spec.openapis.org/oas/v3.0.3#example-object
403+
if ($key === 'externalValue') {
404+
$referencedDocument[$key] = $this->makeRelativePath($oContextUri, $resolvedUri);
405+
}
406+
407+
$oContext->getCache()->set($cachePointer, $cacheType, $referencedDocument);
408+
376409
}
410+
411+
377412
return $referencedDocument;
378413
}
379414

0 commit comments

Comments
 (0)