Completed
Push — master ( 9c755e...912090 )
by Joschi
02:56
created

MicrodataElementProcessor::processProperties()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2.1481

Importance

Changes 0
Metric Value
dl 0
loc 14
ccs 6
cts 9
cp 0.6667
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 9
nc 2
nop 3
crap 2.1481
1
<?php
2
3
/**
4
 * rdfa-lite-microdata
5
 *
6
 * @category Jkphl
7
 * @package Jkphl\RdfaLiteMicrodata
8
 * @subpackage Jkphl\RdfaLiteMicrodata\Infrastructure
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\RdfaLiteMicrodata\Infrastructure\Parser;
38
39
use Jkphl\RdfaLiteMicrodata\Application\Context\ContextInterface;
40
use Jkphl\RdfaLiteMicrodata\Application\Parser\DOMIterator;
41
use Jkphl\RdfaLiteMicrodata\Application\Parser\RootThing;
42
use Jkphl\RdfaLiteMicrodata\Domain\Thing\ThingInterface;
43
use Jkphl\RdfaLiteMicrodata\Domain\Type\TypeInterface;
44
use Jkphl\RdfaLiteMicrodata\Domain\Vocabulary\VocabularyInterface;
45
46
/**
47
 * Microdata element processor
48
 *
49
 * @package Jkphl\RdfaLiteMicrodata
50
 * @subpackage Jkphl\RdfaLiteMicrodata\Infrastructure
51
 */
52
class MicrodataElementProcessor extends AbstractElementProcessor
53
{
54
    /**
55
     * Constructor
56
     */
57 4
    public function __construct()
58
    {
59 4
        $this->setHtml(true);
60 4
    }
61
62
    /**
63
     * Process a DOM element
64
     *
65
     * @param \DOMElement $element DOM element
66
     * @param ContextInterface $context Inherited Context
67
     * @return ContextInterface Local context for this element
68
     */
69 4
    public function processElement(\DOMElement $element, ContextInterface $context)
70
    {
71
        // Create a property
72 4
        $propertyContext = $this->processProperty($element, $context);
73
74
        // Process the element in case it's an anonymous thing with no children
75 4
        if (!$element->childNodes->length) {
76 4
            $this->processChild($element, $context);
77
        }
78
79 4
        return $propertyContext;
80
    }
81
82
    /**
83
     * Create a property
84
     *
85
     * @param \DOMElement $element DOM element
86
     * @param ContextInterface $context Inherited Context
87
     * @return ContextInterface Local context for this element
88
     */
89 4
    protected function processProperty(\DOMElement $element, ContextInterface $context)
90
    {
91 4
        if ($element->hasAttribute('itemprop') && !($context->getParentThing() instanceof RootThing)) {
92 4
            $itemprops = trim($element->getAttribute('itemprop'));
93 4
            $itemprops = strlen($itemprops) ? preg_split('/\s+/', $itemprops) : [];
94 4
            $context = $this->processProperties($itemprops, $element, $context);
95
        }
96
97 4
        return $context;
98
    }
99
100
    /**
101
     * Process properties
102
     *
103
     * @param array $itemprops Properties
104
     * @param \DOMElement $element DOM element
105
     * @param ContextInterface $context Inherited Context
106
     * @return ContextInterface Local context for this element
107
     */
108 4
    protected function processProperties(array $itemprops, \DOMElement $element, ContextInterface $context)
109
    {
110 4
        foreach ($itemprops as $index => $itemprop) {
111 4
            $context = $this->processPropertyPrefixName(
112 4
                null,
113
                $itemprop,
114
                $element,
115
                $context,
116 4
                $index == (count($itemprops) - 1)
117
            );
118
        }
119
120 4
        return $context;
121
    }
122
123
    /**
124
     * Create a nested child
125
     *
126
     * @param \DOMElement $element DOM element
127
     * @param ContextInterface $context Context
128
     * @return ContextInterface Context for children
129
     */
130 4
    protected function processChild(\DOMElement $element, ContextInterface $context)
131
    {
132 4
        if ($element->hasAttribute('itemscope') && empty($element->getAttribute('itemprop'))) {
133 4
            $context = $this->createAndAddChild($element, $context);
134
        }
135
136 4
        return $context;
137
    }
138
139
    /**
140
     * Create and add a nested child
141
     *
142
     * @param \DOMElement $element DOM element
143
     * @param ContextInterface $context Context
144
     * @return ContextInterface Context for children
145
     */
146 4
    protected function createAndAddChild(\DOMElement $element, ContextInterface $context)
147
    {
148 4
        $thing = $this->getThing(
149 4
            trim($element->getAttribute('itemtype')) ?: null,
150 4
            trim($element->getAttribute('itemid')) ?: null,
151 4
            $context
152
        );
153
154
        // Process item references
155 4
        $this->processItemReferences($element, $context, $thing);
156
157
        // Add the new thing as a child to the current context
158
        // and set the thing as parent thing for nested iterations
159 4
        return $context->addChild($thing)->setParentThing($thing);
160
    }
161
162
    /**
163
     * Process item references
164
     *
165
     * @param \DOMElement $element DOM element
166
     * @param ContextInterface $context Context
167
     * @param ThingInterface $thing Thing
168
     * @return ThingInterface Thing
169
     */
170 4
    protected function processItemReferences(\DOMElement $element, ContextInterface $context, ThingInterface $thing)
171
    {
172
        // If the element has item references
173 4
        if ($element->hasAttribute('itemref')) {
174 1
            $itemrefElements = $this->getItemReferenceElements($element);
175 1
            if (count($itemrefElements)) {
176 1
                $this->processItemReferenceElements($itemrefElements, $context, $thing);
177
            }
178
        }
179
180 4
        return $thing;
181
    }
182
183
    /**
184
     * Find all reference elements
185
     *
186
     * @param \DOMElement $element DOM element
187
     * @return \DOMElement[] Reference elements
188
     */
189 1
    protected function getItemReferenceElements(\DOMElement $element)
190
    {
191 1
        $itemrefElements = [];
192 1
        $itemrefs = trim($element->getAttribute('itemref'));
193 1
        $itemrefs = strlen($itemrefs) ? preg_split('/\s+/', $itemrefs) : [];
194 1
        foreach ($itemrefs as $itemref) {
195 1
            $itemrefElement = $element->ownerDocument->getElementById($itemref);
196 1
            if ($itemrefElement instanceof \DOMElement) {
197 1
                $itemrefElements[] = $itemrefElement;
198
            }
199
        }
200 1
        return $itemrefElements;
201
    }
202
203
    /**
204
     * Process item reference elements
205
     *
206
     * @param \DOMElement[] $itemrefElements Item reference DOM elements
207
     * @param ContextInterface $context Context
208
     * @param ThingInterface $thing Thing
209
     * @return void
210
     */
211 1
    protected function processItemReferenceElements(
212
        array $itemrefElements,
213
        ContextInterface $context,
214
        ThingInterface $thing
215
    ) {
216 1
        $iterator = new DOMIterator(
217
            $itemrefElements,
218 1
            $context->setParentThing($thing),
219 1
            new MicrodataElementProcessor()
220
        );
221
222
        // Iterate through all $node
223 1
        foreach ($iterator->getRecursiveIterator() as $node) {
224 1
            $node || true;
225
        }
226 1
    }
227
228
    /**
229
     * Return the resource ID
230
     *
231
     * @param \DOMElement $element DOM element
232
     * @return string|null Resource ID
233
     */
234 4
    protected function getResourceId(\DOMElement $element)
235
    {
236 4
        return trim($element->getAttribute('itemid')) ?: null;
237
    }
238
239
    /**
240
     * Return a property child value
241
     *
242
     * @param \DOMElement $element DOM element
243
     * @param ContextInterface $context Context
244
     * @return ThingInterface|null Property child value
245
     */
246 4
    protected function getPropertyChildValue(\DOMElement $element, ContextInterface $context)
247
    {
248
        // If the property creates a new type: Return the element itself
249 4
        if ($element->hasAttribute('itemscope')) {
250 4
            $thing = $this->getThing($element->getAttribute('itemtype'), null, $context);
251 4
            return $this->processItemReferences($element, $context, $thing);
252
        }
253
254 4
        return null;
255
    }
256
257
    /**
258
     * Return a vocabulary by prefix with fallback to the default vocabulary
259
     *
260
     * @param string $prefix Vocabulary prefix
261
     * @param ContextInterface $context Context
262
     * @return VocabularyInterface Vocabulary
263
     */
264 4
    protected function getVocabulary($prefix, ContextInterface $context)
265
    {
266
        /** @var VocabularyInterface $vocabulary */
267 4
        $vocabulary = $prefix ?: $context->getDefaultVocabulary();
268 4
        return $vocabulary;
269
    }
270
271
    /**
272
     * Split a value into a vocabulary prefix and a name
273
     *
274
     * @param string $prefixName Prefixed name
275
     * @return array Prefix and name
276
     */
277 3
    protected function getPrefixName($prefixName)
278
    {
279 3
        return [null, $prefixName];
280
    }
281
282
    /**
283
     * Instanciate a type
284
     *
285
     * @param string $prefixedType Prefixed type
286
     * @param ContextInterface $context Context
287
     * @return TypeInterface Type
288
     */
289 3
    protected function getType($prefixedType, ContextInterface $context)
290
    {
291 3
        $defaultVocabulary = $context->getDefaultVocabulary();
292 3
        if ($defaultVocabulary && strpos($prefixedType, $defaultVocabulary->getUri()) === 0) {
293 3
            $prefixedType = substr($prefixedType, strlen($defaultVocabulary->getUri()));
294
        }
295 3
        return parent::getType($prefixedType, $context);
296
    }
297
}
298