1<?php 2namespace TYPO3\CMS\Core\Resource\Service; 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 17use TYPO3\CMS\Core\Resource; 18use TYPO3\CMS\Core\Utility\GeneralUtility; 19use TYPO3\CMS\Core\Utility\MathUtility; 20use TYPO3\CMS\Extbase\Object\ObjectManager; 21use TYPO3\CMS\Extbase\SignalSlot\Dispatcher; 22 23/** 24 * File processing service 25 */ 26class FileProcessingService 27{ 28 /** 29 * @var Resource\ResourceStorage 30 */ 31 protected $storage; 32 33 /** 34 * @var Resource\Driver\DriverInterface 35 */ 36 protected $driver; 37 38 /** 39 * @var Dispatcher 40 */ 41 protected $signalSlotDispatcher; 42 43 const SIGNAL_PreFileProcess = 'preFileProcess'; 44 const SIGNAL_PostFileProcess = 'postFileProcess'; 45 46 /** 47 * Creates this object. 48 * 49 * @param Resource\ResourceStorage $storage 50 * @param Resource\Driver\DriverInterface $driver 51 */ 52 public function __construct(Resource\ResourceStorage $storage, Resource\Driver\DriverInterface $driver) 53 { 54 $this->storage = $storage; 55 $this->driver = $driver; 56 } 57 58 /** 59 * Processes a file 60 * 61 * @param Resource\FileInterface $fileObject The file object 62 * @param Resource\ResourceStorage $targetStorage The storage to store the processed file in 63 * @param string $taskType 64 * @param array $configuration 65 * 66 * @return Resource\ProcessedFile 67 * @throws \InvalidArgumentException 68 */ 69 public function processFile(Resource\FileInterface $fileObject, Resource\ResourceStorage $targetStorage, $taskType, $configuration) 70 { 71 // Enforce default configuration for preview processing here, 72 // to be sure we find already processed files below, 73 // which we wouldn't if we would change the configuration later, as configuration is part of the lookup. 74 if ($taskType === Resource\ProcessedFile::CONTEXT_IMAGEPREVIEW) { 75 $configuration = Resource\Processing\LocalPreviewHelper::preProcessConfiguration($configuration); 76 } 77 // Ensure that the processing configuration which is part of the hash sum is properly cast, so 78 // unnecessary duplicate images are not produced, see #80942 79 foreach ($configuration as &$value) { 80 if (MathUtility::canBeInterpretedAsInteger($value)) { 81 $value = (int)$value; 82 } 83 } 84 85 /** @var Resource\ProcessedFileRepository $processedFileRepository */ 86 $processedFileRepository = GeneralUtility::makeInstance(Resource\ProcessedFileRepository::class); 87 88 $processedFile = $processedFileRepository->findOneByOriginalFileAndTaskTypeAndConfiguration($fileObject, $taskType, $configuration); 89 90 // set the storage of the processed file 91 // Pre-process the file 92 $this->emitPreFileProcessSignal($processedFile, $fileObject, $taskType, $configuration); 93 94 // Only handle the file if it is not processed yet 95 // (maybe modified or already processed by a signal) 96 // or (in case of preview images) already in the DB/in the processing folder 97 if (!$processedFile->isProcessed()) { 98 $this->process($processedFile, $targetStorage); 99 } 100 101 // Post-process (enrich) the file 102 $this->emitPostFileProcessSignal($processedFile, $fileObject, $taskType, $configuration); 103 104 return $processedFile; 105 } 106 107 /** 108 * Processes the file 109 * 110 * @param Resource\ProcessedFile $processedFile 111 * @param Resource\ResourceStorage $targetStorage The storage to put the processed file into 112 */ 113 protected function process(Resource\ProcessedFile $processedFile, Resource\ResourceStorage $targetStorage) 114 { 115 // We only have to trigger the file processing if the file either is new, does not exist or the 116 // original file has changed since the last processing run (the last case has to trigger a reprocessing 117 // even if the original file was used until now) 118 if ($processedFile->isNew() || (!$processedFile->usesOriginalFile() && !$processedFile->exists()) || 119 $processedFile->isOutdated()) { 120 $task = $processedFile->getTask(); 121 /** @var Resource\Processing\LocalImageProcessor $processor */ 122 $processor = GeneralUtility::makeInstance(Resource\Processing\LocalImageProcessor::class); 123 $processor->processTask($task); 124 125 if ($task->isExecuted() && $task->isSuccessful() && $processedFile->isProcessed()) { 126 /** @var Resource\ProcessedFileRepository $processedFileRepository */ 127 $processedFileRepository = GeneralUtility::makeInstance(Resource\ProcessedFileRepository::class); 128 $processedFileRepository->add($processedFile); 129 } 130 } 131 } 132 133 /** 134 * Get the SignalSlot dispatcher 135 * 136 * @return Dispatcher 137 */ 138 protected function getSignalSlotDispatcher() 139 { 140 if (!isset($this->signalSlotDispatcher)) { 141 $this->signalSlotDispatcher = GeneralUtility::makeInstance(ObjectManager::class)->get(Dispatcher::class); 142 } 143 return $this->signalSlotDispatcher; 144 } 145 146 /** 147 * Emits file pre-processing signal. 148 * 149 * @param Resource\ProcessedFile $processedFile 150 * @param Resource\FileInterface $file 151 * @param string $context 152 * @param array $configuration 153 */ 154 protected function emitPreFileProcessSignal(Resource\ProcessedFile $processedFile, Resource\FileInterface $file, $context, array $configuration = []) 155 { 156 $this->getSignalSlotDispatcher()->dispatch( 157 Resource\ResourceStorage::class, 158 self::SIGNAL_PreFileProcess, 159 [$this, $this->driver, $processedFile, $file, $context, $configuration] 160 ); 161 } 162 163 /** 164 * Emits file post-processing signal. 165 * 166 * @param Resource\ProcessedFile $processedFile 167 * @param Resource\FileInterface $file 168 * @param $context 169 * @param array $configuration 170 */ 171 protected function emitPostFileProcessSignal(Resource\ProcessedFile $processedFile, Resource\FileInterface $file, $context, array $configuration = []) 172 { 173 $this->getSignalSlotDispatcher()->dispatch( 174 Resource\ResourceStorage::class, 175 self::SIGNAL_PostFileProcess, 176 [$this, $this->driver, $processedFile, $file, $context, $configuration] 177 ); 178 } 179} 180