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