forked from friendica/php-json-ld
Update flatten algorithm, fix @list merge bugs.
This commit is contained in:
parent
160c1b379a
commit
f17859714b
210
jsonld.php
210
jsonld.php
|
@ -691,26 +691,36 @@ class JsonLdProcessor {
|
||||||
* @param stdClass $subject the subject to add the value to.
|
* @param stdClass $subject the subject to add the value to.
|
||||||
* @param string $property the property that relates the value to the subject.
|
* @param string $property the property that relates the value to the subject.
|
||||||
* @param mixed $value the value to add.
|
* @param mixed $value the value to add.
|
||||||
* @param bool [$propertyIsArray] true if the property is always an array,
|
* @param bool [$property_is_array] true if the property is always an array,
|
||||||
* false if not (default: false).
|
* false if not (default: false).
|
||||||
|
* @param bool [$property_is_list] true if the property is a @list, false
|
||||||
|
* if not (default: false).
|
||||||
*/
|
*/
|
||||||
public static function addValue(
|
public static function addValue(
|
||||||
$subject, $property, $value, $propertyIsArray=false) {
|
$subject, $property, $value,
|
||||||
|
$property_is_array=false, $property_is_list=false) {
|
||||||
|
if($property === '@list') {
|
||||||
|
$property_is_list = true;
|
||||||
|
}
|
||||||
|
|
||||||
if(is_array($value)) {
|
if(is_array($value)) {
|
||||||
if(count($value) === 0 && $propertyIsArray &&
|
if(count($value) === 0 && $property_is_array &&
|
||||||
!property_exists($subject, $property)) {
|
!property_exists($subject, $property)) {
|
||||||
$subject->{$property} = array();
|
$subject->{$property} = array();
|
||||||
}
|
}
|
||||||
foreach($value as $v) {
|
foreach($value as $v) {
|
||||||
self::addValue($subject, $property, $v, $propertyIsArray);
|
self::addValue(
|
||||||
|
$subject, $property, $v, $property_is_array, $property_is_list);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(property_exists($subject, $property)) {
|
else if(property_exists($subject, $property)) {
|
||||||
$has_value = self::hasValue($subject, $property, $value);
|
// check if subject already has value unless property is list
|
||||||
|
$has_value = (!$property_is_list &&
|
||||||
|
self::hasValue($subject, $property, $value));
|
||||||
|
|
||||||
// make property an array if value not present or always an array
|
// make property an array if value not present or always an array
|
||||||
if(!is_array($subject->{$property}) &&
|
if(!is_array($subject->{$property}) &&
|
||||||
(!$has_value || $propertyIsArray)) {
|
(!$has_value || $property_is_array)) {
|
||||||
$subject->{$property} = array($subject->{$property});
|
$subject->{$property} = array($subject->{$property});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -721,7 +731,7 @@ class JsonLdProcessor {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// add new value as set or single value
|
// add new value as set or single value
|
||||||
$subject->{$property} = $propertyIsArray ? array($value) : $value;
|
$subject->{$property} = $property_is_array ? array($value) : $value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -754,11 +764,11 @@ class JsonLdProcessor {
|
||||||
* @param stdClass $subject the subject.
|
* @param stdClass $subject the subject.
|
||||||
* @param string $property the property that relates the value to the subject.
|
* @param string $property the property that relates the value to the subject.
|
||||||
* @param mixed $value the value to remove.
|
* @param mixed $value the value to remove.
|
||||||
* @param bool [$propertyIsArray] true if the property is always an array,
|
* @param bool [$property_is_array] true if the property is always an array,
|
||||||
* false if not (default: false).
|
* false if not (default: false).
|
||||||
*/
|
*/
|
||||||
public static function removeValue(
|
public static function removeValue(
|
||||||
$subject, $property, $value, $propertyIsArray=false) {
|
$subject, $property, $value, $property_is_array=false) {
|
||||||
|
|
||||||
// filter out value
|
// filter out value
|
||||||
$filter = function($e) use ($value) {
|
$filter = function($e) use ($value) {
|
||||||
|
@ -770,7 +780,7 @@ class JsonLdProcessor {
|
||||||
if(count($values) === 0) {
|
if(count($values) === 0) {
|
||||||
self::removeProperty($subject, $property);
|
self::removeProperty($subject, $property);
|
||||||
}
|
}
|
||||||
else if(count($values) === 1 && !$propertyIsArray) {
|
else if(count($values) === 1 && !$property_is_array) {
|
||||||
$subject->{$property} = $values[0];
|
$subject->{$property} = $values[0];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -1242,11 +1252,12 @@ class JsonLdProcessor {
|
||||||
|
|
||||||
// if @container is @set or @list or value is an empty array, use
|
// if @container is @set or @list or value is an empty array, use
|
||||||
// an array when adding value
|
// an array when adding value
|
||||||
$isArray = ($container === '@set' || $container === '@list' ||
|
$is_array = ($container === '@set' || $container === '@list' ||
|
||||||
(is_array($v) && count($v) === 0));
|
(is_array($v) && count($v) === 0));
|
||||||
|
|
||||||
// add compact value
|
// add compact value
|
||||||
self::addValue($rval, $prop, $v, $isArray);
|
self::addValue(
|
||||||
|
$rval, $prop, $v, $is_array, ($container === '@list'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $rval;
|
return $rval;
|
||||||
|
@ -1472,11 +1483,17 @@ class JsonLdProcessor {
|
||||||
// create framing state
|
// create framing state
|
||||||
$state = (object)array(
|
$state = (object)array(
|
||||||
'options' => $options,
|
'options' => $options,
|
||||||
'subjects' => new stdClass());
|
'graphs' => (object)array(
|
||||||
|
'@default' => new stdClass(),
|
||||||
|
'@merged' => new stdClass()));
|
||||||
|
|
||||||
// produce a map of all subjects and name each bnode
|
// produce a map of all graphs and name each bnode
|
||||||
$namer = new UniqueNamer('_:t');
|
$namer = new UniqueNamer('_:t');
|
||||||
$this->_flatten($state->subjects, $input, $namer, null, null);
|
$this->_flatten($input, $state->graphs, '@default', $namer, null, null);
|
||||||
|
$namer = new UniqueNamer('_:t');
|
||||||
|
$this->_flatten($input, $state->graphs, '@merged', $namer, null, null);
|
||||||
|
// FIXME: currently uses subjects from @merged graph only
|
||||||
|
$state->subjects = $state->graphs->{'@merged'};
|
||||||
|
|
||||||
// frame the subjects
|
// frame the subjects
|
||||||
$framed = new ArrayObject();
|
$framed = new ArrayObject();
|
||||||
|
@ -2303,98 +2320,111 @@ class JsonLdProcessor {
|
||||||
/**
|
/**
|
||||||
* Recursively flattens the subjects in the given JSON-LD expanded input.
|
* Recursively flattens the subjects in the given JSON-LD expanded input.
|
||||||
*
|
*
|
||||||
* @param stdClass $subjects a map of subject @id to subject.
|
|
||||||
* @param mixed $input the JSON-LD expanded input.
|
* @param mixed $input the JSON-LD expanded input.
|
||||||
|
* @param stdClass $graphs a map of graph name to subject map.
|
||||||
|
* @[ara, string $graph the name of the current graph.
|
||||||
* @param UniqueNamer $namer the blank node namer.
|
* @param UniqueNamer $namer the blank node namer.
|
||||||
* @param mixed $name the name assigned to the current input if it is a bnode.
|
* @param mixed $name the name assigned to the current input if it is a bnode.
|
||||||
* @param mixed $list the list to append to, null for none.
|
* @param mixed $list the list to append to, null for none.
|
||||||
*/
|
*/
|
||||||
protected function _flatten($subjects, $input, $namer, $name, $list) {
|
protected function _flatten($input, $graphs, $graph, $namer, $name, $list) {
|
||||||
// recurse through array
|
// recurse through array
|
||||||
if(is_array($input)) {
|
if(is_array($input)) {
|
||||||
foreach($input as $e) {
|
foreach($input as $e) {
|
||||||
$this->_flatten($subjects, $e, $namer, null, $list);
|
$this->_flatten($e, $graphs, $graph, $namer, null, $list);
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
// handle subject
|
|
||||||
else if(is_object($input)) {
|
|
||||||
// add value to list
|
|
||||||
if(self::_isValue($input) && $list) {
|
|
||||||
$list[] = $input;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get name for subject
|
// add non-object or value
|
||||||
if($name === null) {
|
if(!is_object($input) || self::_isValue($input)) {
|
||||||
if(property_exists($input, '@id')) {
|
|
||||||
$name = $input->{'@id'};
|
|
||||||
}
|
|
||||||
if(self::_isBlankNode($input)) {
|
|
||||||
$name = $namer->getName($name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// add subject reference to list
|
|
||||||
if($list !== null) {
|
if($list !== null) {
|
||||||
$list[] = (object)array('@id' => $name);
|
$list[] = $input;
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// create new subject or merge into existing one
|
// Note: At this point, input must be a subject.
|
||||||
if(property_exists($subjects, $name)) {
|
|
||||||
$subject = $subjects->{$name};
|
// get name for subject
|
||||||
|
if($name === null) {
|
||||||
|
if(property_exists($input, '@id')) {
|
||||||
|
$name = $input->{'@id'};
|
||||||
}
|
}
|
||||||
else {
|
if(self::_isBlankNode($input)) {
|
||||||
$subject = $subjects->{$name} = new stdClass();
|
$name = $namer->getName($name);
|
||||||
}
|
|
||||||
|
|
||||||
$subject->{'@id'} = $name;
|
|
||||||
foreach($input as $prop => $objects) {
|
|
||||||
// skip @id
|
|
||||||
if($prop === '@id') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy non-@type keywords
|
|
||||||
if($prop !== '@type' && self::_isKeyword($prop)) {
|
|
||||||
$subject->{$prop} = $objects;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// iterate over objects
|
|
||||||
foreach($objects as $o) {
|
|
||||||
// handle embedded subject or subject reference
|
|
||||||
if(self::_isSubject($o) || self::_isSubjectReference($o)) {
|
|
||||||
// rename blank node @id
|
|
||||||
$id = property_exists($o, '@id') ? $o->{'@id'} : null;
|
|
||||||
if(self::_isBlankNode($o)) {
|
|
||||||
$id = $namer->getName($id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// add reference and recurse
|
|
||||||
self::addValue($subject, $prop, (object)array('@id' => $id), true);
|
|
||||||
$this->_flatten($subjects, $o, $namer, $id, null);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// recurse into list
|
|
||||||
if(self::_isList($o)) {
|
|
||||||
$l = new ArrayObject();
|
|
||||||
$this->_flatten($subjects, $o->{'@list'}, $namer, $name, $l);
|
|
||||||
$o = (object)array('@list' => (array)$l);
|
|
||||||
}
|
|
||||||
// special-handle @type blank nodes
|
|
||||||
else if($prop === '@type' && strpos($o, '_:') === 0) {
|
|
||||||
$o = $namer->getName($o);
|
|
||||||
}
|
|
||||||
|
|
||||||
// add non-subject
|
|
||||||
self::addValue($subject, $prop, $o, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// add non-object to list
|
|
||||||
else if($list !== null) {
|
// add subject reference to list
|
||||||
$list[] = $input;
|
if($list !== null) {
|
||||||
|
$list[] = (object)array('@id' => $name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// create new subject or merge into existing one
|
||||||
|
if(!property_exists($graphs, $graph)) {
|
||||||
|
$graphs->{$graph} = new stdClass();
|
||||||
|
}
|
||||||
|
$subjects = $graphs->{$graph};
|
||||||
|
if(!property_exists($subjects, $name)) {
|
||||||
|
$subjects->{$name} = new stdClass();
|
||||||
|
}
|
||||||
|
$subject = $subjects->{$name};
|
||||||
|
$subject->{'@id'} = $name;
|
||||||
|
foreach($input as $prop => $objects) {
|
||||||
|
// skip @id
|
||||||
|
if($prop === '@id') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// recurse into graph
|
||||||
|
if($prop === '@graph') {
|
||||||
|
// add graph subjects map entry
|
||||||
|
if(!property_exists($graphs, $name)) {
|
||||||
|
$graphs->{$name} = new stdClass();
|
||||||
|
}
|
||||||
|
$g = ($graph === '@merged') ? $graph : $name;
|
||||||
|
$this->_flatten($objects, $graphs, $g, $namer, null, null);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy non-@type keywords
|
||||||
|
if($prop !== '@type' && self::_isKeyword($prop)) {
|
||||||
|
$subject->{$prop} = $objects;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterate over objects
|
||||||
|
foreach($objects as $o) {
|
||||||
|
// handle embedded subject or subject reference
|
||||||
|
if(self::_isSubject($o) || self::_isSubjectReference($o)) {
|
||||||
|
// rename blank node @id
|
||||||
|
$id = property_exists($o, '@id') ? $o->{'@id'} : null;
|
||||||
|
if(self::_isBlankNode($o)) {
|
||||||
|
$id = $namer->getName($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add reference and recurse
|
||||||
|
self::addValue($subject, $prop, (object)array('@id' => $id), true);
|
||||||
|
$this->_flatten($o, $graphs, $graph, $namer, $id, null);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// recurse into list
|
||||||
|
if(self::_isList($o)) {
|
||||||
|
$_list = new ArrayObject();
|
||||||
|
$this->_flatten(
|
||||||
|
$o->{'@list'}, $graphs, $graph, $namer, $name, $_list);
|
||||||
|
$o = (object)array('@list' => (array)$_list);
|
||||||
|
}
|
||||||
|
// special-handle @type blank nodes
|
||||||
|
else if($prop === '@type' && strpos($o, '_:') === 0) {
|
||||||
|
$o = $namer->getName($o);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add non-subject
|
||||||
|
self::addValue($subject, $prop, $o, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue