hardf — это библиотека PHP 7.1+, которая позволяет обрабатывать связанные данные (RDF). Он предлагает:
И анализатор, и сериализатор поддерживают потоковую передачу .
Эта библиотека представляет собой порт N3.js на PHP.
Мы используем тройное представление в PHP, перенесенное из библиотеки NodeJS N3.js. Проверьте https://github.com/rdfjs/N3.js/tree/v0.10.0#triple-representation для получения дополнительной информации.
Мы специально сосредоточились на производительности, а не на удобстве для разработчиков. Таким образом, мы реализовали это тройное представление, используя ассоциативные массивы, а не объект PHP. Таким образом, то же самое, что и для N3.js, теперь является массивом. Например:
<?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
];
Кодируйте литералы следующим образом (аналогично N3.js)
' "Tom"@en-gb ' // lowercase language
'" 1 "^^http: //www.w3.org/2001/XMLSchema#integer' // no angular brackets <>
Установите эту библиотеку с помощью композитора:
composer require pietercolpaert/ hardf
use pietercolpaert hardf TriGWriter ;
Класс, экземпляр которого должен быть создан и который может писать TriG или Turtle.
Пример использования:
$ 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 ();
Помимо TriG, класс TriGParser также анализирует Turtle, N-Triples, N-Quads и представление команды W3C N3.
$ parser = new TriGParser ( $ options , $ tripleCallback , $ prefixCallback );
$ parser -> setTripleCallback ( $ function );
$ parser -> setPrefixCallback ( $ function );
$ parser -> parse ( $ input , $ tripleCallback , $ prefixCallback );
$ parser -> parseChunk ( $ input );
$ parser -> end ();
Использование возвращаемых значений и передача их писателю:
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 ();
Использование обратных вызовов и передача их писателю:
$ 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 ;
});
Когда вам нужно разобрать большой файл, вам нужно будет разобрать только куски и уже их обработать. Вы можете сделать это следующим образом:
$ 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
(без учета регистра)turtle
- Черепахаtrig
- ТриГtriple
, например triple
, ntriples
, N-Triples
- N-тройкуquad
, например quad
, nquads
, N-Quads
- N-Quadsn3
, например n3
- N3blankNodePrefix
(по умолчанию b0_
), используемый для пустых имен узлов, например, TriGWriter(["blankNodePrefix" => 'foo'])
будет анализировать _:bar
как _:foobar
.documentIRI
устанавливает базовый URI, используемый для разрешения относительных URI (неприменимо, если format
указывает n-тройки или n-четверки)lexer
позволяет использовать собственный класс лексера. Лексер должен предоставлять следующие общедоступные методы: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
- [...] Некоторые документы Turtle и N3 могут использовать синтаксис IRI относительно базового IRI (см. здесь и здесь), например
<> <someProperty> "some value" .
Для правильного анализа таких документов необходимо знать базу документов IRI. В противном случае мы можем получить пустые IRI (например, для субъекта в приведенном выше примере).
Иногда базовый IRI закодирован в документе, например
@base <http://some.base/iri/> .
<> <someProperty> "some value" .
но иногда оно отсутствует. В таком случае спецификация Turtle требует от нас следовать разделу 5.1.1 RFC3986, в котором говорится, что если базовый IRI не инкапсулирован в документе, его следует считать URI поиска документа (например, URL-адрес, по которому вы загрузили документ). from или путь к файлу, преобразованный в URL). К сожалению, парсер hardf не может угадать это, и вы должны предоставить его с помощью опции создания парсера documentIRI
, например
parser = new TriGParser ([ " documentIRI " => " http://some.base/iri/ " ]);
Короче говоря, если вы столкнулись с subject/predicate/object on line X can not be parsed without knowing the the document base IRI.(...)
, инициализируйте синтаксический анализатор с параметром documentIRI
.
use pietercolpaert hardf Util ;
Статический класс с парой полезных функций для обработки нашего конкретного тройного представления. Это поможет вам создавать и оценивать литералы, IRI и расширять префиксы.
$ 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 );
Дополнительную информацию см. в документации по адресу https://github.com/RubenVerborgh/N3.js#utility.
Мы также предлагаем 2 простых инструмента в bin/
в качестве примера реализации: один валидатор и один транслятор. Попробуйте, например:
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
Мы сравнили производительность двух файлов черепах и проанализировали ее с помощью библиотеки EasyRDF в PHP, библиотеки N3.js для NodeJS и с помощью hardf . Вот результаты:
#тройки | рамки | время (мс) | память (МБ) |
---|---|---|---|
1866 | hardf без opcache | 27,6 | 0,722 |
1866 | hardf с opcache | 24,5 | 0,380 |
1866 | EasyRDF без opcache | 5166,5 | 2,772 |
1866 | EasyRDF с opcache | 5176,2 | 2.421 |
1866 | ARC2 с opcache | 71,9 | 1,966 |
1866 | N3.js | 24,0 | 28.ххх |
3 896 560 | hardf без opcache | 40 017,7 | 0,722 |
3 896 560 | hardf с opcache | 33 155,3 | 0,380 |
3 896 560 | N3.js | 7004,0 | 59.ххх |
3 896 560 | ARC2 с opcache | 203 152,6 | 3570,808 |
Авторские права на библиотеку hardf принадлежат Рубену Верборгу и Питеру Колпаерту, и она выпущена по лицензии MIT.
Вклады приветствуются, а отчеты об ошибках или запросы на включение всегда полезны. Если вы планируете реализовать более крупную функцию, лучше сначала обсудить это, сообщив о проблеме.