Completed
Push — v2 ( b41681...2062ad )
by Joschi
04:41
created

Item   B

Complexity

Total Complexity 37

Size/Duplication

Total Lines 331
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 7

Test Coverage

Coverage 98.84%

Importance

Changes 0
Metric Value
wmc 37
lcom 2
cbo 7
dl 0
loc 331
ccs 85
cts 86
cp 0.9884
rs 8.6
c 0
b 0
f 0

17 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 15 3
A setup() 0 13 3
A validateTypes() 0 14 2
A validateProperties() 0 11 2
A getType() 0 4 1
A getId() 0 4 1
A getLanguage() 0 4 1
A getProperties() 0 4 1
A getProperty() 0 12 3
A isEmpty() 0 4 1
A validateProperty() 0 21 3
A validatePropertyStructure() 0 10 3
A validatePropertyProperties() 0 7 4
A validatePropertyName() 0 14 2
A validatePropertyValues() 0 12 2
A processPropertyValue() 0 15 3
A validateType() 0 5 2
1
<?php
2
3
/**
4
 * micrometa
5
 *
6
 * @category Jkphl
7
 * @package Jkphl\Micrometa
8
 * @subpackage Jkphl\Micrometa\Domain\Miom
9
 * @author Joschi Kuphal <[email protected]> / @jkphl
10
 * @copyright Copyright © 2017 Joschi Kuphal <[email protected]> / @jkphl
11
 * @license http://opensource.org/licenses/MIT The MIT License (MIT)
12
 */
13
14
/***********************************************************************************
15
 *  The MIT License (MIT)
16
 *
17
 *  Copyright © 2017 Joschi Kuphal <[email protected]> / @jkphl
18
 *
19
 *  Permission is hereby granted, free of charge, to any person obtaining a copy of
20
 *  this software and associated documentation files (the "Software"), to deal in
21
 *  the Software without restriction, including without limitation the rights to
22
 *  use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
23
 *  the Software, and to permit persons to whom the Software is furnished to do so,
24
 *  subject to the following conditions:
25
 *
26
 *  The above copyright notice and this permission notice shall be included in all
27
 *  copies or substantial portions of the Software.
28
 *
29
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
31
 *  FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
32
 *  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
33
 *  IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
34
 *  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35
 ***********************************************************************************/
36
37
namespace Jkphl\Micrometa\Domain\Item;
38
39
use Jkphl\Micrometa\Domain\Exceptions\InvalidArgumentException;
40
use Jkphl\Micrometa\Domain\Factory\IriFactory;
41
use Jkphl\Micrometa\Domain\Factory\PropertyListFactory;
42
use Jkphl\Micrometa\Domain\Factory\PropertyListFactoryInterface;
43
use Jkphl\Micrometa\Domain\Value\ValueInterface;
44
45
/**
46
 * Micro information item
47
 *
48
 * @package Jkphl\Micrometa
49
 * @subpackage Jkphl\Micrometa\Domain
50
 */
51
class Item implements ItemInterface
52
{
53
    /**
54
     * Item type(s)
55
     *
56
     * @var \stdClass[]
57
     */
58
    protected $type;
59
60
    /**
61
     * Item properties
62
     *
63
     * @var PropertyListInterface
64
     */
65
    protected $properties;
66
67
    /**
68
     * Item ID
69
     *
70
     * @var string
71
     */
72
    protected $itemId;
73
74
    /**
75
     * Item language
76
     *
77
     * @var string
78
     */
79
    protected $itemLanguage;
80
81
    /**
82
     * Property list factory
83
     *
84
     * @var PropertyListFactoryInterface
85
     */
86
    protected $propertyListFactory;
87
88
    /**
89
     * Item constructor
90
     *
91
     * @param string|\stdClass|\stdClass[] $type Item type(s)
92
     * @param \stdClass[] $properties Item properties
93
     * @param string|null $itemId Item id
94
     * @param string|null $itemLanguage Item language
95
     * @param PropertyListFactoryInterface|null $propertyListFactory Property list factory
96
     */
97 52
    public function __construct(
98
        $type,
99
        array $properties = [],
100
        $itemId = null,
101
        $itemLanguage = null,
102
        PropertyListFactoryInterface $propertyListFactory = null
103
    ) {
104 52
        $this->setup(
105 52
            $propertyListFactory ?: new PropertyListFactory(),
0 ignored issues
show
Compatibility introduced by
$propertyListFactory ?: ...y\PropertyListFactory() of type object<Jkphl\Micrometa\D...tyListFactoryInterface> is not a sub-type of object<Jkphl\Micrometa\D...ry\PropertyListFactory>. It seems like you assume a concrete implementation of the interface Jkphl\Micrometa\Domain\F...rtyListFactoryInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
106 52
            is_array($type) ? $type : [$type],
107
            $properties,
108 52
            trim($itemId),
109 52
            trim($itemLanguage)
110
        );
111 47
    }
112
113
    /**
114
     * Setup the item
115
     *
116
     * @param PropertyListFactory $propertyListFactory Property list factory
117
     * @param string[]|\stdClass[] $type Item type(s)
118
     * @param \stdClass[] $properties Item properties
119
     * @param string $itemId Item ID
120
     * @param string $itemLanguage Item language
121
     */
122 52
    protected function setup(
123
        PropertyListFactory $propertyListFactory,
124
        array $type,
125
        array $properties,
126
        $itemId,
127
        $itemLanguage
128
    ) {
129 52
        $this->propertyListFactory = $propertyListFactory;
130 52
        $this->type = $this->validateTypes($type);
0 ignored issues
show
Documentation introduced by
$type is of type array<integer,string|object<stdClass>>, but the function expects a array<integer,object<stdClass>>.

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...
131 50
        $this->properties = $this->validateProperties($properties);
132 47
        $this->itemId = $itemId ?: null;
133 47
        $this->itemLanguage = $itemLanguage ?: null;
134 47
    }
135
136
    /**
137
     * Validate and sanitize the item types
138
     *
139
     * @param \stdClass[] $types Item types
140
     * @return array Validated item types
141
     * @throws InvalidArgumentException If there are no valid types
142
     */
143 52
    protected function validateTypes(array $types)
144
    {
145 52
        $nonEmptyTypes = array_filter(array_map([$this, 'validateType'], $types));
146
147
        // If there are no valid types
148 51
        if (!count($nonEmptyTypes)) {
149 1
            throw new InvalidArgumentException(
150 1
                InvalidArgumentException::EMPTY_TYPES_STR,
151 1
                InvalidArgumentException::EMPTY_TYPES
152
            );
153
        }
154
155 50
        return array_values($nonEmptyTypes);
156
    }
157
158
    /**
159
     * Validate the item properties
160
     *
161
     * @param array $properties Item properties
162
     * @return PropertyListInterface Validated item properties
163
     * @throws InvalidArgumentException If the property name is empty
164
     */
165 50
    protected function validateProperties(array $properties)
166
    {
167 50
        $validatedProperties = $this->propertyListFactory->create();
168
169
        // Run through all validated properties
170 50
        foreach (array_filter(array_map([$this, 'validateProperty'], $properties)) as $property) {
171 34
            $validatedProperties->add($property);
172
        }
173
174 47
        return $validatedProperties;
175
    }
176
177
    /**
178
     * Return the item types
179
     *
180
     * @return \stdClass[] Item types
181
     */
182 26
    public function getType()
183
    {
184 26
        return $this->type;
185
    }
186
187
    /**
188
     * Return the item ID (if any)
189
     *
190
     * @return string|null Item id
191
     */
192 21
    public function getId()
193
    {
194 21
        return $this->itemId;
195
    }
196
197
    /**
198
     * Return the item language (if any)
199
     *
200
     * @return string|null Item language
201
     */
202 20
    public function getLanguage()
203
    {
204 20
        return $this->itemLanguage;
205
    }
206
207
    /**
208
     * Return all item properties
209
     *
210
     * @return PropertyListInterface Item properties list
211
     */
212 21
    public function getProperties()
213
    {
214 21
        return $this->properties;
215
    }
216
217
    /**
218
     * Return the values of a particular property
219
     *
220
     * @param string|\stdClass|Iri $name Property name
221
     * @param string|null $profile Property profile
222
     * @return array Item property values
223
     */
224 11
    public function getProperty($name, $profile = null)
225
    {
226 11
        $iri = IriFactory::create(
227 11
            (($profile === null) || is_object($name)) ?
228 9
                $name :
229
                (object)[
230 4
                    'profile' => $profile,
231 11
                    'name' => $name
232
                ]
233
        );
234 11
        return $this->properties->offsetGet($iri);
235
    }
236
237
    /**
238
     * Return whether the value should be considered empty
239
     *
240
     * @return boolean Value is empty
241
     */
242 23
    public function isEmpty()
243
    {
244 23
        return false;
245
    }
246
247
    /**
248
     * Validate a single property
249
     *
250
     * @param \stdClass $property Property
251
     * @return \stdClass Validated property
252
     */
253 39
    protected function validateProperty($property)
254
    {
255
        // Validate the property structure
256 39
        $this->validatePropertyStructure($property);
257
258
        // If the property has values
259 38
        if (count($property->values)) {
260
            // Validate the property name
261 37
            $property->name = $this->validatePropertyName($property);
262
263
            // Validate the property values
264 36
            $property->values = $this->validatePropertyValues($property->values);
265
266
            // If the property has significant values
267 35
            if (count($property->values)) {
268 34
                return $property;
269
            }
270
        }
271
272 2
        return null;
273
    }
274
275
    /**
276
     * Validate the structure of a property object
277
     *
278
     * @param \stdClass $property Property object
279
     * @throws InvalidArgumentException If the property object is invalid
280
     */
281 39
    protected function validatePropertyStructure($property)
282
    {
283
        // If the property object is invalid
284 39
        if (!is_object($property) || !$this->validatePropertyProperties($property)) {
285 1
            throw new InvalidArgumentException(
286 1
                InvalidArgumentException::INVALID_PROPERTY_STR,
287 1
                InvalidArgumentException::INVALID_PROPERTY
288
            );
289
        }
290 38
    }
291
292
    /**
293
     * Validate the properties of a property
294
     *
295
     * @param \stdClass $property Property
296
     * @return bool Property properties are valid
297
     */
298 39
    protected function validatePropertyProperties($property)
299
    {
300 39
        return isset($property->profile)
301 39
            && isset($property->name)
302 39
            && isset($property->values)
303 39
            && is_array($property->values);
304
    }
305
306
    /**
307
     * Validate a property name
308
     *
309
     * @param \stdClass $property Property
310
     * @return string Property name
311
     */
312 37
    protected function validatePropertyName($property)
313
    {
314 37
        $propertyName = trim($property->name);
315
316
        // If the property name is empty
317 37
        if (!strlen($propertyName)) {
318 1
            throw new InvalidArgumentException(
319 1
                InvalidArgumentException::EMPTY_PROPERTY_NAME_STR,
320 1
                InvalidArgumentException::EMPTY_PROPERTY_NAME
321
            );
322
        }
323
324 36
        return $propertyName;
325
    }
326
327
    /**
328
     * Validate a list of property values
329
     *
330
     * @param array $values Property values
331
     * @return array Validated property values
332
     * @throws InvalidArgumentException If the value is not a nested item
333
     */
334 36
    protected function validatePropertyValues(array $values)
335
    {
336 36
        $nonEmptyPropertyValues = [];
337
338
        // Run through all property values
339
        /** @var ValueInterface $value */
340 36
        foreach ($values as $value) {
341 36
            $this->processPropertyValue($value, $nonEmptyPropertyValues);
342
        }
343
344 35
        return $nonEmptyPropertyValues;
345
    }
346
347
    /**
348
     * Process a (non-empty) property value
349
     *
350
     * @param ValueInterface $value Property value
351
     * @param array $nonEmptyPropertyValues Non-empty property values
352
     */
353 36
    protected function processPropertyValue($value, array &$nonEmptyPropertyValues)
354
    {
355
        // If the value is not a nested item
356 36
        if (!($value instanceof ValueInterface)) {
357 1
            throw new InvalidArgumentException(
358 1
                sprintf(InvalidArgumentException::INVALID_PROPERTY_VALUE_STR, gettype($value)),
359 1
                InvalidArgumentException::INVALID_PROPERTY_VALUE
360
            );
361
        }
362
363
        // If the value isn't empty
364 35
        if (!$value->isEmpty()) {
365 34
            $nonEmptyPropertyValues[] = $value;
366
        }
367 35
    }
368
369
    /**
370
     * Validate a single item type
371
     *
372
     * @param \stdClass|Iri|string $type Item type
373
     * @return Iri|null Validated item type
374
     * @throws InvalidArgumentException If the item type object is invalid
375
     */
376 52
    protected function validateType($type)
377
    {
378 52
        $type = IriFactory::create($type);
379 51
        return strlen($type->name) ? $type : null;
380
    }
381
}
382