socialweb / rdf
An RDF 1.1 library with N-Quads and RDF Dataset Canonicalization (RDFC-1.0)
Requires
- php: ^8.5
Requires (Dev)
- captainhook/plugin-composer: ^5.3
- ergebnis/composer-normalize: ^2.53
- fakerphp/faker: ^1.24
- infection/infection: ^0.35
- phpstan/extension-installer: ^1.4
- phpstan/phpstan: ^2.2
- phpstan/phpstan-phpunit: ^2.0
- phpunit/phpunit: ^13.3
- ramsey/coding-standard: ^2.4
- ramsey/composer-repl: ^1.5
- ramsey/conventional-commits: ^1.7
- roave/security-advisories: dev-latest
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
About
socialweb/rdf is a PHP library for the RDF 1.1 data model. It provides:
- a core model of terms (
Iri,BlankNode,Literal), quads, and datasets, - an N-Quads 1.1 parser and a serializer that writes the canonical form of N-Quads, and
- RDF Dataset Canonicalization (RDFC-1.0), which turns any dataset into a stable byte sequence that can be hashed and signed.
The library implements these three specifications faithfully. It has no package
dependencies, never makes network requests, does bounded work on untrusted
input, and fails explicitly rather than repairing malformed data. Terms and
quads are immutable value objects; Dataset is the one mutable type.
Not included, by design: other syntaxes such as Turtle, pattern matching, literal value conversion, IRI normalization, and the RDF 1.2 additions.
This project adheres to a code of conduct. By participating in this project and its community, you are expected to uphold this code.
Installation
Install this package as a dependency using Composer.
composer require socialweb/rdf
Usage
Canonicalizing an N-Quads document
Parse the document into a dataset, canonicalize it, and hash the canonical N-Quads. Two documents that describe the same graph, with different blank node labels or statement order, produce the same bytes.
use SocialWeb\Rdf\Canonicalization\Canonicalizer; use SocialWeb\Rdf\NQuads\Parser; $dataset = (new Parser())->parse($nquadsText); $result = (new Canonicalizer())->canonicalize($dataset); $canonicalNQuads = $result->toNQuads(); $hash = hash('sha256', $canonicalNQuads);
Parser::parse() raises SocialWeb\Rdf\Exception\MalformedNQuads, which
carries the line and column of the first problem, if the document does not
follow the N-Quads grammar.
Canonicalizing a dataset produced by another library
Any code that can enumerate statements can build a Dataset. Map each
statement's components to this library's terms: Iri for IRIs, BlankNode
for blank nodes, and Literal for literals, with DefaultGraph as the graph
name of statements that belong to no named graph.
use SocialWeb\Rdf\BlankNode; use SocialWeb\Rdf\Canonicalization\Canonicalizer; use SocialWeb\Rdf\Dataset; use SocialWeb\Rdf\Iri; use SocialWeb\Rdf\Literal; use SocialWeb\Rdf\Quad; $dataset = new Dataset(); foreach ($statements as $statement) { $dataset->add(new Quad( $statement->subjectIsBlank ? new BlankNode($statement->subject) : new Iri($statement->subject), new Iri($statement->predicate), $statement->objectIsLiteral ? new Literal($statement->object, language: $statement->language) : new Iri($statement->object), )); } $result = (new Canonicalizer())->canonicalize($dataset); $result->toNQuads(); // the canonical N-Quads document $result->dataset(); // a Dataset with canonical blank node labels $result->issuedIdentifiers(); // each input blank node label to its canonical label
Building a dataset by hand
use SocialWeb\Rdf\BlankNode; use SocialWeb\Rdf\Dataset; use SocialWeb\Rdf\Iri; use SocialWeb\Rdf\Literal; use SocialWeb\Rdf\NQuads\Serializer; use SocialWeb\Rdf\Quad; $alice = new BlankNode('alice'); $name = new Iri('http://xmlns.com/foaf/0.1/name'); $knows = new Iri('http://xmlns.com/foaf/0.1/knows'); $dataset = new Dataset([ new Quad($alice, $name, new Literal('Alice', language: 'en')), new Quad($alice, $knows, new BlankNode('bob')), new Quad(new BlankNode('bob'), $name, new Literal('Bob')), ]); echo (new Serializer())->serialize($dataset);
This prints:
_:alice <http://xmlns.com/foaf/0.1/name> "Alice"@en .
_:alice <http://xmlns.com/foaf/0.1/knows> _:bob .
_:bob <http://xmlns.com/foaf/0.1/name> "Bob" .
Limiting the work canonicalization may do
RDFC-1.0 can take a very long time on datasets built to defeat it, so the
canonicalizer bounds the number of deep comparisons it makes and raises
SocialWeb\Rdf\Exception\IterationLimitExceeded when the bound is reached.
The bound is set by the constructor's work factor, following the reference
implementation: the number of blank nodes whose first-degree hashes are not
unique, raised to the power of the work factor.
use SocialWeb\Rdf\Canonicalization\Canonicalizer; new Canonicalizer(); // SHA-256, work factor 1 new Canonicalizer('sha384'); // SHA-384, work factor 1 new Canonicalizer(workFactor: 2); // allows the square of that count new Canonicalizer(maxDeepIterations: 1000); // an explicit limit instead
The default work factor of 1 handles typical data, including trees of blank nodes and blank nodes that can be told apart by their immediate neighbors. Datasets in which several blank nodes are only distinguishable through longer chains of other blank nodes, such as cycles, need a work factor of 2 or more. The W3C test suite runs its medium-complexity cases with a work factor of 2 and its high-complexity cases with 3.
Contributing
Contributions are welcome! To contribute, please familiarize yourself with CONTRIBUTING.md.
Coordinated Disclosure
Keeping user information safe and secure is a top priority, and we welcome the contribution of external security researchers. If you believe you've found a security issue in software that is maintained in this repository, please read SECURITY.md for instructions on submitting a vulnerability report.
Copyright and License
socialweb/rdf is copyright © socialweb/rdf Contributors and licensed for use under the terms of the GNU Lesser General Public License (LGPL-3.0-or-later) as published by the Free Software Foundation. Please see COPYING.LESSER, COPYING, and NOTICE for more information.