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