Completed
Push — dev ( 0cb5be...30da7c )
by Jakob
02:44
created

DataType::fieldType()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 16
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 9
nc 5
nop 2
1
<?php declare(strict_types = 1);
2
3
namespace JSKOS;
4
5
use InvalidArgumentException;
6
7
const IRI_PATTERN = '!^((?P<scheme>[a-z][a-z0-9+.-]*):)' .
8
        '((?P<doubleslash>//)(?P<authority>[^/?#]*))?(?P<path>[^?#]*)' .
9
        '((?<querydef>\?)(?P<query>[^#]*))?(#(?P<fragment>.*))?!';
10
11
const DATE_PATTERN = '!^(?P<year>-?\d\d\d\d)' .
12
        '(-((?P<month>\d\d)(' .
13
        '-(?P<day>\d\d)' .
14
        '(T\d\d:\d\d:\d\d(\.\d+)?)?' .
15
        '(Z|[+-]\d\d:\d\d)?' .
16
        ')?))?$!';
17
18
const LANGUAGE_PATTERN = '/^[a-z]{2,3}(?:-[A-Z]{2,3}(?:-[a-zA-Z]{4})?)?$/';
19
20
const LANGUAGE_RANGE_PATTERN = '/^([a-z]{2,3}(?:-[A-Z]{2,3}(?:-[a-zA-Z]{4})?)?)?-$/';
21
22
/**
23
 * Base class of JSKOS Resources and fields.
24
 */
25
abstract class DataType extends PrettyJsonSerializable
26
{
27
    use Constructor;
28
29
    const FIELDS = [];
30
31
    /**
32
     * Get field definition from FIELDS, including parent classes.
33
     */
34
    protected static function fieldType(string $field, bool $strict=false)
35
    {
36
        $class = get_called_class();
37
        while ($class && $class != self::class) {
38
            if (isset($class::FIELDS[$field])) {
39
                return $class::FIELDS[$field];
40
            }
41
            $class = get_parent_class($class);
42
        }
43
44
        if ($strict) {
45
            throw new InvalidArgumentException(
46
                get_called_class() . "->$field does not exist"
47
            );
48
        }
49
    }
50
51
    private function fieldException($field, $message)
52
    {
53
        return new InvalidArgumentException(
54
            get_called_class()."->$field must $message"
55
        );
56
    }
57
58
    protected function setField(string $field, $value, bool $strict=true)
59
    {
60
        if ($field == '@context') {
61
            return;
62
        }
63
64
        $type = static::fieldType($field, $strict);
65
        if (!$type) {
66
            return;
67
        }
68
69
        if (is_null($value)) {
70
            $value = null;
71
        } elseif (is_array($type)) { # Set or Listing
72
            if ($type[0] == 'Set') {
73
                if (is_array($value)) {
74
                    $class = 'JSKOS\\'.$type[1];
75
                    $value = new Set(
76
                        array_map(function ($m) use ($class) {
77
                            if (is_null($m)) {
78
                                return null;
79
                            }
80
                            if ($m instanceof $class) {
81
                                return $m;
82
                            }
83
                            return new $class($m);
84
                        }, $value)
85
                    );
86
                } elseif (!is_a($value, 'JSKOS\Set')) {
87
                    throw $this->fieldException($field, "be a Set");
88
                } else {
0 ignored issues
show
Unused Code introduced by
This else statement is empty and can be removed.

This check looks for the else branches of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These else branches can be removed.

if (rand(1, 6) > 3) {
print "Check failed";
} else {
    //print "Check succeeded";
}

could be turned into

if (rand(1, 6) > 3) {
    print "Check failed";
}

This is much more concise to read.

Loading history...
89
                    # TODO: check member types
90
                }
91
            } else { # Listing
92
                if (is_array($value)) {
93
                    $value = new Listing($value);
94
                } else {
95
                    throw $this->fieldException($field, "be a Listing");
96
                }
97
                # TODO: check member types
98
            }
99
        } elseif ($type == 'LanguageMapOfStrings') {
100
            if (!($value instanceof LanguageMapOfStrings)) {
101
                $value = new LanguageMapOfStrings($value);
102
            }
103
        } elseif ($type == 'LanguageMapOfLists') {
104
            if (!($value instanceof LanguageMapOfLists)) {
105
                $value = new LanguageMapOfLists($value);
106
            }
107
        } elseif (!DataType::hasType($value, $type)) {
108
            if ($type == 'ConceptScheme') {
109
                $value = new ConceptScheme($value);
110
            } else {
111
                throw $this->fieldException($field, "match JSKOS\DataType::is$type");
112
            }
113
        }
114
115
        $this->$field = $value;
116
    }
117
118
    public function __set($field, $value)
119
    {
120
        $this->setField($field, $value);
121
    }
122
123
    public function &__get($field)
124
    {
125
        $type = static::fieldType($field);
126
        if ($type) {
127
            if (is_null($this->$field)) {
128
                $null = null;
129
                return $null;
130
            } else {
131
                return $this->$field;
132
            }
133
        } else {
134
            trigger_error(
135
                "Undefined property: ".get_called_class()."::$$field",
136
                \E_USER_NOTICE
137
            );
138
        }
139
    }
140
141
    public function __isset($field): bool
142
    {
143
        return !!static::fieldType($field);
144
    }
145
146
    /**
147
     * Check whether a given value looks like an URI.
148
     */
149
    public static function isURI($uri): bool
150
    {
151
        return is_string($uri) && preg_match(IRI_PATTERN, $uri) === 1;
152
    }
153
154
    /**
155
     * Check whether a given value looks like an http/https URL.
156
     */
157
    public static function isURL($url): bool
158
    {
159
        return is_string($url) &&
160
               preg_match(IRI_PATTERN, $url, $match) &&
161
               ($match[2] == 'http' || $match[2] == 'https');
162
    }
163
164
    /**
165
     * Check whether a given value looks like a date.
166
     */
167
    public static function isDate($date): bool
168
    {
169
        return is_string($date) && preg_match(DATE_PATTERN, $date) === 1;
170
    }
171
172
    /**
173
     * Check whether a given value looks like a language tag.
174
     */
175
    public static function isLanguage($language): bool
176
    {
177
        return is_string($language) && preg_match(LANGUAGE_PATTERN, $language) === 1;
178
    }
179
180
    /**
181
     * Check whether a given value looks like a language range.
182
     */
183
    public static function isLanguageRange($range): bool
184
    {
185
        return is_string($range) && preg_match(LANGUAGE_RANGE_PATTERN, $range) === 1;
186
    }
187
188
    /**
189
     * Check whether a given value looks like a language or language range.
190
     */
191
    public static function isLanguageOrRange($language): bool
192
    {
193
        return is_string($language) &&
194
               (preg_match(LANGUAGE_PATTERN, $language) === 1 ||
195
               preg_match(LANGUAGE_RANGE_PATTERN, $language) === 1);
196
    }
197
198
    /**
199
     * Check whether a given value is a string.
200
     */
201
    public static function isString($string): bool
202
    {
203
        return is_string($string);
204
    }
205
206
    /**
207
     * Check whether a given value is a concept scheme
208
     */
209
    public static function isConceptScheme($scheme): bool
210
    {
211
        return $scheme instanceof ConceptScheme;
212
    }
213
214
    /**
215
     * Check whether a given value is of given type.
216
     */
217
    public static function hasType($value, string $type): bool
218
    {
219
        $method = "is$type";
220
        return self::$method($value);
221
    }
222
}
223