Passed
Push — master ( a7d46b...0a8eff )
by
unknown
14:11
created

ImageService::getImageFromSourceString()   B

Complexity

Conditions 7
Paths 8

Size

Total Lines 24
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 16
dl 0
loc 24
rs 8.8333
c 0
b 0
f 0
cc 7
nc 8
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the TYPO3 CMS project.
7
 *
8
 * It is free software; you can redistribute it and/or modify it under
9
 * the terms of the GNU General Public License, either version 2
10
 * of the License, or any later version.
11
 *
12
 * For the full copyright and license information, please read the
13
 * LICENSE.txt file that was distributed with this source code.
14
 *
15
 * The TYPO3 project - inspiring people to share!
16
 */
17
18
namespace TYPO3\CMS\Extbase\Service;
19
20
use Psr\Http\Message\ServerRequestInterface;
21
use TYPO3\CMS\Core\Http\ApplicationType;
22
use TYPO3\CMS\Core\LinkHandling\LinkService;
23
use TYPO3\CMS\Core\Page\AssetCollector;
24
use TYPO3\CMS\Core\Resource\File;
25
use TYPO3\CMS\Core\Resource\FileInterface;
26
use TYPO3\CMS\Core\Resource\FileReference;
27
use TYPO3\CMS\Core\Resource\ProcessedFile;
28
use TYPO3\CMS\Core\Resource\ResourceFactory;
29
use TYPO3\CMS\Core\SingletonInterface;
30
use TYPO3\CMS\Core\Utility\GeneralUtility;
31
use TYPO3\CMS\Core\Utility\MathUtility;
32
33
/**
34
 * Service for processing images
35
 */
36
class ImageService implements SingletonInterface
37
{
38
    /**
39
     * @var ResourceFactory
40
     */
41
    protected $resourceFactory;
42
43
    /**
44
     * ImageService constructor.
45
     *
46
     * @param ResourceFactory|null $resourceFactory
47
     */
48
    public function __construct(ResourceFactory $resourceFactory = null)
49
    {
50
        $this->resourceFactory = $resourceFactory ?? GeneralUtility::makeInstance(ResourceFactory::class);
51
    }
52
53
    /**
54
     * Create a processed file
55
     *
56
     * @param FileInterface|FileReference $image
57
     * @param array $processingInstructions
58
     * @return ProcessedFile
59
     */
60
    public function applyProcessingInstructions($image, array $processingInstructions): ProcessedFile
61
    {
62
        /*
63
         * todo: this method should be split to be able to have a proper method signature.
64
         * todo: actually, this method only really works with objects of type \TYPO3\CMS\Core\Resource\File, as this
65
         * todo: is the only implementation that supports the support method.
66
         */
67
        if (is_callable([$image, 'getOriginalFile'])) {
68
            // Get the original file from the file reference
69
            $image = $image->getOriginalFile();
0 ignored issues
show
Bug introduced by
The method getOriginalFile() does not exist on TYPO3\CMS\Core\Resource\FileInterface. It seems like you code against a sub-type of TYPO3\CMS\Core\Resource\FileInterface such as TYPO3\CMS\Core\Resource\FileReference or TYPO3\CMS\Core\Resource\ProcessedFile. ( Ignorable by Annotation )

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

69
            /** @scrutinizer ignore-call */ 
70
            $image = $image->getOriginalFile();
Loading history...
70
        }
71
72
        $processedImage = $image->process(ProcessedFile::CONTEXT_IMAGECROPSCALEMASK, $processingInstructions);
0 ignored issues
show
Bug introduced by
The method process() does not exist on TYPO3\CMS\Core\Resource\FileInterface. It seems like you code against a sub-type of TYPO3\CMS\Core\Resource\FileInterface such as TYPO3\CMS\Core\Resource\File. ( Ignorable by Annotation )

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

72
        /** @scrutinizer ignore-call */ 
73
        $processedImage = $image->process(ProcessedFile::CONTEXT_IMAGECROPSCALEMASK, $processingInstructions);
Loading history...
Bug introduced by
The method process() does not exist on TYPO3\CMS\Core\Resource\FileReference. ( Ignorable by Annotation )

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

72
        /** @scrutinizer ignore-call */ 
73
        $processedImage = $image->process(ProcessedFile::CONTEXT_IMAGECROPSCALEMASK, $processingInstructions);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
73
        $this->setCompatibilityValues($processedImage);
74
75
        return $processedImage;
76
    }
77
78
    /**
79
     * Get public url of image depending on the environment
80
     *
81
     * @param FileInterface $image
82
     * @param bool|false $absolute Force absolute URL
83
     * @return string
84
     */
85
    public function getImageUri(FileInterface $image, bool $absolute = false): string
86
    {
87
        $imageUrl = $image->getPublicUrl();
88
        if ($imageUrl === null) {
89
            // Image is missing probably, return an empty string instead of parsing
90
            return '';
91
        }
92
93
        $parsedUrl = parse_url($imageUrl);
94
        // no prefix in case of an already fully qualified URL
95
        if (isset($parsedUrl['host'])) {
96
            $uriPrefix = '';
97
        } elseif (($GLOBALS['TYPO3_REQUEST'] ?? null) instanceof ServerRequestInterface
98
            && ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isFrontend()
99
        ) {
100
            $uriPrefix = $GLOBALS['TSFE']->absRefPrefix;
101
        } else {
102
            $uriPrefix = GeneralUtility::getIndpEnv('TYPO3_SITE_PATH');
103
        }
104
105
        if ($absolute) {
106
            // If full URL has no scheme we add the same scheme as used by the site
107
            // so we have an absolute URL also usable outside of browser scope (e.g. in an email message)
108
            if (isset($parsedUrl['host']) && !isset($parsedUrl['scheme'])) {
109
                $uriPrefix = (GeneralUtility::getIndpEnv('TYPO3_SSL') ? 'https:' : 'http:') . $uriPrefix;
110
            }
111
            return GeneralUtility::locationHeaderUrl($uriPrefix . $imageUrl);
112
        }
113
        return $uriPrefix . $imageUrl;
114
    }
115
116
    /**
117
     * Get File or FileReference object
118
     *
119
     * This method is a factory and compatibility method that does not belong to
120
     * this service, but is put here for pragmatic reasons for the time being.
121
     * It should be removed once we do not support string sources for images anymore.
122
     *
123
     * @param string $src
124
     * @param FileInterface|\TYPO3\CMS\Extbase\Domain\Model\FileReference|null $image
125
     * @param bool $treatIdAsReference
126
     * @return FileInterface|File|FileReference
127
     * @throws \UnexpectedValueException
128
     * @internal
129
     */
130
    public function getImage(string $src, $image, bool $treatIdAsReference): FileInterface
131
    {
132
        if ($image === null) {
133
            $image = $this->getImageFromSourceString($src, $treatIdAsReference);
134
        } elseif (is_callable([$image, 'getOriginalResource'])) {
135
            // We have a domain model, so we need to fetch the FAL resource object from there
136
            $image = $image->getOriginalResource();
0 ignored issues
show
Bug introduced by
The method getOriginalResource() does not exist on TYPO3\CMS\Core\Resource\FileInterface. ( Ignorable by Annotation )

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

136
            /** @scrutinizer ignore-call */ 
137
            $image = $image->getOriginalResource();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
137
        }
138
139
        if (!($image instanceof File || $image instanceof FileReference)) {
140
            throw new \UnexpectedValueException('Supplied file object type ' . get_class($image) . ' for ' . $src . ' must be File or FileReference.', 1382687163);
141
        }
142
143
        return $image;
144
    }
145
146
    /**
147
     * Get File or FileReference object by src
148
     *
149
     * @param string $src
150
     * @param bool $treatIdAsReference
151
     * @return FileInterface|FileReference|\TYPO3\CMS\Core\Resource\Folder
152
     */
153
    protected function getImageFromSourceString(string $src, bool $treatIdAsReference): object
154
    {
155
        if (($GLOBALS['TYPO3_REQUEST'] ?? null) instanceof ServerRequestInterface
156
            && ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isBackend()
157
            && strpos($src, '../') === 0
158
        ) {
159
            $src = substr($src, 3);
160
        }
161
        if (MathUtility::canBeInterpretedAsInteger($src)) {
162
            if ($treatIdAsReference) {
163
                $image = $this->resourceFactory->getFileReferenceObject($src);
0 ignored issues
show
Bug introduced by
$src of type string is incompatible with the type integer expected by parameter $uid of TYPO3\CMS\Core\Resource\...etFileReferenceObject(). ( Ignorable by Annotation )

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

163
                $image = $this->resourceFactory->getFileReferenceObject(/** @scrutinizer ignore-type */ $src);
Loading history...
164
            } else {
165
                $image = $this->resourceFactory->getFileObject($src);
0 ignored issues
show
Bug introduced by
$src of type string is incompatible with the type integer expected by parameter $uid of TYPO3\CMS\Core\Resource\...actory::getFileObject(). ( Ignorable by Annotation )

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

165
                $image = $this->resourceFactory->getFileObject(/** @scrutinizer ignore-type */ $src);
Loading history...
166
            }
167
        } elseif (strpos($src, 't3://file') === 0) {
168
            // We have a t3://file link to a file in FAL
169
            $linkService = GeneralUtility::makeInstance(LinkService::class);
170
            $data = $linkService->resolveByStringRepresentation($src);
171
            $image = $data['file'];
172
        } else {
173
            // We have a combined identifier or legacy (storage 0) path
174
            $image = $this->resourceFactory->retrieveFileOrFolderObject($src);
175
        }
176
        return $image;
177
    }
178
179
    /**
180
     * Set compatibility values to frontend controller object
181
     * in case we are in frontend environment.
182
     *
183
     * @param ProcessedFile $processedImage
184
     */
185
    protected function setCompatibilityValues(ProcessedFile $processedImage): void
186
    {
187
        $publicUrl = $processedImage->getPublicUrl();
188
        if ($publicUrl !== null) {
189
            // only add the processed image to AssetCollector if the public url is not NULL
190
            $imageInfoValues = $this->getCompatibilityImageResourceValues($processedImage);
191
            GeneralUtility::makeInstance(AssetCollector::class)->addMedia(
192
                $publicUrl,
193
                $imageInfoValues
194
            );
195
        }
196
    }
197
198
    /**
199
     * Calculates the compatibility values
200
     * This is duplicate code taken from ContentObjectRenderer::getImgResource()
201
     * Ideally we should get rid of this code in both places.
202
     *
203
     * @param ProcessedFile $processedImage
204
     * @return array
205
     */
206
    protected function getCompatibilityImageResourceValues(ProcessedFile $processedImage): array
207
    {
208
        $originalFile = $processedImage->getOriginalFile();
209
        return [
210
            0 => $processedImage->getProperty('width'),
211
            1 => $processedImage->getProperty('height'),
212
            2 => $processedImage->getExtension(),
213
            3 => $processedImage->getPublicUrl(),
214
            'origFile' => $originalFile->getPublicUrl(),
215
            'origFile_mtime' => $originalFile->getModificationTime(),
216
        ];
217
    }
218
}
219