Completed
Push — master ( 05bb5a...5289b8 )
by Rafał
34:32 queued 04:15
created

EmbeddedImageProcessor::process()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 34
rs 9.0648
c 0
b 0
f 0
cc 5
nc 5
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Superdesk Web Publisher Content Bundle.
7
 *
8
 * Copyright 2018 Sourcefabric z.ú. and contributors.
9
 *
10
 * For the full copyright and license information, please see the
11
 * AUTHORS and LICENSE files distributed with this source code.
12
 *
13
 * @copyright 2018 Sourcefabric z.ú
14
 * @license http://www.superdesk.org/license
15
 */
16
17
namespace SWP\Bundle\ContentBundle\Processor;
18
19
use SWP\Bundle\ContentBundle\File\FileExtensionCheckerInterface;
20
use SWP\Bundle\ContentBundle\Manager\MediaManagerInterface;
21
use SWP\Bundle\ContentBundle\Model\ArticleInterface;
22
use SWP\Bundle\ContentBundle\Model\ArticleMediaInterface;
23
use SWP\Bundle\ContentBundle\Model\ImageRendition;
24
use Symfony\Component\DomCrawler\Crawler;
25
26
class EmbeddedImageProcessor implements EmbeddedImageProcessorInterface
27
{
28
    private const DEFAULT_ARTICLE_BODY_IMAGE_RENDITION = 'original';
29
30
    /**
31
     * @var MediaManagerInterface
32
     */
33
    private $mediaManager;
34
35
    /**
36
     * @var FileExtensionCheckerInterface
37
     */
38
    private $fileExtensionChecker;
39
40
    /**
41
     * @var string
42
     */
43
    private $defaultImageRendition;
44
45
    public function __construct(MediaManagerInterface $mediaManager, FileExtensionCheckerInterface $fileExtensionChecker)
46
    {
47
        $this->mediaManager = $mediaManager;
48
        $this->fileExtensionChecker = $fileExtensionChecker;
49
    }
50
51
    public function process(ArticleInterface $article, ArticleMediaInterface $articleMedia): void
52
    {
53
        $body = $article->getBody();
54
        $mediaId = str_replace('/', '\\/', $articleMedia->getKey());
55
56
        preg_match(
57
            "/(<!-- ?EMBED START Image {id: \"$mediaId\"} ?-->)(.+?)(<!-- ?EMBED END Image {id: \"$mediaId\"} ?-->)/im",
58
            str_replace(PHP_EOL, '', $body),
59
            $embeds
60
        );
61
62
        if (empty($embeds)) {
63
            return;
64
        }
65
66
        $figureString = $embeds[2];
67
        $crawler = new Crawler($figureString);
68
        $images = $crawler->filter('figure img');
69
70
        /** @var \DOMElement $imageElement */
71
        foreach ($images as $imageElement) {
72
            /** @var ImageRendition $rendition */
73
            foreach ($articleMedia->getRenditions() as $rendition) {
74
                if ($this->getDefaultImageRendition() === $rendition->getName()) {
75
                    $this->processImageElement($imageElement, $rendition, $mediaId);
76
                }
77
            }
78
        }
79
80
        $figCaptionNode = $crawler->filter('figure figcaption')->getNode(0);
81
        $this->appendImageByline($articleMedia, $figCaptionNode);
0 ignored issues
show
Bug introduced by
It seems like $figCaptionNode defined by $crawler->filter('figure figcaption')->getNode(0) on line 80 can be null; however, SWP\Bundle\ContentBundle...or::appendImageByline() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
82
83
        $article->setBody(str_replace($figureString, $crawler->filter('body')->html(), $body));
84
    }
85
86
    public function setDefaultImageRendition(string $renditionName): void
87
    {
88
        $this->defaultImageRendition = $renditionName;
89
    }
90
91
    public function supports(string $type): bool
92
    {
93
        return $this->fileExtensionChecker->isImage($type);
94
    }
95
96
    private function appendImageByline(ArticleMediaInterface $articleMedia, \DOMElement $figCaptionNode): void
97
    {
98
        $element = new \DOMElement('span');
99
        $figCaptionNode->appendChild($element);
100
101
        $authorDiv = $figCaptionNode->childNodes[1];
102
        $authorDiv->textContent = $this->applyByline($articleMedia);
103
    }
104
105
    public function applyByline(ArticleMediaInterface $articleMedia): string
106
    {
107
        return $articleMedia->getByLine();
108
    }
109
110
    protected function processImageElement(\DOMElement $imageElement, ImageRendition $rendition, string $mediaId): void
111
    {
112
        $attributes = $imageElement->attributes;
113
        $altAttribute = null;
114
        if ($imageElement->hasAttribute('alt')) {
115
            $altAttribute = $attributes->getNamedItem('alt');
116
        }
117
118
        while ($attributes->length) {
0 ignored issues
show
Bug introduced by
The property length does not seem to exist in DOMNamedNodeMap.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
119
            $imageElement->removeAttribute($attributes->item(0)->name);
0 ignored issues
show
Bug introduced by
The property name does not seem to exist in DOMNode.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
120
        }
121
122
        $imageElement->setAttribute('src', $this->mediaManager->getMediaUri($rendition->getImage()));
123
        $imageElement->setAttribute('data-media-id', $mediaId);
124
        $imageElement->setAttribute('data-image-id', $rendition->getImage()->getAssetId());
125
        $imageElement->setAttribute('data-rendition-name', $this->getDefaultImageRendition());
126
        $imageElement->setAttribute('width', (string) $rendition->getWidth());
127
        $imageElement->setAttribute('height', (string) $rendition->getHeight());
128
129
        if (null !== $altAttribute) {
130
            $imageElement->setAttribute('alt', $altAttribute->nodeValue);
131
        }
132
    }
133
134
    protected function getDefaultImageRendition(): string
135
    {
136
        if (null === $this->defaultImageRendition) {
137
            return self::DEFAULT_ARTICLE_BODY_IMAGE_RENDITION;
138
        }
139
140
        return $this->defaultImageRendition;
141
    }
142
}
143