hardf est une bibliothèque PHP 7.1+ qui vous permet de gérer les données liées (RDF). Il propose :
L'analyseur et le sérialiseur prennent en charge le streaming .
Cette bibliothèque est un portage de N3.js vers PHP
Nous utilisons la triple représentation en PHP portée de la bibliothèque NodeJS N3.js. Vérifiez https://github.com/rdfjs/N3.js/tree/v0.10.0#triple-representation pour plus d'informations
Nous nous sommes volontairement concentrés sur les performances et non sur la convivialité pour les développeurs. Nous avons ainsi implémenté cette triple représentation en utilisant des tableaux associatifs plutôt qu'un objet PHP. Ainsi, la même chose qui vaut pour N3.js est désormais un tableau. Par exemple :
<?php
$ triple = [
' subject ' => ' http://example.org/cartoons#Tom ' ,
' predicate ' => ' http://www.w3.org/1999/02/22-rdf-syntax-ns#type ' ,
' object ' => ' http://example.org/cartoons#Cat ' ,
' graph ' => ' http://example.org/mycartoon ' , #optional
];
Encodez les littéraux comme suit (similaire à N3.js)
' "Tom"@en-gb ' // lowercase language
'" 1 "^^http: //www.w3.org/2001/XMLSchema#integer' // no angular brackets <>
Installez cette bibliothèque à l'aide de composer :
composer require pietercolpaert/ hardf
use pietercolpaert hardf TriGWriter ;
Une classe qui doit être instanciée et qui peut écrire TriG ou Turtle
Exemple d'utilisation :
$ writer = new TriGWriter ([
" prefixes " => [
" schema " => " http://schema.org/ " ,
" dct " => " http://purl.org/dc/terms/ " ,
" geo " => " http://www.w3.org/2003/01/geo/wgs84_pos# " ,
" rdf " => " http://www.w3.org/1999/02/22-rdf-syntax-ns# " ,
" rdfs " => " http://www.w3.org/2000/01/rdf-schema# "
],
" format " => " n-quads " //Other possible values: n-quads, trig or turtle
]);
$ writer -> addPrefix ( " ex " , " http://example.org/ " );
$ writer -> addTriple ( " schema:Person " , " dct:title " , "" Person " @en " , " http://example.org/#test " );
$ writer -> addTriple ( " schema:Person " , " schema:label " , "" Person " @en " , " http://example.org/#test " );
$ writer -> addTriple ( " ex:1 " , " dct:title " , "" Person1 " @en " , " http://example.org/#test " );
$ writer -> addTriple ( " ex:1 " , " http://www.w3.org/1999/02/22-rdf-syntax-ns#type " , " schema:Person " , " http://example.org/#test " );
$ writer -> addTriple ( " ex:2 " , " dct:title " , "" Person2 " @en " , " http://example.org/#test " );
$ writer -> addTriple ( " schema:Person " , " dct:title " , "" Person " @en " , " http://example.org/#test2 " );
echo $ writer -> end ();
//The method names should speak for themselves:
$ writer = new TriGWriter ([ " prefixes " : [ /* ... */ ]]);
$ writer -> addTriple ( $ subject , $ predicate , $ object , $ graphl );
$ writer -> addTriples ( $ triples );
$ writer -> addPrefix ( $ prefix , $ iri );
$ writer -> addPrefixes ( $ prefixes );
//Creates blank node($predicate and/or $object are optional)
$ writer -> blank ( $ predicate , $ object );
//Creates rdf:list with $elements
$ list = $ writer -> addList ( $ elements );
//Returns the current output it is already able to create and clear the internal memory use (useful for streaming)
$ out .= $ writer -> read ();
//Alternatively, you can listen for new chunks through a callback:
$ writer -> setReadCallback ( function ( $ output ) { echo $ output });
//Call this at the end. The return value will be the full triple output, or the rest of the output such as closing dots and brackets, unless a callback was set.
$ out .= $ writer -> end ();
//OR
$ writer -> end ();
À côté de TriG, la classe TriGParser analyse également Turtle, N-Triples, N-Quads et la soumission de l'équipe W3C N3.
$ parser = new TriGParser ( $ options , $ tripleCallback , $ prefixCallback );
$ parser -> setTripleCallback ( $ function );
$ parser -> setPrefixCallback ( $ function );
$ parser -> parse ( $ input , $ tripleCallback , $ prefixCallback );
$ parser -> parseChunk ( $ input );
$ parser -> end ();
Utiliser les valeurs de retour et les transmettre à un rédacteur :
use pietercolpaert hardf TriGParser ;
use pietercolpaert hardf TriGWriter ;
$ parser = new TriGParser ([ " format " => " n-quads " ]); //also parser n-triples, n3, turtle and trig. Format is optional
$ writer = new TriGWriter ();
$ triples = $ parser -> parse ( " <A> <B> <C> <G> . " );
$ writer -> addTriples ( $ triples );
echo $ writer -> end ();
Utiliser des rappels et les transmettre à un rédacteur :
$ parser = new TriGParser ();
$ writer = new TriGWriter ([ " format " => " trig " ]);
$ parser -> parse ( " <http://A> <https://B> <http://C> <http://G> . <A2> <https://B2> <http://C2> <http://G3> . " , function ( $ e , $ triple ) use ( $ writer ) {
if (! isset ( $ e ) && isset ( $ triple )) {
$ writer -> addTriple ( $ triple );
echo $ writer -> read (); //write out what we have so far
} else if (! isset ( $ triple )) // flags the end of the file
echo $ writer -> end (); //write the end
else
echo " Error occured: " . $ e ;
});
Lorsque vous devez analyser un fichier volumineux, vous devrez analyser uniquement des morceaux et déjà les traiter. Vous pouvez procéder comme suit :
$ writer = new TriGWriter ([ " format " => " n-quads " ]);
$ tripleCallback = function ( $ error , $ triple ) use ( $ writer ) {
if ( isset ( $ error ))
throw $ error ;
else if ( isset ( $ triple )) {
$ writer -> write ();
echo $ writer -> read ();
else if ( isset ( $ error )) {
throw $ error ;
} else {
echo $ writer -> end ();
}
};
$ prefixCallback = function ( $ prefix , $ iri ) use (& $ writer ) {
$ writer -> addPrefix ( $ prefix , $ iri );
};
$ parser = new TriGParser ([ " format " => " trig " ], $ tripleCallback , $ prefixCallback );
$ parser -> parseChunk ( $ chunk );
$ parser -> parseChunk ( $ chunk );
$ parser -> parseChunk ( $ chunk );
$ parser -> end (); //Needs to be called
format
format d'entrée (insensible à la casse)turtle
- Tortuetrig
- TriGtriple
, par exemple triple
, ntriples
, N-Triples
- N-Triplesquad
, par exemple quad
, nquads
, N-Quads
- N-Quadsn3
, par exemple n3
- N3blankNodePrefix
(par défaut b0_
) préfixe forcé sur les noms de nœuds vides, par exemple TriGWriter(["blankNodePrefix" => 'foo'])
analysera _:bar
comme _:foobar
.documentIRI
définit l'URI de base utilisé pour résoudre les URI relatifs (non applicable si format
indique n-triples ou n-quads)lexer
permet d'utiliser sa propre classe lexer. Un lexer doit fournir les méthodes publiques suivantes :tokenize(string $input, bool $finalize = true): array<array{'subject': string, 'predicate': string, 'object': string, 'graph': string}>
tokenizeChunk(string $input): array<array{'subject': string, 'predicate': string, 'object': string, 'graph': string}>
end(): array<array{'subject': string, 'predicate': string, 'object': string, 'graph': string}>
explicitQuantifiers
- [...] Certains documents Turtle et N3 peuvent utiliser la syntaxe IRI relative à l'IRI de base (voir ici et ici), par exemple
<> <someProperty> "some value" .
Pour analyser correctement ces documents, l'IRI de la base du document doit être connu. Sinon, nous pourrions nous retrouver avec des IRI vides (par exemple pour le sujet dans l'exemple ci-dessus).
Parfois, l'IRI de base est codé dans le document, par exemple
@base <http://some.base/iri/> .
<> <someProperty> "some value" .
mais parfois il manque. Dans un tel cas, la spécification Turtle nous oblige à suivre la section 5.1.1 de la RFC3986 qui dit que si l'IRI de base n'est pas encapsulé dans le document, il doit être supposé qu'il s'agit de l'URI de récupération du document (par exemple, l'URL avec laquelle vous avez téléchargé le document). à partir d'un chemin de fichier converti en URL). Malheureusement, cela ne peut pas être deviné par l'analyseur hardf et doit être fourni par vous en utilisant l'option de création de l'analyseur documentIRI
, par exemple
parser = new TriGParser ([ " documentIRI " => " http://some.base/iri/ " ]);
Pour faire court, si vous rencontrez le subject/predicate/object on line X can not be parsed without knowing the the document base IRI.(...)
, veuillez initialiser l'analyseur avec l'option documentIRI
.
use pietercolpaert hardf Util ;
Une classe statique avec quelques fonctions utiles pour gérer notre triple représentation spécifique. Il vous aidera à créer et évaluer des littéraux, des IRI et à développer des préfixes.
$ bool = isIRI ( $ term );
$ bool = isLiteral ( $ term );
$ bool = isBlank ( $ term );
$ bool = isDefaultGraph ( $ term );
$ bool = inDefaultGraph ( $ triple );
$ value = getLiteralValue ( $ literal );
$ literalType = getLiteralType ( $ literal );
$ lang = getLiteralLanguage ( $ literal );
$ bool = isPrefixedName ( $ term );
$ expanded = expandPrefixedName ( $ prefixedName , $ prefixes );
$ iri = createIRI ( $ iri );
$ literalObject = createLiteral ( $ value , $ modifier = null );
Consultez la documentation sur https://github.com/RubenVerborgh/N3.js#utility pour plus d'informations.
Nous proposons également 2 outils simples dans bin/
à titre d'exemple d'implémentation : un validateur et un traducteur. Essayez par exemple :
curl -H " accept: application/trig " http://fragments.dbpedia.org/2015/en | php bin/validator.php trig
curl -H " accept: application/trig " http://fragments.dbpedia.org/2015/en | php bin/convert.php trig n-triples
Nous avons comparé les performances de deux fichiers Turtle et les avons analysées avec la bibliothèque EasyRDF en PHP, la bibliothèque N3.js pour NodeJS et avec hardf . Voici les résultats :
#triples | cadre | temps (ms) | mémoire (Mo) |
---|---|---|---|
1 866 | hardf sans opcache | 27.6 | 0,722 |
1 866 | hardf avec opcache | 24,5 | 0,380 |
1 866 | EasyRDF sans opcache | 5 166,5 | 2.772 |
1 866 | EasyRDF avec opcache | 5 176,2 | 2.421 |
1 866 | ARC2 avec opcache | 71,9 | 1.966 |
1 866 | N3.js | 24,0 | 28.xxx |
3 896 560 | hardf sans opcache | 40 017,7 | 0,722 |
3 896 560 | hardf avec opcache | 33 155,3 | 0,380 |
3 896 560 | N3.js | 7 004,0 | 59.xxx |
3 896 560 | ARC2 avec opcache | 203 152,6 | 3 570,808 |
La bibliothèque hardf est protégée par les droits d'auteur de Ruben Verborgh et Pieter Colpaert et publiée sous la licence MIT.
Les contributions sont les bienvenues et les rapports de bogues ou les demandes d'extraction sont toujours utiles. Si vous envisagez d'implémenter une fonctionnalité plus importante, il est préférable d'en discuter d'abord en signalant un problème.