[TASK] Re-work/simplify copyright header in PHP files - Part 3
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Resource / AbstractFile.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
17 use TYPO3\CMS\Core\Utility\PathUtility;
18
19 /**
20 * Abstract file representation in the file abstraction layer.
21 *
22 * @author Ingmar Schlecht <ingmar@typo3.org>
23 */
24 abstract class AbstractFile implements FileInterface {
25
26 /**
27 * Various file properties
28 *
29 * Note that all properties, which only the persisted (indexed) files have are stored in this
30 * overall properties array only. The only properties which really exist as object properties of
31 * the file object are the storage, the identifier, the fileName and the indexing status.
32 *
33 * @var array
34 */
35 protected $properties;
36
37 /**
38 * The storage this file is located in
39 *
40 * @var ResourceStorage
41 */
42 protected $storage = NULL;
43
44 /**
45 * The identifier of this file to identify it on the storage.
46 * On some drivers, this is the path to the file, but drivers could also just
47 * provide any other unique identifier for this file on the specific storage.
48 *
49 * @var string
50 */
51 protected $identifier;
52
53 /**
54 * The file name of this file
55 *
56 * @var string
57 */
58 protected $name;
59
60 /**
61 * If set to true, this file is regarded as being deleted.
62 *
63 * @var boolean
64 */
65 protected $deleted = FALSE;
66
67 /**
68 * any other file
69 */
70 const FILETYPE_UNKNOWN = 0;
71
72 /**
73 * Any kind of text
74 * @see http://www.iana.org/assignments/media-types/text
75 */
76 const FILETYPE_TEXT = 1;
77
78 /**
79 * Any kind of image
80 * @see http://www.iana.org/assignments/media-types/image
81 */
82 const FILETYPE_IMAGE = 2;
83
84 /**
85 * Any kind of audio file
86 * @see http://www.iana.org/assignments/media-types/audio
87 */
88 const FILETYPE_AUDIO = 3;
89
90 /**
91 * Any kind of video
92 * @see http://www.iana.org/assignments/media-types/video
93 */
94 const FILETYPE_VIDEO = 4;
95
96 /**
97 * Any kind of application
98 * @see http://www.iana.org/assignments/media-types/application
99 */
100 const FILETYPE_APPLICATION = 5;
101
102 /**
103 * Any kind of software, often known as "application"
104 * @deprecated since 6.1, will be removed in 6.3. Use rather FILETYPE_APPLICATION which matches the Iana standard.
105 */
106 const FILETYPE_SOFTWARE = 5;
107
108 /******************
109 * VARIOUS FILE PROPERTY GETTERS
110 ******************/
111 /**
112 * Returns true if the given property key exists for this file.
113 *
114 * @param string $key
115 * @return boolean
116 */
117 public function hasProperty($key) {
118 return array_key_exists($key, $this->properties);
119 }
120
121 /**
122 * Returns a property value
123 *
124 * @param string $key
125 * @return mixed Property value
126 */
127 public function getProperty($key) {
128 if ($this->hasProperty($key)) {
129 return $this->properties[$key];
130 } else {
131 return NULL;
132 }
133 }
134
135 /**
136 * Returns the properties of this object.
137 *
138 * @return array
139 */
140 public function getProperties() {
141 return $this->properties;
142 }
143
144 /**
145 * Returns the identifier of this file
146 *
147 * @return string
148 */
149 public function getIdentifier() {
150 return $this->identifier;
151 }
152
153 /**
154 * Get hashed identifier
155 *
156 * @return string
157 */
158 public function getHashedIdentifier() {
159 return $this->properties['identifier_hash'];
160 }
161
162 /**
163 * Returns the name of this file
164 *
165 * @return string
166 */
167 public function getName() {
168 // Do not check if file has been deleted because we might need the
169 // name for undeleting it.
170 return $this->name;
171 }
172
173 /**
174 * Returns the basename (the name without extension) of this file.
175 *
176 * @return string
177 */
178 public function getNameWithoutExtension() {
179 return PathUtility::pathinfo($this->getName(), PATHINFO_FILENAME);
180 }
181
182 /**
183 * Returns the size of this file
184 *
185 * @throws \RuntimeException
186 * @return integer
187 */
188 public function getSize() {
189 if ($this->deleted) {
190 throw new \RuntimeException('File has been deleted.', 1329821480);
191 }
192 return $this->properties['size'] ?: array_pop($this->getStorage()->getFileInfoByIdentifier($this->getIdentifier(), array('size')));
193 }
194
195 /**
196 * Returns the uid of this file
197 *
198 * @return integer
199 */
200 public function getUid() {
201 return $this->getProperty('uid');
202 }
203
204 /**
205 * Returns the Sha1 of this file
206 *
207 * @throws \RuntimeException
208 * @return string
209 */
210 public function getSha1() {
211 if ($this->deleted) {
212 throw new \RuntimeException('File has been deleted.', 1329821481);
213 }
214 return $this->getStorage()->hashFile($this, 'sha1');
215 }
216
217 /**
218 * Returns the creation time of the file as Unix timestamp
219 *
220 * @throws \RuntimeException
221 * @return integer
222 */
223 public function getCreationTime() {
224 if ($this->deleted) {
225 throw new \RuntimeException('File has been deleted.', 1329821487);
226 }
227 return $this->getProperty('creation_date');
228 }
229
230 /**
231 * Returns the date (as UNIX timestamp) the file was last modified.
232 *
233 * @throws \RuntimeException
234 * @return integer
235 */
236 public function getModificationTime() {
237 if ($this->deleted) {
238 throw new \RuntimeException('File has been deleted.', 1329821488);
239 }
240 return $this->getProperty('modification_date');
241 }
242
243 /**
244 * Get the extension of this file in a lower-case variant
245 *
246 * @return string The file extension
247 */
248 public function getExtension() {
249 $pathinfo = PathUtility::pathinfo($this->getName());
250
251 $extension = strtolower($pathinfo['extension']);
252
253 return $extension;
254 }
255
256 /**
257 * Get the MIME type of this file
258 *
259 * @return array file information
260 */
261 public function getMimeType() {
262 return $this->properties['mimetype'] ?: array_pop($this->getStorage()->getFileInfoByIdentifier($this->getIdentifier(), array('mimetype')));
263 }
264
265 /**
266 * Returns the fileType of this file
267 * basically there are only five main "file types"
268 * "audio"
269 * "image"
270 * "software"
271 * "text"
272 * "video"
273 * "other"
274 * see the constants in this class
275 *
276 * @return integer $fileType
277 */
278 public function getType() {
279 // this basically extracts the mimetype and guess the filetype based
280 // on the first part of the mimetype works for 99% of all cases, and
281 // we don't need to make an SQL statement like EXT:media does currently
282 if (!$this->properties['type']) {
283 $mimeType = $this->getMimeType();
284 list($fileType) = explode('/', $mimeType);
285 switch (strtolower($fileType)) {
286 case 'text':
287 $this->properties['type'] = self::FILETYPE_TEXT;
288 break;
289 case 'image':
290 $this->properties['type'] = self::FILETYPE_IMAGE;
291 break;
292 case 'audio':
293 $this->properties['type'] = self::FILETYPE_AUDIO;
294 break;
295 case 'video':
296 $this->properties['type'] = self::FILETYPE_VIDEO;
297 break;
298 case 'application':
299
300 case 'software':
301 $this->properties['type'] = self::FILETYPE_APPLICATION;
302 break;
303 default:
304 $this->properties['type'] = self::FILETYPE_UNKNOWN;
305 }
306 }
307 return $this->properties['type'];
308 }
309
310 /******************
311 * CONTENTS RELATED
312 ******************/
313 /**
314 * Get the contents of this file
315 *
316 * @throws \RuntimeException
317 * @return string File contents
318 */
319 public function getContents() {
320 if ($this->deleted) {
321 throw new \RuntimeException('File has been deleted.', 1329821479);
322 }
323 return $this->getStorage()->getFileContents($this);
324 }
325
326 /**
327 * Replace the current file contents with the given string
328 *
329 * @param string $contents The contents to write to the file.
330 *
331 * @throws \RuntimeException
332 * @return File The file object (allows chaining).
333 */
334 public function setContents($contents) {
335 if ($this->deleted) {
336 throw new \RuntimeException('File has been deleted.', 1329821478);
337 }
338 $this->getStorage()->setFileContents($this, $contents);
339 return $this;
340 }
341
342 /****************************************
343 * STORAGE AND MANAGEMENT RELATED METHDOS
344 ****************************************/
345
346 /**
347 * Get the storage this file is located in
348 *
349 * @return ResourceStorage
350 * @throws \RuntimeException
351 */
352 public function getStorage() {
353 if ($this->storage === NULL) {
354 throw new \RuntimeException('You\'re using fileObjects without a storage.', 1381570091);
355 }
356 return $this->storage;
357 }
358
359 /**
360 * Checks if this file exists. This should normally always return TRUE;
361 * it might only return FALSE when this object has been created from an
362 * index record without checking for.
363 *
364 * @return boolean TRUE if this file physically exists
365 */
366 public function exists() {
367 if ($this->deleted) {
368 return FALSE;
369 }
370 return $this->storage->hasFile($this->getIdentifier());
371 }
372
373 /**
374 * Sets the storage this file is located in. This is only meant for
375 * \TYPO3\CMS\Core\Resource-internal usage; don't use it to move files.
376 *
377 * @internal Should only be used by other parts of the File API (e.g. drivers after moving a file)
378 * @param ResourceStorage $storage
379 * @return File
380 */
381 public function setStorage(ResourceStorage $storage) {
382 $this->storage = $storage;
383 $this->properties['storage'] = $storage->getUid();
384 return $this;
385 }
386
387 /**
388 * Set the identifier of this file
389 *
390 * @internal Should only be used by other parts of the File API (e.g. drivers after moving a file)
391 * @param string $identifier
392 * @return File
393 */
394 public function setIdentifier($identifier) {
395 $this->identifier = $identifier;
396 return $this;
397 }
398
399 /**
400 * Returns a combined identifier of this file, i.e. the storage UID and the
401 * folder identifier separated by a colon ":".
402 *
403 * @return string Combined storage and file identifier, e.g. StorageUID:path/and/fileName.png
404 */
405 public function getCombinedIdentifier() {
406 if (is_array($this->properties) && \TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($this->properties['storage'])) {
407 $combinedIdentifier = $this->properties['storage'] . ':' . $this->getIdentifier();
408 } else {
409 $combinedIdentifier = $this->getStorage()->getUid() . ':' . $this->getIdentifier();
410 }
411 return $combinedIdentifier;
412 }
413
414 /**
415 * Deletes this file from its storage. This also means that this object becomes useless.
416 *
417 * @return boolean TRUE if deletion succeeded
418 */
419 public function delete() {
420 // The storage will mark this file as deleted
421 return $this->getStorage()->deleteFile($this);
422 }
423
424 /**
425 * Marks this file as deleted. This should only be used inside the
426 * File Abstraction Layer, as it is a low-level API method.
427 *
428 * @return void
429 */
430 public function setDeleted() {
431 $this->deleted = TRUE;
432 }
433
434 /**
435 * Returns TRUE if this file has been deleted
436 *
437 * @return boolean
438 */
439 public function isDeleted() {
440 return $this->deleted;
441 }
442
443 /**
444 * Renames this file.
445 *
446 * @param string $newName The new file name
447 *
448 * @throws \RuntimeException
449 * @return File
450 */
451 public function rename($newName) {
452 if ($this->deleted) {
453 throw new \RuntimeException('File has been deleted.', 1329821482);
454 }
455 return $this->getStorage()->renameFile($this, $newName);
456 }
457
458 /**
459 * Copies this file into a target folder
460 *
461 * @param Folder $targetFolder Folder to copy file into.
462 * @param string $targetFileName an optional destination fileName
463 * @param string $conflictMode overrideExistingFile", "renameNewFile", "cancel
464 *
465 * @throws \RuntimeException
466 * @return File The new (copied) file.
467 */
468 public function copyTo(Folder $targetFolder, $targetFileName = NULL, $conflictMode = 'renameNewFile') {
469 if ($this->deleted) {
470 throw new \RuntimeException('File has been deleted.', 1329821483);
471 }
472 return $targetFolder->getStorage()->copyFile($this, $targetFolder, $targetFileName, $conflictMode);
473 }
474
475 /**
476 * Moves the file into the target folder
477 *
478 * @param Folder $targetFolder Folder to move file into.
479 * @param string $targetFileName an optional destination fileName
480 * @param string $conflictMode overrideExistingFile", "renameNewFile", "cancel
481 *
482 * @throws \RuntimeException
483 * @return File This file object, with updated properties.
484 */
485 public function moveTo(Folder $targetFolder, $targetFileName = NULL, $conflictMode = 'renameNewFile') {
486 if ($this->deleted) {
487 throw new \RuntimeException('File has been deleted.', 1329821484);
488 }
489 return $targetFolder->getStorage()->moveFile($this, $targetFolder, $targetFileName, $conflictMode);
490 }
491
492 /*****************
493 * SPECIAL METHODS
494 *****************/
495 /**
496 * Returns a publicly accessible URL for this file
497 *
498 * WARNING: Access to the file may be restricted by further means, e.g. some
499 * web-based authentication. You have to take care of this yourself.
500 *
501 * @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)
502 *
503 * @return null|string
504 */
505 public function getPublicUrl($relativeToCurrentScript = FALSE) {
506 if ($this->deleted) {
507 return NULL;
508 } else {
509 return $this->getStorage()->getPublicUrl($this, $relativeToCurrentScript);
510 }
511 }
512
513 /**
514 * Returns a path to a local version of this file to process it locally (e.g. with some system tool).
515 * If the file is normally located on a remote storages, this creates a local copy.
516 * If the file is already on the local system, this only makes a new copy if $writable is set to TRUE.
517 *
518 * @param boolean $writable Set this to FALSE if you only want to do read operations on the file.
519 *
520 * @throws \RuntimeException
521 * @return string
522 */
523 public function getForLocalProcessing($writable = TRUE) {
524 if ($this->deleted) {
525 throw new \RuntimeException('File has been deleted.', 1329821486);
526 }
527 return $this->getStorage()->getFileForLocalProcessing($this, $writable);
528 }
529
530 /***********************
531 * INDEX RELATED METHODS
532 ***********************/
533 /**
534 * Updates properties of this object.
535 * This method is used to reconstitute settings from the
536 * database into this object after being intantiated.
537 *
538 * @param array $properties
539 */
540 abstract public function updateProperties(array $properties);
541
542 /**
543 * Returns the parent folder.
544 *
545 * @return FolderInterface
546 */
547 public function getParentFolder() {
548 return $this->getStorage()->getFolder($this->getStorage()->getFolderIdentifierFromFileIdentifier($this->getIdentifier()));
549 }
550 }