Convert @subject and @iri to @id. Remove @vocab.

This commit is contained in:
Dave Longley 2011-12-19 21:29:11 -05:00
parent 7124a88b7a
commit 7d06cd0c92

View file

@ -34,7 +34,7 @@ function jsonld_normalize($input)
function jsonld_expand($input) function jsonld_expand($input)
{ {
$p = new JsonLdProcessor(); $p = new JsonLdProcessor();
return $p->expand(new stdClass(), null, $input, false); return $p->expand(new stdClass(), null, $input);
} }
/** /**
@ -245,7 +245,7 @@ function jsonld_frame($input, $frame, $options=null)
$subjects = new stdClass(); $subjects = new stdClass();
foreach($input as $i) foreach($input as $i)
{ {
$subjects->{$i->{'@subject'}->{'@iri'}} = $i; $subjects->{$i->{'@id'}} = $i;
} }
// frame input // frame input
@ -382,10 +382,9 @@ function _getKeywords($ctx)
// state // state
$rval = (object)array( $rval = (object)array(
'@iri' => '@iri', '@id' => '@id',
'@language' => '@language', '@language' => '@language',
'@literal' => '@literal', '@literal' => '@literal',
'@subject' => '@subject',
'@type' => '@type' '@type' => '@type'
); );
@ -493,9 +492,9 @@ function _getTermIri($ctx, $term)
{ {
$rval = $ctx->$term; $rval = $ctx->$term;
} }
else if(is_object($ctx->$term) and property_exists($ctx->$term, '@iri')) else if(is_object($ctx->$term) and property_exists($ctx->$term, '@id'))
{ {
$rval = $ctx->$term->{'@iri'}; $rval = $ctx->$term->{'@id'};
} }
} }
return $rval; return $rval;
@ -593,7 +592,7 @@ function _compactIri($ctx, $iri, $usedCtx)
*/ */
function _expandTerm($ctx, $term, $usedCtx) function _expandTerm($ctx, $term, $usedCtx)
{ {
$rval; $rval = $term;
// get JSON-LD keywords // get JSON-LD keywords
$keywords = _getKeywords($ctx); $keywords = _getKeywords($ctx);
@ -605,7 +604,7 @@ function _expandTerm($ctx, $term, $usedCtx)
// get the potential prefix // get the potential prefix
$prefix = substr($term, 0, $idx); $prefix = substr($term, 0, $idx);
// 1.1. See if the prefix is in the context: // expand term if prefix is in context, otherwise leave it be
if(property_exists($ctx, $prefix)) if(property_exists($ctx, $prefix))
{ {
// prefix found, expand property to absolute IRI // prefix found, expand property to absolute IRI
@ -616,11 +615,6 @@ function _expandTerm($ctx, $term, $usedCtx)
$usedCtx->$prefix = _clone($ctx->$prefix); $usedCtx->$prefix = _clone($ctx->$prefix);
} }
} }
// 1.2. Prefix is not in context, property is already an absolute IRI:
else
{
$rval = $term;
}
} }
// 2. If the property is in the context, then it's a term. // 2. If the property is in the context, then it's a term.
else if(property_exists($ctx, $term)) else if(property_exists($ctx, $term))
@ -631,26 +625,15 @@ function _expandTerm($ctx, $term, $usedCtx)
$usedCtx->$term = _clone($ctx->$term); $usedCtx->$term = _clone($ctx->$term);
} }
} }
// 3. The property is the special-case @subject. // 3. The property is a keyword.
else if($term === $keywords->{'@subject'})
{
$rval = '@subject';
}
// 4. The property is the special-case @type.
else if($term === $keywords->{'@type'})
{
$rval = '@type';
}
// 5. The property is a relative IRI, prepend the default vocab.
else else
{ {
$rval = $term; foreach($keywords as $key => $value)
if(property_exists($ctx, '@vocab'))
{ {
$rval = $ctx->{'@vocab'} . $rval; if($term === $value)
if($usedCtx !== null)
{ {
$usedCtx->{'@vocab'} = $ctx->{'@vocab'}; $rval = $key;
break;
} }
} }
} }
@ -658,6 +641,51 @@ function _expandTerm($ctx, $term, $usedCtx)
return $rval; return $rval;
} }
/**
* Gets whether or not a value is a reference to a subject (or a subject with
* no properties).
*
* @param value the value to check.
*
* @return true if the value is a reference to a subject, false if not.
*/
function _isReference($value)
{
// Note: A value is a reference to a subject if all of these hold true:
// 1. It is an Object.
// 2. It is has an @id key.
// 3. It has only 1 key.
return ($value !== null and
is_object($value) and
property_exists($value, '@id') and
count(get_object_vars($value)) === 1);
};
/**
* Gets whether or not a value is a subject with properties.
*
* @param value the value to check.
*
* @return true if the value is a subject with properties, false if not.
*/
function _isSubject($value)
{
$rval = false;
// Note: A value is a subject if all of these hold true:
// 1. It is an Object.
// 2. It is not a literal.
// 3. It has more than 1 key OR any existing key is not '@id'.
if($value !== null and is_object($value) and
!property_exists($value, '@literal'))
{
$keyCount = count(get_object_vars($value));
$rval = ($keyCount > 1 or !property_exists($value, '@id'));
}
return $rval;
}
function _isBlankNodeIri($v) function _isBlankNodeIri($v)
{ {
return strpos($v, '_:') === 0; return strpos($v, '_:') === 0;
@ -667,18 +695,16 @@ function _isNamedBlankNode($v)
{ {
// look for "_:" at the beginning of the subject // look for "_:" at the beginning of the subject
return ( return (
is_object($v) and property_exists($v, '@subject') and is_object($v) and property_exists($v, '@id') and
property_exists($v->{'@subject'}, '@iri') and _isBlankNodeIri($v->{'@id'}));
_isBlankNodeIri($v->{'@subject'}->{'@iri'}));
} }
function _isBlankNode($v) function _isBlankNode($v)
{ {
// look for no subject or named blank node // look for a subject with no ID or a blank node ID
return ( return (
is_object($v) and _isSubject($v) and
!(property_exists($v, '@iri') or property_exists($v, '@literal')) and (!property_exists($v, '@id') or _isNamedBlankNode($v)));
(!property_exists($v, '@subject') or _isNamedBlankNode($v)));
} }
/** /**
@ -781,10 +807,10 @@ function _compareObjects($o1, $o2)
$rval = _compareObjectKeys($o1, $o2, '@language'); $rval = _compareObjectKeys($o1, $o2, '@language');
} }
} }
// both are '@iri' objects // both are '@id' objects
else else
{ {
$rval = _compare($o1->{'@iri'}, $o2->{'@iri'}); $rval = _compare($o1->{'@id'}, $o2->{'@id'});
} }
} }
} }
@ -801,8 +827,7 @@ function _compareObjects($o1, $o2)
*/ */
function _filterBlankNodes($e) function _filterBlankNodes($e)
{ {
return (is_string($e) or return !_isNamedBlankNode($e);
!(property_exists($e, '@iri') and _isBlankNodeIri($e->{'@iri'})));
} }
/** /**
@ -829,49 +854,53 @@ function _compareBlankNodeObjects($a, $b)
3.2.6. The bnode with the alphabetically-first @type is first. 3.2.6. The bnode with the alphabetically-first @type is first.
3.2.7. The bnode with a @language is first. 3.2.7. The bnode with a @language is first.
3.2.8. The bnode with the alphabetically-first @language is first. 3.2.8. The bnode with the alphabetically-first @language is first.
3.2.9. The bnode with the alphabetically-first @iri is first. 3.2.9. The bnode with the alphabetically-first @id is first.
*/ */
foreach($a as $p => $value) foreach($a as $p => $value)
{ {
// step #3.1 // skip IDs (IRIs)
$lenA = is_array($a->$p) ? count($a->$p) : 1; if($p !== '@id')
$lenB = is_array($b->$p) ? count($b->$p) : 1;
$rval = _compare($lenA, $lenB);
// step #3.2.1
if($rval === 0)
{ {
// normalize objects to an array // step #3.1
$objsA = $a->$p; $lenA = is_array($a->$p) ? count($a->$p) : 1;
$objsB = $b->$p; $lenB = is_array($b->$p) ? count($b->$p) : 1;
if(!is_array($objsA)) $rval = _compare($lenA, $lenB);
// step #3.2.1
if($rval === 0)
{ {
$objsA = array($objsA); // normalize objects to an array
$objsB = array($objsB); $objsA = $a->$p;
$objsB = $b->$p;
if(!is_array($objsA))
{
$objsA = array($objsA);
$objsB = array($objsB);
}
// compare non-bnodes (remove bnodes from comparison)
$objsA = array_filter($objsA, '_filterBlankNodes');
$objsB = array_filter($objsB, '_filterBlankNodes');
$objsALen = count($objsA);
$rval = _compare($objsALen, count($objsB));
} }
// filter non-bnodes (remove bnodes from comparison) // steps #3.2.2-3.2.9
$objsA = array_filter($objsA, '_filterBlankNodes'); if($rval === 0)
$objsB = array_filter($objsB, '_filterBlankNodes');
$objsALen = count($objsA);
$rval = _compare($objsALen, count($objsB));
}
// steps #3.2.2-3.2.9
if($rval === 0)
{
usort($objsA, '_compareObjects');
usort($objsB, '_compareObjects');
for($i = 0; $i < $objsALen and $rval === 0; ++$i)
{ {
$rval = _compareObjects($objsA[$i], $objsB[$i]); usort($objsA, '_compareObjects');
usort($objsB, '_compareObjects');
for($i = 0; $i < $objsALen and $rval === 0; ++$i)
{
$rval = _compareObjects($objsA[$i], $objsB[$i]);
}
} }
}
if($rval !== 0) if($rval !== 0)
{ {
break; break;
}
} }
} }
@ -929,17 +958,17 @@ function _collectSubjects($input, $subjects, $bnodes)
} }
else if(is_object($input)) else if(is_object($input))
{ {
if(property_exists($input, '@subject')) if(property_exists($input, '@id'))
{ {
// graph literal // graph literal/disjoint graph
if(is_array($input->{'@subject'})) if(is_array($input->{'@id'}))
{ {
_collectSubjects($input->{'@subject'}, $subjects, $bnodes); _collectSubjects($input->{'@id'}, $subjects, $bnodes);
} }
// named subject // named subject
else else if(_isSubject($input))
{ {
$subjects->{$input->{'@subject'}->{'@iri'}} = $input; $subjects->{$input->{'@id'}} = $input;
} }
} }
// unnamed blank node // unnamed blank node
@ -968,8 +997,8 @@ class DuplicateIriFilter
public function filter($e) public function filter($e)
{ {
return (is_object($e) and property_exists($e, '@iri') and return (is_object($e) and property_exists($e, '@id') and
$e->{'@iri'} === $this->iri); $e->{'@id'} === $this->iri);
} }
} }
@ -1001,8 +1030,13 @@ function _flatten($parent, $parentProperty, $value, $subjects)
} }
else if(is_object($value)) else if(is_object($value))
{ {
// already-expanded value or special-case reference-only @type
if(property_exists($value, '@literal') or $parentProperty === '@type')
{
$flattened = _clone($value);
}
// graph literal/disjoint graph // graph literal/disjoint graph
if(property_exists($value, '@subject') and is_array($value->{'@subject'})) else if(is_array($value->{'@id'}))
{ {
// cannot flatten embedded graph literals // cannot flatten embedded graph literals
if($parent !== null) if($parent !== null)
@ -1011,43 +1045,35 @@ function _flatten($parent, $parentProperty, $value, $subjects)
} }
// top-level graph literal // top-level graph literal
foreach($value->{'@subject'} as $k => $v) foreach($value->{'@id'} as $k => $v)
{ {
_flatten($parent, $parentProperty, $v, $subjects); _flatten($parent, $parentProperty, $v, $subjects);
} }
} }
// already-expanded value // regular subject
else if(
property_exists($value, '@literal') or
property_exists($value, '@iri'))
{
$flattened = _clone($value);
}
// subject
else else
{ {
// create or fetch existing subject // create or fetch existing subject
if(property_exists($subjects, $value->{'@subject'}->{'@iri'})) if(property_exists($subjects, $value->{'@id'}))
{ {
// FIXME: '@subject' might be a graph literal (as {}) // FIXME: '@id' might be a graph literal (as {})
$subject = $subjects->{$value->{'@subject'}->{'@iri'}}; $subject = $subjects->{$value->{'@id'}};
} }
else else
{ {
// FIXME: '@id' might be a graph literal (as {})
$subject = new stdClass(); $subject = new stdClass();
if(property_exists($value, '@subject')) $subject->{'@id'} = $value->{'@id'};
{ $subjects->{$value->{'@id'}} = $subject;
// FIXME: '@subject' might be a graph literal (as {})
$subjects->{$value->{'@subject'}->{'@iri'}} = $subject;
}
} }
$flattened = $subject; $flattened = new stdClass();
$flattened->{'@id'} = $subject->{'@id'};
// flatten embeds // flatten embeds
foreach($value as $key => $v) foreach($value as $key => $v)
{ {
// drop null values // drop null values, skip @id (it is already set above)
if($v !== null) if($v !== null and $key !== '@id')
{ {
if(property_exists($subject, $key)) if(property_exists($subject, $key))
{ {
@ -1065,7 +1091,7 @@ function _flatten($parent, $parentProperty, $value, $subjects)
$subject->$key = new ArrayObject(); $subject->$key = new ArrayObject();
} }
_flatten($subject->$key, null, $value->$key, $subjects); _flatten($subject->$key, $key, $value->$key, $subjects);
$subject->$key = (array)$subject->$key; $subject->$key = (array)$subject->$key;
if(count($subject->$key) === 1) if(count($subject->$key) === 1)
{ {
@ -1086,24 +1112,15 @@ function _flatten($parent, $parentProperty, $value, $subjects)
// add flattened value to parent // add flattened value to parent
if($flattened !== null and $parent !== null) if($flattened !== null and $parent !== null)
{ {
// remove top-level '@subject' for subjects
// 'http://mypredicate': {'@subject': {'@iri': 'http://mysubject'}}
// becomes
// 'http://mypredicate': {'@iri': 'http://mysubject'}
if(is_object($flattened) and property_exists($flattened, '@subject'))
{
$flattened = $flattened->{'@subject'};
}
if($parent instanceof ArrayObject) if($parent instanceof ArrayObject)
{ {
// do not add duplicate IRIs for the same property // do not add duplicate IRIs for the same property
$duplicate = false; $duplicate = false;
if(is_object($flattened) and property_exists($flattened, '@iri')) if(is_object($flattened) and property_exists($flattened, '@id'))
{ {
$duplicate = count(array_filter( $duplicate = count(array_filter(
(array)$parent, array( (array)$parent, array(
new DuplicateIriFilter($flattened->{'@iri'}), 'filter'))) > 0; new DuplicateIriFilter($flattened->{'@id'}), 'filter'))) > 0;
} }
if(!$duplicate) if(!$duplicate)
{ {
@ -1236,7 +1253,7 @@ function _compareSerializations($s1, $s2)
*/ */
function _compareIris($a, $b) function _compareIris($a, $b)
{ {
return _compare($a->{'@subject'}->{'@iri'}, $b->{'@subject'}->{'@iri'}); return _compare($a->{'@id'}, $b->{'@id'});
} }
/** /**
@ -1315,18 +1332,15 @@ class JsonLdProcessor
// graph literal/disjoint graph // graph literal/disjoint graph
else if( else if(
is_object($value) and is_object($value) and
property_exists($value, '@subject') and property_exists($value, '@id') and
is_array($value->{'@subject'})) is_array($value->{'@id'}))
{ {
$rval = new stdClass(); $rval = new stdClass();
$rval->{$keywords->{'@subject'}} = $this->compact( $rval->{$keywords->{'@id'}} = $this->compact(
$ctx, $property, $value->{'@subject'}, $usedCtx); $ctx, $property, $value->{'@id'}, $usedCtx);
} }
// value has sub-properties if it doesn't define a literal or IRI value // recurse if value is a subject
else if( else if(_isSubject($value))
is_object($value) and
!property_exists($value, '@literal') and
!property_exists($value, '@iri'))
{ {
// recursively handle sub-properties that aren't a sub-context // recursively handle sub-properties that aren't a sub-context
$rval = new stdClass(); $rval = new stdClass();
@ -1361,10 +1375,10 @@ class JsonLdProcessor
{ {
$type = $value->{'@type'}; $type = $value->{'@type'};
} }
// type is IRI // type is ID (IRI)
else if(property_exists($value, '@iri')) else if(property_exists($value, '@id'))
{ {
$type = '@iri'; $type = '@id';
} }
// can be coerced to any type // can be coerced to any type
else else
@ -1409,9 +1423,9 @@ class JsonLdProcessor
{ {
if(is_object($value)) if(is_object($value))
{ {
if(property_exists($value, '@iri')) if(property_exists($value, '@id'))
{ {
$rval = $value->{'@iri'}; $rval = $value->{'@id'};
} }
else if(property_exists($value, '@literal')) else if(property_exists($value, '@literal'))
{ {
@ -1453,12 +1467,12 @@ class JsonLdProcessor
} }
// compact IRI // compact IRI
if($type === '@iri') if($type === '@id')
{ {
if(is_object($rval)) if(is_object($rval))
{ {
$rval->{$keywords->{'@iri'}} = _compactIri( $rval->{$keywords->{'@id'}} = _compactIri(
$ctx, $rval->{$keywords->{'@iri'}}, $usedCtx); $ctx, $rval->{$keywords->{'@id'}}, $usedCtx);
} }
else else
{ {
@ -1514,51 +1528,22 @@ class JsonLdProcessor
$ctx = jsonld_merge_contexts($ctx, $value->{'@context'}); $ctx = jsonld_merge_contexts($ctx, $value->{'@context'});
} }
// get JSON-LD keywords // recursively handle sub-properties that aren't a sub-context
$keywords = _getKeywords($ctx); $rval = new stdClass();
foreach($value as $key => $v)
// value has sub-properties if it doesn't define a literal or IRI value
if(!(property_exists($value, $keywords->{'@literal'}) or
property_exists($value, $keywords->{'@iri'})))
{ {
// recursively handle sub-properties that aren't a sub-context // preserve frame keywords
$rval = new stdClass(); if($key === '@embed' or $key === '@explicit' or
foreach($value as $key => $v) $key === '@default' or $key === '@omitDefault')
{ {
// preserve frame keywords _setProperty($rval, $key, _clone($v));
if($key === '@embed' or $key === '@explicit' or
$key === '@default' or $key === '@omitDefault')
{
_setProperty($rval, $key, _clone($v));
}
else if($key !== '@context')
{
// set object to expanded property
_setProperty(
$rval, _expandTerm($ctx, $key, null),
$this->expand($ctx, $key, $v));
}
} }
} else if($key !== '@context')
// only need to expand key words
else
{
$rval = new stdClass();
if(property_exists($value, $keywords->{'@iri'}))
{ {
$rval->{'@iri'} = $value->{$keywords->{'@iri'}}; // set object to expanded property
} _setProperty(
else $rval, _expandTerm($ctx, $key, null),
{ $this->expand($ctx, $key, $v));
$rval->{'@literal'} = $value->{$keywords->{'@literal'}};
if(property_exists($value, $keywords->{'@language'}))
{
$rval->{'@language'} = $value->{$keywords->{'@language'}};
}
else if(property_exists($value, $keywords->{'@type'}))
{
$rval->{'@type'} = $value->{$keywords->{'@type'}};
}
} }
} }
} }
@ -1588,15 +1573,21 @@ class JsonLdProcessor
} }
} }
// coerce to appropriate type (do not expand subjects) // special-case expand @id and @type (skips '@id' expansion)
if($coerce !== null and $property !== $keywords->{'@subject'}) if($property === $keywords->{'@id'} or
$property === $keywords->{'@type'})
{
$rval = _expandTerm($ctx, $value, null);
}
// coerce to appropriate type
else if($coerce !== null)
{ {
$rval = new stdClass(); $rval = new stdClass();
// expand IRI // expand ID (IRI)
if($coerce === '@iri') if($coerce === '@id')
{ {
$rval->{'@iri'} = _expandTerm($ctx, $value, null); $rval->{'@id'} = _expandTerm($ctx, $value, null);
} }
// other type // other type
else else
@ -1647,7 +1638,7 @@ class JsonLdProcessor
$this->ng->c14n = null; $this->ng->c14n = null;
// expand input // expand input
$expanded = $this->expand(new stdClass(), null, $input, true); $expanded = $this->expand(new stdClass(), null, $input);
// assign names to unnamed bnodes // assign names to unnamed bnodes
$this->nameBlankNodes($expanded); $this->nameBlankNodes($expanded);
@ -1696,9 +1687,9 @@ class JsonLdProcessor
$p = _expandTerm($ctx, $property, null); $p = _expandTerm($ctx, $property, null);
// built-in type coercion JSON-LD-isms // built-in type coercion JSON-LD-isms
if($p === '@subject' or $p === '@type') if($p === '@id' or $p === '@type')
{ {
$rval = '@iri'; $rval = '@id';
} }
else else
{ {
@ -1738,12 +1729,11 @@ class JsonLdProcessor
// uniquely name all unnamed bnodes // uniquely name all unnamed bnodes
foreach($bnodes as $i => $bnode) foreach($bnodes as $i => $bnode)
{ {
if(!property_exists($bnode, '@subject')) if(!property_exists($bnode, '@id'))
{ {
// generate names until one is unique // generate names until one is unique
while(property_exists($subjects, $ng->next())); while(property_exists($subjects, $ng->next()));
$bnode->{'@subject'} = new stdClass(); $bnode->{'@id'} = $ng->current();
$bnode->{'@subject'}->{'@iri'} = $ng->current();
$subjects->{$ng->current()} = $bnode; $subjects->{$ng->current()} = $bnode;
} }
} }
@ -1758,10 +1748,10 @@ class JsonLdProcessor
*/ */
public function renameBlankNode($b, $id) public function renameBlankNode($b, $id)
{ {
$old = $b->{'@subject'}->{'@iri'}; $old = $b->{'@id'};
// update bnode IRI // update bnode IRI
$b->{'@subject'}->{'@iri'} = $id; $b->{'@id'} = $id;
// update subjects map // update subjects map
$subjects = $this->subjects; $subjects = $this->subjects;
@ -1799,10 +1789,10 @@ class JsonLdProcessor
for($n = 0; $n < $length; ++$n) for($n = 0; $n < $length; ++$n)
{ {
if(is_object($tmp[$n]) and if(is_object($tmp[$n]) and
property_exists($tmp[$n], '@iri') and property_exists($tmp[$n], '@id') and
$tmp[$n]->{'@iri'} === $old) $tmp[$n]->{'@id'} === $old)
{ {
$tmp[$n]->{'@iri'} = $id; $tmp[$n]->{'@id'} = $id;
} }
} }
} }
@ -1845,7 +1835,7 @@ class JsonLdProcessor
$bnodes = array(); $bnodes = array();
foreach($input as $v) foreach($input as $v)
{ {
$iri = $v->{'@subject'}->{'@iri'}; $iri = $v->{'@id'};
$subjects->$iri = $v; $subjects->$iri = $v;
$edges->refs->$iri = new stdClass(); $edges->refs->$iri = new stdClass();
$edges->refs->$iri->all = array(); $edges->refs->$iri->all = array();
@ -1870,13 +1860,13 @@ class JsonLdProcessor
// and initialize serializations // and initialize serializations
foreach($bnodes as $i => $bnode) foreach($bnodes as $i => $bnode)
{ {
$iri = $bnode->{'@subject'}->{'@iri'}; $iri = $bnode->{'@id'};
if($c14n->inNamespace($iri)) if($c14n->inNamespace($iri))
{ {
// generate names until one is unique // generate names until one is unique
while(property_exists($subjects, $ngTmp->next())); while(property_exists($subjects, $ngTmp->next()));
$this->renameBlankNode($bnode, $ngTmp->current()); $this->renameBlankNode($bnode, $ngTmp->current());
$iri = $bnode->{'@subject'}->{'@iri'}; $iri = $bnode->{'@id'};
} }
$this->serializations->$iri = new stdClass(); $this->serializations->$iri = new stdClass();
$this->serializations->$iri->props = null; $this->serializations->$iri->props = null;
@ -1896,7 +1886,7 @@ class JsonLdProcessor
// name all bnodes according to the first bnode's relation mappings // name all bnodes according to the first bnode's relation mappings
// (if it has mappings then a resort will be necessary) // (if it has mappings then a resort will be necessary)
$bnode = array_shift($bnodes); $bnode = array_shift($bnodes);
$iri = $bnode->{'@subject'}->{'@iri'}; $iri = $bnode->{'@id'};
$resort = ($this->serializations->$iri->{'props'} !== null); $resort = ($this->serializations->$iri->{'props'} !== null);
$dirs = array('props', 'refs'); $dirs = array('props', 'refs');
foreach($dirs as $dir) foreach($dirs as $dir)
@ -1933,7 +1923,7 @@ class JsonLdProcessor
$bnodes = array(); $bnodes = array();
foreach($tmp as $i => $b) foreach($tmp as $i => $b)
{ {
$iriB = $b->{'@subject'}->{'@iri'}; $iriB = $b->{'@id'};
if(!$c14n->inNamespace($iriB)) if(!$c14n->inNamespace($iriB))
{ {
// mark serializations related to the named bnodes as dirty // mark serializations related to the named bnodes as dirty
@ -2005,7 +1995,7 @@ class JsonLdProcessor
$first = true; $first = true;
foreach($b as $p => $o) foreach($b as $p => $o)
{ {
if($p !== '@subject') if($p !== '@id')
{ {
if($first) if($first)
{ {
@ -2025,16 +2015,16 @@ class JsonLdProcessor
{ {
if(is_object($obj)) if(is_object($obj))
{ {
// iri // ID (IRI)
if(property_exists($obj, '@iri')) if(property_exists($obj, '@id'))
{ {
if(_isBlankNodeIri($obj->{'@iri'})) if(_isBlankNodeIri($obj->{'@id'}))
{ {
$rval .= '_:'; $rval .= '_:';
} }
else else
{ {
$rval .= '<' . $obj->{'@iri'} . '>'; $rval .= '<' . $obj->{'@id'} . '>';
} }
} }
// literal // literal
@ -2294,8 +2284,8 @@ class JsonLdProcessor
$rval = 0; $rval = 0;
// compare IRIs // compare IRIs
$iriA = $a->{'@subject'}->{'@iri'}; $iriA = $a->{'@id'};
$iriB = $b->{'@subject'}->{'@iri'}; $iriB = $b->{'@id'};
if($iriA === $iriB) if($iriA === $iriB)
{ {
$rval = 0; $rval = 0;
@ -2397,8 +2387,8 @@ class JsonLdProcessor
// step #4 // step #4
if($rval === 0) if($rval === 0)
{ {
$edgesA = $this->edges->refs->{$a->{'@subject'}->{'@iri'}}->all; $edgesA = $this->edges->refs->{$a->{'@id'}}->all;
$edgesB = $this->edges->refs->{$b->{'@subject'}->{'@iri'}}->all; $edgesB = $this->edges->refs->{$b->{'@id'}}->all;
$edgesALen = count($edgesA); $edgesALen = count($edgesA);
$rval = _compare($edgesALen, count($edgesB)); $rval = _compare($edgesALen, count($edgesB));
} }
@ -2487,16 +2477,16 @@ class JsonLdProcessor
{ {
foreach($subject as $key => $object) foreach($subject as $key => $object)
{ {
if($key !== '@subject') if($key !== '@id')
{ {
// normalize to array for single codepath // normalize to array for single codepath
$tmp = !is_array($object) ? array($object) : $object; $tmp = !is_array($object) ? array($object) : $object;
foreach($tmp as $o) foreach($tmp as $o)
{ {
if(is_object($o) and property_exists($o, '@iri') and if(is_object($o) and property_exists($o, '@id') and
property_exists($this->subjects, $o->{'@iri'})) property_exists($this->subjects, $o->{'@id'}))
{ {
$objIri = $o->{'@iri'}; $objIri = $o->{'@id'};
// map object to this subject // map object to this subject
$e = new stdClass(); $e = new stdClass();
@ -2545,7 +2535,7 @@ function _isType($input, $frame)
// check if type(s) are specified in frame and input // check if type(s) are specified in frame and input
$type = '@type'; $type = '@type';
if(property_exists($frame, $type) and if(property_exists($frame, $type) and
is_object($input) and property_exists($input, '@subject') and is_object($input) and
property_exists($input, $type)) property_exists($input, $type))
{ {
$tmp = is_array($input->$type) ? $input->$type : array($input->$type); $tmp = is_array($input->$type) ? $input->$type : array($input->$type);
@ -2553,10 +2543,10 @@ function _isType($input, $frame)
$length = count($types); $length = count($types);
for($t = 0; $t < $length and !$rval; ++$t) for($t = 0; $t < $length and !$rval; ++$t)
{ {
$type = $types[$t]->{'@iri'}; $type = $types[$t];
foreach($tmp as $e) foreach($tmp as $e)
{ {
if($e->{'@iri'} === $type) if($e === $type)
{ {
$rval = true; $rval = true;
break; break;
@ -2603,7 +2593,7 @@ function _isDuckType($input, $frame)
$rval = true; $rval = true;
} }
// input must be a subject with all the given properties // input must be a subject with all the given properties
else if(is_object($input) and property_exists($input, '@subject')) else if(is_object($input) and property_exists($input, '@id'))
{ {
$rval = true; $rval = true;
foreach($props as $prop) foreach($props as $prop)
@ -2632,7 +2622,7 @@ function removeDependentEmbeds($iri, $embeds)
foreach($iris as $i => $embed) foreach($iris as $i => $embed)
{ {
if($embed->parent !== null and if($embed->parent !== null and
$embed->parent->{'@subject'}->{'@iri'} === $iri) $embed->parent->{'@id'} === $iri)
{ {
unset($embeds->$i); unset($embeds->$i);
removeDependentEmbeds($i, $embeds); removeDependentEmbeds($i, $embeds);
@ -2659,7 +2649,7 @@ function _subframe(
$parent, $parentKey, $options) $parent, $parentKey, $options)
{ {
// get existing embed entry // get existing embed entry
$iri = $value->{'@subject'}->{'@iri'}; $iri = $value->{'@id'};
$embed = property_exists($embeds, $iri) ? $embeds->{$iri} : null; $embed = property_exists($embeds, $iri) ? $embeds->{$iri} : null;
// determine if value should be embedded or referenced, // determine if value should be embedded or referenced,
@ -2675,7 +2665,9 @@ function _subframe(
if(!$embedOn) if(!$embedOn)
{ {
// not embedding, so only use subject IRI as reference // not embedding, so only use subject IRI as reference
$value = $value->{'@subject'}; $tmp = new stdClass();
$tmp->{'@id'} = $value->{'@id'};
$value = $tmp;
} }
else else
{ {
@ -2695,17 +2687,21 @@ function _subframe(
for($i = 0; $i < $arrLen; ++$i) for($i = 0; $i < $arrLen; ++$i)
{ {
$obj = $embed->parent->{$embed->key}[$i]; $obj = $embed->parent->{$embed->key}[$i];
if(is_object($obj) and property_exists($obj, '@subject') and if(is_object($obj) and property_exists($obj, '@id') and
$obj->{'@subject'}->{'@iri'} === $iri) $obj->{'@id'} === $iri)
{ {
$embed->parent->{$embed->key}[$i] = $value->{'@subject'}; $tmp = new stdClass();
$tmp->{'@id'} = $value->{'@id'};
$embed->parent->{$embed->key}[$i] = $tmp;
break; break;
} }
} }
} }
else else
{ {
$embed->parent->{$embed->key} = $value->{'@subject'}; $tmp = new stdClass();
$tmp->{'@id'} = $value->{'@id'};
$embed->parent->{$embed->key} = $tmp;
} }
// recursively remove any dependent dangling embeds // recursively remove any dependent dangling embeds
@ -2725,8 +2721,8 @@ function _subframe(
// remove keys from the value that aren't in the frame // remove keys from the value that aren't in the frame
foreach($value as $key => $v) foreach($value as $key => $v)
{ {
// do not remove @subject or any frame key // do not remove @id or any frame key
if($key !== '@subject' and !property_exists($frame, $key)) if($key !== '@id' and !property_exists($frame, $key))
{ {
unset($value->$key); unset($value->$key);
} }
@ -2758,12 +2754,12 @@ function _subframe(
$length = count($input); $length = count($input);
for($n = 0; $n < $length; ++$n) for($n = 0; $n < $length; ++$n)
{ {
// replace reference to subject w/subject // replace reference to subject w/embedded subject
if(is_object($input[$n]) and if(is_object($input[$n]) and
property_exists($input[$n], '@iri') and property_exists($input[$n], '@id') and
property_exists($subjects, $input[$n]->{'@iri'})) property_exists($subjects, $input[$n]->{'@id'}))
{ {
$input[$n] = $subjects->{$input[$n]->{'@iri'}}; $input[$n] = $subjects->{$input[$n]->{'@id'}};
} }
} }
$value->$key = _frame( $value->$key = _frame(
@ -2876,10 +2872,10 @@ function _frame(
{ {
// dereference input if it refers to a subject // dereference input if it refers to a subject
$next = $input[$n]; $next = $input[$n];
if(is_object($next) and property_exists($next, '@iri') and if(is_object($next) and property_exists($next, '@id') and
property_exists($subjects, $next->{'@iri'})) property_exists($subjects, $next->{'@id'}))
{ {
$next = $subjects->{$next->{'@iri'}}; $next = $subjects->{$next->{'@id'}};
} }
// add input to list if it matches frame specific type or duck-type // add input to list if it matches frame specific type or duck-type
@ -2901,7 +2897,7 @@ function _frame(
$frame = $frames[$i1]; $frame = $frames[$i1];
// if value is a subject, do subframing // if value is a subject, do subframing
if(is_object($value) and property_exists($value, '@subject')) if(_isSubject($value))
{ {
$value = _subframe( $value = _subframe(
$subjects, $value, $frame, $embeds, $autoembed, $subjects, $value, $frame, $embeds, $autoembed,
@ -2915,10 +2911,9 @@ function _frame(
} }
else else
{ {
// determine if value is a reference // determine if value is a reference to an embed
$isRef = ($value !== null and is_object($value) and $isRef = (_isReference($value) and
property_exists($value, '@iri') and property_exists($embeds, $value->{'@id'}));
property_exists($embeds, $value->{'@iri'}));
// push any value that isn't a parentless reference // push any value that isn't a parentless reference
if(!($parent === null and $isRef)) if(!($parent === null and $isRef))