Completed
Push — master ( ee8493...f42dec )
by Henri
02:42
created

Concept::getNotation()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 9
rs 9.6666
cc 2
eloc 5
nc 2
nop 0
1
<?php
2
/**
3
 * Copyright (c) 2012-2013 Aalto University and University of Helsinki
4
 * MIT License
5
 * see LICENSE.txt for more information
6
 */
7
8
/**
9
 * Dataobject for a single concept.
10
 */
11
12
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...
13
{
14
    /**
15
     * Stores a label string if the concept has been found through
16
     * a altLabel/label in a another language than the ui.
17
     */
18
    private $foundby;
19
    /** Type of foundby match: 'alt', 'hidden' or 'lang' */
20
    private $foundbytype;
21
    /** the EasyRdf_Graph object of the concept */
22
    private $graph;
23
    private $clang;
24
25
    /** concept properties that should not be shown to users */
26
    private $DELETED_PROPERTIES = array(
27
        'skosext:broaderGeneric', # these are remnants of bad modeling
28
        'skosext:broaderPartitive', #
29
30
        'skos:hiddenLabel', # because it's supposed to be hidden
31
        'skos:prefLabel', # handled separately by getLabel
32
        'rdfs:label', # handled separately by getLabel
33
34
        'skos:topConceptOf', # because it's too technical, not relevant for users
35
        'skos:inScheme', # should be evident in any case
36
        'skos:member', # this is shouldn't be shown on the group page
37
        'dc:created', # handled separately
38
        'dc:modified', # handled separately
39
    );
40
41
    /** related concepts that should be shown to users in the appendix */
42
    private $MAPPING_PROPERTIES = array(
43
        'skos:exactMatch',
44
        'skos:narrowMatch',
45
        'skos:broadMatch',
46
        'skos:closeMatch',
47
        'skos:relatedMatch',
48
        'rdfs:seeAlso',
49
        'owl:sameAs',
50
    );
51
52
    /**
53
     * Initializing the concept object requires the following parameters.
54
     * @param Model $model
55
     * @param Vocabulary $vocab
56
     * @param EasyRdf_Resource $resource
57
     * @param EasyRdf_Graph $graph
58
     */
59
    public function __construct($model, $vocab, $resource, $graph, $clang)
60
    {
61
        parent::__construct($model, $vocab, $resource);
62
        $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");
63
        $this->graph = $graph;
64
        $this->clang = $clang;
65
        // setting the Punic plugins locale for localized datetime conversions
66
        if ($this->clang && $this->clang !== '') {
67
            Punic\Data::setDefaultLocale($clang);
68
        }
69
70
    }
71
72
    /**
73
     * Returns the concept uri.
74
     * @return string
75
     */
76
    public function getUri()
77
    {
78
        return $this->resource->getUri();
79
    }
80
81
    public function getType()
82
    {
83
        return $this->resource->types();
84
    }
85
86
    /**
87
     * Returns a boolean value indicating if the concept has been deprecated.
88
     * @return boolean
89
     */
90
    public function getDeprecated()
91
    {
92
        foreach ($this->resource->all('rdf:type') as $type) {
93
            if (strpos($type->getUri(), 'DeprecatedConcept')) {
94
                return true;
95
            }
96
        }
97
98
        return false;
99
    }
100
101
    /**
102
     * Returns a label for the concept in the ui language or if not possible in any language.
103
     * @return string
104
     */
105
    public function getLabel()
106
    {
107
        $lang = $this->clang;
108
        // 1. label in current language
109
        if ($this->resource->label($lang) !== null) {
110
            return $this->resource->label($lang);
111
        }
112
113
        // 2. label in the vocabulary default language
114 View Code Duplication
        if ($this->resource->label($this->vocab->getConfig()->getDefaultLanguage()) !== 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...
115
            return $this->resource->label($this->vocab->getConfig()->getDefaultLanguage());
116
        }
117
118
        // 3. label in any language
119
        $label = $this->resource->label();
120
        // if the label lang code is a subset of the ui lang eg. en-GB
121
        if ($label !== null && strpos($label->getLang(), $this->getEnvLang() . '-') === 0) {
122
            return $label->getValue();
123
        }
124
125
        if ($label !== null) {
126
            return $label->getValue() . " (" . $label->getLang() . ")";
127
        }
128
129
        // empty
130
        return "";
131
    }
132
133
    /**
134
     * Returns a notation for the concept or null if it has not been defined.
135
     * @return string eg. '999'
136
     */
137
    public function getNotation()
138
    {
139
        $notation = $this->resource->get('skos:notation');
140
        if ($notation !== null) {
141
            return $notation->getValue();
142
        }
143
144
        return null;
145
    }
146
147
    /**
148
     * Returns the Vocabulary object or undefined if that is not available.
149
     * @return Vocabulary
150
     */
151
    public function getVocab()
152
    {
153
        return $this->vocab;
154
    }
155
156
    /**
157
     * Returns the vocabulary shortname string or id if that is not available.
158
     * @return string
159
     */
160
    public function getShortName()
161
    {
162
        return $this->vocab ? $this->vocab->getShortName() : null;
163
    }
164
165
    /**
166
     * Returns the vocabulary shortname string or id if that is not available.
167
     * @return string
168
     */
169
    public function getVocabTitle()
170
    {
171
        return $this->vocab ? $this->vocab->getTitle() : null;
172
    }
173
174
    /**
175
     * Setter for the $clang property.
176
     * @param string $clang language code eg. 'en'
177
     */
178
    public function setContentLang($clang)
179
    {
180
        $this->clang = $clang;
181
    }
182
183
    public function getContentLang()
184
    {
185
        return $this->clang;
186
    }
187
188
    /**
189
     * Setter for the $foundby property.
190
     * @param string $label label that was matched
191
     * @param string $type type of match: 'alt', 'hidden', or 'lang'
192
     */
193
    public function setFoundBy($label, $type)
194
    {
195
        $this->foundby = $label;
196
        $this->foundbytype = $type;
197
    }
198
199
    /**
200
     * Getter for the $foundby property.
201
     * @return string
202
     */
203
    public function getFoundBy()
204
    {
205
        return $this->foundby;
206
    }
207
208
    /**
209
     * Getter for the $foundbytype property.
210
     * @return string
211
     */
212
    public function getFoundByType()
213
    {
214
        return $this->foundbytype;
215
    }
216
217
    public function getMappingProperties()
218
    {
219
        $ret = array();
220
221
        $long_uris = $this->resource->propertyUris();
222
        foreach ($long_uris as &$prop) {
223 View Code Duplication
            if (EasyRdf_Namespace::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...
224
                // shortening property labels if possible
225
                $prop = $sprop = EasyRdf_Namespace::shorten($prop);
226
            } else {
227
                $sprop = "<$prop>";
228
            }
229
            // EasyRdf requires full URIs to be in angle brackets
230
231
            if (in_array($prop, $this->MAPPING_PROPERTIES) && !in_array($prop, $this->DELETED_PROPERTIES)) {
232
                $propres = new EasyRdf_Resource($prop, $this->graph);
233
                $proplabel = $propres->label($this->getEnvLang()) ? $propres->label($this->getEnvLang()) : $propres->label(); // current language
234
                $propobj = new ConceptProperty($prop, $proplabel);
235
                if ($propobj->getLabel() !== null) {
236
                    // only display properties for which we have a label
237
                    $ret[$prop] = $propobj;
238
                }
239
240
                // Iterating through every resource and adding these to the data object.
241
                foreach ($this->resource->allResources($sprop) as $val) {
242
                    if (isset($ret[$prop])) {
243
                        // checking if the target vocabulary can be found at the skosmos endpoint
244
                        $exuri = $val->getUri();
245
                        $exvoc = $this->model->guessVocabularyFromURI($exuri);
246
                        // if not querying the uri itself
247
                        if (!$exvoc) {
248
                            $response = null;
249
                            // if told to do so in the vocabulary configuration
250
                            if ($this->vocab->getConfig()->getExternalResourcesLoading()) {
251
                                $response = $this->model->getResourceFromUri($exuri);
252
                            }
253
254
                            if ($response) {
255
                                $ret[$prop]->addValue(new ConceptMappingPropertyValue($this->model, $this->vocab, $response, $prop), $this->clang);
256
                                continue;
257
                            }
258
                        }
259
                        $ret[$prop]->addValue(new ConceptMappingPropertyValue($this->model, $this->vocab, $val, $prop, $this->clang), $this->clang);
260
                    }
261
                }
262
            }
263
        }
264
265
        // sorting the properties to a order preferred in the Skosmos concept page.
266
        $ret = $this->arbitrarySort($ret);
267
268
        return $ret;
269
    }
270
271
    /**
272
     * Iterates over all the properties of the concept and returns those in an array.
273
     * @return array
274
     */
275
    public function getProperties()
276
    {
277
        $properties = array();
278
        $narrowers_by_uri = array();
279
        $in_a_collection = array();
280
        $members_array = array();
281
        $long_uris = $this->resource->propertyUris();
282
        $duplicates = array();
283
        $ret = array();
284
285
        // looking for collections and linking those with their narrower concepts
286
        if ($this->vocab->getConfig()->getArrayClassURI() !== null) {
287
            $collections = $this->graph->allOfType($this->vocab->getConfig()->getArrayClassURI());
288
            if (sizeof($collections) > 0) {
289
                // indexing the narrowers once to avoid iterating all of them with every collection
290
                foreach ($this->resource->allResources('skos:narrower') as $narrower) {
291
                    $narrowers_by_uri[$narrower->getUri()] = $narrower;
292
                }
293
294
                foreach ($collections as $coll) {
295
                    $curr_coll_members = $this->getCollectionMembers($coll, $narrowers_by_uri);
296
                    foreach ($curr_coll_members as $collection) {
297
                        if ($collection->getSubMembers()) {
298
                            $submembers = $collection->getSubMembers();
299
                            foreach ($submembers as $member) {
300
                                $in_a_collection[$member->getUri()] = true;
301
                            }
302
303
                        }
304
                    }
305
306
                    if (isset($collection) && $collection->getSubMembers()) {
307
                        $members_array = array_merge($curr_coll_members, $members_array);
308
                    }
309
310
                }
311
                $properties['skos:narrower'] = $members_array;
312
            }
313
        }
314
315
        foreach ($long_uris as &$prop) {
316 View Code Duplication
            if (EasyRdf_Namespace::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...
317
                // shortening property labels if possible
318
                $prop = $sprop = EasyRdf_Namespace::shorten($prop);
319
            } else {
320
                $sprop = "<$prop>";
321
            }
322
            // EasyRdf requires full URIs to be in angle brackets
323
324
            if (!in_array($prop, $this->DELETED_PROPERTIES)) {
325
                $propres = new EasyRdf_Resource($prop, $this->graph);
326
                $proplabel = $propres->label($this->getEnvLang()) ? $propres->label($this->getEnvLang()) : $propres->label();
327
                $superprop = $propres->get('rdfs:subPropertyOf') ? $propres->get('rdfs:subPropertyOf')->getURI() : null;
328
                if ($superprop) {
329
                    $superprop = EasyRdf_Namespace::shorten($superprop) ? EasyRdf_Namespace::shorten($superprop) : $superprop;
330
                }
331
                $propobj = new ConceptProperty($prop, $proplabel, $superprop);
332
333
                if ($propobj->getLabel() !== null) {
334
                    // only display properties for which we have a label
335
                    $ret[$prop] = $propobj;
336
                }
337
338
                // searching for subproperties of literals too
339
                foreach ($this->graph->allResources($prop, 'rdfs:subPropertyOf') as $subi) {
340
                    $suburi = EasyRdf_Namespace::shorten($subi->getUri()) ? EasyRdf_Namespace::shorten($subi->getUri()) : $subi->getUri();
341
                    $duplicates[$suburi] = $prop;
342
                }
343
344
                // Iterating through every literal and adding these to the data object.
345
                foreach ($this->resource->allLiterals($sprop) as $val) {
346
                    $literal = new ConceptPropertyValueLiteral($val, $prop);
347
                    // only add literals when they match the content/hit language or have no language defined
348
                    if (isset($ret[$prop]) && ($literal->getLang() === $this->clang || $literal->getLang() === null)) {
349
                        $ret[$prop]->addValue($literal);
350
                    }
351
352
                }
353
354
                // Iterating through every resource and adding these to the data object.
355
                foreach ($this->resource->allResources($sprop) as $val) {
356
                    // skipping narrower concepts which are already shown in a collection
357
                    if ($sprop === 'skos:narrower' && array_key_exists($val->getUri(), $in_a_collection)) {
358
                        continue;
359
                    }
360
361
                    // hiding rdf:type property if it's just skos:Concept
362
                    if ($sprop === 'rdf:type' && $val->shorten() === 'skos:Concept') {
363
                        continue;
364
                    }
365
366
                    // handled by getMappingProperties()
367
                    if (in_array($sprop, $this->MAPPING_PROPERTIES)) {
368
                        continue;
369
                    }
370
371 View Code Duplication
                    if (isset($ret[$prop])) {
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...
372
                        $ret[$prop]->addValue(new ConceptPropertyValue($this->model, $this->vocab, $val, $prop, $this->clang), $this->clang);
373
                    }
374
375
                }
376
            }
377
        }
378
        // adding narrowers part of a collection
379
        foreach ($properties as $prop => $values) {
380
            foreach ($values as $value) {
381
                $ret[$prop]->addValue($value, $this->clang);
382
            }
383
        }
384
385
        foreach ($ret as $key => $prop) {
386
            if (sizeof($prop->getValues()) === 0) {
387
                unset($ret[$key]);
388
            }
389
        }
390
391
        $ret = $this->removeDuplicatePropertyValues($ret, $duplicates);
392
        // sorting the properties to the order preferred in the Skosmos concept page.
393
        $ret = $this->arbitrarySort($ret);
394
        return $ret;
395
    }
396
397
    /**
398
     * Removes properties that have duplicate values.
399
     * @param $ret the array of properties generated by getProperties
400
     * @param $duplicates array of properties found are a subProperty of a another property
401
     * @return array of ConceptProperties
402
     */
403
    public function removeDuplicatePropertyValues($ret, $duplicates)
404
    {
405
        $propertyValues = array();
406
407
        foreach ($ret as $prop) {
408
            foreach ($prop->getValues() as $value) {
409
                $label = $value->getLabel();
410
                $propertyValues[(method_exists($label, 'getValue')) ? $label->getValue() : $label][] = $value->getType();
411
            }
412
        }
413
414
        foreach ($propertyValues as $value => $propnames) {
415
            // if there are multiple properties with the same string value.
416
            if (count($propnames) > 1) {
417
                foreach ($propnames as $property) {
418
                    // if there is a more accurate property delete the more generic one.
419
                    if (isset($duplicates[$property])) {
420
                        unset($ret[$property]);
421
                    }
422
                }
423
424
            }
425
        }
426
        return $ret;
427
    }
428
429
    /**
430
     * Gets the creation date and modification date if available.
431
     * @return String containing the date information in a human readable format.
432
     */
433
    public function getDate()
434
    {
435
        $ret = '';
436
        $created = '';
437
        $modified = '';
438
        try {
439
            // finding the created properties
440
            if ($this->resource->get('dc:created')) {
441
                $created = $this->resource->get('dc:created')->getValue();
442
            }
443
444
            // finding the modified properties
445
            if ($this->resource->get('dc:modified')) {
446
                $modified = $this->resource->get('dc:modified')->getValue();
447
            }
448
449
            // making a human readable string from the timestamps
450
            if ($created != '') {
451
                $ret = gettext('skosmos:created') . ' ' . (Punic\Calendar::formatDate($created, 'short'));
452
            }
453
454
            if ($modified != '') {
455
                if ($created != '') {
456
                    $ret .= ', ' . gettext('skosmos:modified') . ' ' . (Punic\Calendar::formatDate($modified, 'short'));
457
                } else {
458
                    $ret .= ' ' . ucfirst(gettext('skosmos:modified')) . ' ' . (Punic\Calendar::formatDate($modified, 'short'));
459
                }
460
461
            }
462
        } catch (Exception $e) {
463
            trigger_error($e->getMessage(), E_USER_WARNING);
464
            $ret = '';
465
            if ($this->resource->get('dc:modified')) {
466
                $modified = (string) $this->resource->get('dc:modified');
467
                $ret = gettext('skosmos:modified') . ' ' . $modified; 
468
            }
469
            if ($this->resource->get('dc:created')) {
470
                $created .= (string) $this->resource->get('dc:created');
471
                $ret .= ' ' . gettext('skosmos:created') . ' ' . $created; 
472
            }
473
        }
474
        return $ret;
475
    }
476
477
    /**
478
     * Gets the members of a specific collection.
479
     * @param $coll
480
     * @param array containing all narrowers as EasyRdf_Resource
481
     * @return array containing ConceptPropertyValue objects
482
     */
483
    private function getCollectionMembers($coll, $narrowers)
484
    {
485
        $members_array = array();
486
        $coll_label = $coll->label()->getValue($this->clang) ? $coll->label($this->clang) : $coll->label();
487
        if ($coll_label) {
488
            $coll_label = $coll_label->getValue();
489
        }
490
491
        $members_array[$coll_label] = new ConceptPropertyValue($this->model, $this->vocab, $coll, 'skos:narrower', $this->clang);
492
        foreach ($coll->allResources('skos:member') as $member) {
493
            if (array_key_exists($member->getUri(), $narrowers)) {
494
                $narrower = $narrowers[$member->getUri()];
495 View Code Duplication
                if (isset($narrower)) {
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...
496
                    $members_array[$coll_label]->addSubMember(new ConceptPropertyValue($this->model, $this->vocab, $narrower, 'skos:member', $this->clang), $this->clang);
497
                }
498
499
            }
500
        }
501
502
        return $members_array;
503
    }
504
505
    /**
506
     * Gets the groups the concept belongs to.
507
     */
508
    public function getGroupProperties()
509
    {
510
        return $this->getReverseResources(false);
511
    }
512
513
    /**
514
     * Gets the groups/arrays the concept belongs to.
515
     */
516
    public function getReverseResources($includeArrays) {
517
        $groups = array();
518
        $reverseResources = $this->graph->resourcesMatching('skos:member', $this->resource);
519
        if (isset($reverseResources)) {
520
            $arrayClassURI = $this->vocab !== null ? $this->vocab->getConfig()->getArrayClassURI() : null;
521
            $arrayClass = $arrayClassURI !== null ? EasyRdf_Namespace::shorten($arrayClassURI) : null;
522
            foreach ($reverseResources as $reverseResource) {
523
                if (in_array($arrayClass, $reverseResource->types()) === $includeArrays) {
524
                    $property = in_array($arrayClass, $reverseResource->types()) ? "skosmos:memberOfArray" : "skosmos:memberOf";
525
                    $coll_label = $reverseResource->label($this->clang) ? $reverseResource->label($this->clang) : $reverseResource->label();
526
                    if ($coll_label) {
527
                        $coll_label = $coll_label->getValue();
528
                    }
529
530
                    $groups[$coll_label] = new ConceptPropertyValue($this->model, $this->vocab, $reverseResource, $property, $this->clang);
531
                    ksort($groups);
532
                    $super = $this->graph->resourcesMatching('skos:member', $reverseResource);
533
                    while (isset($super) && !empty($super)) {
534
                        foreach ($super as $res) {
535
                            $superprop = new ConceptPropertyValue($this->model, $this->vocab, $res, 'skosmos:memberOfSuper', $this->clang);
536
                            array_unshift($groups, $superprop);
537
                            $super = $this->graph->resourcesMatching('skos:member', $res);
538
                        }
539
                    }
540
                }
541
            }
542
        }
543
        return $groups;
544
    }
545
546
    public function getArrayProperties() {
547
        return $this->getReverseResources(true);
548
    }
549
550
    /**
551
     * Gets the values for the property in question in all other languages than the ui language.
552
     */
553
    public function getForeignLabels()
554
    {
555
        $labels = array();
556 View Code Duplication
        foreach ($this->resource->allLiterals('skos:prefLabel') as $lit) {
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...
557
            // filtering away subsets of the current language eg. en vs en-GB
558
            if ($lit->getLang() != $this->clang && strpos($lit->getLang(), $this->getEnvLang() . '-') !== 0) {
559
                $langName = Punic\Language::getName($lit->getLang(), $this->getEnvLang()) !== $lit->getLang() ? Punic\Language::getName($lit->getLang(), $this->getEnvLang()) : gettext($lit->getLang());
560
                $labels[$langName][] = new ConceptPropertyValueLiteral($lit, 'skos:prefLabel');
561
            }
562
563
        }
564 View Code Duplication
        foreach ($this->resource->allLiterals('skos:altLabel') as $lit) {
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...
565
            // filtering away subsets of the current language eg. en vs en-GB
566
            if ($lit->getLang() != $this->clang && strpos($lit->getLang(), $this->getEnvLang() . '-') !== 0) {
567
                $langName = Punic\Language::getName($lit->getLang(), $this->getEnvLang()) ? Punic\Language::getName($lit->getLang(), $this->getEnvLang()) : gettext($lit->getLang());
568
                $labels[$langName][] = new ConceptPropertyValueLiteral($lit, 'skos:altLabel');
569
            }
570
571
        }
572
        ksort($labels);
573
        return $labels;
574
    }
575
576
    /**
577
     * Gets the values for the property in question in all other languages than the ui language.
578
     * @param string $property
579
     */
580
    public function getAllLabels($property)
581
    {
582
        $labels = array();
583
        if (EasyRdf_Namespace::shorten($property) !== null) {
584
            // shortening property labels if possible
585
            $property = EasyRdf_Namespace::shorten($property);
586
        } else {
587
            $property = "<$property>";
588
        }
589
        // EasyRdf requires full URIs to be in angle brackets
590
        foreach ($this->resource->allLiterals($property) as $lit) {
591
            $labels[Punic\Language::getName($lit->getLang(), $this->getEnvLang())][] = new ConceptPropertyValueLiteral($lit, $property);
592
        }
593
        ksort($labels);
594
        return $labels;
595
    }
596
}
597