• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

sweetrdf / quickRdfIo / #112

24 Apr 2025 02:36PM UTC coverage: 90.805%. Remained the same
#112

push

php-coveralls

zozlak
Implement rdf-interface 3.1.0

* Implement $baseUri parameter handling in parse() and parseStream() of
  all parsers.
* Util::parse(): pass document base URI to the parser.

(closes #11)

33 of 34 new or added lines in 7 files covered. (97.06%)

21 existing lines in 4 files now uncovered.

869 of 957 relevant lines covered (90.8%)

6.37 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.94
/src/quickRdfIo/TriGParser.php
1
<?php
2

3
/*
4
 * The MIT License
5
 *
6
 * Copyright 2021 zozlak.
7
 *
8
 * Permission is hereby granted, free of charge, to any person obtaining a copy
9
 * of this software and associated documentation files (the "Software"), to deal
10
 * in the Software without restriction, including without limitation the rights
11
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
 * copies of the Software, and to permit persons to whom the Software is
13
 * furnished to do so, subject to the following conditions:
14
 *
15
 * The above copyright notice and this permission notice shall be included in
16
 * all copies or substantial portions of the Software.
17
 *
18
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
 * THE SOFTWARE.
25
 */
26

27
namespace quickRdfIo;
28

29
use ArrayIterator;
30
use Psr\Http\Message\StreamInterface;
31
use pietercolpaert\hardf\Util;
32
use pietercolpaert\hardf\TriGParser as Parser;
33
use rdfInterface\QuadIteratorInterface as iQuadIterator;
34
use rdfInterface\ParserInterface as iParser;
35
use rdfInterface\QuadInterface as iQuad;
36
use rdfInterface\DataFactoryInterface as iDataFactory;
37

38
/**
39
 * Description of Parser
40
 *
41
 * @author zozlak
42
 */
43
class TriGParser implements iParser, iQuadIterator {
44

45
    use TmpStreamParserTrait;
46
    use StreamSkipBomTrait;
47
    use CreateBlankNodeTrait;
48

49
    const CHUNK_SIZE = 8192;
50

51
    private iDataFactory $dataFactory;
52

53
    /**
54
     *
55
     * @var array<mixed>
56
     */
57
    private array $options;
58
    private Parser $parser;
59
    private StreamInterface $input;
60
    private string $chunk;
61

62
    /**
63
     *
64
     * @var ArrayIterator<int, iQuad>
65
     */
66
    private ArrayIterator $quadsBuffer;
67
    private int $n;
68

69
    /**
70
     *
71
     * @var callable|null
72
     */
73
    private $prefixCallback;
74

75
    /**
76
     *
77
     * @param iDataFactory $dataFactory factory to be used to generate RDF terms.
78
     * @param array<mixed> $options options to be passed to the pietercolpaert\hardf\TriGParser
79
     *   constructor
80
     * @param callable|null $prefixCallback a callable for handling prefixes
81
     *   to be passed to the pietercolpaert\hardf\TriGParser constructor
82
     * @see \pietercolpaert\hardf\TriGParser::__construct()
83
     */
84
    public function __construct(iDataFactory $dataFactory, array $options = [],
85
                                callable | null $prefixCallback = null
86
    ) {
87
        $this->dataFactory    = $dataFactory;
8✔
88
        $this->options        = $options;
8✔
89
        $this->prefixCallback = $prefixCallback;
8✔
90
    }
91

92
    public function __destruct() {
93
        $this->closeTmpStream();
1✔
94
    }
95

96
    /**
97
     * 
98
     * @param resource | StreamInterface $input
99
     * @return iQuadIterator
100
     */
101
    public function parseStream($input, string $baseUri = ''): iQuadIterator {
102
        if (is_resource($input)) {
8✔
103
            $input = new ResourceWrapper($input);
7✔
104
        }
105
        if (!($input instanceof StreamInterface)) {
8✔
UNCOV
106
            throw new RdfIoException("Input has to be a resource or " . StreamInterface::class . " object");
×
107
        }
108

109
        $this->input       = $input;
8✔
110
        $this->n           = -1;
8✔
111
        $this->chunk       = '';
8✔
112
        $this->quadsBuffer = new ArrayIterator();
8✔
113
        $this->parser      = new Parser($this->options, null, $this->prefixCallback);
8✔
114
        $this->baseUri     = $baseUri;
8✔
115
        return $this;
8✔
116
    }
117

118
    public function current(): iQuad {
119
        return $this->quadsBuffer->current();
8✔
120
    }
121

122
    public function key(): mixed {
123
        return $this->n;
3✔
124
    }
125

126
    public function next(): void {
127
        $this->quadsBuffer->next();
8✔
128
        if (!$this->quadsBuffer->valid()) {
8✔
129
            $this->quadsBuffer = new ArrayIterator();
8✔
130
            $this->parser->setTripleCallback(function (
8✔
131
                ?\Exception $e, ?array $quad
8✔
132
            ): void {
8✔
133
                if ($e) {
8✔
UNCOV
134
                    throw $e;
×
135
                }
136
                if ($quad) {
8✔
137
                    $df   = $this->dataFactory;
8✔
138
                    $sbj  = Util::isBlank($quad['subject']) ?
8✔
139
                        $this->createBlankNode($quad['subject']) : $df::namedNode($quad['subject']);
8✔
140
                    $prop = $df::namedNode($quad['predicate']);
8✔
141
                    if (substr($quad['object'], 0, 1) !== '"') {
8✔
142
                        $obj = Util::isBlank($quad['object']) ?
7✔
143
                            $this->createBlankNode($quad['object']) : $df::namedNode($quad['object']);
7✔
144
                    } else {
145
                        // as Util::getLiteralValue() doesn't work for multiline values
146
                        $value    = substr($quad['object'], 1, strrpos($quad['object'], '"') - 1);
7✔
147
                        $lang     = Util::getLiteralLanguage($quad['object']);
7✔
148
                        $datatype = empty($lang) ? Util::getLiteralType($quad['object']) : '';
7✔
149
                        $obj      = $df::Literal($value, $lang, $datatype);
7✔
150
                    }
151
                    $graph               = !empty($quad['graph']) ?
8✔
152
                        $df::namedNode($quad['graph']) : $df::defaultGraph();
8✔
153
                    $this->quadsBuffer[] = $df::quad($sbj, $prop, $obj, $graph);
8✔
154
                }
155
            });
8✔
156
            while (!$this->input->eof() && $this->quadsBuffer->count() === 0) {
8✔
157
                $this->chunk .= $this->input->read(self::CHUNK_SIZE);
8✔
158
                $p           = strlen($this->chunk) - 1;
8✔
159
                $cp          = ord($this->chunk[$p]);
8✔
160
                if ($cp < 127) {
8✔
161
                    // chunk ends with a single-byte UTF-8 character - just parse whole chunk
162
                    $this->parser->parseChunk($this->chunk);
8✔
163
                    $this->chunk = '';
8✔
164
                } else {
165
                    // find the position of the first byte of the UTF-8 character ending the chunk
166
                    // parse the chunk part excluding it
167
                    while ($cp < 192) {
1✔
UNCOV
168
                        $p--;
×
UNCOV
169
                        $cp = ord($this->chunk[$p]);
×
170
                    }
171
                    $this->parser->parseChunk(substr($this->chunk, 0, $p));
1✔
172
                    $this->chunk = substr($this->chunk, $p);
1✔
173
                }
174
            }
175
            if ($this->input->eof()) {
8✔
176
                $this->parser->end();
8✔
177
            }
178
        }
179
        $this->n++;
8✔
180
    }
181

182
    public function rewind(): void {
183
        $this->n = -1;
8✔
184
        if ($this->input->tell() !== 0) {
8✔
UNCOV
185
            $this->input->rewind();
×
186
        }
187
        $this->skipBom($this->input);
8✔
188
        $this->next();
8✔
189
    }
190

191
    public function valid(): bool {
192
        return $this->quadsBuffer->valid();
8✔
193
    }
194
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc