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