TYPO3 CMS  TYPO3_8-7
ProcessedFile.php
Go to the documentation of this file.
1 <?php
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 
133  protected function reconstituteFromDatabaseRecord(array $databaseRow)
134  {
135  $this->taskType = $this->taskType ?: $databaseRow['task_type'];
136  $this->processingConfiguration = $this->processingConfiguration ?: unserialize($databaseRow['configuration']);
137 
138  $this->originalFileSha1 = $databaseRow['originalfilesha1'];
139  $this->identifier = $databaseRow['identifier'];
140  $this->name = $databaseRow['name'];
141  $this->properties = $databaseRow;
142 
143  if (!empty($databaseRow['storage']) && (int)$this->storage->getUid() !== (int)$databaseRow['storage']) {
144  $this->storage = ResourceFactory::getInstance()->getStorageObject($databaseRow['storage']);
145  }
146  }
147 
148  /********************************
149  * VARIOUS FILE PROPERTY GETTERS
150  ********************************/
151 
157  // @todo replace these usages with direct calls to the task object
158  public function calculateChecksum()
159  {
160  return $this->getTask()->getConfigurationChecksum();
161  }
162 
163  /*******************
164  * CONTENTS RELATED
165  *******************/
173  public function setContents($contents)
174  {
175  throw new \BadMethodCallException('Setting contents not possible for processed file.', 1305438528);
176  }
177 
184  public function updateWithLocalFile($filePath)
185  {
186  if ($this->identifier === null) {
187  throw new \RuntimeException('Cannot update original file!', 1350582054);
188  }
189  $processingFolder = $this->originalFile->getStorage()->getProcessingFolder($this->originalFile);
190  $addedFile = $this->storage->updateProcessedFile($filePath, $this, $processingFolder);
191 
192  // Update some related properties
193  $this->identifier = $addedFile->getIdentifier();
194  $this->originalFileSha1 = $this->originalFile->getSha1();
195  if ($addedFile instanceof AbstractFile) {
196  $this->updateProperties($addedFile->getProperties());
197  }
198  $this->deleted = false;
199  $this->updated = true;
200  }
201 
202  /*****************************************
203  * STORAGE AND MANAGEMENT RELATED METHDOS
204  *****************************************/
210  public function isIndexed()
211  {
212  // Processed files are never indexed; instead you might be looking for isPersisted()
213  return false;
214  }
215 
221  public function isPersisted()
222  {
223  return is_array($this->properties) && array_key_exists('uid', $this->properties) && $this->properties['uid'] > 0;
224  }
225 
231  public function isNew()
232  {
233  return !$this->isPersisted();
234  }
235 
242  public function isUpdated()
243  {
244  return $this->updated;
245  }
246 
252  public function setName($name)
253  {
254  // Remove the existing file, but only we actually have a name or the name has changed
255  if (!empty($this->name) && $this->name !== $name && $this->exists()) {
256  $this->delete();
257  }
258 
259  $this->name = $name;
260  // @todo this is a *weird* hack that will fail if the storage is non-hierarchical!
261  $this->identifier = $this->storage->getProcessingFolder($this->originalFile)->getIdentifier() . $this->name;
262 
263  $this->updated = true;
264  }
265 
273  public function exists()
274  {
275  if ($this->usesOriginalFile()) {
276  return $this->originalFile->exists();
277  }
278 
279  return parent::exists();
280  }
281 
282  /******************
283  * SPECIAL METHODS
284  ******************/
285 
291  public function isProcessed()
292  {
293  return $this->updated || ($this->isPersisted() && !$this->needsReprocessing());
294  }
295 
301  public function getOriginalFile()
302  {
303  return $this->originalFile;
304  }
305 
315  public function getIdentifier()
316  {
317  return (!$this->usesOriginalFile()) ? $this->identifier : $this->getOriginalFile()->getIdentifier();
318  }
319 
329  public function getName()
330  {
331  if ($this->usesOriginalFile()) {
332  return $this->originalFile->getName();
333  }
334  return $this->name;
335  }
336 
343  public function updateProperties(array $properties)
344  {
345  if (!is_array($this->properties)) {
346  $this->properties = [];
347  }
348 
349  if (array_key_exists('uid', $properties) && MathUtility::canBeInterpretedAsInteger($properties['uid'])) {
350  $this->properties['uid'] = $properties['uid'];
351  }
352 
353  // @todo we should have a blacklist of properties that might not be updated
354  $this->properties = array_merge($this->properties, $properties);
355 
356  // @todo when should this update be done?
357  if (!$this->isUnchanged() && $this->exists()) {
358  $this->properties = array_merge($this->properties, $this->storage->getFileInfo($this));
359  }
360  }
361 
367  public function toArray()
368  {
369  if ($this->usesOriginalFile()) {
370  $properties = $this->originalFile->getProperties();
371  unset($properties['uid']);
372  unset($properties['pid']);
373  unset($properties['identifier']);
374  unset($properties['name']);
375 
376  // Use width + height set in processed file
377  $properties['width'] = $this->properties['width'];
378  $properties['height'] = $this->properties['height'];
379  } else {
381  $properties['identifier'] = $this->getIdentifier();
382  $properties['name'] = $this->getName();
383  }
384 
385  $properties['configuration'] = serialize($this->processingConfiguration);
386 
387  return array_merge($properties, [
388  'storage' => $this->getStorage()->getUid(),
389  'checksum' => $this->calculateChecksum(),
390  'task_type' => $this->taskType,
391  'configurationsha1' => sha1($properties['configuration']),
392  'original' => $this->originalFile->getUid(),
393  'originalfilesha1' => $this->originalFileSha1
394  ]);
395  }
396 
402  protected function isUnchanged()
403  {
404  return !$this->properties['width'] && $this->usesOriginalFile();
405  }
406 
410  public function setUsesOriginalFile()
411  {
412  // @todo check if some of these properties can/should be set in a generic update method
413  $this->identifier = $this->originalFile->getIdentifier();
414  $this->updated = true;
415  $this->originalFileSha1 = $this->originalFile->getSha1();
416  }
417 
421  public function usesOriginalFile()
422  {
423  return $this->identifier === null || $this->identifier === $this->originalFile->getIdentifier();
424  }
425 
431  public function isOutdated()
432  {
433  return $this->needsReprocessing();
434  }
435 
442  public function delete($force = false)
443  {
444  if (!$force && $this->isUnchanged()) {
445  return false;
446  }
447  // Only delete file when original isn't used
448  if (!$this->usesOriginalFile()) {
449  return parent::delete();
450  }
451  return true;
452  }
453 
461  public function getProperty($key)
462  {
463  // The uid always (!) has to come from this file and never the original file (see getOriginalFile() to get this)
464  if ($this->isUnchanged() && $key !== 'uid') {
465  return $this->originalFile->getProperty($key);
466  }
467  return $this->properties[$key];
468  }
469 
475  public function getUid()
476  {
477  return $this->properties['uid'];
478  }
479 
485  public function needsReprocessing()
486  {
487  $fileMustBeRecreated = false;
488 
489  // if original is missing we can not reprocess the file
490  if ($this->originalFile->isMissing()) {
491  return false;
492  }
493 
494  // processedFile does not exist
495  if (!$this->usesOriginalFile() && !$this->exists()) {
496  $fileMustBeRecreated = true;
497  }
498 
499  // hash does not match
500  if (array_key_exists('checksum', $this->properties) && $this->calculateChecksum() !== $this->properties['checksum']) {
501  $fileMustBeRecreated = true;
502  }
503 
504  // original file changed
505  if ($this->originalFile->getSha1() !== $this->originalFileSha1) {
506  $fileMustBeRecreated = true;
507  }
508 
509  if (!array_key_exists('uid', $this->properties)) {
510  $fileMustBeRecreated = true;
511  }
512 
513  // remove outdated file
514  if ($fileMustBeRecreated && $this->exists()) {
515  $this->delete();
516  }
517  return $fileMustBeRecreated;
518  }
519 
525  public function getProcessingConfiguration()
526  {
528  }
529 
535  public function getTaskIdentifier()
536  {
537  return $this->taskType;
538  }
539 
546  public function getTask()
547  {
548  if ($this->task == null) {
549  $this->task = $this->taskTypeRegistry->getTaskForType($this->taskType, $this, $this->processingConfiguration);
550  }
551 
552  return $this->task;
553  }
554 
561  {
562  $name = $this->originalFile->getNameWithoutExtension();
563  $name .= '_' . $this->originalFile->getUid();
564  $name .= '_' . $this->calculateChecksum();
565 
566  return $name;
567  }
568 
575  public function getPublicUrl($relativeToCurrentScript = false)
576  {
577  if ($this->deleted) {
578  return null;
579  }
580  if ($this->usesOriginalFile()) {
581  return $this->getOriginalFile()->getPublicUrl($relativeToCurrentScript);
582  }
583  return $this->getStorage()->getPublicUrl($this, $relativeToCurrentScript);
584  }
585 }
reconstituteFromDatabaseRecord(array $databaseRow)
static makeInstance($className,... $constructorArguments)
getPublicUrl($relativeToCurrentScript=false)
__construct(File $originalFile, $taskType, array $processingConfiguration, array $databaseRow=null)