Completed
Push — master ( 3f4ab3...622388 )
by
unknown
50:01 queued 35:09
created

ImageContentObject   F

Complexity

Total Complexity 73

Size/Duplication

Total Lines 324
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 162
dl 0
loc 324
rs 2.56
c 0
b 0
f 0
wmc 73

8 Methods

Rating   Name   Duplication   Size   Complexity  
F getImageSourceCollection() 0 119 32
A getTypoScriptFrontendController() 0 3 1
C cImage() 0 58 12
A getBorderAttr() 0 12 5
A getImageTagTemplate() 0 9 4
A linkWrap() 0 10 3
C getAltParam() 0 26 12
A render() 0 11 4

How to fix   Complexity   

Complex Class

Complex classes like ImageContentObject often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ImageContentObject, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace TYPO3\CMS\Frontend\ContentObject;
3
4
/*
5
 * This file is part of the TYPO3 CMS project.
6
 *
7
 * It is free software; you can redistribute it and/or modify it under
8
 * the terms of the GNU General Public License, either version 2
9
 * of the License, or any later version.
10
 *
11
 * For the full copyright and license information, please read the
12
 * LICENSE.txt file that was distributed with this source code.
13
 *
14
 * The TYPO3 project - inspiring people to share!
15
 */
16
17
use TYPO3\CMS\Core\Core\Environment;
18
use TYPO3\CMS\Core\Page\AssetCollector;
19
use TYPO3\CMS\Core\Service\MarkerBasedTemplateService;
20
use TYPO3\CMS\Core\TypoScript\TypoScriptService;
21
use TYPO3\CMS\Core\Utility\GeneralUtility;
22
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
23
24
/**
25
 * Contains IMAGE class object.
26
 */
27
class ImageContentObject extends AbstractContentObject
28
{
29
    /**
30
     * Rendering the cObject, IMAGE
31
     *
32
     * @param array $conf Array of TypoScript properties
33
     * @return string Output
34
     */
35
    public function render($conf = [])
36
    {
37
        if (!empty($conf['if.']) && !$this->cObj->checkIf($conf['if.'])) {
38
            return '';
39
        }
40
41
        $theValue = $this->cImage($conf['file'], $conf);
42
        if (isset($conf['stdWrap.'])) {
43
            $theValue = $this->cObj->stdWrap($theValue, $conf['stdWrap.']);
44
        }
45
        return $theValue;
46
    }
47
48
    /**
49
     * Returns a <img> tag with the image file defined by $file and processed according to the properties in the TypoScript array.
50
     * Mostly this function is a sub-function to the IMAGE function which renders the IMAGE cObject in TypoScript.
51
     *
52
     * @param string $file File TypoScript resource
53
     * @param array $conf TypoScript configuration properties
54
     * @return string HTML <img> tag, (possibly wrapped in links and other HTML) if any image found.
55
     */
56
    protected function cImage($file, $conf)
57
    {
58
        $tsfe = $this->getTypoScriptFrontendController();
59
        $info = $this->cObj->getImgResource($file, $conf['file.']);
60
        $tsfe->lastImageInfo = $info;
0 ignored issues
show
Bug Best Practice introduced by
The property $lastImageInfo is declared private in TYPO3\CMS\Frontend\Contr...criptFrontendController. Since you implement __set, consider adding a @property or @property-write.
Loading history...
61
        if (!is_array($info)) {
62
            return '';
63
        }
64
        if (is_file(Environment::getPublicPath() . '/' . $info['3'])) {
65
            $source = $tsfe->absRefPrefix . str_replace('%2F', '/', rawurlencode($info['3']));
66
        } else {
67
            $source = $info[3];
68
        }
69
        // Remove file objects for AssetCollector, as it only allows to store scalar values
70
        unset($info['originalFile'], $info['processedFile']);
71
        GeneralUtility::makeInstance(AssetCollector::class)->addMedia(
72
            $source,
73
            $info
74
        );
75
76
        $layoutKey = $this->cObj->stdWrap($conf['layoutKey'], $conf['layoutKey.']);
77
        $imageTagTemplate = $this->getImageTagTemplate($layoutKey, $conf);
78
        $sourceCollection = $this->getImageSourceCollection($layoutKey, $conf, $file);
79
80
        // This array is used to collect the image-refs on the page...
81
        $tsfe->imagesOnPage[] = $source;
0 ignored issues
show
Bug Best Practice introduced by
The property $imagesOnPage is declared private in TYPO3\CMS\Frontend\Contr...criptFrontendController. Since you implement __set, consider adding a @property or @property-write.
Loading history...
82
        $altParam = $this->getAltParam($conf);
83
        $params = $this->cObj->stdWrapValue('params', $conf);
84
        if ($params !== '' && $params[0] !== ' ') {
85
            $params = ' ' . $params;
86
        }
87
88
        $imageTagValues = [
89
            'width' =>  (int)$info[0],
90
            'height' => (int)$info[1],
91
            'src' => htmlspecialchars($source),
92
            'params' => $params,
93
            'altParams' => $altParam,
94
            'border' =>  $this->getBorderAttr(' border="' . (int)$conf['border'] . '"'),
95
            'sourceCollection' => $sourceCollection,
96
            'selfClosingTagSlash' => !empty($tsfe->xhtmlDoctype) ? ' /' : '',
97
        ];
98
99
        $markerTemplateEngine = GeneralUtility::makeInstance(MarkerBasedTemplateService::class);
100
        $theValue = $markerTemplateEngine->substituteMarkerArray($imageTagTemplate, $imageTagValues, '###|###', true, true);
101
102
        $linkWrap = isset($conf['linkWrap.']) ? $this->cObj->stdWrap($conf['linkWrap'], $conf['linkWrap.']) : $conf['linkWrap'];
103
        if ($linkWrap) {
104
            $theValue = $this->linkWrap($theValue, $linkWrap);
105
        } elseif ($conf['imageLinkWrap']) {
106
            $originalFile = !empty($info['originalFile']) ? $info['originalFile'] : $info['origFile'];
107
            $theValue = $this->cObj->imageLinkWrap($theValue, $originalFile, $conf['imageLinkWrap.']);
108
        }
109
        $wrap = isset($conf['wrap.']) ? $this->cObj->stdWrap($conf['wrap'], $conf['wrap.']) : $conf['wrap'];
110
        if ((string)$wrap !== '') {
111
            $theValue = $this->cObj->wrap($theValue, $conf['wrap']);
112
        }
113
        return $theValue;
114
    }
115
116
    /**
117
     * Returns the 'border' attribute for an <img> tag only if the doctype is not xhtml_strict, xhtml_11 or html5
118
     * or if the config parameter 'disableImgBorderAttr' is not set.
119
     *
120
     * @param string $borderAttr The border attribute
121
     * @return string The border attribute
122
     */
123
    protected function getBorderAttr($borderAttr)
124
    {
125
        $tsfe = $this->getTypoScriptFrontendController();
126
        $docType = $tsfe->xhtmlDoctype;
127
        if (
128
            $docType !== 'xhtml_strict' && $docType !== 'xhtml_11'
129
            && $tsfe->config['config']['doctype'] !== 'html5'
130
            && !$tsfe->config['config']['disableImgBorderAttr']
131
        ) {
132
            return $borderAttr;
133
        }
134
        return '';
135
    }
136
137
    /**
138
     * Returns the html-template for rendering the image-Tag if no template is defined via typoscript the
139
     * default <img> tag template is returned
140
     *
141
     * @param string $layoutKey rendering key
142
     * @param array $conf TypoScript configuration properties
143
     * @return string
144
     */
145
    protected function getImageTagTemplate($layoutKey, $conf): string
146
    {
147
        if ($layoutKey && isset($conf['layout.']) && isset($conf['layout.'][$layoutKey . '.'])) {
148
            return $this->cObj->stdWrap(
149
                $conf['layout.'][$layoutKey . '.']['element'] ?? '',
150
                $conf['layout.'][$layoutKey . '.']['element.'] ?? []
151
            );
152
        }
153
        return '<img src="###SRC###" width="###WIDTH###" height="###HEIGHT###" ###PARAMS### ###ALTPARAMS### ###BORDER######SELFCLOSINGTAGSLASH###>';
154
    }
155
156
    /**
157
     * Render alternate sources for the image tag. If no source collection is given an empty string is returned.
158
     *
159
     * @param string $layoutKey rendering key
160
     * @param array $conf TypoScript configuration properties
161
     * @param string $file
162
     * @throws \UnexpectedValueException
163
     * @return string
164
     */
165
    protected function getImageSourceCollection($layoutKey, $conf, $file)
166
    {
167
        $sourceCollection = '';
168
        if ($layoutKey
169
            && isset($conf['sourceCollection.']) && $conf['sourceCollection.']
170
            && (
171
                isset($conf['layout.'][$layoutKey . '.']['source']) && $conf['layout.'][$layoutKey . '.']['source']
172
                || isset($conf['layout.'][$layoutKey . '.']['source.']) && $conf['layout.'][$layoutKey . '.']['source.']
173
            )
174
        ) {
0 ignored issues
show
Coding Style introduced by
Blank line found at start of control structure
Loading history...
175
176
            // find active sourceCollection
177
            $activeSourceCollections = [];
178
            foreach ($conf['sourceCollection.'] as $sourceCollectionKey => $sourceCollectionConfiguration) {
179
                if (substr($sourceCollectionKey, -1) === '.') {
180
                    if (empty($sourceCollectionConfiguration['if.']) || $this->cObj->checkIf($sourceCollectionConfiguration['if.'])) {
181
                        $activeSourceCollections[] = $sourceCollectionConfiguration;
182
                    }
183
                }
184
            }
185
186
            // apply option split to configurations
187
            $tsfe = $this->getTypoScriptFrontendController();
188
            $typoScriptService = GeneralUtility::makeInstance(TypoScriptService::class);
189
            $srcLayoutOptionSplitted = $typoScriptService->explodeConfigurationForOptionSplit((array)$conf['layout.'][$layoutKey . '.'], count($activeSourceCollections));
190
191
            // render sources
192
            foreach ($activeSourceCollections as $key => $sourceConfiguration) {
193
                $sourceLayout = $this->cObj->stdWrap(
194
                    $srcLayoutOptionSplitted[$key]['source'] ?? '',
195
                    $srcLayoutOptionSplitted[$key]['source.'] ?? []
196
                );
197
198
                $sourceRenderConfiguration = [
199
                    'file' => $file,
200
                    'file.' => $conf['file.'] ?? null
201
                ];
202
203
                if (isset($sourceConfiguration['quality']) || isset($sourceConfiguration['quality.'])) {
204
                    $imageQuality = $sourceConfiguration['quality'] ?? '';
205
                    if (isset($sourceConfiguration['quality.'])) {
206
                        $imageQuality = $this->cObj->stdWrap($sourceConfiguration['quality'], $sourceConfiguration['quality.']);
207
                    }
208
                    if ($imageQuality) {
209
                        $sourceRenderConfiguration['file.']['params'] = '-quality ' . (int)$imageQuality;
210
                    }
211
                }
212
213
                if (isset($sourceConfiguration['pixelDensity'])) {
214
                    $pixelDensity = (int)$this->cObj->stdWrap(
215
                        $sourceConfiguration['pixelDensity'] ?? '',
216
                        $sourceConfiguration['pixelDensity.'] ?? []
217
                    );
218
                } else {
219
                    $pixelDensity = 1;
220
                }
221
                $dimensionKeys = ['width', 'height', 'maxW', 'minW', 'maxH', 'minH', 'maxWidth', 'maxHeight', 'XY'];
222
                foreach ($dimensionKeys as $dimensionKey) {
223
                    $dimension = $this->cObj->stdWrap(
224
                        $sourceConfiguration[$dimensionKey] ?? '',
225
                        $sourceConfiguration[$dimensionKey . '.'] ?? []
226
                    );
227
                    if (!$dimension) {
228
                        $dimension = $this->cObj->stdWrap(
229
                            $conf['file.'][$dimensionKey] ?? '',
230
                            $conf['file.'][$dimensionKey . '.'] ?? []
231
                        );
232
                    }
233
                    if ($dimension) {
234
                        if (strpos($dimension, 'c') !== false && ($dimensionKey === 'width' || $dimensionKey === 'height')) {
235
                            $dimensionParts = explode('c', $dimension, 2);
236
                            $dimension = ((int)$dimensionParts[0] * $pixelDensity) . 'c';
237
                            if ($dimensionParts[1]) {
238
                                $dimension .= $dimensionParts[1];
239
                            }
240
                        } elseif ($dimensionKey === 'XY') {
241
                            $dimensionParts = GeneralUtility::intExplode(',', $dimension, false, 2);
242
                            $dimension = $dimensionParts[0] * $pixelDensity;
243
                            if ($dimensionParts[1]) {
244
                                $dimension .= ',' . $dimensionParts[1] * $pixelDensity;
245
                            }
246
                        } else {
247
                            $dimension = (int)$dimension * $pixelDensity;
248
                        }
249
                        $sourceRenderConfiguration['file.'][$dimensionKey] = $dimension;
250
                        // Remove the stdWrap properties for dimension as they have been processed already above.
251
                        unset($sourceRenderConfiguration['file.'][$dimensionKey . '.']);
252
                    }
253
                }
254
                $sourceInfo = $this->cObj->getImgResource($sourceRenderConfiguration['file'], $sourceRenderConfiguration['file.']);
255
                if ($sourceInfo) {
256
                    $sourceConfiguration['width'] = $sourceInfo[0];
257
                    $sourceConfiguration['height'] = $sourceInfo[1];
258
                    $urlPrefix = '';
259
                    if (parse_url($sourceInfo[3], PHP_URL_HOST) === null) {
260
                        $urlPrefix = $tsfe->absRefPrefix;
261
                    }
262
                    $sourceConfiguration['src'] = htmlspecialchars($urlPrefix . $sourceInfo[3]);
263
                    $sourceConfiguration['selfClosingTagSlash'] = !empty($tsfe->xhtmlDoctype) ? ' /' : '';
264
265
                    $markerTemplateEngine = GeneralUtility::makeInstance(MarkerBasedTemplateService::class);
266
                    $oneSourceCollection = $markerTemplateEngine->substituteMarkerArray($sourceLayout, $sourceConfiguration, '###|###', true, true);
267
268
                    foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getImageSourceCollection'] ?? [] as $className) {
269
                        $hookObject = GeneralUtility::makeInstance($className);
270
                        if (!$hookObject instanceof ContentObjectOneSourceCollectionHookInterface) {
271
                            throw new \UnexpectedValueException(
272
                                '$hookObject must implement interface ' . ContentObjectOneSourceCollectionHookInterface::class,
273
                                1380017853
274
                            );
275
                        }
276
                        $oneSourceCollection = $hookObject->getOneSourceCollection((array)$sourceRenderConfiguration, (array)$sourceConfiguration, $oneSourceCollection, $this->cObj);
277
                    }
278
279
                    $sourceCollection .= $oneSourceCollection;
280
                }
281
            }
282
        }
283
        return $sourceCollection;
284
    }
285
286
    /**
287
     * Wraps the input string by the $wrap value and implements the "linkWrap" data type as well.
288
     * The "linkWrap" data type means that this function will find any integer encapsulated in {} (curly braces) in the first wrap part and substitute it with the corresponding page uid from the rootline where the found integer is pointing to the key in the rootline. See link below.
289
     *
290
     * @param string $content Input string
291
     * @param string $wrap A string where the first two parts separated by "|" (vertical line) will be wrapped around the input string
292
     * @return string Wrapped output string
293
     * @see wrap()
294
     * @see cImage()
295
     */
296
    protected function linkWrap($content, $wrap)
297
    {
298
        $wrapArr = explode('|', $wrap);
299
        if (preg_match('/\\{([0-9]*)\\}/', $wrapArr[0], $reg)) {
300
            $uid = $this->getTypoScriptFrontendController()->tmpl->rootLine[$reg[1]]['uid'] ?? null;
301
            if ($uid) {
302
                $wrapArr[0] = str_replace($reg[0], $uid, $wrapArr[0]);
303
            }
304
        }
305
        return trim($wrapArr[0] ?? '') . $content . trim($wrapArr[1] ?? '');
306
    }
307
308
    /**
309
     * An abstraction method which creates an alt or title parameter for an HTML img, applet, area or input element and the FILE content element.
310
     * From the $conf array it implements the properties "altText", "titleText" and "longdescURL"
311
     *
312
     * @param array $conf TypoScript configuration properties
313
     * @param bool $longDesc If set, the longdesc attribute will be generated - must only be used for img elements!
314
     * @return string Parameter string containing alt and title parameters (if any)
315
     * @see cImage()
316
     */
317
    public function getAltParam($conf, $longDesc = true)
318
    {
319
        $altText = isset($conf['altText.']) ? trim($this->cObj->stdWrap($conf['altText'], $conf['altText.'])) : trim($conf['altText']);
320
        $titleText = isset($conf['titleText.']) ? trim($this->cObj->stdWrap($conf['titleText'], $conf['titleText.'])) : trim($conf['titleText']);
321
        if (isset($conf['longdescURL.']) && $this->getTypoScriptFrontendController()->config['config']['doctype'] !== 'html5') {
322
            $longDescUrl = $this->cObj->typoLink_URL($conf['longdescURL.']);
323
        } else {
324
            $longDescUrl = trim($conf['longdescURL']);
325
        }
326
        $longDescUrl = strip_tags($longDescUrl);
327
328
        // "alt":
329
        $altParam = ' alt="' . htmlspecialchars($altText) . '"';
330
        // "title":
331
        $emptyTitleHandling = isset($conf['emptyTitleHandling.']) ? $this->cObj->stdWrap($conf['emptyTitleHandling'], $conf['emptyTitleHandling.']) : $conf['emptyTitleHandling'];
332
        // Choices: 'keepEmpty' | 'useAlt' | 'removeAttr'
333
        if ($titleText || $emptyTitleHandling === 'keepEmpty') {
334
            $altParam .= ' title="' . htmlspecialchars($titleText) . '"';
335
        } elseif (!$titleText && $emptyTitleHandling === 'useAlt') {
336
            $altParam .= ' title="' . htmlspecialchars($altText) . '"';
337
        }
338
        // "longDesc" URL
339
        if ($longDesc && !empty($longDescUrl)) {
340
            $altParam .= ' longdesc="' . htmlspecialchars($longDescUrl) . '"';
341
        }
342
        return $altParam;
343
    }
344
345
    /**
346
     * @return TypoScriptFrontendController
347
     */
348
    protected function getTypoScriptFrontendController()
349
    {
350
        return $GLOBALS['TSFE'];
351
    }
352
}
353