‪TYPO3CMS  ‪main
ProcessedFile.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 
24 
47 {
48  /*********************************************
49  * FILE PROCESSING CONTEXTS
50  *********************************************/
55  public const ‪CONTEXT_IMAGEPREVIEW = 'Image.Preview';
61  public const ‪CONTEXT_IMAGECROPSCALEMASK = 'Image.CropScaleMask';
62 
68  protected ‪$taskType;
69 
73  protected ‪$task;
74 
78  protected ‪$taskTypeRegistry;
79 
86 
92  protected ‪$originalFile;
93 
101  protected ‪$originalFileSha1;
102 
109  protected ‪$updated = false;
110 
117  protected ‪$processingUrl = '';
118 
125  public function ‪__construct(‪File ‪$originalFile, ‪$taskType, array ‪$processingConfiguration, array $databaseRow = null)
126  {
127  $this->originalFile = ‪$originalFile;
128  $this->originalFileSha1 = $this->originalFile->‪getSha1();
129  $this->storage = ‪$originalFile->‪getStorage()->getProcessingFolder()->‪getStorage();
130  $this->taskType = ‪$taskType;
131  $this->processingConfiguration = ‪$processingConfiguration;
132  if (is_array($databaseRow)) {
133  $this->‪reconstituteFromDatabaseRecord($databaseRow);
134  }
135  $this->taskTypeRegistry = GeneralUtility::makeInstance(TaskTypeRegistry::class);
136  }
137 
141  protected function ‪reconstituteFromDatabaseRecord(array $databaseRow)
142  {
143  $this->taskType = $this->taskType ?: $databaseRow['task_type'];
144  // @todo In case the original configuration contained file objects the reconstitution fails. See ConfigurationService->serialize()
145  $this->processingConfiguration = $this->processingConfiguration ?: unserialize($databaseRow['configuration'] ?? '');
146 
147  $this->originalFileSha1 = $databaseRow['originalfilesha1'];
148  $this->identifier = $databaseRow['identifier'];
149  $this->name = $databaseRow['name'];
150  $this->‪properties = $databaseRow;
151  $this->processingUrl = $databaseRow['processing_url'] ?? '';
152 
153  if (!empty($databaseRow['storage']) && (int)$this->storage->getUid() !== (int)$databaseRow['storage']) {
154  $this->storage = GeneralUtility::makeInstance(StorageRepository::class)->findByUid($databaseRow['storage']);
155  }
156  }
157 
158  /********************************
159  * VARIOUS FILE PROPERTY GETTERS
160  ********************************/
161 
167  // @todo replace these usages with direct calls to the task object
168  public function ‪calculateChecksum()
169  {
170  return $this->‪getTask()->getConfigurationChecksum();
171  }
172 
173  /*******************
174  * CONTENTS RELATED
175  *******************/
181  public function ‪setContents(string $contents): self
182  {
183  throw new \BadMethodCallException('Setting contents not possible for processed file.', 1305438528);
184  }
185 
192  public function ‪updateWithLocalFile($filePath)
193  {
194  if (empty($this->identifier)) {
195  throw new \RuntimeException('Cannot update original file!', 1350582054);
196  }
197  $processingFolder = $this->originalFile->getStorage()->getProcessingFolder($this->originalFile);
198  $addedFile = $this->storage->updateProcessedFile($filePath, $this, $processingFolder);
199 
200  // Update some related properties
201  $this->identifier = $addedFile->getIdentifier();
202  $this->originalFileSha1 = $this->originalFile->getSha1();
203  if ($addedFile instanceof AbstractFile) {
204  $this->‪updateProperties($addedFile->getProperties());
205  }
206  $this->deleted = false;
207  $this->updated = true;
208  }
209 
210  /*****************************************
211  * STORAGE AND MANAGEMENT RELATED METHODS
212  *****************************************/
218  public function ‪isIndexed(): bool
219  {
220  // Processed files are never indexed; instead you might be looking for isPersisted()
221  return false;
222  }
223 
229  public function ‪isPersisted()
230  {
231  return is_array($this->‪properties) && array_key_exists('uid', $this->‪properties) && $this->‪properties['uid'] > 0;
232  }
233 
239  public function ‪isNew()
240  {
241  return !$this->‪isPersisted();
242  }
243 
250  public function ‪isUpdated()
251  {
253  }
254 
260  public function ‪setName(‪$name)
261  {
262  // Remove the existing file, but only we actually have a name or the name has changed
263  if (!empty($this->name) && $this->name !== ‪$name && $this->‪exists()) {
264  $this->delete();
265  }
266 
267  $this->name = ‪$name;
268  // @todo this is a *weird* hack that will fail if the storage is non-hierarchical!
269  $this->identifier = $this->storage->getProcessingFolder($this->originalFile)->getIdentifier() . ‪$this->name;
270 
271  $this->updated = true;
272  }
273 
281  public function ‪exists()
282  {
283  if ($this->‪usesOriginalFile()) {
284  return $this->originalFile->exists();
285  }
286 
287  return parent::exists();
288  }
289 
290  /******************
291  * SPECIAL METHODS
292  ******************/
293 
299  public function ‪isProcessed()
300  {
301  return $this->updated || ($this->‪isPersisted() && !$this->‪needsReprocessing());
302  }
303 
309  public function ‪getOriginalFile()
310  {
311  return ‪$this->originalFile;
312  }
313 
323  public function ‪getIdentifier(): string
324  {
325  return (!$this->‪usesOriginalFile()) ? $this->identifier : $this->‪getOriginalFile()->‪getIdentifier();
326  }
327 
337  public function ‪getName(): string
338  {
339  if ($this->‪usesOriginalFile()) {
340  return $this->originalFile->getName();
341  }
342  return ‪$this->name;
343  }
344 
349  public function ‪updateProperties(array ‪$properties)
350  {
351  if (!is_array($this->‪properties)) {
352  $this->‪properties = [];
353  }
354 
355  if (array_key_exists('uid', ‪$properties) && ‪MathUtility::canBeInterpretedAsInteger(‪$properties['uid'])) {
356  $this->‪properties['uid'] = $properties['uid'];
357  }
358  if (isset(‪$properties['processing_url'])) {
359  $this->processingUrl = ‪$properties['processing_url'];
360  }
361 
362  // @todo we should have a blacklist of properties that might not be updated
363  $this->‪properties = array_merge($this->‪properties, $properties);
364 
365  // @todo when should this update be done?
366  if (!$this->‪isUnchanged() && $this->‪exists()) {
368  if ($this->‪usesOriginalFile()) {
369  ‪$storage = $this->originalFile->getStorage();
370  }
371  $this->‪properties = array_merge($this->‪properties, ‪$storage->‪getFileInfo($this));
372  }
373  }
374 
380  public function toArray(): array
381  {
382  if ($this->‪usesOriginalFile()) {
383  $properties = $this->originalFile->getProperties();
384  unset(‪$properties['uid']);
385  ‪$properties['identifier'] = '';
386  ‪$properties['name'] = null;
387  ‪$properties['processing_url'] = '';
388 
389  // Use width + height set in processed file
390  ‪$properties['width'] = $this->‪properties['width'] ?? 0;
391  $properties['height'] = $this->‪properties['height'] ?? 0;
392  } else {
393  $properties = ‪$this->properties;
394  ‪$properties['identifier'] = $this->‪getIdentifier();
395  $properties['name'] = $this->‪getName();
396  }
397 
398  $properties['configuration'] = (new ConfigurationService())->serialize($this->processingConfiguration);
399 
400  return array_merge(‪$properties, [
401  'storage' => $this->‪getStorage()->‪getUid(),
402  'checksum' => $this->‪calculateChecksum(),
403  'task_type' => $this->taskType,
404  'configurationsha1' => sha1(‪$properties['configuration']),
405  'original' => $this->originalFile->getUid(),
406  'originalfilesha1' => $this->originalFileSha1,
407  ]);
408  }
409 
415  protected function ‪isUnchanged()
416  {
417  return !($this->‪properties['width'] ?? false) && $this->‪usesOriginalFile();
418  }
419 
423  public function ‪setUsesOriginalFile()
424  {
425  // @todo check if some of these properties can/should be set in a generic update method
426  $this->identifier = $this->originalFile->getIdentifier();
427  $this->updated = true;
428  $this->processingUrl = '';
429  $this->originalFileSha1 = $this->originalFile->getSha1();
430  }
431 
432  public function ‪updateProcessingUrl(string ‪$url): void
433  {
434  $this->updated = true;
435  $this->processingUrl = ‪$url;
436  }
437 
441  public function ‪usesOriginalFile()
442  {
443  return empty($this->identifier) || $this->identifier === $this->originalFile->getIdentifier();
444  }
445 
451  public function ‪isOutdated()
452  {
453  return $this->‪needsReprocessing();
454  }
455 
459  public function delete(bool $force = false): bool
460  {
461  if (!$force && $this->‪isUnchanged()) {
462  return false;
463  }
464  // Only delete file when original isn't used
465  if (!$this->‪usesOriginalFile()) {
466  return parent::delete();
467  }
468  return true;
469  }
470 
476  public function ‪getProperty(string $key): mixed
477  {
478  // The uid always (!) has to come from this file and never the original file (see getOriginalFile() to get this)
479  if ($this->‪isUnchanged() && $key !== 'uid') {
480  return $this->originalFile->getProperty($key);
481  }
482  return $this->‪properties[$key] ?? null;
483  }
484 
490  public function ‪getUid()
491  {
492  return $this->‪properties['uid'] ?? 0;
493  }
494 
500  public function ‪needsReprocessing()
501  {
502  $fileMustBeRecreated = false;
503 
504  // if original is missing we can not reprocess the file
505  if ($this->originalFile->isMissing()) {
506  return false;
507  }
508 
509  // processedFile does not exist
510  if (!$this->‪usesOriginalFile() && !$this->‪exists()) {
511  $fileMustBeRecreated = true;
512  }
513 
514  // hash does not match
515  if (array_key_exists('checksum', $this->‪properties) && $this->‪calculateChecksum() !== $this->‪properties['checksum']) {
516  $fileMustBeRecreated = true;
517  }
518 
519  // original file changed
520  if ($this->originalFile->getSha1() !== $this->originalFileSha1) {
521  $fileMustBeRecreated = true;
522  }
523 
524  if (!array_key_exists('uid', $this->‪properties)) {
525  $fileMustBeRecreated = true;
526  }
527 
528  // remove outdated file
529  if ($fileMustBeRecreated && $this->‪exists()) {
530  $this->delete();
531  }
532  return $fileMustBeRecreated;
533  }
534 
540  public function ‪getProcessingConfiguration()
541  {
543  }
544 
550  public function ‪getTaskIdentifier()
551  {
553  }
554 
560  public function ‪getTask(): Processing\TaskInterface
561  {
562  if ($this->task === null) {
563  $this->task = $this->taskTypeRegistry->getTaskForType($this->taskType, $this, $this->processingConfiguration);
564  }
565 
567  }
568 
575  {
576  ‪$name = $this->originalFile->getNameWithoutExtension();
577  ‪$name .= '_' . $this->originalFile->getUid();
578  ‪$name .= '_' . $this->‪calculateChecksum();
579 
580  return ‪$name;
581  }
582 
588  public function ‪getPublicUrl(): ?string
589  {
590  if ($this->processingUrl) {
592  }
593  if ($this->deleted) {
594  return null;
595  }
596  if ($this->‪usesOriginalFile()) {
597  return $this->‪getOriginalFile()->‪getPublicUrl();
598  }
599  return $this->‪getStorage()->‪getPublicUrl($this);
600  }
601 }
‪TYPO3\CMS\Core\Resource\ProcessedFile\$taskTypeRegistry
‪Processing TaskTypeRegistry $taskTypeRegistry
Definition: ProcessedFile.php:75
‪TYPO3\CMS\Core\Resource\ProcessedFile\getOriginalFile
‪File getOriginalFile()
Definition: ProcessedFile.php:301
‪TYPO3\CMS\Core\Resource\ProcessedFile\CONTEXT_IMAGEPREVIEW
‪const CONTEXT_IMAGEPREVIEW
Definition: ProcessedFile.php:55
‪TYPO3\CMS\Core\Resource\ProcessedFile\usesOriginalFile
‪bool usesOriginalFile()
Definition: ProcessedFile.php:433
‪TYPO3\CMS\Core\Resource\ProcessedFile\$properties
‪$properties['identifier']
Definition: ProcessedFile.php:377
‪TYPO3\CMS\Core\Resource\ResourceStorage\getFileInfo
‪array getFileInfo(FileInterface $fileObject)
Definition: ResourceStorage.php:1440
‪TYPO3\CMS\Core\Resource\ProcessedFile\getUid
‪int getUid()
Definition: ProcessedFile.php:482
‪TYPO3\CMS\Core\Resource\ProcessedFile\getPublicUrl
‪non empty string null getPublicUrl()
Definition: ProcessedFile.php:580
‪TYPO3\CMS\Core\Resource\Processing\TaskInterface
Definition: TaskInterface.php:33
‪TYPO3\CMS\Core\Resource\ProcessedFile\needsReprocessing
‪bool needsReprocessing()
Definition: ProcessedFile.php:492
‪TYPO3\CMS\Core\Resource\ProcessedFile\CONTEXT_IMAGECROPSCALEMASK
‪const CONTEXT_IMAGECROPSCALEMASK
Definition: ProcessedFile.php:61
‪TYPO3\CMS\Core\Resource\ProcessedFile\isIndexed
‪false isIndexed()
Definition: ProcessedFile.php:210
‪TYPO3\CMS\Core\Resource\ProcessedFile\getProperty
‪getProperty(string $key)
Definition: ProcessedFile.php:468
‪TYPO3\CMS\Core\Resource\ProcessedFile\$processingConfiguration
‪array $processingConfiguration
Definition: ProcessedFile.php:81
‪TYPO3\CMS\Core\Resource\ProcessedFile\calculateChecksum
‪string calculateChecksum()
Definition: ProcessedFile.php:160
‪TYPO3\CMS\Core\Resource\ProcessedFile\isUnchanged
‪bool isUnchanged()
Definition: ProcessedFile.php:407
‪TYPO3\CMS\Core\Resource\ProcessedFile\$originalFileSha1
‪string $originalFileSha1
Definition: ProcessedFile.php:95
‪TYPO3\CMS\Core\Resource\ProcessedFile\setName
‪setName($name)
Definition: ProcessedFile.php:252
‪TYPO3\CMS\Core\Resource\ProcessedFile\exists
‪bool exists()
Definition: ProcessedFile.php:273
‪TYPO3\CMS\Core\Resource\ProcessedFile\setUsesOriginalFile
‪setUsesOriginalFile()
Definition: ProcessedFile.php:415
‪TYPO3\CMS\Core\Resource\Processing\TaskTypeRegistry
Definition: TaskTypeRegistry.php:26
‪TYPO3\CMS\Core\Resource\ProcessedFile\$updated
‪bool $updated
Definition: ProcessedFile.php:102
‪TYPO3\CMS\Core\Resource\AbstractFile\properties
‪array< non-empty-string, function getProperties() { return $this-> properties
Definition: AbstractFile.php:134
‪TYPO3\CMS\Core\Resource\ProcessedFile\getProcessingConfiguration
‪array getProcessingConfiguration()
Definition: ProcessedFile.php:532
‪TYPO3\CMS\Core\Resource\ProcessedFile\generateProcessedFileNameWithoutExtension
‪string generateProcessedFileNameWithoutExtension()
Definition: ProcessedFile.php:566
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger(mixed $var)
Definition: MathUtility.php:69
‪TYPO3\CMS\Core\Resource\AbstractFile
Definition: AbstractFile.php:28
‪TYPO3\CMS\Core\Resource\File\getPublicUrl
‪getPublicUrl()
Definition: File.php:301
‪TYPO3\CMS\Core\Resource\File\getSha1
‪non empty string getSha1()
Definition: File.php:114
‪TYPO3\CMS\Core\Resource\ProcessedFile\getTaskIdentifier
‪string getTaskIdentifier()
Definition: ProcessedFile.php:542
‪TYPO3\CMS\Core\Resource\ProcessedFile\isProcessed
‪bool isProcessed()
Definition: ProcessedFile.php:291
‪TYPO3\CMS\Core\Resource\ProcessedFile\isUpdated
‪bool isUpdated()
Definition: ProcessedFile.php:242
‪TYPO3\CMS\Core\Resource\AbstractFile\getStorage
‪int< 0, getSize():int { if( $this->deleted) { throw new \RuntimeException( 'File has been deleted.', 1329821480);} if(empty( $this->properties[ 'size'])) { $fileInfo=$this-> getStorage() -> getFileInfoByIdentifier($this->getIdentifier(), ['size'])
‪TYPO3\CMS\Core\Resource\File
Definition: File.php:26
‪TYPO3\CMS\Core\Resource\AbstractFile\$name
‪string $name
Definition: AbstractFile.php:58
‪TYPO3\CMS\Core\Resource\ProcessedFile\$task
‪Processing TaskInterface $task
Definition: ProcessedFile.php:71
‪TYPO3\CMS\Core\Resource\ProcessedFile\getIdentifier
‪non empty string getIdentifier()
Definition: ProcessedFile.php:315
‪TYPO3\CMS\Core\Resource
Definition: generateMimeTypes.php:52
‪TYPO3\CMS\Core\Resource\ProcessedFile\updateWithLocalFile
‪updateWithLocalFile($filePath)
Definition: ProcessedFile.php:184
‪TYPO3\CMS\Core\Resource\ProcessedFile
Definition: ProcessedFile.php:47
‪TYPO3\CMS\Core\Resource\ProcessedFile\$originalFile
‪File $originalFile
Definition: ProcessedFile.php:87
‪TYPO3\CMS\Webhooks\Message\$url
‪identifier readonly UriInterface $url
Definition: LoginErrorOccurredMessage.php:36
‪TYPO3\CMS\Core\Resource\ProcessedFile\__construct
‪__construct(File $originalFile, $taskType, array $processingConfiguration, array $databaseRow=null)
Definition: ProcessedFile.php:117
‪TYPO3\CMS\Core\Resource\Service\ConfigurationService
Definition: ConfigurationService.php:34
‪TYPO3\CMS\Core\Resource\ProcessedFile\reconstituteFromDatabaseRecord
‪reconstituteFromDatabaseRecord(array $databaseRow)
Definition: ProcessedFile.php:133
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:24
‪TYPO3\CMS\Core\Resource\ProcessedFile\setContents
‪setContents(string $contents)
Definition: ProcessedFile.php:173
‪TYPO3\CMS\Core\Resource\ProcessedFile\isPersisted
‪bool isPersisted()
Definition: ProcessedFile.php:221
‪TYPO3\CMS\Core\Resource\AbstractFile\$storage
‪ResourceStorage null $storage
Definition: AbstractFile.php:44
‪TYPO3\CMS\Core\Resource\ProcessedFile\getName
‪non empty string getName()
Definition: ProcessedFile.php:329
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:51
‪TYPO3\CMS\Core\Resource\ProcessedFile\updateProcessingUrl
‪updateProcessingUrl(string $url)
Definition: ProcessedFile.php:424
‪TYPO3\CMS\Core\Resource\ProcessedFile\updateProperties
‪updateProperties(array $properties)
Definition: ProcessedFile.php:341
‪TYPO3\CMS\Core\Resource\AbstractFile\getIdentifier
‪getIdentifier()
Definition: AbstractFile.php:137
‪TYPO3\CMS\Core\Resource\ProcessedFile\getTask
‪getTask()
Definition: ProcessedFile.php:552
‪TYPO3\CMS\Core\Resource\ProcessedFile\$taskType
‪string $taskType
Definition: ProcessedFile.php:67
‪TYPO3\CMS\Core\Resource\ProcessedFile\isNew
‪bool isNew()
Definition: ProcessedFile.php:231
‪TYPO3\CMS\Core\Resource\ProcessedFile\isOutdated
‪bool isOutdated()
Definition: ProcessedFile.php:443
‪TYPO3\CMS\Core\Resource\ProcessedFile\$processingUrl
‪string $processingUrl
Definition: ProcessedFile.php:109