Updated to use new framing algorithm.
This commit is contained in:
parent
ec4110fe5c
commit
c8d381111b
1 changed files with 170 additions and 97 deletions
267
jsonld.php
267
jsonld.php
|
@ -305,7 +305,8 @@ function jsonld_frame($input, $frame, $options=null)
|
|||
}
|
||||
|
||||
// frame input
|
||||
$rval = _frame($subjects, $input, $frame, new stdClass(), $options);
|
||||
$rval = _frame(
|
||||
$subjects, $input, $frame, new stdClass(), false, null, null, $options);
|
||||
|
||||
// apply context
|
||||
if($ctx !== null and $rval !== null)
|
||||
|
@ -2558,6 +2559,163 @@ function _isDuckType($input, $frame)
|
|||
return $rval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subframes a value.
|
||||
*
|
||||
* @param subjects a map of subjects in the graph.
|
||||
* @param value the value to subframe.
|
||||
* @param frame the frame to use.
|
||||
* @param embeds a map of previously embedded subjects, used to prevent cycles.
|
||||
* @param autoembed true if auto-embed is on, false if not.
|
||||
* @param parent the parent object.
|
||||
* @param parentKey the parent object.
|
||||
* @param options the framing options.
|
||||
*
|
||||
* @return the framed input.
|
||||
*/
|
||||
function _subframe(
|
||||
$subjects, $value, $frame, $embeds, $autoembed,
|
||||
$parent, $parentKey, $options)
|
||||
{
|
||||
// get existing embed entry
|
||||
$iri = $value->{'@subject'}->{'@iri'};
|
||||
$embed = property_exists($embeds, $iri) ? $embeds->{$iri} : null;
|
||||
|
||||
// determine if value should be embedded or referenced,
|
||||
// embed is ON if:
|
||||
// 1. The frame OR default option specifies @embed as ON, AND
|
||||
// 2. There is no existing embed OR it is an autoembed, AND
|
||||
// autoembed mode is off.
|
||||
$embedOn =
|
||||
((property_exists($frame, '@embed') and $frame->{'@embed'}) or
|
||||
$options->defaults->embedOn) and
|
||||
($embed === null or ($embed->autoembed and !$autoembed));
|
||||
|
||||
if(!$embedOn)
|
||||
{
|
||||
// not embedding, so only use subject IRI as reference
|
||||
$value = $value->{'@subject'};
|
||||
}
|
||||
else
|
||||
{
|
||||
// create new embed entry
|
||||
if($embed === null)
|
||||
{
|
||||
$embed = new stdClass();
|
||||
$embeds->{$iri} = $embed;
|
||||
}
|
||||
else
|
||||
{
|
||||
// replace the existing embed with a reference and update embed info
|
||||
$embed->{$parent}->{$entry->key} = $value->{'@subject'};
|
||||
}
|
||||
|
||||
// update embed entry
|
||||
$embed->autoembed = $autoembed;
|
||||
$embed->parent = $parent;
|
||||
$embed->key = $parentKey;
|
||||
|
||||
// check explicit flag
|
||||
$explicitOn = property_exists($frame, '@explicit') ?
|
||||
$frame->{'@explicit'} : $options->defaults->explicitOn;
|
||||
if($explicitOn)
|
||||
{
|
||||
// remove keys from the value that aren't in the frame
|
||||
foreach($value as $key => $v)
|
||||
{
|
||||
// do not remove @subject or any frame key
|
||||
if($key !== '@subject' and !property_exists($frame, $key))
|
||||
{
|
||||
unset($value->$key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// iterate over keys in value
|
||||
foreach($value as $key => $v)
|
||||
{
|
||||
// skip keywords and type
|
||||
if(strpos($key, '@') !== 0 and $key !== JSONLD_RDF_TYPE)
|
||||
{
|
||||
// get the subframe if available
|
||||
if(property_exists($frame, $key))
|
||||
{
|
||||
$f = $frame->{$key};
|
||||
$_autoembed = false;
|
||||
}
|
||||
// use a catch-all subframe to preserve data from graph
|
||||
else
|
||||
{
|
||||
$f = is_array($v) ? array() : new stdClass();
|
||||
$_autoembed = true;
|
||||
}
|
||||
|
||||
// build input and do recursion
|
||||
$input = is_array($v) ? $v : array($v);
|
||||
$length = count($input);
|
||||
for($n = 0; $n < $length; ++$n)
|
||||
{
|
||||
// replace reference to subject w/subject
|
||||
if(is_object($input[$n]) and
|
||||
property_exists($input[$n], '@iri') and
|
||||
property_exists($subjects, $input[$n]->{'@iri'}))
|
||||
{
|
||||
$input[$n] = $subjects->{$input[$n]->{'@iri'}};
|
||||
}
|
||||
}
|
||||
$value->$key = _frame(
|
||||
$subjects, $input, $f, $embeds, $_autoembed,
|
||||
$value, $key, $options);
|
||||
}
|
||||
}
|
||||
|
||||
// iterate over frame keys to add any missing values
|
||||
foreach($frame as $key => $f)
|
||||
{
|
||||
// skip keywords, type query, and keys in value
|
||||
if(strpos($key, '@') !== 0 and $key !== JSONLD_RDF_TYPE and
|
||||
!property_exists($value, $key))
|
||||
{
|
||||
// add empty array to value
|
||||
if(is_array($f))
|
||||
{
|
||||
// add empty array/null property to value
|
||||
$value->$key = array();
|
||||
}
|
||||
// add default value to value
|
||||
else
|
||||
{
|
||||
// use first subframe if frame is an array
|
||||
if(is_array($f))
|
||||
{
|
||||
$f = (count($f) > 0) ? $f[0] : new stdClass();
|
||||
}
|
||||
|
||||
// determine if omit default is on
|
||||
$omitOn = property_exists($f, '@omitDefault') ?
|
||||
$f->{'@omitDefault'} :
|
||||
$options->defaults->omitDefaultOn;
|
||||
if(!$omitOn)
|
||||
{
|
||||
if(property_exists($f, '@default'))
|
||||
{
|
||||
// use specified default value
|
||||
$value->{$key} = $f->{'@default'};
|
||||
}
|
||||
else
|
||||
{
|
||||
// build-in default value is: null
|
||||
$value->{$key} = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively frames the given input according to the given frame.
|
||||
*
|
||||
|
@ -2565,11 +2723,16 @@ function _isDuckType($input, $frame)
|
|||
* @param input the input to frame.
|
||||
* @param frame the frame to use.
|
||||
* @param embeds a map of previously embedded subjects, used to prevent cycles.
|
||||
* @param autoembed true if auto-embed is on, false if not.
|
||||
* @param parent the parent object (for subframing), null for none.
|
||||
* @param parentKey the parent key (for subframing), null for none.
|
||||
* @param options the framing options.
|
||||
*
|
||||
* @return the framed input.
|
||||
*/
|
||||
function _frame($subjects, $input, $frame, $embeds, $options)
|
||||
function _frame(
|
||||
$subjects, $input, $frame, $embeds, $autoembed,
|
||||
$parent, $parentKey, $options)
|
||||
{
|
||||
$rval = null;
|
||||
|
||||
|
@ -2634,102 +2797,12 @@ function _frame($subjects, $input, $frame, $embeds, $options)
|
|||
{
|
||||
$frame = $frames[$i1];
|
||||
|
||||
// determine if value should be embedded or referenced
|
||||
$embedOn = property_exists($frame, '@embed') ?
|
||||
$frame->{'@embed'} : $options->defaults->embedOn;
|
||||
if(!$embedOn)
|
||||
// if value is a subject, do subframing
|
||||
if(is_object($value) and property_exists($value, '@subject'))
|
||||
{
|
||||
// if value is a subject, only use subject IRI as reference
|
||||
if(is_object($value) and property_exists($value, '@subject'))
|
||||
{
|
||||
$value = $value->{'@subject'};
|
||||
}
|
||||
}
|
||||
else if(
|
||||
is_object($value) and property_exists($value, '@subject') and
|
||||
property_exists($embeds, $value->{'@subject'}->{'@iri'}))
|
||||
{
|
||||
// TODO: possibly support multiple embeds in the future ... and
|
||||
// instead only prevent cycles?
|
||||
throw new Exception(
|
||||
'Multiple embeds of the same subject is not supported. ' .
|
||||
'subject=' . $value->{'@subject'}->{'@iri'});
|
||||
}
|
||||
// if value is a subject, do embedding and subframing
|
||||
else if(is_object($value) and property_exists($value, '@subject'))
|
||||
{
|
||||
$embeds->{$value->{'@subject'}->{'@iri'}} = true;
|
||||
|
||||
// if explicit is on, remove keys from value that aren't in frame
|
||||
$explicitOn = property_exists($frame, '@explicit') ?
|
||||
$frame->{'@explicit'} : $options->defaults->explicitOn;
|
||||
if($explicitOn)
|
||||
{
|
||||
foreach($value as $key => $v)
|
||||
{
|
||||
// do not remove subject or any key in the frame
|
||||
if($key !== '@subject' and !property_exists($frame, $key))
|
||||
{
|
||||
unset($value->$key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// iterate over frame keys to do subframing
|
||||
foreach($frame as $key => $f)
|
||||
{
|
||||
// skip keywords and type query
|
||||
if(strpos($key, '@') !== 0 and $key !== JSONLD_RDF_TYPE)
|
||||
{
|
||||
if(property_exists($value, $key))
|
||||
{
|
||||
// build input and do recursion
|
||||
$input = is_array($value->$key) ?
|
||||
$value->$key : array($value->$key);
|
||||
$length = count($input);
|
||||
for($n = 0; $n < $length; ++$n)
|
||||
{
|
||||
// replace reference to subject w/subject
|
||||
if(is_object($input[$n]) and
|
||||
property_exists($input[$n], '@iri') and
|
||||
property_exists($subjects, $input[$n]->{'@iri'}))
|
||||
{
|
||||
$input[$n] = $subjects->{$input[$n]->{'@iri'}};
|
||||
}
|
||||
}
|
||||
$value->$key = _frame(
|
||||
$subjects, $input, $f, $embeds, $options);
|
||||
}
|
||||
else
|
||||
{
|
||||
// add empty array/null property to value
|
||||
$value->$key = is_array($f) ? array() : null;
|
||||
}
|
||||
|
||||
// handle setting default value
|
||||
if($value->$key === null)
|
||||
{
|
||||
// use first subframe if frame is an array
|
||||
if(is_array($f))
|
||||
{
|
||||
$f = (count($f) > 0) ? $f[0] : new stdClass();
|
||||
}
|
||||
|
||||
// determine if omit default is on
|
||||
$omitOn = property_exists($f, '@omitDefault') ?
|
||||
$f->{'@omitDefault'} :
|
||||
$options->defaults->omitDefaultOn;
|
||||
if($omitOn)
|
||||
{
|
||||
unset($value->$key);
|
||||
}
|
||||
else if(property_exists($f, '@default'))
|
||||
{
|
||||
$value->$key = $f->{'@default'};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$value = _subframe(
|
||||
$subjects, $value, $frame, $embeds, $autoembed,
|
||||
$parent, $parentKey, $options);
|
||||
}
|
||||
|
||||
// add value to output
|
||||
|
|
Loading…
Reference in a new issue