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": { "require": {
"php": ">=5.3.0", "php": ">=5.4.0",
"ext-json": "*" "ext-json": "*"
}, },
"autoload": { "autoload": {

2676
jsonld.php
View file

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

454
test.php
View file

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