TYPO3  7.6
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 
39 {
40  /*********************************************
41  * FILE PROCESSING CONTEXTS
42  *********************************************/
47  const CONTEXT_IMAGEPREVIEW = 'Image.Preview';
53  const CONTEXT_IMAGECROPSCALEMASK = 'Image.CropScaleMask';
54 
60  protected $taskType;
61 
65  protected $task;
66 
70  protected $taskTypeRegistry;
71 
78 
84  protected $originalFile;
85 
93  protected $originalFileSha1;
94 
101  protected $updated = false;
102 
112  public function __construct(File $originalFile, $taskType, array $processingConfiguration, array $databaseRow = null)
113  {
114  $this->originalFile = $originalFile;
115  $this->originalFileSha1 = $this->originalFile->getSha1();
116  $this->storage = $originalFile->getStorage()->getProcessingFolder()->getStorage();
117  $this->taskType = $taskType;
118  $this->processingConfiguration = $processingConfiguration;
119  if (is_array($databaseRow)) {
120  $this->reconstituteFromDatabaseRecord($databaseRow);
121  }
122  $this->taskTypeRegistry = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Resource\Processing\TaskTypeRegistry::class);
123  }
124 
131  protected function reconstituteFromDatabaseRecord(array $databaseRow)
132  {
133  $this->taskType = $this->taskType ?: $databaseRow['task_type'];
134  $this->processingConfiguration = $this->processingConfiguration ?: unserialize($databaseRow['configuration']);
135 
136  $this->originalFileSha1 = $databaseRow['originalfilesha1'];
137  $this->identifier = $databaseRow['identifier'];
138  $this->name = $databaseRow['name'];
139  $this->properties = $databaseRow;
140  }
141 
142  /********************************
143  * VARIOUS FILE PROPERTY GETTERS
144  ********************************/
145 
151  // @todo replace these usages with direct calls to the task object
152  public function calculateChecksum()
153  {
154  return $this->getTask()->getConfigurationChecksum();
155  }
156 
157  /*******************
158  * CONTENTS RELATED
159  *******************/
167  public function setContents($contents)
168  {
169  throw new \BadMethodCallException('Setting contents not possible for processed file.', 1305438528);
170  }
171 
179  public function updateWithLocalFile($filePath)
180  {
181  if ($this->identifier === null) {
182  throw new \RuntimeException('Cannot update original file!', 1350582054);
183  }
184  $processingFolder = $this->originalFile->getStorage()->getProcessingFolder();
185  $addedFile = $this->storage->updateProcessedFile($filePath, $this, $processingFolder);
186 
187  // Update some related properties
188  $this->identifier = $addedFile->getIdentifier();
189  $this->originalFileSha1 = $this->originalFile->getSha1();
190  $this->updateProperties($addedFile->getProperties());
191  $this->deleted = false;
192  $this->updated = true;
193  }
194 
195  /*****************************************
196  * STORAGE AND MANAGEMENT RELATED METHDOS
197  *****************************************/
203  public function isIndexed()
204  {
205  // Processed files are never indexed; instead you might be looking for isPersisted()
206  return false;
207  }
208 
214  public function isPersisted()
215  {
216  return is_array($this->properties) && array_key_exists('uid', $this->properties) && $this->properties['uid'] > 0;
217  }
218 
224  public function isNew()
225  {
226  return !$this->isPersisted();
227  }
228 
235  public function isUpdated()
236  {
237  return $this->updated;
238  }
239 
245  public function setName($name)
246  {
247  // Remove the existing file
248  if ($this->name !== $name && $this->name !== '' && $this->exists()) {
249  $this->delete();
250  }
251 
252  $this->name = $name;
253  // @todo this is a *weird* hack that will fail if the storage is non-hierarchical!
254  $this->identifier = $this->storage->getProcessingFolder()->getIdentifier() . $this->name;
255 
256  $this->updated = true;
257  }
258 
259  /******************
260  * SPECIAL METHODS
261  ******************/
262 
268  public function isProcessed()
269  {
270  return $this->updated || ($this->isPersisted() && !$this->needsReprocessing());
271  }
272 
278  public function getOriginalFile()
279  {
280  return $this->originalFile;
281  }
282 
292  public function getIdentifier()
293  {
294  return (!$this->usesOriginalFile()) ? $this->identifier : $this->getOriginalFile()->getIdentifier();
295  }
296 
306  public function getName()
307  {
308  if ($this->usesOriginalFile()) {
309  return $this->originalFile->getName();
310  } else {
311  return $this->name;
312  }
313  }
314 
321  public function updateProperties(array $properties)
322  {
323  if (!is_array($this->properties)) {
324  $this->properties = array();
325  }
326 
327  if (array_key_exists('uid', $properties) && \TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($properties['uid'])) {
328  $this->properties['uid'] = $properties['uid'];
329  }
330 
331  // @todo we should have a blacklist of properties that might not be updated
332  $this->properties = array_merge($this->properties, $properties);
333 
334  // @todo when should this update be done?
335  if (!$this->isUnchanged() && $this->exists()) {
336  $this->properties = array_merge($this->properties, $this->storage->getFileInfo($this));
337  }
338  }
339 
345  public function toArray()
346  {
347  if ($this->usesOriginalFile()) {
348  $properties = $this->originalFile->getProperties();
349  unset($properties['uid']);
350  unset($properties['pid']);
351  unset($properties['identifier']);
352  unset($properties['name']);
353 
354  // Use width + height set in processed file
355  $properties['width'] = $this->properties['width'];
356  $properties['height'] = $this->properties['height'];
357  } else {
359  $properties['identifier'] = $this->getIdentifier();
360  $properties['name'] = $this->getName();
361  }
362 
363  $properties['configuration'] = serialize($this->processingConfiguration);
364 
365  return array_merge($properties, array(
366  'storage' => $this->getStorage()->getUid(),
367  'checksum' => $this->calculateChecksum(),
368  'task_type' => $this->taskType,
369  'configurationsha1' => sha1($properties['configuration']),
370  'original' => $this->originalFile->getUid(),
371  'originalfilesha1' => $this->originalFileSha1
372  ));
373  }
374 
380  protected function isUnchanged()
381  {
382  return !$this->properties['width'] && $this->usesOriginalFile();
383  }
384 
388  public function setUsesOriginalFile()
389  {
390  // @todo check if some of these properties can/should be set in a generic update method
391  $this->identifier = $this->originalFile->getIdentifier();
392  $this->updated = true;
393  $this->originalFileSha1 = $this->originalFile->getSha1();
394  }
395 
399  public function usesOriginalFile()
400  {
401  return $this->identifier == null || $this->identifier === $this->originalFile->getIdentifier();
402  }
403 
409  public function isOutdated()
410  {
411  return $this->needsReprocessing();
412  }
413 
420  public function delete($force = false)
421  {
422  if (!$force && $this->isUnchanged()) {
423  return false;
424  }
425  // Only delete file when original isn't used
426  if (!$this->usesOriginalFile()) {
427  return parent::delete();
428  } else {
429  return true;
430  }
431  }
432 
440  public function getProperty($key)
441  {
442  // The uid always (!) has to come from this file and never the original file (see getOriginalFile() to get this)
443  if ($this->isUnchanged() && $key !== 'uid') {
444  return $this->originalFile->getProperty($key);
445  } else {
446  return $this->properties[$key];
447  }
448  }
449 
455  public function getUid()
456  {
457  return $this->properties['uid'];
458  }
459 
460 
466  public function needsReprocessing()
467  {
468  $fileMustBeRecreated = false;
469 
470  // if original is missing we can not reprocess the file
471  if ($this->originalFile->isMissing()) {
472  return false;
473  }
474 
475  // processedFile does not exist
476  if (!$this->usesOriginalFile() && !$this->exists()) {
477  $fileMustBeRecreated = true;
478  }
479 
480  // hash does not match
481  if (array_key_exists('checksum', $this->properties) && $this->calculateChecksum() !== $this->properties['checksum']) {
482  $fileMustBeRecreated = true;
483  }
484 
485  // original file changed
486  if ($this->originalFile->getSha1() !== $this->originalFileSha1) {
487  $fileMustBeRecreated = true;
488  }
489 
490  if (!array_key_exists('uid', $this->properties)) {
491  $fileMustBeRecreated = true;
492  }
493 
494  // remove outdated file
495  if ($fileMustBeRecreated && $this->exists()) {
496  $this->delete();
497  }
498  return $fileMustBeRecreated;
499  }
500 
506  public function getProcessingConfiguration()
507  {
509  }
510 
516  public function getTaskIdentifier()
517  {
518  return $this->taskType;
519  }
520 
527  public function getTask()
528  {
529  if ($this->task == null) {
530  $this->task = $this->taskTypeRegistry->getTaskForType($this->taskType, $this, $this->processingConfiguration);
531  }
532 
533  return $this->task;
534  }
535 
542  {
543  $name = $this->originalFile->getNameWithoutExtension();
544  $name .= '_' . $this->originalFile->getUid();
545  $name .= '_' . $this->calculateChecksum();
546 
547  return $name;
548  }
549 
556  public function getPublicUrl($relativeToCurrentScript = false)
557  {
558  if ($this->deleted) {
559  return null;
560  } elseif ($this->usesOriginalFile()) {
561  return $this->getOriginalFile()->getPublicUrl($relativeToCurrentScript);
562  } else {
563  return $this->getStorage()->getPublicUrl($this, $relativeToCurrentScript);
564  }
565  }
566 }