Skip to content

Instantly share code, notes, and snippets.

@pjdietz
Created July 22, 2014 19:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pjdietz/0f53907ce95ea586664a to your computer and use it in GitHub Desktop.
Save pjdietz/0f53907ce95ea586664a to your computer and use it in GitHub Desktop.
$ref Resolver
<?php
/**
* Iterates through an object or array and resolves or "flatten" and $ref
* properties by reading the object the reference points to and augmenting
* the containing object with the result.
*
* This class works with any stdClass object, but is mostly likely to be
* useful for working with JSON Schema documents and Swagger configurations.
*/
class RefResolver
{
/**
* Function that given a string URI returns an object or null.
* @var callable
*/
private $readReferenceFn;
/**
* Create new ref flattener.
*
* Optionally provide a custom function for reading a resource. The
* callabl must accept one argument, the string value of a $ref property,
* and return the object resolved at that reference or null.
*
* @param callable $readReferenceFn
*/
public function __construct($readReferenceFn = null)
{
$this->readReferenceFn = $readReferenceFn;
}
/**
* Resolve $ref references in the oject's structure by reading the object
* at the reference and augmenting the contain object.
*
* @param object|array $obj Object (or array) to flesh out
*/
public function resolve(&$obj)
{
// Flatten each member of an array.
if (is_array($obj)) {
foreach ($obj as $v) {
$this->resolve($v);
}
return;
}
// Do nothing if not an array or object.
if (!is_object($obj)) {
return;
}
// Check if the object contains a $ref member.
// If it does, attempt to read the results of the reference.
if (isset($obj->{'$ref'})) {
$resolved = $this->readReference($obj->{'$ref'});
// Remove $ref and augment $obj with the resolved reference.
if (is_object($resolved)) {
unset($obj->{'$ref'});
$this->resolve($resolved);
$this->augment($obj, $resolved);
}
}
foreach ($obj as $k => $v) {
$this->resolve($v);
}
}
/**
* Read an object identified by the value of a $ref member.
*
* If the instance's readReferenceFn member is set, call it, passing $ref,
* and return the result. Otherwise, use file_get_contents() and
* json_decode() to read the reference.
*
* @param string $ref Value of a $ref property
* @return object|null
*/
private function readReference($ref)
{
if (is_callable($this->readReferenceFn)) {
$callable = $this->readReferenceFn;
return $callable($ref);
}
$contents = @file_get_contents($ref);
if ($contents) {
return json_decode($contents);
}
return null;
}
/**
* Merge the members of $source onto $target
*
* @param object $target
* @param object $source
*/
private function augment(&$target, $source)
{
foreach ($source as $property => $value) {
$target->{$property} = $value;
}
}
}
@pjdietz
Copy link
Author

pjdietz commented Jul 22, 2014

Note, there's currently nothing in here to safeguard against circular references.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment