‪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 
66  protected string ‪$taskType;
67 
70 
74  protected ?array ‪$processingConfiguration;
75 
80 
86  protected ?string ‪$originalFileSha1;
87 
92  protected bool ‪$updated = false;
93 
98  protected string ‪$processingUrl;
99 
104  public function ‪__construct(‪File ‪$originalFile, string ‪$taskType, array ‪$processingConfiguration, array $databaseRow = null)
105  {
106  $this->originalFile = ‪$originalFile;
107  $this->originalFileSha1 = $this->originalFile->‪getSha1();
108  $this->storage = ‪$originalFile->‪getStorage()->getProcessingFolder()->‪getStorage();
109  $this->taskType = ‪$taskType;
110  $this->processingConfiguration = ‪$processingConfiguration;
111  if (is_array($databaseRow)) {
112  $this->‪reconstituteFromDatabaseRecord($databaseRow);
113  }
114  $this->taskTypeRegistry = GeneralUtility::makeInstance(TaskTypeRegistry::class);
115  }
116 
120  protected function ‪reconstituteFromDatabaseRecord(array $databaseRow): void
121  {
122  $this->taskType = $this->taskType ?: $databaseRow['task_type'];
123  // @todo In case the original configuration contained file objects the reconstitution fails. See ConfigurationService->serialize()
124  $this->processingConfiguration = $this->processingConfiguration ?: (array)unserialize($databaseRow['configuration'] ?? '');
125 
126  $this->originalFileSha1 = $databaseRow['originalfilesha1'];
127  $this->identifier = $databaseRow['identifier'];
128  $this->name = $databaseRow['name'];
129  $this->‪properties = $databaseRow;
130  $this->processingUrl = $databaseRow['processing_url'] ?? '';
131 
132  if (!empty($databaseRow['storage']) && (int)$this->storage->getUid() !== (int)$databaseRow['storage']) {
133  $this->storage = GeneralUtility::makeInstance(StorageRepository::class)->findByUid($databaseRow['storage']);
134  }
135  }
136 
137  /********************************
138  * VARIOUS FILE PROPERTY GETTERS
139  ********************************/
140 
144  protected function ‪calculateChecksum(): string
145  {
146  return $this->‪getTask()->getConfigurationChecksum();
147  }
148 
149  /*******************
150  * CONTENTS RELATED
151  *******************/
157  public function ‪setContents(string $contents): self
158  {
159  throw new \BadMethodCallException('Setting contents not possible for processed file.', 1305438528);
160  }
161 
168  public function ‪updateWithLocalFile(string $filePath): void
169  {
170  if (empty($this->identifier)) {
171  throw new \RuntimeException('Cannot update original file!', 1350582054);
172  }
173  $processingFolder = $this->originalFile->getStorage()->getProcessingFolder($this->originalFile);
174  $addedFile = $this->storage->updateProcessedFile($filePath, $this, $processingFolder);
175 
176  // Update some related properties
177  $this->identifier = $addedFile->getIdentifier();
178  $this->originalFileSha1 = $this->originalFile->getSha1();
179  if ($addedFile instanceof ‪AbstractFile) {
180  $this->‪updateProperties($addedFile->getProperties());
181  }
182  $this->deleted = false;
183  $this->updated = true;
184  }
185 
186  /*****************************************
187  * STORAGE AND MANAGEMENT RELATED METHODS
188  *****************************************/
194  public function ‪isIndexed(): bool
195  {
196  // Processed files are never indexed; instead you might be looking for isPersisted()
197  return false;
198  }
199 
203  public function ‪isPersisted(): bool
204  {
205  return array_key_exists('uid', $this->‪properties) && $this->‪properties['uid'] > 0;
206  }
207 
211  public function ‪isNew(): bool
212  {
213  return !$this->‪isPersisted();
214  }
215 
220  public function ‪isUpdated(): bool
221  {
222  return ‪$this->updated;
223  }
224 
228  public function ‪setName(string ‪$name): void
229  {
230  // Remove the existing file, but only we actually have a name or the name has changed
231  if (!empty($this->name) && $this->name !== ‪$name && $this->‪exists()) {
232  $this->delete();
233  }
234 
235  $this->name = ‪$name;
236  // @todo this is a *weird* hack that will fail if the storage is non-hierarchical!
237  $this->identifier = $this->storage->getProcessingFolder($this->originalFile)->getIdentifier() . ‪$this->name;
238 
239  $this->updated = true;
240  }
241 
249  public function ‪exists(): bool
250  {
251  if ($this->‪usesOriginalFile()) {
252  return $this->originalFile->exists();
253  }
254 
255  return parent::exists();
256  }
257 
258  /******************
259  * SPECIAL METHODS
260  ******************/
261 
265  public function ‪isProcessed(): bool
266  {
267  return $this->updated || ($this->‪isPersisted() && !$this->‪needsReprocessing());
268  }
269 
273  public function ‪getOriginalFile(): ‪File
274  {
275  return ‪$this->originalFile;
276  }
277 
287  public function ‪getIdentifier(): string
288  {
289  return (!$this->‪usesOriginalFile()) ? $this->identifier : $this->‪getOriginalFile()->getIdentifier();
290  }
291 
301  public function ‪getName(): string
302  {
303  if ($this->‪usesOriginalFile()) {
304  return $this->originalFile->getName();
305  }
306  return ‪$this->name;
307  }
308 
313  public function ‪updateProperties(array ‪$properties): void
314  {
315  if (array_key_exists('uid', ‪$properties) && ‪MathUtility::canBeInterpretedAsInteger(‪$properties['uid'])) {
316  $this->‪properties['uid'] = $properties['uid'];
317  }
318  if (isset(‪$properties['processing_url'])) {
319  $this->processingUrl = ‪$properties['processing_url'];
320  }
321 
322  // @todo we should have a blacklist of properties that might not be updated
323  $this->‪properties = array_merge($this->‪properties, $properties);
324 
325  // @todo when should this update be done?
326  if (!$this->‪isUnchanged() && $this->‪exists()) {
328  if ($this->‪usesOriginalFile()) {
329  ‪$storage = $this->originalFile->getStorage();
330  }
331  $this->‪properties = array_merge($this->‪properties, ‪$storage->‪getFileInfo($this));
332  }
333  }
334 
340  public function toArray(): array
341  {
342  if ($this->‪usesOriginalFile()) {
343  ‪$properties = $this->originalFile->getProperties();
344  unset(‪$properties['uid']);
345  ‪$properties['identifier'] = '';
346  ‪$properties['name'] = null;
347  ‪$properties['processing_url'] = '';
348 
349  // Use width + height set in processed file
350  ‪$properties['width'] = $this->‪properties['width'] ?? 0;
351  ‪$properties['height'] = $this->‪properties['height'] ?? 0;
352  } else {
354  ‪$properties['identifier'] = $this->‪getIdentifier();
355  ‪$properties['name'] = $this->‪getName();
356  }
357 
358  ‪$properties['configuration'] = (new ConfigurationService())->serialize($this->processingConfiguration);
359 
360  return array_merge(‪$properties, [
361  'storage' => $this->‪getStorage()->‪getUid(),
362  'checksum' => $this->‪calculateChecksum(),
363  'task_type' => $this->taskType,
364  'configurationsha1' => sha1(‪$properties['configuration']),
365  'original' => $this->originalFile->getUid(),
366  'originalfilesha1' => $this->originalFileSha1,
367  ]);
368  }
369 
373  protected function ‪isUnchanged(): bool
374  {
375  return !($this->‪properties['width'] ?? false) && $this->‪usesOriginalFile();
376  }
377 
381  public function ‪setUsesOriginalFile(): void
382  {
383  // @todo check if some of these properties can/should be set in a generic update method
384  $this->identifier = $this->originalFile->getIdentifier();
385  $this->updated = true;
386  $this->processingUrl = '';
387  $this->originalFileSha1 = $this->originalFile->getSha1();
388  }
389 
390  public function ‪updateProcessingUrl(string ‪$url): void
391  {
392  $this->updated = true;
393  $this->processingUrl = ‪$url;
394  }
395 
396  public function ‪usesOriginalFile(): bool
397  {
398  return empty($this->identifier) || $this->identifier === $this->originalFile->getIdentifier();
399  }
400 
404  public function ‪isOutdated(): bool
405  {
406  return $this->‪needsReprocessing();
407  }
408 
412  public function delete(bool $force = false): bool
413  {
414  if (!$force && $this->‪isUnchanged()) {
415  return false;
416  }
417  // Only delete file when original isn't used
418  if (!$this->‪usesOriginalFile()) {
419  return parent::delete();
420  }
421  return true;
422  }
423 
429  public function ‪getProperty(string $key): mixed
430  {
431  // The uid always (!) has to come from this file and never the original file (see getOriginalFile() to get this)
432  if ($this->‪isUnchanged() && $key !== 'uid') {
433  return $this->originalFile->getProperty($key);
434  }
435  return $this->‪properties[$key] ?? null;
436  }
437 
441  public function ‪getUid(): int
442  {
443  return (int)($this->‪properties['uid'] ?? 0);
444  }
445 
449  public function ‪needsReprocessing(): bool
450  {
451  $fileMustBeRecreated = false;
452 
453  // if original is missing we can not reprocess the file
454  if ($this->originalFile->isMissing()) {
455  return false;
456  }
457 
458  // processedFile does not exist
459  if (!$this->‪usesOriginalFile() && !$this->‪exists()) {
460  $fileMustBeRecreated = true;
461  }
462 
463  // hash does not match
464  if (array_key_exists('checksum', $this->‪properties) && $this->‪calculateChecksum() !== $this->‪properties['checksum']) {
465  $fileMustBeRecreated = true;
466  }
467 
468  // original file changed
469  if ($this->originalFile->getSha1() !== $this->originalFileSha1) {
470  $fileMustBeRecreated = true;
471  }
472 
473  if (!array_key_exists('uid', $this->‪properties)) {
474  $fileMustBeRecreated = true;
475  }
476 
477  // remove outdated file
478  if ($fileMustBeRecreated && $this->‪exists()) {
479  $this->delete();
480  }
481  return $fileMustBeRecreated;
482  }
483 
487  public function ‪getProcessingConfiguration(): array
488  {
490  }
491 
495  public function ‪getTaskIdentifier(): string
496  {
497  return ‪$this->taskType;
498  }
499 
503  public function ‪getTask(): Processing\‪TaskInterface
504  {
505  if ($this->task === null) {
506  $this->task = $this->taskTypeRegistry->getTaskForType($this->taskType, $this, $this->processingConfiguration);
507  }
508 
509  return ‪$this->task;
510  }
511 
516  {
517  ‪$name = $this->originalFile->getNameWithoutExtension();
518  ‪$name .= '_' . $this->originalFile->getUid();
519  ‪$name .= '_' . $this->‪calculateChecksum();
520 
521  return ‪$name;
522  }
523 
529  public function ‪getPublicUrl(): ?string
530  {
531  if (isset($this->processingUrl) && $this->processingUrl !== '') {
533  }
534  if ($this->deleted) {
535  return null;
536  }
537  if ($this->‪usesOriginalFile()) {
538  return $this->‪getOriginalFile()->getPublicUrl();
539  }
540  return $this->‪getStorage()->‪getPublicUrl($this);
541  }
542 }
‪TYPO3\CMS\Core\Resource\ProcessedFile\$taskTypeRegistry
‪Processing TaskTypeRegistry $taskTypeRegistry
Definition: ProcessedFile.php:69
‪TYPO3\CMS\Core\Resource\ProcessedFile\CONTEXT_IMAGEPREVIEW
‪const CONTEXT_IMAGEPREVIEW
Definition: ProcessedFile.php:55
‪TYPO3\CMS\Core\Resource\ProcessedFile\$properties
‪$properties['identifier']
Definition: ProcessedFile.php:345
‪TYPO3\CMS\Core\Resource\ResourceStorage\getFileInfo
‪array getFileInfo(FileInterface $fileObject)
Definition: ResourceStorage.php:1473
‪TYPO3\CMS\Core\Resource\ProcessedFile\isOutdated
‪isOutdated()
Definition: ProcessedFile.php:404
‪TYPO3\CMS\Core\Resource\ProcessedFile\getPublicUrl
‪non empty string null getPublicUrl()
Definition: ProcessedFile.php:529
‪TYPO3\CMS\Core\Resource\ProcessedFile\getProcessingConfiguration
‪getProcessingConfiguration()
Definition: ProcessedFile.php:487
‪TYPO3\CMS\Core\Resource\Processing\TaskInterface
Definition: TaskInterface.php:34
‪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:194
‪TYPO3\CMS\Core\Resource\ProcessedFile\getProperty
‪getProperty(string $key)
Definition: ProcessedFile.php:429
‪TYPO3\CMS\Core\Resource\ProcessedFile\$processingConfiguration
‪array $processingConfiguration
Definition: ProcessedFile.php:74
‪TYPO3\CMS\Core\Resource\ProcessedFile\isNew
‪isNew()
Definition: ProcessedFile.php:211
‪TYPO3\CMS\Core\Resource\ProcessedFile\$originalFileSha1
‪string $originalFileSha1
Definition: ProcessedFile.php:86
‪TYPO3\CMS\Core\Resource\ProcessedFile\setName
‪setName(string $name)
Definition: ProcessedFile.php:228
‪TYPO3\CMS\Core\Resource\ProcessedFile\exists
‪bool exists()
Definition: ProcessedFile.php:249
‪TYPO3\CMS\Core\Resource\ProcessedFile\setUsesOriginalFile
‪setUsesOriginalFile()
Definition: ProcessedFile.php:381
‪TYPO3\CMS\Core\Resource\Processing\TaskTypeRegistry
Definition: TaskTypeRegistry.php:26
‪TYPO3\CMS\Core\Resource\ProcessedFile\isUnchanged
‪isUnchanged()
Definition: ProcessedFile.php:373
‪TYPO3\CMS\Core\Resource\ProcessedFile\$updated
‪bool $updated
Definition: ProcessedFile.php:92
‪TYPO3\CMS\Core\Resource\AbstractFile\properties
‪array< non-empty-string, function getProperties() { return $this-> properties
Definition: AbstractFile.php:141
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger(mixed $var)
Definition: MathUtility.php:69
‪TYPO3\CMS\Core\Resource\ProcessedFile\getUid
‪getUid()
Definition: ProcessedFile.php:441
‪TYPO3\CMS\Core\Resource\ProcessedFile\isUpdated
‪isUpdated()
Definition: ProcessedFile.php:220
‪TYPO3\CMS\Core\Resource\AbstractFile
Definition: AbstractFile.php:29
‪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\calculateChecksum
‪calculateChecksum()
Definition: ProcessedFile.php:144
‪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\ProcessedFile\isPersisted
‪isPersisted()
Definition: ProcessedFile.php:203
‪TYPO3\CMS\Core\Resource\ProcessedFile\__construct
‪__construct(File $originalFile, string $taskType, array $processingConfiguration, array $databaseRow=null)
Definition: ProcessedFile.php:104
‪TYPO3\CMS\Core\Resource\AbstractFile\$name
‪string $name
Definition: AbstractFile.php:59
‪TYPO3\CMS\Core\Resource\ProcessedFile\$task
‪Processing TaskInterface $task
Definition: ProcessedFile.php:68
‪TYPO3\CMS\Core\Resource\ProcessedFile\usesOriginalFile
‪usesOriginalFile()
Definition: ProcessedFile.php:396
‪TYPO3\CMS\Core\Resource\ProcessedFile\isProcessed
‪isProcessed()
Definition: ProcessedFile.php:265
‪TYPO3\CMS\Core\Resource\ProcessedFile\getIdentifier
‪non empty string getIdentifier()
Definition: ProcessedFile.php:287
‪TYPO3\CMS\Core\Resource\ProcessedFile\updateWithLocalFile
‪updateWithLocalFile(string $filePath)
Definition: ProcessedFile.php:168
‪TYPO3\CMS\Core\Resource
Definition: generateMimeTypes.php:52
‪TYPO3\CMS\Core\Resource\ProcessedFile
Definition: ProcessedFile.php:47
‪TYPO3\CMS\Core\Resource\ProcessedFile\needsReprocessing
‪needsReprocessing()
Definition: ProcessedFile.php:449
‪TYPO3\CMS\Core\Resource\ProcessedFile\$originalFile
‪File $originalFile
Definition: ProcessedFile.php:79
‪TYPO3\CMS\Webhooks\Message\$url
‪identifier readonly UriInterface $url
Definition: LoginErrorOccurredMessage.php:36
‪TYPO3\CMS\Core\Resource\Service\ConfigurationService
Definition: ConfigurationService.php:34
‪TYPO3\CMS\Core\Resource\ProcessedFile\getOriginalFile
‪getOriginalFile()
Definition: ProcessedFile.php:273
‪TYPO3\CMS\Core\Resource\ProcessedFile\reconstituteFromDatabaseRecord
‪reconstituteFromDatabaseRecord(array $databaseRow)
Definition: ProcessedFile.php:120
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:24
‪TYPO3\CMS\Core\Resource\ProcessedFile\setContents
‪setContents(string $contents)
Definition: ProcessedFile.php:157
‪TYPO3\CMS\Core\Resource\ProcessedFile\generateProcessedFileNameWithoutExtension
‪generateProcessedFileNameWithoutExtension()
Definition: ProcessedFile.php:515
‪TYPO3\CMS\Core\Resource\AbstractFile\$storage
‪ResourceStorage null $storage
Definition: AbstractFile.php:45
‪TYPO3\CMS\Core\Resource\ProcessedFile\getName
‪non empty string getName()
Definition: ProcessedFile.php:301
‪TYPO3\CMS\Core\Resource\ProcessedFile\getTaskIdentifier
‪getTaskIdentifier()
Definition: ProcessedFile.php:495
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\Resource\ProcessedFile\updateProcessingUrl
‪updateProcessingUrl(string $url)
Definition: ProcessedFile.php:390
‪TYPO3\CMS\Core\Resource\ProcessedFile\updateProperties
‪updateProperties(array $properties)
Definition: ProcessedFile.php:313
‪TYPO3\CMS\Core\Resource\ProcessedFile\getTask
‪getTask()
Definition: ProcessedFile.php:503
‪TYPO3\CMS\Core\Resource\ProcessedFile\$taskType
‪string $taskType
Definition: ProcessedFile.php:66
‪TYPO3\CMS\Core\Resource\ProcessedFile\$processingUrl
‪string $processingUrl
Definition: ProcessedFile.php:98