[TASK] Do not set GeneralUtility::$container in unit tests
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Resource / FileReference.php
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 use TYPO3\CMS\Core\Utility\ArrayUtility;
17
18 /**
19 * Representation of a specific usage of a file with possibilities to override certain
20 * properties of the original file just for this usage of the file.
21 *
22 * It acts as a decorator over the original file in the way that most method calls are
23 * directly passed along to the original file object.
24 *
25 * All file related methods are directly passed along; only meta data functionality is adopted
26 * in this decorator class to priorities possible overrides for the metadata for this specific usage
27 * of the file.
28 */
29 class FileReference implements FileInterface
30 {
31 /**
32 * Various properties of the FileReference. Note that these information can be different
33 * to the ones found in the originalFile.
34 *
35 * @var array
36 */
37 protected $propertiesOfFileReference;
38
39 /**
40 * The identifier of this file to identify it on the storage.
41 * On some drivers, this is the path to the file, but drivers could also just
42 * provide any other unique identifier for this file on the specific storage.
43 *
44 * @var string
45 */
46 protected $uidOfFileReference;
47
48 /**
49 * The file name of this file. It's either the fileName of the original underlying file,
50 * or the overlay file name supplied by the user for this particular usage (FileReference) of the file.
51 *
52 * @var string
53 */
54 protected $name;
55
56 /**
57 * Reference to the original File object underlying this FileReference.
58 *
59 * @var File
60 */
61 protected $originalFile;
62
63 /**
64 * Properties merged with the parent object (File) if
65 * the value is not defined (NULL). Thus, FileReference properties act
66 * as overlays for the defined File properties.
67 *
68 * @var array
69 */
70 protected $mergedProperties = [];
71
72 /**
73 * Constructor for a file in use object. Should normally not be used
74 * directly, use the corresponding factory methods instead.
75 *
76 * @param array $fileReferenceData
77 * @param ResourceFactory $factory
78 *
79 * @throws \RuntimeException
80 * @throws \InvalidArgumentException
81 */
82 public function __construct(array $fileReferenceData, $factory = null)
83 {
84 $this->propertiesOfFileReference = $fileReferenceData;
85 if (!$fileReferenceData['uid_local']) {
86 throw new \InvalidArgumentException('Incorrect reference to original file given for FileReference.', 1300098528);
87 }
88 if (!$factory) {
89 /** @var ResourceFactory $factory */
90 $factory = ResourceFactory::getInstance();
91 }
92 $this->originalFile = $factory->getFileObject($fileReferenceData['uid_local']);
93 if (!is_object($this->originalFile)) {
94 throw new \RuntimeException(
95 'Original file not found for FileReference. UID given: "' . $fileReferenceData['uid_local'] . '"',
96 1300098529
97 );
98 }
99 $this->name = $fileReferenceData['name'] !== '' ? $fileReferenceData['name'] : $this->originalFile->getName();
100 }
101
102 /*******************************
103 * VARIOUS FILE PROPERTY GETTERS
104 *******************************/
105 /**
106 * Returns true if the given key exists for this file.
107 *
108 * @param string $key The property to be looked up
109 * @return bool
110 */
111 public function hasProperty($key)
112 {
113 return array_key_exists($key, $this->getProperties());
114 }
115
116 /**
117 * Gets a property, falling back to values of the parent.
118 *
119 * @param string $key The property to be looked up
120 * @return mixed
121 * @throws \InvalidArgumentException
122 */
123 public function getProperty($key)
124 {
125 if (!$this->hasProperty($key)) {
126 throw new \InvalidArgumentException('Property "' . $key . '" was not found in file reference or original file.', 1314226805);
127 }
128 $properties = $this->getProperties();
129 return $properties[$key];
130 }
131
132 /**
133 * Gets a property of the file reference.
134 *
135 * @param string $key The property to be looked up
136 * @return mixed
137 * @throws \InvalidArgumentException
138 */
139 public function getReferenceProperty($key)
140 {
141 if (!array_key_exists($key, $this->propertiesOfFileReference)) {
142 throw new \InvalidArgumentException('Property "' . $key . '" of file reference was not found.', 1360684914);
143 }
144 return $this->propertiesOfFileReference[$key];
145 }
146
147 /**
148 * Gets all properties, falling back to values of the parent.
149 *
150 * @return array
151 */
152 public function getProperties()
153 {
154 if (empty($this->mergedProperties)) {
155 $this->mergedProperties = $this->propertiesOfFileReference;
156 ArrayUtility::mergeRecursiveWithOverrule(
157 $this->mergedProperties,
158 $this->originalFile->getProperties(),
159 true,
160 true,
161 false
162 );
163 array_walk($this->mergedProperties, [$this, 'restoreNonNullValuesCallback']);
164 }
165
166 return $this->mergedProperties;
167 }
168
169 /**
170 * Callback to handle the NULL value feature
171 *
172 * @param mixed $value
173 * @param mixed $key
174 */
175 protected function restoreNonNullValuesCallback(&$value, $key)
176 {
177 if (array_key_exists($key, $this->propertiesOfFileReference) && $this->propertiesOfFileReference[$key] !== null) {
178 $value = $this->propertiesOfFileReference[$key];
179 }
180 }
181
182 /**
183 * Gets all properties of the file reference.
184 *
185 * @return array
186 */
187 public function getReferenceProperties()
188 {
189 return $this->propertiesOfFileReference;
190 }
191
192 /**
193 * Returns the name of this file
194 *
195 * @return string
196 */
197 public function getName()
198 {
199 return $this->originalFile->getName();
200 }
201
202 /**
203 * Returns the title text to this image
204 *
205 * @todo Possibly move this to the image domain object instead
206 *
207 * @return string
208 */
209 public function getTitle()
210 {
211 return $this->getProperty('title');
212 }
213
214 /**
215 * Returns the alternative text to this image
216 *
217 * @todo Possibly move this to the image domain object instead
218 *
219 * @return string
220 */
221 public function getAlternative()
222 {
223 return $this->getProperty('alternative');
224 }
225
226 /**
227 * Returns the description text to this file
228 *
229 * @todo Possibly move this to the image domain object instead
230 *
231 * @return string
232 */
233 public function getDescription()
234 {
235 return $this->getProperty('description');
236 }
237
238 /**
239 * Returns the link that should be active when clicking on this image
240 *
241 * @todo Move this to the image domain object instead
242 *
243 * @return string
244 */
245 public function getLink()
246 {
247 return $this->propertiesOfFileReference['link'];
248 }
249
250 /**
251 * Returns the uid of this File In Use
252 *
253 * @return int
254 */
255 public function getUid()
256 {
257 return (int)$this->propertiesOfFileReference['uid'];
258 }
259
260 /**
261 * Returns the size of this file
262 *
263 * @return int
264 */
265 public function getSize()
266 {
267 return (int)$this->originalFile->getSize();
268 }
269
270 /**
271 * Returns the Sha1 of this file
272 *
273 * @return string
274 */
275 public function getSha1()
276 {
277 return $this->originalFile->getSha1();
278 }
279
280 /**
281 * Get the file extension of this file
282 *
283 * @return string The file extension
284 */
285 public function getExtension()
286 {
287 return $this->originalFile->getExtension();
288 }
289
290 /**
291 * Returns the basename (the name without extension) of this file.
292 *
293 * @return string
294 */
295 public function getNameWithoutExtension()
296 {
297 return $this->originalFile->getNameWithoutExtension();
298 }
299
300 /**
301 * Get the MIME type of this file
302 *
303 * @return string mime type
304 */
305 public function getMimeType()
306 {
307 return $this->originalFile->getMimeType();
308 }
309
310 /**
311 * Returns the modification time of the file as Unix timestamp
312 *
313 * @return int
314 */
315 public function getModificationTime()
316 {
317 return (int)$this->originalFile->getModificationTime();
318 }
319
320 /**
321 * Returns the creation time of the file as Unix timestamp
322 *
323 * @return int
324 */
325 public function getCreationTime()
326 {
327 return (int)$this->originalFile->getCreationTime();
328 }
329
330 /**
331 * Returns the fileType of this file
332 *
333 * @return int $fileType
334 */
335 public function getType()
336 {
337 return (int)$this->originalFile->getType();
338 }
339
340 /**
341 * Check if file is marked as missing by indexer
342 *
343 * @return bool
344 */
345 public function isMissing()
346 {
347 return (bool)$this->originalFile->getProperty('missing');
348 }
349
350 /******************
351 * CONTENTS RELATED
352 ******************/
353 /**
354 * Get the contents of this file
355 *
356 * @return string File contents
357 */
358 public function getContents()
359 {
360 return $this->originalFile->getContents();
361 }
362
363 /**
364 * Replace the current file contents with the given string
365 *
366 * @param string $contents The contents to write to the file.
367 * @return File The file object (allows chaining).
368 */
369 public function setContents($contents)
370 {
371 return $this->originalFile->setContents($contents);
372 }
373
374 /****************************************
375 * STORAGE AND MANAGEMENT RELATED METHDOS
376 ****************************************/
377 /**
378 * Get the storage the original file is located in
379 *
380 * @return ResourceStorage
381 */
382 public function getStorage()
383 {
384 return $this->originalFile->getStorage();
385 }
386
387 /**
388 * Returns the identifier of the underlying original file
389 *
390 * @return string
391 */
392 public function getIdentifier()
393 {
394 return $this->originalFile->getIdentifier();
395 }
396
397 /**
398 * Returns a combined identifier of the underlying original file
399 *
400 * @return string Combined storage and file identifier, e.g. StorageUID:path/and/fileName.png
401 */
402 public function getCombinedIdentifier()
403 {
404 return $this->originalFile->getCombinedIdentifier();
405 }
406
407 /**
408 * Deletes only this particular FileReference from the persistence layer
409 * (database table sys_file_reference) but leaves the original file untouched.
410 *
411 * @throws \BadMethodCallException
412 */
413 public function delete()
414 {
415 // @todo Implement this function. This should only delete the
416 // FileReference (sys_file_reference) record, not the file itself.
417 throw new \BadMethodCallException('Function not implemented FileReference::delete().', 1333754461);
418 //return $this->fileRepository->removeUsageRecord($this);
419 }
420
421 /**
422 * Renames the fileName in this particular usage.
423 *
424 * @param string $newName The new name
425 * @param string $conflictMode
426 */
427 public function rename($newName, $conflictMode = DuplicationBehavior::RENAME)
428 {
429 // @todo Implement this function. This should only rename the
430 // FileReference (sys_file_reference) record, not the file itself.
431 throw new \BadMethodCallException('Function not implemented FileReference::rename().', 1333754473);
432 //return $this->fileRepository->renameUsageRecord($this, $newName);
433 }
434
435 /*****************
436 * SPECIAL METHODS
437 *****************/
438 /**
439 * Returns a publicly accessible URL for this file
440 *
441 * WARNING: Access to the file may be restricted by further means, e.g.
442 * some web-based authentication. You have to take care of this yourself.
443 *
444 * @param bool $relativeToCurrentScript Determines whether the URL returned should be relative to the current script, in case it is relative at all (only for the LocalDriver)
445 * @return string|null NULL if file is missing or deleted, the generated url otherwise
446 */
447 public function getPublicUrl($relativeToCurrentScript = false)
448 {
449 return $this->originalFile->getPublicUrl($relativeToCurrentScript);
450 }
451
452 /**
453 * Returns TRUE if this file is indexed.
454 * This is always true for FileReference objects, as they rely on a
455 * sys_file_reference record to be present, which in turn can only exist if
456 * the original file is indexed.
457 *
458 * @return bool
459 */
460 public function isIndexed()
461 {
462 return true;
463 }
464
465 /**
466 * Returns a path to a local version of this file to process it locally (e.g. with some system tool).
467 * If the file is normally located on a remote storages, this creates a local copy.
468 * If the file is already on the local system, this only makes a new copy if $writable is set to TRUE.
469 *
470 * @param bool $writable Set this to FALSE if you only want to do read operations on the file.
471 * @return string
472 */
473 public function getForLocalProcessing($writable = true)
474 {
475 return $this->originalFile->getForLocalProcessing($writable);
476 }
477
478 /**
479 * Returns an array representation of the file.
480 * (This is used by the generic listing module vidi when displaying file records.)
481 *
482 * @return array Array of main data of the file. Don't rely on all data to be present here, it's just a selection of the most relevant information.
483 */
484 public function toArray()
485 {
486 $array = array_merge($this->originalFile->toArray(), $this->propertiesOfFileReference);
487 return $array;
488 }
489
490 /**
491 * Gets the original file being referenced.
492 *
493 * @return File
494 */
495 public function getOriginalFile()
496 {
497 return $this->originalFile;
498 }
499
500 /**
501 * Get hashed identifier
502 *
503 * @return string
504 */
505 public function getHashedIdentifier()
506 {
507 return $this->getStorage()->hashFileIdentifier($this->getIdentifier());
508 }
509
510 /**
511 * Returns the parent folder.
512 *
513 * @return FolderInterface
514 */
515 public function getParentFolder()
516 {
517 return $this->originalFile->getParentFolder();
518 }
519 }