Passed
Push — webp ( af5ea3 )
by Arnaud
03:08
created

Parsedown::blockImage()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 85
Code Lines 43

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 2
Bugs 1 Features 0
Metric Value
cc 6
eloc 43
c 2
b 1
f 0
nc 8
nop 1
dl 0
loc 85
ccs 0
cts 33
cp 0
crap 42
rs 8.6097

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * This file is part of the Cecil/Cecil package.
4
 *
5
 * Copyright (c) Arnaud Ligny <[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 Cecil\Converter;
12
13
use Cecil\Assets\Asset;
14
use Cecil\Assets\Image;
15
use Cecil\Builder;
16
17
class Parsedown extends \ParsedownToC
18
{
19
    /** @var Builder */
20
    protected $builder;
21
22
    /** {@inheritdoc} */
23
    protected $regexAttribute = '(?:[#.][-\w:\\\]+[ ]*|[-\w:\\\]+(?:=(?:["\'][^\n]*?["\']|[^\s]+)?)?[ ]*)';
24
25
    /** Regex to verify there is an image in <figure> block */
26
    private $MarkdownImageRegex = "~^!\[.*?\]\(.*?\)~";
27
28 1
    public function __construct(Builder $builder)
29
    {
30 1
        $this->builder = $builder;
31 1
        if ($this->builder->getConfig()->get('body.images.caption.enabled')) {
32
            $this->BlockTypes['!'][] = 'Image';
33
        }
34 1
        parent::__construct(['selectors' => $this->builder->getConfig()->get('body.toc')]);
35 1
    }
36
37
    /**
38
     * {@inheritdoc}
39
     */
40 1
    protected function inlineImage($excerpt)
41
    {
42 1
        $image = parent::inlineImage($excerpt);
43 1
        if (!isset($image)) {
44
            return null;
45
        }
46
        // clean source path / URL
47 1
        $image['element']['attributes']['src'] = trim($this->removeQuery($image['element']['attributes']['src']));
48
        // create asset
49 1
        $asset = new Asset($this->builder, $image['element']['attributes']['src']);
50
        // is asset is valid? (if yes get width)
51 1
        if (false === $width = $asset->getWidth()) {
52
            return $image;
53
        }
54 1
        $image['element']['attributes']['src'] = $asset;
55
        /**
56
         * Should be lazy loaded?
57
         */
58 1
        if ($this->builder->getConfig()->get('body.images.lazy.enabled')) {
59 1
            $image['element']['attributes']['loading'] = 'lazy';
60
        }
61
        /**
62
         * Should be resized?
63
         */
64 1
        $assetResized = null;
65 1
        if (array_key_exists('width', $image['element']['attributes'])
66 1
            && (int) $image['element']['attributes']['width'] < $width
67 1
            && $this->builder->getConfig()->get('body.images.resize.enabled')
68
        ) {
69 1
            $width = (int) $image['element']['attributes']['width'];
70
71
            try {
72 1
                $assetResized = $asset->resize($width);
73
            } catch (\Exception $e) {
74
                $this->builder->getLogger()->debug($e->getMessage());
75
76
                return $image;
77
            }
78 1
            $image['element']['attributes']['src'] = $assetResized;
79
        }
80
        // set width
81 1
        if (!array_key_exists('width', $image['element']['attributes'])) {
82 1
            $image['element']['attributes']['width'] = $width;
83
        }
84
        // set height
85 1
        if (!array_key_exists('height', $image['element']['attributes'])) {
86 1
            $image['element']['attributes']['height'] = $asset->getHeight();
87
        }
88
        /**
89
         * Should be responsive?
90
         */
91 1
        if ($this->builder->getConfig()->get('body.images.responsive.enabled')) {
92 1
            if ($srcset = Image::getSrcset(
93 1
                $assetResized ?? $asset,
94 1
                $this->builder->getConfig()->get('assets.images.responsive.width.steps') ?? 5,
95 1
                $this->builder->getConfig()->get('assets.images.responsive.width.min') ?? 320,
96 1
                $this->builder->getConfig()->get('assets.images.responsive.width.max') ?? 1280
97
            )) {
98 1
                $image['element']['attributes']['srcset'] = $srcset;
99 1
                $image['element']['attributes']['sizes'] = $this->builder->getConfig()->get('assets.images.responsive.sizes.default');
100
            }
101
        }
102
103 1
        return $image;
104
    }
105
106
    /**
107
     * {@inheritdoc}
108
     */
109 1
    protected function parseAttributeData($attributeString)
110
    {
111 1
        $attributes = preg_split('/[ ]+/', $attributeString, -1, PREG_SPLIT_NO_EMPTY);
112 1
        $Data = [];
113 1
        $HtmlAtt = [];
114
115 1
        foreach ($attributes as $attribute) {
116 1
            switch ($attribute[0]) {
117 1
                case '#': // ID
118
                    $Data['id'] = substr($attribute, 1);
119
                    break;
120 1
                case '.': // Classes
121
                    $classes[] = substr($attribute, 1);
122
                    break;
123
                default:  // Attributes
124 1
                    parse_str($attribute, $parsed);
125 1
                    $HtmlAtt = array_merge($HtmlAtt, $parsed);
126
            }
127
        }
128
129 1
        if (isset($classes)) {
130
            $Data['class'] = implode(' ', $classes);
131
        }
132 1
        if (!empty($HtmlAtt)) {
133 1
            foreach ($HtmlAtt as $a => $v) {
134 1
                $Data[$a] = trim($v, '"');
135
            }
136
        }
137
138 1
        return $Data;
139
    }
140
141
    /**
142
     * Enhanced image block with <figure>/<figcaption>.
143
     */
144
    protected function blockImage($Line)
145
    {
146
        if (1 !== preg_match($this->MarkdownImageRegex, $Line['text'])) {
147
            return;
148
        }
149
150
        $InlineImage = $this->inlineImage($Line);
151
        if (!isset($InlineImage)) {
152
            return;
153
        }
154
155
        $block = $InlineImage;
156
157
        /*
158
        <figure>
159
            <picture>
160
                <source type="image/webp"
161
                    srcset="..."
162
                    sizes="..."
163
                >
164
                <img src="..."
165
                    srcset="..."
166
                    sizes="..."
167
                >
168
            </picture>
169
            <figcaption>...</figcaption>
170
        </figure>
171
        */
172
173
        // creates a <picture> element with <source> and <img> elements
174
        if ($this->builder->getConfig()->get('body.images.webp.enabled') ?? false) {
175
            $assetWebp = Image::convertTopWebp($InlineImage['element']['attributes']['src'], $this->builder->getConfig()->get('assets.images.quality') ?? 85);
176
            $srcset = Image::getSrcset(
177
                $assetWebp,
178
                $this->builder->getConfig()->get('assets.images.responsive.width.steps') ?? 5,
179
                $this->builder->getConfig()->get('assets.images.responsive.width.min') ?? 320,
180
                $this->builder->getConfig()->get('assets.images.responsive.width.max') ?? 1280
181
            );
182
            if (empty($srcset)) {
183
                $srcset = (string) $assetWebp;
184
            }
185
            $PictureBlock = [
186
                'element' => [
187
                    'name'    => 'picture',
188
                    'handler' => 'elements',
189
                ],
190
            ];
191
            $source = [
192
                'element' => [
193
                    'name'       => 'source',
194
                    'attributes' => [
195
                        'type'   => 'image/webp',
196
                        'srcset' => $srcset,
197
                        'sizes'  => $this->builder->getConfig()->get('assets.images.responsive.sizes.default'),
198
                    ],
199
                ],
200
            ];
201
            $PictureBlock['element']['text'][] = $source['element'];
202
            $PictureBlock['element']['text'][] = $InlineImage['element'];
203
            $block = $PictureBlock;
204
        }
205
206
        // put <img> or <picture> in a <figure> element if there is a title
207
        if (!empty($InlineImage['element']['attributes']['title'])) {
208
            $FigureBlock = [
209
                'element' => [
210
                    'name'    => 'figure',
211
                    'handler' => 'elements',
212
                    'text'    => [
213
                        $block['element'],
214
                    ],
215
                ],
216
            ];
217
            $InlineFigcaption = [
218
                'element' => [
219
                    'name' => 'figcaption',
220
                    'text' => $InlineImage['element']['attributes']['title'],
221
                ],
222
            ];
223
            $FigureBlock['element']['text'][] = $InlineFigcaption['element'];
224
225
            return $FigureBlock;
226
        }
227
228
        return $block;
229
    }
230
231
    /**
232
     * Removes query string from URL.
233
     */
234
    private function removeQuery(string $path): string
235
    {
236 1
        return strtok($path, '?');
237
    }
238
}
239