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