‪TYPO3CMS  ‪main
ProcessedFileRepository.php
Go to the documentation of this file.
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 
19 
20 use Psr\Log\LoggerAwareInterface;
21 use Psr\Log\LoggerAwareTrait;
31 
38 class ‪ProcessedFileRepository implements LoggerAwareInterface, ‪SingletonInterface
39 {
40  use LoggerAwareTrait;
41 
48  protected array ‪$tableColumns = [];
49 
50  public function ‪__construct(
51  protected readonly ‪ResourceFactory $factory,
52  protected readonly ‪TaskTypeRegistry $taskTypeRegistry
53  ) {}
54 
58  public function ‪findByUid(int ‪$uid): ‪ProcessedFile
59  {
60  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_file_processedfile');
61  $row = $queryBuilder
62  ->select('*')
63  ->from('sys_file_processedfile')
64  ->where(
65  $queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter(‪$uid, ‪Connection::PARAM_INT))
66  )
67  ->executeQuery()
68  ->fetchAssociative();
69  if (!is_array($row)) {
70  throw new \RuntimeException('Could not find row with UID "' . ‪$uid . '" in table "sys_file_processedfile"', 1695122090);
71  }
72  return $this->‪createDomainObject($row);
73  }
74 
76  {
77  $processedFileObject = null;
78  if ($storage->‪hasFile(‪$identifier)) {
79  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_file_processedfile');
80  $databaseRow = $queryBuilder
81  ->select('*')
82  ->from('sys_file_processedfile')
83  ->where(
84  $queryBuilder->expr()->eq(
85  'storage',
86  $queryBuilder->createNamedParameter($storage->‪getUid(), ‪Connection::PARAM_INT)
87  ),
88  $queryBuilder->expr()->eq(
89  'identifier',
90  $queryBuilder->createNamedParameter(‪$identifier)
91  )
92  )
93  ->executeQuery()
94  ->fetchAssociative();
95 
96  if ($databaseRow) {
97  $processedFileObject = $this->‪createDomainObject($databaseRow);
98  }
99  }
100  return $processedFileObject;
101  }
102 
107  public function ‪countByStorage(‪ResourceStorage $storage): int
108  {
109  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
110  ->getQueryBuilderForTable('sys_file_processedfile');
111  return (int)$queryBuilder
112  ->count('uid')
113  ->from('sys_file_processedfile')
114  ->where(
115  $queryBuilder->expr()->eq(
116  'storage',
117  $queryBuilder->createNamedParameter($storage->‪getUid(), ‪Connection::PARAM_INT)
118  )
119  )
120  ->executeQuery()
121  ->fetchOne();
122  }
123 
127  public function ‪add(‪ProcessedFile $processedFile): void
128  {
129  if ($processedFile->‪isPersisted()) {
130  $this->‪update($processedFile);
131  } else {
132  $currentTimestamp = GeneralUtility::makeInstance(Context::class)->getPropertyFromAspect('date', 'timestamp');
133  $insertFields = $processedFile->toArray();
134  $insertFields['crdate'] = $currentTimestamp;
135  $insertFields['tstamp'] = $currentTimestamp;
136  $insertFields = $this->‪cleanUnavailableColumns($insertFields);
137 
138  $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('sys_file_processedfile');
139  $connection->insert(
140  'sys_file_processedfile',
141  $insertFields,
142  ['configuration' => ‪Connection::PARAM_LOB]
143  );
144 
145  ‪$uid = $connection->lastInsertId();
146  $processedFile->‪updateProperties(['uid' => ‪$uid]);
147  }
148  }
149 
154  public function ‪update(‪ProcessedFile $processedFile): void
155  {
156  if ($processedFile->‪isPersisted()) {
157  ‪$uid = (int)$processedFile->‪getUid();
158  $updateFields = $this->‪cleanUnavailableColumns($processedFile->toArray());
159  unset($updateFields['uid']);
160  $updateFields['tstamp'] = time();
161 
162  $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('sys_file_processedfile');
163  $connection->update(
164  'sys_file_processedfile',
165  $updateFields,
166  [
167  'uid' => ‪$uid,
168  ],
169  ['configuration' => ‪Connection::PARAM_LOB]
170  );
171  }
172  }
173 
177  public function ‪findOneByOriginalFileAndTaskTypeAndConfiguration(‪File $file, string $taskType, array $configuration): ‪ProcessedFile
178  {
179  // Creating a task object to only fetch cleaned configuration properties
180  $task = $this->‪prepareTaskObject($file, $taskType, $configuration);
181  $configuration = $task->getConfiguration();
182 
183  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_file_processedfile');
184  $databaseRow = $queryBuilder
185  ->select('*')
186  ->from('sys_file_processedfile')
187  ->where(
188  $queryBuilder->expr()->eq(
189  'original',
190  $queryBuilder->createNamedParameter($file->‪getUid(), ‪Connection::PARAM_INT)
191  ),
192  $queryBuilder->expr()->eq('task_type', $queryBuilder->createNamedParameter($taskType)),
193  $queryBuilder->expr()->eq(
194  'configurationsha1',
195  $queryBuilder->createNamedParameter(sha1((new ‪ConfigurationService())->serialize($configuration)))
196  )
197  )
198  ->executeQuery()
199  ->fetchAssociative();
200 
201  if (is_array($databaseRow)) {
202  $processedFile = $this->‪createDomainObject($databaseRow);
203  } else {
204  $processedFile = $this->‪createNewProcessedFileObject($file, $taskType, $configuration);
205  }
206  return $processedFile;
207  }
208 
212  public function ‪findAllByOriginalFile(‪File $file): array
213  {
214  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_file_processedfile');
215  $result = $queryBuilder
216  ->select('*')
217  ->from('sys_file_processedfile')
218  ->where(
219  $queryBuilder->expr()->eq(
220  'original',
221  $queryBuilder->createNamedParameter($file->‪getUid(), ‪Connection::PARAM_INT)
222  )
223  )
224  ->executeQuery();
225 
226  $itemList = [];
227  while ($row = $result->fetchAssociative()) {
228  $itemList[] = $this->‪createDomainObject($row);
229  }
230  return $itemList;
231  }
232 
240  public function ‪removeAll(int $storageUid = null): int
241  {
242  $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('sys_file_processedfile');
243  $queryBuilder = $connection->createQueryBuilder();
244  $where = [
245  $queryBuilder->expr()->neq('identifier', $queryBuilder->createNamedParameter('')),
246  ];
247  if ($storageUid !== null) {
248  $where[] = $queryBuilder->expr()->eq(
249  'storage',
250  $queryBuilder->createNamedParameter($storageUid, ‪Connection::PARAM_INT)
251  );
252  }
253  $result = $queryBuilder
254  ->select('*')
255  ->from('sys_file_processedfile')
256  ->where(...$where)
257  ->executeQuery();
258 
259  $errorCount = 0;
260 
261  while ($row = $result->fetchAssociative()) {
262  if ($storageUid && $storageUid !== (int)$row['storage']) {
263  continue;
264  }
265  try {
266  $file = $this->‪createDomainObject($row);
267  $file->getStorage()->setEvaluatePermissions(false);
268  $file->delete(true);
269  } catch (\‪Exception $e) {
270  $this->logger->error('Failed to delete file {identifier} in storage uid {storage}.', [
271  'identifier' => $row['identifier'],
272  'storage' => $row['storage'],
273  'exception' => $e,
274  ]);
275  ++$errorCount;
276  }
277  }
278 
279  if ($storageUid === null) {
280  // Truncate entire table if not restricted to specific storage
281  $connection->truncate('sys_file_processedfile');
282  } else {
283  // else remove db rows of this storage only
284  $connection->delete('sys_file_processedfile', ['storage' => $storageUid], [‪Connection::PARAM_INT]);
285  }
286 
287  return $errorCount;
288  }
289 
293  protected function ‪createNewProcessedFileObject(‪FileInterface $originalFile, string $taskType, array $configuration): ‪ProcessedFile
294  {
295  return GeneralUtility::makeInstance(
296  ProcessedFile::class,
297  $originalFile,
298  $taskType,
299  $configuration
300  );
301  }
302 
303  protected function ‪createDomainObject(array $databaseRow): ‪ProcessedFile
304  {
305  $originalFile = $this->factory->getFileObject((int)$databaseRow['original']);
306  $taskType = $databaseRow['task_type'];
307  // Allow deserialization of Area class, since Area objects get serialized in configuration
308  // TODO: This should be changed to json encode and decode at some point
309  $configuration = unserialize(
310  $databaseRow['configuration'],
311  [
312  'allowed_classes' => [
313  Area::class,
314  ],
315  ]
316  );
317 
318  return GeneralUtility::makeInstance(
319  ProcessedFile::class,
320  $originalFile,
321  $taskType,
322  $configuration,
323  $databaseRow
324  );
325  }
326 
330  protected function ‪cleanUnavailableColumns(array $data): array
331  {
332  // As determining the table columns is a costly operation this is done only once during runtime and cached then
333  if ($this->tableColumns === []) {
334  $this->tableColumns = GeneralUtility::makeInstance(ConnectionPool::class)
335  ->getConnectionForTable('sys_file_processedfile')
336  ->createSchemaManager()
337  ->listTableColumns('sys_file_processedfile');
338  }
339 
340  return array_intersect_key($data, $this->tableColumns);
341  }
342 
358  protected function ‪prepareTaskObject(‪FileInterface $fileObject, string $taskType, array $configuration): ‪TaskInterface
359  {
360  $temporaryProcessedFile = $this->‪createNewProcessedFileObject($fileObject, $taskType, $configuration);
361  $taskObject = $this->taskTypeRegistry->getTaskForType($taskType, $temporaryProcessedFile, $configuration);
362  $taskObject->sanitizeConfiguration();
363  return $taskObject;
364  }
365 }
‪TYPO3\CMS\Core\Resource\ProcessedFileRepository
Definition: ProcessedFileRepository.php:39
‪TYPO3\CMS\Core\Database\Connection\PARAM_INT
‪const PARAM_INT
Definition: Connection.php:52
‪TYPO3\CMS\Core\Resource\ResourceStorage\getUid
‪int getUid()
Definition: ResourceStorage.php:337
‪TYPO3\CMS\Core\Resource\FileInterface
Definition: FileInterface.php:26
‪TYPO3\CMS\Core\Resource\ProcessedFileRepository\countByStorage
‪countByStorage(ResourceStorage $storage)
Definition: ProcessedFileRepository.php:107
‪TYPO3\CMS\Core\Resource\Processing\TaskInterface
Definition: TaskInterface.php:34
‪TYPO3\CMS\Core\Imaging\ImageManipulation\Area
Definition: Area.php:23
‪TYPO3\CMS\Core\Resource\ProcessedFileRepository\findByStorageAndIdentifier
‪findByStorageAndIdentifier(ResourceStorage $storage, string $identifier)
Definition: ProcessedFileRepository.php:75
‪TYPO3\CMS\Core\Resource\ProcessedFileRepository\findByUid
‪findByUid(int $uid)
Definition: ProcessedFileRepository.php:58
‪TYPO3\CMS\Core\Resource\ProcessedFileRepository\createDomainObject
‪createDomainObject(array $databaseRow)
Definition: ProcessedFileRepository.php:303
‪TYPO3\CMS\Core\Resource\Processing\TaskTypeRegistry
Definition: TaskTypeRegistry.php:26
‪TYPO3\CMS\Core\Context\Context
Definition: Context.php:54
‪TYPO3\CMS\Core\Resource\ResourceStorage\hasFile
‪bool hasFile($identifier)
Definition: ResourceStorage.php:1653
‪TYPO3\CMS\Core\Resource\ProcessedFileRepository\cleanUnavailableColumns
‪cleanUnavailableColumns(array $data)
Definition: ProcessedFileRepository.php:330
‪TYPO3\CMS\Core\Resource\ProcessedFile\getUid
‪getUid()
Definition: ProcessedFile.php:441
‪TYPO3\CMS\Core\Resource\ProcessedFileRepository\createNewProcessedFileObject
‪createNewProcessedFileObject(FileInterface $originalFile, string $taskType, array $configuration)
Definition: ProcessedFileRepository.php:293
‪TYPO3\CMS\Core\Resource\ResourceFactory
Definition: ResourceFactory.php:42
‪TYPO3\CMS\Core\Resource\File
Definition: File.php:26
‪TYPO3\CMS\Core\Resource\ProcessedFileRepository\$tableColumns
‪array $tableColumns
Definition: ProcessedFileRepository.php:48
‪TYPO3\CMS\Core\Resource\ProcessedFile\isPersisted
‪isPersisted()
Definition: ProcessedFile.php:203
‪TYPO3\CMS\Core\Resource
Definition: generateMimeTypes.php:52
‪TYPO3\CMS\Core\Resource\ProcessedFile
Definition: ProcessedFile.php:47
‪TYPO3\CMS\Core\Resource\ProcessedFileRepository\add
‪add(ProcessedFile $processedFile)
Definition: ProcessedFileRepository.php:127
‪TYPO3\CMS\Core\Resource\AbstractFile\getUid
‪return MathUtility::canBeInterpretedAsInteger($size) ?(int) $size int getUid()
Definition: AbstractFile.php:195
‪TYPO3\CMS\Core\Resource\Exception
Definition: Exception.php:21
‪TYPO3\CMS\Core\Database\Connection
Definition: Connection.php:41
‪TYPO3\CMS\Core\Resource\ResourceStorage
Definition: ResourceStorage.php:128
‪TYPO3\CMS\Core\Resource\Service\ConfigurationService
Definition: ConfigurationService.php:34
‪TYPO3\CMS\Webhooks\Message\$uid
‪identifier readonly int $uid
Definition: PageModificationMessage.php:35
‪TYPO3\CMS\Core\SingletonInterface
Definition: SingletonInterface.php:22
‪TYPO3\CMS\Core\Resource\ProcessedFileRepository\findOneByOriginalFileAndTaskTypeAndConfiguration
‪findOneByOriginalFileAndTaskTypeAndConfiguration(File $file, string $taskType, array $configuration)
Definition: ProcessedFileRepository.php:177
‪TYPO3\CMS\Core\Resource\ProcessedFileRepository\findAllByOriginalFile
‪ProcessedFile[] findAllByOriginalFile(File $file)
Definition: ProcessedFileRepository.php:212
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\Resource\ProcessedFile\updateProperties
‪updateProperties(array $properties)
Definition: ProcessedFile.php:313
‪TYPO3\CMS\Core\Resource\ProcessedFileRepository\update
‪update(ProcessedFile $processedFile)
Definition: ProcessedFileRepository.php:154
‪TYPO3\CMS\Core\Resource\ProcessedFileRepository\__construct
‪__construct(protected readonly ResourceFactory $factory, protected readonly TaskTypeRegistry $taskTypeRegistry)
Definition: ProcessedFileRepository.php:50
‪TYPO3\CMS\Core\Resource\ProcessedFileRepository\removeAll
‪int removeAll(int $storageUid=null)
Definition: ProcessedFileRepository.php:240
‪TYPO3\CMS\Core\Resource\ProcessedFileRepository\prepareTaskObject
‪prepareTaskObject(FileInterface $fileObject, string $taskType, array $configuration)
Definition: ProcessedFileRepository.php:358
‪TYPO3\CMS\Webhooks\Message\$identifier
‪identifier readonly string $identifier
Definition: FileAddedMessage.php:37
‪TYPO3\CMS\Core\Database\Connection\PARAM_LOB
‪const PARAM_LOB
Definition: Connection.php:62