Passed
Pull Request — master (#1412)
by Jakob
05:09
created

GlobalConfig::configResource()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 4
nc 2
nop 2
dl 0
loc 6
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Setting some often needed namespace prefixes
5
 */
6
EasyRdf\RdfNamespace::set('skosmos', 'http://purl.org/net/skosmos#');
7
EasyRdf\RdfNamespace::set('skosext', 'http://purl.org/finnonto/schema/skosext#');
8
EasyRdf\RdfNamespace::delete('geo');
9
EasyRdf\RdfNamespace::set('wgs84', 'http://www.w3.org/2003/01/geo/wgs84_pos#');
10
EasyRdf\RdfNamespace::set('isothes', 'http://purl.org/iso25964/skos-thes#');
11
EasyRdf\RdfNamespace::set('mads', 'http://www.loc.gov/mads/rdf/v1#');
12
EasyRdf\RdfNamespace::set('wd', 'http://www.wikidata.org/entity/');
13
EasyRdf\RdfNamespace::set('wdt', 'http://www.wikidata.org/prop/direct/');
14
15
/**
16
 * GlobalConfig provides access to the Skosmos configuration in config.ttl.
17
 */
18
class GlobalConfig extends BaseConfig {
19
20
    /** Cache reference */
21
    private $cache;
22
    /** Location of the configuration file. Used for caching. */
23
    private $filePath;
24
    /** Namespaces from vocabularies configuration file. */
25
    private $namespaces;
26
    /** EasyRdf\Graph graph */
27
    private $graph;
28
    /**
29
     * @var int the time the config file was last modified
30
     */
31
    private $configModifiedTime = null;
32
33
    public function __construct($config_name='/../config.ttl')
34
    {
35
        $this->cache = new Cache();
36
        try {
37
            $this->filePath = realpath( dirname(__FILE__) . $config_name );
38
            if (!file_exists($this->filePath)) {
39
                throw new Exception('config.ttl file is missing, please provide one.');
40
            }
41
            $this->initializeConfig();
42
        } catch (Exception $e) {
43
            echo "Error: " . $e->getMessage();
44
            return;
45
        }
46
    }
47
48
    public function getCache()
49
    {
50
        return $this->cache;
51
    }
52
53
    /**
54
     * @return int the time the config file was last modified
55
     */
56
    public function getConfigModifiedTime()
57
    {
58
        return $this->configModifiedTime;
59
    }
60
61
    /**
62
     * Initialize configuration, reading the configuration file from the disk,
63
     * and creating the graph and resources objects. Uses a cache if available,
64
     * in order to avoid re-loading the complete configuration on each request.
65
     */
66
    private function initializeConfig()
67
    {
68
        try {
69
            // retrieve last modified time for config file (filemtime returns int|bool!)
70
            $configModifiedTime = filemtime($this->filePath);
71
            if (!is_bool($configModifiedTime)) {
0 ignored issues
show
introduced by
The condition is_bool($configModifiedTime) is always false.
Loading history...
72
                $this->configModifiedTime = $configModifiedTime;
73
            }
74
            // use APC user cache to store parsed config.ttl configuration
75
            if ($this->cache->isAvailable() && !is_null($this->configModifiedTime)) {
76
                // @codeCoverageIgnoreStart
77
                $key = realpath($this->filePath) . ", " . $this->configModifiedTime;
78
                $nskey = "namespaces of " . $key;
79
                $this->graph = $this->cache->fetch($key);
80
                $this->namespaces = $this->cache->fetch($nskey);
81
                if ($this->graph && $this->namespaces) { // found in cache
82
                    $this->resource = $this->configResource($this->graph, "cache");
83
                } else {
84
                    $this->parseConfig($this->filePath);
85
                    $this->cache->store($key, $this->graph);
86
                    $this->cache->store($nskey, $this->namespaces);
87
                }
88
                // @codeCoverageIgnoreEnd
89
            } else { // APC not available, parse on every request
90
                $this->parseConfig($this->filePath);
91
            }
92
93
            $this->initializeNamespaces();
94
        } catch (Exception $e) {
95
            echo "Error: " . $e->getMessage();
96
        }      
97
    }
98
99
    /**
100
     * Ensure there is exactely one skosmos:Configuration and return it.
101
     */
102
    private function configResource($graph, $source) {
103
        $configResources = $graph->allOfType("skosmos:Configuration");
104
        if (is_null($configResources) || !is_array($configResources) || count($configResources) !== 1) {
105
            throw new Exception("$source must have exactly one skosmos:Configuration");
106
        }
107
        return $configResources[0];
108
    }
109
110
    /**
111
     * Retrieves, parses and includes configuration in existing configuration.
112
     * @param \EasyRdf\Resource URL or file of configuration in Turtle syntax.
0 ignored issues
show
Bug introduced by
The type URL was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
113
     */
114
    private function includeConfig($location) {
115
        $location = $location->getUri();
116
117
        if (str_starts_with($location, "http://") || str_starts_with($location, "https://")) {
118
            $ch = curl_init($location);
119
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
120
            curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type: text/turtle'));
121
            $turtle = curl_exec($ch);
122
            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
123
            if ($httpCode != 200 && $httpCode != 303) {
124
                throw new Exception("Failed to include configuration from $location");
125
            }
126
            curl_close($ch);
127
        } else {
128
            if (file_exists($location)) {
129
                $turtle = file_get_contents($location);
130
            } else {
131
                throw new Exception("Included config file $location does not exist!");
132
            }
133
        }
134
135
        $parser = new SkosmosTurtleParser();
136
        try {
137
            $graph = $parser->parseGraph($turtle, $location);
138
        } catch (Exception $e) {
139
            throw new Exception("Failed to parse $location: " . $e->getMessage());
140
        }
141
142
        // Add triples from included configuration, adjust :config resource
143
        $configResource = $this->configResource($graph, $location);
144
        foreach($graph->resources() as $resource) {
145
            $subject = $resource == $configResource ? $this->resource : $resource;
146
            foreach($graph->properties($resource) as $property) {
147
                foreach($resource->all($property) as $value) {
148
                    $this->graph->add($subject, $property, $value);
149
                }
150
            }
151
        }
152
    }
153
154
    /**
155
     * Parses configuration from the config.ttl file
156
     * @param string $filename path to config.ttl file
157
     * @throws \EasyRdf\Exception
158
     */
159
    private function parseConfig($filename)
160
    {
161
        $parser = new SkosmosTurtleParser();
162
        $this->graph = $parser->parseGraph(file_get_contents($filename), $filename);
163
        $this->namespaces = $parser->getNamespaces();
164
165
        $this->resource = $this->configResource($this->graph, $filename);
166
167
        $includes = $this->graph->allResources($this->resource, "skosmos:includeConfig");
168
        foreach($includes as $location) {
169
            $this->includeConfig($location);
170
        }
171
    }
172
173
    /**
174
     * Returns the graph created after parsing the configuration file.
175
     * @return \EasyRdf\Graph
176
     */
177
    public function getGraph()
178
    {
179
        return $this->graph;
180
    }
181
182
    /**
183
     * Registers RDF namespaces from the config.ttl file for use by EasyRdf (e.g. serializing)
184
     */
185
    private function initializeNamespaces() {
186
        foreach ($this->namespaces as $prefix => $fullUri) {
187
            if ($prefix != '' && EasyRdf\RdfNamespace::get($prefix) === null) // if not already defined
188
            {
189
                EasyRdf\RdfNamespace::set($prefix, $fullUri);
190
            }
191
        }
192
    }
193
194
    /**
195
     * Returns the UI languages specified in the configuration or defaults to
196
     * only show English
197
     * @return array
198
     */
199
    public function getLanguages()
200
    {
201
        $languageResources = $this->getResource()->getResource('skosmos:languages');
202
        if (!is_null($languageResources) && !empty($languageResources)) {
203
            $languages = array();
204
            foreach ($languageResources as $languageResource) {
205
                /** @var \EasyRdf\Literal $languageName */
206
                $languageName = $languageResource->getLiteral('rdfs:label');
207
                /** @var \EasyRdf\Literal $languageValue */
208
                $languageValue = $languageResource->getLiteral('rdf:value');
209
                if ($languageName && $languageValue) {
210
                    $languages[$languageName->getValue()] = $languageValue->getValue();
211
                }
212
            }
213
            return $languages;
214
        } else {
215
            return array('en' => 'en_GB.utf8');
216
        }
217
    }
218
219
    /**
220
     * Returns the external HTTP request timeout in seconds or the default value
221
     * of 5 seconds if not specified in the configuration.
222
     * @return integer
223
     */
224
    public function getHttpTimeout()
225
    {
226
        return $this->getLiteral('skosmos:httpTimeout', 5);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getLiteral...kosmos:httpTimeout', 5) returns the type string which is incompatible with the documented return type integer.
Loading history...
227
    }
228
229
    /**
230
     * Returns the SPARQL HTTP request timeout in seconds or the default value
231
     * of 20 seconds if not specified in the configuration.
232
     * @return integer
233
     */
234
    public function getSparqlTimeout()
235
    {
236
        return $this->getLiteral('skosmos:sparqlTimeout', 20);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getLiteral...mos:sparqlTimeout', 20) returns the type string which is incompatible with the documented return type integer.
Loading history...
237
    }
238
239
    /**
240
     * Returns the sparql endpoint address defined in the configuration. If
241
     * not then defaulting to http://localhost:3030/ds/sparql
242
     * @return string
243
     */
244
    public function getDefaultEndpoint()
245
    {
246
        $endpoint = $this->resource->get('skosmos:sparqlEndpoint');
247
        if ($endpoint) {
248
            return $endpoint->getUri();
249
        } elseif (getenv('SKOSMOS_SPARQL_ENDPOINT')) {
250
            return getenv('SKOSMOS_SPARQL_ENDPOINT');
251
        } else {
252
            return 'http://localhost:3030/ds/sparql';
253
        }
254
    }
255
256
    /**
257
     * Returns the maximum number of items to return in transitive queries if defined
258
     * in the configuration or the default value of 1000.
259
     * @return integer
260
     */
261
    public function getDefaultTransitiveLimit()
262
    {
263
        return $this->getLiteral('skosmos:transitiveLimit', 1000);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getLiteral...transitiveLimit', 1000) returns the type string which is incompatible with the documented return type integer.
Loading history...
264
    }
265
266
    /**
267
     * Returns the maximum number of items to load at a time if defined
268
     * in the configuration or the default value of 20.
269
     * @return integer
270
     */
271
    public function getSearchResultsSize()
272
    {
273
        return $this->getLiteral('skosmos:searchResultsSize', 20);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getLiteral...searchResultsSize', 20) returns the type string which is incompatible with the documented return type integer.
Loading history...
274
    }
275
276
    /**
277
     * Returns the configured location for the twig template cache and if not
278
     * defined defaults to "/tmp/skosmos-template-cache"
279
     * @return string
280
     */
281
    public function getTemplateCache()
282
    {
283
        return $this->getLiteral('skosmos:templateCache', '/tmp/skosmos-template-cache');
284
    }
285
286
    /**
287
     * Returns the defined sparql-query extension eg. "JenaText" or
288
     * if not defined falling back to SPARQL 1.1
289
     * @return string
290
     */
291
    public function getDefaultSparqlDialect()
292
    {
293
        return $this->getLiteral('skosmos:sparqlDialect', 'Generic');
294
    }
295
296
    /**
297
     * Returns the feedback address defined in the configuration.
298
     * @return string
299
     */
300
    public function getFeedbackAddress()
301
    {
302
        return $this->getLiteral('skosmos:feedbackAddress', null);
303
    }
304
305
    /**
306
     * Returns the feedback sender address defined in the configuration.
307
     * @return string
308
     */
309
    public function getFeedbackSender()
310
    {
311
        return $this->getLiteral('skosmos:feedbackSender', null);
312
    }
313
314
    /**
315
     * Returns the feedback envelope sender address defined in the configuration.
316
     * @return string
317
     */
318
    public function getFeedbackEnvelopeSender()
319
    {
320
        return $this->getLiteral('skosmos:feedbackEnvelopeSender', null);
321
    }
322
323
    /**
324
     * Returns true if exception logging has been configured.
325
     * @return boolean
326
     */
327
    public function getLogCaughtExceptions()
328
    {
329
        return $this->getBoolean('skosmos:logCaughtExceptions', FALSE);
330
    }
331
332
    /**
333
     * Returns true if browser console logging has been enabled,
334
     * @return boolean
335
     */
336
    public function getLoggingBrowserConsole()
337
    {
338
        return $this->getBoolean('skosmos:logBrowserConsole', FALSE);
339
    }
340
341
    /**
342
     * Returns the name of a log file if configured, or NULL otherwise.
343
     * @return string
344
     */
345
    public function getLoggingFilename()
346
    {
347
        return $this->getLiteral('skosmos:logFileName', null);
348
    }
349
350
    /**
351
     * @return string
352
     */
353
    public function getServiceName()
354
    {
355
        return $this->getLiteral('skosmos:serviceName', 'Skosmos');
356
    }
357
358
    /**
359
     * @return string
360
     */
361
    public function getCustomCss()
362
    {
363
        return $this->getLiteral('skosmos:customCss', null);
364
    }
365
366
    /**
367
     * @return boolean
368
     */
369
    public function getUiLanguageDropdown()
370
    {
371
        return $this->getBoolean('skosmos:uiLanguageDropdown', FALSE);
372
    }
373
374
    /**
375
     * @return string
376
     */
377
    public function getBaseHref()
378
    {
379
        return $this->getLiteral('skosmos:baseHref', null);
380
    }
381
382
    /**
383
     * @return array
384
     */
385
    public function getGlobalPlugins()
386
    {
387
        $globalPlugins = array();
388
        $globalPluginsResource =  $this->getResource()->getResource("skosmos:globalPlugins");
389
        if ($globalPluginsResource) {
0 ignored issues
show
introduced by
$globalPluginsResource is of type EasyRdf\Resource, thus it always evaluated to true.
Loading history...
390
            foreach ($globalPluginsResource as $resource) {
391
                $globalPlugins[] = $resource->getValue();
392
            }
393
        }
394
        return $globalPlugins;
395
    }
396
397
    /**
398
     * @return boolean
399
     */
400
    public function getHoneypotEnabled()
401
    {
402
        return $this->getBoolean('skosmos:uiHoneypotEnabled', TRUE);
403
    }
404
405
    /**
406
     * @return integer
407
     */
408
    public function getHoneypotTime()
409
    {
410
        return $this->getLiteral('skosmos:uiHoneypotTime', 5);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getLiteral...mos:uiHoneypotTime', 5) returns the type string which is incompatible with the documented return type integer.
Loading history...
411
    }
412
413
    /**
414
     * @return boolean
415
     */
416
    public function getCollationEnabled()
417
    {
418
        return $this->getBoolean('skosmos:sparqlCollationEnabled', FALSE);
419
    }
420
}
421