|
1
|
|
|
<?php |
|
|
|
|
|
|
2
|
|
|
|
|
3
|
|
|
/** |
|
4
|
|
|
* Setting some often needed namespace prefixes |
|
5
|
|
|
*/ |
|
6
|
|
|
EasyRdf_Namespace::set('skosmos', 'http://purl.org/net/skosmos#'); |
|
7
|
|
|
EasyRdf_Namespace::set('void', 'http://rdfs.org/ns/void#'); |
|
8
|
|
|
EasyRdf_Namespace::set('skosext', 'http://purl.org/finnonto/schema/skosext#'); |
|
9
|
|
|
EasyRdf_Namespace::set('isothes', 'http://purl.org/iso25964/skos-thes#'); |
|
10
|
|
|
EasyRdf_Namespace::set('mads', 'http://www.loc.gov/mads/rdf/v1#'); |
|
11
|
|
|
|
|
12
|
|
|
/** |
|
13
|
|
|
* Model provides access to the data. |
|
14
|
|
|
* @property EasyRdf_Graph $graph |
|
15
|
|
|
* @property GlobalConfig $globalConfig |
|
16
|
|
|
*/ |
|
17
|
|
|
class Model |
|
|
|
|
|
|
18
|
|
|
{ |
|
19
|
|
|
/** EasyRdf_Graph graph instance */ |
|
20
|
|
|
private $graph; |
|
21
|
|
|
/** Namespaces from vocabularies configuration file */ |
|
22
|
|
|
private $namespaces; |
|
23
|
|
|
/** cache for Vocabulary objects */ |
|
24
|
|
|
private $allVocabularies = null; |
|
25
|
|
|
/** cache for Vocabulary objects */ |
|
26
|
|
|
private $vocabsByGraph = null; |
|
27
|
|
|
/** cache for Vocabulary objects */ |
|
28
|
|
|
private $vocabsByUriSpace = null; |
|
29
|
|
|
/** how long to store retrieved URI information in APC cache */ |
|
30
|
|
|
const URI_FETCH_TTL = 86400; // 1 day |
|
31
|
|
|
private $globalConfig; |
|
32
|
|
|
private $cache; |
|
33
|
|
|
|
|
34
|
|
|
/** |
|
35
|
|
|
* Initializes the Model object |
|
36
|
|
|
*/ |
|
37
|
|
|
public function __construct($config) |
|
38
|
|
|
{ |
|
39
|
|
|
$this->globalConfig = $config; |
|
40
|
|
|
try { |
|
41
|
|
|
$this->cache = new Cache(); |
|
42
|
|
|
$this->initializeVocabularies(); |
|
43
|
|
|
$this->initializeNamespaces(); |
|
44
|
|
|
$this->initializeLogging(); |
|
45
|
|
|
} catch (Exception $e) { |
|
46
|
|
|
header("HTTP/1.0 404 Not Found"); |
|
47
|
|
|
echo("Error: Vocabularies configuration file 'vocabularies.ttl' not found."); |
|
48
|
|
|
return; |
|
49
|
|
|
} |
|
50
|
|
|
} |
|
51
|
|
|
|
|
52
|
|
|
/** |
|
53
|
|
|
* Returns the GlobalConfig object given to the Model as a constructor parameter. |
|
54
|
|
|
* @return GlobalConfig |
|
55
|
|
|
*/ |
|
56
|
|
|
public function getConfig() { |
|
57
|
|
|
return $this->globalConfig; |
|
58
|
|
|
} |
|
59
|
|
|
|
|
60
|
|
|
/** |
|
61
|
|
|
* Initializes the configuration from the vocabularies.ttl file |
|
62
|
|
|
*/ |
|
63
|
|
|
private function initializeVocabularies() |
|
64
|
|
|
{ |
|
65
|
|
|
if (!file_exists($this->getConfig()->getVocabularyConfigFile())) { |
|
66
|
|
|
throw new Exception($this->getConfig()->getVocabularyConfigFile() . ' is missing, please provide one.'); |
|
67
|
|
|
} |
|
68
|
|
|
|
|
69
|
|
|
try { |
|
70
|
|
|
// use APC user cache to store parsed vocabularies.ttl configuration |
|
71
|
|
|
if ($this->cache->isAvailable()) { |
|
72
|
|
|
// @codeCoverageIgnoreStart |
|
73
|
|
|
$key = realpath($this->getConfig()->getVocabularyConfigFile()) . ", " . filemtime($this->getConfig()->getVocabularyConfigFile()); |
|
74
|
|
|
$nskey = "namespaces of " . $key; |
|
75
|
|
|
$this->graph = $this->cache->fetch($key); |
|
76
|
|
|
$this->namespaces = $this->cache->fetch($nskey); |
|
77
|
|
|
if ($this->graph === false || $this->namespaces === false) { // was not found in cache |
|
78
|
|
|
$this->parseVocabularies($this->getConfig()->getVocabularyConfigFile()); |
|
79
|
|
|
$this->cache->store($key, $this->graph); |
|
80
|
|
|
$this->cache->store($nskey, $this->namespaces); |
|
81
|
|
|
} |
|
82
|
|
|
// @codeCoverageIgnoreEnd |
|
83
|
|
|
} else { // APC not available, parse on every request |
|
84
|
|
|
$this->parseVocabularies($this->getConfig()->getVocabularyConfigFile()); |
|
85
|
|
|
} |
|
86
|
|
|
} catch (Exception $e) { |
|
87
|
|
|
echo "Error: " . $e->getMessage(); |
|
88
|
|
|
} |
|
89
|
|
|
} |
|
90
|
|
|
|
|
91
|
|
|
/** |
|
92
|
|
|
* Parses vocabulary configuration and RDF namespaces from the vocabularies.ttl file |
|
93
|
|
|
* @param string $filename path to vocabularies.ttl file |
|
94
|
|
|
*/ |
|
95
|
|
|
|
|
96
|
|
|
private function parseVocabularies($filename) |
|
97
|
|
|
{ |
|
98
|
|
|
$this->graph = new EasyRdf_Graph(); |
|
99
|
|
|
$parser = new NamespaceExposingTurtleParser(); |
|
100
|
|
|
$parser->parse($this->graph, file_get_contents($filename), 'turtle', $filename); |
|
101
|
|
|
$this->namespaces = $parser->getNamespaces(); |
|
102
|
|
|
} |
|
103
|
|
|
|
|
104
|
|
|
/** |
|
105
|
|
|
* Registers RDF namespaces from the vocabularies.ttl file for use by EasyRdf (e.g. serializing) |
|
106
|
|
|
*/ |
|
107
|
|
|
|
|
108
|
|
|
private function initializeNamespaces() { |
|
109
|
|
|
foreach ($this->namespaces as $prefix => $fullUri) { |
|
110
|
|
|
if ($prefix != '' && EasyRdf_Namespace::get($prefix) === null) // if not already defined |
|
111
|
|
|
{ |
|
112
|
|
|
EasyRdf_Namespace::set($prefix, $fullUri); |
|
113
|
|
|
} |
|
114
|
|
|
} |
|
115
|
|
|
} |
|
116
|
|
|
|
|
117
|
|
|
/** |
|
118
|
|
|
* Configures the logging facility |
|
119
|
|
|
*/ |
|
120
|
|
|
private function initializeLogging() { |
|
121
|
|
|
$this->logger = new \Monolog\Logger('general'); |
|
|
|
|
|
|
122
|
|
|
$formatter = new \Monolog\Formatter\LineFormatter("[%datetime%] %level_name% %message%\n"); |
|
123
|
|
|
$formatter->allowInlineLineBreaks(true); |
|
124
|
|
|
if ($this->getConfig()->getLoggingBrowserConsole()) { |
|
125
|
|
|
$browserHandler = new \Monolog\Handler\BrowserConsoleHandler(\Monolog\Logger::INFO); |
|
126
|
|
|
$browserHandler->setFormatter($formatter); |
|
127
|
|
|
$this->logger->pushHandler($browserHandler); |
|
128
|
|
|
} |
|
129
|
|
|
if ($this->getConfig()->getLoggingFilename() !== null) { |
|
130
|
|
|
$streamHandler = new \Monolog\Handler\StreamHandler($this->getConfig()->getLoggingFilename(), \Monolog\Logger::INFO); |
|
131
|
|
|
$streamHandler->setFormatter($formatter); |
|
132
|
|
|
$this->logger->pushHandler($streamHandler); |
|
133
|
|
|
} |
|
134
|
|
|
if (!$this->logger->getHandlers()) { |
|
135
|
|
|
// add a NullHandler to suppress the default Monolog logging to stderr |
|
136
|
|
|
$nullHandler = new \Monolog\Handler\NullHandler(); |
|
137
|
|
|
$this->logger->pushHandler($nullHandler); |
|
138
|
|
|
} |
|
139
|
|
|
} |
|
140
|
|
|
|
|
141
|
|
|
/** |
|
142
|
|
|
* Return the logging facility |
|
143
|
|
|
* @return object logger |
|
144
|
|
|
*/ |
|
145
|
|
|
public function getLogger() { |
|
146
|
|
|
return $this->logger; |
|
147
|
|
|
} |
|
148
|
|
|
|
|
149
|
|
|
/** |
|
150
|
|
|
* Return the version of this Skosmos installation, or "unknown" if |
|
151
|
|
|
* it cannot be determined. The version information is based on Git tags. |
|
152
|
|
|
* @return string version |
|
153
|
|
|
*/ |
|
154
|
|
|
public function getVersion() |
|
155
|
|
|
{ |
|
156
|
|
|
$ver = null; |
|
157
|
|
|
if (file_exists('.git')) { |
|
158
|
|
|
$ver = shell_exec('git describe --tags'); |
|
159
|
|
|
} |
|
160
|
|
|
|
|
161
|
|
|
if ($ver === null) { |
|
162
|
|
|
return "unknown"; |
|
163
|
|
|
} |
|
164
|
|
|
|
|
165
|
|
|
return $ver; |
|
166
|
|
|
} |
|
167
|
|
|
|
|
168
|
|
|
/** |
|
169
|
|
|
* Return all the vocabularies available. |
|
170
|
|
|
* @param boolean $categories whether you want everything included in a subarray of |
|
171
|
|
|
* a category. |
|
172
|
|
|
* @param boolean $shortname set to true if you want the vocabularies sorted by |
|
173
|
|
|
* their shortnames instead of ther titles. |
|
174
|
|
|
*/ |
|
175
|
|
|
public function getVocabularyList($categories = true, $shortname = false) |
|
176
|
|
|
{ |
|
177
|
|
|
$cats = $this->getVocabularyCategories(); |
|
178
|
|
|
$ret = array(); |
|
179
|
|
|
foreach ($cats as $cat) { |
|
180
|
|
|
$catlabel = $cat->getTitle(); |
|
181
|
|
|
|
|
182
|
|
|
// find all the vocabs in this category |
|
183
|
|
|
$vocs = array(); |
|
184
|
|
|
foreach ($cat->getVocabularies() as $voc) { |
|
185
|
|
|
$vocs[$shortname ? $voc->getConfig()->getShortname() : $voc->getConfig()->getTitle()] = $voc; |
|
186
|
|
|
} |
|
187
|
|
|
uksort($vocs, 'strcoll'); |
|
188
|
|
|
|
|
189
|
|
|
if (sizeof($vocs) > 0 && $categories) { |
|
190
|
|
|
$ret[$catlabel] = $vocs; |
|
191
|
|
|
} elseif (sizeof($vocs) > 0) { |
|
192
|
|
|
$ret = array_merge($ret, $vocs); |
|
193
|
|
|
} |
|
194
|
|
|
|
|
195
|
|
|
} |
|
196
|
|
|
|
|
197
|
|
|
if (!$categories) { |
|
198
|
|
|
uksort($ret, 'strcoll'); |
|
199
|
|
|
} |
|
200
|
|
|
|
|
201
|
|
|
return $ret; |
|
202
|
|
|
} |
|
203
|
|
|
|
|
204
|
|
|
/** |
|
205
|
|
|
* Return all types (RDFS/OWL classes) present in the specified vocabulary or all vocabularies. |
|
206
|
|
|
* @return array Array with URIs (string) as key and array of (label, superclassURI) as value |
|
207
|
|
|
*/ |
|
208
|
|
|
public function getTypes($vocid = null, $lang = null) |
|
209
|
|
|
{ |
|
210
|
|
|
$sparql = (isset($vocid)) ? $this->getVocabulary($vocid)->getSparql() : $this->getDefaultSparql(); |
|
211
|
|
|
$result = $sparql->queryTypes($lang); |
|
212
|
|
|
|
|
213
|
|
|
foreach ($result as $uri => $values) { |
|
214
|
|
|
if (empty($values)) { |
|
215
|
|
|
$shorteneduri = EasyRdf_Namespace::shorten($uri); |
|
216
|
|
|
if ($shorteneduri !== null) { |
|
217
|
|
|
$trans = gettext($shorteneduri); |
|
218
|
|
|
if ($trans) { |
|
219
|
|
|
$result[$uri] = array('label' => $trans); |
|
220
|
|
|
} |
|
221
|
|
|
} |
|
222
|
|
|
} |
|
223
|
|
|
} |
|
224
|
|
|
|
|
225
|
|
|
return $result; |
|
226
|
|
|
} |
|
227
|
|
|
|
|
228
|
|
|
/** |
|
229
|
|
|
* Return the languages present in the configured vocabularies. |
|
230
|
|
|
* @return array Array with lang codes (string) |
|
231
|
|
|
*/ |
|
232
|
|
|
public function getLanguages($lang) |
|
233
|
|
|
{ |
|
234
|
|
|
$vocabs = $this->getVocabularyList(false); |
|
235
|
|
|
$ret = array(); |
|
236
|
|
|
foreach ($vocabs as $vocab) { |
|
237
|
|
|
foreach ($vocab->getConfig()->getLanguages() as $langcode) { |
|
238
|
|
|
$langlit = Punic\Language::getName($langcode, $lang); |
|
239
|
|
|
$ret[$langlit] = $langcode; |
|
240
|
|
|
} |
|
241
|
|
|
} |
|
242
|
|
|
ksort($ret); |
|
243
|
|
|
return array_unique($ret); |
|
244
|
|
|
} |
|
245
|
|
|
|
|
246
|
|
|
/** |
|
247
|
|
|
* returns a concept's RDF data in downloadable format |
|
248
|
|
|
* @param string $vocid vocabulary id, or null for global data (from all vocabularies) |
|
249
|
|
|
* @param string $uri concept URI |
|
250
|
|
|
* @param string $format the format in which you want to get the result, currently this function supports |
|
251
|
|
|
* text/turtle, application/rdf+xml and application/json |
|
252
|
|
|
* @return string RDF data in the requested serialization |
|
253
|
|
|
*/ |
|
254
|
|
|
public function getRDF($vocid, $uri, $format) |
|
255
|
|
|
{ |
|
256
|
|
|
|
|
257
|
|
|
if ($format == 'text/turtle') { |
|
258
|
|
|
$retform = 'turtle'; |
|
259
|
|
|
$serialiser = new EasyRdf_Serialiser_Turtle(); |
|
260
|
|
|
} elseif ($format == 'application/ld+json' || $format == 'application/json') { |
|
261
|
|
|
$retform = 'jsonld'; // serve JSON-LD for both JSON-LD and plain JSON requests |
|
262
|
|
|
$serialiser = new EasyRdf_Serialiser_JsonLd(); |
|
263
|
|
|
} else { |
|
264
|
|
|
$retform = 'rdfxml'; |
|
265
|
|
|
$serialiser = new EasyRdf_Serialiser_RdfXml(); |
|
266
|
|
|
} |
|
267
|
|
|
|
|
268
|
|
|
if ($vocid !== null) { |
|
269
|
|
|
$vocab = $this->getVocabulary($vocid); |
|
270
|
|
|
$sparql = $vocab->getSparql(); |
|
271
|
|
|
$arrayClass = $vocab->getConfig()->getArrayClassURI(); |
|
272
|
|
|
$vocabs = array($vocab); |
|
273
|
|
|
} else { |
|
274
|
|
|
$sparql = $this->getDefaultSparql(); |
|
275
|
|
|
$arrayClass = null; |
|
276
|
|
|
$vocabs = null; |
|
277
|
|
|
} |
|
278
|
|
|
$result = $sparql->queryConceptInfoGraph($uri, $arrayClass, $vocabs); |
|
279
|
|
|
|
|
280
|
|
|
if (!$result->isEmpty()) { |
|
281
|
|
|
return $serialiser->serialise($result, $retform); |
|
282
|
|
|
} |
|
283
|
|
|
} |
|
284
|
|
|
|
|
285
|
|
|
/** |
|
286
|
|
|
* Makes a SPARQL-query to the endpoint that retrieves concept |
|
287
|
|
|
* references as it's search results. |
|
288
|
|
|
* @param ConceptSearchParameters $params an object that contains all the parameters needed for the search |
|
289
|
|
|
* @return array search results |
|
290
|
|
|
*/ |
|
291
|
|
|
public function searchConcepts($params) |
|
292
|
|
|
{ |
|
293
|
|
|
// don't even try to search for empty prefix if no other search criteria (group or parent concept) has been set |
|
294
|
|
|
if (($params->getSearchTerm() === "" || !preg_match('/[^*]/', $params->getSearchTerm())) && !$params->getGroupLimit() && !$params->getParentLimit()) { |
|
295
|
|
|
return array(); |
|
296
|
|
|
} |
|
297
|
|
|
|
|
298
|
|
|
$vocabs = $params->getVocabs(); |
|
299
|
|
|
|
|
300
|
|
|
if (sizeof($vocabs) === 1) { // search within vocabulary |
|
301
|
|
|
$voc = $vocabs[0]; |
|
302
|
|
|
$sparql = $voc->getSparql(); |
|
303
|
|
|
} else { // multi-vocabulary or global search |
|
304
|
|
|
$voc = null; |
|
305
|
|
|
$sparql = $this->getDefaultSparql(); |
|
306
|
|
|
} |
|
307
|
|
|
|
|
308
|
|
|
$results = $sparql->queryConcepts($vocabs, $params->getAdditionalFields(), $params->getUnique(), $params); |
|
309
|
|
|
if ($params->getRest() && $results && $params->getSearchLimit() !== 0) { |
|
310
|
|
|
$results = array_slice($results, $params->getOffset(), $params->getSearchLimit()); |
|
311
|
|
|
} |
|
312
|
|
|
$ret = array(); |
|
313
|
|
|
|
|
314
|
|
|
foreach ($results as $hit) { |
|
315
|
|
|
if (sizeof($vocabs) == 1) { |
|
316
|
|
|
$hit['vocab'] = $vocabs[0]->getId(); |
|
317
|
|
|
} else { |
|
318
|
|
|
try { |
|
319
|
|
|
$voc = $this->getVocabularyByGraph($hit['graph']); |
|
320
|
|
|
$hit['vocab'] = $voc->getId(); |
|
321
|
|
|
} catch (Exception $e) { |
|
322
|
|
|
trigger_error($e->getMessage(), E_USER_WARNING); |
|
323
|
|
|
$voc = null; |
|
324
|
|
|
$hit['vocab'] = "???"; |
|
325
|
|
|
} |
|
326
|
|
|
} |
|
327
|
|
|
unset($hit['graph']); |
|
328
|
|
|
|
|
329
|
|
|
$hit['voc'] = $voc; |
|
330
|
|
|
|
|
331
|
|
|
// if uri is a external vocab uri that is included in the current vocab |
|
332
|
|
|
$realvoc = $this->guessVocabularyFromURI($hit['uri']); |
|
333
|
|
|
if ($realvoc != $voc) { |
|
334
|
|
|
unset($hit['localname']); |
|
335
|
|
|
$hit['exvocab'] = ($realvoc !== null) ? $realvoc->getId() : "???"; |
|
336
|
|
|
} |
|
337
|
|
|
|
|
338
|
|
|
$ret[] = $hit; |
|
339
|
|
|
} |
|
340
|
|
|
|
|
341
|
|
|
return $ret; |
|
342
|
|
|
} |
|
343
|
|
|
|
|
344
|
|
|
/** |
|
345
|
|
|
* Function for performing a search for concepts and their data fields. |
|
346
|
|
|
* @param ConceptSearchParameters $params an object that contains all the parameters needed for the search |
|
347
|
|
|
* @return array array with keys 'count' and 'results' |
|
348
|
|
|
*/ |
|
349
|
|
|
public function searchConceptsAndInfo($params) |
|
350
|
|
|
{ |
|
351
|
|
|
$params->setUnique(true); |
|
352
|
|
|
$allhits = $this->searchConcepts($params); |
|
353
|
|
|
$count = sizeof($allhits); |
|
354
|
|
|
$hits = array_slice($allhits, $params->getOffset(), $params->getSearchLimit()); |
|
355
|
|
|
|
|
356
|
|
|
$uris = array(); |
|
357
|
|
|
$vocabs = array(); |
|
358
|
|
|
$uniqueVocabs = array(); |
|
359
|
|
|
foreach ($hits as $hit) { |
|
360
|
|
|
$uniqueVocabs[$hit['voc']->getId()] = $hit['voc']->getId(); |
|
361
|
|
|
$vocabs[] = $hit['voc']; |
|
362
|
|
|
$uris[] = $hit['uri']; |
|
363
|
|
|
} |
|
364
|
|
|
if (sizeof($uniqueVocabs) == 1) { |
|
365
|
|
|
$voc = $vocabs[0]; |
|
366
|
|
|
$sparql = $voc->getSparql(); |
|
367
|
|
|
$arrayClass = $voc->getConfig()->getArrayClassURI(); |
|
368
|
|
|
} else { |
|
369
|
|
|
$arrayClass = null; |
|
370
|
|
|
$sparql = $this->getDefaultSparql(); |
|
371
|
|
|
} |
|
372
|
|
|
$ret = $sparql->queryConceptInfo($uris, $arrayClass, $vocabs, $params->getSearchLang()); |
|
373
|
|
|
|
|
374
|
|
|
// For marking that the concept has been found through an alternative label, hidden |
|
375
|
|
|
// label or a label in another language |
|
376
|
|
|
foreach ($hits as $idx => $hit) { |
|
377
|
|
|
if (isset($hit['altLabel']) && isset($ret[$idx])) { |
|
378
|
|
|
$ret[$idx]->setFoundBy($hit['altLabel'], 'alt'); |
|
379
|
|
|
} |
|
380
|
|
|
|
|
381
|
|
|
if (isset($hit['hiddenLabel']) && isset($ret[$idx])) { |
|
382
|
|
|
$ret[$idx]->setFoundBy($hit['hiddenLabel'], 'hidden'); |
|
383
|
|
|
} |
|
384
|
|
|
|
|
385
|
|
|
if (isset($hit['matchedPrefLabel'])) { |
|
386
|
|
|
$ret[$idx]->setFoundBy($hit['matchedPrefLabel'], 'lang'); |
|
387
|
|
|
} |
|
388
|
|
|
|
|
389
|
|
|
if ($ret[$idx] && isset($hit['lang'])) { |
|
390
|
|
|
$ret[$idx]->setContentLang($hit['lang']); |
|
391
|
|
|
} |
|
392
|
|
|
|
|
393
|
|
|
} |
|
394
|
|
|
|
|
395
|
|
|
return array('count' => $count, 'results' => $ret); |
|
396
|
|
|
} |
|
397
|
|
|
|
|
398
|
|
|
/** |
|
399
|
|
|
* Creates dataobjects from an input array. |
|
400
|
|
|
* @param string $class the type of class eg. 'Vocabulary'. |
|
401
|
|
|
* @param array $resarr contains the EasyRdf_Resources. |
|
402
|
|
|
*/ |
|
403
|
|
|
private function createDataObjects($class, $resarr) |
|
404
|
|
|
{ |
|
405
|
|
|
$ret = array(); |
|
406
|
|
|
foreach ($resarr as $res) { |
|
407
|
|
|
$ret[] = new $class($this, $res); |
|
408
|
|
|
} |
|
409
|
|
|
|
|
410
|
|
|
return $ret; |
|
411
|
|
|
} |
|
412
|
|
|
|
|
413
|
|
|
/** |
|
414
|
|
|
* Returns the cached vocabularies. |
|
415
|
|
|
* @return array of Vocabulary dataobjects |
|
416
|
|
|
*/ |
|
417
|
|
|
public function getVocabularies() |
|
418
|
|
|
{ |
|
419
|
|
|
if ($this->allVocabularies === null) { // initialize cache |
|
420
|
|
|
$vocs = $this->graph->allOfType('skosmos:Vocabulary'); |
|
421
|
|
|
$this->allVocabularies = $this->createDataObjects("Vocabulary", $vocs); |
|
422
|
|
|
foreach ($this->allVocabularies as $voc) { |
|
423
|
|
|
// register vocabulary ids as RDF namespace prefixes |
|
424
|
|
|
$prefix = preg_replace('/\W+/', '', $voc->getId()); // strip non-word characters |
|
425
|
|
|
try { |
|
426
|
|
|
if ($prefix != '' && EasyRdf_Namespace::get($prefix) === null) // if not already defined |
|
427
|
|
|
{ |
|
428
|
|
|
EasyRdf_Namespace::set($prefix, $voc->getUriSpace()); |
|
429
|
|
|
} |
|
430
|
|
|
|
|
431
|
|
|
} catch (Exception $e) { |
|
432
|
|
|
// not valid as namespace identifier, ignore |
|
433
|
|
|
} |
|
434
|
|
|
} |
|
435
|
|
|
} |
|
436
|
|
|
|
|
437
|
|
|
return $this->allVocabularies; |
|
438
|
|
|
} |
|
439
|
|
|
|
|
440
|
|
|
/** |
|
441
|
|
|
* Returns the cached vocabularies from a category. |
|
442
|
|
|
* @param EasyRdf_Resource $cat the category in question |
|
443
|
|
|
* @return array of vocabulary dataobjects |
|
444
|
|
|
*/ |
|
445
|
|
|
public function getVocabulariesInCategory($cat) |
|
446
|
|
|
{ |
|
447
|
|
|
$vocs = $this->graph->resourcesMatching('dc:subject', $cat); |
|
448
|
|
|
|
|
449
|
|
|
return $this->createDataObjects("Vocabulary", $vocs); |
|
450
|
|
|
} |
|
451
|
|
|
|
|
452
|
|
|
/** |
|
453
|
|
|
* Creates dataobjects of all the different vocabulary categories (Health etc.). |
|
454
|
|
|
* @return array of Dataobjects of the type VocabularyCategory. |
|
455
|
|
|
*/ |
|
456
|
|
|
public function getVocabularyCategories() |
|
457
|
|
|
{ |
|
458
|
|
|
$cats = $this->graph->allOfType('skos:Concept'); |
|
459
|
|
|
if(empty($cats)) { |
|
460
|
|
|
return array(new VocabularyCategory($this, null)); |
|
461
|
|
|
} |
|
462
|
|
|
|
|
463
|
|
|
return $this->createDataObjects("VocabularyCategory", $cats); |
|
464
|
|
|
} |
|
465
|
|
|
|
|
466
|
|
|
/** |
|
467
|
|
|
* Returns the label defined in vocabularies.ttl with the appropriate language. |
|
468
|
|
|
* @param string $lang language code of returned labels, eg. 'fi' |
|
469
|
|
|
* @return string the label for vocabulary categories. |
|
470
|
|
|
*/ |
|
471
|
|
|
public function getClassificationLabel($lang) |
|
472
|
|
|
{ |
|
473
|
|
|
$cats = $this->graph->allOfType('skos:ConceptScheme'); |
|
474
|
|
|
$label = $cats ? $cats[0]->label($lang) : null; |
|
475
|
|
|
|
|
476
|
|
|
return $label; |
|
477
|
|
|
} |
|
478
|
|
|
|
|
479
|
|
|
/** |
|
480
|
|
|
* Returns a single cached vocabulary. |
|
481
|
|
|
* @param string $vocid the vocabulary id eg. 'mesh'. |
|
482
|
|
|
* @return vocabulary dataobject |
|
483
|
|
|
*/ |
|
484
|
|
|
public function getVocabulary($vocid) |
|
485
|
|
|
{ |
|
486
|
|
|
$vocs = $this->getVocabularies(); |
|
487
|
|
|
foreach ($vocs as $voc) { |
|
488
|
|
|
if ($voc->getId() == $vocid) { |
|
489
|
|
|
return $voc; |
|
490
|
|
|
} |
|
491
|
|
|
} |
|
492
|
|
|
throw new Exception("Vocabulary id '$vocid' not found in configuration."); |
|
493
|
|
|
} |
|
494
|
|
|
|
|
495
|
|
|
/** |
|
496
|
|
|
* Return the vocabulary that is stored in the given graph on the given endpoint. |
|
497
|
|
|
* |
|
498
|
|
|
* @param $graph string graph URI |
|
499
|
|
|
* @param $endpoint string endpoint URL (default SPARQL endpoint if omitted) |
|
500
|
|
|
* @return Vocabulary vocabulary of this URI, or null if not found |
|
501
|
|
|
*/ |
|
502
|
|
|
public function getVocabularyByGraph($graph, $endpoint = null) |
|
503
|
|
|
{ |
|
504
|
|
|
if ($endpoint === null) { |
|
505
|
|
|
$endpoint = $this->getConfig()->getDefaultEndpoint(); |
|
506
|
|
|
} |
|
507
|
|
|
if ($this->vocabsByGraph === null) { // initialize cache |
|
508
|
|
|
$this->vocabsByGraph = array(); |
|
509
|
|
|
foreach ($this->getVocabularies() as $voc) { |
|
510
|
|
|
$key = json_encode(array($voc->getGraph(), $voc->getEndpoint())); |
|
511
|
|
|
$this->vocabsByGraph[$key] = $voc; |
|
512
|
|
|
} |
|
513
|
|
|
} |
|
514
|
|
|
|
|
515
|
|
|
$key = json_encode(array($graph, $endpoint)); |
|
516
|
|
|
if (array_key_exists($key, $this->vocabsByGraph)) { |
|
517
|
|
|
return $this->vocabsByGraph[$key]; |
|
518
|
|
|
} else { |
|
519
|
|
|
throw new Exception("no vocabulary found for graph $graph and endpoint $endpoint"); |
|
520
|
|
|
} |
|
521
|
|
|
|
|
522
|
|
|
} |
|
523
|
|
|
|
|
524
|
|
|
/** |
|
525
|
|
|
* When multiple vocabularies share same URI namespace, return the |
|
526
|
|
|
* vocabulary in which the URI is actually defined (has a label). |
|
527
|
|
|
* |
|
528
|
|
|
* @param Vocabulary[] $vocabs vocabularies to search |
|
529
|
|
|
* @param string $uri URI to look for |
|
530
|
|
|
* @return Vocabulary the vocabulary with the URI |
|
531
|
|
|
*/ |
|
532
|
|
|
|
|
533
|
|
|
private function disambiguateVocabulary($vocabs, $uri) |
|
534
|
|
|
{ |
|
535
|
|
|
// if there is only one candidate vocabulary, return it |
|
536
|
|
|
if (sizeof($vocabs) == 1) { |
|
537
|
|
|
return $vocabs[0]; |
|
538
|
|
|
} |
|
539
|
|
|
|
|
540
|
|
|
foreach ($vocabs as $vocab) { |
|
541
|
|
|
if ($vocab->getConceptLabel($uri, null) !== null) |
|
542
|
|
|
return $vocab; |
|
543
|
|
|
} |
|
544
|
|
|
|
|
545
|
|
|
// if the URI couldn't be found, fall back to the first vocabulary |
|
546
|
|
|
return $vocabs[0]; |
|
547
|
|
|
} |
|
548
|
|
|
|
|
549
|
|
|
/** |
|
550
|
|
|
* Guess which vocabulary a URI originates from, based on the declared |
|
551
|
|
|
* vocabulary URI spaces. |
|
552
|
|
|
* |
|
553
|
|
|
* @param $uri string URI to search |
|
554
|
|
|
* @return Vocabulary vocabulary of this URI, or null if not found |
|
555
|
|
|
*/ |
|
556
|
|
|
public function guessVocabularyFromURI($uri) |
|
557
|
|
|
{ |
|
558
|
|
|
if ($this->vocabsByUriSpace === null) { // initialize cache |
|
559
|
|
|
$this->vocabsByUriSpace = array(); |
|
560
|
|
|
foreach ($this->getVocabularies() as $voc) { |
|
561
|
|
|
$this->vocabsByUriSpace[$voc->getUriSpace()][] = $voc; |
|
562
|
|
|
} |
|
563
|
|
|
} |
|
564
|
|
|
|
|
565
|
|
|
// try to guess the URI space and look it up in the cache |
|
566
|
|
|
$res = new EasyRdf_Resource($uri); |
|
567
|
|
|
$namespace = substr($uri, 0, -strlen($res->localName())); |
|
568
|
|
|
if (array_key_exists($namespace, $this->vocabsByUriSpace)) { |
|
569
|
|
|
$vocabs = $this->vocabsByUriSpace[$namespace]; |
|
570
|
|
|
return $this->disambiguateVocabulary($vocabs, $uri); |
|
571
|
|
|
} |
|
572
|
|
|
|
|
573
|
|
|
// didn't work, try to match with each URI space separately |
|
574
|
|
|
foreach ($this->vocabsByUriSpace as $urispace => $vocabs) { |
|
575
|
|
|
if (strpos($uri, $urispace) === 0) { |
|
576
|
|
|
return $this->disambiguateVocabulary($vocabs, $uri); |
|
577
|
|
|
} |
|
578
|
|
|
} |
|
579
|
|
|
|
|
580
|
|
|
// not found |
|
581
|
|
|
return null; |
|
582
|
|
|
} |
|
583
|
|
|
|
|
584
|
|
|
/** |
|
585
|
|
|
* Get the label for a resource, preferring 1. the given language 2. configured languages 3. any language. |
|
586
|
|
|
* @param EasyRdf_Resource $res resource whose label to return |
|
587
|
|
|
* @param string $lang preferred language |
|
588
|
|
|
* @return EasyRdf_Literal label as an EasyRdf_Literal object, or null if not found |
|
589
|
|
|
*/ |
|
590
|
|
|
public function getResourceLabel($res, $lang) |
|
591
|
|
|
{ |
|
592
|
|
|
$langs = array_merge(array($lang), array_keys($this->getConfig()->getLanguages())); |
|
593
|
|
|
foreach ($langs as $l) { |
|
594
|
|
|
$label = $res->label($l); |
|
595
|
|
|
if ($label !== null) { |
|
596
|
|
|
return $label; |
|
597
|
|
|
} |
|
598
|
|
|
|
|
599
|
|
|
} |
|
600
|
|
|
return $res->label(); // desperate check for label in any language; will return null if even this fails |
|
601
|
|
|
} |
|
602
|
|
|
|
|
603
|
|
|
private function fetchResourceFromUri($uri) |
|
604
|
|
|
{ |
|
605
|
|
|
try { |
|
606
|
|
|
// change the timeout setting for external requests |
|
607
|
|
|
$httpclient = EasyRdf_Http::getDefaultHttpClient(); |
|
608
|
|
|
$httpclient->setConfig(array('timeout' => $this->getConfig()->getHttpTimeout())); |
|
609
|
|
|
EasyRdf_Http::setDefaultHttpClient($httpclient); |
|
610
|
|
|
|
|
611
|
|
|
$client = EasyRdf_Graph::newAndLoad(EasyRdf_Utils::removeFragmentFromUri($uri)); |
|
612
|
|
|
return $client->resource($uri); |
|
613
|
|
|
} catch (Exception $e) { |
|
614
|
|
|
return null; |
|
615
|
|
|
} |
|
616
|
|
|
} |
|
617
|
|
|
|
|
618
|
|
|
public function getResourceFromUri($uri) |
|
619
|
|
|
{ |
|
620
|
|
|
// prevent parsing errors for sources which return invalid JSON (see #447) |
|
621
|
|
|
// 1. Unregister the legacy RDF/JSON parser, we don't want to use it |
|
622
|
|
|
EasyRdf_Format::unregister('json'); |
|
623
|
|
|
// 2. Add "application/json" as a possible MIME type for the JSON-LD format |
|
624
|
|
|
$jsonld = EasyRdf_Format::getFormat('jsonld'); |
|
625
|
|
|
$mimetypes = $jsonld->getMimeTypes(); |
|
626
|
|
|
$mimetypes['application/json'] = 0.5; |
|
627
|
|
|
$jsonld->setMimeTypes($mimetypes); |
|
628
|
|
|
|
|
629
|
|
|
// using apc cache for the resource if available |
|
630
|
|
|
if ($this->cache->isAvailable()) { |
|
631
|
|
|
// @codeCoverageIgnoreStart |
|
632
|
|
|
$key = 'fetch: ' . EasyRdf_Utils::removeFragmentFromUri($uri); |
|
633
|
|
|
$resource = $this->cache->fetch($key); |
|
634
|
|
|
if ($resource === null || $resource === false) { // was not found in cache, or previous request failed |
|
635
|
|
|
$resource = $this->fetchResourceFromUri($uri); |
|
636
|
|
|
$this->cache->store($key, $resource, self::URI_FETCH_TTL); |
|
637
|
|
|
} |
|
638
|
|
|
// @codeCoverageIgnoreEnd |
|
639
|
|
|
} else { // APC not available, parse on every request |
|
640
|
|
|
$resource = $this->fetchResourceFromUri($uri); |
|
641
|
|
|
} |
|
642
|
|
|
return $resource; |
|
643
|
|
|
} |
|
644
|
|
|
|
|
645
|
|
|
/** |
|
646
|
|
|
* Returns a SPARQL endpoint object. |
|
647
|
|
|
* @param string $dialect eg. 'JenaText'. |
|
648
|
|
|
* @param string $endpoint url address of endpoint |
|
649
|
|
|
* @param string $graph uri for the target graph. |
|
650
|
|
|
*/ |
|
651
|
|
|
public function getSparqlImplementation($dialect, $endpoint, $graph) |
|
652
|
|
|
{ |
|
653
|
|
|
$classname = $dialect . "Sparql"; |
|
654
|
|
|
|
|
655
|
|
|
return new $classname($endpoint, $graph, $this); |
|
656
|
|
|
} |
|
657
|
|
|
|
|
658
|
|
|
/** |
|
659
|
|
|
* Returns a SPARQL endpoint object using the default implementation set in the config.inc. |
|
660
|
|
|
*/ |
|
661
|
|
|
public function getDefaultSparql() |
|
662
|
|
|
{ |
|
663
|
|
|
return $this->getSparqlImplementation($this->getConfig()->getDefaultSparqlDialect(), $this->getConfig()->getDefaultEndpoint(), '?graph'); |
|
664
|
|
|
} |
|
665
|
|
|
|
|
666
|
|
|
} |
|
667
|
|
|
|
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.