Update file formatting to PSR-2 (with tabs)

- Use short array syntax
- Bump required PHP version to PHP 5.4
- Update version to 1.0.0
This commit is contained in:
Hypolite Petovan 2018-09-27 19:59:13 -04:00
commit 23cd99e8fb
3 changed files with 6385 additions and 6301 deletions

View file

@ -24,7 +24,7 @@
}
],
"require": {
"php": ">=5.3.0",
"php": ">=5.4.0",
"ext-json": "*"
},
"autoload": {

2676
jsonld.php
View file

@ -1,4 +1,5 @@
<?php
/**
* PHP implementation of the JSON-LD API.
* Version: 0.4.8-dev
@ -48,7 +49,8 @@
*
* @return mixed the compacted JSON-LD output.
*/
function jsonld_compact($input, $ctx, $options=array()) {
function jsonld_compact($input, $ctx, $options = [])
{
$p = new JsonLdProcessor();
return $p->compact($input, $ctx, $options);
}
@ -63,7 +65,8 @@ function jsonld_compact($input, $ctx, $options=array()) {
*
* @return array the expanded JSON-LD output.
*/
function jsonld_expand($input, $options=array()) {
function jsonld_expand($input, $options = [])
{
$p = new JsonLdProcessor();
return $p->expand($input, $options);
}
@ -80,7 +83,8 @@ function jsonld_expand($input, $options=array()) {
*
* @return mixed the flattened JSON-LD output.
*/
function jsonld_flatten($input, $ctx, $options=array()) {
function jsonld_flatten($input, $ctx, $options = [])
{
$p = new JsonLdProcessor();
return $p->flatten($input, $ctx, $options);
}
@ -100,7 +104,8 @@ function jsonld_flatten($input, $ctx, $options=array()) {
*
* @return stdClass the framed JSON-LD output.
*/
function jsonld_frame($input, $frame, $options=array()) {
function jsonld_frame($input, $frame, $options = [])
{
$p = new JsonLdProcessor();
return $p->frame($input, $frame, $options);
}
@ -119,16 +124,17 @@ function jsonld_frame($input, $frame, $options=array()) {
*
* @return the linked JSON-LD output.
*/
function jsonld_link($input, $ctx, $options) {
function jsonld_link($input, $ctx, $options)
{
// API matches running frame with a wildcard frame and embed: '@link'
// get arguments
$frame = new stdClass();
if($ctx) {
if ($ctx) {
$frame->{'@context'} = $ctx;
}
$frame->{'@embed'} = '@link';
return jsonld_frame($input, $frame, $options);
};
}
/**
* Performs RDF dataset normalization on the given input. The input is
@ -146,7 +152,8 @@ function jsonld_link($input, $ctx, $options) {
*
* @return mixed the normalized output.
*/
function jsonld_normalize($input, $options=array()) {
function jsonld_normalize($input, $options = [])
{
$p = new JsonLdProcessor();
return $p->normalize($input, $options);
}
@ -166,7 +173,8 @@ function jsonld_normalize($input, $options=array()) {
*
* @return array the JSON-LD output.
*/
function jsonld_from_rdf($input, $options=array()) {
function jsonld_from_rdf($input, $options = [])
{
$p = new JsonLdProcessor();
return $p->fromRDF($input, $options);
}
@ -185,7 +193,8 @@ function jsonld_from_rdf($input, $options=array()) {
*
* @return mixed the resulting RDF dataset (or a serialization of it).
*/
function jsonld_to_rdf($input, $options=array()) {
function jsonld_to_rdf($input, $options = [])
{
$p = new JsonLdProcessor();
return $p->toRDF($input, $options);
}
@ -201,9 +210,10 @@ function jsonld_to_rdf($input, $options=array()) {
*
* @return the encoded JSON data.
*/
function jsonld_encode($input, $options=0, $depth=512) {
function jsonld_encode($input, $options = 0, $depth = 512)
{
// newer PHP has a flag to avoid escaped '/'
if(defined('JSON_UNESCAPED_SLASHES')) {
if (defined('JSON_UNESCAPED_SLASHES')) {
return json_encode($input, JSON_UNESCAPED_SLASHES | $options, $depth);
}
// use a simple string replacement of '\/' to '/'.
@ -217,7 +227,8 @@ function jsonld_encode($input, $options=0, $depth=512) {
*
* @return mixed the resolved JSON-LD object, null on error.
*/
function jsonld_decode($input) {
function jsonld_decode($input)
{
return json_decode($input);
}
@ -241,32 +252,34 @@ function jsonld_decode($input) {
*
* @return assoc the parsed result.
*/
function jsonld_parse_link_header($header) {
$rval = array();
function jsonld_parse_link_header($header)
{
$rval = [];
// split on unbracketed/unquoted commas
if(!preg_match_all(
'/(?:<[^>]*?>|"[^"]*?"|[^,])+/', $header, $entries, PREG_SET_ORDER)) {
if (!preg_match_all('/(?:<[^>]*?>|"[^"]*?"|[^,])+/', $header, $entries, PREG_SET_ORDER)) {
return $rval;
}
$r_link_header = '/\s*<([^>]*?)>\s*(?:;\s*(.*))?/';
foreach($entries as $entry) {
if(!preg_match($r_link_header, $entry[0], $match)) {
foreach ($entries as $entry) {
if (!preg_match($r_link_header, $entry[0], $match)) {
continue;
}
$result = (object)array('target' => $match[1]);
$result = (object) ['target' => $match[1]];
$params = $match[2];
$r_params = '/(.*?)=(?:(?:"([^"]*?)")|([^"]*?))\s*(?:(?:;\s*)|$)/';
preg_match_all($r_params, $params, $matches, PREG_SET_ORDER);
foreach($matches as $match) {
foreach ($matches as $match) {
$result->{$match[1]} = $match[2] ?: $match[3];
}
$rel = property_exists($result, 'rel') ? $result->rel : '';
if(!isset($rval[$rel])) {
if (!isset($rval[$rel])) {
$rval[$rel] = $result;
} else if(is_array($rval[$rel])) {
} else if (is_array($rval[$rel])) {
$rval[$rel][] = $result;
} else {
$rval[$rel] = array($rval[$rel], $result);
$rval[$rel] = [$rval[$rel], $result];
}
}
return $rval;
@ -277,7 +290,8 @@ function jsonld_parse_link_header($header) {
*
* @param mixed input the JSON-LD input.
*/
function jsonld_relabel_blank_nodes($input) {
function jsonld_relabel_blank_nodes($input)
{
$p = new JsonLdProcessor();
return $p->_labelBlankNodes(new UniqueNamer('_:b'), $input);
}
@ -298,7 +312,8 @@ $jsonld_default_load_document = 'jsonld_default_document_loader';
*
* @param callable load_document(url) the document loader.
*/
function jsonld_set_document_loader($load_document) {
function jsonld_set_document_loader($load_document)
{
global $jsonld_default_load_document;
$jsonld_default_load_document = $load_document;
}
@ -310,18 +325,20 @@ function jsonld_set_document_loader($load_document) {
*
* @return the JSON-LD.
*/
function jsonld_get_url($url) {
function jsonld_get_url($url)
{
global $jsonld_default_load_document;
if($jsonld_default_load_document !== null) {
if ($jsonld_default_load_document !== null) {
$document_loader = $jsonld_default_load_document;
} else {
$document_loader = 'jsonld_default_document_loader';
}
$remote_doc = call_user_func($document_loader, $url);
if($remote_doc) {
if ($remote_doc) {
return $remote_doc->document;
}
return null;
}
@ -332,28 +349,29 @@ function jsonld_get_url($url) {
*
* @return stdClass the RemoteDocument object.
*/
function jsonld_default_document_loader($url) {
$doc = (object)array(
'contextUrl' => null, 'document' => null, 'documentUrl' => $url);
$redirects = array();
function jsonld_default_document_loader($url)
{
$doc = (object) [
'contextUrl' => null, 'document' => null, 'documentUrl' => $url];
$redirects = [];
$opts = array(
'http' => array(
$opts = [
'http' => [
'method' => 'GET',
'header' =>
"Accept: application/ld+json\r\n"),
"Accept: application/ld+json\r\n"],
/* Note: Use jsonld_default_secure_document_loader for security. */
'ssl' => array(
'ssl' => [
'verify_peer' => false,
'allow_self_signed' => true)
);
'allow_self_signed' => true]
];
$context = stream_context_create($opts);
$content_type = null;
stream_context_set_params($context, array('notification' =>
stream_context_set_params($context, ['notification' =>
function($notification_code, $severity, $message) use (
&$redirects, &$content_type) {
switch($notification_code) {
switch ($notification_code) {
case STREAM_NOTIFY_REDIRECTED:
$redirects[] = $message;
break;
@ -361,46 +379,49 @@ function jsonld_default_document_loader($url) {
$content_type = $message;
break;
};
}));
}]);
$result = @file_get_contents($url, false, $context);
if($result === false) {
if ($result === false) {
throw new JsonLdException(
'Could not retrieve a JSON-LD document from the URL: ' . $url,
'jsonld.LoadDocumentError', 'loading document failed');
'Could not retrieve a JSON-LD document from the URL: ' . $url, 'jsonld.LoadDocumentError', 'loading document failed');
}
$link_header = array();
foreach($http_response_header as $header) {
if(strpos($header, 'link') === 0) {
$link_header = [];
foreach ($http_response_header as $header) {
if (strpos($header, 'link') === 0) {
$value = explode(': ', $header);
if(count($value) > 1) {
if (count($value) > 1) {
$link_header[] = $value[1];
}
}
}
$link_header = jsonld_parse_link_header(join(',', $link_header));
if(isset($link_header['http://www.w3.org/ns/json-ld#context'])) {
if (isset($link_header['http://www.w3.org/ns/json-ld#context'])) {
$link_header = $link_header['http://www.w3.org/ns/json-ld#context'];
} else {
$link_header = null;
}
if($link_header && $content_type !== 'application/ld+json') {
if ($link_header && $content_type !== 'application/ld+json') {
// only 1 related link header permitted
if(is_array($link_header)) {
if (is_array($link_header)) {
throw new JsonLdException(
'URL could not be dereferenced, it has more than one ' .
'associated HTTP Link Header.', 'jsonld.LoadDocumentError',
'multiple context link headers', array('url' => $url));
'associated HTTP Link Header.', 'jsonld.LoadDocumentError', 'multiple context link headers', ['url' => $url]);
}
$doc->{'contextUrl'} = $link_header->target;
}
// update document url based on redirects
$redirs = count($redirects);
if($redirs > 0) {
if ($redirs > 0) {
$url = $redirects[$redirs - 1];
}
$doc->document = $result;
$doc->documentUrl = $url;
return $doc;
}
@ -411,33 +432,36 @@ function jsonld_default_document_loader($url) {
*
* @return stdClass the RemoteDocument object.
*/
function jsonld_default_secure_document_loader($url) {
if(strpos($url, 'https') !== 0) {
function jsonld_default_secure_document_loader($url)
{
if (strpos($url, 'https') !== 0) {
throw new JsonLdException(
"Could not GET url: '$url'; 'https' is required.",
'jsonld.LoadDocumentError', 'loading document failed');
'jsonld.LoadDocumentError',
'loading document failed'
);
}
$doc = (object)array(
'contextUrl' => null, 'document' => null, 'documentUrl' => $url);
$redirects = array();
$doc = (object) [
'contextUrl' => null, 'document' => null, 'documentUrl' => $url];
$redirects = [];
// default JSON-LD https GET implementation
$opts = array(
'http' => array(
$opts = [
'http' => [
'method' => 'GET',
'header' =>
"Accept: application/ld+json\r\n"),
'ssl' => array(
"Accept: application/ld+json\r\n"],
'ssl' => [
'verify_peer' => true,
'allow_self_signed' => false,
'cafile' => '/etc/ssl/certs/ca-certificates.crt'));
'cafile' => '/etc/ssl/certs/ca-certificates.crt']];
$context = stream_context_create($opts);
$content_type = null;
stream_context_set_params($context, array('notification' =>
stream_context_set_params($context, ['notification' =>
function($notification_code, $severity, $message) use (
&$redirects, &$content_type) {
switch($notification_code) {
switch ($notification_code) {
case STREAM_NOTIFY_REDIRECTED:
$redirects[] = $message;
break;
@ -445,50 +469,54 @@ function jsonld_default_secure_document_loader($url) {
$content_type = $message;
break;
};
}));
}]);
$result = @file_get_contents($url, false, $context);
if($result === false) {
if ($result === false) {
throw new JsonLdException(
'Could not retrieve a JSON-LD document from the URL: ' + $url,
'jsonld.LoadDocumentError', 'loading document failed');
'Could not retrieve a JSON-LD document from the URL: ' + $url, 'jsonld.LoadDocumentError', 'loading document failed');
}
$link_header = array();
foreach($http_response_header as $header) {
if(strpos($header, 'link') === 0) {
$link_header = [];
foreach ($http_response_header as $header) {
if (strpos($header, 'link') === 0) {
$value = explode(': ', $header);
if(count($value) > 1) {
if (count($value) > 1) {
$link_header[] = $value[1];
}
}
}
$link_header = jsonld_parse_link_header(join(',', $link_header));
if(isset($link_header['http://www.w3.org/ns/json-ld#context'])) {
if (isset($link_header['http://www.w3.org/ns/json-ld#context'])) {
$link_header = $link_header['http://www.w3.org/ns/json-ld#context'];
} else {
$link_header = null;
}
if($link_header && $content_type !== 'application/ld+json') {
if ($link_header && $content_type !== 'application/ld+json') {
// only 1 related link header permitted
if(is_array($link_header)) {
if (is_array($link_header)) {
throw new JsonLdException(
'URL could not be dereferenced, it has more than one ' .
'associated HTTP Link Header.', 'jsonld.LoadDocumentError',
'multiple context link headers', array('url' => $url));
'associated HTTP Link Header.', 'jsonld.LoadDocumentError', 'multiple context link headers', ['url' => $url]);
}
$doc->{'contextUrl'} = $link_header->target;
}
// update document url based on redirects
foreach($redirects as $redirect) {
if(strpos($redirect, 'https') !== 0) {
foreach ($redirects as $redirect) {
if (strpos($redirect, 'https') !== 0) {
throw new JsonLdException(
"Could not GET redirected url: '$redirect'; 'https' is required.",
'jsonld.LoadDocumentError', 'loading document failed');
'jsonld.LoadDocumentError',
'loading document failed'
);
}
$url = $redirect;
}
$doc->document = $result;
$doc->documentUrl = $url;
return $doc;
}
@ -505,7 +533,8 @@ $jsonld_rdf_parsers = new stdClass();
* @param callable $parser(input) the parser function (takes a string as
* a parameter and returns an RDF dataset).
*/
function jsonld_register_rdf_parser($content_type, $parser) {
function jsonld_register_rdf_parser($content_type, $parser)
{
global $jsonld_rdf_parsers;
$jsonld_rdf_parsers->{$content_type} = $parser;
}
@ -515,9 +544,10 @@ function jsonld_register_rdf_parser($content_type, $parser) {
*
* @param string $content_type the content-type for the parser.
*/
function jsonld_unregister_rdf_parser($content_type) {
function jsonld_unregister_rdf_parser($content_type)
{
global $jsonld_rdf_parsers;
if(property_exists($jsonld_rdf_parsers, $content_type)) {
if (property_exists($jsonld_rdf_parsers, $content_type)) {
unset($jsonld_rdf_parsers->{$content_type});
}
}
@ -529,45 +559,46 @@ function jsonld_unregister_rdf_parser($content_type) {
*
* @return assoc the parsed URL.
*/
function jsonld_parse_url($url) {
if($url === null) {
function jsonld_parse_url($url)
{
if ($url === null) {
$url = '';
}
$keys = array(
$keys = [
'href', 'protocol', 'scheme', '?authority', 'authority',
'?auth', 'auth', 'user', 'pass', 'host', '?port', 'port', 'path',
'?query', 'query', '?fragment', 'fragment');
'?query', 'query', '?fragment', 'fragment'];
$regex = "/^(([^:\/?#]+):)?(\/\/(((([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(:(\d*))?))?([^?#]*)(\?([^#]*))?(#(.*))?/";
preg_match($regex, $url, $match);
$rval = array();
$flags = array();
$rval = [];
$flags = [];
$len = count($keys);
for($i = 0; $i < $len; ++$i) {
for ($i = 0; $i < $len; ++$i) {
$key = $keys[$i];
if(strpos($key, '?') === 0) {
if (strpos($key, '?') === 0) {
$flags[substr($key, 1)] = !empty($match[$i]);
} else if(!isset($match[$i])) {
} else if (!isset($match[$i])) {
$rval[$key] = null;
} else {
$rval[$key] = $match[$i];
}
}
if(!$flags['authority']) {
if (!$flags['authority']) {
$rval['authority'] = null;
}
if(!$flags['auth']) {
if (!$flags['auth']) {
$rval['auth'] = $rval['user'] = $rval['pass'] = null;
}
if(!$flags['port']) {
if (!$flags['port']) {
$rval['port'] = null;
}
if(!$flags['query']) {
if (!$flags['query']) {
$rval['query'] = null;
}
if(!$flags['fragment']) {
if (!$flags['fragment']) {
$rval['fragment'] = null;
}
@ -583,24 +614,25 @@ function jsonld_parse_url($url) {
* @param string $path the path to remove dot segments from.
* @param bool $has_authority true if the URL has an authority, false if not.
*/
function jsonld_remove_dot_segments($path, $has_authority) {
function jsonld_remove_dot_segments($path, $has_authority)
{
$rval = '';
if(strpos($path, '/') === 0) {
if (strpos($path, '/') === 0) {
$rval = '/';
}
// RFC 3986 5.2.4 (reworked)
$input = explode('/', $path);
$output = array();
while(count($input) > 0) {
if($input[0] === '.' || ($input[0] === '' && count($input) > 1)) {
$output = [];
while (count($input) > 0) {
if ($input[0] === '.' || ($input[0] === '' && count($input) > 1)) {
array_shift($input);
continue;
}
if($input[0] === '..') {
if ($input[0] === '..') {
array_shift($input);
if($has_authority ||
if ($has_authority ||
(count($output) > 0 && $output[count($output) - 1] !== '..')) {
array_pop($output);
} else {
@ -623,19 +655,20 @@ function jsonld_remove_dot_segments($path, $has_authority) {
*
* @return string the absolute IRI.
*/
function jsonld_prepend_base($base, $iri) {
function jsonld_prepend_base($base, $iri)
{
// skip IRI processing
if($base === null) {
if ($base === null) {
return $iri;
}
// already an absolute IRI
if(strpos($iri, ':') !== false) {
if (strpos($iri, ':') !== false) {
return $iri;
}
// parse base if it is a string
if(is_string($base)) {
if (is_string($base)) {
$base = jsonld_parse_url($base);
}
@ -643,24 +676,24 @@ function jsonld_prepend_base($base, $iri) {
$rel = jsonld_parse_url($iri);
// per RFC3986 5.2.2
$transform = array('protocol' => $base['protocol']);
$transform = ['protocol' => $base['protocol']];
if($rel['authority'] !== null) {
if ($rel['authority'] !== null) {
$transform['authority'] = $rel['authority'];
$transform['path'] = $rel['path'];
$transform['query'] = $rel['query'];
} else {
$transform['authority'] = $base['authority'];
if($rel['path'] === '') {
if ($rel['path'] === '') {
$transform['path'] = $base['path'];
if($rel['query'] !== null) {
if ($rel['query'] !== null) {
$transform['query'] = $rel['query'];
} else {
$transform['query'] = $base['query'];
}
} else {
if(strpos($rel['path'], '/') === 0) {
if (strpos($rel['path'], '/') === 0) {
// IRI represents an absolute path
$transform['path'] = $rel['path'];
} else {
@ -668,11 +701,11 @@ function jsonld_prepend_base($base, $iri) {
$path = $base['path'];
// append relative path to the end of the last directory from base
if($rel['path'] !== '') {
if ($rel['path'] !== '') {
$idx = strrpos($path, '/');
$idx = ($idx === false) ? 0 : $idx + 1;
$path = substr($path, 0, $idx);
if(strlen($path) > 0 && substr($path, -1) !== '/') {
if (strlen($path) > 0 && substr($path, -1) !== '/') {
$path .= '/';
}
$path .= $rel['path'];
@ -690,19 +723,19 @@ function jsonld_prepend_base($base, $iri) {
// construct URL
$rval = $transform['protocol'];
if($transform['authority'] !== null) {
if ($transform['authority'] !== null) {
$rval .= '//' . $transform['authority'];
}
$rval .= $transform['path'];
if($transform['query'] !== null) {
if ($transform['query'] !== null) {
$rval .= '?' . $transform['query'];
}
if($rel['fragment'] !== null) {
if ($rel['fragment'] !== null) {
$rval .= '#' . $rel['fragment'];
}
// handle empty base
if($rval === '') {
if ($rval === '') {
$rval = './';
}
@ -718,27 +751,28 @@ function jsonld_prepend_base($base, $iri) {
* @return string the relative IRI if relative to base, otherwise the absolute
* IRI.
*/
function jsonld_remove_base($base, $iri) {
function jsonld_remove_base($base, $iri)
{
// skip IRI processing
if($base === null) {
if ($base === null) {
return $iri;
}
if(is_string($base)) {
if (is_string($base)) {
$base = jsonld_parse_url($base);
}
// establish base root
$root = '';
if($base['href'] !== '') {
if ($base['href'] !== '') {
$root .= "{$base['protocol']}//{$base['authority']}";
} else if(strpos($iri, '//') === false) {
} else if (strpos($iri, '//') === false) {
// support network-path reference with empty base
$root .= '//';
}
// IRI not relative to base
if($root === '' || strpos($iri, $root) !== 0) {
if ($root === '' || strpos($iri, $root) !== 0) {
return $iri;
}
@ -750,8 +784,8 @@ function jsonld_remove_base($base, $iri) {
$base_segments = explode('/', $base['normalizedPath']);
$iri_segments = explode('/', $rel['normalizedPath']);
$last = ($rel['query'] || $rel['fragment']) ? 0 : 1;
while(count($base_segments) > 0 && count($iri_segments) > $last) {
if($base_segments[0] !== $iri_segments[0]) {
while (count($base_segments) > 0 && count($iri_segments) > $last) {
if ($base_segments[0] !== $iri_segments[0]) {
break;
}
array_shift($base_segments);
@ -760,11 +794,11 @@ function jsonld_remove_base($base, $iri) {
// use '../' for each non-matching base segment
$rval = '';
if(count($base_segments) > 0) {
if (count($base_segments) > 0) {
// don't count the last segment (if it ends with '/' last path doesn't
// count and if it doesn't end with '/' it isn't a path)
array_pop($base_segments);
foreach($base_segments as $segment) {
foreach ($base_segments as $segment) {
$rval .= '../';
}
}
@ -773,25 +807,26 @@ function jsonld_remove_base($base, $iri) {
$rval .= implode('/', $iri_segments);
// add query and hash
if($rel['query'] !== null) {
if ($rel['query'] !== null) {
$rval .= "?{$rel['query']}";
}
if($rel['fragment'] !== null) {
if ($rel['fragment'] !== null) {
$rval .= "#{$rel['fragment']}";
}
if($rval === '') {
if ($rval === '') {
$rval = './';
}
return $rval;
}
/**
* A JSON-LD processor.
*/
class JsonLdProcessor {
class JsonLdProcessor
{
/** XSD constants */
const XSD_BOOLEAN = 'http://www.w3.org/2001/XMLSchema#boolean';
const XSD_DOUBLE = 'http://www.w3.org/2001/XMLSchema#double';
@ -804,8 +839,7 @@ class JsonLdProcessor {
const RDF_REST = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest';
const RDF_NIL = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil';
const RDF_TYPE = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type';
const RDF_LANGSTRING =
'http://www.w3.org/1999/02/22-rdf-syntax-ns#langString';
const RDF_LANGSTRING = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#langString';
/** Restraints */
const MAX_CONTEXT_URLS = 10;
@ -816,7 +850,10 @@ class JsonLdProcessor {
/**
* Constructs a JSON-LD processor.
*/
public function __construct() {}
public function __construct()
{
}
/**
* Performs JSON-LD compaction.
@ -835,44 +872,43 @@ class JsonLdProcessor {
*
* @return mixed the compacted JSON-LD output.
*/
public function compact($input, $ctx, $options) {
public function compact($input, $ctx, $options)
{
global $jsonld_default_load_document;
if($ctx === null) {
if ($ctx === null) {
throw new JsonLdException(
'The compaction context must not be null.',
'jsonld.CompactError', 'invalid local context');
'The compaction context must not be null.', 'jsonld.CompactError', 'invalid local context');
}
// nothing to compact
if($input === null) {
if ($input === null) {
return null;
}
self::setdefaults($options, array(
self::setdefaults($options, [
'base' => is_string($input) ? $input : '',
'compactArrays' => true,
'graph' => false,
'skipExpansion' => false,
'activeCtx' => false,
'documentLoader' => $jsonld_default_load_document,
'link' => false));
if($options['link']) {
'link' => false]);
if ($options['link']) {
// force skip expansion when linking, "link" is not part of the
// public API, it should only be called from framing
$options['skipExpansion'] = true;
}
if($options['skipExpansion'] === true) {
if ($options['skipExpansion'] === true) {
$expanded = $input;
} else {
// expand input
try {
$expanded = $this->expand($input, $options);
} catch(JsonLdException $e) {
} catch (JsonLdException $e) {
throw new JsonLdException(
'Could not expand input before compaction.',
'jsonld.CompactError', null, null, $e);
'Could not expand input before compaction.', 'jsonld.CompactError', null, null, $e);
}
}
@ -880,31 +916,30 @@ class JsonLdProcessor {
$active_ctx = $this->_getInitialContext($options);
try {
$active_ctx = $this->processContext($active_ctx, $ctx, $options);
} catch(JsonLdException $e) {
} catch (JsonLdException $e) {
throw new JsonLdException(
'Could not process context before compaction.',
'jsonld.CompactError', null, null, $e);
'Could not process context before compaction.', 'jsonld.CompactError', null, null, $e);
}
// do compaction
$compacted = $this->_compact($active_ctx, null, $expanded, $options);
if($options['compactArrays'] &&
if ($options['compactArrays'] &&
!$options['graph'] && is_array($compacted)) {
if(count($compacted) === 1) {
if (count($compacted) === 1) {
// simplify to a single item
$compacted = $compacted[0];
} else if(count($compacted) === 0) {
} else if (count($compacted) === 0) {
// simplify to an empty object
$compacted = new stdClass();
}
} else if($options['graph']) {
} else if ($options['graph']) {
// always use array if graph option is on
$compacted = self::arrayify($compacted);
}
// follow @context key
if(is_object($ctx) && property_exists($ctx, '@context')) {
if (is_object($ctx) && property_exists($ctx, '@context')) {
$ctx = $ctx->{'@context'};
}
@ -914,9 +949,9 @@ class JsonLdProcessor {
// remove empty contexts
$tmp = $ctx;
$ctx = array();
foreach($tmp as $v) {
if(!is_object($v) || count(array_keys((array)$v)) > 0) {
$ctx = [];
foreach ($tmp as $v) {
if (!is_object($v) || count(array_keys((array) $v)) > 0) {
$ctx[] = $v;
}
}
@ -924,32 +959,32 @@ class JsonLdProcessor {
// remove array if only one context
$ctx_length = count($ctx);
$has_context = ($ctx_length > 0);
if($ctx_length === 1) {
if ($ctx_length === 1) {
$ctx = $ctx[0];
}
// add context and/or @graph
if(is_array($compacted)) {
if (is_array($compacted)) {
// use '@graph' keyword
$kwgraph = $this->_compactIri($active_ctx, '@graph');
$graph = $compacted;
$compacted = new stdClass();
if($has_context) {
if ($has_context) {
$compacted->{'@context'} = $ctx;
}
$compacted->{$kwgraph} = $graph;
} else if(is_object($compacted) && $has_context) {
} else if (is_object($compacted) && $has_context) {
// reorder keys so @context is first
$graph = $compacted;
$compacted = new stdClass();
$compacted->{'@context'} = $ctx;
foreach($graph as $k => $v) {
foreach ($graph as $k => $v) {
$compacted->{$k} = $v;
}
}
if($options['activeCtx']) {
return array('compacted' => $compacted, 'activeCtx' => $active_ctx);
if ($options['activeCtx']) {
return ['compacted' => $compacted, 'activeCtx' => $active_ctx];
}
return $compacted;
@ -968,53 +1003,51 @@ class JsonLdProcessor {
*
* @return array the expanded JSON-LD output.
*/
public function expand($input, $options) {
public function expand($input, $options)
{
global $jsonld_default_load_document;
self::setdefaults($options, array(
self::setdefaults($options, [
'keepFreeFloatingNodes' => false,
'documentLoader' => $jsonld_default_load_document));
'documentLoader' => $jsonld_default_load_document]);
// if input is a string, attempt to dereference remote document
if(is_string($input)) {
if (is_string($input)) {
$remote_doc = call_user_func($options['documentLoader'], $input);
} else {
$remote_doc = (object)array(
$remote_doc = (object) [
'contextUrl' => null,
'documentUrl' => null,
'document' => $input);
'document' => $input];
}
try {
if($remote_doc->document === null) {
if ($remote_doc->document === null) {
throw new JsonLdException(
'No remote document found at the given URL.',
'jsonld.NullRemoteDocument');
'No remote document found at the given URL.', 'jsonld.NullRemoteDocument');
}
if(is_string($remote_doc->document)) {
if (is_string($remote_doc->document)) {
$remote_doc->document = self::_parse_json($remote_doc->document);
}
} catch(Exception $e) {
} catch (Exception $e) {
throw new JsonLdException(
'Could not retrieve a JSON-LD document from the URL.',
'jsonld.LoadDocumentError', 'loading document failed',
array('remoteDoc' => $remote_doc), $e);
'Could not retrieve a JSON-LD document from the URL.', 'jsonld.LoadDocumentError', 'loading document failed', ['remoteDoc' => $remote_doc], $e);
}
// set default base
self::setdefault($options, 'base', $remote_doc->documentUrl ?: '');
// build meta-object and retrieve all @context urls
$input = (object)array(
$input = (object) [
'document' => self::copy($remote_doc->document),
'remoteContext' => (object)array(
'@context' => $remote_doc->contextUrl));
if(isset($options['expandContext'])) {
'remoteContext' => (object) [
'@context' => $remote_doc->contextUrl]];
if (isset($options['expandContext'])) {
$expand_context = self::copy($options['expandContext']);
if(is_object($expand_context) &&
if (is_object($expand_context) &&
property_exists($expand_context, '@context')) {
$input->expandContext = $expand_context;
} else {
$input->expandContext = (object)array('@context' => $expand_context);
$input->expandContext = (object) ['@context' => $expand_context];
}
}
@ -1022,10 +1055,9 @@ class JsonLdProcessor {
try {
$this->_retrieveContextUrls(
$input, new stdClass(), $options['documentLoader'], $options['base']);
} catch(Exception $e) {
} catch (Exception $e) {
throw new JsonLdException(
'Could not perform JSON-LD expansion.',
'jsonld.ExpandError', null, null, $e);
'Could not perform JSON-LD expansion.', 'jsonld.ExpandError', null, null, $e);
}
$active_ctx = $this->_getInitialContext($options);
@ -1033,13 +1065,13 @@ class JsonLdProcessor {
$remote_context = $input->remoteContext->{'@context'};
// process optional expandContext
if(property_exists($input, 'expandContext')) {
if (property_exists($input, 'expandContext')) {
$active_ctx = self::_processContext(
$active_ctx, $input->expandContext, $options);
}
// process remote context from HTTP Link Header
if($remote_context) {
if ($remote_context) {
$active_ctx = self::_processContext(
$active_ctx, $remote_context, $options);
}
@ -1048,11 +1080,11 @@ class JsonLdProcessor {
$expanded = $this->_expand($active_ctx, null, $document, $options, false);
// optimize away @graph with no other properties
if(is_object($expanded) && property_exists($expanded, '@graph') &&
count(array_keys((array)$expanded)) === 1) {
if (is_object($expanded) && property_exists($expanded, '@graph') &&
count(array_keys((array) $expanded)) === 1) {
$expanded = $expanded->{'@graph'};
} else if($expanded === null) {
$expanded = array();
} else if ($expanded === null) {
$expanded = [];
}
// normalize to an array
return self::arrayify($expanded);
@ -1070,25 +1102,25 @@ class JsonLdProcessor {
*
* @return array the flattened output.
*/
public function flatten($input, $ctx, $options) {
public function flatten($input, $ctx, $options)
{
global $jsonld_default_load_document;
self::setdefaults($options, array(
self::setdefaults($options, [
'base' => is_string($input) ? $input : '',
'documentLoader' => $jsonld_default_load_document));
'documentLoader' => $jsonld_default_load_document]);
try {
// expand input
$expanded = $this->expand($input, $options);
} catch(Exception $e) {
} catch (Exception $e) {
throw new JsonLdException(
'Could not expand input before flattening.',
'jsonld.FlattenError', null, null, $e);
'Could not expand input before flattening.', 'jsonld.FlattenError', null, null, $e);
}
// do flattening
$flattened = $this->_flatten($expanded);
if($ctx === null) {
if ($ctx === null) {
return $flattened;
}
@ -1097,10 +1129,9 @@ class JsonLdProcessor {
$options['skipExpansion'] = true;
try {
$compacted = $this->compact($flattened, $ctx, $options);
} catch(Exception $e) {
} catch (Exception $e) {
throw new JsonLdException(
'Could not compact flattened output.',
'jsonld.FlattenError', null, null, $e);
'Could not compact flattened output.', 'jsonld.FlattenError', null, null, $e);
}
return $compacted;
@ -1123,50 +1154,48 @@ class JsonLdProcessor {
*
* @return stdClass the framed JSON-LD output.
*/
public function frame($input, $frame, $options) {
public function frame($input, $frame, $options)
{
global $jsonld_default_load_document;
self::setdefaults($options, array(
self::setdefaults($options, [
'base' => is_string($input) ? $input : '',
'compactArrays' => true,
'embed' => '@last',
'explicit' => false,
'requireAll' => true,
'omitDefault' => false,
'documentLoader' => $jsonld_default_load_document));
'documentLoader' => $jsonld_default_load_document]);
// if frame is a string, attempt to dereference remote document
if(is_string($frame)) {
if (is_string($frame)) {
$remote_frame = call_user_func($options['documentLoader'], $frame);
} else {
$remote_frame = (object)array(
$remote_frame = (object) [
'contextUrl' => null,
'documentUrl' => null,
'document' => $frame);
'document' => $frame];
}
try {
if($remote_frame->document === null) {
if ($remote_frame->document === null) {
throw new JsonLdException(
'No remote document found at the given URL.',
'jsonld.NullRemoteDocument');
'No remote document found at the given URL.', 'jsonld.NullRemoteDocument');
}
if(is_string($remote_frame->document)) {
if (is_string($remote_frame->document)) {
$remote_frame->document = self::_parse_json($remote_frame->document);
}
} catch(Exception $e) {
} catch (Exception $e) {
throw new JsonLdException(
'Could not retrieve a JSON-LD document from the URL.',
'jsonld.LoadDocumentError', 'loading document failed',
array('remoteDoc' => $remote_frame), $e);
'Could not retrieve a JSON-LD document from the URL.', 'jsonld.LoadDocumentError', 'loading document failed', ['remoteDoc' => $remote_frame], $e);
}
// preserve frame context
$frame = $remote_frame->document;
if($frame !== null) {
if ($frame !== null) {
$ctx = (property_exists($frame, '@context') ?
$frame->{'@context'} : new stdClass());
if($remote_frame->contextUrl !== null) {
if($ctx !== null) {
if ($remote_frame->contextUrl !== null) {
if ($ctx !== null) {
$ctx = $remote_frame->contextUrl;
} else {
$ctx = self::arrayify($ctx);
@ -1179,10 +1208,9 @@ class JsonLdProcessor {
try {
// expand input
$expanded = $this->expand($input, $options);
} catch(Exception $e) {
} catch (Exception $e) {
throw new JsonLdException(
'Could not expand input before framing.',
'jsonld.FrameError', null, null, $e);
'Could not expand input before framing.', 'jsonld.FrameError', null, null, $e);
}
try {
@ -1190,10 +1218,9 @@ class JsonLdProcessor {
$opts = $options;
$opts['keepFreeFloatingNodes'] = true;
$expanded_frame = $this->expand($frame, $opts);
} catch(Exception $e) {
} catch (Exception $e) {
throw new JsonLdException(
'Could not expand frame before framing.',
'jsonld.FrameError', null, null, $e);
'Could not expand frame before framing.', 'jsonld.FrameError', null, null, $e);
}
// do framing
@ -1207,10 +1234,9 @@ class JsonLdProcessor {
$options['link'] = new ArrayObject();
$options['activeCtx'] = true;
$result = $this->compact($framed, $ctx, $options);
} catch(Exception $e) {
} catch (Exception $e) {
throw new JsonLdException(
'Could not compact framed output.',
'jsonld.FrameError', null, null, $e);
'Could not compact framed output.', 'jsonld.FrameError', null, null, $e);
}
$compacted = $result['compacted'];
@ -1240,14 +1266,15 @@ class JsonLdProcessor {
*
* @return mixed the normalized output.
*/
public function normalize($input, $options) {
public function normalize($input, $options)
{
global $jsonld_default_load_document;
self::setdefaults($options, array(
self::setdefaults($options, [
'base' => is_string($input) ? $input : '',
'documentLoader' => $jsonld_default_load_document));
'documentLoader' => $jsonld_default_load_document]);
if(isset($options['inputFormat'])) {
if($options['inputFormat'] != 'application/nquads') {
if (isset($options['inputFormat'])) {
if ($options['inputFormat'] != 'application/nquads') {
throw new JsonLdException(
'Unknown normalization input format.', 'jsonld.NormalizeError');
}
@ -1256,15 +1283,14 @@ class JsonLdProcessor {
try {
// convert to RDF dataset then do normalization
$opts = $options;
if(isset($opts['format'])) {
if (isset($opts['format'])) {
unset($opts['format']);
}
$opts['produceGeneralizedRdf'] = false;
$dataset = $this->toRDF($input, $opts);
} catch(Exception $e) {
} catch (Exception $e) {
throw new JsonLdException(
'Could not convert input to RDF dataset before normalization.',
'jsonld.NormalizeError', null, null, $e);
'Could not convert input to RDF dataset before normalization.', 'jsonld.NormalizeError', null, null, $e);
}
}
@ -1287,30 +1313,30 @@ class JsonLdProcessor {
*
* @return array the JSON-LD output.
*/
public function fromRDF($dataset, $options) {
public function fromRDF($dataset, $options)
{
global $jsonld_rdf_parsers;
self::setdefaults($options, array(
self::setdefaults($options, [
'useRdfType' => false,
'useNativeTypes' => false));
'useNativeTypes' => false]);
if(!isset($options['format']) && is_string($dataset)) {
if (!isset($options['format']) && is_string($dataset)) {
// set default format to nquads
$options['format'] = 'application/nquads';
}
// handle special format
if(isset($options['format']) && $options['format']) {
if (isset($options['format']) && $options['format']) {
// supported formats (processor-specific and global)
if(($this->rdfParsers !== null &&
if (($this->rdfParsers !== null &&
!property_exists($this->rdfParsers, $options['format'])) ||
$this->rdfParsers === null &&
!property_exists($jsonld_rdf_parsers, $options['format'])) {
throw new JsonLdException(
'Unknown input format.',
'jsonld.UnknownFormat', null, array('format' => $options['format']));
'Unknown input format.', 'jsonld.UnknownFormat', null, ['format' => $options['format']]);
}
if($this->rdfParsers !== null) {
if ($this->rdfParsers !== null) {
$callable = $this->rdfParsers->{$options['format']};
} else {
$callable = $jsonld_rdf_parsers->{$options['format']};
@ -1337,35 +1363,35 @@ class JsonLdProcessor {
*
* @return mixed the resulting RDF dataset (or a serialization of it).
*/
public function toRDF($input, $options) {
public function toRDF($input, $options)
{
global $jsonld_default_load_document;
self::setdefaults($options, array(
self::setdefaults($options, [
'base' => is_string($input) ? $input : '',
'produceGeneralizedRdf' => false,
'documentLoader' => $jsonld_default_load_document));
'documentLoader' => $jsonld_default_load_document]);
try {
// expand input
$expanded = $this->expand($input, $options);
} catch(JsonLdException $e) {
} catch (JsonLdException $e) {
throw new JsonLdException(
'Could not expand input before serialization to RDF.',
'jsonld.RdfError', null, null, $e);
'Could not expand input before serialization to RDF.', 'jsonld.RdfError', null, null, $e);
}
// create node map for default graph (and any named graphs)
$namer = new UniqueNamer('_:b');
$node_map = (object)array('@default' => new stdClass());
$node_map = (object) ['@default' => new stdClass()];
$this->_createNodeMap($expanded, $node_map, '@default', $namer);
// output RDF dataset
$dataset = new stdClass();
$graph_names = array_keys((array)$node_map);
$graph_names = array_keys((array) $node_map);
sort($graph_names);
foreach($graph_names as $graph_name) {
foreach ($graph_names as $graph_name) {
$graph = $node_map->{$graph_name};
// skip relative IRIs
if($graph_name === '@default' || self::_isAbsoluteIri($graph_name)) {
if ($graph_name === '@default' || self::_isAbsoluteIri($graph_name)) {
$dataset->{$graph_name} = $this->_graphToRDF($graph, $namer, $options);
}
}
@ -1373,14 +1399,13 @@ class JsonLdProcessor {
$rval = $dataset;
// convert to output format
if(isset($options['format']) && $options['format']) {
if (isset($options['format']) && $options['format']) {
// supported formats
if($options['format'] === 'application/nquads') {
if ($options['format'] === 'application/nquads') {
$rval = self::toNQuads($dataset);
} else {
throw new JsonLdException(
'Unknown output format.', 'jsonld.UnknownFormat',
null, array('format' => $options['format']));
'Unknown output format.', 'jsonld.UnknownFormat', null, ['format' => $options['format']]);
}
}
@ -1398,31 +1423,30 @@ class JsonLdProcessor {
*
* @return stdClass the new active context.
*/
public function processContext($active_ctx, $local_ctx, $options) {
public function processContext($active_ctx, $local_ctx, $options)
{
global $jsonld_default_load_document;
self::setdefaults($options, array(
self::setdefaults($options, [
'base' => '',
'documentLoader' => $jsonld_default_load_document));
'documentLoader' => $jsonld_default_load_document]);
// return initial context early for null context
if($local_ctx === null) {
if ($local_ctx === null) {
return $this->_getInitialContext($options);
}
// retrieve URLs in local_ctx
$local_ctx = self::copy($local_ctx);
if(is_string($local_ctx) or (
if (is_string($local_ctx) or (
is_object($local_ctx) && !property_exists($local_ctx, '@context'))) {
$local_ctx = (object)array('@context' => $local_ctx);
$local_ctx = (object) ['@context' => $local_ctx];
}
try {
$this->_retrieveContextUrls(
$local_ctx, new stdClass(),
$options['documentLoader'], $options['base']);
} catch(Exception $e) {
$local_ctx, new stdClass(), $options['documentLoader'], $options['base']);
} catch (Exception $e) {
throw new JsonLdException(
'Could not process JSON-LD context.',
'jsonld.ContextError', null, null, $e);
'Could not process JSON-LD context.', 'jsonld.ContextError', null, null, $e);
}
// process context
@ -1437,9 +1461,10 @@ class JsonLdProcessor {
*
* @return bool true if the subject has the given property, false if not.
*/
public static function hasProperty($subject, $property) {
public static function hasProperty($subject, $property)
{
$rval = false;
if(property_exists($subject, $property)) {
if (property_exists($subject, $property)) {
$value = $subject->{$property};
$rval = (!is_array($value) || count($value) > 0);
}
@ -1455,22 +1480,23 @@ class JsonLdProcessor {
*
* @return bool true if the value exists, false if not.
*/
public static function hasValue($subject, $property, $value) {
public static function hasValue($subject, $property, $value)
{
$rval = false;
if(self::hasProperty($subject, $property)) {
if (self::hasProperty($subject, $property)) {
$val = $subject->{$property};
$is_list = self::_isList($val);
if(is_array($val) || $is_list) {
if($is_list) {
if (is_array($val) || $is_list) {
if ($is_list) {
$val = $val->{'@list'};
}
foreach($val as $v) {
if(self::compareValues($value, $v)) {
foreach ($val as $v) {
if (self::compareValues($value, $v)) {
$rval = true;
break;
}
}
} else if(!is_array($value)) {
} else if (!is_array($value)) {
// avoid matching the set of values with an array value parameter
$rval = self::compareValues($value, $val);
}
@ -1497,38 +1523,39 @@ class JsonLdProcessor {
* (default: true).
*/
public static function addValue(
$subject, $property, $value, $options=array()) {
self::setdefaults($options, array(
$subject, $property, $value, $options = [])
{
self::setdefaults($options, [
'allowDuplicate' => true,
'propertyIsArray' => false));
'propertyIsArray' => false]);
if(is_array($value)) {
if(count($value) === 0 && $options['propertyIsArray'] &&
if (is_array($value)) {
if (count($value) === 0 && $options['propertyIsArray'] &&
!property_exists($subject, $property)) {
$subject->{$property} = array();
$subject->{$property} = [];
}
foreach($value as $v) {
foreach ($value as $v) {
self::addValue($subject, $property, $v, $options);
}
} else if(property_exists($subject, $property)) {
} else if (property_exists($subject, $property)) {
// check if subject already has value if duplicates not allowed
$has_value = (!$options['allowDuplicate'] &&
self::hasValue($subject, $property, $value));
// make property an array if value not present or always an array
if(!is_array($subject->{$property}) &&
if (!is_array($subject->{$property}) &&
(!$has_value || $options['propertyIsArray'])) {
$subject->{$property} = array($subject->{$property});
$subject->{$property} = [$subject->{$property}];
}
// add new value
if(!$has_value) {
if (!$has_value) {
$subject->{$property}[] = $value;
}
} else {
// add new value as set or single value
$subject->{$property} = ($options['propertyIsArray'] ?
array($value) : $value);
[$value] : $value);
}
}
@ -1540,9 +1567,10 @@ class JsonLdProcessor {
*
* @return array all of the values for a subject's property as an array.
*/
public static function getValues($subject, $property) {
public static function getValues($subject, $property)
{
$rval = (property_exists($subject, $property) ?
$subject->{$property} : array());
$subject->{$property} : []);
return self::arrayify($rval);
}
@ -1552,7 +1580,8 @@ class JsonLdProcessor {
* @param stdClass $subject the subject.
* @param string $property the property.
*/
public static function removeProperty($subject, $property) {
public static function removeProperty($subject, $property)
{
unset($subject->{$property});
}
@ -1567,9 +1596,10 @@ class JsonLdProcessor {
* false if not (default: false).
*/
public static function removeValue(
$subject, $property, $value, $options=array()) {
self::setdefaults($options, array(
'propertyIsArray' => false));
$subject, $property, $value, $options = [])
{
self::setdefaults($options, [
'propertyIsArray' => false]);
// filter out value
$filter = function($e) use ($value) {
@ -1578,9 +1608,9 @@ class JsonLdProcessor {
$values = self::getValues($subject, $property);
$values = array_values(array_filter($values, $filter));
if(count($values) === 0) {
if (count($values) === 0) {
self::removeProperty($subject, $property);
} else if(count($values) === 1 && !$options['propertyIsArray']) {
} else if (count($values) === 1 && !$options['propertyIsArray']) {
$subject->{$property} = $values[0];
} else {
$subject->{$property} = $values;
@ -1601,14 +1631,15 @@ class JsonLdProcessor {
*
* @return bool true if v1 and v2 are considered equal, false if not.
*/
public static function compareValues($v1, $v2) {
public static function compareValues($v1, $v2)
{
// 1. equal primitives
if($v1 === $v2) {
if ($v1 === $v2) {
return true;
}
// 2. equal @values
if(self::_isValue($v1) && self::_isValue($v2)) {
if (self::_isValue($v1) && self::_isValue($v2)) {
return (
self::_compareKeyValues($v1, $v2, '@value') &&
self::_compareKeyValues($v1, $v2, '@type') &&
@ -1617,7 +1648,7 @@ class JsonLdProcessor {
}
// 3. equal @ids
if(is_object($v1) && property_exists($v1, '@id') &&
if (is_object($v1) && property_exists($v1, '@id') &&
is_object($v2) && property_exists($v2, '@id')) {
return $v1->{'@id'} === $v2->{'@id'};
}
@ -1636,30 +1667,31 @@ class JsonLdProcessor {
*
* @return mixed the value.
*/
public static function getContextValue($ctx, $key, $type) {
public static function getContextValue($ctx, $key, $type)
{
$rval = null;
// return null for invalid key
if($key === null) {
if ($key === null) {
return $rval;
}
// get default language
if($type === '@language' && property_exists($ctx, $type)) {
if ($type === '@language' && property_exists($ctx, $type)) {
$rval = $ctx->{$type};
}
// get specific entry information
if(property_exists($ctx->mappings, $key)) {
if (property_exists($ctx->mappings, $key)) {
$entry = $ctx->mappings->{$key};
if($entry === null) {
if ($entry === null) {
return null;
}
if($type === null) {
if ($type === null) {
// return whole entry
$rval = $entry;
} else if(property_exists($entry, $type)) {
} else if (property_exists($entry, $type)) {
// return entry value for type
$rval = $entry->{$type};
}
@ -1675,7 +1707,8 @@ class JsonLdProcessor {
*
* @return stdClass an RDF dataset.
*/
public static function parseNQuads($input) {
public static function parseNQuads($input)
{
// define partial regexes
$iri = '(?:<([^:]+:[^>]*)>)';
$bnode = '(_:(?:[A-Za-z][A-Za-z0-9]*))';
@ -1702,29 +1735,28 @@ class JsonLdProcessor {
// split N-Quad input into lines
$lines = preg_split($eoln, $input);
$line_number = 0;
foreach($lines as $line) {
foreach ($lines as $line) {
$line_number += 1;
// skip empty lines
if(preg_match($empty, $line)) {
if (preg_match($empty, $line)) {
continue;
}
// parse quad
if(!preg_match($quad, $line, $match)) {
if (!preg_match($quad, $line, $match)) {
throw new JsonLdException(
'Error while parsing N-Quads; invalid quad.',
'jsonld.ParseError', null, array('line' => $line_number));
'Error while parsing N-Quads; invalid quad.', 'jsonld.ParseError', null, ['line' => $line_number]);
}
// create RDF triple
$triple = (object)array(
$triple = (object) [
'subject' => new stdClass(),
'predicate' => new stdClass(),
'object' => new stdClass());
'object' => new stdClass()];
// get subject
if($match[1] !== '') {
if ($match[1] !== '') {
$triple->subject->type = 'IRI';
$triple->subject->value = $match[1];
} else {
@ -1737,21 +1769,19 @@ class JsonLdProcessor {
$triple->predicate->value = $match[3];
// get object
if($match[4] !== '') {
if ($match[4] !== '') {
$triple->object->type = 'IRI';
$triple->object->value = $match[4];
} else if($match[5] !== '') {
} else if ($match[5] !== '') {
$triple->object->type = 'blank node';
$triple->object->value = $match[5];
} else {
$triple->object->type = 'literal';
$unescaped = str_replace(
array('\"', '\t', '\n', '\r', '\\\\'),
array('"', "\t", "\n", "\r", '\\'),
$match[6]);
if(isset($match[7]) && $match[7] !== '') {
['\"', '\t', '\n', '\r', '\\\\'], ['"', "\t", "\n", "\r", '\\'], $match[6]);
if (isset($match[7]) && $match[7] !== '') {
$triple->object->datatype = $match[7];
} else if(isset($match[8]) && $match[8] !== '') {
} else if (isset($match[8]) && $match[8] !== '') {
$triple->object->datatype = self::RDF_LANGSTRING;
$triple->object->language = $match[8];
} else {
@ -1762,26 +1792,26 @@ class JsonLdProcessor {
// get graph name ('@default' is used for the default graph)
$name = '@default';
if(isset($match[9]) && $match[9] !== '') {
if (isset($match[9]) && $match[9] !== '') {
$name = $match[9];
} else if(isset($match[10]) && $match[10] !== '') {
} else if (isset($match[10]) && $match[10] !== '') {
$name = $match[10];
}
// initialize graph in dataset
if(!property_exists($dataset, $name)) {
$dataset->{$name} = array($triple);
if (!property_exists($dataset, $name)) {
$dataset->{$name} = [$triple];
} else {
// add triple if unique to its graph
$unique = true;
$triples = &$dataset->{$name};
foreach($triples as $t) {
if(self::_compareRDFTriples($t, $triple)) {
foreach ($triples as $t) {
if (self::_compareRDFTriples($t, $triple)) {
$unique = false;
break;
}
}
if($unique) {
if ($unique) {
$triples[] = $triple;
}
}
@ -1797,11 +1827,12 @@ class JsonLdProcessor {
*
* @return string the N-Quads string.
*/
public static function toNQuads($dataset) {
$quads = array();
foreach($dataset as $graph_name => $triples) {
foreach($triples as $triple) {
if($graph_name === '@default') {
public static function toNQuads($dataset)
{
$quads = [];
foreach ($dataset as $graph_name => $triples) {
foreach ($triples as $triple) {
if ($graph_name === '@default') {
$graph_name = null;
}
$quads[] = self::toNQuad($triple, $graph_name);
@ -1822,7 +1853,8 @@ class JsonLdProcessor {
*
* @return string the N-Quad string.
*/
public static function toNQuad($triple, $graph_name, $bnode=null) {
public static function toNQuad($triple, $graph_name, $bnode = null)
{
$s = $triple->subject;
$p = $triple->predicate;
$o = $triple->object;
@ -1831,9 +1863,9 @@ class JsonLdProcessor {
$quad = '';
// subject is an IRI
if($s->type === 'IRI') {
if ($s->type === 'IRI') {
$quad .= "<{$s->value}>";
} else if($bnode !== null) {
} else if ($bnode !== null) {
// bnode normalization mode
$quad .= ($s->value === $bnode) ? '_:a' : '_:z';
} else {
@ -1843,9 +1875,9 @@ class JsonLdProcessor {
$quad .= ' ';
// predicate is an IRI
if($p->type === 'IRI') {
if ($p->type === 'IRI') {
$quad .= "<{$p->value}>";
} else if($bnode !== null) {
} else if ($bnode !== null) {
// FIXME: TBD what to do with bnode predicates during normalization
// bnode normalization mode
$quad .= '_:p';
@ -1856,10 +1888,10 @@ class JsonLdProcessor {
$quad .= ' ';
// object is IRI, bnode, or literal
if($o->type === 'IRI') {
if ($o->type === 'IRI') {
$quad .= "<{$o->value}>";
} else if($o->type === 'blank node') {
if($bnode !== null) {
} else if ($o->type === 'blank node') {
if ($bnode !== null) {
// normalization mode
$quad .= ($o->value === $bnode) ? '_:a' : '_:z';
} else {
@ -1868,24 +1900,22 @@ class JsonLdProcessor {
}
} else {
$escaped = str_replace(
array('\\', "\t", "\n", "\r", '"'),
array('\\\\', '\t', '\n', '\r', '\"'),
$o->value);
['\\', "\t", "\n", "\r", '"'], ['\\\\', '\t', '\n', '\r', '\"'], $o->value);
$quad .= '"' . $escaped . '"';
if($o->datatype === self::RDF_LANGSTRING) {
if($o->language) {
if ($o->datatype === self::RDF_LANGSTRING) {
if ($o->language) {
$quad .= "@{$o->language}";
}
} else if($o->datatype !== self::XSD_STRING) {
} else if ($o->datatype !== self::XSD_STRING) {
$quad .= "^^<{$o->datatype}>";
}
}
// graph
if($g !== null) {
if(strpos($g, '_:') !== 0) {
if ($g !== null) {
if (strpos($g, '_:') !== 0) {
$quad .= " <$g>";
} else if($bnode) {
} else if ($bnode) {
$quad .= ' _:g';
} else {
$quad .= " $g";
@ -1904,8 +1934,9 @@ class JsonLdProcessor {
* @param callable $parser(input) the parser function (takes a string as
* a parameter and returns an RDF dataset).
*/
public function registerRDFParser($content_type, $parser) {
if($this->rdfParsers === null) {
public function registerRDFParser($content_type, $parser)
{
if ($this->rdfParsers === null) {
$this->rdfParsers = new stdClass();
}
$this->rdfParsers->{$content_type} = $parser;
@ -1918,11 +1949,12 @@ class JsonLdProcessor {
*
* @param string $content_type the content-type for the parser.
*/
public function unregisterRDFParser($content_type) {
if($this->rdfParsers !== null &&
public function unregisterRDFParser($content_type)
{
if ($this->rdfParsers !== null &&
property_exists($this->rdfParsers, $content_type)) {
unset($this->rdfParsers->{$content_type});
if(count(get_object_vars($content_type)) === 0) {
if (count(get_object_vars($content_type)) === 0) {
$this->rdfParsers = null;
}
}
@ -1936,8 +1968,9 @@ class JsonLdProcessor {
*
* @return array an array.
*/
public static function arrayify($value) {
return is_array($value) ? $value : array($value);
public static function arrayify($value)
{
return is_array($value) ? $value : [$value];
}
/**
@ -1947,8 +1980,9 @@ class JsonLdProcessor {
*
* @return mixed the cloned value.
*/
public static function copy($value) {
if(is_object($value) || is_array($value)) {
public static function copy($value)
{
if (is_object($value) || is_array($value)) {
return unserialize(serialize($value));
}
return $value;
@ -1962,7 +1996,8 @@ class JsonLdProcessor {
* @param string $key the key to update.
* @param mixed $value the value to set.
*/
public static function setdefault(&$arr, $key, $value) {
public static function setdefault(&$arr, $key, $value)
{
isset($arr[$key]) or $arr[$key] = $value;
}
@ -1972,8 +2007,9 @@ class JsonLdProcessor {
* @param &assoc $arr the object to update.
* @param assoc $defaults the default keys and values.
*/
public static function setdefaults(&$arr, $defaults) {
foreach($defaults as $key => $value) {
public static function setdefaults(&$arr, $defaults)
{
foreach ($defaults as $key => $value) {
self::setdefault($arr, $key, $value);
}
}
@ -1991,23 +2027,24 @@ class JsonLdProcessor {
* @return mixed the compacted value.
*/
protected function _compact(
$active_ctx, $active_property, $element, $options) {
$active_ctx, $active_property, $element, $options)
{
// recursively compact array
if(is_array($element)) {
$rval = array();
foreach($element as $e) {
if (is_array($element)) {
$rval = [];
foreach ($element as $e) {
// compact, dropping any null values
$compacted = $this->_compact(
$active_ctx, $active_property, $e, $options);
if($compacted !== null) {
if ($compacted !== null) {
$rval[] = $compacted;
}
}
if($options['compactArrays'] && count($rval) === 1) {
if ($options['compactArrays'] && count($rval) === 1) {
// use single element if no container is specified
$container = self::getContextValue(
$active_ctx, $active_property, '@container');
if($container === null) {
if ($container === null) {
$rval = $rval[0];
}
}
@ -2015,28 +2052,28 @@ class JsonLdProcessor {
}
// recursively compact object
if(is_object($element)) {
if($options['link'] && property_exists($element, '@id') &&
if (is_object($element)) {
if ($options['link'] && property_exists($element, '@id') &&
isset($options['link'][$element->{'@id'}])) {
// check for a linked element to reuse
$linked = $options['link'][$element->{'@id'}];
foreach($linked as $link) {
if($link['expanded'] === $element) {
foreach ($linked as $link) {
if ($link['expanded'] === $element) {
return $link['compacted'];
}
}
}
// do value compaction on @values and subject references
if(self::_isValue($element) || self::_isSubjectReference($element)) {
if (self::_isValue($element) || self::_isSubjectReference($element)) {
$rval = $this->_compactValue($active_ctx, $active_property, $element);
if($options['link'] && self::_isSubjectReference($element)) {
if ($options['link'] && self::_isSubjectReference($element)) {
// store linked element
if(!isset($options['link'][$element->{'@id'}])) {
$options['link'][$element->{'@id'}] = array();
if (!isset($options['link'][$element->{'@id'}])) {
$options['link'][$element->{'@id'}] = [];
}
$options['link'][$element->{'@id'}][] = array(
'expanded' => $element, 'compacted' => $rval);
$options['link'][$element->{'@id'}][] = [
'expanded' => $element, 'compacted' => $rval];
}
return $rval;
}
@ -2046,34 +2083,33 @@ class JsonLdProcessor {
$rval = new stdClass();
if($options['link'] && property_exists($element, '@id')) {
if ($options['link'] && property_exists($element, '@id')) {
// store linked element
if(!isset($options['link'][$element->{'@id'}])) {
$options['link'][$element->{'@id'}] = array();
if (!isset($options['link'][$element->{'@id'}])) {
$options['link'][$element->{'@id'}] = [];
}
$options['link'][$element->{'@id'}][] = array(
'expanded' => $element, 'compacted' => $rval);
$options['link'][$element->{'@id'}][] = [
'expanded' => $element, 'compacted' => $rval];
}
// process element keys in order
$keys = array_keys((array)$element);
$keys = array_keys((array) $element);
sort($keys);
foreach($keys as $expanded_property) {
foreach ($keys as $expanded_property) {
$expanded_value = $element->{$expanded_property};
// compact @id and @type(s)
if($expanded_property === '@id' || $expanded_property === '@type') {
if(is_string($expanded_value)) {
if ($expanded_property === '@id' || $expanded_property === '@type') {
if (is_string($expanded_value)) {
// compact single @id
$compacted_value = $this->_compactIri(
$active_ctx, $expanded_value, null,
array('vocab' => ($expanded_property === '@type')));
$active_ctx, $expanded_value, null, ['vocab' => ($expanded_property === '@type')]);
} else {
// expanded value must be a @type array
$compacted_value = array();
foreach($expanded_value as $ev) {
$compacted_value = [];
foreach ($expanded_value as $ev) {
$compacted_value[] = $this->_compactIri(
$active_ctx, $ev, null, array('vocab' => true));
$active_ctx, $ev, null, ['vocab' => true]);
}
}
@ -2082,20 +2118,19 @@ class JsonLdProcessor {
$is_array = (is_array($compacted_value) &&
count($expanded_value) === 0);
self::addValue(
$rval, $alias, $compacted_value,
array('propertyIsArray' => $is_array));
$rval, $alias, $compacted_value, ['propertyIsArray' => $is_array]);
continue;
}
// handle @reverse
if($expanded_property === '@reverse') {
if ($expanded_property === '@reverse') {
// recursively compact expanded value
$compacted_value = $this->_compact(
$active_ctx, '@reverse', $expanded_value, $options);
// handle double-reversed properties
foreach($compacted_value as $compacted_property => $value) {
if(property_exists($active_ctx->mappings, $compacted_property) &&
foreach ($compacted_value as $compacted_property => $value) {
if (property_exists($active_ctx->mappings, $compacted_property) &&
$active_ctx->mappings->{$compacted_property} &&
$active_ctx->mappings->{$compacted_property}->reverse) {
$container = self::getContextValue(
@ -2103,13 +2138,12 @@ class JsonLdProcessor {
$use_array = ($container === '@set' ||
!$options['compactArrays']);
self::addValue(
$rval, $compacted_property, $value,
array('propertyIsArray' => $use_array));
$rval, $compacted_property, $value, ['propertyIsArray' => $use_array]);
unset($compacted_value->{$compacted_property});
}
}
if(count(array_keys((array)$compacted_value)) > 0) {
if (count(array_keys((array) $compacted_value)) > 0) {
// use keyword alias and add value
$alias = $this->_compactIri($active_ctx, $expanded_property);
self::addValue($rval, $alias, $compacted_value);
@ -2119,11 +2153,11 @@ class JsonLdProcessor {
}
// handle @index property
if($expanded_property === '@index') {
if ($expanded_property === '@index') {
// drop @index if inside an @index container
$container = self::getContextValue(
$active_ctx, $active_property, '@container');
if($container === '@index') {
if ($container === '@index') {
continue;
}
@ -2134,7 +2168,7 @@ class JsonLdProcessor {
}
// skip array processing for keywords that aren't @graph or @list
if($expanded_property !== '@graph' && $expanded_property !== '@list' &&
if ($expanded_property !== '@graph' && $expanded_property !== '@list' &&
self::_isKeyword($expanded_property)) {
// use keyword alias and add value as is
$alias = $this->_compactIri($active_ctx, $expanded_property);
@ -2143,68 +2177,61 @@ class JsonLdProcessor {
}
// Note: expanded value must be an array due to expansion algorithm.
// preserve empty arrays
if(count($expanded_value) === 0) {
if (count($expanded_value) === 0) {
$item_active_property = $this->_compactIri(
$active_ctx, $expanded_property, $expanded_value,
array('vocab' => true), $inside_reverse);
$active_ctx, $expanded_property, $expanded_value, ['vocab' => true], $inside_reverse);
self::addValue(
$rval, $item_active_property, array(),
array('propertyIsArray' => true));
$rval, $item_active_property, [], ['propertyIsArray' => true]);
}
// recusively process array values
foreach($expanded_value as $expanded_item) {
foreach ($expanded_value as $expanded_item) {
// compact property and get container type
$item_active_property = $this->_compactIri(
$active_ctx, $expanded_property, $expanded_item,
array('vocab' => true), $inside_reverse);
$active_ctx, $expanded_property, $expanded_item, ['vocab' => true], $inside_reverse);
$container = self::getContextValue(
$active_ctx, $item_active_property, '@container');
// get @list value if appropriate
$is_list = self::_isList($expanded_item);
$list = null;
if($is_list) {
if ($is_list) {
$list = $expanded_item->{'@list'};
}
// recursively compact expanded item
$compacted_item = $this->_compact(
$active_ctx, $item_active_property,
$is_list ? $list : $expanded_item, $options);
$active_ctx, $item_active_property, $is_list ? $list : $expanded_item, $options);
// handle @list
if($is_list) {
if ($is_list) {
// ensure @list value is an array
$compacted_item = self::arrayify($compacted_item);
if($container !== '@list') {
if ($container !== '@list') {
// wrap using @list alias
$compacted_item = (object)array(
$this->_compactIri($active_ctx, '@list') => $compacted_item);
$compacted_item = (object) [
$this->_compactIri($active_ctx, '@list') => $compacted_item];
// include @index from expanded @list, if any
if(property_exists($expanded_item, '@index')) {
$compacted_item->{$this->_compactIri($active_ctx, '@index')} =
$expanded_item->{'@index'};
if (property_exists($expanded_item, '@index')) {
$compacted_item->{$this->_compactIri($active_ctx, '@index')} = $expanded_item->{'@index'};
}
} else if(property_exists($rval, $item_active_property)) {
} else if (property_exists($rval, $item_active_property)) {
// can't use @list container for more than 1 list
throw new JsonLdException(
'JSON-LD compact error; property has a "@list" @container ' .
'rule but there is more than a single @list that matches ' .
'the compacted term in the document. Compaction might mix ' .
'unwanted items into the list.', 'jsonld.SyntaxError',
'compaction to list of lists');
'unwanted items into the list.', 'jsonld.SyntaxError', 'compaction to list of lists');
}
}
// handle language and index maps
if($container === '@language' || $container === '@index') {
if ($container === '@language' || $container === '@index') {
// get or create the map object
if(property_exists($rval, $item_active_property)) {
if (property_exists($rval, $item_active_property)) {
$map_object = $rval->{$item_active_property};
} else {
$rval->{$item_active_property} = $map_object = new stdClass();
@ -2212,7 +2239,7 @@ class JsonLdProcessor {
// if container is a language map, simplify compacted value to
// a simple string
if($container === '@language' && self::_isValue($compacted_item)) {
if ($container === '@language' && self::_isValue($compacted_item)) {
$compacted_item = $compacted_item->{'@value'};
}
@ -2232,8 +2259,7 @@ class JsonLdProcessor {
// add compact value
self::addValue(
$rval, $item_active_property, $compacted_item,
array('propertyIsArray' => $is_array));
$rval, $item_active_property, $compacted_item, ['propertyIsArray' => $is_array]);
}
}
}
@ -2259,31 +2285,31 @@ class JsonLdProcessor {
* @return mixed the expanded value.
*/
protected function _expand(
$active_ctx, $active_property, $element, $options, $inside_list) {
$active_ctx, $active_property, $element, $options, $inside_list)
{
// nothing to expand
if($element === null) {
if ($element === null) {
return $element;
}
// recursively expand array
if(is_array($element)) {
$rval = array();
if (is_array($element)) {
$rval = [];
$container = self::getContextValue(
$active_ctx, $active_property, '@container');
$inside_list = $inside_list || $container === '@list';
foreach($element as $e) {
foreach ($element as $e) {
// expand element
$e = $this->_expand(
$active_ctx, $active_property, $e, $options, $inside_list);
if($inside_list && (is_array($e) || self::_isList($e))) {
if ($inside_list && (is_array($e) || self::_isList($e))) {
// lists of lists are illegal
throw new JsonLdException(
'Invalid JSON-LD syntax; lists of lists are not permitted.',
'jsonld.SyntaxError', 'list of lists');
'Invalid JSON-LD syntax; lists of lists are not permitted.', 'jsonld.SyntaxError', 'list of lists');
}
// drop null values
if($e !== null) {
if(is_array($e)) {
if ($e !== null) {
if (is_array($e)) {
$rval = array_merge($rval, $e);
} else {
$rval[] = $e;
@ -2293,12 +2319,11 @@ class JsonLdProcessor {
return $rval;
}
if(!is_object($element)) {
if (!is_object($element)) {
// drop free-floating scalars that are not in lists
if(!$inside_list &&
if (!$inside_list &&
($active_property === null ||
$this->_expandIri($active_ctx, $active_property,
array('vocab' => true)) === '@graph')) {
$this->_expandIri($active_ctx, $active_property, ['vocab' => true]) === '@graph')) {
return null;
}
@ -2307,166 +2332,146 @@ class JsonLdProcessor {
}
// recursively expand object:
// if element has a context, process it
if(property_exists($element, '@context')) {
if (property_exists($element, '@context')) {
$active_ctx = $this->_processContext(
$active_ctx, $element->{'@context'}, $options);
}
// expand the active property
$expanded_active_property = $this->_expandIri(
$active_ctx, $active_property, array('vocab' => true));
$active_ctx, $active_property, ['vocab' => true]);
$rval = new stdClass();
$keys = array_keys((array)$element);
$keys = array_keys((array) $element);
sort($keys);
foreach($keys as $key) {
foreach ($keys as $key) {
$value = $element->{$key};
if($key === '@context') {
if ($key === '@context') {
continue;
}
// expand key to IRI
$expanded_property = $this->_expandIri(
$active_ctx, $key, array('vocab' => true));
$active_ctx, $key, ['vocab' => true]);
// drop non-absolute IRI keys that aren't keywords
if($expanded_property === null ||
if ($expanded_property === null ||
!(self::_isAbsoluteIri($expanded_property) ||
self::_isKeyword($expanded_property))) {
continue;
}
if(self::_isKeyword($expanded_property)) {
if($expanded_active_property === '@reverse') {
if (self::_isKeyword($expanded_property)) {
if ($expanded_active_property === '@reverse') {
throw new JsonLdException(
'Invalid JSON-LD syntax; a keyword cannot be used as a @reverse ' .
'property.', 'jsonld.SyntaxError', 'invalid reverse property map',
array('value' => $value));
'property.', 'jsonld.SyntaxError', 'invalid reverse property map', ['value' => $value]);
}
if(property_exists($rval, $expanded_property)) {
if (property_exists($rval, $expanded_property)) {
throw new JsonLdException(
'Invalid JSON-LD syntax; colliding keywords detected.',
'jsonld.SyntaxError', 'colliding keywords',
array('keyword' => $expanded_property));
'Invalid JSON-LD syntax; colliding keywords detected.', 'jsonld.SyntaxError', 'colliding keywords', ['keyword' => $expanded_property]);
}
}
// syntax error if @id is not a string
if($expanded_property === '@id' && !is_string($value)) {
if(!isset($options['isFrame']) || !$options['isFrame']) {
if ($expanded_property === '@id' && !is_string($value)) {
if (!isset($options['isFrame']) || !$options['isFrame']) {
throw new JsonLdException(
'Invalid JSON-LD syntax; "@id" value must a string.',
'jsonld.SyntaxError', 'invalid @id value',
array('value' => $value));
'Invalid JSON-LD syntax; "@id" value must a string.', 'jsonld.SyntaxError', 'invalid @id value', ['value' => $value]);
}
if(!is_object($value)) {
if (!is_object($value)) {
throw new JsonLdException(
'Invalid JSON-LD syntax; "@id" value must a string or an object.',
'jsonld.SyntaxError', 'invalid @id value',
array('value' => $value));
'Invalid JSON-LD syntax; "@id" value must a string or an object.', 'jsonld.SyntaxError', 'invalid @id value', ['value' => $value]);
}
}
// validate @type value
if($expanded_property === '@type') {
if ($expanded_property === '@type') {
$this->_validateTypeValue($value);
}
// @graph must be an array or an object
if($expanded_property === '@graph' &&
if ($expanded_property === '@graph' &&
!(is_object($value) || is_array($value))) {
throw new JsonLdException(
'Invalid JSON-LD syntax; "@graph" value must not be an ' .
'object or an array.', 'jsonld.SyntaxError',
'invalid @graph value', array('value' => $value));
'object or an array.', 'jsonld.SyntaxError', 'invalid @graph value', ['value' => $value]);
}
// @value must not be an object or an array
if($expanded_property === '@value' &&
if ($expanded_property === '@value' &&
(is_object($value) || is_array($value))) {
throw new JsonLdException(
'Invalid JSON-LD syntax; "@value" value must not be an ' .
'object or an array.', 'jsonld.SyntaxError',
'invalid value object value', array('value' => $value));
'object or an array.', 'jsonld.SyntaxError', 'invalid value object value', ['value' => $value]);
}
// @language must be a string
if($expanded_property === '@language') {
if($value === null) {
if ($expanded_property === '@language') {
if ($value === null) {
// drop null @language values, they expand as if they didn't exist
continue;
}
if(!is_string($value)) {
if (!is_string($value)) {
throw new JsonLdException(
'Invalid JSON-LD syntax; "@language" value must not be a string.',
'jsonld.SyntaxError', 'invalid language-tagged string',
array('value' => $value));
'Invalid JSON-LD syntax; "@language" value must not be a string.', 'jsonld.SyntaxError', 'invalid language-tagged string', ['value' => $value]);
}
// ensure language value is lowercase
$value = strtolower($value);
}
// @index must be a string
if($expanded_property === '@index') {
if(!is_string($value)) {
if ($expanded_property === '@index') {
if (!is_string($value)) {
throw new JsonLdException(
'Invalid JSON-LD syntax; "@index" value must be a string.',
'jsonld.SyntaxError', 'invalid @index value',
array('value' => $value));
'Invalid JSON-LD syntax; "@index" value must be a string.', 'jsonld.SyntaxError', 'invalid @index value', ['value' => $value]);
}
}
// @reverse must be an object
if($expanded_property === '@reverse') {
if(!is_object($value)) {
if ($expanded_property === '@reverse') {
if (!is_object($value)) {
throw new JsonLdException(
'Invalid JSON-LD syntax; "@reverse" value must be an object.',
'jsonld.SyntaxError', 'invalid @reverse value',
array('value' => $value));
'Invalid JSON-LD syntax; "@reverse" value must be an object.', 'jsonld.SyntaxError', 'invalid @reverse value', ['value' => $value]);
}
$expanded_value = $this->_expand(
$active_ctx, '@reverse', $value, $options, $inside_list);
// properties double-reversed
if(property_exists($expanded_value, '@reverse')) {
foreach($expanded_value->{'@reverse'} as $rproperty => $rvalue) {
if (property_exists($expanded_value, '@reverse')) {
foreach ($expanded_value->{'@reverse'} as $rproperty => $rvalue) {
self::addValue(
$rval, $rproperty, $rvalue, array('propertyIsArray' => true));
$rval, $rproperty, $rvalue, ['propertyIsArray' => true]);
}
}
// FIXME: can this be merged with code below to simplify?
// merge in all reversed properties
if(property_exists($rval, '@reverse')) {
if (property_exists($rval, '@reverse')) {
$reverse_map = $rval->{'@reverse'};
} else {
$reverse_map = null;
}
foreach($expanded_value as $property => $items) {
if($property === '@reverse') {
foreach ($expanded_value as $property => $items) {
if ($property === '@reverse') {
continue;
}
if($reverse_map === null) {
if ($reverse_map === null) {
$reverse_map = $rval->{'@reverse'} = new stdClass();
}
self::addValue(
$reverse_map, $property, array(),
array('propertyIsArray' => true));
foreach($items as $item) {
if(self::_isValue($item) || self::_isList($item)) {
$reverse_map, $property, [], ['propertyIsArray' => true]);
foreach ($items as $item) {
if (self::_isValue($item) || self::_isList($item)) {
throw new JsonLdException(
'Invalid JSON-LD syntax; "@reverse" value must not be a ' +
'@value or an @list.', 'jsonld.SyntaxError',
'invalid reverse property value',
array('value' => $expanded_value));
'@value or an @list.', 'jsonld.SyntaxError', 'invalid reverse property value', ['value' => $expanded_value]);
}
self::addValue(
$reverse_map, $property, $item,
array('propertyIsArray' => true));
$reverse_map, $property, $item, ['propertyIsArray' => true]);
}
}
@ -2475,20 +2480,20 @@ class JsonLdProcessor {
$container = self::getContextValue($active_ctx, $key, '@container');
if($container === '@language' && is_object($value)) {
if ($container === '@language' && is_object($value)) {
// handle language map container (skip if value is not an object)
$expanded_value = $this->_expandLanguageMap($value);
} else if($container === '@index' && is_object($value)) {
} else if ($container === '@index' && is_object($value)) {
// handle index container (skip if value is not an object)
$expanded_value = array();
$value_keys = array_keys((array)$value);
$expanded_value = [];
$value_keys = array_keys((array) $value);
sort($value_keys);
foreach($value_keys as $value_key) {
foreach ($value_keys as $value_key) {
$val = $value->{$value_key};
$val = self::arrayify($val);
$val = $this->_expand($active_ctx, $key, $val, $options, false);
foreach($val as $item) {
if(!property_exists($item, '@index')) {
foreach ($val as $item) {
if (!property_exists($item, '@index')) {
$item->{'@index'} = $value_key;
}
$expanded_value[] = $item;
@ -2497,17 +2502,16 @@ class JsonLdProcessor {
} else {
// recurse into @list or @set
$is_list = ($expanded_property === '@list');
if($is_list || $expanded_property === '@set') {
if ($is_list || $expanded_property === '@set') {
$next_active_property = $active_property;
if($is_list && $expanded_active_property === '@graph') {
if ($is_list && $expanded_active_property === '@graph') {
$next_active_property = null;
}
$expanded_value = $this->_expand(
$active_ctx, $next_active_property, $value, $options, $is_list);
if($is_list && self::_isList($expanded_value)) {
if ($is_list && self::_isList($expanded_value)) {
throw new JsonLdException(
'Invalid JSON-LD syntax; lists of lists are not permitted.',
'jsonld.SyntaxError', 'list of lists');
'Invalid JSON-LD syntax; lists of lists are not permitted.', 'jsonld.SyntaxError', 'list of lists');
}
} else {
// recursively expand value with key as new active property
@ -2517,40 +2521,37 @@ class JsonLdProcessor {
}
// drop null values if property is not @value
if($expanded_value === null && $expanded_property !== '@value') {
if ($expanded_value === null && $expanded_property !== '@value') {
continue;
}
// convert expanded value to @list if container specifies it
if($expanded_property !== '@list' && !self::_isList($expanded_value) &&
if ($expanded_property !== '@list' && !self::_isList($expanded_value) &&
$container === '@list') {
// ensure expanded value is an array
$expanded_value = (object)array(
'@list' => self::arrayify($expanded_value));
$expanded_value = (object) [
'@list' => self::arrayify($expanded_value)];
}
// FIXME: can this be merged with code above to simplify?
// merge in reverse properties
if(property_exists($active_ctx->mappings, $key) &&
if (property_exists($active_ctx->mappings, $key) &&
$active_ctx->mappings->{$key} &&
$active_ctx->mappings->{$key}->reverse) {
if(property_exists($rval, '@reverse')) {
if (property_exists($rval, '@reverse')) {
$reverse_map = $rval->{'@reverse'};
} else {
$reverse_map = $rval->{'@reverse'} = new stdClass();
}
$expanded_value = self::arrayify($expanded_value);
foreach($expanded_value as $item) {
if(self::_isValue($item) || self::_isList($item)) {
foreach ($expanded_value as $item) {
if (self::_isValue($item) || self::_isList($item)) {
throw new JsonLdException(
'Invalid JSON-LD syntax; "@reverse" value must not be a ' +
'@value or an @list.', 'jsonld.SyntaxError',
'invalid reverse property value',
array('value' => $expanded_value));
'@value or an @list.', 'jsonld.SyntaxError', 'invalid reverse property value', ['value' => $expanded_value]);
}
self::addValue(
$reverse_map, $expanded_property, $item,
array('propertyIsArray' => true));
$reverse_map, $expanded_property, $item, ['propertyIsArray' => true]);
}
continue;
}
@ -2558,95 +2559,86 @@ class JsonLdProcessor {
// add value for property
// use an array except for certain keywords
$use_array = (!in_array(
$expanded_property, array(
'@index', '@id', '@type', '@value', '@language')));
$expanded_property, [
'@index', '@id', '@type', '@value', '@language']));
self::addValue(
$rval, $expanded_property, $expanded_value,
array('propertyIsArray' => $use_array));
$rval, $expanded_property, $expanded_value, ['propertyIsArray' => $use_array]);
}
// get property count on expanded output
$keys = array_keys((array)$rval);
$keys = array_keys((array) $rval);
$count = count($keys);
// @value must only have @language or @type
if(property_exists($rval, '@value')) {
if (property_exists($rval, '@value')) {
// @value must only have @language or @type
if(property_exists($rval, '@type') &&
if (property_exists($rval, '@type') &&
property_exists($rval, '@language')) {
throw new JsonLdException(
'Invalid JSON-LD syntax; an element containing "@value" may not ' .
'contain both "@type" and "@language".',
'jsonld.SyntaxError', 'invalid value object',
array('element' => $rval));
'contain both "@type" and "@language".', 'jsonld.SyntaxError', 'invalid value object', ['element' => $rval]);
}
$valid_count = $count - 1;
if(property_exists($rval, '@type')) {
if (property_exists($rval, '@type')) {
$valid_count -= 1;
}
if(property_exists($rval, '@index')) {
if (property_exists($rval, '@index')) {
$valid_count -= 1;
}
if(property_exists($rval, '@language')) {
if (property_exists($rval, '@language')) {
$valid_count -= 1;
}
if($valid_count !== 0) {
if ($valid_count !== 0) {
throw new JsonLdException(
'Invalid JSON-LD syntax; an element containing "@value" may only ' .
'have an "@index" property and at most one other property ' .
'which can be "@type" or "@language".',
'jsonld.SyntaxError', 'invalid value object',
array('element' => $rval));
'which can be "@type" or "@language".', 'jsonld.SyntaxError', 'invalid value object', ['element' => $rval]);
}
// drop null @values
if($rval->{'@value'} === null) {
if ($rval->{'@value'} === null) {
$rval = null;
} else if(property_exists($rval, '@language') &&
} else if (property_exists($rval, '@language') &&
!is_string($rval->{'@value'})) {
// if @language is present, @value must be a string
throw new JsonLdException(
'Invalid JSON-LD syntax; only strings may be language-tagged.',
'jsonld.SyntaxError', 'invalid language-tagged value',
array('element' => $rval));
} else if(property_exists($rval, '@type') &&
'Invalid JSON-LD syntax; only strings may be language-tagged.', 'jsonld.SyntaxError', 'invalid language-tagged value', ['element' => $rval]);
} else if (property_exists($rval, '@type') &&
(!self::_isAbsoluteIri($rval->{'@type'}) ||
strpos($rval->{'@type'}, '_:') === 0)) {
throw new JsonLdException(
'Invalid JSON-LD syntax; an element containing "@value" ' .
'and "@type" must have an absolute IRI for the value ' .
'of "@type".', 'jsonld.SyntaxError', 'invalid typed value',
array('element' => $rval));
'of "@type".', 'jsonld.SyntaxError', 'invalid typed value', ['element' => $rval]);
}
} else if(property_exists($rval, '@type') && !is_array($rval->{'@type'})) {
} else if (property_exists($rval, '@type') && !is_array($rval->{'@type'})) {
// convert @type to an array
$rval->{'@type'} = array($rval->{'@type'});
} else if(property_exists($rval, '@set') ||
$rval->{'@type'} = [$rval->{'@type'}];
} else if (property_exists($rval, '@set') ||
property_exists($rval, '@list')) {
// handle @set and @list
if($count > 1 && !($count === 2 && property_exists($rval, '@index'))) {
if ($count > 1 && !($count === 2 && property_exists($rval, '@index'))) {
throw new JsonLdException(
'Invalid JSON-LD syntax; if an element has the property "@set" ' .
'or "@list", then it can have at most one other property that is ' .
'"@index".', 'jsonld.SyntaxError', 'invalid set or list object',
array('element' => $rval));
'"@index".', 'jsonld.SyntaxError', 'invalid set or list object', ['element' => $rval]);
}
// optimize away @set
if(property_exists($rval, '@set')) {
if (property_exists($rval, '@set')) {
$rval = $rval->{'@set'};
$keys = array_keys((array)$rval);
$keys = array_keys((array) $rval);
$count = count($keys);
}
} else if($count === 1 && property_exists($rval, '@language')) {
} else if ($count === 1 && property_exists($rval, '@language')) {
// drop objects with only @language
$rval = null;
}
// drop certain top-level objects that do not occur in lists
if(is_object($rval) &&
if (is_object($rval) &&
!$options['keepFreeFloatingNodes'] && !$inside_list &&
($active_property === null || $expanded_active_property === '@graph')) {
// drop empty object or top-level @value/@list, or object with only @id
if($count === 0 || property_exists($rval, '@value') ||
if ($count === 0 || property_exists($rval, '@value') ||
property_exists($rval, '@list') ||
($count === 1 && property_exists($rval, '@id'))) {
$rval = null;
@ -2663,47 +2655,48 @@ class JsonLdProcessor {
*
* @return array the flattened output.
*/
protected function _flatten($input) {
protected function _flatten($input)
{
// produce a map of all subjects and name each bnode
$namer = new UniqueNamer('_:b');
$graphs = (object)array('@default' => new stdClass());
$graphs = (object) ['@default' => new stdClass()];
$this->_createNodeMap($input, $graphs, '@default', $namer);
// add all non-default graphs to default graph
$default_graph = $graphs->{'@default'};
$graph_names = array_keys((array)$graphs);
foreach($graph_names as $graph_name) {
if($graph_name === '@default') {
$graph_names = array_keys((array) $graphs);
foreach ($graph_names as $graph_name) {
if ($graph_name === '@default') {
continue;
}
$node_map = $graphs->{$graph_name};
if(!property_exists($default_graph, $graph_name)) {
$default_graph->{$graph_name} = (object)array(
'@id' => $graph_name, '@graph' => array());
if (!property_exists($default_graph, $graph_name)) {
$default_graph->{$graph_name} = (object) [
'@id' => $graph_name, '@graph' => []];
}
$subject = $default_graph->{$graph_name};
if(!property_exists($subject, '@graph')) {
$subject->{'@graph'} = array();
if (!property_exists($subject, '@graph')) {
$subject->{'@graph'} = [];
}
$ids = array_keys((array)$node_map);
$ids = array_keys((array) $node_map);
sort($ids);
foreach($ids as $id) {
foreach ($ids as $id) {
$node = $node_map->{$id};
// only add full subjects
if(!self::_isSubjectReference($node)) {
if (!self::_isSubjectReference($node)) {
$subject->{'@graph'}[] = $node;
}
}
}
// produce flattened output
$flattened = array();
$keys = array_keys((array)$default_graph);
$flattened = [];
$keys = array_keys((array) $default_graph);
sort($keys);
foreach($keys as $key) {
foreach ($keys as $key) {
$node = $default_graph->{$key};
// only add full subjects to top-level
if(!self::_isSubjectReference($node)) {
if (!self::_isSubjectReference($node)) {
$flattened[] = $node;
}
}
@ -2719,15 +2712,16 @@ class JsonLdProcessor {
*
* @return array the framed output.
*/
protected function _frame($input, $frame, $options) {
protected function _frame($input, $frame, $options)
{
// create framing state
$state = (object)array(
$state = (object) [
'options' => $options,
'graphs' => (object)array(
'graphs' => (object) [
'@default' => new stdClass(),
'@merged' => new stdClass()),
'subjectStack' => array(),
'link' => new stdClass());
'@merged' => new stdClass()],
'subjectStack' => [],
'link' => new stdClass()];
// produce a map of all graphs and name each bnode
// FIXME: currently uses subjects from @merged graph only
@ -2737,10 +2731,10 @@ class JsonLdProcessor {
// frame the subjects
$framed = new ArrayObject();
$keys = array_keys((array)$state->subjects);
$keys = array_keys((array) $state->subjects);
sort($keys);
$this->_matchFrame($state, $keys, $frame, $framed, null);
return (array)$framed;
return (array) $framed;
}
/**
@ -2751,35 +2745,36 @@ class JsonLdProcessor {
*
* @return mixed the normalized output.
*/
protected function _normalize($dataset, $options) {
protected function _normalize($dataset, $options)
{
// create quads and map bnodes to their associated quads
$quads = array();
$quads = [];
$bnodes = new stdClass();
foreach($dataset as $graph_name => $triples) {
if($graph_name === '@default') {
foreach ($dataset as $graph_name => $triples) {
if ($graph_name === '@default') {
$graph_name = null;
}
foreach($triples as $triple) {
foreach ($triples as $triple) {
$quad = $triple;
if($graph_name !== null) {
if(strpos($graph_name, '_:') === 0) {
$quad->name = (object)array(
'type' => 'blank node', 'value' => $graph_name);
if ($graph_name !== null) {
if (strpos($graph_name, '_:') === 0) {
$quad->name = (object) [
'type' => 'blank node', 'value' => $graph_name];
} else {
$quad->name = (object)array(
'type' => 'IRI', 'value' => $graph_name);
$quad->name = (object) [
'type' => 'IRI', 'value' => $graph_name];
}
}
$quads[] = $quad;
foreach(array('subject', 'object', 'name') as $attr) {
if(property_exists($quad, $attr) &&
foreach (['subject', 'object', 'name'] as $attr) {
if (property_exists($quad, $attr) &&
$quad->{$attr}->type === 'blank node') {
$id = $quad->{$attr}->value;
if(property_exists($bnodes, $id)) {
if (property_exists($bnodes, $id)) {
$bnodes->{$id}->quads[] = $quad;
} else {
$bnodes->{$id} = (object)array('quads' => array($quad));
$bnodes->{$id} = (object) ['quads' => [$quad]];
}
}
}
@ -2791,23 +2786,23 @@ class JsonLdProcessor {
// continue to hash bnode quads while bnodes are assigned names
$unnamed = null;
$nextUnnamed = array_keys((array)$bnodes);
$nextUnnamed = array_keys((array) $bnodes);
$duplicates = null;
do {
$unnamed = $nextUnnamed;
$nextUnnamed = array();
$nextUnnamed = [];
$duplicates = new stdClass();
$unique = new stdClass();
foreach($unnamed as $bnode) {
foreach ($unnamed as $bnode) {
// hash quads for each unnamed bnode
$hash = $this->_hashQuads($bnode, $bnodes, $namer);
// store hash as unique or a duplicate
if(property_exists($duplicates, $hash)) {
if (property_exists($duplicates, $hash)) {
$duplicates->{$hash}[] = $bnode;
$nextUnnamed[] = $bnode;
} else if(property_exists($unique, $hash)) {
$duplicates->{$hash} = array($unique->{$hash}, $bnode);
} else if (property_exists($unique, $hash)) {
$duplicates->{$hash} = [$unique->{$hash}, $bnode];
$nextUnnamed[] = $unique->{$hash};
$nextUnnamed[] = $bnode;
unset($unique->{$hash});
@ -2817,24 +2812,23 @@ class JsonLdProcessor {
}
// name unique bnodes in sorted hash order
$hashes = array_keys((array)$unique);
$hashes = array_keys((array) $unique);
sort($hashes);
foreach($hashes as $hash) {
foreach ($hashes as $hash) {
$namer->getName($unique->{$hash});
}
}
while(count($unnamed) > count($nextUnnamed));
} while (count($unnamed) > count($nextUnnamed));
// enumerate duplicate hash groups in sorted order
$hashes = array_keys((array)$duplicates);
$hashes = array_keys((array) $duplicates);
sort($hashes);
foreach($hashes as $hash) {
foreach ($hashes as $hash) {
// process group
$group = $duplicates->{$hash};
$results = array();
foreach($group as $bnode) {
$results = [];
foreach ($group as $bnode) {
// skip already-named bnodes
if($namer->isNamed($bnode)) {
if ($namer->isNamed($bnode)) {
continue;
}
@ -2850,16 +2844,16 @@ class JsonLdProcessor {
$b = $b->hash;
return ($a < $b) ? -1 : (($a > $b) ? 1 : 0);
});
foreach($results as $result) {
foreach ($results as $result) {
// name all bnodes in path namer in key-entry order
foreach($result->pathNamer->order as $bnode) {
foreach ($result->pathNamer->order as $bnode) {
$namer->getName($bnode);
}
}
}
// create normalized array
$normalized = array();
$normalized = [];
/* Note: At this point all bnodes in the set of RDF quads have been
assigned canonical names, which have been stored in the 'namer' object.
@ -2867,9 +2861,9 @@ class JsonLdProcessor {
via the 'namer' object. */
// update bnode names in each quad and serialize
foreach($quads as $quad) {
foreach(array('subject', 'object', 'name') as $attr) {
if(property_exists($quad, $attr) &&
foreach ($quads as $quad) {
foreach (['subject', 'object', 'name'] as $attr) {
if (property_exists($quad, $attr) &&
$quad->{$attr}->type === 'blank node' &&
strpos($quad->{$attr}->value, '_:c14n') !== 0) {
$quad->{$attr}->value = $namer->getName($quad->{$attr}->value);
@ -2883,13 +2877,12 @@ class JsonLdProcessor {
sort($normalized);
// handle output format
if(isset($options['format']) && $options['format']) {
if($options['format'] === 'application/nquads') {
if (isset($options['format']) && $options['format']) {
if ($options['format'] === 'application/nquads') {
return implode($normalized);
}
throw new JsonLdException(
'Unknown output format.',
'jsonld.UnknownFormat', null, array('format' => $options['format']));
'Unknown output format.', 'jsonld.UnknownFormat', null, ['format' => $options['format']]);
}
// return RDF dataset
@ -2904,85 +2897,86 @@ class JsonLdProcessor {
*
* @return array the JSON-LD output.
*/
protected function _fromRDF($dataset, $options) {
protected function _fromRDF($dataset, $options)
{
$default_graph = new stdClass();
$graph_map = (object)array('@default' => $default_graph);
$referenced_once = (object)array();
$graph_map = (object) ['@default' => $default_graph];
$referenced_once = (object) [];
foreach($dataset as $name => $graph) {
if(!property_exists($graph_map, $name)) {
foreach ($dataset as $name => $graph) {
if (!property_exists($graph_map, $name)) {
$graph_map->{$name} = new stdClass();
}
if($name !== '@default' && !property_exists($default_graph, $name)) {
$default_graph->{$name} = (object)array('@id' => $name);
if ($name !== '@default' && !property_exists($default_graph, $name)) {
$default_graph->{$name} = (object) ['@id' => $name];
}
$node_map = $graph_map->{$name};
foreach($graph as $triple) {
foreach ($graph as $triple) {
// get subject, predicate, object
$s = $triple->subject->value;
$p = $triple->predicate->value;
$o = $triple->object;
if(!property_exists($node_map, $s)) {
$node_map->{$s} = (object)array('@id' => $s);
if (!property_exists($node_map, $s)) {
$node_map->{$s} = (object) ['@id' => $s];
}
$node = $node_map->{$s};
$object_is_id = ($o->type === 'IRI' || $o->type === 'blank node');
if($object_is_id && !property_exists($node_map, $o->value)) {
$node_map->{$o->value} = (object)array('@id' => $o->value);
if ($object_is_id && !property_exists($node_map, $o->value)) {
$node_map->{$o->value} = (object) ['@id' => $o->value];
}
if($p === self::RDF_TYPE && !$options['useRdfType'] && $object_is_id) {
if ($p === self::RDF_TYPE && !$options['useRdfType'] && $object_is_id) {
self::addValue(
$node, '@type', $o->value, array('propertyIsArray' => true));
$node, '@type', $o->value, ['propertyIsArray' => true]);
continue;
}
$value = self::_RDFToObject($o, $options['useNativeTypes']);
self::addValue($node, $p, $value, array('propertyIsArray' => true));
self::addValue($node, $p, $value, ['propertyIsArray' => true]);
// object may be an RDF list/partial list node but we can't know
// easily until all triples are read
if($object_is_id) {
if($o->value === self::RDF_NIL) {
if ($object_is_id) {
if ($o->value === self::RDF_NIL) {
$object = $node_map->{$o->value};
if(!property_exists($object, 'usages')) {
$object->usages = array();
if (!property_exists($object, 'usages')) {
$object->usages = [];
}
$object->usages[] = (object)array(
$object->usages[] = (object) [
'node' => $node,
'property' => $p,
'value' => $value);
} else if(property_exists($referenced_once, $o->value)) {
'value' => $value];
} else if (property_exists($referenced_once, $o->value)) {
// object referenced more than once
$referenced_once->{$o->value} = false;
} else {
// track single reference
$referenced_once->{$o->value} = (object)array(
$referenced_once->{$o->value} = (object) [
'node' => $node,
'property' => $p,
'value' => $value);
'value' => $value];
}
}
}
}
// convert linked lists to @list arrays
foreach($graph_map as $name => $graph_object) {
foreach ($graph_map as $name => $graph_object) {
// no @lists to be converted, continue
if(!property_exists($graph_object, self::RDF_NIL)) {
if (!property_exists($graph_object, self::RDF_NIL)) {
continue;
}
// iterate backwards through each RDF list
$nil = $graph_object->{self::RDF_NIL};
foreach($nil->usages as $usage) {
foreach ($nil->usages as $usage) {
$node = $usage->node;
$property = $usage->property;
$head = $usage->value;
$list = array();
$list_nodes = array();
$list = [];
$list_nodes = [];
// ensure node is a well-formed list node; it must:
// 1. Be referenced only once.
@ -2990,8 +2984,8 @@ class JsonLdProcessor {
// 3. Have an array for rdf:rest that has 1 item.
// 4. Have no keys other than: @id, rdf:first, rdf:rest, and,
// optionally, @type where the value is rdf:List.
$node_key_count = count(array_keys((array)$node));
while($property === self::RDF_REST &&
$node_key_count = count(array_keys((array) $node));
while ($property === self::RDF_REST &&
property_exists($referenced_once, $node->{'@id'}) &&
is_object($referenced_once->{$node->{'@id'}}) &&
property_exists($node, self::RDF_FIRST) &&
@ -3012,18 +3006,18 @@ class JsonLdProcessor {
$node = $usage->node;
$property = $usage->property;
$head = $usage->value;
$node_key_count = count(array_keys((array)$node));
$node_key_count = count(array_keys((array) $node));
// if node is not a blank node, then list head found
if(strpos($node->{'@id'}, '_:') !== 0) {
if (strpos($node->{'@id'}, '_:') !== 0) {
break;
}
}
// list is nested in another list
if($property === self::RDF_FIRST) {
if ($property === self::RDF_FIRST) {
// empty list
if($node->{'@id'} === self::RDF_NIL) {
if ($node->{'@id'} === self::RDF_NIL) {
// can't convert rdf:nil to a @list object because it would
// result in a list of lists which isn't supported
continue;
@ -3038,7 +3032,7 @@ class JsonLdProcessor {
// transform list into @list object
unset($head->{'@id'});
$head->{'@list'} = array_reverse($list);
foreach($list_nodes as $list_node) {
foreach ($list_nodes as $list_node) {
unset($graph_object->{$list_node});
}
}
@ -3046,26 +3040,26 @@ class JsonLdProcessor {
unset($nil->usages);
}
$result = array();
$subjects = array_keys((array)$default_graph);
$result = [];
$subjects = array_keys((array) $default_graph);
sort($subjects);
foreach($subjects as $subject) {
foreach ($subjects as $subject) {
$node = $default_graph->{$subject};
if(property_exists($graph_map, $subject)) {
$node->{'@graph'} = array();
if (property_exists($graph_map, $subject)) {
$node->{'@graph'} = [];
$graph_object = $graph_map->{$subject};
$subjects_ = array_keys((array)$graph_object);
$subjects_ = array_keys((array) $graph_object);
sort($subjects_);
foreach($subjects_ as $subject_) {
foreach ($subjects_ as $subject_) {
$node_ = $graph_object->{$subject_};
// only add full subjects to top-level
if(!self::_isSubjectReference($node_)) {
if (!self::_isSubjectReference($node_)) {
$node->{'@graph'}[] = $node_;
}
}
}
// only add full subjects to top-level
if(!self::_isSubjectReference($node)) {
if (!self::_isSubjectReference($node)) {
$result[] = $node;
}
}
@ -3082,48 +3076,47 @@ class JsonLdProcessor {
*
* @return stdClass the new active context.
*/
protected function _processContext($active_ctx, $local_ctx, $options) {
protected function _processContext($active_ctx, $local_ctx, $options)
{
global $jsonld_cache;
// normalize local context to an array
if(is_object($local_ctx) && property_exists($local_ctx, '@context') &&
if (is_object($local_ctx) && property_exists($local_ctx, '@context') &&
is_array($local_ctx->{'@context'})) {
$local_ctx = $local_ctx->{'@context'};
}
$ctxs = self::arrayify($local_ctx);
// no contexts in array, clone existing context
if(count($ctxs) === 0) {
if (count($ctxs) === 0) {
return self::_cloneActiveContext($active_ctx);
}
// process each context in order, update active context
// on each iteration to ensure proper caching
$rval = $active_ctx;
foreach($ctxs as $ctx) {
foreach ($ctxs as $ctx) {
// reset to initial context
if($ctx === null) {
if ($ctx === null) {
$rval = $active_ctx = $this->_getInitialContext($options);
continue;
}
// dereference @context key if present
if(is_object($ctx) && property_exists($ctx, '@context')) {
if (is_object($ctx) && property_exists($ctx, '@context')) {
$ctx = $ctx->{'@context'};
}
// context must be an object by now, all URLs retrieved before this call
if(!is_object($ctx)) {
if (!is_object($ctx)) {
throw new JsonLdException(
'Invalid JSON-LD syntax; @context must be an object.',
'jsonld.SyntaxError', 'invalid local context',
array('context' => $ctx));
'Invalid JSON-LD syntax; @context must be an object.', 'jsonld.SyntaxError', 'invalid local context', ['context' => $ctx]);
}
// get context from cache if available
if(property_exists($jsonld_cache, 'activeCtx')) {
if (property_exists($jsonld_cache, 'activeCtx')) {
$cached = $jsonld_cache->activeCtx->get($active_ctx, $ctx);
if($cached) {
if ($cached) {
$rval = $active_ctx = $cached;
$must_clone = true;
continue;
@ -3138,22 +3131,20 @@ class JsonLdProcessor {
$defined = new stdClass();
// handle @base
if(property_exists($ctx, '@base')) {
if (property_exists($ctx, '@base')) {
$base = $ctx->{'@base'};
if($base === null) {
if ($base === null) {
$base = null;
} else if(!is_string($base)) {
} else if (!is_string($base)) {
throw new JsonLdException(
'Invalid JSON-LD syntax; the value of "@base" in a ' .
'@context must be a string or null.',
'jsonld.SyntaxError', 'invalid base IRI', array('context' => $ctx));
} else if($base !== '' && !self::_isAbsoluteIri($base)) {
'@context must be a string or null.', 'jsonld.SyntaxError', 'invalid base IRI', ['context' => $ctx]);
} else if ($base !== '' && !self::_isAbsoluteIri($base)) {
throw new JsonLdException(
'Invalid JSON-LD syntax; the value of "@base" in a ' .
'@context must be an absolute IRI or the empty string.',
'jsonld.SyntaxError', 'invalid base IRI', array('context' => $ctx));
'@context must be an absolute IRI or the empty string.', 'jsonld.SyntaxError', 'invalid base IRI', ['context' => $ctx]);
}
if($base !== null) {
if ($base !== null) {
$base = jsonld_parse_url($base);
}
$rval->{'@base'} = $base;
@ -3161,22 +3152,18 @@ class JsonLdProcessor {
}
// handle @vocab
if(property_exists($ctx, '@vocab')) {
if (property_exists($ctx, '@vocab')) {
$value = $ctx->{'@vocab'};
if($value === null) {
if ($value === null) {
unset($rval->{'@vocab'});
} else if(!is_string($value)) {
} else if (!is_string($value)) {
throw new JsonLdException(
'Invalid JSON-LD syntax; the value of "@vocab" in a ' .
'@context must be a string or null.',
'jsonld.SyntaxError', 'invalid vocab mapping',
array('context' => $ctx));
} else if(!self::_isAbsoluteIri($value)) {
'@context must be a string or null.', 'jsonld.SyntaxError', 'invalid vocab mapping', ['context' => $ctx]);
} else if (!self::_isAbsoluteIri($value)) {
throw new JsonLdException(
'Invalid JSON-LD syntax; the value of "@vocab" in a ' .
'@context must be an absolute IRI.',
'jsonld.SyntaxError', 'invalid vocab mapping',
array('context' => $ctx));
'@context must be an absolute IRI.', 'jsonld.SyntaxError', 'invalid vocab mapping', ['context' => $ctx]);
} else {
$rval->{'@vocab'} = $value;
}
@ -3184,16 +3171,14 @@ class JsonLdProcessor {
}
// handle @language
if(property_exists($ctx, '@language')) {
if (property_exists($ctx, '@language')) {
$value = $ctx->{'@language'};
if($value === null) {
if ($value === null) {
unset($rval->{'@language'});
} else if(!is_string($value)) {
} else if (!is_string($value)) {
throw new JsonLdException(
'Invalid JSON-LD syntax; the value of "@language" in a ' .
'@context must be a string or null.',
'jsonld.SyntaxError', 'invalid default language',
array('context' => $ctx));
'@context must be a string or null.', 'jsonld.SyntaxError', 'invalid default language', ['context' => $ctx]);
} else {
$rval->{'@language'} = strtolower($value);
}
@ -3201,12 +3186,12 @@ class JsonLdProcessor {
}
// process all other keys
foreach($ctx as $k => $v) {
foreach ($ctx as $k => $v) {
$this->_createTermDefinition($rval, $ctx, $k, $defined);
}
// cache result
if(property_exists($jsonld_cache, 'activeCtx')) {
if (property_exists($jsonld_cache, 'activeCtx')) {
$jsonld_cache->activeCtx->set($active_ctx, $ctx, $rval);
}
}
@ -3221,26 +3206,25 @@ class JsonLdProcessor {
*
* @return array the expanded language map.
*/
protected function _expandLanguageMap($language_map) {
$rval = array();
$keys = array_keys((array)$language_map);
protected function _expandLanguageMap($language_map)
{
$rval = [];
$keys = array_keys((array) $language_map);
sort($keys);
foreach($keys as $key) {
foreach ($keys as $key) {
$values = $language_map->{$key};
$values = self::arrayify($values);
foreach($values as $item) {
if($item === null) {
foreach ($values as $item) {
if ($item === null) {
continue;
}
if(!is_string($item)) {
if (!is_string($item)) {
throw new JsonLdException(
'Invalid JSON-LD syntax; language map values must be strings.',
'jsonld.SyntaxError', 'invalid language map value',
array('languageMap', $language_map));
'Invalid JSON-LD syntax; language map values must be strings.', 'jsonld.SyntaxError', 'invalid language map value', ['languageMap', $language_map]);
}
$rval[] = (object)array(
$rval[] = (object) [
'@value' => $item,
'@language' => strtolower($key));
'@language' => strtolower($key)];
}
}
return $rval;
@ -3254,30 +3238,31 @@ class JsonLdProcessor {
*
* @return mixed the element.
*/
public function _labelBlankNodes($namer, $element) {
if(is_array($element)) {
public function _labelBlankNodes($namer, $element)
{
if (is_array($element)) {
$length = count($element);
for($i = 0; $i < $length; ++$i) {
for ($i = 0; $i < $length; ++$i) {
$element[$i] = $this->_labelBlankNodes($namer, $element[$i]);
}
} else if(self::_isList($element)) {
} else if (self::_isList($element)) {
$element->{'@list'} = $this->_labelBlankNodes(
$namer, $element->{'@list'});
} else if(is_object($element)) {
} else if (is_object($element)) {
// rename blank node
if(self::_isBlankNode($element)) {
if (self::_isBlankNode($element)) {
$name = null;
if(property_exists($element, '@id')) {
if (property_exists($element, '@id')) {
$name = $element->{'@id'};
}
$element->{'@id'} = $namer->getName($name);
}
// recursively apply to all keys
$keys = array_keys((array)$element);
$keys = array_keys((array) $element);
sort($keys);
foreach($keys as $key) {
if($key !== '@id') {
foreach ($keys as $key) {
if ($key !== '@id') {
$element->{$key} = $this->_labelBlankNodes($namer, $element->{$key});
}
}
@ -3296,52 +3281,53 @@ class JsonLdProcessor {
*
* @return mixed the expanded value.
*/
protected function _expandValue($active_ctx, $active_property, $value) {
protected function _expandValue($active_ctx, $active_property, $value)
{
// nothing to expand
if($value === null) {
if ($value === null) {
return null;
}
// special-case expand @id and @type (skips '@id' expansion)
$expanded_property = $this->_expandIri(
$active_ctx, $active_property, array('vocab' => true));
if($expanded_property === '@id') {
return $this->_expandIri($active_ctx, $value, array('base' => true));
} else if($expanded_property === '@type') {
$active_ctx, $active_property, ['vocab' => true]);
if ($expanded_property === '@id') {
return $this->_expandIri($active_ctx, $value, ['base' => true]);
} else if ($expanded_property === '@type') {
return $this->_expandIri(
$active_ctx, $value, array('vocab' => true, 'base' => true));
$active_ctx, $value, ['vocab' => true, 'base' => true]);
}
// get type definition from context
$type = self::getContextValue($active_ctx, $active_property, '@type');
// do @id expansion (automatic for @graph)
if($type === '@id' || ($expanded_property === '@graph' &&
if ($type === '@id' || ($expanded_property === '@graph' &&
is_string($value))) {
return (object)array('@id' => $this->_expandIri(
$active_ctx, $value, array('base' => true)));
return (object) ['@id' => $this->_expandIri(
$active_ctx, $value, ['base' => true])];
}
// do @id expansion w/vocab
if($type === '@vocab') {
return (object)array('@id' => $this->_expandIri(
$active_ctx, $value, array('vocab' => true, 'base' => true)));
if ($type === '@vocab') {
return (object) ['@id' => $this->_expandIri(
$active_ctx, $value, ['vocab' => true, 'base' => true])];
}
// do not expand keyword values
if(self::_isKeyword($expanded_property)) {
if (self::_isKeyword($expanded_property)) {
return $value;
}
$rval = new stdClass();
// other type
if($type !== null) {
if ($type !== null) {
$rval->{'@type'} = $type;
} else if(is_string($value)) {
} else if (is_string($value)) {
// check for language tagging for strings
$language = self::getContextValue(
$active_ctx, $active_property, '@language');
if($language !== null) {
if ($language !== null) {
$rval->{'@language'} = $language;
}
}
@ -3359,29 +3345,30 @@ class JsonLdProcessor {
*
* @return array the array of RDF triples for the given graph.
*/
protected function _graphToRDF($graph, $namer, $options) {
$rval = array();
protected function _graphToRDF($graph, $namer, $options)
{
$rval = [];
$ids = array_keys((array)$graph);
$ids = array_keys((array) $graph);
sort($ids);
foreach($ids as $id) {
foreach ($ids as $id) {
$node = $graph->{$id};
if($id === '"') {
if ($id === '"') {
$id = '';
}
$properties = array_keys((array)$node);
$properties = array_keys((array) $node);
sort($properties);
foreach($properties as $property) {
foreach ($properties as $property) {
$items = $node->{$property};
if($property === '@type') {
if ($property === '@type') {
$property = self::RDF_TYPE;
} else if(self::_isKeyword($property)) {
} else if (self::_isKeyword($property)) {
continue;
}
foreach($items as $item) {
foreach ($items as $item) {
// skip relative IRI subjects and predicates
if(!(self::_isAbsoluteIri($id) && self::_isAbsoluteIri($property))) {
if (!(self::_isAbsoluteIri($id) && self::_isAbsoluteIri($property))) {
continue;
}
@ -3397,12 +3384,12 @@ class JsonLdProcessor {
$predicate->value = $property;
// skip bnode predicates unless producing generalized RDF
if($predicate->type === 'blank node' &&
if ($predicate->type === 'blank node' &&
!$options['produceGeneralizedRdf']) {
continue;
}
if(self::_isList($item)) {
if (self::_isList($item)) {
// convert @list to triples
$this->_listToRDF(
$item->{'@list'}, $namer, $subject, $predicate, $rval);
@ -3410,11 +3397,11 @@ class JsonLdProcessor {
// convert value or node object to triple
$object = $this->_objectToRDF($item);
// skip null objects (they are relative IRIs)
if($object) {
$rval[] = (object)array(
if ($object) {
$rval[] = (object) [
'subject' => $subject,
'predicate' => $predicate,
'object' => $object);
'object' => $object];
}
}
}
@ -3435,35 +3422,36 @@ class JsonLdProcessor {
* @param &array $triples the array of triples to append to.
*/
protected function _listToRDF(
$list, $namer, $subject, $predicate, &$triples) {
$first = (object)array('type' => 'IRI', 'value' => self::RDF_FIRST);
$rest = (object)array('type' => 'IRI', 'value' => self::RDF_REST);
$nil = (object)array('type' => 'IRI', 'value' => self::RDF_NIL);
$list, $namer, $subject, $predicate, &$triples)
{
$first = (object) ['type' => 'IRI', 'value' => self::RDF_FIRST];
$rest = (object) ['type' => 'IRI', 'value' => self::RDF_REST];
$nil = (object) ['type' => 'IRI', 'value' => self::RDF_NIL];
foreach($list as $item) {
$blank_node = (object)array(
'type' => 'blank node', 'value' => $namer->getName());
$triples[] = (object)array(
foreach ($list as $item) {
$blank_node = (object) [
'type' => 'blank node', 'value' => $namer->getName()];
$triples[] = (object) [
'subject' => $subject,
'predicate' => $predicate,
'object' => $blank_node);
'object' => $blank_node];
$subject = $blank_node;
$predicate = $first;
$object = $this->_objectToRDF($item);
// skip null objects (they are relative IRIs)
if($object) {
$triples[] = (object)array(
if ($object) {
$triples[] = (object) [
'subject' => $subject,
'predicate' => $predicate,
'object' => $object);
'object' => $object];
}
$predicate = $rest;
}
$triples[] = (object)array(
'subject' => $subject, 'predicate' => $predicate, 'object' => $nil);
$triples[] = (object) [
'subject' => $subject, 'predicate' => $predicate, 'object' => $nil];
}
/**
@ -3474,27 +3462,28 @@ class JsonLdProcessor {
*
* @return stdClass the RDF literal or RDF resource.
*/
protected function _objectToRDF($item) {
protected function _objectToRDF($item)
{
$object = new stdClass();
if(self::_isValue($item)) {
if (self::_isValue($item)) {
$object->type = 'literal';
$value = $item->{'@value'};
$datatype = property_exists($item, '@type') ? $item->{'@type'} : null;
// convert to XSD datatypes as appropriate
if(is_bool($value)) {
if (is_bool($value)) {
$object->value = ($value ? 'true' : 'false');
$object->datatype = $datatype ? $datatype : self::XSD_BOOLEAN;
} else if(is_double($value) || $datatype == self::XSD_DOUBLE) {
} else if (is_double($value) || $datatype == self::XSD_DOUBLE) {
// canonical double representation
$object->value = preg_replace(
'/(\d)0*E\+?/', '$1E', sprintf('%1.15E', $value));
$object->datatype = $datatype ? $datatype : self::XSD_DOUBLE;
} else if(is_integer($value)) {
} else if (is_integer($value)) {
$object->value = strval($value);
$object->datatype = $datatype ? $datatype : self::XSD_INTEGER;
} else if(property_exists($item, '@language')) {
} else if (property_exists($item, '@language')) {
$object->value = $value;
$object->datatype = $datatype ? $datatype : self::RDF_LANGSTRING;
$object->language = $item->{'@language'};
@ -3510,7 +3499,7 @@ class JsonLdProcessor {
}
// skip relative IRIs
if($object->type === 'IRI' && !self::_isAbsoluteIri($object->value)) {
if ($object->type === 'IRI' && !self::_isAbsoluteIri($object->value)) {
return null;
}
@ -3525,46 +3514,47 @@ class JsonLdProcessor {
*
* @return stdClass the JSON-LD object.
*/
protected function _RDFToObject($o, $use_native_types) {
protected function _RDFToObject($o, $use_native_types)
{
// convert IRI/blank node object to JSON-LD
if($o->type === 'IRI' || $o->type === 'blank node') {
return (object)array('@id' => $o->value);
if ($o->type === 'IRI' || $o->type === 'blank node') {
return (object) ['@id' => $o->value];
}
// convert literal object to JSON-LD
$rval = (object)array('@value' => $o->value);
$rval = (object) ['@value' => $o->value];
if(property_exists($o, 'language')) {
if (property_exists($o, 'language')) {
// add language
$rval->{'@language'} = $o->language;
} else {
// add datatype
$type = $o->datatype;
// use native types for certain xsd types
if($use_native_types) {
if($type === self::XSD_BOOLEAN) {
if($rval->{'@value'} === 'true') {
if ($use_native_types) {
if ($type === self::XSD_BOOLEAN) {
if ($rval->{'@value'} === 'true') {
$rval->{'@value'} = true;
} else if($rval->{'@value'} === 'false') {
} else if ($rval->{'@value'} === 'false') {
$rval->{'@value'} = false;
}
} else if(is_numeric($rval->{'@value'})) {
if($type === self::XSD_INTEGER) {
} else if (is_numeric($rval->{'@value'})) {
if ($type === self::XSD_INTEGER) {
$i = intval($rval->{'@value'});
if(strval($i) === $rval->{'@value'}) {
if (strval($i) === $rval->{'@value'}) {
$rval->{'@value'} = $i;
}
} else if($type === self::XSD_DOUBLE) {
} else if ($type === self::XSD_DOUBLE) {
$rval->{'@value'} = doubleval($rval->{'@value'});
}
}
// do not add native type
if(!in_array($type, array(
if (!in_array($type, [
self::XSD_BOOLEAN, self::XSD_INTEGER, self::XSD_DOUBLE,
self::XSD_STRING))) {
self::XSD_STRING])) {
$rval->{'@type'} = $type;
}
} else if($type !== self::XSD_STRING) {
} else if ($type !== self::XSD_STRING) {
$rval->{'@type'} = $type;
}
}
@ -3584,121 +3574,120 @@ class JsonLdProcessor {
* @param mixed $list the list to append to, null for none.
*/
protected function _createNodeMap(
$input, $graphs, $graph, $namer, $name=null, $list=null) {
$input, $graphs, $graph, $namer, $name = null, $list = null)
{
// recurse through array
if(is_array($input)) {
foreach($input as $e) {
if (is_array($input)) {
foreach ($input as $e) {
$this->_createNodeMap($e, $graphs, $graph, $namer, null, $list);
}
return;
}
// add non-object to list
if(!is_object($input)) {
if($list !== null) {
if (!is_object($input)) {
if ($list !== null) {
$list[] = $input;
}
return;
}
// add values to list
if(self::_isValue($input)) {
if(property_exists($input, '@type')) {
if (self::_isValue($input)) {
if (property_exists($input, '@type')) {
$type = $input->{'@type'};
// rename @type blank node
if(strpos($type, '_:') === 0) {
if (strpos($type, '_:') === 0) {
$type = $input->{'@type'} = $namer->getName($type);
}
}
if($list !== null) {
if ($list !== null) {
$list[] = $input;
}
return;
}
// Note: At this point, input must be a subject.
// spec requires @type to be named first, so assign names early
if(property_exists($input, '@type')) {
foreach($input->{'@type'} as $type) {
if(strpos($type, '_:') === 0) {
if (property_exists($input, '@type')) {
foreach ($input->{'@type'} as $type) {
if (strpos($type, '_:') === 0) {
$namer->getName($type);
}
}
}
// get name for subject
if($name === null) {
if(property_exists($input, '@id')) {
if ($name === null) {
if (property_exists($input, '@id')) {
$name = $input->{'@id'};
}
if(self::_isBlankNode($input)) {
if (self::_isBlankNode($input)) {
$name = $namer->getName($name);
}
}
// add subject reference to list
if($list !== null) {
$list[] = (object)array('@id' => $name);
if ($list !== null) {
$list[] = (object) ['@id' => $name];
}
// create new subject or merge into existing one
if(!property_exists($graphs, $graph)) {
if (!property_exists($graphs, $graph)) {
$graphs->{$graph} = new stdClass();
}
$subjects = $graphs->{$graph};
if(!property_exists($subjects, $name)) {
if($name === '') {
if (!property_exists($subjects, $name)) {
if ($name === '') {
$subjects->{'"'} = new stdClass();
} else {
$subjects->{$name} = new stdClass();
}
}
if($name === '') {
if ($name === '') {
$subject = $subjects->{'"'};
} else {
$subject = $subjects->{$name};
}
$subject->{'@id'} = $name;
$properties = array_keys((array)$input);
$properties = array_keys((array) $input);
sort($properties);
foreach($properties as $property) {
foreach ($properties as $property) {
// skip @id
if($property === '@id') {
if ($property === '@id') {
continue;
}
// handle reverse properties
if($property === '@reverse') {
$referenced_node = (object)array('@id' => $name);
if ($property === '@reverse') {
$referenced_node = (object) ['@id' => $name];
$reverse_map = $input->{'@reverse'};
foreach($reverse_map as $reverse_property => $items) {
foreach($items as $item) {
foreach ($reverse_map as $reverse_property => $items) {
foreach ($items as $item) {
$item_name = null;
if(property_exists($item, '@id')) {
if (property_exists($item, '@id')) {
$item_name = $item->{'@id'};
}
if(self::_isBlankNode($item)) {
if (self::_isBlankNode($item)) {
$item_name = $namer->getName($item_name);
}
$this->_createNodeMap($item, $graphs, $graph, $namer, $item_name);
if($item_name === '') {
if ($item_name === '') {
$item_name = '"';
}
self::addValue(
$subjects->{$item_name}, $reverse_property, $referenced_node,
array('propertyIsArray' => true, 'allowDuplicate' => false));
$subjects->{$item_name}, $reverse_property, $referenced_node, ['propertyIsArray' => true, 'allowDuplicate' => false]);
}
}
continue;
}
// recurse into graph
if($property === '@graph') {
if ($property === '@graph') {
// add graph subjects map entry
if(!property_exists($graphs, $name)) {
if (!property_exists($graphs, $name)) {
// FIXME: temporary hack to avoid empty property bug
if(!$name) {
if (!$name) {
$name = '"';
}
$graphs->{$name} = new stdClass();
@ -3710,14 +3699,12 @@ class JsonLdProcessor {
}
// copy non-@type keywords
if($property !== '@type' && self::_isKeyword($property)) {
if($property === '@index' && property_exists($subject, '@index') &&
if ($property !== '@type' && self::_isKeyword($property)) {
if ($property === '@index' && property_exists($subject, '@index') &&
($input->{'@index'} !== $subject->{'@index'} ||
$input->{'@index'}->{'@id'} !== $subject->{'@index'}->{'@id'})) {
throw new JsonLdException(
'Invalid JSON-LD syntax; conflicting @index property detected.',
'jsonld.SyntaxError', 'conflicting indexes',
array('subject' => $subject));
'Invalid JSON-LD syntax; conflicting @index property detected.', 'jsonld.SyntaxError', 'conflicting indexes', ['subject' => $subject]);
}
$subject->{$property} = $input->{$property};
continue;
@ -3727,50 +3714,47 @@ class JsonLdProcessor {
$objects = $input->{$property};
// if property is a bnode, assign it a new id
if(strpos($property, '_:') === 0) {
if (strpos($property, '_:') === 0) {
$property = $namer->getName($property);
}
// ensure property is added for empty arrays
if(count($objects) === 0) {
if (count($objects) === 0) {
self::addValue(
$subject, $property, array(), array('propertyIsArray' => true));
$subject, $property, [], ['propertyIsArray' => true]);
continue;
}
foreach($objects as $o) {
if($property === '@type') {
foreach ($objects as $o) {
if ($property === '@type') {
// rename @type blank nodes
$o = (strpos($o, '_:') === 0) ? $namer->getName($o) : $o;
}
// handle embedded subject or subject reference
if(self::_isSubject($o) || self::_isSubjectReference($o)) {
if (self::_isSubject($o) || self::_isSubjectReference($o)) {
// rename blank node @id
$id = property_exists($o, '@id') ? $o->{'@id'} : null;
if(self::_isBlankNode($o)) {
if (self::_isBlankNode($o)) {
$id = $namer->getName($id);
}
// add reference and recurse
self::addValue(
$subject, $property, (object)array('@id' => $id),
array('propertyIsArray' => true, 'allowDuplicate' => false));
$subject, $property, (object) ['@id' => $id], ['propertyIsArray' => true, 'allowDuplicate' => false]);
$this->_createNodeMap($o, $graphs, $graph, $namer, $id, null);
} else if(self::_isList($o)) {
} else if (self::_isList($o)) {
// handle @list
$_list = new ArrayObject();
$this->_createNodeMap(
$o->{'@list'}, $graphs, $graph, $namer, $name, $_list);
$o = (object)array('@list' => (array)$_list);
$o = (object) ['@list' => (array) $_list];
self::addValue(
$subject, $property, $o,
array('propertyIsArray' => true, 'allowDuplicate' => false));
$subject, $property, $o, ['propertyIsArray' => true, 'allowDuplicate' => false]);
} else {
// handle @value
$this->_createNodeMap($o, $graphs, $graph, $namer, $name, null);
self::addValue(
$subject, $property, $o,
array('propertyIsArray' => true, 'allowDuplicate' => false));
$subject, $property, $o, ['propertyIsArray' => true, 'allowDuplicate' => false]);
}
}
}
@ -3786,28 +3770,28 @@ class JsonLdProcessor {
* @param mixed $property the parent property, initialized to null.
*/
protected function _matchFrame(
$state, $subjects, $frame, $parent, $property) {
$state, $subjects, $frame, $parent, $property)
{
// validate the frame
$this->_validateFrame($frame);
$frame = $frame[0];
// get flags for current frame
$options = $state->options;
$flags = array(
$flags = [
'embed' => $this->_getFrameFlag($frame, $options, 'embed'),
'explicit' => $this->_getFrameFlag($frame, $options, 'explicit'),
'requireAll' => $this->_getFrameFlag($frame, $options, 'requireAll'));
'requireAll' => $this->_getFrameFlag($frame, $options, 'requireAll')];
// filter out subjects that match the frame
$matches = $this->_filterSubjects($state, $subjects, $frame, $flags);
// add matches to output
foreach($matches as $id => $subject) {
if($flags['embed'] === '@link' && property_exists($state->link, $id)) {
foreach ($matches as $id => $subject) {
if ($flags['embed'] === '@link' && property_exists($state->link, $id)) {
// TODO: may want to also match an existing linked subject against
// the current frame ... so different frames could produce different
// subjects that are only shared in-memory when the frames are the same
// add existing linked subject
$this->_addFrameOutput($parent, $property, $state->link->{$id});
continue;
@ -3816,7 +3800,7 @@ class JsonLdProcessor {
/* Note: In order to treat each top-level match as a compartmentalized
result, clear the unique embedded subjects map when the property is null,
which only occurs at the top-level. */
if($property === null) {
if ($property === null) {
$state->uniqueEmbeds = new stdClass();
}
@ -3829,59 +3813,59 @@ class JsonLdProcessor {
// embed, the subject cannot be embedded, just add the reference;
// note that a circular reference won't occur when the embed flag is
// `@link` as the above check will short-circuit before reaching this point
if($flags['embed'] === '@never' ||
if ($flags['embed'] === '@never' ||
$this->_createsCircularReference($subject, $state->subjectStack)) {
$this->_addFrameOutput($parent, $property, $output);
continue;
}
// if only the last match should be embedded
if($flags['embed'] === '@last') {
if ($flags['embed'] === '@last') {
// remove any existing embed
if(property_exists($state->uniqueEmbeds, $id)) {
if (property_exists($state->uniqueEmbeds, $id)) {
$this->_removeEmbed($state, $id);
}
$state->uniqueEmbeds->{$id} = array(
'parent' => $parent, 'property' => $property);
$state->uniqueEmbeds->{$id} = [
'parent' => $parent, 'property' => $property];
}
// push matching subject onto stack to enable circular embed checks
$state->subjectStack[] = $subject;
// iterate over subject properties
$props = array_keys((array)$subject);
$props = array_keys((array) $subject);
sort($props);
foreach($props as $prop) {
foreach ($props as $prop) {
// copy keywords to output
if(self::_isKeyword($prop)) {
if (self::_isKeyword($prop)) {
$output->{$prop} = self::copy($subject->{$prop});
continue;
}
// explicit is on and property isn't in the frame, skip processing
if($flags['explicit'] && !property_exists($frame, $prop)) {
if ($flags['explicit'] && !property_exists($frame, $prop)) {
continue;
}
// add objects
$objects = $subject->{$prop};
foreach($objects as $o) {
foreach ($objects as $o) {
// recurse into list
if(self::_isList($o)) {
if (self::_isList($o)) {
// add empty list
$list = (object)array('@list' => array());
$list = (object) ['@list' => []];
$this->_addFrameOutput($output, $prop, $list);
// add list objects
$src = $o->{'@list'};
foreach($src as $o) {
if(self::_isSubjectReference($o)) {
foreach ($src as $o) {
if (self::_isSubjectReference($o)) {
// recurse into subject reference
$subframe = (property_exists($frame, $prop) ?
$frame->{$prop}[0]->{'@list'} :
$this->_createImplicitFrame($flags));
$this->_matchFrame(
$state, array($o->{'@id'}), $subframe, $list, '@list');
$state, [$o->{'@id'}], $subframe, $list, '@list');
} else {
// include other values automatically
$this->_addFrameOutput($list, '@list', self::copy($o));
@ -3890,12 +3874,12 @@ class JsonLdProcessor {
continue;
}
if(self::_isSubjectReference($o)) {
if (self::_isSubjectReference($o)) {
// recurse into subject reference
$subframe = (property_exists($frame, $prop) ?
$frame->{$prop} : $this->_createImplicitFrame($flags));
$this->_matchFrame(
$state, array($o->{'@id'}), $subframe, $output, $prop);
$state, [$o->{'@id'}], $subframe, $output, $prop);
} else {
// include other values automatically
$this->_addFrameOutput($output, $prop, self::copy($o));
@ -3904,11 +3888,11 @@ class JsonLdProcessor {
}
// handle defaults
$props = array_keys((array)$frame);
$props = array_keys((array) $frame);
sort($props);
foreach($props as $prop) {
foreach ($props as $prop) {
// skip keywords
if(self::_isKeyword($prop)) {
if (self::_isKeyword($prop)) {
continue;
}
@ -3917,13 +3901,13 @@ class JsonLdProcessor {
$next = $frame->{$prop}[0];
$omit_default_on = $this->_getFrameFlag(
$next, $options, 'omitDefault');
if(!$omit_default_on && !property_exists($output, $prop)) {
if (!$omit_default_on && !property_exists($output, $prop)) {
$preserve = '@null';
if(property_exists($next, '@default')) {
if (property_exists($next, '@default')) {
$preserve = self::copy($next->{'@default'});
}
$preserve = self::arrayify($preserve);
$output->{$prop} = array((object)array('@preserve' => $preserve));
$output->{$prop} = [(object) ['@preserve' => $preserve]];
}
}
@ -3945,12 +3929,13 @@ class JsonLdProcessor {
*
* @return array the implicit frame.
*/
function _createImplicitFrame($flags) {
function _createImplicitFrame($flags)
{
$frame = new stdClass();
foreach($flags as $key => $value) {
$frame->{'@' . $key} = array($flags[$key]);
foreach ($flags as $key => $value) {
$frame->{'@' . $key} = [$flags[$key]];
}
return array($frame);
return [$frame];
}
/**
@ -3962,9 +3947,10 @@ class JsonLdProcessor {
*
* @return bool true if a circular reference would be created, false if not.
*/
function _createsCircularReference($subject_to_embed, $subject_stack) {
for($i = count($subject_stack) - 1; $i >= 0; --$i) {
if($subject_stack[$i]->{'@id'} === $subject_to_embed->{'@id'}) {
function _createsCircularReference($subject_to_embed, $subject_stack)
{
for ($i = count($subject_stack) - 1; $i >= 0; --$i) {
if ($subject_stack[$i]->{'@id'} === $subject_to_embed->{'@id'}) {
return true;
}
}
@ -3980,20 +3966,21 @@ class JsonLdProcessor {
*
* @return mixed $the flag value.
*/
protected function _getFrameFlag($frame, $options, $name) {
protected function _getFrameFlag($frame, $options, $name)
{
$flag = "@$name";
$rval = (property_exists($frame, $flag) ?
$frame->{$flag}[0] : $options[$name]);
if($name === 'embed') {
if ($name === 'embed') {
// default is "@last"
// backwards-compatibility support for "embed" maps:
// true => "@last"
// false => "@never"
if($rval === true) {
if ($rval === true) {
$rval = '@last';
} else if($rval === false) {
} else if ($rval === false) {
$rval = '@never';
} else if($rval !== '@always' && $rval !== '@never' &&
} else if ($rval !== '@always' && $rval !== '@never' &&
$rval !== '@link') {
$rval = '@last';
}
@ -4006,11 +3993,11 @@ class JsonLdProcessor {
*
* @param array $frame the frame to validate.
*/
protected function _validateFrame($frame) {
if(!is_array($frame) || count($frame) !== 1 || !is_object($frame[0])) {
protected function _validateFrame($frame)
{
if (!is_array($frame) || count($frame) !== 1 || !is_object($frame[0])) {
throw new JsonLdException(
'Invalid JSON-LD syntax; a JSON-LD frame must be a single object.',
'jsonld.SyntaxError', null, array('frame' => $frame));
'Invalid JSON-LD syntax; a JSON-LD frame must be a single object.', 'jsonld.SyntaxError', null, ['frame' => $frame]);
}
}
@ -4024,12 +4011,13 @@ class JsonLdProcessor {
*
* @return stdClass all of the matched subjects.
*/
protected function _filterSubjects($state, $subjects, $frame, $flags) {
protected function _filterSubjects($state, $subjects, $frame, $flags)
{
$rval = new stdClass();
sort($subjects);
foreach($subjects as $id) {
foreach ($subjects as $id) {
$subject = $state->subjects->{$id};
if($this->_filterSubject($subject, $frame, $flags)) {
if ($this->_filterSubject($subject, $frame, $flags)) {
$rval->{$id} = $subject;
}
}
@ -4045,14 +4033,15 @@ class JsonLdProcessor {
*
* @return bool true if the subject matches, false if not.
*/
protected function _filterSubject($subject, $frame, $flags) {
protected function _filterSubject($subject, $frame, $flags)
{
// check @type (object value means 'any' type, fall through to ducktyping)
if(property_exists($frame, '@type') &&
if (property_exists($frame, '@type') &&
!(count($frame->{'@type'}) === 1 && is_object($frame->{'@type'}[0]))) {
$types = $frame->{'@type'};
foreach($types as $type) {
foreach ($types as $type) {
// any matching @type is a match
if(self::hasValue($subject, '@type', $type)) {
if (self::hasValue($subject, '@type', $type)) {
return true;
}
}
@ -4062,17 +4051,17 @@ class JsonLdProcessor {
// check ducktype
$wildcard = true;
$matches_some = false;
foreach($frame as $k => $v) {
if(self::_isKeyword($k)) {
foreach ($frame as $k => $v) {
if (self::_isKeyword($k)) {
// skip non-@id and non-@type
if($k !== '@id' && $k !== '@type') {
if ($k !== '@id' && $k !== '@type') {
continue;
}
$wildcard = false;
// check @id for a specific @id value
if($k === '@id' && is_string($v)) {
if(!property_exists($subject, $k) || $subject->{$k} !== $v) {
if ($k === '@id' && is_string($v)) {
if (!property_exists($subject, $k) || $subject->{$k} !== $v) {
return false;
}
$matches_some = true;
@ -4082,9 +4071,9 @@ class JsonLdProcessor {
$wildcard = false;
if(property_exists($subject, $k)) {
if (property_exists($subject, $k)) {
// $v === [] means do not match if property is present
if(is_array($v) && count($v) === 0) {
if (is_array($v) && count($v) === 0) {
return false;
}
$matches_some = true;
@ -4094,7 +4083,7 @@ class JsonLdProcessor {
// all properties must match to be a duck unless a @default is specified
$has_default = (is_array($v) && count($v) === 1 && is_object($v[0]) &&
property_exists($v[0], '@default'));
if($flags['requireAll'] && !$has_default) {
if ($flags['requireAll'] && !$has_default) {
return false;
}
}
@ -4109,20 +4098,21 @@ class JsonLdProcessor {
* @param stdClass $state the current framing state.
* @param string $id the @id of the embed to remove.
*/
protected function _removeEmbed($state, $id) {
protected function _removeEmbed($state, $id)
{
// get existing embed
$embeds = $state->uniqueEmbeds;
$embed = $embeds->{$id};
$property = $embed['property'];
// create reference to replace embed
$subject = (object)array('@id' => $id);
$subject = (object) ['@id' => $id];
// remove existing embed
if(is_array($embed->parent)) {
if (is_array($embed->parent)) {
// replace subject with reference
foreach($embed->parent as $i => $parent) {
if(self::compareValues($parent, $subject)) {
foreach ($embed->parent as $i => $parent) {
if (self::compareValues($parent, $subject)) {
$embed->parent[$i] = $subject;
break;
}
@ -4130,18 +4120,16 @@ class JsonLdProcessor {
} else {
// replace subject with reference
$use_array = is_array($embed->parent->{$property});
self::removeValue($embed->parent, $property, $subject,
array('propertyIsArray' => $use_array));
self::addValue($embed->parent, $property, $subject,
array('propertyIsArray' => $use_array));
self::removeValue($embed->parent, $property, $subject, ['propertyIsArray' => $use_array]);
self::addValue($embed->parent, $property, $subject, ['propertyIsArray' => $use_array]);
}
// recursively remove dependent dangling embeds
$removeDependents = function($id) {
// get embed keys as a separate array to enable deleting keys in map
$ids = array_keys((array)$embeds);
foreach($ids as $next) {
if(property_exists($embeds, $next) &&
$ids = array_keys((array) $embeds);
foreach ($ids as $next) {
if (property_exists($embeds, $next) &&
is_object($embeds->{$next}->parent) &&
$embeds->{$next}->parent->{'@id'} === $id) {
unset($embeds->{$next});
@ -4159,10 +4147,11 @@ class JsonLdProcessor {
* @param string $property the parent property.
* @param mixed $output the output to add.
*/
protected function _addFrameOutput($parent, $property, $output) {
if(is_object($parent) && !($parent instanceof ArrayObject)) {
protected function _addFrameOutput($parent, $property, $output)
{
if (is_object($parent) && !($parent instanceof ArrayObject)) {
self::addValue(
$parent, $property, $output, array('propertyIsArray' => true));
$parent, $property, $output, ['propertyIsArray' => true]);
} else {
$parent[] = $output;
}
@ -4177,34 +4166,35 @@ class JsonLdProcessor {
*
* @return mixed the resulting output.
*/
protected function _removePreserve($ctx, $input, $options) {
protected function _removePreserve($ctx, $input, $options)
{
// recurse through arrays
if(is_array($input)) {
$output = array();
foreach($input as $e) {
if (is_array($input)) {
$output = [];
foreach ($input as $e) {
$result = $this->_removePreserve($ctx, $e, $options);
// drop nulls from arrays
if($result !== null) {
if ($result !== null) {
$output[] = $result;
}
}
$input = $output;
} else if(is_object($input)) {
} else if (is_object($input)) {
// remove @preserve
if(property_exists($input, '@preserve')) {
if($input->{'@preserve'} === '@null') {
if (property_exists($input, '@preserve')) {
if ($input->{'@preserve'} === '@null') {
return null;
}
return $input->{'@preserve'};
}
// skip @values
if(self::_isValue($input)) {
if (self::_isValue($input)) {
return $input;
}
// recurse through @lists
if(self::_isList($input)) {
if (self::_isList($input)) {
$input->{'@list'} = $this->_removePreserve(
$ctx, $input->{'@list'}, $options);
return $input;
@ -4212,11 +4202,11 @@ class JsonLdProcessor {
// handle in-memory linked nodes
$id_alias = $this->_compactIri($ctx, '@id');
if(property_exists($input, $id_alias)) {
if (property_exists($input, $id_alias)) {
$id = $input->{$id_alias};
if(isset($options['link'][$id])) {
if (isset($options['link'][$id])) {
$idx = array_search($input, $options['link'][$id]);
if($idx === false) {
if ($idx === false) {
// prevent circular visitation
$options['link'][$id][] = $input;
} else {
@ -4225,15 +4215,15 @@ class JsonLdProcessor {
}
} else {
// prevent circular visitation
$options['link'][$id] = array($input);
$options['link'][$id] = [$input];
}
}
// recurse through properties
foreach($input as $prop => $v) {
foreach ($input as $prop => $v) {
$result = $this->_removePreserve($ctx, $v, $options);
$container = self::getContextValue($ctx, $prop, '@container');
if($options['compactArrays'] &&
if ($options['compactArrays'] &&
is_array($result) && count($result) === 1 &&
$container !== '@set' && $container !== '@list') {
$result = $result[0];
@ -4252,22 +4242,22 @@ class JsonLdProcessor {
*
* @return true if the triples are the same, false if not.
*/
protected static function _compareRDFTriples($t1, $t2) {
foreach(array('subject', 'predicate', 'object') as $attr) {
if($t1->{$attr}->type !== $t2->{$attr}->type ||
protected static function _compareRDFTriples($t1, $t2)
{
foreach (['subject', 'predicate', 'object'] as $attr) {
if ($t1->{$attr}->type !== $t2->{$attr}->type ||
$t1->{$attr}->value !== $t2->{$attr}->value) {
return false;
}
}
if(property_exists($t1->object, 'language') !==
property_exists($t1->object, 'language')) {
if (property_exists($t1->object, 'language') !== property_exists($t1->object, 'language')) {
return false;
}
if(property_exists($t1->object, 'language') &&
if (property_exists($t1->object, 'language') &&
$t1->object->language !== $t2->object->language) {
return false;
}
if(property_exists($t1->object, 'datatype') &&
if (property_exists($t1->object, 'datatype') &&
$t1->object->datatype !== $t2->object->datatype) {
return false;
}
@ -4283,16 +4273,17 @@ class JsonLdProcessor {
*
* @return string the new hash.
*/
protected function _hashQuads($id, $bnodes, $namer) {
protected function _hashQuads($id, $bnodes, $namer)
{
// return cached hash
if(property_exists($bnodes->{$id}, 'hash')) {
if (property_exists($bnodes->{$id}, 'hash')) {
return $bnodes->{$id}->hash;
}
// serialize all of bnode's quads
$quads = $bnodes->{$id}->quads;
$nquads = array();
foreach($quads as $quad) {
$nquads = [];
foreach ($quads as $quad) {
$nquads[] = $this->toNQuad($quad, property_exists($quad, 'name') ?
$quad->name->value : null, $id);
}
@ -4319,31 +4310,32 @@ class JsonLdProcessor {
*
* @return stdClass the hash and path namer used.
*/
protected function _hashPaths($id, $bnodes, $namer, $path_namer) {
protected function _hashPaths($id, $bnodes, $namer, $path_namer)
{
// create SHA-1 digest
$md = hash_init('sha1');
// group adjacent bnodes by hash, keep properties and references separate
$groups = new stdClass();
$quads = $bnodes->{$id}->quads;
foreach($quads as $quad) {
foreach ($quads as $quad) {
// get adjacent bnode
$bnode = $this->_getAdjacentBlankNodeName($quad->subject, $id);
if($bnode !== null) {
if ($bnode !== null) {
// normal property
$direction = 'p';
} else {
$bnode = $this->_getAdjacentBlankNodeName($quad->object, $id);
if($bnode !== null) {
if ($bnode !== null) {
// reverse property
$direction = 'r';
}
}
if($bnode !== null) {
if ($bnode !== null) {
// get bnode name (try canonical, path, then hash)
if($namer->isNamed($bnode)) {
if ($namer->isNamed($bnode)) {
$name = $namer->getName($bnode);
} else if($path_namer->isNamed($bnode)) {
} else if ($path_namer->isNamed($bnode)) {
$name = $path_namer->getName($bnode);
} else {
$name = $this->_hashQuads($bnode, $bnodes, $namer);
@ -4357,18 +4349,18 @@ class JsonLdProcessor {
$group_hash = hash_final($group_md);
// add bnode to hash group
if(property_exists($groups, $group_hash)) {
if (property_exists($groups, $group_hash)) {
$groups->{$group_hash}[] = $bnode;
} else {
$groups->{$group_hash} = array($bnode);
$groups->{$group_hash} = [$bnode];
}
}
}
// iterate over groups in sorted hash order
$group_hashes = array_keys((array)$groups);
$group_hashes = array_keys((array) $groups);
sort($group_hashes);
foreach($group_hashes as $group_hash) {
foreach ($group_hashes as $group_hash) {
// digest group hash
hash_update($md, $group_hash);
@ -4376,28 +4368,28 @@ class JsonLdProcessor {
$chosen_path = null;
$chosen_namer = null;
$permutator = new Permutator($groups->{$group_hash});
while($permutator->hasNext()) {
while ($permutator->hasNext()) {
$permutation = $permutator->next();
$path_namer_copy = clone $path_namer;
// build adjacent path
$path = '';
$skipped = false;
$recurse = array();
foreach($permutation as $bnode) {
$recurse = [];
foreach ($permutation as $bnode) {
// use canonical name if available
if($namer->isNamed($bnode)) {
if ($namer->isNamed($bnode)) {
$path .= $namer->getName($bnode);
} else {
// recurse if bnode isn't named in the path yet
if(!$path_namer_copy->isNamed($bnode)) {
if (!$path_namer_copy->isNamed($bnode)) {
$recurse[] = $bnode;
}
$path .= $path_namer_copy->getName($bnode);
}
// skip permutation if path is already >= chosen path
if($chosen_path !== null && strlen($path) >= strlen($chosen_path) &&
if ($chosen_path !== null && strlen($path) >= strlen($chosen_path) &&
$path > $chosen_path) {
$skipped = true;
break;
@ -4405,8 +4397,8 @@ class JsonLdProcessor {
}
// recurse
if(!$skipped) {
foreach($recurse as $bnode) {
if (!$skipped) {
foreach ($recurse as $bnode) {
$result = $this->_hashPaths(
$bnode, $bnodes, $namer, $path_namer_copy);
$path .= $path_namer_copy->getName($bnode);
@ -4414,7 +4406,7 @@ class JsonLdProcessor {
$path_namer_copy = $result->pathNamer;
// skip permutation if path is already >= chosen path
if($chosen_path !== null &&
if ($chosen_path !== null &&
strlen($path) >= strlen($chosen_path) && $path > $chosen_path) {
$skipped = true;
break;
@ -4422,7 +4414,7 @@ class JsonLdProcessor {
}
}
if(!$skipped && ($chosen_path === null || $path < $chosen_path)) {
if (!$skipped && ($chosen_path === null || $path < $chosen_path)) {
$chosen_path = $path;
$chosen_namer = $path_namer_copy;
}
@ -4434,8 +4426,8 @@ class JsonLdProcessor {
}
// return SHA-1 hash and path namer
return (object)array(
'hash' => hash_final($md), 'pathNamer' => $path_namer);
return (object) [
'hash' => hash_final($md), 'pathNamer' => $path_namer];
}
/**
@ -4448,8 +4440,9 @@ class JsonLdProcessor {
*
* @return mixed the adjacent blank node name or null if none was found.
*/
protected function _getAdjacentBlankNodeName($node, $id) {
if($node->type === 'blank node' && $node->value !== $id) {
protected function _getAdjacentBlankNodeName($node, $id)
{
if ($node->type === 'blank node' && $node->value !== $id) {
return $node->value;
}
return null;
@ -4463,16 +4456,17 @@ class JsonLdProcessor {
*
* @return integer -1 if a < b, 1 if a > b, 0 if a == b.
*/
protected function _compareShortestLeast($a, $b) {
protected function _compareShortestLeast($a, $b)
{
$len_a = strlen($a);
$len_b = strlen($b);
if($len_a < $len_b) {
if ($len_a < $len_b) {
return -1;
}
if($len_b < $len_a) {
if ($len_b < $len_a) {
return 1;
}
if($a === $b) {
if ($a === $b) {
return 0;
}
return ($a < $b) ? -1 : 1;
@ -4492,27 +4486,27 @@ class JsonLdProcessor {
* @return mixed the preferred term.
*/
protected function _selectTerm(
$active_ctx, $iri, $value, $containers,
$type_or_language, $type_or_language_value) {
if($type_or_language_value === null) {
$active_ctx, $iri, $value, $containers, $type_or_language, $type_or_language_value)
{
if ($type_or_language_value === null) {
$type_or_language_value = '@null';
}
// options for the value of @type or @language
$prefs = array();
$prefs = [];
// determine prefs for @id based on whether or not value compacts to a term
if(($type_or_language_value === '@id' ||
if (($type_or_language_value === '@id' ||
$type_or_language_value === '@reverse') &&
self::_isSubjectReference($value)) {
// prefer @reverse first
if($type_or_language_value === '@reverse') {
if ($type_or_language_value === '@reverse') {
$prefs[] = '@reverse';
}
// try to compact value to a term
$term = $this->_compactIri(
$active_ctx, $value->{'@id'}, null, array('vocab' => true));
if(property_exists($active_ctx->mappings, $term) &&
$active_ctx, $value->{'@id'}, null, ['vocab' => true]);
if (property_exists($active_ctx->mappings, $term) &&
$active_ctx->mappings->{$term} &&
$active_ctx->mappings->{$term}->{'@id'} === $value->{'@id'}) {
// prefer @vocab
@ -4527,17 +4521,16 @@ class JsonLdProcessor {
$prefs[] = '@none';
$container_map = $active_ctx->inverse->{$iri};
foreach($containers as $container) {
foreach ($containers as $container) {
// if container not available in the map, continue
if(!property_exists($container_map, $container)) {
if (!property_exists($container_map, $container)) {
continue;
}
$type_or_language_value_map =
$container_map->{$container}->{$type_or_language};
foreach($prefs as $pref) {
$type_or_language_value_map = $container_map->{$container}->{$type_or_language};
foreach ($prefs as $pref) {
// if type/language option not available in the map, continue
if(!property_exists($type_or_language_value_map, $pref)) {
if (!property_exists($type_or_language_value_map, $pref)) {
continue;
}
@ -4563,36 +4556,37 @@ class JsonLdProcessor {
* @return string the compacted term, prefix, keyword alias, or original IRI.
*/
protected function _compactIri(
$active_ctx, $iri, $value=null, $relative_to=array(), $reverse=false) {
$active_ctx, $iri, $value = null, $relative_to = [], $reverse = false)
{
// can't compact null
if($iri === null) {
if ($iri === null) {
return $iri;
}
$inverse_ctx = $this->_getInverseContext($active_ctx);
if(self::_isKeyword($iri)) {
if (self::_isKeyword($iri)) {
// a keyword can only be compacted to simple alias
if(property_exists($inverse_ctx, $iri)) {
if (property_exists($inverse_ctx, $iri)) {
return $inverse_ctx->$iri->{'@none'}->{'@type'}->{'@none'};
}
return $iri;
}
if(!isset($relative_to['vocab'])) {
if (!isset($relative_to['vocab'])) {
$relative_to['vocab'] = false;
}
// use inverse context to pick a term if iri is relative to vocab
if($relative_to['vocab'] && property_exists($inverse_ctx, $iri)) {
if ($relative_to['vocab'] && property_exists($inverse_ctx, $iri)) {
$default_language = '@none';
if(property_exists($active_ctx, '@language')) {
if (property_exists($active_ctx, '@language')) {
$default_language = $active_ctx->{'@language'};
}
// prefer @index if available in value
$containers = array();
if(is_object($value) && property_exists($value, '@index')) {
$containers = [];
if (is_object($value) && property_exists($value, '@index')) {
$containers[] = '@index';
}
@ -4600,26 +4594,26 @@ class JsonLdProcessor {
$type_or_language = '@language';
$type_or_language_value = '@null';
if($reverse) {
if ($reverse) {
$type_or_language = '@type';
$type_or_language_value = '@reverse';
$containers[] = '@set';
} else if(self::_isList($value)) {
} else if (self::_isList($value)) {
// choose the most specific term that works for all elements in @list
// only select @list containers if @index is NOT in value
if(!property_exists($value, '@index')) {
if (!property_exists($value, '@index')) {
$containers[] = '@list';
}
$list = $value->{'@list'};
$common_language = (count($list) === 0) ? $default_language : null;
$common_type = null;
foreach($list as $item) {
foreach ($list as $item) {
$item_language = '@none';
$item_type = '@none';
if(self::_isValue($item)) {
if(property_exists($item, '@language')) {
if (self::_isValue($item)) {
if (property_exists($item, '@language')) {
$item_language = $item->{'@language'};
} else if(property_exists($item, '@type')) {
} else if (property_exists($item, '@type')) {
$item_type = $item->{'@type'};
} else {
// plain literal
@ -4628,42 +4622,42 @@ class JsonLdProcessor {
} else {
$item_type = '@id';
}
if($common_language === null) {
if ($common_language === null) {
$common_language = $item_language;
} else if($item_language !== $common_language &&
} else if ($item_language !== $common_language &&
self::_isValue($item)) {
$common_language = '@none';
}
if($common_type === null) {
if ($common_type === null) {
$common_type = $item_type;
} else if($item_type !== $common_type) {
} else if ($item_type !== $common_type) {
$common_type = '@none';
}
// there are different languages and types in the list, so choose
// the most generic term, no need to keep iterating the list
if($common_language === '@none' && $common_type === '@none') {
if ($common_language === '@none' && $common_type === '@none') {
break;
}
}
if($common_language === null) {
if ($common_language === null) {
$common_language = '@none';
}
if($common_type === null) {
if ($common_type === null) {
$common_type = '@none';
}
if($common_type !== '@none') {
if ($common_type !== '@none') {
$type_or_language = '@type';
$type_or_language_value = $common_type;
} else {
$type_or_language_value = $common_language;
}
} else {
if(self::_isValue($value)) {
if(property_exists($value, '@language') &&
if (self::_isValue($value)) {
if (property_exists($value, '@language') &&
!property_exists($value, '@index')) {
$containers[] = '@language';
$type_or_language_value = $value->{'@language'};
} else if(property_exists($value, '@type')) {
} else if (property_exists($value, '@type')) {
$type_or_language = '@type';
$type_or_language_value = $value->{'@type'};
}
@ -4677,23 +4671,22 @@ class JsonLdProcessor {
// do term selection
$containers[] = '@none';
$term = $this->_selectTerm(
$active_ctx, $iri, $value,
$containers, $type_or_language, $type_or_language_value);
if($term !== null) {
$active_ctx, $iri, $value, $containers, $type_or_language, $type_or_language_value);
if ($term !== null) {
return $term;
}
}
// no term match, use @vocab if available
if($relative_to['vocab']) {
if(property_exists($active_ctx, '@vocab')) {
if ($relative_to['vocab']) {
if (property_exists($active_ctx, '@vocab')) {
// determine if vocab is a prefix of the iri
$vocab = $active_ctx->{'@vocab'};
if(strpos($iri, $vocab) === 0 && $iri !== $vocab) {
if (strpos($iri, $vocab) === 0 && $iri !== $vocab) {
// use suffix as relative iri if it is not a term in the active
// context
$suffix = substr($iri, strlen($vocab));
if(!property_exists($active_ctx->mappings, $suffix)) {
if (!property_exists($active_ctx->mappings, $suffix)) {
return $suffix;
}
}
@ -4703,14 +4696,14 @@ class JsonLdProcessor {
// no term or @vocab match, check for possible CURIEs
$choice = null;
$idx = 0;
$partial_matches = array();
$partial_matches = [];
$iri_map = $active_ctx->fast_curie_map;
// check for partial matches of against `iri`, which means look until
// iri.length - 1, not full length
$max_partial_length = strlen($iri) - 1;
for(; $idx < $max_partial_length && isset($iri_map[$iri[$idx]]); ++$idx) {
for (; $idx < $max_partial_length && isset($iri_map[$iri[$idx]]); ++$idx) {
$iri_map = $iri_map[$iri[$idx]];
if(isset($iri_map[''])) {
if (isset($iri_map[''])) {
$entry = $iri_map[''][0];
$entry->iri_length = $idx + 1;
$partial_matches[] = $entry;
@ -4718,9 +4711,9 @@ class JsonLdProcessor {
}
// check partial matches in reverse order to prefer longest ones first
$partial_matches = array_reverse($partial_matches);
foreach($partial_matches as $entry) {
foreach ($partial_matches as $entry) {
$terms = $entry->terms;
foreach($terms as $term) {
foreach ($terms as $term) {
// a CURIE is usable if:
// 1. it has no mapping, OR
// 2. value is null, which means we're not compacting an @value, AND
@ -4732,7 +4725,7 @@ class JsonLdProcessor {
// select curie if it is shorter or the same length but
// lexicographically less than the current choice
if($is_usable_curie && ($choice === null ||
if ($is_usable_curie && ($choice === null ||
self::_compareShortestLeast($curie, $choice) < 0)) {
$choice = $curie;
}
@ -4740,12 +4733,12 @@ class JsonLdProcessor {
}
// return chosen curie
if($choice !== null) {
if ($choice !== null) {
return $choice;
}
// compact IRI relative to base
if(!$relative_to['vocab']) {
if (!$relative_to['vocab']) {
return jsonld_remove_base($active_ctx->{'@base'}, $iri);
}
@ -4764,9 +4757,10 @@ class JsonLdProcessor {
*
* @return mixed the compaction result.
*/
protected function _compactValue($active_ctx, $active_property, $value) {
protected function _compactValue($active_ctx, $active_property, $value)
{
// value is a @value
if(self::_isValue($value)) {
if (self::_isValue($value)) {
// get context rules
$type = self::getContextValue($active_ctx, $active_property, '@type');
$language = self::getContextValue(
@ -4779,9 +4773,9 @@ class JsonLdProcessor {
$container !== '@index');
// if there's no @index to preserve
if(!$preserve_index) {
if (!$preserve_index) {
// matching @type or @language specified in context, compact value
if(self::_hasKeyValue($value, '@type', $type) ||
if (self::_hasKeyValue($value, '@type', $type) ||
self::_hasKeyValue($value, '@language', $language)) {
return $value->{'@value'};
}
@ -4791,7 +4785,7 @@ class JsonLdProcessor {
// 1. @value is the only key or @index isn't being preserved
// 2. there is no default language or @value is not a string or
// the key has a mapping with a null @language
$key_count = count(array_keys((array)$value));
$key_count = count(array_keys((array) $value));
$is_value_only_key = ($key_count === 1 ||
($key_count === 2 && property_exists($value, '@index') &&
!$preserve_index));
@ -4802,7 +4796,7 @@ class JsonLdProcessor {
$active_ctx->mappings->{$active_property} !== null &&
self::_hasKeyValue(
$active_ctx->mappings->{$active_property}, '@language', null));
if($is_value_only_key &&
if ($is_value_only_key &&
(!$has_default_language || !$is_value_string || $has_null_mapping)) {
return $value->{'@value'};
}
@ -4810,18 +4804,17 @@ class JsonLdProcessor {
$rval = new stdClass();
// preserve @index
if($preserve_index) {
if ($preserve_index) {
$rval->{$this->_compactIri($active_ctx, '@index')} = $value->{'@index'};
}
// compact @type IRI
if(property_exists($value, '@type')) {
if (property_exists($value, '@type')) {
$rval->{$this->_compactIri($active_ctx, '@type')} = $this->_compactIri(
$active_ctx, $value->{'@type'}, null, array('vocab' => true));
} else if(property_exists($value, '@language')) {
$active_ctx, $value->{'@type'}, null, ['vocab' => true]);
} else if (property_exists($value, '@language')) {
// alias @language
$rval->{$this->_compactIri($active_ctx, '@language')} =
$value->{'@language'};
$rval->{$this->_compactIri($active_ctx, '@language')} = $value->{'@language'};
}
// alias @value
@ -4832,20 +4825,19 @@ class JsonLdProcessor {
// value is a subject reference
$expanded_property = $this->_expandIri(
$active_ctx, $active_property, array('vocab' => true));
$active_ctx, $active_property, ['vocab' => true]);
$type = self::getContextValue($active_ctx, $active_property, '@type');
$compacted = $this->_compactIri(
$active_ctx, $value->{'@id'}, null,
array('vocab' => ($type === '@vocab')));
$active_ctx, $value->{'@id'}, null, ['vocab' => ($type === '@vocab')]);
// compact to scalar
if($type === '@id' || $type === '@vocab' ||
if ($type === '@id' || $type === '@vocab' ||
$expanded_property === '@graph') {
return $compacted;
}
$rval = (object)array(
$this->_compactIri($active_ctx, '@id') => $compacted);
$rval = (object) [
$this->_compactIri($active_ctx, '@id') => $compacted];
return $rval;
}
@ -4859,31 +4851,28 @@ class JsonLdProcessor {
* and prevent double definitions.
*/
protected function _createTermDefinition(
$active_ctx, $local_ctx, $term, $defined) {
if(property_exists($defined, $term)) {
$active_ctx, $local_ctx, $term, $defined)
{
if (property_exists($defined, $term)) {
// term already defined
if($defined->{$term}) {
if ($defined->{$term}) {
return;
}
// cycle detected
throw new JsonLdException(
'Cyclical context definition detected.',
'jsonld.CyclicalContext', 'cyclic IRI mapping',
array('context' => $local_ctx, 'term' => $term));
'Cyclical context definition detected.', 'jsonld.CyclicalContext', 'cyclic IRI mapping', ['context' => $local_ctx, 'term' => $term]);
}
// now defining term
$defined->{$term} = false;
if(self::_isKeyword($term)) {
if (self::_isKeyword($term)) {
throw new JsonLdException(
'Invalid JSON-LD syntax; keywords cannot be overridden.',
'jsonld.SyntaxError', 'keyword redefinition',
array('context' => $local_ctx, 'term' => $term));
'Invalid JSON-LD syntax; keywords cannot be overridden.', 'jsonld.SyntaxError', 'keyword redefinition', ['context' => $local_ctx, 'term' => $term]);
}
// remove old mapping
if(property_exists($active_ctx->mappings, $term)) {
if (property_exists($active_ctx->mappings, $term)) {
unset($active_ctx->mappings->{$term});
}
@ -4891,7 +4880,7 @@ class JsonLdProcessor {
$value = $local_ctx->{$term};
// clear context entry
if($value === null || (is_object($value) &&
if ($value === null || (is_object($value) &&
self::_hasKeyValue($value, '@id', null))) {
$active_ctx->mappings->{$term} = null;
$defined->{$term} = true;
@ -4899,68 +4888,56 @@ class JsonLdProcessor {
}
// convert short-hand value to object w/@id
if(is_string($value)) {
$value = (object)array('@id' => $value);
if (is_string($value)) {
$value = (object) ['@id' => $value];
}
if(!is_object($value)) {
if (!is_object($value)) {
throw new JsonLdException(
'Invalid JSON-LD syntax; @context property values must be ' .
'strings or objects.', 'jsonld.SyntaxError', 'invalid term definition',
array('context' => $local_ctx));
'strings or objects.', 'jsonld.SyntaxError', 'invalid term definition', ['context' => $local_ctx]);
}
// create new mapping
$mapping = $active_ctx->mappings->{$term} = new stdClass();
$mapping->reverse = false;
if(property_exists($value, '@reverse')) {
if(property_exists($value, '@id')) {
if (property_exists($value, '@reverse')) {
if (property_exists($value, '@id')) {
throw new JsonLdException(
'Invalid JSON-LD syntax; a @reverse term definition must not ' +
'contain @id.', 'jsonld.SyntaxError', 'invalid reverse property',
array('context' => $local_ctx));
'contain @id.', 'jsonld.SyntaxError', 'invalid reverse property', ['context' => $local_ctx]);
}
$reverse = $value->{'@reverse'};
if(!is_string($reverse)) {
if (!is_string($reverse)) {
throw new JsonLdException(
'Invalid JSON-LD syntax; a @context @reverse value must be a string.',
'jsonld.SyntaxError', 'invalid IRI mapping',
array('context' => $local_ctx));
'Invalid JSON-LD syntax; a @context @reverse value must be a string.', 'jsonld.SyntaxError', 'invalid IRI mapping', ['context' => $local_ctx]);
}
// expand and add @id mapping
$id = $this->_expandIri(
$active_ctx, $reverse, array('vocab' => true, 'base' => false),
$local_ctx, $defined);
if(!self::_isAbsoluteIri($id)) {
$active_ctx, $reverse, ['vocab' => true, 'base' => false], $local_ctx, $defined);
if (!self::_isAbsoluteIri($id)) {
throw new JsonLdException(
'Invalid JSON-LD syntax; @context @reverse value must be ' .
'an absolute IRI or a blank node identifier.',
'jsonld.SyntaxError', 'invalid IRI mapping',
array('context' => $local_ctx));
'an absolute IRI or a blank node identifier.', 'jsonld.SyntaxError', 'invalid IRI mapping', ['context' => $local_ctx]);
}
$mapping->{'@id'} = $id;
$mapping->reverse = true;
} else if(property_exists($value, '@id')) {
} else if (property_exists($value, '@id')) {
$id = $value->{'@id'};
if(!is_string($id)) {
if (!is_string($id)) {
throw new JsonLdException(
'Invalid JSON-LD syntax; @context @id value must be a string.',
'jsonld.SyntaxError', 'invalid IRI mapping',
array('context' => $local_ctx));
'Invalid JSON-LD syntax; @context @id value must be a string.', 'jsonld.SyntaxError', 'invalid IRI mapping', ['context' => $local_ctx]);
}
if($id !== $term) {
if ($id !== $term) {
// add @id to mapping
$id = $this->_expandIri(
$active_ctx, $id, array('vocab' => true, 'base' => false),
$local_ctx, $defined);
if(!self::_isAbsoluteIri($id) && !self::_isKeyword($id)) {
$active_ctx, $id, ['vocab' => true, 'base' => false], $local_ctx, $defined);
if (!self::_isAbsoluteIri($id) && !self::_isKeyword($id)) {
throw new JsonLdException(
'Invalid JSON-LD syntax; @context @id value must be an ' .
'absolute IRI, a blank node identifier, or a keyword.',
'jsonld.SyntaxError', 'invalid IRI mapping',
array('context' => $local_ctx));
'absolute IRI, a blank node identifier, or a keyword.', 'jsonld.SyntaxError', 'invalid IRI mapping', ['context' => $local_ctx]);
}
$mapping->{'@id'} = $id;
}
@ -4971,17 +4948,17 @@ class JsonLdProcessor {
$colon = strpos($term, ':');
$mapping->_term_has_colon = ($colon !== false);
if(!property_exists($mapping, '@id')) {
if (!property_exists($mapping, '@id')) {
// see if the term has a prefix
if($mapping->_term_has_colon) {
if ($mapping->_term_has_colon) {
$prefix = substr($term, 0, $colon);
if(property_exists($local_ctx, $prefix)) {
if (property_exists($local_ctx, $prefix)) {
// define parent prefix
$this->_createTermDefinition(
$active_ctx, $local_ctx, $prefix, $defined);
}
if(property_exists($active_ctx->mappings, $prefix) &&
if (property_exists($active_ctx->mappings, $prefix) &&
$active_ctx->mappings->{$prefix}) {
// set @id based on prefix parent
$suffix = substr($term, $colon + 1);
@ -4993,11 +4970,9 @@ class JsonLdProcessor {
}
} else {
// non-IRIs *must* define @ids if @vocab is not available
if(!property_exists($active_ctx, '@vocab')) {
if (!property_exists($active_ctx, '@vocab')) {
throw new JsonLdException(
'Invalid JSON-LD syntax; @context terms must define an @id.',
'jsonld.SyntaxError', 'invalid IRI mapping',
array('context' => $local_ctx, 'term' => $term));
'Invalid JSON-LD syntax; @context terms must define an @id.', 'jsonld.SyntaxError', 'invalid IRI mapping', ['context' => $local_ctx, 'term' => $term]);
}
// prepend vocab to term
$mapping->{'@id'} = $active_ctx->{'@vocab'} . $term;
@ -5010,31 +4985,26 @@ class JsonLdProcessor {
// IRI mapping now defined
$defined->{$term} = true;
if(property_exists($value, '@type')) {
if (property_exists($value, '@type')) {
$type = $value->{'@type'};
if(!is_string($type)) {
if (!is_string($type)) {
throw new JsonLdException(
'Invalid JSON-LD syntax; @context @type values must be strings.',
'jsonld.SyntaxError', 'invalid type mapping',
array('context' => $local_ctx));
'Invalid JSON-LD syntax; @context @type values must be strings.', 'jsonld.SyntaxError', 'invalid type mapping', ['context' => $local_ctx]);
}
if($type !== '@id' && $type !== '@vocab') {
if ($type !== '@id' && $type !== '@vocab') {
// expand @type to full IRI
$type = $this->_expandIri(
$active_ctx, $type, array('vocab' => true), $local_ctx, $defined);
if(!self::_isAbsoluteIri($type)) {
$active_ctx, $type, ['vocab' => true], $local_ctx, $defined);
if (!self::_isAbsoluteIri($type)) {
throw new JsonLdException(
'Invalid JSON-LD syntax; an @context @type value must ' .
'be an absolute IRI.', 'jsonld.SyntaxError',
'invalid type mapping', array('context' => $local_ctx));
'be an absolute IRI.', 'jsonld.SyntaxError', 'invalid type mapping', ['context' => $local_ctx]);
}
if(strpos($type, '_:') === 0) {
if (strpos($type, '_:') === 0) {
throw new JsonLdException(
'Invalid JSON-LD syntax; an @context @type values must ' .
'be an IRI, not a blank node identifier.',
'jsonld.SyntaxError', 'invalid type mapping',
array('context' => $local_ctx));
'be an IRI, not a blank node identifier.', 'jsonld.SyntaxError', 'invalid type mapping', ['context' => $local_ctx]);
}
}
@ -5042,41 +5012,36 @@ class JsonLdProcessor {
$mapping->{'@type'} = $type;
}
if(property_exists($value, '@container')) {
if (property_exists($value, '@container')) {
$container = $value->{'@container'};
if($container !== '@list' && $container !== '@set' &&
if ($container !== '@list' && $container !== '@set' &&
$container !== '@index' && $container !== '@language') {
throw new JsonLdException(
'Invalid JSON-LD syntax; @context @container value must be ' .
'one of the following: @list, @set, @index, or @language.',
'jsonld.SyntaxError', 'invalid container mapping',
array('context' => $local_ctx));
'one of the following: @list, @set, @index, or @language.', 'jsonld.SyntaxError', 'invalid container mapping', ['context' => $local_ctx]);
}
if($mapping->reverse && $container !== '@index' &&
if ($mapping->reverse && $container !== '@index' &&
$container !== '@set' && $container !== null) {
throw new JsonLdException(
'Invalid JSON-LD syntax; @context @container value for a @reverse ' +
'type definition must be @index or @set.',
'jsonld.SyntaxError', 'invalid reverse property',
array('context' => $local_ctx));
'type definition must be @index or @set.', 'jsonld.SyntaxError', 'invalid reverse property', ['context' => $local_ctx]);
}
// add @container to mapping
$mapping->{'@container'} = $container;
}
if(property_exists($value, '@language') &&
if (property_exists($value, '@language') &&
!property_exists($value, '@type')) {
$language = $value->{'@language'};
if($language !== null && !is_string($language)) {
if ($language !== null && !is_string($language)) {
throw new JsonLdException(
'Invalid JSON-LD syntax; @context @language value must be ' .
'a string or null.', 'jsonld.SyntaxError',
'invalid language mapping', array('context' => $local_ctx));
'a string or null.', 'jsonld.SyntaxError', 'invalid language mapping', ['context' => $local_ctx]);
}
// add @language to mapping
if($language !== null) {
if ($language !== null) {
$language = strtolower($language);
}
$mapping->{'@language'} = $language;
@ -5084,11 +5049,9 @@ class JsonLdProcessor {
// disallow aliasing @context and @preserve
$id = $mapping->{'@id'};
if($id === '@context' || $id === '@preserve') {
if ($id === '@context' || $id === '@preserve') {
throw new JsonLdException(
'Invalid JSON-LD syntax; @context and @preserve cannot be aliased.',
'jsonld.SyntaxError', 'invalid keyword alias',
array('context' => $local_ctx));
'Invalid JSON-LD syntax; @context and @preserve cannot be aliased.', 'jsonld.SyntaxError', 'invalid keyword alias', ['context' => $local_ctx]);
}
}
@ -5110,24 +5073,25 @@ class JsonLdProcessor {
* @return mixed the expanded value.
*/
function _expandIri(
$active_ctx, $value, $relative_to=array(), $local_ctx=null, $defined=null) {
$active_ctx, $value, $relative_to = [], $local_ctx = null, $defined = null)
{
// already expanded
if($value === null || self::_isKeyword($value)) {
if ($value === null || self::_isKeyword($value)) {
return $value;
}
// define term dependency if not defined
if($local_ctx !== null && property_exists($local_ctx, $value) &&
if ($local_ctx !== null && property_exists($local_ctx, $value) &&
!self::_hasKeyValue($defined, $value, true)) {
$this->_createTermDefinition($active_ctx, $local_ctx, $value, $defined);
}
if(isset($relative_to['vocab']) && $relative_to['vocab']) {
if(property_exists($active_ctx->mappings, $value)) {
if (isset($relative_to['vocab']) && $relative_to['vocab']) {
if (property_exists($active_ctx->mappings, $value)) {
$mapping = $active_ctx->mappings->{$value};
// value is explicitly ignored with a null mapping
if($mapping === null) {
if ($mapping === null) {
return null;
}
@ -5138,26 +5102,26 @@ class JsonLdProcessor {
// split value into prefix:suffix
$colon = strpos($value, ':');
if($colon !== false) {
if ($colon !== false) {
$prefix = substr($value, 0, $colon);
$suffix = substr($value, $colon + 1);
// do not expand blank nodes (prefix of '_') or already-absolute
// IRIs (suffix of '//')
if($prefix === '_' || strpos($suffix, '//') === 0) {
if ($prefix === '_' || strpos($suffix, '//') === 0) {
return $value;
}
// prefix dependency not defined, define it
if($local_ctx !== null && property_exists($local_ctx, $prefix)) {
if ($local_ctx !== null && property_exists($local_ctx, $prefix)) {
$this->_createTermDefinition(
$active_ctx, $local_ctx, $prefix, $defined);
}
// use mapping if prefix is defined
if(property_exists($active_ctx->mappings, $prefix)) {
if (property_exists($active_ctx->mappings, $prefix)) {
$mapping = $active_ctx->mappings->{$prefix};
if($mapping) {
if ($mapping) {
return $mapping->{'@id'} . $suffix;
}
}
@ -5167,14 +5131,14 @@ class JsonLdProcessor {
}
// prepend vocab
if(isset($relative_to['vocab']) && $relative_to['vocab'] &&
if (isset($relative_to['vocab']) && $relative_to['vocab'] &&
property_exists($active_ctx, '@vocab')) {
return $active_ctx->{'@vocab'} . $value;
}
// prepend base
$rval = $value;
if(isset($relative_to['base']) && $relative_to['base']) {
if (isset($relative_to['base']) && $relative_to['base']) {
$rval = jsonld_prepend_base($active_ctx->{'@base'}, $rval);
}
@ -5190,28 +5154,29 @@ class JsonLdProcessor {
* the @contexts from the urls map, false not to.
* @param string $base the base URL to resolve relative URLs with.
*/
protected function _findContextUrls($input, $urls, $replace, $base) {
if(is_array($input)) {
foreach($input as $e) {
protected function _findContextUrls($input, $urls, $replace, $base)
{
if (is_array($input)) {
foreach ($input as $e) {
$this->_findContextUrls($e, $urls, $replace, $base);
}
} else if(is_object($input)) {
foreach($input as $k => &$v) {
if($k !== '@context') {
} else if (is_object($input)) {
foreach ($input as $k => &$v) {
if ($k !== '@context') {
$this->_findContextUrls($v, $urls, $replace, $base);
continue;
}
// array @context
if(is_array($v)) {
if (is_array($v)) {
$length = count($v);
for($i = 0; $i < $length; ++$i) {
if(is_string($v[$i])) {
for ($i = 0; $i < $length; ++$i) {
if (is_string($v[$i])) {
$url = jsonld_prepend_base($base, $v[$i]);
// replace w/@context if requested
if($replace) {
if ($replace) {
$ctx = $urls->{$url};
if(is_array($ctx)) {
if (is_array($ctx)) {
// add flattened context
array_splice($v, $i, 1, $ctx);
$i += count($ctx) - 1;
@ -5219,19 +5184,19 @@ class JsonLdProcessor {
} else {
$v[$i] = $ctx;
}
} else if(!property_exists($urls, $url)) {
} else if (!property_exists($urls, $url)) {
// @context URL found
$urls->{$url} = false;
}
}
}
} else if(is_string($v)) {
} else if (is_string($v)) {
// string @context
$v = jsonld_prepend_base($base, $v);
// replace w/@context if requested
if($replace) {
if ($replace) {
$input->{$k} = $urls->{$v};
} else if(!property_exists($urls, $v)) {
} else if (!property_exists($urls, $v)) {
// @context URL found
$urls->{$v} = false;
}
@ -5253,12 +5218,11 @@ class JsonLdProcessor {
* @return mixed the result.
*/
protected function _retrieveContextUrls(
&$input, $cycles, $load_document, $base='') {
if(count(get_object_vars($cycles)) > self::MAX_CONTEXT_URLS) {
&$input, $cycles, $load_document, $base = '')
{
if (count(get_object_vars($cycles)) > self::MAX_CONTEXT_URLS) {
throw new JsonLdException(
'Maximum number of @context URLs exceeded.',
'jsonld.ContextUrlError', 'loading remote context failed',
array('max' => self::MAX_CONTEXT_URLS));
'Maximum number of @context URLs exceeded.', 'jsonld.ContextUrlError', 'loading remote context failed', ['max' => self::MAX_CONTEXT_URLS]);
}
// for tracking the URLs to retrieve
@ -5271,27 +5235,24 @@ class JsonLdProcessor {
$this->_findContextUrls($input, $urls, false, $base);
// queue all unretrieved URLs
$queue = array();
foreach($urls as $url => $ctx) {
if($ctx === false) {
$queue = [];
foreach ($urls as $url => $ctx) {
if ($ctx === false) {
// validate URL
if(!preg_match($regex, $url)) {
if (!preg_match($regex, $url)) {
throw new JsonLdException(
'Malformed or unsupported URL.', 'jsonld.InvalidUrl',
'loading remote context failed', array('url' => $url));
'Malformed or unsupported URL.', 'jsonld.InvalidUrl', 'loading remote context failed', ['url' => $url]);
}
$queue[] = $url;
}
}
// retrieve URLs in queue
foreach($queue as $url) {
foreach ($queue as $url) {
// check for context URL cycle
if(property_exists($cycles, $url)) {
if (property_exists($cycles, $url)) {
throw new JsonLdException(
'Cyclical @context URLs detected.',
'jsonld.ContextUrlError', 'recursive context inclusion',
array('url' => $url));
'Cyclical @context URLs detected.', 'jsonld.ContextUrlError', 'recursive context inclusion', ['url' => $url]);
}
$_cycles = self::copy($cycles);
$_cycles->{$url} = true;
@ -5301,33 +5262,30 @@ class JsonLdProcessor {
$ctx = $remote_doc->document;
// parse string context as JSON
if(is_string($ctx)) {
if (is_string($ctx)) {
try {
$ctx = self::_parse_json($ctx);
} catch(Exception $e) {
} catch (Exception $e) {
throw new JsonLdException(
'Could not parse JSON from URL.',
'jsonld.ParseError', 'loading remote context failed',
array('url' => $url), $e);
'Could not parse JSON from URL.', 'jsonld.ParseError', 'loading remote context failed', ['url' => $url], $e);
}
}
// ensure ctx is an object
if(!is_object($ctx)) {
if (!is_object($ctx)) {
throw new JsonLdException(
'Derefencing a URL did not result in a valid JSON-LD object.',
'jsonld.InvalidUrl', 'invalid remote context', array('url' => $url));
'Derefencing a URL did not result in a valid JSON-LD object.', 'jsonld.InvalidUrl', 'invalid remote context', ['url' => $url]);
}
// use empty context if no @context key is present
if(!property_exists($ctx, '@context')) {
$ctx = (object)array('@context' => new stdClass());
if (!property_exists($ctx, '@context')) {
$ctx = (object) ['@context' => new stdClass()];
} else {
$ctx = (object)array('@context' => $ctx->{'@context'});
$ctx = (object) ['@context' => $ctx->{'@context'}];
}
// append context URL to context if given
if($remote_doc->contextUrl !== null) {
if ($remote_doc->contextUrl !== null) {
$ctx->{'@context'} = self::arrayify($ctx->{'@context'});
$ctx->{'@context'}[] = $remote_doc->contextUrl;
}
@ -5349,11 +5307,12 @@ class JsonLdProcessor {
*
* @return stdClass the initial context.
*/
protected function _getInitialContext($options) {
return (object)array(
protected function _getInitialContext($options)
{
return (object) [
'@base' => jsonld_parse_url($options['base']),
'mappings' => new stdClass(),
'inverse' => null);
'inverse' => null];
}
/**
@ -5364,9 +5323,10 @@ class JsonLdProcessor {
*
* @return stdClass the inverse context.
*/
protected function _getInverseContext($active_ctx) {
protected function _getInverseContext($active_ctx)
{
// inverse context already generated
if($active_ctx->inverse) {
if ($active_ctx->inverse) {
return $active_ctx->inverse;
}
@ -5374,27 +5334,27 @@ class JsonLdProcessor {
// variables for building fast CURIE map
$fast_curie_map = $active_ctx->fast_curie_map = new ArrayObject();
$iris_to_terms = array();
$iris_to_terms = [];
// handle default language
$default_language = '@none';
if(property_exists($active_ctx, '@language')) {
if (property_exists($active_ctx, '@language')) {
$default_language = $active_ctx->{'@language'};
}
// create term selections for each mapping in the context, ordered by
// shortest and then lexicographically least
$mappings = $active_ctx->mappings;
$terms = array_keys((array)$mappings);
usort($terms, array($this, '_compareShortestLeast'));
foreach($terms as $term) {
$terms = array_keys((array) $mappings);
usort($terms, [$this, '_compareShortestLeast']);
foreach ($terms as $term) {
$mapping = $mappings->{$term};
if($mapping === null) {
if ($mapping === null) {
continue;
}
// add term selection where it applies
if(property_exists($mapping, '@container')) {
if (property_exists($mapping, '@container')) {
$container = $mapping->{'@container'};
} else {
$container = '@none';
@ -5403,49 +5363,49 @@ class JsonLdProcessor {
// iterate over every IRI in the mapping
$iris = $mapping->{'@id'};
$iris = self::arrayify($iris);
foreach($iris as $iri) {
foreach ($iris as $iri) {
$is_keyword = self::_isKeyword($iri);
// initialize container map
if(!property_exists($inverse, $iri)) {
if (!property_exists($inverse, $iri)) {
$inverse->{$iri} = new stdClass();
if(!$is_keyword && !$mapping->_term_has_colon) {
if (!$is_keyword && !$mapping->_term_has_colon) {
// init IRI to term map and fast CURIE map
$iris_to_terms[$iri] = new ArrayObject();
$iris_to_terms[$iri][] = $term;
$fast_curie_entry = (object)array(
'iri' => $iri, 'terms' => $iris_to_terms[$iri]);
if(!array_key_exists($iri[0], (array)$fast_curie_map)) {
$fast_curie_entry = (object) [
'iri' => $iri, 'terms' => $iris_to_terms[$iri]];
if (!array_key_exists($iri[0], (array) $fast_curie_map)) {
$fast_curie_map[$iri[0]] = new ArrayObject();
}
$fast_curie_map[$iri[0]][] = $fast_curie_entry;
}
} else if(!$is_keyword && !$mapping->_term_has_colon) {
} else if (!$is_keyword && !$mapping->_term_has_colon) {
// add IRI to term match
$iris_to_terms[$iri][] = $term;
}
$container_map = $inverse->{$iri};
// add new entry
if(!property_exists($container_map, $container)) {
$container_map->{$container} = (object)array(
if (!property_exists($container_map, $container)) {
$container_map->{$container} = (object) [
'@language' => new stdClass(),
'@type' => new stdClass());
'@type' => new stdClass()];
}
$entry = $container_map->{$container};
if($mapping->reverse) {
if ($mapping->reverse) {
// term is preferred for values using @reverse
$this->_addPreferredTerm(
$mapping, $term, $entry->{'@type'}, '@reverse');
} else if(property_exists($mapping, '@type')) {
} else if (property_exists($mapping, '@type')) {
// term is preferred for values using specific type
$this->_addPreferredTerm(
$mapping, $term, $entry->{'@type'}, $mapping->{'@type'});
} else if(property_exists($mapping, '@language')) {
} else if (property_exists($mapping, '@language')) {
// term is preferred for values using specific language
$language = $mapping->{'@language'};
if($language === null) {
if ($language === null) {
$language = '@null';
}
$this->_addPreferredTerm(
@ -5467,7 +5427,7 @@ class JsonLdProcessor {
}
// build fast CURIE map
foreach($fast_curie_map as $key => $value) {
foreach ($fast_curie_map as $key => $value) {
$this->_buildIriMap($fast_curie_map, $key, 1);
}
@ -5482,25 +5442,26 @@ class JsonLdProcessor {
* @param string $key the current key in the map to work on.
* @param int $idx the index into the IRI to compare.
*/
function _buildIriMap($iri_map, $key, $idx) {
function _buildIriMap($iri_map, $key, $idx)
{
$entries = $iri_map[$key];
$next = $iri_map[$key] = new ArrayObject();
foreach($entries as $entry) {
foreach ($entries as $entry) {
$iri = $entry->iri;
if($idx >= strlen($iri)) {
if ($idx >= strlen($iri)) {
$letter = '';
} else {
$letter = $iri[$idx];
}
if(!isset($next[$letter])) {
if (!isset($next[$letter])) {
$next[$letter] = new ArrayObject();
}
$next[$letter][] = $entry;
}
foreach($next as $key => $value) {
if($key === '') {
foreach ($next as $key => $value) {
if ($key === '') {
continue;
}
$this->_buildIriMap($next, $key, $idx + 1);
@ -5516,8 +5477,9 @@ class JsonLdProcessor {
* add to.
* @param string $type_or_language_value the key in the entry to add to.
*/
function _addPreferredTerm($mapping, $term, $entry, $type_or_language_value) {
if(!property_exists($entry, $type_or_language_value)) {
function _addPreferredTerm($mapping, $term, $entry, $type_or_language_value)
{
if (!property_exists($entry, $type_or_language_value)) {
$entry->{$type_or_language_value} = $term;
}
}
@ -5527,15 +5489,16 @@ class JsonLdProcessor {
*
* @return stdClass a clone (child) of the active context.
*/
protected function _cloneActiveContext($active_ctx) {
protected function _cloneActiveContext($active_ctx)
{
$child = new stdClass();
$child->{'@base'} = $active_ctx->{'@base'};
$child->mappings = self::copy($active_ctx->mappings);
$child->inverse = null;
if(property_exists($active_ctx, '@language')) {
if (property_exists($active_ctx, '@language')) {
$child->{'@language'} = $active_ctx->{'@language'};
}
if(property_exists($active_ctx, '@vocab')) {
if (property_exists($active_ctx, '@vocab')) {
$child->{'@vocab'} = $active_ctx->{'@vocab'};
}
return $child;
@ -5548,11 +5511,12 @@ class JsonLdProcessor {
*
* @return bool true if the value is a keyword, false if not.
*/
protected static function _isKeyword($v) {
if(!is_string($v)) {
protected static function _isKeyword($v)
{
if (!is_string($v)) {
return false;
}
switch($v) {
switch ($v) {
case '@base':
case '@context':
case '@container':
@ -5584,7 +5548,8 @@ class JsonLdProcessor {
*
* @return bool true if the value is an empty Object, false if not.
*/
protected static function _isEmptyObject($v) {
protected static function _isEmptyObject($v)
{
return is_object($v) && count(get_object_vars($v)) === 0;
}
@ -5593,30 +5558,30 @@ class JsonLdProcessor {
*
* @param mixed $v the value to check.
*/
protected static function _validateTypeValue($v) {
protected static function _validateTypeValue($v)
{
// must be a string or empty object
if(is_string($v) || self::_isEmptyObject($v)) {
if (is_string($v) || self::_isEmptyObject($v)) {
return;
}
// must be an array
$is_valid = false;
if(is_array($v)) {
if (is_array($v)) {
// must contain only strings
$is_valid = true;
foreach($v as $e) {
if(!(is_string($e))) {
foreach ($v as $e) {
if (!(is_string($e))) {
$is_valid = false;
break;
}
}
}
if(!$is_valid) {
if (!$is_valid) {
throw new JsonLdException(
'Invalid JSON-LD syntax; "@type" value must a string, an array ' .
'of strings, or an empty object.',
'jsonld.SyntaxError', 'invalid type value', array('value' => $v));
'of strings, or an empty object.', 'jsonld.SyntaxError', 'invalid type value', ['value' => $v]);
}
}
@ -5627,13 +5592,14 @@ class JsonLdProcessor {
*
* @return bool true if the value is a subject with properties, false if not.
*/
protected static function _isSubject($v) {
protected static function _isSubject($v)
{
// Note: A value is a subject if all of these hold true:
// 1. It is an Object.
// 2. It is not a @value, @set, or @list.
// 3. It has more than 1 key OR any existing key is not @id.
$rval = false;
if(is_object($v) &&
if (is_object($v) &&
!property_exists($v, '@value') &&
!property_exists($v, '@set') &&
!property_exists($v, '@list')) {
@ -5650,7 +5616,8 @@ class JsonLdProcessor {
*
* @return bool true if the value is a subject reference, false if not.
*/
protected static function _isSubjectReference($v) {
protected static function _isSubjectReference($v)
{
// Note: A value is a subject reference if all of these hold true:
// 1. It is an Object.
// 2. It has a single key: @id.
@ -5665,7 +5632,8 @@ class JsonLdProcessor {
*
* @return bool true if the value is a @value, false if not.
*/
protected static function _isValue($v) {
protected static function _isValue($v)
{
// Note: A value is a @value if all of these hold true:
// 1. It is an Object.
// 2. It has the @value property.
@ -5679,7 +5647,8 @@ class JsonLdProcessor {
*
* @return bool true if the value is a @list, false if not.
*/
protected static function _isList($v) {
protected static function _isList($v)
{
// Note: A value is a @list if all of these hold true:
// 1. It is an Object.
// 2. It has the @list property.
@ -5693,14 +5662,15 @@ class JsonLdProcessor {
*
* @return bool true if the value is a blank node, false if not.
*/
protected static function _isBlankNode($v) {
protected static function _isBlankNode($v)
{
// Note: A value is a blank node if all of these hold true:
// 1. It is an Object.
// 2. If it has an @id key its value begins with '_:'.
// 3. It has no keys OR is not a @value, @set, or @list.
$rval = false;
if(is_object($v)) {
if(property_exists($v, '@id')) {
if (is_object($v)) {
if (property_exists($v, '@id')) {
$rval = (strpos($v->{'@id'}, '_:') === 0);
} else {
$rval = (count(get_object_vars($v)) === 0 ||
@ -5719,7 +5689,8 @@ class JsonLdProcessor {
*
* @return bool true if the value is an absolute IRI, false if not.
*/
protected static function _isAbsoluteIri($v) {
protected static function _isAbsoluteIri($v)
{
return strpos($v, ':') !== false;
}
@ -5733,7 +5704,8 @@ class JsonLdProcessor {
*
* @return bool true if the target has the given key and its value matches.
*/
protected static function _hasKeyValue($target, $key, $value) {
protected static function _hasKeyValue($target, $key, $value)
{
return (property_exists($target, $key) && $target->{$key} === $value);
}
@ -5748,8 +5720,9 @@ class JsonLdProcessor {
* @return bool true if both objects have the same value for the key or
* neither has the key.
*/
protected static function _compareKeyValues($o1, $o2, $key) {
if(property_exists($o1, $key)) {
protected static function _compareKeyValues($o1, $o2, $key)
{
if (property_exists($o1, $key)) {
return property_exists($o2, $key) && $o1->{$key} === $o2->{$key};
}
return !property_exists($o2, $key);
@ -5762,94 +5735,103 @@ class JsonLdProcessor {
*
* @return mixed the parsed JSON object or array.
*/
protected static function _parse_json($json) {
protected static function _parse_json($json)
{
$rval = json_decode($json);
$error = json_last_error();
if($error === JSON_ERROR_NONE && $rval === null) {
if ($error === JSON_ERROR_NONE && $rval === null) {
$error = JSON_ERROR_SYNTAX;
}
switch($error) {
switch ($error) {
case JSON_ERROR_NONE:
break;
case JSON_ERROR_DEPTH:
throw new JsonLdException(
'Could not parse JSON; the maximum stack depth has been exceeded.',
'jsonld.ParseError');
'Could not parse JSON; the maximum stack depth has been exceeded.', 'jsonld.ParseError');
case JSON_ERROR_STATE_MISMATCH:
throw new JsonLdException(
'Could not parse JSON; invalid or malformed JSON.',
'jsonld.ParseError');
'Could not parse JSON; invalid or malformed JSON.', 'jsonld.ParseError');
case JSON_ERROR_CTRL_CHAR:
case JSON_ERROR_SYNTAX:
throw new JsonLdException(
'Could not parse JSON; syntax error, malformed JSON.',
'jsonld.ParseError');
'Could not parse JSON; syntax error, malformed JSON.', 'jsonld.ParseError');
case JSON_ERROR_UTF8:
throw new JsonLdException(
'Could not parse JSON from URL; malformed UTF-8 characters.',
'jsonld.ParseError');
'Could not parse JSON from URL; malformed UTF-8 characters.', 'jsonld.ParseError');
default:
throw new JsonLdException(
'Could not parse JSON from URL; unknown error.',
'jsonld.ParseError');
'Could not parse JSON from URL; unknown error.', 'jsonld.ParseError');
}
return $rval;
}
}
// register the N-Quads RDF parser
jsonld_register_rdf_parser(
'application/nquads', array('JsonLdProcessor', 'parseNQuads'));
'application/nquads', ['JsonLdProcessor', 'parseNQuads']);
/**
* A JSON-LD Exception.
*/
class JsonLdException extends Exception {
class JsonLdException extends Exception
{
public function __construct(
$msg, $type, $code='error', $details=null, $previous=null) {
$msg, $type, $code = 'error', $details = null, $previous = null)
{
$this->type = $type;
$this->code = $code;
$this->details = $details;
$this->cause = $previous;
parent::__construct($msg, 0, $previous);
}
public function __toString() {
public function __toString()
{
$rval = __CLASS__ . ": [{$this->type}]: {$this->message}\n";
if($this->code) {
if ($this->code) {
$rval .= 'Code: ' . $this->code . "\n";
}
if($this->details) {
if ($this->details) {
$rval .= 'Details: ' . print_r($this->details, true) . "\n";
}
if($this->cause) {
if ($this->cause) {
$rval .= 'Cause: ' . $this->cause;
}
$rval .= $this->getTraceAsString() . "\n";
return $rval;
}
};
}
;
/**
* A UniqueNamer issues unique names, keeping track of any previously issued
* names.
*/
class UniqueNamer {
class UniqueNamer
{
/**
* Constructs a new UniqueNamer.
*
* @param prefix the prefix to use ('<prefix><counter>').
*/
public function __construct($prefix) {
public function __construct($prefix)
{
$this->prefix = $prefix;
$this->counter = 0;
$this->existing = new stdClass();
$this->order = array();
$this->order = [];
}
/**
* Clones this UniqueNamer.
*/
public function __clone() {
public function __clone()
{
$this->existing = clone $this->existing;
}
@ -5861,9 +5843,10 @@ class UniqueNamer {
*
* @return string the new name.
*/
public function getName($old_name=null) {
public function getName($old_name = null)
{
// return existing old name
if($old_name && property_exists($this->existing, $old_name)) {
if ($old_name && property_exists($this->existing, $old_name)) {
return $this->existing->{$old_name};
}
@ -5872,7 +5855,7 @@ class UniqueNamer {
$this->counter += 1;
// save mapping
if($old_name !== null) {
if ($old_name !== null) {
$this->existing->{$old_name} = $name;
$this->order[] = $old_name;
}
@ -5887,22 +5870,27 @@ class UniqueNamer {
*
* @return true if the old name has been assigned a new name, false if not.
*/
public function isNamed($old_name) {
public function isNamed($old_name)
{
return property_exists($this->existing, $old_name);
}
}
/**
* A Permutator iterates over all possible permutations of the given array
* of elements.
*/
class Permutator {
class Permutator
{
/**
* Constructs a new Permutator.
*
* @param array $list the array of elements to iterate over.
*/
public function __construct($list) {
public function __construct($list)
{
// original array
$this->list = $list;
sort($this->list);
@ -5910,7 +5898,7 @@ class Permutator {
$this->done = false;
// directional info for permutation algorithm
$this->left = new stdClass();
foreach($list as $v) {
foreach ($list as $v) {
$this->left->{$v} = true;
}
}
@ -5920,7 +5908,8 @@ class Permutator {
*
* @return bool true if there is another permutation, false if not.
*/
public function hasNext() {
public function hasNext()
{
return !$this->done;
}
@ -5930,7 +5919,8 @@ class Permutator {
*
* @return array the next permutation.
*/
public function next() {
public function next()
{
// copy current permutation
$rval = $this->list;
@ -5942,10 +5932,10 @@ class Permutator {
$k = null;
$pos = 0;
$length = count($this->list);
for($i = 0; $i < $length; ++$i) {
for ($i = 0; $i < $length; ++$i) {
$element = $this->list[$i];
$left = $this->left->{$element};
if(($k === null || $element > $k) &&
if (($k === null || $element > $k) &&
(($left && $i > 0 && $element > $this->list[$i - 1]) ||
(!$left && $i < ($length - 1) && $element > $this->list[$i + 1]))) {
$k = $element;
@ -5954,7 +5944,7 @@ class Permutator {
}
// no more permutations
if($k === null) {
if ($k === null) {
$this->done = true;
} else {
// swap k and the element it is looking at
@ -5963,8 +5953,8 @@ class Permutator {
$this->list[$swap] = $k;
// reverse the direction of all elements larger than k
for($i = 0; $i < $length; ++$i) {
if($this->list[$i] > $k) {
for ($i = 0; $i < $length; ++$i) {
if ($this->list[$i] > $k) {
$this->left->{$this->list[$i]} = !$this->left->{$this->list[$i]};
}
}
@ -5972,20 +5962,23 @@ class Permutator {
return $rval;
}
}
/**
* An ActiveContextCache caches active contexts so they can be reused without
* the overhead of recomputing them.
*/
class ActiveContextCache {
class ActiveContextCache
{
/**
* Constructs a new ActiveContextCache.
*
* @param int size the maximum size of the cache, defaults to 100.
*/
public function __construct($size=100) {
$this->order = array();
public function __construct($size = 100)
{
$this->order = [];
$this->cache = new stdClass();
$this->size = $size;
}
@ -5999,15 +5992,17 @@ class ActiveContextCache {
*
* @return mixed a shared copy of the cached active context or null.
*/
public function get($active_ctx, $local_ctx) {
public function get($active_ctx, $local_ctx)
{
$key1 = serialize($active_ctx);
$key2 = serialize($local_ctx);
if(property_exists($this->cache, $key1)) {
if (property_exists($this->cache, $key1)) {
$level1 = $this->cache->{$key1};
if(property_exists($level1, $key2)) {
if (property_exists($level1, $key2)) {
return $level1->{$key2};
}
}
return null;
}
@ -6019,18 +6014,21 @@ class ActiveContextCache {
* @param stdClass $local_ctx the just-processed local context.
* @param stdClass $result the resulting active context.
*/
public function set($active_ctx, $local_ctx, $result) {
if(count($this->order) === $this->size) {
public function set($active_ctx, $local_ctx, $result)
{
if (count($this->order) === $this->size) {
$entry = array_shift($this->order);
unset($this->cache->{$entry->activeCtx}->{$entry->localCtx});
}
$key1 = serialize($active_ctx);
$key2 = serialize($local_ctx);
$this->order[] = (object)array(
'activeCtx' => $key1, 'localCtx' => $key2);
if(!property_exists($this->cache, $key1)) {
$this->order[] = (object) [
'activeCtx' => $key1, 'localCtx' => $key2];
if (!property_exists($this->cache, $key1)) {
$this->cache->{$key1} = new stdClass();
}
$this->cache->{$key1}->{$key2} = JsonLdProcessor::copy($result);
}
}

454
test.php
View file

@ -1,4 +1,5 @@
<?php
/**
* PHP unit tests for JSON-LD.
*
@ -8,14 +9,16 @@
*/
require_once('jsonld.php');
class JsonLdTestCase extends PHPUnit_Framework_TestCase {
class JsonLdTestCase extends PHPUnit_Framework_TestCase
{
/**
* Runs this test case. Overridden to attach to EARL report w/o need for
* an external XML configuration file.
*
* @param PHPUnit_Framework_TestResult $result the test result.
*/
public function run(PHPUnit_Framework_TestResult $result = NULL) {
public function run(PHPUnit_Framework_TestResult $result = NULL)
{
global $EARL;
$EARL->attach($result);
$this->result = $result;
@ -31,11 +34,12 @@ class JsonLdTestCase extends PHPUnit_Framework_TestCase {
* @group json-ld.org
* @dataProvider expandProvider
*/
public function testExpand($test) {
public function testExpand($test)
{
$this->test = $test;
$input = $test->readUrl('input');
$options = $test->createOptions();
$test->run('jsonld_expand', array($input, $options));
$test->run('jsonld_expand', [$input, $options]);
}
/**
@ -47,12 +51,13 @@ class JsonLdTestCase extends PHPUnit_Framework_TestCase {
* @group json-ld.org
* @dataProvider compactProvider
*/
public function testCompact($test) {
public function testCompact($test)
{
$this->test = $test;
$input = $test->readUrl('input');
$context = $test->readProperty('context');
$options = $test->createOptions();
$test->run('jsonld_compact', array($input, $context, $options));
$test->run('jsonld_compact', [$input, $context, $options]);
}
/**
@ -64,12 +69,13 @@ class JsonLdTestCase extends PHPUnit_Framework_TestCase {
* @group json-ld.org
* @dataProvider flattenProvider
*/
public function testFlatten($test) {
public function testFlatten($test)
{
$this->test = $test;
$input = $test->readUrl('input');
$context = $test->readProperty('context');
$options = $test->createOptions();
$test->run('jsonld_flatten', array($input, $context, $options));
$test->run('jsonld_flatten', [$input, $context, $options]);
}
/**
@ -81,11 +87,12 @@ class JsonLdTestCase extends PHPUnit_Framework_TestCase {
* @group json-ld.org
* @dataProvider toRdfProvider
*/
public function testToRdf($test) {
public function testToRdf($test)
{
$this->test = $test;
$input = $test->readUrl('input');
$options = $test->createOptions(array('format' => 'application/nquads'));
$test->run('jsonld_to_rdf', array($input, $options));
$options = $test->createOptions(['format' => 'application/nquads']);
$test->run('jsonld_to_rdf', [$input, $options]);
}
/**
@ -97,11 +104,12 @@ class JsonLdTestCase extends PHPUnit_Framework_TestCase {
* @group json-ld.org
* @dataProvider fromRdfProvider
*/
public function testFromRdf($test) {
public function testFromRdf($test)
{
$this->test = $test;
$input = $test->readProperty('input');
$options = $test->createOptions(array('format' => 'application/nquads'));
$test->run('jsonld_from_rdf', array($input, $options));
$options = $test->createOptions(['format' => 'application/nquads']);
$test->run('jsonld_from_rdf', [$input, $options]);
}
/**
@ -113,12 +121,13 @@ class JsonLdTestCase extends PHPUnit_Framework_TestCase {
* @group json-ld.org
* @dataProvider frameProvider
*/
public function testFrame($test) {
public function testFrame($test)
{
$this->test = $test;
$input = $test->readUrl('input');
$frame = $test->readProperty('frame');
$options = $test->createOptions();
$test->run('jsonld_frame', array($input, $frame, $options));
$test->run('jsonld_frame', [$input, $frame, $options]);
}
/**
@ -130,11 +139,12 @@ class JsonLdTestCase extends PHPUnit_Framework_TestCase {
* @group json-ld.org
* @dataProvider normalizeProvider
*/
public function testNormalize($test) {
public function testNormalize($test)
{
$this->test = $test;
$input = $test->readUrl('input');
$options = $test->createOptions(array('format' => 'application/nquads'));
$test->run('jsonld_normalize', array($input, $options));
$options = $test->createOptions(['format' => 'application/nquads']);
$test->run('jsonld_normalize', [$input, $options]);
}
/**
@ -146,14 +156,15 @@ class JsonLdTestCase extends PHPUnit_Framework_TestCase {
* @group normalization
* @dataProvider urgna2012Provider
*/
public function testUrgna2012($test) {
public function testUrgna2012($test)
{
$this->test = $test;
$input = $test->readProperty('action');
$options = $test->createOptions(array(
$options = $test->createOptions([
'algorithm' => 'URGNA2012',
'inputFormat' => 'application/nquads',
'format' => 'application/nquads'));
$test->run('jsonld_normalize', array($input, $options));
'format' => 'application/nquads']);
$test->run('jsonld_normalize', [$input, $options]);
}
/**
@ -165,78 +176,90 @@ class JsonLdTestCase extends PHPUnit_Framework_TestCase {
* @group normalization
* @dataProvider urdna2015Provider
*/
public function testUrdna2015($test) {
public function testUrdna2015($test)
{
$this->test = $test;
$input = $test->readProperty('action');
$options = $test->createOptions(array(
$options = $test->createOptions([
'algorithm' => 'URDNA2015',
'inputFormat' => 'application/nquads',
'format' => 'application/nquads'));
$test->run('jsonld_normalize', array($input, $options));
'format' => 'application/nquads']);
$test->run('jsonld_normalize', [$input, $options]);
}
public function expandProvider() {
public function expandProvider()
{
return new JsonLdTestIterator('jld:ExpandTest');
}
public function compactProvider() {
public function compactProvider()
{
return new JsonLdTestIterator('jld:CompactTest');
}
public function flattenProvider() {
public function flattenProvider()
{
return new JsonLdTestIterator('jld:FlattenTest');
}
public function toRdfProvider() {
public function toRdfProvider()
{
return new JsonLdTestIterator('jld:ToRDFTest');
}
public function fromRdfProvider() {
public function fromRdfProvider()
{
return new JsonLdTestIterator('jld:FromRDFTest');
}
public function normalizeProvider() {
public function normalizeProvider()
{
return new JsonLdTestIterator('jld:NormalizeTest');
}
public function frameProvider() {
public function frameProvider()
{
return new JsonLdTestIterator('jld:FrameTest');
}
public function urgna2012Provider() {
public function urgna2012Provider()
{
return new JsonLdTestIterator('rdfn:Urgna2012EvalTest');
}
public function urdna2015Provider() {
public function urdna2015Provider()
{
return new JsonLdTestIterator('rdfn:Urdna2015EvalTest');
}
}
class JsonLdManifest {
public function __construct($data, $filename) {
class JsonLdManifest
{
public function __construct($data, $filename)
{
$this->data = $data;
$this->filename = $filename;
$this->dirname = dirname($filename);
}
public function load(&$tests) {
public function load(&$tests)
{
$entries = array_merge(
JsonLdProcessor::getValues($this->data, 'sequence'),
JsonLdProcessor::getValues($this->data, 'entries'));
JsonLdProcessor::getValues($this->data, 'sequence'), JsonLdProcessor::getValues($this->data, 'entries'));
$includes = JsonLdProcessor::getValues($this->data, 'include');
foreach($includes as $include) {
foreach ($includes as $include) {
array_push($entries, $include . '.jsonld');
}
foreach($entries as $entry) {
if(is_string($entry)) {
foreach ($entries as $entry) {
if (is_string($entry)) {
$filename = join(
DIRECTORY_SEPARATOR, array($this->dirname, $entry));
DIRECTORY_SEPARATOR, [$this->dirname, $entry]);
$entry = Util::readJson($filename);
} else {
$filename = $this->filename;
}
if(JsonLdProcessor::hasValue($entry, '@type', 'mf:Manifest') ||
if (JsonLdProcessor::hasValue($entry, '@type', 'mf:Manifest') ||
JsonLdProcessor::hasValue($entry, 'type', 'mf:Manifest')) {
// entry is another manifest
$manifest = new JsonLdManifest($entry, $filename);
@ -245,11 +268,10 @@ class JsonLdManifest {
// assume entry is a test
$test = new JsonLdTest($this, $entry, $filename);
$types = array_merge(
JsonLdProcessor::getValues($test->data, '@type'),
JsonLdProcessor::getValues($test->data, 'type'));
foreach($types as $type) {
if(!isset($tests[$type])) {
$tests[$type] = array();
JsonLdProcessor::getValues($test->data, '@type'), JsonLdProcessor::getValues($test->data, 'type'));
foreach ($types as $type) {
if (!isset($tests[$type])) {
$tests[$type] = [];
}
$tests[$type][] = $test;
}
@ -258,39 +280,39 @@ class JsonLdManifest {
}
}
class JsonLdTest {
public function __construct($manifest, $data, $filename) {
class JsonLdTest
{
public function __construct($manifest, $data, $filename)
{
$this->manifest = $manifest;
$this->data = $data;
$this->filename = $filename;
$this->dirname = dirname($filename);
$this->isPositive =
JsonLdProcessor::hasValue(
$this->isPositive = JsonLdProcessor::hasValue(
$data, '@type', 'jld:PositiveEvaluationTest') ||
JsonLdProcessor::hasValue(
$data, 'type', 'jld:PositiveEvaluationTest');
$this->isNegative =
JsonLdProcessor::hasValue(
$this->isNegative = JsonLdProcessor::hasValue(
$data, '@type', 'jld:NegativeEvaluationTest') ||
JsonLdProcessor::hasValue(
$data, 'type', 'jld:NegativeEvaluationTest');
// generate test name
if(isset($manifest->data->name)) {
if (isset($manifest->data->name)) {
$manifestLabel = $manifest->data->name;
} else if(isset($manifest->data->label)) {
} else if (isset($manifest->data->label)) {
$manifestLabel = $manifest->data->label;
} else {
$manifestLabel = 'UNNAMED';
}
if(isset($this->data->id)) {
if (isset($this->data->id)) {
$testId = $this->data->id;
} else {
$testId = $this->data->{'@id'};
}
if(isset($this->data->name)) {
if (isset($this->data->name)) {
$testLabel = $this->data->name;
} else if(isset($this->data->label)) {
} else if (isset($this->data->label)) {
$testLabel = $this->data->label;
} else {
$testLabel = 'UNNAMED';
@ -299,26 +321,28 @@ class JsonLdTest {
$this->name = $manifestLabel . ' ' . $testId . ' - ' . $testLabel;
// expand @id and input base
if(isset($manifest->data->baseIri)) {
if (isset($manifest->data->baseIri)) {
$data->{'@id'} = ($manifest->data->baseIri .
basename($manifest->filename) . $data->{'@id'});
$this->base = $manifest->data->baseIri . $data->input;
}
}
private function _getResultProperty() {
if(isset($this->data->expect)) {
private function _getResultProperty()
{
if (isset($this->data->expect)) {
return 'expect';
} else if(isset($this->data->result)) {
} else if (isset($this->data->result)) {
return 'result';
} else {
throw new Exception('No test result property found.');
}
}
public function run($fn, $params) {
public function run($fn, $params)
{
// read expected data
if($this->isNegative) {
if ($this->isNegative) {
$this->expected = $this->data->expect;
} else {
$this->expected = $this->readProperty($this->_getResultProperty());
@ -326,13 +350,13 @@ class JsonLdTest {
try {
$this->actual = call_user_func_array($fn, $params);
if($this->isNegative) {
if ($this->isNegative) {
throw new Exception('Expected an error; one was not raised.');
}
PHPUnit_Framework_TestCase::assertEquals($this->expected, $this->actual);
} catch(Exception $e) {
} catch (Exception $e) {
// assume positive test
if($this->isNegative) {
if ($this->isNegative) {
$this->actual = $this->getJsonLdErrorCode($e);
PHPUnit_Framework_TestCase::assertEquals(
$this->expected, $this->actual);
@ -342,82 +366,93 @@ class JsonLdTest {
}
}
public function readUrl($property) {
if(!property_exists($this->data, $property)) {
public function readUrl($property)
{
if (!property_exists($this->data, $property)) {
return null;
}
return $this->manifest->data->baseIri . $this->data->{$property};
}
public function readProperty($property) {
public function readProperty($property)
{
$data = $this->data;
if(!property_exists($data, $property)) {
if (!property_exists($data, $property)) {
return null;
}
$filename = join(
DIRECTORY_SEPARATOR, array($this->dirname, $data->{$property}));
DIRECTORY_SEPARATOR, [$this->dirname, $data->{$property}]);
$extension = pathinfo($filename, PATHINFO_EXTENSION);
if($extension === 'jsonld') {
if ($extension === 'jsonld') {
return Util::readJson($filename);
}
return Util::readFile($filename);
}
public function createOptions($opts=array()) {
$http_options = array(
'contentType', 'httpLink', 'httpStatus', 'redirectTo');
public function createOptions($opts = [])
{
$http_options = [
'contentType', 'httpLink', 'httpStatus', 'redirectTo'];
$test_options = (property_exists($this->data, 'option') ?
$this->data->option : array());
$options = array();
foreach($test_options as $k => $v) {
if(!in_array($k, $http_options)) {
$this->data->option : []);
$options = [];
foreach ($test_options as $k => $v) {
if (!in_array($k, $http_options)) {
$options[$k] = $v;
}
}
$options['documentLoader'] = $this->createDocumentLoader();
$options = array_merge($options, $opts);
if(isset($options['expandContext'])) {
if (isset($options['expandContext'])) {
$filename = join(
DIRECTORY_SEPARATOR, array($this->dirname, $options['expandContext']));
DIRECTORY_SEPARATOR, [$this->dirname, $options['expandContext']]);
$options['expandContext'] = Util::readJson($filename);
}
return $options;
}
public function createDocumentLoader() {
public function createDocumentLoader()
{
$base = 'http://json-ld.org/test-suite';
$test = $this;
$load_locally = function($url) use ($test, $base) {
$doc = (object)array(
'contextUrl' => null, 'documentUrl' => $url, 'document' => null);
$doc = (object) [
'contextUrl' => null, 'documentUrl' => $url, 'document' => null];
$options = (property_exists($test->data, 'option') ?
$test->data->option : null);
if($options and $url === $test->base) {
if(property_exists($options, 'redirectTo') &&
if ($options and $url === $test->base) {
if (property_exists($options, 'redirectTo') &&
property_exists($options, 'httpStatus') &&
$options->httpStatus >= '300') {
$options->httpStatus >= '300'
) {
$doc->documentUrl = ($test->manifest->data->baseIri .
$options->redirectTo);
} else if(property_exists($options, 'httpLink')) {
} else if (property_exists($options, 'httpLink')) {
$content_type = (property_exists($options, 'contentType') ?
$options->contentType : null);
$extension = pathinfo($url, PATHINFO_EXTENSION);
if(!$content_type && $extension === 'jsonld') {
if (!$content_type && $extension === 'jsonld') {
$content_type = 'application/ld+json';
}
$link_header = $options->httpLink;
if(is_array($link_header)) {
if (is_array($link_header)) {
$link_header = join(',', $link_header);
}
$link_header = jsonld_parse_link_header($link_header);
if(isset($link_header['http://www.w3.org/ns/json-ld#context'])) {
if (isset($link_header['http://www.w3.org/ns/json-ld#context'])) {
$link_header = $link_header['http://www.w3.org/ns/json-ld#context'];
} else {
$link_header = null;
}
if($link_header && $content_type !== 'application/ld+json') {
if(is_array($link_header)) {
if ($link_header && $content_type !== 'application/ld+json') {
if (is_array($link_header)) {
throw new Exception('multiple context link headers');
}
$doc->contextUrl = $link_header->target;
@ -425,27 +460,29 @@ class JsonLdTest {
}
}
global $ROOT_MANIFEST_DIR;
if(strpos($doc->documentUrl, ':') === false) {
if (strpos($doc->documentUrl, ':') === false) {
$filename = join(
DIRECTORY_SEPARATOR, array(
$ROOT_MANIFEST_DIR, $doc->documentUrl));
DIRECTORY_SEPARATOR, [
$ROOT_MANIFEST_DIR, $doc->documentUrl]);
$doc->documentUrl = 'file://' . $filename;
} else {
$filename = join(
DIRECTORY_SEPARATOR, array(
$ROOT_MANIFEST_DIR, substr($doc->documentUrl, strlen($base))));
DIRECTORY_SEPARATOR, [
$ROOT_MANIFEST_DIR, substr($doc->documentUrl, strlen($base))]);
}
try {
$doc->document = Util::readJson($filename);
} catch(Exception $e) {
} catch (Exception $e) {
throw new Exception('loading document failed');
}
return $doc;
};
$local_loader = function($url) use ($test, $base, $load_locally) {
// always load remote-doc and non-base tests remotely
if((strpos($url, $base) !== 0 && strpos($url, ':') !== false) ||
if ((strpos($url, $base) !== 0 && strpos($url, ':') !== false) ||
$test->manifest->data->name === 'Remote document') {
return call_user_func('jsonld_default_document_loader', $url);
}
@ -457,20 +494,25 @@ class JsonLdTest {
return $local_loader;
}
public function getJsonLdErrorCode($err) {
if($err instanceof JsonLdException) {
if($err->getCode()) {
public function getJsonLdErrorCode($err)
{
if ($err instanceof JsonLdException) {
if ($err->getCode()) {
return $err->getCode();
}
if($err->cause) {
if ($err->cause) {
return $this->getJsonLdErrorCode($err->cause);
}
}
return $err->getMessage();
}
}
class JsonLdTestIterator implements Iterator {
class JsonLdTestIterator implements Iterator
{
/**
* The current test index.
*/
@ -486,13 +528,15 @@ class JsonLdTestIterator implements Iterator {
*
* @param string $type the type of tests to iterate over.
*/
public function __construct($type) {
public function __construct($type)
{
global $TESTS;
if(isset($TESTS[$type])) {
if (isset($TESTS[$type])) {
$this->tests = $TESTS[$type];
} else {
$this->tests = array();
$this->tests = [];
}
$this->count = count($this->tests);
}
@ -501,8 +545,9 @@ class JsonLdTestIterator implements Iterator {
*
* @return assoc the parameters for the next test.
*/
public function current() {
return array('test' => $this->tests[$this->index]);
public function current()
{
return ['test' => $this->tests[$this->index]];
}
/**
@ -510,21 +555,24 @@ class JsonLdTestIterator implements Iterator {
*
* @return int the current test number.
*/
public function key() {
public function key()
{
return $this->index;
}
/**
* Proceeds to the next test.
*/
public function next() {
public function next()
{
$this->index += 1;
}
/**
* Rewinds to the first test.
*/
public function rewind() {
public function rewind()
{
$this->index = 0;
}
@ -533,36 +581,38 @@ class JsonLdTestIterator implements Iterator {
*
* @return bool true if there are more tests to be run.
*/
public function valid() {
public function valid()
{
return $this->index < $this->count;
}
}
class EarlReport extends PHPUnit_Util_Printer
implements PHPUnit_Framework_TestListener {
public function __construct() {
class EarlReport extends PHPUnit_Util_Printer implements PHPUnit_Framework_TestListener
{
public function __construct()
{
$this->filename = null;
$this->attached = false;
$this->report = (object)array(
'@context' => (object)array(
$this->report = (object) [
'@context' => (object) [
'doap' => 'http://usefulinc.com/ns/doap#',
'foaf' => 'http://xmlns.com/foaf/0.1/',
'dc' => 'http://purl.org/dc/terms/',
'earl' => 'http://www.w3.org/ns/earl#',
'xsd' => 'http://www.w3.org/2001/XMLSchema#',
'doap:homepage' => (object)array('@type' => '@id'),
'doap:license' => (object)array('@type' => '@id'),
'dc:creator' => (object)array('@type' => '@id'),
'foaf:homepage' => (object)array('@type' => '@id'),
'subjectOf' => (object)array('@reverse' => 'earl:subject'),
'earl:assertedBy' => (object)array('@type' => '@id'),
'earl:mode' => (object)array('@type' => '@id'),
'earl:test' => (object)array('@type' => '@id'),
'earl:outcome' => (object)array('@type' => '@id'),
'dc:date' => (object)array('@type' => 'xsd:date')
),
'doap:homepage' => (object) ['@type' => '@id'],
'doap:license' => (object) ['@type' => '@id'],
'dc:creator' => (object) ['@type' => '@id'],
'foaf:homepage' => (object) ['@type' => '@id'],
'subjectOf' => (object) ['@reverse' => 'earl:subject'],
'earl:assertedBy' => (object) ['@type' => '@id'],
'earl:mode' => (object) ['@type' => '@id'],
'earl:test' => (object) ['@type' => '@id'],
'earl:outcome' => (object) ['@type' => '@id'],
'dc:date' => (object) ['@type' => 'xsd:date']
],
'@id' => 'https://github.com/digitalbazaar/php-json-ld',
'@type' => array('doap:Project', 'earl:TestSubject', 'earl:Software'),
'@type' => ['doap:Project', 'earl:TestSubject', 'earl:Software'],
'doap:name' => 'php-json-ld',
'dc:title' => 'php-json-ld',
'doap:homepage' => 'https://github.com/digitalbazaar/php-json-ld',
@ -570,18 +620,18 @@ class EarlReport extends PHPUnit_Util_Printer
'doap:description' => 'A JSON-LD processor for PHP',
'doap:programming-language' => 'PHP',
'dc:creator' => 'https://github.com/dlongley',
'doap:developer' => (object)array(
'doap:developer' => (object) [
'@id' => 'https://github.com/dlongley',
'@type' => array('foaf:Person', 'earl:Assertor'),
'@type' => ['foaf:Person', 'earl:Assertor'],
'foaf:name' => 'Dave Longley',
'foaf:homepage' => 'https://github.com/dlongley'
),
'dc:date' => array(
],
'dc:date' => [
'@value' => gmdate('Y-m-d'),
'@type' => 'xsd:date'
),
'subjectOf' => array()
);
],
'subjectOf' => []
];
}
/**
@ -589,8 +639,9 @@ class EarlReport extends PHPUnit_Util_Printer
*
* @param PHPUnit_Framework_Test $result the result to attach to.
*/
public function attach(PHPUnit_Framework_TestResult $result) {
if(!$this->attached && $this->filename) {
public function attach(PHPUnit_Framework_TestResult $result)
{
if (!$this->attached && $this->filename) {
$this->attached = true;
$result->addListener($this);
}
@ -602,26 +653,29 @@ class EarlReport extends PHPUnit_Util_Printer
* @param JsonLdTest $test the JsonLdTest for the assertion is for.
* @param bool $passed whether or not the test passed.
*/
public function addAssertion($test, $passed) {
$this->report->{'subjectOf'}[] = (object)array(
public function addAssertion($test, $passed)
{
$this->report->{'subjectOf'}[] = (object) [
'@type' => 'earl:Assertion',
'earl:assertedBy' => $this->report->{'doap:developer'}->{'@id'},
'earl:mode' => 'earl:automatic',
'earl:test' => $test->data->{'@id'},
'earl:result' => (object)array(
'earl:result' => (object) [
'@type' => 'earl:TestResult',
'dc:date' => gmdate(DateTime::ISO8601),
'earl:outcome' => $passed ? 'earl:passed' : 'earl:failed'
)
);
]
];
return $this;
}
/**
* Writes this EARL report to a file.
*/
public function flush() {
if($this->filename) {
public function flush()
{
if ($this->filename) {
printf("\nWriting EARL report to: %s\n", $this->filename);
$fd = fopen($this->filename, 'w');
fwrite($fd, Util::jsonldEncode($this->report));
@ -629,23 +683,25 @@ class EarlReport extends PHPUnit_Util_Printer
}
}
public function endTest(PHPUnit_Framework_Test $test, $time) {
public function endTest(PHPUnit_Framework_Test $test, $time)
{
$this->addAssertion($test->test, true);
}
public function addError(
PHPUnit_Framework_Test $test, Exception $e, $time) {
PHPUnit_Framework_Test $test, Exception $e, $time)
{
$this->addAssertion($test->test, false);
}
public function addFailure(
PHPUnit_Framework_Test $test,
PHPUnit_Framework_AssertionFailedError $e, $time) {
PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
{
$this->addAssertion($test->test, false);
if($test->result->shouldStop()) {
if(isset($test->test->name)) {
if ($test->result->shouldStop()) {
if (isset($test->test->name)) {
$name = $test->test->name;
} else if(isset($test->test->label)) {
} else if (isset($test->test->label)) {
$name = $test->test->label;
} else {
$name = 'UNNAMED';
@ -659,45 +715,72 @@ class EarlReport extends PHPUnit_Util_Printer
}
public function addIncompleteTest(
PHPUnit_Framework_Test $test, Exception $e, $time) {
PHPUnit_Framework_Test $test, Exception $e, $time)
{
$this->addAssertion($test->test, false);
}
public function addRiskyTest(
PHPUnit_Framework_Test $test, Exception $e, $time) {}
PHPUnit_Framework_Test $test, Exception $e, $time)
{
}
public function addSkippedTest(
PHPUnit_Framework_Test $test, Exception $e, $time) {}
public function startTest(PHPUnit_Framework_Test $test) {}
public function startTestSuite(PHPUnit_Framework_TestSuite $suite) {}
public function endTestSuite(PHPUnit_Framework_TestSuite $suite) {}
PHPUnit_Framework_Test $test, Exception $e, $time)
{
}
public function startTest(PHPUnit_Framework_Test $test)
{
}
public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
{
}
public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
{
}
}
class Util {
public static function readFile($filename) {
class Util
{
public static function readFile($filename)
{
$rval = @file_get_contents($filename);
if($rval === false) {
if ($rval === false) {
throw new Exception('File read error: ' . $filename);
}
return $rval;
}
public static function readJson($filename) {
public static function readJson($filename)
{
$rval = json_decode(self::readFile($filename));
if($rval === null) {
if ($rval === null) {
throw new Exception('JSON parse error');
}
return $rval;
}
public static function readNQuads($filename) {
public static function readNQuads($filename)
{
return self::readFile($filename);
}
public static function jsonldEncode($input) {
public static function jsonldEncode($input)
{
// newer PHP has a flag to avoid escaped '/'
if(defined('JSON_UNESCAPED_SLASHES')) {
if (defined('JSON_UNESCAPED_SLASHES')) {
$options = JSON_UNESCAPED_SLASHES;
if(defined('JSON_PRETTY_PRINT')) {
if (defined('JSON_PRETTY_PRINT')) {
$options |= JSON_PRETTY_PRINT;
}
$json = json_encode($input, $options);
@ -705,59 +788,62 @@ class Util {
// use a simple string replacement of '\/' to '/'.
$json = str_replace('\\/', '/', json_encode($input));
}
return $json;
}
}
// tests to skip
$SKIP_TESTS = array();
$SKIP_TESTS = [];
// root manifest directory
$ROOT_MANIFEST_DIR;
// parsed tests; keyed by type
$TESTS = array();
$TESTS = [];
// parsed command line options
$OPTIONS = array();
$OPTIONS = [];
// parse command line options
global $argv;
$args = $argv;
$total = count($args);
$start = false;
for($i = 0; $i < $total; ++$i) {
for ($i = 0; $i < $total; ++$i) {
$arg = $args[$i];
if(!$start) {
if(realpath($arg) === realpath(__FILE__)) {
if (!$start) {
if (realpath($arg) === realpath(__FILE__)) {
$start = true;
}
continue;
}
if($arg[0] !== '-') {
if ($arg[0] !== '-') {
break;
}
$i += 1;
$OPTIONS[$arg] = $args[$i];
}
if(!isset($OPTIONS['-d'])) {
if (!isset($OPTIONS['-d'])) {
$dvar = 'path to json-ld.org/test-suite';
$evar = 'file to write EARL report to';
echo "php-json-ld Tests\n";
echo "Usage: phpunit test.php -d <$dvar> [-e <$evar>]\n\n";
exit(0);
}
// EARL Report
$EARL = new EarlReport();
if(isset($OPTIONS['-e'])) {
if (isset($OPTIONS['-e'])) {
$EARL->filename = $OPTIONS['-e'];
}
// load root manifest
$ROOT_MANIFEST_DIR = realpath($OPTIONS['-d']);
$filename = join(
DIRECTORY_SEPARATOR, array($ROOT_MANIFEST_DIR, 'manifest.jsonld'));
DIRECTORY_SEPARATOR, [$ROOT_MANIFEST_DIR, 'manifest.jsonld']);
$root_manifest = Util::readJson($filename);
$manifest = new JsonLdManifest($root_manifest, $filename);
$manifest->load($TESTS);