Completed
Push — master ( c44150...21058a )
by Joschi
09:02
created

ItemFactory   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 210
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 27
lcom 1
cbo 6
dl 0
loc 210
ccs 60
cts 60
cp 1
rs 10
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A processPropertyValue() 0 14 4
A normalizeEmptyValue() 0 4 2
A getProperties() 0 10 4
A processProperty() 0 13 4
A validatePropertyStructure() 0 4 4
A getPropertyValues() 0 12 2
A processLanguageTaggedPropertyValue() 0 17 4
A __invoke() 0 19 2
1
<?php
2
3
/**
4
 * micrometa
5
 *
6
 * @category Jkphl
7
 * @package Jkphl\Micrometa
8
 * @subpackage Jkphl\Micrometa\Application
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\Application\Factory;
38
39
use Jkphl\Micrometa\Application\Contract\ValueInterface;
40
use Jkphl\Micrometa\Application\Exceptions\InvalidArgumentException;
41
use Jkphl\Micrometa\Application\Item\Item;
42
use Jkphl\Micrometa\Application\Item\ItemInterface;
43
use Jkphl\Micrometa\Application\Value\AlternateValues;
44
use Jkphl\Micrometa\Application\Value\StringValue;
45
use Jkphl\Micrometa\Ports\Exceptions\RuntimeException;
46
47
/**
48
 * Item factory
49
 *
50
 * @package Jkphl\Micrometa
51
 * @subpackage Jkphl\Micrometa\Application
52
 */
53
class ItemFactory
54
{
55
    /**
56
     * Parser format
57
     *
58
     * @var int
59
     */
60
    protected $format;
61
    /**
62
     * Property lit factory
63
     *
64
     * @var PropertyListFactoryInterface
65
     */
66
    protected $propertyListFactory;
67
68
    /**
69
     * Item factory constructor
70
     *
71
     * @param int $format Parser format
72
     */
73 18
    public function __construct($format)
74
    {
75 18
        $this->format = $format;
76 18
        $this->propertyListFactory = new PropertyListFactory();
77 18
    }
78
79
    /**
80
     * Prepare a single property value
81
     *
82
     * @param mixed $propertyValue Property Value
83
     * @return ValueInterface Value
84
     */
85 14
    protected function processPropertyValue($propertyValue)
86
    {
87
        // If this is an item value
88 14
        if (is_object($propertyValue) && isset($propertyValue->type)) {
89 9
            return $this->__invoke($propertyValue);
90
91
            // Else these are alternate values
92 14
        } elseif (is_array($propertyValue)) {
93 2
            return new AlternateValues(array_map([$this, __METHOD__], $propertyValue));
94
        }
95
96 14
        list($propertyValue, $language) = $this->processLanguageTaggedPropertyValue($propertyValue);
97 13
        return new StringValue($propertyValue, $language);
98
    }
99
100
    /**
101
     * Create an item instance
102
     *
103
     * The item object is expected to be layed out like this:
104
     *
105
     * {
106
     *     format: 2, // Parser formag
107
     *     type: 'type', // String / IRI object (see below) or list of strings / IRI objects
108
     *     properties: [...], // List of item property objects (see below)
109
     *     value: 'Item value', // Item value (optional)
110
     *     id: 'item-1', // Item ID (optional)
111
     *     children: [...] // Nested item objects (optional)
112
     * }
113
     *
114
     * The item property objects are expected to be layed out like this:
115
     *
116
     * {
117
     *      name: 'name', // Property name
118
     *      profile: 'http://microformats.org/profile/', // Profile
119
     *      values: [...] // List of property values
120
     * }
121
     *
122
     * Item property values may be either
123
     *
124
     * - a string: Interpreted as simple value
125
     * - an array: Interpreted as alternate simple values
126
     * - an object: Interpreted as an object property (recursively processed)
127
     *
128
     * IRI objects are expected to be layed out like this:
129
     *
130
     * {
131
     *      name: 'h-entry',
132
     *      profile: 'http://microformats.org/profile/', // Profile (optional)
133
     * }
134
     *
135
     * @param \stdClass $item Raw item
136
     * @return ItemInterface Item instance
137
     */
138 17
    public function __invoke(\stdClass $item)
139
    {
140 17
        $type = $this->normalizeEmptyValue($item, 'type');
141 17
        $itemId = $this->normalizeEmptyValue($item, 'id');
142 17
        $itemLanguage = $this->normalizeEmptyValue($item, 'lang');
143 17
        $value = $this->normalizeEmptyValue($item, 'value');
144 17
        $children = isset($item->children) ? array_map([$this, __METHOD__], $item->children) : [];
145 17
        $properties = $this->getProperties($item);
146 16
        return new Item(
147 16
            $this->format,
148 16
            $this->propertyListFactory,
149 16
            $type,
150 16
            $properties,
151 16
            $children,
152 16
            $itemId,
153 16
            $itemLanguage,
154 16
            $value
155
        );
156
    }
157
158
    /**
159
     * Normalize an empty item value
160
     *
161
     * @param \stdClass $item Item
162
     * @param string $property Property name
163
     * @return string|null Normalized property value
164
     */
165 17
    protected function normalizeEmptyValue(\stdClass $item, $property)
166
    {
167 17
        return isset($item->$property) ? $item->$property : null;
168
    }
169
170
    /**
171
     * Prepare item properties
172
     *
173
     * @param \stdClass $item Item
174
     * @return array Properties
175
     */
176 17
    protected function getProperties(\stdClass $item)
177
    {
178 17
        $properties = [];
179 17
        if (isset($item->properties) && is_array($item->properties)) {
180 16
            foreach ($item->properties as $property) {
181 16
                $this->processProperty($properties, $property);
182
            }
183
        }
184 16
        return $properties;
185
    }
186
187
    /**
188
     * Process a property
189
     *
190
     * @param array $properties Properties
191
     * @param \stdClass $property Property
192
     */
193 16
    protected function processProperty(array &$properties, $property)
194
    {
195
        try {
196 16
            if ($this->validatePropertyStructure($property)) {
197 15
                $property->values = $this->getPropertyValues($property->values);
198 13
                if (count($property->values)) {
199 14
                    $properties[] = $property;
200
                }
201
            }
202 2
        } catch (InvalidArgumentException $exception) {
203
            // Skip this property
204
        }
205 15
    }
206
207
    /**
208
     * Validate if an object is a valid property
209
     *
210
     * @param \stdClass $property Property
211
     * @return bool Is a valid property
212
     */
213 16
    protected function validatePropertyStructure($property)
214
    {
215 16
        return is_object($property) && isset($property->profile) && isset($property->name) && isset($property->values);
216
    }
217
218
    /**
219
     * Prepare item property values
220
     *
221
     * @param array $propertyValues Property values
222
     * @return array Expanded property values
223
     * @throws InvalidArgumentException If it's not a list of property values
224
     */
225 15
    protected function getPropertyValues($propertyValues)
226
    {
227
        // If it's not a list of property values
228 15
        if (!is_array($propertyValues)) {
229 1
            throw new InvalidArgumentException(
230 1
                InvalidArgumentException::INVALID_PROPERTY_VALUES_STR,
231 1
                InvalidArgumentException::INVALID_PROPERTY_VALUES
232
            );
233
        }
234
235 14
        return array_map([$this, 'processPropertyValue'], $propertyValues);
236
    }
237
238
    /**
239
     * Process a language tagged property value
240
     *
241
     * @param string|\stdClass $propertyValue Property value
242
     * @return array Language and property value
243
     * @throws RuntimeException If this is an invalid language tagged value
244
     */
245 14
    protected function processLanguageTaggedPropertyValue($propertyValue)
246
    {
247 14
        $language = null;
248 14
        if (is_object($propertyValue)) {
249
            // If this is an invalid language tagged object
250 3
            if (!isset($propertyValue->lang) || !isset($propertyValue->value)) {
251 1
                throw new RuntimeException(
252 1
                    RuntimeException::INVALID_LANGUAGE_TAGGED_VALUE_STR,
253 1
                    RuntimeException::INVALID_LANGUAGE_TAGGED_VALUE
254
                );
255
            }
256
257 2
            $language = $propertyValue->lang;
258 2
            $propertyValue = $propertyValue->value;
259
        }
260 13
        return [$propertyValue, $language];
261
    }
262
}
263