Completed
Push — master ( 754f5c...d85481 )
by Joschi
05:03
created

ItemFactory::__invoke()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 19
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 2.122

Importance

Changes 0
Metric Value
dl 0
loc 19
ccs 11
cts 16
cp 0.6875
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 16
nc 2
nop 1
crap 2.122
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
     * Process a language tagged property value
102
     *
103
     * @param string|\stdClass $propertyValue Property value
104
     * @return array Language and property value
105
     * @throws RuntimeException If this is an invalid language tagged value
106
     */
107 14
    protected function processLanguageTaggedPropertyValue($propertyValue)
108
    {
109 14
        $language = null;
110 14
        if (is_object($propertyValue)) {
111
            // If this is an invalid language tagged object
112 3
            if (!isset($propertyValue->lang) || !isset($propertyValue->value)) {
113 1
                throw new RuntimeException(
114 1
                    RuntimeException::INVALID_LANGUAGE_TAGGED_VALUE_STR,
115 1
                    RuntimeException::INVALID_LANGUAGE_TAGGED_VALUE
116
                );
117
            }
118
119 2
            $language = $propertyValue->lang;
120 2
            $propertyValue = $propertyValue->value;
121
        }
122 13
        return [$propertyValue, $language];
123
    }
124
125
    /**
126
     * Create an item instance
127
     *
128
     * The item object is expected to be layed out like this:
129
     *
130
     * {
131
     *     format: 2, // Parser formag
132
     *     type: 'type', // String / IRI object (see below) or list of strings / IRI objects
133
     *     properties: [...], // List of item property objects (see below)
134
     *     value: 'Item value', // Item value (optional)
135
     *     id: 'item-1', // Item ID (optional)
136
     *     children: [...] // Nested item objects (optional)
137
     * }
138
     *
139
     * The item property objects are expected to be layed out like this:
140
     *
141
     * {
142
     *      name: 'name', // Property name
143
     *      profile: 'http://microformats.org/profile/', // Profile
144
     *      values: [...] // List of property values
145
     * }
146
     *
147
     * Item property values may be either
148
     *
149
     * - a string: Interpreted as simple value
150
     * - an array: Interpreted as alternate simple values
151
     * - an object: Interpreted as an object property (recursively processed)
152
     *
153
     * IRI objects are expected to be layed out like this:
154
     *
155
     * {
156
     *      name: 'h-entry',
157
     *      profile: 'http://microformats.org/profile/', // Profile (optional)
158
     * }
159
     *
160
     * @param \stdClass $item Raw item
161
     * @return ItemInterface Item instance
162
     */
163 17
    public function __invoke(\stdClass $item)
164
    {
165 17
        $type = $this->normalizeEmptyValue($item, 'type');
166 17
        $itemId = $this->normalizeEmptyValue($item, 'id');
167 17
        $itemLanguage = $this->normalizeEmptyValue($item, 'lang');
168 17
        $value = $this->normalizeEmptyValue($item, 'value');
169 17
        $children = isset($item->children) ? array_map([$this, __METHOD__], $item->children) : [];
170 17
        $properties = $this->getProperties($item);
171 16
        return new Item(
172 16
            $this->format,
173 16
            $this->propertyListFactory,
174
            $type,
175
            $properties,
176
            $children,
177
            $itemId,
178
            $itemLanguage,
179 16
            $value
180
        );
181
    }
182
183
    /**
184
     * Normalize an empty item value
185
     *
186
     * @param \stdClass $item Item
187
     * @param string $property Property name
188
     * @return string|null Normalized property value
189
     */
190 17
    protected function normalizeEmptyValue(\stdClass $item, $property)
191
    {
192 17
        return isset($item->$property) ? $item->$property : null;
193
    }
194
195
    /**
196
     * Prepare item properties
197
     *
198
     * @param \stdClass $item Item
199
     * @return array Properties
200
     */
201 17
    protected function getProperties(\stdClass $item)
202
    {
203 17
        $properties = [];
204 17
        if (isset($item->properties) && is_array($item->properties)) {
205 16
            foreach ($item->properties as $property) {
206 16
                $this->processProperty($properties, $property);
207
            }
208
        }
209 16
        return $properties;
210
    }
211
212
213
    /**
214
     * Process a property
215
     *
216
     * @param array $properties Properties
217
     * @param \stdClass $property Property
218
     */
219 16
    protected function processProperty(array &$properties, $property)
220
    {
221
        try {
222 16
            if ($this->validatePropertyStructure($property)) {
223 15
                $property->values = $this->getPropertyValues($property->values);
224 13
                if (count($property->values)) {
225 14
                    $properties[] = $property;
226
                }
227
            }
228 2
        } catch (InvalidArgumentException $exception) {
229
            // Skip this property
230
        }
231 15
    }
232
233
    /**
234
     * Validate if an object is a valid property
235
     *
236
     * @param \stdClass $property Property
237
     * @return bool Is a valid property
238
     */
239 16
    protected function validatePropertyStructure($property)
240
    {
241 16
        return is_object($property) && isset($property->profile) && isset($property->name) && isset($property->values);
242
    }
243
244
    /**
245
     * Prepare item property values
246
     *
247
     * @param array $propertyValues Property values
248
     * @return array Expanded property values
249
     * @throws InvalidArgumentException If it's not a list of property values
250
     */
251 15
    protected function getPropertyValues($propertyValues)
252
    {
253
        // If it's not a list of property values
254 15
        if (!is_array($propertyValues)) {
255 1
            throw new InvalidArgumentException(
256 1
                InvalidArgumentException::INVALID_PROPERTY_VALUES_STR,
257 1
                InvalidArgumentException::INVALID_PROPERTY_VALUES
258
            );
259
        }
260
261 14
        return array_map([$this, 'processPropertyValue'], $propertyValues);
262
    }
263
}
264