GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Branch api-revamp (c37467)
by Yusuf
02:04
created

MicrodataElementParser::getChildElementNodes()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 5
nc 3
nop 1
dl 0
loc 11
ccs 6
cts 6
cp 1
crap 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace YusufKandemir\MicrodataParser;
4
5
class MicrodataElementParser
6
{
7
    /** @var array "tag name" to "attribute name" mapping */
8
    private static $tagNameLookup = [
9
        'audio' => 'src',
10
        'embed' => 'src',
11
        'iframe' => 'src',
12
        'img' => 'src',
13
        'source' => 'src',
14
        'track' => 'src',
15
        'video' => 'src',
16
        'a' => 'href',
17
        'area' => 'href',
18
        'link' => 'href',
19
        'object' => 'data',
20
        'data' => 'value',
21
        'meter' => 'value',
22
        'time' => 'datetime',
23
    ];
24
25
    /** @var array Attributes that have absolute values */
26
    private static $absoluteAttributes = ['src', 'href', 'data',];
27
28
    /**
29
     * Handler will be called with $value(non-absolute uri string) and $base(base uri) parameters
30
     *
31
     * Should return the processed uri (string)
32
     *
33
     * @var callable|null
34
     */
35
    private $absoluteUriHandler;
36
37
    /**
38
     * @param callable|null $absoluteUriHandler Can be set later with setAbsoluteUriHandler()
39
     */
40 26
    public function __construct(callable $absoluteUriHandler = null)
41
    {
42 13
        $this->absoluteUriHandler = $absoluteUriHandler ?: function ($value, $base) {
43 30
            return $base . $value;
44 39
        };
45 39
    }
46
47
    /**
48
     * Set absolute uri handler
49
     *
50
     * @param callable $handler
51
     */
52
    public function setAbsoluteUriHandler(callable $handler)
53
    {
54
        $this->absoluteUriHandler = $handler;
55
    }
56
57
    /**
58
     * @see https://www.w3.org/TR/2018/WD-microdata-20180426/#dfn-get-the-object
59
     *
60
     * @param \DOMElement $item
61
     * @param array $memory
62
     *
63
     * @return \stdClass
64
     */
65 39
    public function parse(\DOMElement $item, $memory = []) : \stdClass
66
    {
67 39
        $result = new \stdClass;
68
69 39
        $memory[] = $item;
70
71 39
        $result->type = $this->tokenizeAttribute($item, 'itemtype');
72
        // @todo Check if types are valid absolute urls
73
74 39
        if ($item->hasAttribute('itemid')) {
75 9
            $result->id = $item->getAttribute('itemid');
76
        }
77
        // @todo Check if item ids are valid absolute urls or like isbn:xxx
78
79 39
        $properties = new \stdClass;
80
81 39
        foreach ($this->getProperties($item) as $element) {
82 39
            $value = $this->getPropertyValue($element, $this->absoluteUriHandler);
83
84 39
            if ($this->isItem($value)) {
85 18
                foreach ($memory as $memory_item) {
86 18
                    if ($element->isSameNode($memory_item)) {
87
                        $value = 'ERROR';
88 18
                        break;
89
                    }
90
                }
91
92 18
                if ($value != 'ERROR') {
93 18
                    $value = $this->parse($value, $memory);
0 ignored issues
show
Bug introduced by
It seems like $value can also be of type string; however, parameter $item of YusufKandemir\MicrodataP...aElementParser::parse() does only seem to accept DOMElement, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

93
                    $value = $this->parse(/** @scrutinizer ignore-type */ $value, $memory);
Loading history...
94
                }
95
            }
96
97 39
            foreach ($this->getPropertyNames($element) as $name) {
98 39
                $properties->{$name}[] = $value;
99
            }
100
        }
101
102 39
        $result->properties = $properties;
103
104 39
        return $result;
105
    }
106
107
    /**
108
     * @see https://www.w3.org/TR/2018/WD-microdata-20180426/#dfn-item-properties for details of algorithm
109
     *
110
     * @param \DOMElement $element
111
     *
112
     * @return array
113
     */
114 39
    protected function getProperties(\DOMElement $element) : array
115
    {
116 39
        $results = [];
117 39
        $memory = [$element];
118 39
        $pending = $this->getChildElementNodes($element);
119
120 39
        $pending = array_merge($pending, $this->getReferenceElements($element));
121
122 39
        while ($pending) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $pending of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
123 39
            $current = array_pop($pending);
124
125 39
            foreach ($memory as $memory_item) {
126 39
                if ($current->isSameNode($memory_item)) {
127 39
                    continue 2; // Skip next part and continue while loop if memory contains $current
128
                }
129
            }
130
131 39
            $memory[] = $current;
132
133 39
            if (! $current->hasAttribute('itemscope')) {
134 39
                $pending = array_merge($pending, $this->getChildElementNodes($current));
135
            }
136
137 39
            if ($current->hasAttribute('itemprop') && $this->hasPropertyNames($current)) {
138 39
                $results[] = $current;
139
            }
140
        }
141
142 39
        return array_reverse($results);
143
    }
144
145
    /**
146
     * @param \DOMElement $element
147
     *
148
     * @return bool
149
     */
150 39
    public function hasPropertyNames(\DOMElement $element) : bool
151
    {
152 39
        return !empty($this->tokenizeAttribute($element, 'itemprop'));
153
    }
154
155
    /**
156
     * @see https://www.w3.org/TR/2018/WD-microdata-20180426/#dfn-property-name
157
     *
158
     * @param \DOMElement $element
159
     *
160
     * @return array
161
     */
162 39
    public function getPropertyNames(\DOMElement $element) : array
163
    {
164 39
        $tokens = $this->tokenizeAttribute($element, 'itemprop');
165
166 39
        $properties = [];
167
168 39
        foreach ($tokens as $token) {
169 39
            if (!$this->isAbsoluteUri($token) && $this->tokenizeAttribute($element, 'itemtype')) {
170 18
                $token = /*$vocabularyIdentifier . */ $token;
171
            }
172
173 39
            $properties[] = $token;
174
        }
175
176 39
        return \array_unique($properties);
177
    }
178
179
    /**
180
     * @see https://www.w3.org/TR/2018/WD-microdata-20180426/#dfn-property-value for details of algorithm
181
     *
182
     * @param \DOMElement $element
183
     * @param callable $absoluteUriHandler
184
     *
185
     * @return \DOMElement|string
186
     */
187 39
    public function getPropertyValue(\DOMElement $element, callable $absoluteUriHandler = null)
188
    {
189 39
        if ($element->hasAttribute('itemscope')) {
190 18
            return $element;
191
        }
192
193 39
        if ($element->hasAttribute('content')) {
194 9
            return $element->getAttribute('content');
195
        }
196
197 39
        $value = '';
198
199 39
        if (\array_key_exists($element->tagName, self::$tagNameLookup)) {
200 39
            $attribute = self::$tagNameLookup[$element->tagName];
201 39
            $value = $element->getAttribute($attribute);
202
203 39
            if (!empty($value) && \in_array($attribute, self::$absoluteAttributes) && !$this->isAbsoluteUri($value)) {
204 30
                $value = $absoluteUriHandler($value, $element->ownerDocument->documentURI);
205
            }
206
        }
207
208 39
        return $value ?: $element->textContent;
209
    }
210
211
    /**
212
     * Finds the elements that given element references through the document
213
     *
214
     * @see https://www.w3.org/TR/microdata/#dfn-item-properties 4th step
215
     *
216
     * @param \DOMElement $element
217
     *
218
     * @return array
219
     */
220 39
    protected function getReferenceElements(\DOMElement $element): array
221
    {
222 39
        $referenceElements = [];
223
224 39
        if ($element->hasAttribute('itemref')) {
225 12
            $tokens = $this->tokenizeAttribute($element, 'itemref');
226
227 12
            foreach ($tokens as $token) {
228 12
                $referenceElement = $element->ownerDocument->getElementById($token);
229
230 12
                if ($referenceElement instanceof \DOMElement) {
231 12
                    $referenceElements[] = $referenceElement;
232
                }
233
            }
234
        }
235
236 39
        return $referenceElements;
237
    }
238
239
    /**
240
     * Filters out TextNodes etc. and returns child ElementNodes as array
241
     *
242
     * @param \DOMElement $element
243
     *
244
     * @return array Result array which contains child ElementNodes
245
     */
246 39
    protected function getChildElementNodes(\DOMElement $element)
247
    {
248 39
        $childNodes = [];
249
250 39
        foreach ($element->childNodes as $childNode) {
251 39
            if ($childNode->nodeType == XML_ELEMENT_NODE) {
252 39
                $childNodes[] = $childNode;
253
            }
254
        }
255
256 39
        return $childNodes;
257
    }
258
259
    /**
260
     * Tokenizes value of given attribute
261
     *
262
     * @param \DOMElement $element
263
     * @param string $attributeName Name of the attribute
264
     *
265
     * @return array|array[]|false|string[]
266
     */
267 39
    public function tokenizeAttribute(\DOMElement $element, string $attributeName)
268
    {
269 39
        $attribute = [];
270
271 39
        if ($element->hasAttribute($attributeName)) {
272 39
            $attribute = $this->tokenize($element->getAttribute($attributeName));
273
        }
274
275 39
        return $attribute;
276
    }
277
278
    /**
279
     * Splits given attribute value in space characters to array
280
     *
281
     * @see \preg_split() for possible return values and behaviour
282
     *
283
     * @see https://www.w3.org/TR/2018/WD-microdata-20180426/#dfn-split-a-string-on-spaces for definition of tokens
284
     *
285
     * @param string $attribute
286
     *
287
     * @return array[]|false|string[]
288
     */
289 39
    protected function tokenize(string $attribute)
290
    {
291 39
        return preg_split('/\s+/', trim($attribute));
292
    }
293
294
    /**
295
     * Checks a string to see if its absolute uri or not
296
     * Note: As it uses a simple regex to check, it is not that reliable
297
     *
298
     * @see \preg_match() for return values
299
     *
300
     * @param string $uri
301
     *
302
     * @return false|int
303
     */
304 39
    protected function isAbsoluteUri(string $uri)
305
    {
306 39
        return preg_match("/^\w+:/", trim($uri));
307
    }
308
309
    /**
310
     * Check if the given parameter is a DOMElement and has itemscope attribute
311
     *
312
     * @param $element
313
     *
314
     * @return bool
315
     */
316 39
    protected function isItem($element) : bool
317
    {
318 39
        return $element instanceof \DOMElement && $element->hasAttribute('itemscope');
319
    }
320
}
321