Completed
Pull Request — master (#257)
by Éloi
01:56
created

Media::handleMediaGroup()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 5
cts 5
cp 1
rs 10
c 0
b 0
f 0
cc 4
nc 3
nop 2
crap 4
1
<?php declare(strict_types=1);
2
/*
3
 * This file is part of the feed-io package.
4
 *
5
 * (c) Alexandre Debril <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace FeedIo\Rule;
12
13
use FeedIo\Feed\Item\MediaInterface;
14
use FeedIo\Feed\ItemInterface;
15
use FeedIo\Feed\Item\MediaCategory;
16
use FeedIo\Feed\Item\MediaCommunity;
17
use FeedIo\Feed\Item\MediaContent;
18
use FeedIo\Feed\Item\MediaContentMedium;
19
use FeedIo\Feed\Item\MediaContentExpression;
20
use FeedIo\Feed\Item\MediaCopyright;
21
use FeedIo\Feed\Item\MediaDescriptionType;
22
use FeedIo\Feed\Item\MediaEmbed;
23
use FeedIo\Feed\Item\MediaTitleType;
24
use FeedIo\Feed\Item\MediaHash;
25
use FeedIo\Feed\Item\MediaHashAlgo;
26
use FeedIo\Feed\Item\MediaLicense;
27
use FeedIo\Feed\Item\MediaCreditScheme;
28
use FeedIo\Feed\Item\MediaPeerLink;
29
use FeedIo\Feed\Item\MediaPriceType;
30
use FeedIo\Feed\Item\MediaPlayer;
31
use FeedIo\Feed\Item\MediaTextType;
32
use FeedIo\Feed\Item\MediaRating;
33
use FeedIo\Feed\Item\MediaRestriction;
34
use FeedIo\Feed\Item\MediaRestrictionRelationship;
35
use FeedIo\Feed\Item\MediaRestrictionType;
36
use FeedIo\Feed\Item\MediaRightsStatus;
37
use FeedIo\Feed\Item\MediaStatus;
38
use FeedIo\Feed\Item\MediaStatusValue;
39
use FeedIo\Feed\Item\MediaCredit;
40
use FeedIo\Feed\Item\MediaPrice;
41
use FeedIo\Feed\Item\MediaSubtitle;
42
use FeedIo\Feed\Item\MediaText;
43
use FeedIo\Feed\Item\MediaThumbnail;
44
use FeedIo\Feed\Item\MediaScene;
45
use FeedIo\Feed\NodeInterface;
46
use FeedIo\Parser\UrlGenerator;
47
use FeedIo\RuleAbstract;
48
49
class Media extends RuleAbstract
50
{
51
    const NODE_NAME = 'enclosure';
52
53
    const MRSS_NAMESPACE = "http://search.yahoo.com/mrss/";
54
55
    protected $urlAttributeName = 'url';
56
57
    /**
58
     * @var UrlGenerator
59
     */
60
    protected $urlGenerator;
61
62 71
    public function __construct(string $nodeName = null)
63
    {
64 71
        $this->urlGenerator = new UrlGenerator();
65 71
        parent::__construct($nodeName);
66 71
    }
67
68
    /**
69
     * @return string
70
     */
71 5
    public function getUrlAttributeName() : string
72
    {
73 5
        return $this->urlAttributeName;
74
    }
75
76
    /**
77
     * @param  string $name
78
     */
79 13
    public function setUrlAttributeName(string $name) : void
80
    {
81 13
        $this->urlAttributeName = $name;
82 13
    }
83
84
    /**
85
     * @param  NodeInterface $node
86
     * @param  \DOMElement   $element
87
     */
88 51
    public function setProperty(NodeInterface $node, \DOMElement $element) : void
89
    {
90 51
        if ($node instanceof ItemInterface) {
91 51
            switch ($element->nodeName) {
92 51
                case 'media:content':
93 45
                    $this->handleMediaRoot($node, $element);
94 45
                    break;
95
96 7
                case 'media:group':
97 3
                    $this->handleMediaGroup($node, $element);
98 3
                    break;
99
100
                default:
101 4
                    $media = $node->newMedia();
102 4
                    $media->setNodeName($element->nodeName);
103
                    $media
104 4
                        ->setType($this->getAttributeValue($element, 'type'))
105 4
                        ->setLength($this->getAttributeValue($element, 'length'));
106 4
                    $this->setUrl($media, $node, $this->getAttributeValue($element, $this->getUrlAttributeName()));
107 4
                    $node->addMedia($media);
108 4
                    break;
109
            }
110
        }
111 51
    }
112
113
    /**
114
     * @param MediaInterface $media
115
     * @param NodeInterface $node
116
     * @param string|null $url
117
     */
118 51
    protected function setUrl(MediaInterface $media, NodeInterface $node, string $url = null): void
119
    {
120 51
        if (! is_null($url)) {
121 51
            $media->setUrl(
122 51
                $this->urlGenerator->getAbsolutePath($url, $node->getHost())
123
            );
124
        }
125 51
    }
126
127
    /**
128
     * @param  \DomDocument   $document
129
     * @param  MediaInterface $media
130
     * @return \DomElement
131
     */
132 1
    public function createMediaElement(\DomDocument $document, MediaInterface $media) : \DOMElement
133
    {
134 1
        $element = $document->createElement($this->getNodeName());
135 1
        $element->setAttribute($this->getUrlAttributeName(), $media->getUrl());
136 1
        $element->setAttribute('type', $media->getType() ?? '');
137 1
        $element->setAttribute('length', $media->getLength() ?? '');
138
139 1
        return $element;
140
    }
141
142
    /**
143
     * @param NodeInterface $node
144
     * @param \DomElement $element
145
     * @return void
146
     */
147 3
    protected function handleMediaGroup(NodeInterface $node, \DOMElement $element): void
148
    {
149 3
        foreach ($element->childNodes as $mediaContentNode) {
150 3
            if (is_a($mediaContentNode, "DOMNode") && $mediaContentNode->nodeName == "media:content") {
151 3
                $this->handleMediaRoot($node, $mediaContentNode);
152
            }
153
        }
154 3
    }
155
156
    /**
157
     * @param NodeInterface $node
158
     * @param \DomElement $element
159
     * @return void
160
     */
161 47
    protected function handleMediaRoot(NodeInterface $node, \DOMElement $element): void
162
    {
163 47
        $media = $node->newMedia();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface FeedIo\Feed\NodeInterface as the method newMedia() does only exist in the following implementations of said interface: FeedIo\Feed\Item.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
164 47
        $this->setUrl($media, $node, $this->getAttributeValue($element, "url"));
165 47
        $media->setNodeName($element->nodeName);
166
167 47
        $this->handleMediaContent($element, $media);
168
169
        $tags = array(
170 47
            'media:rating' => 'handleMediaRating',
171
            'media:title' => 'handleMediaTitle',
172
            'media:description' => 'handleMediaDescription',
173
            'media:keywords' => 'handleMediaKeywords',
174
            'media:thumbnail' => 'handleMediaThumbnail',
175
            'media:category' => 'handleMediaCategory',
176
            'media:hash' => 'handleMediaHash',
177
            'media:player' => 'handleMediaPlayer',
178
            'media:credit' => 'handleMediaCredit',
179
            'media:copyright' => 'handleMediaCopyright',
180
            'media:text' => 'handleMediaText',
181
            'media:restriction' => 'handleMediaRestriction',
182
            'media:community' => 'handleMediaCommunity',
183
            'media:comments' => 'handleMediaComments',
184
            'media:embed' => 'handleMediaEmbed',
185
            'media:responses' => 'handleMediaResponses',
186
            'media:backLinks' => 'handleMediaBacklinks',
187
            'media:status' => 'handleMediaStatus',
188
            'media:price' => 'handleMediaPrice',
189
            'media:license' => 'handleMediaLicense',
190
            'media:subTitle' => 'handleMediaSubtitle',
191
            'media:peerLink' => 'handleMediaPeerlink',
192
            'media:rights' => 'handleMediaRights',
193
            'media:scenes' => 'handleMediaScenes',
194
        );
195
196 47
        foreach ($tags as $tag => $callback) {
197 47
            $nodes = $this->findTags($element, $tag);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $nodes is correct as $this->findTags($element, $tag) (which targets FeedIo\Rule\Media::findTags()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
198 47
            if ($nodes) {
199 45
                $this->$callback($nodes, $media);
200
            }
201
        }
202
203 47
        $node->addMedia($media);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface FeedIo\Feed\NodeInterface as the method addMedia() does only exist in the following implementations of said interface: FeedIo\Feed\Item.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
204 47
    }
205
206
    /**
207
     * @param \DomElement $element
208
     * @param MediaInterface $media
209
     * @return void
210
     */
211 47
    protected function handleMediaContent(?\DOMElement $element, MediaInterface $media) : void
212
    {
213 47
        $media->setUrl($this->getAttributeValue($element, "url"));
214 47
        $media->setType($this->getAttributeValue($element, "type"));
215
216 47
        $content = new MediaContent();
217 47
        $content->setFileSize(intval($this->getAttributeValue($element, "fileSize")));
218 47
        $content->setBitrate(intval($this->getAttributeValue($element, "bitrate")));
219 47
        $content->setFramerate(intval($this->getAttributeValue($element, "framerate")));
220 47
        $content->setSamplingrate(floatval($this->getAttributeValue($element, "samplingrate")));
221 47
        $content->setDuration(intval($this->getAttributeValue($element, "duration")));
222 47
        $content->setHeight(intval($this->getAttributeValue($element, "height")));
223 47
        $content->setWidth(intval($this->getAttributeValue($element, "width")));
224 47
        $content->setLang($this->getAttributeValue($element, "lang"));
225 47
        $content->setExpression(MediaContentExpression::fromXML(
226 47
            $this->getAttributeValue($element, "expression")
227
        ));
228 47
        $content->setMedium(
229 47
            MediaContentMedium::fromXML(
230 47
                $this->getAttributeValue($element, "medium")
231
            )
232
        );
233
234 47
        switch ($this->getAttributeValue($element, "isDefault")) {
235 47
            case "true":
236 1
                $content->setDefault(true);
237 1
                break;
238 46
            case "false":
239
                $content->setDefault(false);
240
                break;
241
        }
242 47
        $media->setContent($content);
243 47
    }
244
245
    /**
246
     * @param \DomElement $elements
247
     * @param MediaInterface $media
248
     * @return void
249
     */
250 2
    protected function handleMediaRating(?\DOMNodeList $elements, MediaInterface $media) : void
251
    {
252 2
        $element = $elements->item(0);
253 2
        $rating = new MediaRating();
254
255 2
        $rating->setContent($element->nodeValue);
256 2
        $rating->setScheme($this->getAttributeValue($element, "scheme") ?: 'urn:simple');
257
258 2
        $media->setRating($rating);
259 2
    }
260
261
    /**
262
     * @param \DomElement $elements
263
     * @param MediaInterface $media
264
     * @return void
265
     */
266 4
    protected function handleMediaTitle(?\DOMNodeList $elements, MediaInterface $media) : void
267
    {
268 4
        $element = $elements->item(0);
269
270 4
        $media->setTitle($element->nodeValue);
271 4
        $media->setTitleType(MediaTitleType::fromXML(
272 4
            $this->getAttributeValue($element, "type")
273
        ));
274 4
    }
275
276
    /**
277
     * @param \DomElement $elements
278
     * @param MediaInterface $media
279
     * @return void
280
     */
281 5
    protected function handleMediaDescription(?\DOMNodeList $elements, MediaInterface $media) : void
282
    {
283 5
        $element = $elements->item(0);
284
285 5
        $media->setDescription($element->nodeValue);
286 5
        $media->setDescriptionType(MediaDescriptionType::fromXML(
287 5
            $this->getAttributeValue($element, "type")
288
        ));
289 5
    }
290
291
    /**
292
     * @param \DomElement $elements
293
     * @param MediaInterface $media
294
     * @return void
295
     */
296 1
    protected function handleMediaKeywords(?\DOMNodeList $elements, MediaInterface $media) : void
297
    {
298 1
        $element = $elements->item(0);
299
300 1
        $media->setKeywords(array_map("trim", explode(
301 1
            ',',
302 1
            $element->nodeValue
303
        )));
304 1
    }
305
306
    /**
307
     * @param \DomElement $elements
308
     * @param MediaInterface $media
309
     * @return void
310
     */
311 3
    protected function handleMediaThumbnail(?\DOMNodeList $elements, MediaInterface $media) : void
312
    {
313 3
        $element = $elements->item(0);
314 3
        $thumbnail = new MediaThumbnail();
315
316 3
        $thumbnail->setUrl($this->getAttributeValue($element, "url"));
317 3
        $thumbnail->setWidth(intval($this->getAttributeValue($element, "width")));
318 3
        $thumbnail->setHeight(intval($this->getAttributeValue($element, "height")));
319 3
        if ($element->hasAttribute("time")) {
320 1
            $thumbnail->setTime(new \DateTime($this->getAttributeValue($element, "time")));
321
        }
322
323 3
        $media->setThumbnail($thumbnail);
324 3
    }
325
326
    /**
327
     * @param \DomElement $elements
328
     * @param MediaInterface $media
329
     * @return void
330
     */
331 2
    protected function handleMediaCategory(?\DOMNodeList $elements, MediaInterface $media) : void
332
    {
333 2
        $element = $elements->item(0);
334 2
        $category = new MediaCategory();
335
336 2
        $category->setText($element->nodeValue);
337 2
        $category->setLabel($this->getAttributeValue($element, "label"));
338 2
        $default_scheme = "http://search.yahoo.com/mrss/category_schema";
339 2
        $category->setScheme($this->getAttributeValue($element, "scheme") ?: $default_scheme);
340
341 2
        $media->setCategory($category);
342 2
    }
343
344
    /**
345
     * @param \DomElement $elements
346
     * @param MediaInterface $media
347
     * @return void
348
     */
349 2
    protected function handleMediaHash(?\DOMNodeList $elements, MediaInterface $media) : void
350
    {
351 2
        $element = $elements->item(0);
352 2
        $hash = new MediaHash();
353
354 2
        $hash->setContent($element->nodeValue);
355 2
        $hash->setAlgo(MediaHashAlgo::fromXML($this->getAttributeValue($element, "algo")));
356
357 2
        $media->setHash($hash);
358 2
    }
359
360
    /**
361
     * @param \DomElement $elements
362
     * @param MediaInterface $media
363
     * @return void
364
     */
365 2
    protected function handleMediaPlayer(?\DOMNodeList $elements, MediaInterface $media) : void
366
    {
367 2
        $element = $elements->item(0);
368 2
        $player = new MediaPlayer();
369
370 2
        $player->setUrl($this->getAttributeValue($element, "url"));
371 2
        $player->setWidth(intval($this->getAttributeValue($element, "width")));
372 2
        $player->setHeight(intval($this->getAttributeValue($element, "height")));
373
374 2
        $media->setPlayer($player);
375 2
    }
376
377
    /**
378
     * @param \DomElement $elements
379
     * @param MediaInterface $media
380
     * @return void
381
     */
382 2
    protected function handleMediaCredit(?\DOMNodeList $elements, MediaInterface $media) : void
383
    {
384 2
        $credits = array();
385 2
        foreach ($elements as $element) {
0 ignored issues
show
Bug introduced by
The expression $elements of type object<DOMElement> is not traversable.
Loading history...
386 2
            $credit = new MediaCredit();
387
388 2
            $credit->setValue($element->nodeValue);
389 2
            $credit->setScheme(MediaCreditScheme::fromXML($this->getAttributeValue($element, "scheme")));
390 2
            $credit->setRole($this->getAttributeValue($element, "role"));
391 2
            array_push($credits, $credit);
392
        }
393 2
        $media->setCredits($credits);
394 2
    }
395
396
    /**
397
     * @param \DomElement $elements
398
     * @param MediaInterface $media
399
     * @return void
400
     */
401 2
    protected function handleMediaCopyright(?\DOMNodeList $elements, MediaInterface $media) : void
402
    {
403 2
        $element = $elements->item(0);
404 2
        $copyright = new MediaCopyright();
405
406 2
        $copyright->setContent($element->nodeValue);
407 2
        $copyright->setUrl($this->getAttributeValue($element, "url"));
408
409 2
        $media->setCopyright($copyright);
410 2
    }
411
412
    /**
413
     * @param \DomElement $elements
414
     * @param MediaInterface $media
415
     * @return void
416
     */
417 2
    protected function handleMediaText(?\DOMNodeList $elements, MediaInterface $media) : void
418
    {
419 2
        $texts = array();
420 2
        foreach ($elements as $element) {
0 ignored issues
show
Bug introduced by
The expression $elements of type object<DOMElement> is not traversable.
Loading history...
421 2
            $text = new MediaText();
422
423 2
            $text->setValue($element->nodeValue);
424 2
            $text->setType(MediaTextType::fromXML($this->getAttributeValue($element, "type")));
425 2
            $text->setLang($this->getAttributeValue($element, "lang"));
426 2
            if ($element->hasAttribute("start")) {
427 1
                $text->setStart(new \DateTime($this->getAttributeValue($element, "start")));
428
            }
429 2
            if ($element->hasAttribute("end")) {
430 1
                $text->setEnd(new \DateTime($this->getAttributeValue($element, "end")));
431
            }
432 2
            array_push($texts, $text);
433
        }
434 2
        $media->setTexts($texts);
435 2
    }
436
437
    /**
438
     * @param \DomElement $elements
439
     * @param MediaInterface $media
440
     * @return void
441
     */
442 2
    protected function handleMediaRestriction(?\DOMNodeList $elements, MediaInterface $media) : void
443
    {
444 2
        $element = $elements->item(0);
445 2
        $restriction = new MediaRestriction();
446
447 2
        $restriction->setContent($element->nodeValue);
448 2
        $restriction->setType(MediaRestrictionType::fromXML($this->getAttributeValue($element, "type")));
449 2
        $restriction->setRelationship(MediaRestrictionRelationship::fromXML($this->getAttributeValue($element, "relationship")));
450
451 2
        $media->setRestriction($restriction);
452 2
    }
453
454
    /**
455
     * @param \DomElement $elements
456
     * @param MediaInterface $media
457
     * @return void
458
     */
459 3
    protected function handleMediaCommunity(?\DOMNodeList $elements, MediaInterface $media) : void
460
    {
461 3
        $element = $elements->item(0);
462 3
        $community = new MediaCommunity();
463
464 3
        $community->setStarRatingAverage(floatval($this->getChildAttributeValue($element, "starRating", "average", static::MRSS_NAMESPACE)));
465 3
        $community->setStarRatingCount(intval($this->getChildAttributeValue($element, "starRating", "count", static::MRSS_NAMESPACE)));
466 3
        $community->setStarRatingMin(intval($this->getChildAttributeValue($element, "starRating", "min", static::MRSS_NAMESPACE)));
467 3
        $community->setStarRatingMax(intval($this->getChildAttributeValue($element, "starRating", "max", static::MRSS_NAMESPACE)));
468 3
        $community->setNbViews(intval($this->getChildAttributeValue($element, "statistics", "views", static::MRSS_NAMESPACE)));
469 3
        $community->setNbFavorites(intval($this->getChildAttributeValue($element, "statistics", "favorites", static::MRSS_NAMESPACE)));
470
471 3
        $tags = array();
472 3
        $tagsValue = $this->getChildValue($element, "tags", static::MRSS_NAMESPACE);
473 3
        if ($tagsValue) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $tagsValue of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
474 1
            foreach (explode(",", $tagsValue) as $pair) {
475 1
                $values = explode(":", $pair);
476 1
                if (count($values) != 2) {
477
                    continue;
478
                }
479 1
                $key = trim($values[0]);
480 1
                $value = intval($values[1]);
481 1
                $tags[$key] = $value;
482
            }
483
        }
484 3
        $community->setTags($tags);
485
486 3
        $media->setCommunity($community);
487 3
    }
488
489
    /**
490
     * @param \DomElement $elements
491
     * @param MediaInterface $media
492
     * @return void
493
     */
494 2
    protected function handleMediaComments(?\DOMNodeList $elements, MediaInterface $media) : void
495
    {
496 2
        $element = $elements->item(0);
497
498 2
        $comments = array();
499 2
        foreach ($element->childNodes as $node) {
500 1
            if (is_a($node, "DOMNode") && $node->nodeName == "media:comment") {
501 1
                array_push($comments, $node->nodeValue);
502
            }
503
        }
504 2
        $media->setComments($comments);
505 2
    }
506
507
    /**
508
     * @param \DomElement $elements
509
     * @param MediaInterface $media
510
     * @return void
511
     */
512 2
    protected function handleMediaEmbed(?\DOMNodeList $elements, MediaInterface $media) : void
513
    {
514 2
        $element = $elements->item(0);
515 2
        $embed = new MediaEmbed();
516
517 2
        $embed->setUrl($this->getAttributeValue($element, "url"));
518 2
        $embed->setWidth(intval($this->getAttributeValue($element, "width")));
519 2
        $embed->setHeight(intval($this->getAttributeValue($element, "height")));
520
521 2
        $params = array();
522 2
        foreach ($element->childNodes as $node) {
523 1
            if (is_a($node, "DOMNode") && $node->nodeName == "media:param") {
524 1
                $key = $this->getAttributeValue($node, "name");
525 1
                $value = $node->nodeValue;
526 1
                $params[$key] = trim($value);
527
            }
528
        }
529
530 2
        $embed->setParams($params);
531
532 2
        $media->setEmbed($embed);
533 2
    }
534
535
    /**
536
     * @param \DomElement $elements
537
     * @param MediaInterface $media
538
     * @return void
539
     */
540 2
    protected function handleMediaResponses(?\DOMNodeList $elements, MediaInterface $media) : void
541
    {
542 2
        $element = $elements->item(0);
543
544 2
        $responses = array();
545 2
        foreach ($element->childNodes as $node) {
546 1
            if (is_a($node, "DOMNode") && $node->nodeName == "media:response") {
547 1
                array_push($responses, $node->nodeValue);
548
            }
549
        }
550 2
        $media->setResponses($responses);
551 2
    }
552
553
    /**
554
     * @param \DomElement $elements
555
     * @param MediaInterface $media
556
     * @return void
557
     */
558 2
    protected function handleMediaBacklinks(?\DOMNodeList $elements, MediaInterface $media) : void
559
    {
560 2
        $element = $elements->item(0);
561
562 2
        $backlinks = array();
563 2
        foreach ($element->childNodes as $node) {
564 1
            if (is_a($node, "DOMNode") && $node->nodeName == "media:backLink") {
565 1
                array_push($backlinks, $node->nodeValue);
566
            }
567
        }
568 2
        $media->setBacklinks($backlinks);
569 2
    }
570
571
    /**
572
     * @param \DomElement $elements
573
     * @param MediaInterface $media
574
     * @return void
575
     */
576 2
    protected function handleMediaStatus(?\DOMNodeList $elements, MediaInterface $media) : void
577
    {
578 2
        $element = $elements->item(0);
579 2
        $status = new MediaStatus();
580
581 2
        $status->setValue(MediaStatusValue::fromXML($this->getAttributeValue($element, "state")));
582 2
        $status->setReason($this->getAttributeValue($element, "reason"));
583
584 2
        $media->setStatus($status);
585 2
    }
586
587
    /**
588
     * @param \DomElement $elements
589
     * @param MediaInterface $media
590
     * @return void
591
     */
592 2
    protected function handleMediaPrice(?\DOMNodeList $elements, MediaInterface $media) : void
593
    {
594 2
        $prices = array();
595 2
        foreach ($elements as $element) {
0 ignored issues
show
Bug introduced by
The expression $elements of type object<DOMElement> is not traversable.
Loading history...
596 2
            $price = new MediaPrice();
597
598 2
            $price->setType(MediaPriceType::fromXML($this->getAttributeValue($element, "type")));
599 2
            $price->setValue(floatval($this->getAttributeValue($element, "price")));
600 2
            $price->setCurrency($this->getAttributeValue($element, "currency"));
601 2
            array_push($prices, $price);
602
        }
603 2
        $media->setPrices($prices);
604 2
    }
605
606
    /**
607
     * @param \DomElement $elements
608
     * @param MediaInterface $media
609
     * @return void
610
     */
611 1
    protected function handleMediaLicense(?\DOMNodeList $elements, MediaInterface $media) : void
612
    {
613 1
        $element = $elements->item(0);
614 1
        $license = new MediaLicense();
615
616 1
        $license->setContent($element->nodeValue);
617 1
        $license->setUrl($this->getAttributeValue($element, "href"));
618 1
        $license->setType($this->getAttributeValue($element, "type"));
619
620 1
        $media->setLicense($license);
621 1
    }
622
623
    /**
624
     * @param \DomElement $elements
625
     * @param MediaInterface $media
626
     * @return void
627
     */
628 1
    protected function handleMediaSubtitle(?\DOMNodeList $elements, MediaInterface $media) : void
629
    {
630 1
        $subtitles = array();
631 1
        foreach ($elements as $element) {
0 ignored issues
show
Bug introduced by
The expression $elements of type object<DOMElement> is not traversable.
Loading history...
632 1
            $subtitle = new MediaSubtitle();
633
634 1
            $subtitle->setType($this->getAttributeValue($element, "type"));
635 1
            $subtitle->setLang($this->getAttributeValue($element, "lang"));
636 1
            $subtitle->setUrl($this->getAttributeValue($element, "href"));
637 1
            array_push($subtitles, $subtitle);
638
        }
639 1
        $media->setSubTitles($subtitles);
640 1
    }
641
642
    /**
643
     * @param \DomElement $elements
644
     * @param MediaInterface $media
645
     * @return void
646
     */
647 1
    protected function handleMediaPeerlink(?\DOMNodeList $elements, MediaInterface $media) : void
648
    {
649 1
        $element = $elements->item(0);
650 1
        $peerLink = new MediaPeerLink();
651
652 1
        $peerLink->setUrl($this->getAttributeValue($element, "href"));
653 1
        $peerLink->setType($this->getAttributeValue($element, "type"));
654
655 1
        $media->setPeerLink($peerLink);
656 1
    }
657
658
    /**
659
     * @param \DomElement $elements
660
     * @param MediaInterface $media
661
     * @return void
662
     */
663 1
    protected function handleMediaRights(?\DOMNodeList $elements, MediaInterface $media) : void
664
    {
665 1
        $element = $elements->item(0);
666
667 1
        $media->setRights(MediaRightsStatus::fromXML($this->getAttributeValue($element, "status")));
668 1
    }
669
670
    /**
671
     * @param \DomElement $elements
672
     * @param MediaInterface $media
673
     * @return void
674
     */
675 1
    protected function handleMediaScenes(?\DOMNodeList $elements, MediaInterface $media) : void
676
    {
677 1
        $element = $elements->item(0);
678
679 1
        $scenes = array();
680 1
        foreach ($element->childNodes as $node) {
681 1
            if (is_a($node, "DOMNode") && $node->nodeName == "media:scene") {
682 1
                $scene = new MediaScene();
683
684 1
                $scene->setTitle($this->getChildValue($node, "sceneTitle"));
685 1
                $scene->setDescription($this->getChildValue($node, "sceneDescription"));
686 1
                $startTime = $this->getChildValue($node, "sceneStartTime");
687 1
                $endTime = $this->getChildValue($node, "sceneEndTime");
688
689 1
                if ($startTime !== null) {
690 1
                    $scene->setStartTime(new \DateTime($startTime));
691
                }
692
693 1
                if ($endTime !== null) {
694 1
                    $scene->setEndTime(new \DateTime($endTime));
695
                }
696
697 1
                array_push($scenes, $scene);
698
            }
699
        }
700 1
        $media->setScenes($scenes);
701 1
    }
702
703
    /**
704
     * @inheritDoc
705
     */
706 5
    protected function hasValue(NodeInterface $node) : bool
707
    {
708 5
        return $node instanceof ItemInterface && !! $node->getMedias();
709
    }
710
711
    /**
712
     * @inheritDoc
713
     */
714 5
    protected function addElement(\DomDocument $document, \DOMElement $rootElement, NodeInterface $node) : void
715
    {
716 5
        foreach ($node->getMedias() as $media) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface FeedIo\Feed\NodeInterface as the method getMedias() does only exist in the following implementations of said interface: FeedIo\Feed\Item.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
717 1
            $rootElement->appendChild($this->createMediaElement($document, $media));
718
        }
719 5
    }
720
721
    /**
722
     * From http://www.rssboard.org/media-rss#optional-elements
723
     *
724
     * Duplicated elements appearing at deeper levels of the document tree
725
     * have higher priority over other levels. For example, <media:content>
726
     * level elements are favored over <item> level elements. The priority
727
     * level is listed from strongest to weakest:
728
     * <media:content>, <media:group>, <item>, <channel>.
729
     */
730 47
    public function findTags(\DOMElement $mediaContentNode, string $tag) : ? \DOMNodeList
731
    {
732 47
        $xpath = new \DOMXpath($mediaContentNode->ownerDocument);
733
        $queries = [
734 47
            $xpath->query("./descendant::$tag", $mediaContentNode),
735 47
            $xpath->query("./ancestor::media:group/child::$tag", $mediaContentNode),
736 47
            $xpath->query("./ancestor::item/child::$tag", $mediaContentNode),
737 47
            $xpath->query("./ancestor::channel/child::$tag", $mediaContentNode),
738
        ];
739
740 47
        foreach ($queries as $query) {
741 47
            if ($query->count()) {
742 45
                return $query;
743
            }
744
        }
745
746 47
        return null;
747
    }
748
}
749