Passed
Pull Request — main (#1866)
by
unknown
04:06
created

GlobalConfig::initializeNamespaces()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 3
nc 3
nop 0
dl 0
loc 5
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
class ConfigFileNotFoundException extends Exception
16
{
17
    public function __construct(string $path, int $code = 0, ?Throwable $previous = null)
18
    {
19
        $message = "Config file '$path' is missing, please provide one.";
20
        parent::__construct($message, $code, $previous);
21
    }
22
}
23
24
/**
25
 * GlobalConfig provides access to the Skosmos configuration in config.ttl.
26
 */
27
class GlobalConfig extends BaseConfig
28
{
29
    /** Cache reference */
30
    private $cache;
31
    /** Location of the configuration file. Used for caching. */
32
    private $filePath;
33
    /** Namespaces from vocabularies configuration file. */
34
    private $namespaces;
35
    /** EasyRdf\Graph graph */
36
    private $graph;
37
    /**
38
     * @var int the time the config file was last modified
39
     */
40
    private $configModifiedTime = null;
41
42
    private static function getCheckedConfigFileRealPath(string $path): string
43
    {
44
        if (str_starts_with($path, '/')) {
45
            $realpath = realpath($path);
46
        } else {
47
            $realpath = realpath(dirname(__FILE__) . "/" . $path);
48
        }
49
        if (!$realpath) {
50
            throw new ConfigFileNotFoundException($path);
51
        }
52
        return $realpath;
53
    }
54
55
    /**
56
     * Gets the configuration file path.
57
     *
58
     * Returns the full path to the configuration file. If a config name is provided,
59
     * it will be used to construct the path. Otherwise, fallback path will be used.
60
     *
61
     * First fallback is the `SKOSMOS_CONFIG_NAME` environment variable,
62
     * which accepts two formats:
63
     * - Absolute path: A full path to the configuration file (e.g., /etc/skosmos/config.ttl)
64
     * - Relative path: A path relative to the application root (e.g., config/config.ttl)
65
     *
66
     * Second fallback is "config.ttl" path in the root directory.
67
     *
68
     * @param string|null $config_name Optional configuration file name
69
     * @return string The full path to the configuration file. Throws errors on failure,
70
     * e.g. if the file does not exist.
71
     */
72
    public static function getConfigFilePath(?string $config_name = null)
73
    {
74
        $path = '../../config.ttl';
75
        if (isset($config_name)) {
76
            $path = $config_name;
77
        } elseif (getenv('SKOSMOS_CONFIG_NAME')) {
78
            if (str_starts_with(getenv('SKOSMOS_CONFIG_NAME'), '/')) {
79
                $path = getenv('SKOSMOS_CONFIG_NAME');
80
            } else {
81
                $path = dirname(__FILE__) . '/../../' . getenv('SKOSMOS_CONFIG_NAME');
82
            }
83
        }
84
        return GlobalConfig::getCheckedConfigFileRealPath($path);
85
    }
86
87
    public function __construct(Model $model, ?string $config_name = null)
88
    {
89
        $this->cache = new Cache();
90
        $this->filePath = GlobalConfig::getConfigFilePath($config_name);
91
        $resource = $this->initializeConfig();
92
        parent::__construct($model, $resource);
93
    }
94
95
    public function getCache()
96
    {
97
        return $this->cache;
98
    }
99
100
    /**
101
     * @return int the time the config file was last modified
102
     */
103
    public function getConfigModifiedTime()
104
    {
105
        return $this->configModifiedTime;
106
    }
107
108
    /**
109
     * Initialize configuration, reading the configuration file from the disk,
110
     * and creating the graph and resources objects. Uses a cache if available,
111
     * in order to avoid re-loading the complete configuration on each request.
112
     */
113
    private function initializeConfig(): EasyRdf\Resource
114
    {
115
        // retrieve last modified time for config file (filemtime returns int|bool!)
116
        $configModifiedTime = filemtime($this->filePath);
117
        if (!is_bool($configModifiedTime)) {
0 ignored issues
show
introduced by
The condition is_bool($configModifiedTime) is always false.
Loading history...
118
            $this->configModifiedTime = $configModifiedTime;
119
        }
120
        // use APC user cache to store parsed config.ttl configuration
121
        if ($this->cache->isAvailable() && !is_null($this->configModifiedTime)) {
122
            // @codeCoverageIgnoreStart
123
            $key = realpath($this->filePath) . ", " . $this->configModifiedTime;
124
            $nskey = "namespaces of " . $key;
125
            $this->graph = $this->cache->fetch($key);
126
            $this->namespaces = $this->cache->fetch($nskey);
127
            if ($this->graph === false || $this->namespaces === false) { // was not found in cache
128
                $this->parseConfig($this->filePath);
129
                $this->cache->store($key, $this->graph);
130
                $this->cache->store($nskey, $this->namespaces);
131
            }
132
            // @codeCoverageIgnoreEnd
133
        } else { // APC not available, parse on every request
134
            $this->parseConfig($this->filePath);
135
        }
136
        $this->initializeNamespaces();
137
138
        $configResources = $this->graph->allOfType("skosmos:Configuration");
139
        if (is_null($configResources) || !is_array($configResources) || count($configResources) !== 1) {
0 ignored issues
show
introduced by
The condition is_array($configResources) is always true.
Loading history...
140
            throw new Exception("config.ttl must have exactly one skosmos:Configuration");
141
        }
142
        return $configResources[0];
143
    }
144
145
    /**
146
     * Parses configuration from the config.ttl file
147
     * @param string $filename path to config.ttl file
148
     * @throws \EasyRdf\Exception
149
     */
150
    private function parseConfig($filename)
151
    {
152
        $this->graph = new EasyRdf\Graph();
153
        $parser = new SkosmosTurtleParser();
154
        $parser->parse($this->graph, file_get_contents($filename), 'turtle', $filename);
155
        $this->namespaces = $parser->getNamespaces();
156
    }
157
158
    /**
159
     * Returns the graph created after parsing the configuration file.
160
     * @return \EasyRdf\Graph
161
     */
162
    public function getGraph()
163
    {
164
        return $this->graph;
165
    }
166
167
    /**
168
     * Registers RDF namespaces from the config.ttl file for use by EasyRdf (e.g. serializing)
169
     */
170
    private function initializeNamespaces()
171
    {
172
        foreach ($this->namespaces as $prefix => $fullUri) {
173
            if ($prefix != '' && EasyRdf\RdfNamespace::get($prefix) === null) { // if not already defined
174
                EasyRdf\RdfNamespace::set($prefix, $fullUri);
175
            }
176
        }
177
    }
178
179
    /**
180
     * Returns the UI languages specified in the configuration or defaults to
181
     * only show English
182
     * @return array
183
     */
184
    public function getLanguages()
185
    {
186
        $languageResources = $this->getResource()->getResource('skosmos:languages');
187
        if (!is_null($languageResources) && !empty($languageResources)) {
188
            $languages = array();
189
            foreach ($languageResources as $languageResource) {
190
                /** @var \EasyRdf\Literal $languageName */
191
                $languageName = $languageResource->getLiteral('rdfs:label');
192
                /** @var \EasyRdf\Literal $languageValue */
193
                $languageValue = $languageResource->getLiteral('rdf:value');
194
                if ($languageName && $languageValue) {
195
                    $languages[$languageName->getValue()] = $languageValue->getValue();
196
                }
197
            }
198
            return $languages;
199
        } else {
200
            return array('en' => 'en_GB.utf8');
201
        }
202
    }
203
204
    /**
205
     * Returns the external HTTP request timeout in seconds or the default value
206
     * of 5 seconds if not specified in the configuration.
207
     * @return integer
208
     */
209
    public function getHttpTimeout()
210
    {
211
        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...
212
    }
213
214
    /**
215
     * Returns the SPARQL HTTP request timeout in seconds or the default value
216
     * of 20 seconds if not specified in the configuration.
217
     * @return integer
218
     */
219
    public function getSparqlTimeout()
220
    {
221
        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...
222
    }
223
224
    /**
225
     * Returns the sparql endpoint address defined in the configuration. If
226
     * not then defaulting to http://localhost:3030/ds/sparql
227
     * @return string
228
     */
229
    public function getDefaultEndpoint()
230
    {
231
        $endpoint = $this->resource->get('skosmos:sparqlEndpoint');
232
        if ($endpoint) {
233
            return $endpoint->getUri();
234
        } elseif (getenv('SKOSMOS_SPARQL_ENDPOINT')) {
235
            return getenv('SKOSMOS_SPARQL_ENDPOINT');
236
        } else {
237
            return 'http://localhost:3030/ds/sparql';
238
        }
239
    }
240
241
    /**
242
     * Returns the maximum number of items to return in transitive queries if defined
243
     * in the configuration or the default value of 1000.
244
     * @return integer
245
     */
246
    public function getDefaultTransitiveLimit()
247
    {
248
        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...
249
    }
250
251
    /**
252
     * Returns the maximum number of items to load at a time if defined
253
     * in the configuration or the default value of 20.
254
     * @return integer
255
     */
256
    public function getSearchResultsSize()
257
    {
258
        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...
259
    }
260
261
    /**
262
     * Returns the configured location for the twig template cache and if not
263
     * defined defaults to "/tmp/skosmos-template-cache"
264
     * @return string
265
     */
266
    public function getTemplateCache()
267
    {
268
        return $this->getLiteral('skosmos:templateCache', '/tmp/skosmos-template-cache');
269
    }
270
271
    /**
272
     * Returns the defined sparql-query extension eg. "JenaText" or
273
     * if not defined falling back to SPARQL 1.1
274
     * @return string
275
     */
276
    public function getDefaultSparqlDialect()
277
    {
278
        return $this->getLiteral('skosmos:sparqlDialect', 'Generic');
279
    }
280
281
    /**
282
     * Returns the feedback address defined in the configuration.
283
     * @return string
284
     */
285
    public function getFeedbackAddress()
286
    {
287
        return $this->getLiteral('skosmos:feedbackAddress', null);
288
    }
289
290
    /**
291
     * Returns the feedback sender address defined in the configuration.
292
     * @return string
293
     */
294
    public function getFeedbackSender()
295
    {
296
        return $this->getLiteral('skosmos:feedbackSender', null);
297
    }
298
299
    /**
300
     * Returns the feedback envelope sender address defined in the configuration.
301
     * @return string
302
     */
303
    public function getFeedbackEnvelopeSender()
304
    {
305
        return $this->getLiteral('skosmos:feedbackEnvelopeSender', null);
306
    }
307
308
    /**
309
     * Returns true if exception logging has been configured.
310
     * @return boolean
311
     */
312
    public function getLogCaughtExceptions()
313
    {
314
        return $this->getBoolean('skosmos:logCaughtExceptions', false);
315
    }
316
317
    /**
318
     * Returns true if browser console logging has been enabled,
319
     * @return boolean
320
     */
321
    public function getLoggingBrowserConsole()
322
    {
323
        return $this->getBoolean('skosmos:logBrowserConsole', false);
324
    }
325
326
    /**
327
     * Returns the name of a log file if configured, or NULL otherwise.
328
     * @return string
329
     */
330
    public function getLoggingFilename()
331
    {
332
        return $this->getLiteral('skosmos:logFileName', null);
333
    }
334
335
    /**
336
     * @return string
337
     */
338
    public function getServiceName()
339
    {
340
        return $this->getLiteral('skosmos:serviceName', 'Skosmos');
341
    }
342
343
    /**
344
     * Returns the long version of the service name in the requested language.
345
     * @return string the long name of the service
346
     */
347
    public function getServiceNameLong($lang)
348
    {
349
        $val = $this->getLiteral('skosmos:serviceNameLong', false, $lang);
0 ignored issues
show
Bug introduced by
false of type false is incompatible with the type string expected by parameter $default of BaseConfig::getLiteral(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

349
        $val = $this->getLiteral('skosmos:serviceNameLong', /** @scrutinizer ignore-type */ false, $lang);
Loading history...
350
351
        if ($val === false) {
0 ignored issues
show
introduced by
The condition $val === false is always false.
Loading history...
352
            // fall back to short service name if not configured
353
            return $this->getServiceName();
354
        }
355
356
        return $val;
357
    }
358
359
    /**
360
     * Returns the service description in the requested language.
361
     * @return string the description of the service
362
     */
363
    public function getServiceDescription($lang)
364
    {
365
        return $this->getLiteral('skosmos:serviceDescription', null, $lang);
366
    }
367
368
    /**
369
     * Returns the feedback page description in the requested language.
370
     * @return string the description of the feedback page
371
     */
372
    public function getFeedbackDescription($lang)
373
    {
374
        return $this->getLiteral('skosmos:feedbackDescription', null, $lang);
375
    }
376
377
    /**
378
     * Returns the about page description in the requested language.
379
     * @return string the description of the about page
380
     */
381
    public function getAboutDescription($lang)
382
    {
383
        return $this->getLiteral('skosmos:aboutDescription', null, $lang);
384
    }
385
386
    /**
387
     * @return string
388
     */
389
    public function getCustomCss()
390
    {
391
        return $this->getLiteral('skosmos:customCss', null);
392
    }
393
394
    /**
395
     * @return boolean
396
     */
397
    public function getUiLanguageDropdown()
398
    {
399
        return $this->getBoolean('skosmos:uiLanguageDropdown', false);
400
    }
401
402
    /**
403
     * @return boolean
404
     */
405
    public function getUiDevMode()
406
    {
407
        return $this->getBoolean('skosmos:uiDevMode', false);
408
    }
409
410
    /**
411
     * @return string
412
     */
413
    public function getBaseHref()
414
    {
415
        $baseHref = $this->getLiteral('skosmos:baseHref', null);
416
        if ($baseHref) {
417
            return $baseHref;
418
        } elseif (getenv('SKOSMOS_BASE_HREF')) {
419
            return getenv('SKOSMOS_BASE_HREF');
420
        }
421
    }
422
423
    /**
424
     * @return array
425
     */
426
    public function getGlobalPlugins()
427
    {
428
        $globalPlugins = array();
429
        $globalPluginsResource =  $this->getResource()->getResource("skosmos:globalPlugins");
430
        if ($globalPluginsResource) {
0 ignored issues
show
introduced by
$globalPluginsResource is of type EasyRdf\Resource, thus it always evaluated to true.
Loading history...
431
            foreach ($globalPluginsResource as $resource) {
432
                $globalPlugins[] = $resource->getValue();
433
            }
434
        }
435
        return $globalPlugins;
436
    }
437
438
    /**
439
     * @return boolean
440
     */
441
    public function getHoneypotEnabled()
442
    {
443
        return $this->getBoolean('skosmos:feedbackHoneypotEnabled', true);
444
    }
445
446
    /**
447
     * @return integer
448
     */
449
    public function getHoneypotTime()
450
    {
451
        return $this->getLiteral('skosmos:feedbackHoneypotTime', 5);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getLiteral...edbackHoneypotTime', 5) returns the type string which is incompatible with the documented return type integer.
Loading history...
452
    }
453
454
    /**
455
     * @return boolean
456
     */
457
    public function getCollationEnabled()
458
    {
459
        return $this->getBoolean('skosmos:sparqlCollationEnabled', false);
460
    }
461
}
462