TYPO3CMS  8
 All Classes Namespaces Files Functions Variables Pages
ProcessedFile.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Core\Resource;
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 
19 
42 {
43  /*********************************************
44  * FILE PROCESSING CONTEXTS
45  *********************************************/
50  const CONTEXT_IMAGEPREVIEW = 'Image.Preview';
56  const CONTEXT_IMAGECROPSCALEMASK = 'Image.CropScaleMask';
57 
63  protected $taskType;
64 
68  protected $task;
69 
73  protected $taskTypeRegistry;
74 
81 
87  protected $originalFile;
88 
96  protected $originalFileSha1;
97 
104  protected $updated = false;
105 
115  public function __construct(File $originalFile, $taskType, array $processingConfiguration, array $databaseRow = null)
116  {
117  $this->originalFile = $originalFile;
118  $this->originalFileSha1 = $this->originalFile->getSha1();
119  $this->storage = $originalFile->getStorage()->getProcessingFolder()->getStorage();
120  $this->taskType = $taskType;
121  $this->processingConfiguration = $processingConfiguration;
122  if (is_array($databaseRow)) {
123  $this->reconstituteFromDatabaseRecord($databaseRow);
124  }
125  $this->taskTypeRegistry = GeneralUtility::makeInstance(Processing\TaskTypeRegistry::class);
126  }
127 
134  protected function reconstituteFromDatabaseRecord(array $databaseRow)
135  {
136  $this->taskType = $this->taskType ?: $databaseRow['task_type'];
137  $this->processingConfiguration = $this->processingConfiguration ?: unserialize($databaseRow['configuration']);
138 
139  $this->originalFileSha1 = $databaseRow['originalfilesha1'];
140  $this->identifier = $databaseRow['identifier'];
141  $this->name = $databaseRow['name'];
142  $this->properties = $databaseRow;
143  }
144 
145  /********************************
146  * VARIOUS FILE PROPERTY GETTERS
147  ********************************/
148 
154  // @todo replace these usages with direct calls to the task object
155  public function calculateChecksum()
156  {
157  return $this->getTask()->getConfigurationChecksum();
158  }
159 
160  /*******************
161  * CONTENTS RELATED
162  *******************/
170  public function setContents($contents)
171  {
172  throw new \BadMethodCallException('Setting contents not possible for processed file.', 1305438528);
173  }
174 
182  public function updateWithLocalFile($filePath)
183  {
184  if ($this->identifier === null) {
185  throw new \RuntimeException('Cannot update original file!', 1350582054);
186  }
187  $processingFolder = $this->originalFile->getStorage()->getProcessingFolder($this->originalFile);
188  $addedFile = $this->storage->updateProcessedFile($filePath, $this, $processingFolder);
189 
190  // Update some related properties
191  $this->identifier = $addedFile->getIdentifier();
192  $this->originalFileSha1 = $this->originalFile->getSha1();
193  if ($addedFile instanceof AbstractFile) {
194  $this->updateProperties($addedFile->getProperties());
195  }
196  $this->deleted = false;
197  $this->updated = true;
198  }
199 
200  /*****************************************
201  * STORAGE AND MANAGEMENT RELATED METHDOS
202  *****************************************/
208  public function isIndexed()
209  {
210  // Processed files are never indexed; instead you might be looking for isPersisted()
211  return false;
212  }
213 
219  public function isPersisted()
220  {
221  return is_array($this->properties) && array_key_exists('uid', $this->properties) && $this->properties['uid'] > 0;
222  }
223 
229  public function isNew()
230  {
231  return !$this->isPersisted();
232  }
233 
240  public function isUpdated()
241  {
242  return $this->updated;
243  }
244 
250  public function setName($name)
251  {
252  // Remove the existing file
253  if ($this->name !== $name && $this->name !== '' && $this->exists()) {
254  $this->delete();
255  }
256 
257  $this->name = $name;
258  // @todo this is a *weird* hack that will fail if the storage is non-hierarchical!
259  $this->identifier = $this->storage->getProcessingFolder($this->originalFile)->getIdentifier() . $this->name;
260 
261  $this->updated = true;
262  }
263 
264  /******************
265  * SPECIAL METHODS
266  ******************/
267 
273  public function isProcessed()
274  {
275  return $this->updated || ($this->isPersisted() && !$this->needsReprocessing());
276  }
277 
283  public function getOriginalFile()
284  {
285  return $this->originalFile;
286  }
287 
297  public function getIdentifier()
298  {
299  return (!$this->usesOriginalFile()) ? $this->identifier : $this->getOriginalFile()->getIdentifier();
300  }
301 
311  public function getName()
312  {
313  if ($this->usesOriginalFile()) {
314  return $this->originalFile->getName();
315  } else {
316  return $this->name;
317  }
318  }
319 
326  public function updateProperties(array $properties)
327  {
328  if (!is_array($this->properties)) {
329  $this->properties = [];
330  }
331 
332  if (array_key_exists('uid', $properties) && MathUtility::canBeInterpretedAsInteger($properties['uid'])) {
333  $this->properties['uid'] = $properties['uid'];
334  }
335 
336  // @todo we should have a blacklist of properties that might not be updated
337  $this->properties = array_merge($this->properties, $properties);
338 
339  // @todo when should this update be done?
340  if (!$this->isUnchanged() && $this->exists()) {
341  $this->properties = array_merge($this->properties, $this->storage->getFileInfo($this));
342  }
343  }
344 
350  public function toArray()
351  {
352  if ($this->usesOriginalFile()) {
353  $properties = $this->originalFile->getProperties();
354  unset($properties['uid']);
355  unset($properties['pid']);
356  unset($properties['identifier']);
357  unset($properties['name']);
358 
359  // Use width + height set in processed file
360  $properties['width'] = $this->properties['width'];
361  $properties['height'] = $this->properties['height'];
362  } else {
364  $properties['identifier'] = $this->getIdentifier();
365  $properties['name'] = $this->getName();
366  }
367 
368  $properties['configuration'] = serialize($this->processingConfiguration);
369 
370  return array_merge($properties, [
371  'storage' => $this->getStorage()->getUid(),
372  'checksum' => $this->calculateChecksum(),
373  'task_type' => $this->taskType,
374  'configurationsha1' => sha1($properties['configuration']),
375  'original' => $this->originalFile->getUid(),
376  'originalfilesha1' => $this->originalFileSha1
377  ]);
378  }
379 
385  protected function isUnchanged()
386  {
387  return !$this->properties['width'] && $this->usesOriginalFile();
388  }
389 
393  public function setUsesOriginalFile()
394  {
395  // @todo check if some of these properties can/should be set in a generic update method
396  $this->identifier = $this->originalFile->getIdentifier();
397  $this->updated = true;
398  $this->originalFileSha1 = $this->originalFile->getSha1();
399  }
400 
404  public function usesOriginalFile()
405  {
406  return $this->identifier == null || $this->identifier === $this->originalFile->getIdentifier();
407  }
408 
414  public function isOutdated()
415  {
416  return $this->needsReprocessing();
417  }
418 
425  public function delete($force = false)
426  {
427  if (!$force && $this->isUnchanged()) {
428  return false;
429  }
430  // Only delete file when original isn't used
431  if (!$this->usesOriginalFile()) {
432  return parent::delete();
433  } else {
434  return true;
435  }
436  }
437 
445  public function getProperty($key)
446  {
447  // The uid always (!) has to come from this file and never the original file (see getOriginalFile() to get this)
448  if ($this->isUnchanged() && $key !== 'uid') {
449  return $this->originalFile->getProperty($key);
450  } else {
451  return $this->properties[$key];
452  }
453  }
454 
460  public function getUid()
461  {
462  return $this->properties['uid'];
463  }
464 
470  public function needsReprocessing()
471  {
472  $fileMustBeRecreated = false;
473 
474  // if original is missing we can not reprocess the file
475  if ($this->originalFile->isMissing()) {
476  return false;
477  }
478 
479  // processedFile does not exist
480  if (!$this->usesOriginalFile() && !$this->exists()) {
481  $fileMustBeRecreated = true;
482  }
483 
484  // hash does not match
485  if (array_key_exists('checksum', $this->properties) && $this->calculateChecksum() !== $this->properties['checksum']) {
486  $fileMustBeRecreated = true;
487  }
488 
489  // original file changed
490  if ($this->originalFile->getSha1() !== $this->originalFileSha1) {
491  $fileMustBeRecreated = true;
492  }
493 
494  if (!array_key_exists('uid', $this->properties)) {
495  $fileMustBeRecreated = true;
496  }
497 
498  // remove outdated file
499  if ($fileMustBeRecreated && $this->exists()) {
500  $this->delete();
501  }
502  return $fileMustBeRecreated;
503  }
504 
510  public function getProcessingConfiguration()
511  {
513  }
514 
520  public function getTaskIdentifier()
521  {
522  return $this->taskType;
523  }
524 
531  public function getTask()
532  {
533  if ($this->task == null) {
534  $this->task = $this->taskTypeRegistry->getTaskForType($this->taskType, $this, $this->processingConfiguration);
535  }
536 
537  return $this->task;
538  }
539 
546  {
547  $name = $this->originalFile->getNameWithoutExtension();
548  $name .= '_' . $this->originalFile->getUid();
549  $name .= '_' . $this->calculateChecksum();
550 
551  return $name;
552  }
553 
560  public function getPublicUrl($relativeToCurrentScript = false)
561  {
562  if ($this->deleted) {
563  return null;
564  } elseif ($this->usesOriginalFile()) {
565  return $this->getOriginalFile()->getPublicUrl($relativeToCurrentScript);
566  } else {
567  return $this->getStorage()->getPublicUrl($this, $relativeToCurrentScript);
568  }
569  }
570 }
getPublicUrl($relativeToCurrentScript=false)
__construct(File $originalFile, $taskType, array $processingConfiguration, array $databaseRow=null)
reconstituteFromDatabaseRecord(array $databaseRow)
static makeInstance($className,...$constructorArguments)