Completed
Push — master ( a2b3dc...439a57 )
by Henri
14s
created

Concept::processExternalResource()   B

Complexity

Conditions 4
Paths 3

Size

Total Lines 24
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 24
rs 8.6845
c 0
b 0
f 0
cc 4
eloc 14
nc 3
nop 1
1
<?php
2
3
/**
4
 * Dataobject for a single concept.
5
 */
6
7
class Concept extends VocabularyDataObject
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
8
{
9
    /**
10
     * Stores a label string if the concept has been found through
11
     * a altLabel/label in a another language than the ui.
12
     */
13
    private $foundby;
14
    /** Type of foundby match: 'alt', 'hidden' or 'lang' */
15
    private $foundbytype;
16
    /** the EasyRdf\Graph object of the concept */
17
    private $graph;
18
    private $clang;
19
20
    /** concept properties that should not be shown to users */
21
    private $DELETED_PROPERTIES = array(
22
        'skosext:broaderGeneric', # these are remnants of bad modeling
23
        'skosext:broaderPartitive', #
24
25
        'skos:hiddenLabel', # because it's supposed to be hidden
26
        'skos:prefLabel', # handled separately by getLabel
27
        'rdfs:label', # handled separately by getLabel
28
29
        'skos:topConceptOf', # because it's too technical, not relevant for users
30
        'skos:inScheme', # should be evident in any case
31
        'skos:member', # this shouldn't be shown on the group page
32
        'dc:created', # handled separately
33
        'dc:modified', # handled separately
34
    );
35
36
    /** related concepts that should be shown to users in the appendix */
37
    private $MAPPING_PROPERTIES = array(
38
        'skos:exactMatch',
39
        'skos:narrowMatch',
40
        'skos:broadMatch',
41
        'skos:closeMatch',
42
        'skos:relatedMatch',
43
        'rdfs:seeAlso',
44
        'owl:sameAs',
45
    );
46
47
    /** default external properties we are interested in saving/displaying from mapped external objects */
48
    private $DEFAULT_EXT_PROPERTIES = array(
49
        "dc11:title",
50
        "dcterms:title",
51
        "skos:prefLabel",
52
        "skos:exactMatch",
53
        "skos:closeMatch",
54
        "skos:inScheme",
55
        "rdfs:label",
56
        "rdfs:isDefinedBy",
57
        "owl:sameAs",
58
        "rdf:type",
59
        "void:inDataset",
60
        "void:sparqlEndpoint",
61
        "void:uriLookupEndpoint",
62
        "schema:about",
63
        "schema:description",
64
        "schema:inLanguage",
65
        "schema:name",
66
        "schema:isPartOf",
67
        "wdt:P31",
68
        "wdt:P625"
69
    );
70
71
    /**
72
     * Initializing the concept object requires the following parameters.
73
     * @param Model $model
74
     * @param Vocabulary $vocab
75
     * @param EasyRdf\Resource $resource
76
     * @param EasyRdf\Graph $graph
77
     */
78
    public function __construct($model, $vocab, $resource, $graph, $clang)
79
    {
80
        parent::__construct($model, $vocab, $resource);
81
        $this->order = array("rdf:type", "dc:isReplacedBy", "skos:definition", "skos:broader", "skos:narrower", "skos:related", "skos:altLabel", "skosmos:memberOf", "skos:note", "skos:scopeNote", "skos:historyNote", "rdfs:comment", "dc11:source", "dc:source", "skos:prefLabel");
82
        $this->graph = $graph;
83
        $this->clang = $clang;
84
        // setting the Punic plugins locale for localized datetime conversions
85
        if ($this->clang && $this->clang !== '') {
86
            Punic\Data::setDefaultLocale($clang);
87
        }
88
89
    }
90
91
    /**
92
     * Returns the concept uri.
93
     * @return string
94
     */
95
    public function getUri()
96
    {
97
        return $this->resource->getUri();
98
    }
99
100
    public function getType()
101
    {
102
        return $this->resource->types();
103
    }
104
105
106
    /**
107
     * Returns a boolean value indicating whether the resource is a group defined in the vocab config as skosmos:groupClass.
108
     * @return boolean
109
     */
110
    public function isGroup() {
111
        $groupClass = $this->getVocab()->getConfig()->getGroupClassURI();
112
        if ($groupClass) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $groupClass of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
113
            $groupClass = EasyRdf\RdfNamespace::shorten($groupClass) !== null ? EasyRdf\RdfNamespace::shorten($groupClass) : $groupClass;
114
            return in_array($groupClass, $this->getType());
115
        }
116
        return false;
117
    }
118
119
    /**
120
     * Returns a boolean value indicating if the concept has been deprecated.
121
     * @return boolean
122
     */
123
    public function getDeprecated()
124
    {
125
        $deprecatedValue = $this->resource->getLiteral('owl:deprecated');
126
        return ($deprecatedValue !== null && filter_var($deprecatedValue->getValue(), FILTER_VALIDATE_BOOLEAN));
127
    }
128
129
    /**
130
     * Returns a label for the concept in the content language or if not possible in any language.
131
     * @return string
132
     */
133
    public function getLabel()
134
    {
135 View Code Duplication
        foreach ($this->vocab->getConfig()->getLanguageOrder($this->clang) as $fallback) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
136
            if ($this->resource->label($fallback) !== null) {
137
                return $this->resource->label($fallback);
138
            }
139
            // We need to check all the labels in case one of them matches a subtag of the current language
140
            foreach($this->resource->allLiterals('skos:prefLabel') as $label) {
141
                // the label lang code is a subtag of the UI lang eg. en-GB - create a new literal with the main language
142
                if ($label !== null && strpos($label->getLang(), $fallback . '-') === 0) {
143
                    return EasyRdf\Literal::create($label, $fallback);
144
                }
145
            }
146
        }
147
148
        // Last resort: label in any language, including literal with empty language tag
149
        $label = $this->resource->label();
150
        if ($label !== null) {
151
            if (!$label->getLang()) {
152
                return $label->getValue();
153
            }
154
            return EasyRdf\Literal::create($label->getValue(), $label->getLang());
155
        }
156
157
        // empty
158
        return "";
159
    }
160
161
    public function hasXlLabel($prop = 'prefLabel')
162
    {
163
        if ($this->resource->hasProperty('skosxl:' . $prop)) {
164
            return true;
165
        }
166
        return false;
167
    }
168
169
    public function getXlLabel()
170
    {
171
        $labels = $this->resource->allResources('skosxl:prefLabel');
172
        foreach($labels as $labres) {
173
            $label = $labres->getLiteral('skosxl:literalForm');
174
            if ($label !== null && $label->getLang() == $this->clang) {
175
                return new LabelSkosXL($this->model, $labres);
176
            }
177
        }
178
        return null;
179
    }
180
181
    /**
182
     * Returns a notation for the concept or null if it has not been defined.
183
     * @return string eg. '999'
184
     */
185
    public function getNotation()
186
    {
187
        $notation = $this->resource->get('skos:notation');
188
        if ($this->vocab->getConfig()->showNotation() && $notation !== null) {
189
            return $notation->getValue();
190
        }
191
192
        return null;
193
    }
194
195
    /**
196
     * Returns the Vocabulary object or undefined if that is not available.
197
     * @return Vocabulary
198
     */
199
    public function getVocab()
200
    {
201
        return $this->vocab;
202
    }
203
204
    /**
205
     * Returns the vocabulary shortname string or id if that is not available.
206
     * @return string
207
     */
208
    public function getShortName()
209
    {
210
        return $this->vocab ? $this->vocab->getShortName() : null;
211
    }
212
213
    /**
214
     * Returns the vocabulary shortname string or id if that is not available.
215
     * @return string
216
     */
217
    public function getVocabTitle()
218
    {
219
        return $this->vocab ? $this->vocab->getTitle() : null;
220
    }
221
222
    /**
223
     * Setter for the $clang property.
224
     * @param string $clang language code eg. 'en'
225
     */
226
    public function setContentLang($clang)
227
    {
228
        $this->clang = $clang;
229
    }
230
231
    public function getContentLang()
232
    {
233
        return $this->clang;
234
    }
235
236
    /**
237
     * Setter for the $foundby property.
238
     * @param string $label label that was matched
239
     * @param string $type type of match: 'alt', 'hidden', or 'lang'
240
     */
241
    public function setFoundBy($label, $type)
242
    {
243
        $this->foundby = $label;
244
        $this->foundbytype = $type;
245
    }
246
247
    /**
248
     * Getter for the $foundby property.
249
     * @return string
250
     */
251
    public function getFoundBy()
252
    {
253
        return $this->foundby;
254
    }
255
256
    /**
257
     * Getter for the $foundbytype property.
258
     * @return string
259
     */
260
    public function getFoundByType()
261
    {
262
        return $this->foundbytype;
263
    }
264
265
    /**
266
     * Processes a single external resource i.e., adds the properties from
267
     * 1) $this->$DEFAULT_EXT_PROPERTIES
268
     * 2) VocabConfig external properties
269
     * 3) Possible plugin defined external properties
270
     * to $this->graph
271
     * @param EasyRdf\Resource $res
272
     */
273
    public function processExternalResource($res)
274
    {
275
        $exGraph = $res->getGraph();
276
        // catch external subjects that have $res as object
277
        $extSubjects = $exGraph->resourcesMatching("schema:about", $res);
278
279
        $propList =  array_unique(array_merge(
280
            $this->DEFAULT_EXT_PROPERTIES,
281
            $this->getVocab()->getConfig()->getExtProperties(),
282
            $this->getVocab()->getConfig()->getPlugins()->getExtProperties()
283
        ));
284
285
        $seen = array();
286
        $this->addExternalTriplesToGraph($res, $seen, $propList);
287
        foreach ($extSubjects as $extSubject) {
288
            if ($extSubject->isBNode() && array_key_exists($extSubject->getUri(), $seen)) {
0 ignored issues
show
Bug introduced by
The method isBNode cannot be called on $extSubject (of type resource).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
Bug introduced by
The method getUri cannot be called on $extSubject (of type resource).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
289
                // already processed, skip
290
                continue;
291
            }
292
            $seen[$extSubject->getUri()] = 1;
0 ignored issues
show
Bug introduced by
The method getUri cannot be called on $extSubject (of type resource).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
293
            $this->addExternalTriplesToGraph($extSubject, $seen, $propList);
0 ignored issues
show
Documentation introduced by
$extSubject is of type resource, but the function expects a object<EasyRdf\Resource>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
294
        }
295
296
    }
297
298
    /**
299
     * Adds resource properties to $this->graph
300
     * @param EasyRdf\Resource $res
301
     * @param string[] $seen Processed resources so far
302
     * @param string[] $props (optional) limit to these property URIs
303
     */
304
    private function addExternalTriplesToGraph($res, &$seen, $props=null)
305
    {
306
        if (array_key_exists($res->getUri(), $seen) && $seen[$res->getUri()] === 0) {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison === seems to always evaluate to false as the types of $seen[$res->getUri()] (string) and 0 (integer) can never be identical. Maybe you want to use a loose comparison == instead?
Loading history...
307
            return;
308
        }
309
        $seen[$res->getUri()] = 0;
310
311
        if ($res->isBNode() || is_null($props)) {
312
            foreach ($res->propertyUris() as $prop) {
313
                $this->addPropertyValues($res, $prop, $seen);
314
            }
315
        }
316
        else {
317
            foreach ($props as $prop) {
318
                if ($res->hasProperty($prop)) {
319
                    $this->addPropertyValues($res, $prop, $seen);
320
                }
321
            }
322
        }
323
    }
324
325
    /**
326
     * Adds values of a single single property of a resource to $this->graph
327
     * implements Concise Bounded Description definition
328
     * @param EasyRdf\Resource $res
329
     * @param string $prop
330
     * @param string[] $seen Processed resources so far
331
     */
332
    private function addPropertyValues($res, $prop, &$seen)
333
    {
334
        $resList = $res->allResources('<' . $prop . '>');
335
336
        foreach ($resList as $res2) {
337
            if ($res2->isBNode()) {
338
                $this->addExternalTriplesToGraph($res2, $seen);
339
            }
340
            $this->graph->addResource($res, $prop, $res2);
341
            $this->addResourceReifications($res, $prop, $res2, $seen);
342
        }
343
344
        $litList = $res->allLiterals('<' . $prop . '>');
345
346
        foreach ($litList as $lit) {
347
            $this->graph->addLiteral($res, $prop, $lit);
348
            $this->addLiteralReifications($res, $prop, $lit, $seen);
349
        }
350
    }
351
352
    /**
353
     * Adds reifications of a triple having a literal object to $this->graph
354
     * @param EasyRdf\Resource $sub
355
     * @param string $pred
356
     * @param EasyRdf\Literal $obj
357
     * @param string[] $seen Processed resources so far
358
     */
359
    private function addLiteralReifications($sub, $pred, $obj, &$seen)
360
    {
361
        $pos_reifs = $sub->getGraph()->resourcesMatching("rdf:subject", $sub);
362
        foreach ($pos_reifs as $pos_reif) {
363
            $lit = $pos_reif->getLiteral("rdf:object", $obj->getLang());
0 ignored issues
show
Bug introduced by
The method getLiteral cannot be called on $pos_reif (of type resource).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
364
365 View Code Duplication
            if (!is_null($lit) && $lit->getValue() === $obj->getValue() &&
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
366
                $pos_reif->isA("rdf:Statement") &&
0 ignored issues
show
Bug introduced by
The method isA cannot be called on $pos_reif (of type resource).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
367
                $pos_reif->hasProperty("rdf:predicate", new EasyRdf\Resource($pred, $sub->getGraph())))
0 ignored issues
show
Bug introduced by
The method hasProperty cannot be called on $pos_reif (of type resource).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
368
            {
369
                $this->addExternalTriplesToGraph($pos_reif, $seen);
0 ignored issues
show
Documentation introduced by
$pos_reif is of type resource, but the function expects a object<EasyRdf\Resource>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
370
            }
371
        }
372
    }
373
374
    /**
375
     * Adds reifications of a triple having a resource object to $this->graph
376
     * @param EasyRdf\Resource $sub
377
     * @param string $pred
378
     * @param EasyRdf\Resource $obj
379
     * @param string[] $seen Processed resources so far
380
     */
381
    private function addResourceReifications($sub, $pred, $obj, &$seen)
382
    {
383
        $pos_reifs = $sub->getGraph()->resourcesMatching("rdf:subject", $sub);
384
        foreach ($pos_reifs as $pos_reif) {
385 View Code Duplication
            if ($pos_reif->isA("rdf:Statement") &&
0 ignored issues
show
Bug introduced by
The method isA cannot be called on $pos_reif (of type resource).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
386
                $pos_reif->hasProperty("rdf:object", $obj) &&
0 ignored issues
show
Bug introduced by
The method hasProperty cannot be called on $pos_reif (of type resource).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
387
                $pos_reif->hasProperty("rdf:predicate", new EasyRdf\Resource($pred, $sub->getGraph())))
0 ignored issues
show
Bug introduced by
The method hasProperty cannot be called on $pos_reif (of type resource).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
388
            {
389
                $this->addExternalTriplesToGraph($pos_reif, $seen);
0 ignored issues
show
Documentation introduced by
$pos_reif is of type resource, but the function expects a object<EasyRdf\Resource>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
390
            }
391
        }
392
    }
393
394
    public function getMappingProperties()
395
    {
396
        $ret = array();
397
398
        $longUris = $this->resource->propertyUris();
399
        foreach ($longUris as &$prop) {
400 View Code Duplication
            if (EasyRdf\RdfNamespace::shorten($prop) !== null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
401
                // shortening property labels if possible
402
                $prop = $sprop = EasyRdf\RdfNamespace::shorten($prop);
403
            } else {
404
                $sprop = "<$prop>";
405
            }
406
            // EasyRdf requires full URIs to be in angle brackets
407
408
            if (in_array($prop, $this->MAPPING_PROPERTIES) && !in_array($prop, $this->DELETED_PROPERTIES)) {
409
                $propres = new EasyRdf\Resource($prop, $this->graph);
410
                $proplabel = $propres->label($this->getEnvLang()) ? $propres->label($this->getEnvLang()) : $propres->label(); // current language
411
                $propobj = new ConceptProperty($prop, $proplabel);
412
                if ($propobj->getLabel() !== null) {
413
                    // only display properties for which we have a label
414
                    $ret[$prop] = $propobj;
415
                }
416
417
                // Iterating through every resource and adding these to the data object.
418
                foreach ($this->resource->allResources($sprop) as $val) {
419
                    if (isset($ret[$prop])) {
420
                        // checking if the target vocabulary can be found at the skosmos endpoint
421
                        $exuri = $val->getUri();
422
                        // if multiple vocabularies are found, the following method will return in priority the current vocabulary of the concept
423
                        $exvoc = $this->model->guessVocabularyFromURI($exuri, $this->vocab->getId());
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $exvoc is correct as $this->model->guessVocab... $this->vocab->getId()) (which targets Model::guessVocabularyFromURI()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
424
                        // if not querying the uri itself
425
                        if (!$exvoc) {
426
                            $response = null;
427
                            // if told to do so in the vocabulary configuration
428
                            if ($this->vocab->getConfig()->getExternalResourcesLoading()) {
429
                                $response = $this->model->getResourceFromUri($exuri);
430
                            }
431
432 View Code Duplication
                            if ($response) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
433
                                $ret[$prop]->addValue(new ConceptMappingPropertyValue($this->model, $this->vocab, $response, $prop), $this->clang);
434
435
                                $this->processExternalResource($response);
436
437
                                continue;
438
                            }
439
                        }
440
                        $ret[$prop]->addValue(new ConceptMappingPropertyValue($this->model, $this->vocab, $val, $prop, $this->clang), $this->clang);
441
                    }
442
                }
443
            }
444
        }
445
446
        // sorting the properties to a order preferred in the Skosmos concept page.
447
        $ret = $this->arbitrarySort($ret);
448
449
        return $ret;
450
    }
451
452
    /**
453
     * Iterates over all the properties of the concept and returns those in an array.
454
     * @return array
455
     */
456
    public function getProperties()
457
    {
458
        $properties = array();
459
        $narrowersByUri = array();
460
        $inCollection = array();
461
        $membersArray = array();
462
        $longUris = $this->resource->propertyUris();
463
        $duplicates = array();
464
        $ret = array();
465
466
        // looking for collections and linking those with their narrower concepts
467
        if ($this->vocab->getConfig()->getArrayClassURI() !== null) {
468
            $collections = $this->graph->allOfType($this->vocab->getConfig()->getArrayClassURI());
469
            if (sizeof($collections) > 0) {
470
                // indexing the narrowers once to avoid iterating all of them with every collection
471
                foreach ($this->resource->allResources('skos:narrower') as $narrower) {
472
                    $narrowersByUri[$narrower->getUri()] = $narrower;
473
                }
474
475
                foreach ($collections as $coll) {
476
                    $currCollMembers = $this->getCollectionMembers($coll, $narrowersByUri);
477
                    foreach ($currCollMembers as $collection) {
478
                        if ($collection->getSubMembers()) {
479
                            $submembers = $collection->getSubMembers();
480
                            foreach ($submembers as $member) {
481
                                $inCollection[$member->getUri()] = true;
482
                            }
483
484
                        }
485
                    }
486
487
                    if (isset($collection) && $collection->getSubMembers()) {
488
                        $membersArray = array_merge($currCollMembers, $membersArray);
489
                    }
490
491
                }
492
                $properties['skos:narrower'] = $membersArray;
493
            }
494
        }
495
496
        foreach ($longUris as &$prop) {
497
            // storing full URI without brackets in a separate variable
498
            $longUri = $prop;
499 View Code Duplication
            if (EasyRdf\RdfNamespace::shorten($prop) !== null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
500
                // shortening property labels if possible
501
                $prop = $sprop = EasyRdf\RdfNamespace::shorten($prop);
502
            } else {
503
                $sprop = "<$prop>";
504
            }
505
            // EasyRdf requires full URIs to be in angle brackets
506
507
            if (!in_array($prop, $this->DELETED_PROPERTIES) || ($this->isGroup() === false && $prop === 'skos:member')) {
508
                // retrieve property label and super properties from the current vocabulary first
509
                $propres = new EasyRdf\Resource($prop, $this->graph);
510
                $proplabel = $propres->label($this->getEnvLang()) ? $propres->label($this->getEnvLang()) : $propres->label();
511
512
                // if not found in current vocabulary, look up in the default graph to be able
513
                // to read an ontology loaded in a separate graph
514
                // note that this imply that the property has an rdf:type declared for the query to work
515
                if(!$proplabel) {
516
                    $envLangLabels = $this->model->getDefaultSparql()->queryLabel($longUri, $this->getEnvLang());
517
                    $proplabel = ($envLangLabels)?$envLangLabels[$this->getEnvLang()]:$this->model->getDefaultSparql()->queryLabel($longUri, '')[''];
518
                }
519
520
                // look for superproperties in the current graph
521
                $superprops = array();
522
                foreach ($this->graph->allResources($prop, 'rdfs:subPropertyOf') as $subi) {
523
                    $superprops[] = $subi->getUri();
524
                }
525
526
                // also look up superprops in the default graph if not found in current vocabulary
527
                if(!$superprops || empty($superprops)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $superprops of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
528
                    $superprops = $this->model->getDefaultSparql()->querySuperProperties($longUri);
529
                }
530
531
                // we're reading only one super property, even if there are multiple ones
532
                $superprop = ($superprops)?$superprops[0]:null;
533
                if ($superprop) {
534
                    $superprop = EasyRdf\RdfNamespace::shorten($superprop) ? EasyRdf\RdfNamespace::shorten($superprop) : $superprop;
535
                }
536
                $propobj = new ConceptProperty($prop, $proplabel, $superprop);
537
538
                if ($propobj->getLabel() !== null) {
539
                    // only display properties for which we have a label
540
                    $ret[$prop] = $propobj;
541
                }
542
543
                // searching for subproperties of literals too
544
                if($superprops) {
545
                    foreach ($superprops as $subi) {
546
                        $suburi = EasyRdf\RdfNamespace::shorten($subi) ? EasyRdf\RdfNamespace::shorten($subi) : $subi;
547
                        $duplicates[$suburi] = $prop;
548
                    }
549
                }
550
551
                // Iterating through every literal and adding these to the data object.
552
                foreach ($this->resource->allLiterals($sprop) as $val) {
553
                    $literal = new ConceptPropertyValueLiteral($this->model, $this->vocab, $this->resource, $val, $prop);
554
                    // only add literals when they match the content/hit language or have no language defined
555
                    if (isset($ret[$prop]) && ($literal->getLang() === $this->clang || $literal->getLang() === null)) {
556
                        $ret[$prop]->addValue($literal);
557
                    }
558
559
                }
560
561
                // Iterating through every resource and adding these to the data object.
562
                foreach ($this->resource->allResources($sprop) as $val) {
563
                    // skipping narrower concepts which are already shown in a collection
564
                    if ($sprop === 'skos:narrower' && array_key_exists($val->getUri(), $inCollection)) {
565
                        continue;
566
                    }
567
568
                    // hiding rdf:type property if it's just skos:Concept
569
                    if ($sprop === 'rdf:type' && $val->shorten() === 'skos:Concept') {
570
                        continue;
571
                    }
572
573
                    // handled by getMappingProperties()
574
                    if (in_array($sprop, $this->MAPPING_PROPERTIES)) {
575
                        continue;
576
                    }
577
578
                    if (isset($ret[$prop])) {
579
                        // checking if the property value is not in the current vocabulary
580
                        $exvoc = $this->model->guessVocabularyFromURI($val->getUri());
581 View Code Duplication
                        if ($exvoc && $exvoc->getId() !== $this->vocab->getId()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
582
                            $ret[$prop]->addValue(new ConceptMappingPropertyValue($this->model, $this->vocab, $val, $prop, $this->clang), $this->clang);
583
                            continue;
584
                        }
585
                        $ret[$prop]->addValue(new ConceptPropertyValue($this->model, $this->vocab, $val, $prop, $this->clang), $this->clang);
586
                    }
587
588
                }
589
            }
590
        }
591
        // adding narrowers part of a collection
592
        foreach ($properties as $prop => $values) {
593
            foreach ($values as $value) {
594
                $ret[$prop]->addValue($value, $this->clang);
595
            }
596
        }
597
598
        foreach ($ret as $key => $prop) {
599
            if (sizeof($prop->getValues()) === 0) {
600
                unset($ret[$key]);
601
            }
602
        }
603
604
        $ret = $this->removeDuplicatePropertyValues($ret, $duplicates);
605
        // sorting the properties to the order preferred in the Skosmos concept page.
606
        $ret = $this->arbitrarySort($ret);
607
        return $ret;
608
    }
609
610
    /**
611
     * Removes properties that have duplicate values.
612
     * @param $ret the array of properties generated by getProperties
613
     * @param $duplicates array of properties found are a subProperty of a another property
614
     * @return array of ConceptProperties
615
     */
616
    public function removeDuplicatePropertyValues($ret, $duplicates)
617
    {
618
        $propertyValues = array();
619
620
        foreach ($ret as $prop) {
621
            foreach ($prop->getValues() as $value) {
622
                $label = $value->getLabel();
623
                $propertyValues[(method_exists($label, 'getValue')) ? $label->getValue() : $label][] = $value->getType();
624
            }
625
        }
626
627
        foreach ($propertyValues as $value => $propnames) {
628
            // if there are multiple properties with the same string value.
629
            if (count($propnames) > 1) {
630
                foreach ($propnames as $property) {
631
                    // if there is a more accurate property delete the more generic one.
632
                    if (isset($duplicates[$property])) {
633
                        unset($ret[$property]);
634
                    }
635
                }
636
637
            }
638
        }
639
        return $ret;
640
    }
641
642
    /**
643
     * Gets the creation date and modification date if available.
644
     * @return String containing the date information in a human readable format.
645
     */
646
    public function getDate()
647
    {
648
        $ret = '';
649
        $created = '';
650
        $modified = '';
651
        try {
652
            // finding the created properties
653
            if ($this->resource->get('dc:created')) {
654
                $created = $this->resource->get('dc:created')->getValue();
655
            }
656
657
            // finding the modified properties
658
            if ($this->resource->get('dc:modified')) {
659
                $modified = $this->resource->get('dc:modified')->getValue();
660
            }
661
662
            // making a human readable string from the timestamps
663
            if ($created != '') {
664
                $ret = gettext('skosmos:created') . ' ' . (Punic\Calendar::formatDate($created, 'short'));
665
            }
666
667
            if ($modified != '') {
668
                if ($created != '') {
669
                    $ret .= ', ' . gettext('skosmos:modified') . ' ' . (Punic\Calendar::formatDate($modified, 'short'));
670
                } else {
671
                    $ret .= ' ' . ucfirst(gettext('skosmos:modified')) . ' ' . (Punic\Calendar::formatDate($modified, 'short'));
672
                }
673
674
            }
675
        } catch (Exception $e) {
676
            trigger_error($e->getMessage(), E_USER_WARNING);
677
            $ret = '';
678
            if ($this->resource->get('dc:modified')) {
679
                $modified = (string) $this->resource->get('dc:modified');
680
                $ret = gettext('skosmos:modified') . ' ' . $modified;
681
            }
682
            if ($this->resource->get('dc:created')) {
683
                $created .= (string) $this->resource->get('dc:created');
684
                $ret .= ' ' . gettext('skosmos:created') . ' ' . $created;
685
            }
686
        }
687
        return $ret;
688
    }
689
690
    /**
691
     * Gets the members of a specific collection.
692
     * @param $coll
693
     * @param array containing all narrowers as EasyRdf\Resource
694
     * @return array containing ConceptPropertyValue objects
695
     */
696
    private function getCollectionMembers($coll, $narrowers)
697
    {
698
        $membersArray = array();
699
        $collLabel = $coll->label()->getValue($this->clang) ? $coll->label($this->clang) : $coll->label();
700
        if ($collLabel) {
701
            $collLabel = $collLabel->getValue();
702
        }
703
704
        $membersArray[$collLabel] = new ConceptPropertyValue($this->model, $this->vocab, $coll, 'skos:narrower', $this->clang);
705
        foreach ($coll->allResources('skos:member') as $member) {
706
            if (array_key_exists($member->getUri(), $narrowers)) {
707
                $narrower = $narrowers[$member->getUri()];
708
                if (isset($narrower)) {
709
                    $membersArray[$collLabel]->addSubMember(new ConceptPropertyValue($this->model, $this->vocab, $narrower, 'skos:member', $this->clang), $this->clang);
710
                }
711
712
            }
713
        }
714
715
        return $membersArray;
716
    }
717
718
    /**
719
     * Gets the groups the concept belongs to.
720
     */
721
    public function getGroupProperties()
722
    {
723
        return $this->getReverseResources(false);
724
    }
725
726
    /**
727
     * Gets the groups/arrays the concept belongs to.
728
     */
729
    public function getReverseResources($includeArrays) {
730
        $groups = array();
731
        $reverseResources = $this->graph->resourcesMatching('skos:member', $this->resource);
732
        if (isset($reverseResources)) {
733
            $arrayClassURI = $this->vocab !== null ? $this->vocab->getConfig()->getArrayClassURI() : null;
734
            $arrayClass = $arrayClassURI !== null ? EasyRdf\RdfNamespace::shorten($arrayClassURI) : null;
735
            $superGroups = $this->resource->all('isothes:superGroup');
736
            $superGroupUris = array_map(function($obj) { return $obj->getUri(); }, $superGroups);
737
            foreach ($reverseResources as $reverseResource) {
738
                if (in_array($arrayClass, $reverseResource->types()) === $includeArrays) {
0 ignored issues
show
Bug introduced by
The method types cannot be called on $reverseResource (of type resource).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
739
                    // not adding the memberOf if the reverse resource is already covered by isothes:superGroup see issue #433
740
                    if (in_array($reverseResource->getUri(), $superGroupUris)) {
0 ignored issues
show
Bug introduced by
The method getUri cannot be called on $reverseResource (of type resource).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
741
                        continue;
742
                    }
743
                    $property = in_array($arrayClass, $reverseResource->types()) ? "skosmos:memberOfArray" : "skosmos:memberOf";
0 ignored issues
show
Bug introduced by
The method types cannot be called on $reverseResource (of type resource).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
744
                    $collLabel = $reverseResource->label($this->clang) ? $reverseResource->label($this->clang) : $reverseResource->label();
0 ignored issues
show
Bug introduced by
The method label cannot be called on $reverseResource (of type resource).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
745
                    if ($collLabel) {
746
                        $collLabel = $collLabel->getValue();
747
                    }
748
749
                    $groups[$collLabel] = new ConceptPropertyValue($this->model, $this->vocab, $reverseResource, $property, $this->clang);
750
                    ksort($groups);
751
                    $super = $this->graph->resourcesMatching('skos:member', $reverseResource);
752
                    while (isset($super) && !empty($super)) {
753
                        foreach ($super as $res) {
754
                            $superprop = new ConceptPropertyValue($this->model, $this->vocab, $res, 'skosmos:memberOfSuper', $this->clang);
755
                            array_unshift($groups, $superprop);
756
                            $super = $this->graph->resourcesMatching('skos:member', $res);
757
                        }
758
                    }
759
                }
760
            }
761
        }
762
        return $groups;
763
    }
764
765
    public function getArrayProperties() {
766
        return $this->getReverseResources(true);
767
    }
768
769
    /**
770
     * Reads the literal language code and gets a name for it from Punic or alternatively
771
     * tries to search for a gettext translation.
772
     * @param EasyRdf\Literal $lit
773
     * @return string e.g. 'English'
774
     */
775
    private function literalLanguageToString($lit) {
776
        // using empty string as the language literal when there is no langcode set
777
        $langName = '';
778
        if ($lit->getLang() !== null) {
779
            $langName = Punic\Language::getName($lit->getLang(), $this->getEnvLang()) !== $lit->getLang() ? Punic\Language::getName($lit->getLang(), $this->getEnvLang()) : gettext($lit->getLang());
780
        }
781
        return $langName;
782
    }
783
784
    /**
785
     * Gets the values for the property in question in all other languages than the ui language.
786
     */
787
    public function getForeignLabels()
788
    {
789
        $prefLabels = $this->resource->allLiterals('skos:prefLabel');
790
        $labels = array_merge($prefLabels, $this->resource->allLiterals('skos:altLabel'));
791
        $ret = array();
792
        foreach ($labels as $lit) {
793
            // filtering away subsets of the current language eg. en vs en-GB
794
            if ($lit->getLang() != $this->clang && strpos($lit->getLang(), $this->getEnvLang() . '-') !== 0) {
795
                $prop = in_array($lit, $prefLabels) ? 'skos:prefLabel' : 'skos:altLabel';
796
                $ret[$this->literalLanguageToString($lit)][] = new ConceptPropertyValueLiteral($this->model, $this->vocab, $this->resource, $lit, $prop);
797
            }
798
        }
799
        ksort($ret);
800
        return $ret;
801
    }
802
803
    /**
804
     * Gets the values for the property in question in all other languages than the ui language.
805
     * @param string $property
806
     */
807
    public function getAllLabels($property)
808
    {
809
        $labels = array();
810
        // shortening property labels if possible, EasyRdf requires full URIs to be in angle brackets
811
        $property = (EasyRdf\RdfNamespace::shorten($property) !== null) ? EasyRdf\RdfNamespace::shorten($property) : "<$property>";
812
        foreach ($this->resource->allLiterals($property) as $lit) {
813
            $labels[Punic\Language::getName($lit->getLang(), $this->getEnvLang())][] = new ConceptPropertyValueLiteral($this->model, $this->vocab, $this->resource, $lit, $property);
814
        }
815
        ksort($labels);
816
        return $labels;
817
    }
818
819
    /**
820
     * Dump concept graph as JSON-LD.
821
     */
822
    public function dumpJsonLd() {
823
        $context = array(
824
            'skos' => EasyRdf\RdfNamespace::get("skos"),
825
            'isothes' => EasyRdf\RdfNamespace::get("isothes"),
826
            'rdfs' => EasyRdf\RdfNamespace::get("rdfs"),
827
            'owl' =>EasyRdf\RdfNamespace::get("owl"),
828
            'dct' => EasyRdf\RdfNamespace::get("dcterms"),
829
            'dc11' => EasyRdf\RdfNamespace::get("dc11"),
830
            'uri' => '@id',
831
            'type' => '@type',
832
            'lang' => '@language',
833
            'value' => '@value',
834
            'graph' => '@graph',
835
            'label' => 'rdfs:label',
836
            'prefLabel' => 'skos:prefLabel',
837
            'altLabel' => 'skos:altLabel',
838
            'hiddenLabel' => 'skos:hiddenLabel',
839
            'broader' => 'skos:broader',
840
            'narrower' => 'skos:narrower',
841
            'related' => 'skos:related',
842
            'inScheme' => 'skos:inScheme',
843
            'schema' => EasyRdf\RdfNamespace::get("schema"),
844
            'wd' => EasyRdf\RdfNamespace::get("wd"),
845
            'wdt' => EasyRdf\RdfNamespace::get("wdt"),
846
        );
847
        $vocabPrefix = preg_replace('/\W+/', '', $this->vocab->getId()); // strip non-word characters
848
        $vocabUriSpace = $this->vocab->getUriSpace();
849
850
        if (!in_array($vocabUriSpace, $context, true)) {
851
            if (!isset($context[$vocabPrefix])) {
852
                $context[$vocabPrefix] = $vocabUriSpace;
853
            }
854
            else if ($context[$vocabPrefix] !== $vocabUriSpace) {
855
                $i = 2;
856
                while (isset($context[$vocabPrefix . $i]) && $context[$vocabPrefix . $i] !== $vocabUriSpace) {
857
                    $i += 1;
858
                }
859
                $context[$vocabPrefix . $i] = $vocabUriSpace;
860
            }
861
        }
862
        $compactJsonLD = \ML\JsonLD\JsonLD::compact($this->graph->serialise('jsonld'), json_encode($context));
863
        $results = \ML\JsonLD\JsonLD::toString($compactJsonLD);
864
865
        return $results;
866
    }
867
}
868