/** * Transforms a statement array given by EasyRdf to a Saft StatementIterator instance. * * @param array $rdfPhp * @return StatementIterator */ protected function rdfPhpToStatementIterator(array $rdfPhp) { $statements = array(); // go through all subjects foreach ($rdfPhp as $subject => $predicates) { // predicates associated with the subject foreach ($predicates as $property => $objects) { // object(s) foreach ($objects as $object) { /** * Create subject node */ if (true === NodeUtils::simpleCheckURI($subject)) { $s = $this->nodeFactory->createNamedNode($subject); } else { $s = $this->nodeFactory->createLiteral($subject); } /** * Create predicate node */ if (true === NodeUtils::simpleCheckURI($property)) { $p = $this->nodeFactory->createNamedNode($property); } else { $p = $this->nodeFactory->createLiteral($property); } /* * Create object node */ // URI if (NodeUtils::simpleCheckURI($object['value'])) { $o = $this->nodeFactory->createNamedNode($object['value']); // datatype set } elseif (isset($object['datatype'])) { $o = $this->nodeFactory->createLiteral($object['value'], $object['datatype']); // lang set } elseif (isset($object['lang'])) { $o = $this->nodeFactory->createLiteral($object['value'], 'http://www.w3.org/1999/02/22-rdf-syntax-ns#langString', $object['lang']); } else { $o = $this->nodeFactory->createLiteral($object['value']); } // build and add statement $statements[] = $this->statementFactory->createStatement($s, $p, $o); } } } return new ArrayStatementIteratorImpl($statements); }
/** * It basically returns all stored statements. * * @param Statement $Statement It can be either a concrete or pattern-statement. * @param Node $graph optional Overrides target graph. If set, you will get all * matching statements of that graph. * @param array $options optional It contains key-value pairs and should provide additional * introductions for the store and/or its adapter(s). * @return StatementIterator It contains Statement instances of all matching statements of the given * graph. */ public function getMatchingStatements(Statement $statement, Node $graph = null, array $options = array()) { if (null !== $graph) { $graphUri = $graph->getUri(); // no graph information given, use default graph } elseif (null === $graph && null === $statement->getGraph()) { $graphUri = 'http://saft/defaultGraph/'; // no graph given, use graph information from $statement } elseif (null === $graph && $statement->getGraph()->isNamed()) { $graphUri = $statement->getGraph()->getUri(); // no graph given, use graph information from $statement } elseif (null === $graph && false == $statement->getGraph()->isNamed()) { $graphUri = 'http://saft/defaultGraph/'; } if (false == isset($this->statements[$graphUri])) { $this->statements[$graphUri] = array(); } // if not default graph was requested if ('http://saft/defaultGraph/' != $graphUri) { return new ArrayStatementIteratorImpl($this->statements[$graphUri]); // if default graph was requested, return matching statements from all graphs } else { $_statements = array(); foreach ($this->statements as $graphUri => $statements) { foreach ($statements as $statement) { if ('http://saft/defaultGraph/' == $graphUri) { $graph = null; } else { $graph = $this->nodeFactory->createNamedNode($graphUri); } $_statements[] = $this->statementFactory->createStatement($statement->getSubject(), $statement->getPredicate(), $statement->getObject(), $graph); } } return new ArrayStatementIteratorImpl($_statements); } }
/** * Creates an instance of Node by given $value and $type. * * @param mixed $value * @param string $type * @return Node Instance of Node interface. * @throws \Exception */ protected function createNodeByValueAndType($value, $type) { /** * URI */ if ('uri' == $type) { return $this->nodeFactory->createNamedNode($value); /** * Any Pattern */ } elseif ('var' == $type) { return $this->nodeFactory->createAnyPattern(); /** * Typed Literal or Literal */ } elseif ('typed-literal' == $type || 'literal' == $type) { return $this->nodeFactory->createLiteral($value); /** * Unknown type */ } else { throw new \Exception('Unknown type given: ' . $type); } }
/** * This method sends a SPARQL query to the store. * * @param string $query The SPARQL query to send to the store. * @param array $options optional It contains key-value pairs and should provide additional * introductions for the store and/or its adapter(s). * @return Result Returns result of the query. Its type depends on the type of the query. * @throws \Exception If query is no string. * @throws \Exception If query is malformed. * @throws \Exception If query is a DELETE query and contains quads, where the graph of one quad is of type var * @throws \Exception If a non-graph query contains no triples and quads. * @todo handle multiple graphs in FROM clause */ public function query($query, array $options = array()) { $queryObject = $this->queryFactory->createInstanceByQueryString($query); $queryParts = $queryObject->getQueryParts(); // if a non-graph query was given, we assume triples or quads. If neither quads nor triples were found, // throw an exception. if (false === $queryObject->isGraphQuery() && false === isset($queryParts['triple_pattern']) && false === isset($queryParts['quad_pattern'])) { throw new \Exception('Non-graph queries must have triples or quads.'); } // execute query on the store $result = $this->store->query($query); /* * special case: if you execute a SELECT COUNT(*) query, ARC2 will return the number of triples instead of a result set */ $countCheck = preg_match('/selectcount\\([a-z*]\\)(from|where)/si', preg_replace('/\\s+/', '', $query)); if (1 == $countCheck) { $variable = 'callret-0'; // build a set result, because the user expects it as result type because a SELECT query // was sent. $setResult = $this->resultFactory->createSetResult(array(array($variable => $this->nodeFactory->createLiteral($result, 'http://www.w3.org/2001/XMLSchema#int')))); $setResult->setVariables(array($variable)); return $setResult; /* * ARC2 does not support quads, especially not in DELETE queries. The following code construct * tries to close that gap by transforming the query in a one which ARC2 can understand. * * This part transform queries of the kind: * * DELETE WHERE { * Graph <http://localhost/Saft/TestGraph/> { * ?s ?p ?o . * } * } * * to SPARQL+ ones: * * DELETE FROM <http://localhost/Saft/TestGraph/> { * ?s ?p ?o . * } * WHERE { * ?s ?p ?o . * } * * * IMPORTANT: Please adapt * https://github.com/SaftIng/safting.github.io/blob/master/doc/phpframework/addition/ARC2.md * if you change the support for SPARQL 1.0/1.1 here! */ } elseif ($queryObject->isUpdateQuery() && isset($queryParts['quad_pattern']) && 'deleteWhere' === $queryParts['sub_type']) { foreach ($queryParts['quad_pattern'] as $quad) { if ('uri' != $quad['g_type']) { throw new \Exception('The graph of a quad must be an URI here.'); } // subject $s = NodeUtils::createNodeInstance($this->nodeFactory, $quad['s'], $quad['s_type']); $s = SparqlUtils::getNodeInSparqlFormat($s); // predicate $p = NodeUtils::createNodeInstance($this->nodeFactory, $quad['p'], $quad['p_type']); $p = SparqlUtils::getNodeInSparqlFormat($p); // object $o = NodeUtils::createNodeInstance($this->nodeFactory, $quad['o'], $quad['o_type'], $quad['o_datatype'], $quad['o_lang']); $o = SparqlUtils::getNodeInSparqlFormat($o); return $this->query('DELETE FROM <' . $quad['g'] . '> {' . $s . ' ' . $p . ' ' . $o . ' } WHERE {' . $s . ' ' . $p . ' ' . $o . ' }'); } /* * SELECT query */ } elseif ('selectQuery' === AbstractQuery::getQueryType($query)) { /* * For a SELECT query the result looks like: * * array( * 'query_time' => 0.2 * 'query_type' => 'select', * 'result' => array( * 'variables' => array('s', 'o') * 'rows' => * array( * 's' => "http://s/" * 's type' => 'uri', * 'o' => '42', * 'o type' => "literal" * 'o datatype' => "http://www.w3.org/2001/XMLSchema#string" * ) */ $entries = array(); // go through all rows foreach ($result['result']['rows'] as $row) { $newEntry = array(); foreach ($result['result']['variables'] as $variable) { // checks for variable type // example: $row['s type'] switch ($row[$variable . ' type']) { // ARC2 does not differenciate between typed literal and literal, like Virtuoso does // for instance. You have to check for lang and datatype key by yourself. case 'literal': // if language is set if (isset($row[$variable . ' lang'])) { $newEntry[$variable] = $this->nodeFactory->createLiteral($row[$variable], 'http://www.w3.org/1999/02/22-rdf-syntax-ns#langString', $row[$variable . ' lang']); // if datatype is set } elseif (isset($row[$variable . ' datatype'])) { $newEntry[$variable] = $this->nodeFactory->createLiteral($row[$variable], $row[$variable . ' datatype']); // if neither one is set, we assume its a string and use xsd:string as datatype } else { $newEntry[$variable] = $this->nodeFactory->createLiteral($row[$variable], 'http://www.w3.org/2001/XMLSchema#string'); } break; case 'uri': $newEntry[$variable] = $this->nodeFactory->createNamedNode($row[$variable]); break; } } $entries[] = $newEntry; } // Create and fill SetResult instance $setResult = $this->resultFactory->createSetResult($entries); $setResult->setVariables($result['result']['variables']); return $setResult; } else { if ('askQuery' === AbstractQuery::getQueryType($query)) { return $this->resultFactory->createValueResult($result['result']); } else { return $this->resultFactory->createEmptyResult(); } } }
/** * This method sends a SPARQL query to the store. * * @param string $query The SPARQL query to send to the store. * @param array $options optional It contains key-value pairs and should provide additional * introductions for the store and/or its adapter(s). * @return Result Returns result of the query. Its type depends on the type of the query. * @throws \Exception If query is no string. * @throws \Exception If query is malformed. * @throws \Exception If PDO query is false. * @todo handle multiple graphs in FROM clause */ public function query($query, array $options = array()) { $queryObject = $this->queryFactory->createInstanceByQueryString($query); $queryParts = $queryObject->getQueryParts(); // if a non-graph query was given, we assume triples or quads. If neither quads nor triples were found, // throw an exception. if (false === $queryObject->isGraphQuery() && false === isset($queryParts['triple_pattern']) && false === isset($queryParts['quad_pattern'])) { throw new \Exception('Non-graph queries must have triples or quads.'); } /** * SPARQL query (usually to fetch data) */ if ('selectQuery' === AbstractQuery::getQueryType($query)) { // force extended result to have detailed information about given result entries, such as datatype and // language information. $sparqlQuery = 'define output:format "JSON"' . PHP_EOL . $query; // escape characters that delimit the query within the query using addcslashes $graphUri = 'NULL'; $graphSpec = ''; // escape characters that delimit the query within the query $sparqlQuery = $graphSpec . 'CALL DB.DBA.SPARQL_EVAL(\'' . addcslashes($sparqlQuery, '\'\\') . '\', ' . '\'' . $graphUri . '\', 0)'; // execute query try { $pdoQuery = $this->connection->prepare($sparqlQuery, array(\PDO::ATTR_CURSOR => \PDO::CURSOR_FWDONLY)); $pdoQuery->execute(); } catch (\PDOException $e) { throw new \Exception($e->getMessage()); } $entries = array(); // transform result to array in case we fired a non-UPDATE query if (false !== $pdoQuery) { $resultArray = json_decode(current(current($pdoQuery->fetchAll(\PDO::FETCH_ASSOC))), true); $variables = $resultArray['head']['vars']; // in case the result was empty, Virtuoso does not return a list of variables, which are // usually located in the SELECT part. so we try to extract the variables by ourselves. if (0 == count($variables)) { $variables = $queryParts['variables']; } /** * go through all bindings and create according objects for SetResult instance. * * $bindingParts will look like: * * array( * 's' => array( * 'type' => 'uri', * 'value' => '...' * ), ... * ) */ foreach ($resultArray['results']['bindings'] as $bindingParts) { $newEntry = array(); /** * A part looks like: * array( * 'type' => 'uri', * 'value' => '...' * ) */ foreach ($bindingParts as $variable => $part) { switch ($part['type']) { /** * Literal (language'd) */ case 'literal': $newEntry[$variable] = $this->nodeFactory->createLiteral($part['value'], 'http://www.w3.org/1999/02/22-rdf-syntax-ns#langString', $part['xml:lang']); break; /** * Typed-Literal */ /** * Typed-Literal */ case 'typed-literal': $newEntry[$variable] = $this->nodeFactory->createLiteral($part['value'], $part['datatype']); break; /** * NamedNode */ /** * NamedNode */ case 'uri': $newEntry[$variable] = $this->nodeFactory->createNamedNode($part['value']); break; default: throw new \Exception('Unknown type given.'); break; } } $entries[] = $newEntry; } $setResult = $this->resultFactory->createSetResult(new \ArrayIterator($entries)); $setResult->setVariables($variables); return $setResult; } else { throw new \Exception('PDO query is false.'); } /** * SPARPQL Update query */ } else { $sparqlQuery = 'SPARQL ' . $query; // execute query try { $pdoQuery = $this->connection->prepare($sparqlQuery, array(\PDO::ATTR_CURSOR => \PDO::CURSOR_FWDONLY)); $pdoQuery->execute(); } catch (\PDOException $e) { throw new \Exception($e->getMessage()); } // ask result if ('askQuery' === AbstractQuery::getQueryType($query)) { $pdoResult = $pdoQuery->fetchAll(\PDO::FETCH_ASSOC); return $this->resultFactory->createValueResult(true !== empty($pdoResult)); } else { return $this->resultFactory->createEmptyResult(); } } }
/** * This method sends a SPARQL query to the store. * * @param string $query The SPARQL query to send to the store. * @param array $options optional It contains key-value pairs and should provide additional * introductions for the store and/or its adapter(s). * @return Result Returns result of the query. Its type depends on the type of the query. * @throws \Exception If query is no string. * @throws \Exception If query is malformed. * @throws StoreException If server returned an error. * @todo add support for DESCRIBE queries */ public function query($query, array $options = array()) { $queryObject = $this->queryFactory->createInstanceByQueryString($query); $queryParts = $queryObject->getQueryParts(); /** * SPARQL query (usually to fetch data) */ if ('selectQuery' == AbstractQuery::getQueryType($query)) { $resultArray = json_decode($this->client->sendSparqlSelectQuery($query), true); $entries = array(); /** * go through all bindings and create according objects for SetResult instance. * * $bindingParts will look like: * * array( * 's' => array( * 'type' => 'uri', * 'value' => '...' * ), ... * ) */ foreach ($resultArray['results']['bindings'] as $bindingParts) { $newEntry = array(); /** * A part looks like: * array( * 'type' => 'uri', * 'value' => '...' * ) */ foreach ($bindingParts as $variable => $part) { switch ($part['type']) { /** * Literal (language'd) */ case 'literal': $lang = null; if (isset($part['xml:lang'])) { $lang = $part['xml:lang']; } $newEntry[$variable] = $this->nodeFactory->createLiteral($part['value'], 'http://www.w3.org/1999/02/22-rdf-syntax-ns#langString', $lang); break; /** * Typed-Literal */ /** * Typed-Literal */ case 'typed-literal': $newEntry[$variable] = $this->nodeFactory->createLiteral($part['value'], $part['datatype']); break; /** * NamedNode */ /** * NamedNode */ case 'uri': $newEntry[$variable] = $this->nodeFactory->createNamedNode($part['value']); break; /** * BlankNode */ /** * BlankNode */ case 'bnode': $newEntry[$variable] = $this->nodeFactory->createBlankNode($part['value']); break; default: throw new \Exception('Unknown type given.'); break; } } $entries[] = $newEntry; } $return = $this->resultFactory->createSetResult($entries); $return->setVariables($resultArray['head']['vars']); /** * SPARPQL Update query */ } else { $result = $this->client->sendSparqlUpdateQuery($query); $decodedResult = json_decode($result, true); if ('askQuery' === AbstractQuery::getQueryType($query)) { $askResult = json_decode($result, true); if (true === isset($askResult['boolean'])) { $return = $this->resultFactory->createValueResult($askResult['boolean']); // assumption here is, if a string was returned, something went wrong. } elseif (0 < strlen($result)) { throw new \Exception($result); } else { $return = $this->resultFactory->createEmptyResult(); } // usually a SPARQL result does not return a string. if it does anyway, assume there is an error. } elseif (null === $decodedResult && 0 < strlen($result)) { throw new \Exception($result); } else { $return = $this->resultFactory->createEmptyResult(); } } return $return; }