TYPO3CMS  master
 All Classes Namespaces Files Functions Variables Pages
ResourceFactory.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 
26 
27 // @todo implement constructor-level caching
32 {
38  public static function getInstance()
39  {
40  return GeneralUtility::makeInstance(__CLASS__);
41  }
42 
46  protected $storageInstances = [];
47 
51  protected $collectionInstances = [];
52 
56  protected $fileInstances = [];
57 
61  protected $fileReferenceInstances = [];
62 
68  protected $localDriverStorageCache = null;
69 
74 
81  {
82  $this->signalSlotDispatcher = $signalSlotDispatcher ?: GeneralUtility::makeInstance(Dispatcher::class);
83  }
84 
93  public function getDriverObject($driverIdentificationString, array $driverConfiguration)
94  {
96  $driverRegistry = GeneralUtility::makeInstance(Driver\DriverRegistry::class);
97  $driverClass = $driverRegistry->getDriverClass($driverIdentificationString);
98  $driverObject = GeneralUtility::makeInstance($driverClass, $driverConfiguration);
99  return $driverObject;
100  }
101 
112  public function getDefaultStorage()
113  {
115  $storageRepository = GeneralUtility::makeInstance(StorageRepository::class);
116 
117  $allStorages = $storageRepository->findAll();
118  foreach ($allStorages as $storage) {
119  if ($storage->isDefault()) {
120  return $storage;
121  }
122  }
123  return null;
124  }
136  public function getStorageObject($uid, array $recordData = [], &$fileIdentifier = null)
137  {
138  if (!is_numeric($uid)) {
139  throw new \InvalidArgumentException('The UID of storage has to be numeric. UID given: "' . $uid . '"', 1314085991);
140  }
141  $uid = (int)$uid;
142  if ($uid === 0 && $fileIdentifier !== null) {
143  $uid = $this->findBestMatchingStorageByLocalPath($fileIdentifier);
144  }
145  if (!$this->storageInstances[$uid]) {
146  $storageConfiguration = null;
147  $storageObject = null;
148  list($_, $uid, $recordData, $fileIdentifier) = $this->emitPreProcessStorageSignal($uid, $recordData, $fileIdentifier);
149  // If the built-in storage with UID=0 is requested:
150  if ($uid === 0) {
151  $recordData = [
152  'uid' => 0,
153  'pid' => 0,
154  'name' => 'Fallback Storage',
155  'description' => 'Internal storage, mounting the main TYPO3_site directory.',
156  'driver' => 'Local',
157  'processingfolder' => 'typo3temp/assets/_processed_/',
158  // legacy code
159  'configuration' => '',
160  'is_online' => true,
161  'is_browsable' => true,
162  'is_public' => true,
163  'is_writable' => true,
164  'is_default' => false,
165  ];
166  $storageConfiguration = [
167  'basePath' => '/',
168  'pathType' => 'relative'
169  ];
170  } elseif (count($recordData) === 0 || (int)$recordData['uid'] !== $uid) {
172  $storageRepository = GeneralUtility::makeInstance(StorageRepository::class);
174  $storageObject = $storageRepository->findByUid($uid);
175  }
176  if (!$storageObject instanceof ResourceStorage) {
177  $storageObject = $this->createStorageObject($recordData, $storageConfiguration);
178  }
179  $this->emitPostProcessStorageSignal($storageObject);
180  $this->storageInstances[$uid] = $storageObject;
181  }
182  return $this->storageInstances[$uid];
183  }
184 
193  protected function emitPreProcessStorageSignal($uid, $recordData, $fileIdentifier)
194  {
195  return $this->signalSlotDispatcher->dispatch(\TYPO3\CMS\Core\Resource\ResourceFactory::class, self::SIGNAL_PreProcessStorage, [$this, $uid, $recordData, $fileIdentifier]);
196  }
197 
203  protected function emitPostProcessStorageSignal(ResourceStorage $storageObject)
204  {
205  $this->signalSlotDispatcher->dispatch(self::class, self::SIGNAL_PostProcessStorage, [$this, $storageObject]);
206  }
207 
218  protected function findBestMatchingStorageByLocalPath(&$localPath)
219  {
220  if ($this->localDriverStorageCache === null) {
221  $this->initializeLocalStorageCache();
222  }
223 
224  $bestMatchStorageUid = 0;
225  $bestMatchLength = 0;
226  foreach ($this->localDriverStorageCache as $storageUid => $basePath) {
227  $matchLength = strlen(PathUtility::getCommonPrefix([$basePath, $localPath]));
228  $basePathLength = strlen($basePath);
229 
230  if ($matchLength >= $basePathLength && $matchLength > $bestMatchLength) {
231  $bestMatchStorageUid = (int)$storageUid;
232  $bestMatchLength = $matchLength;
233  }
234  }
235  if ($bestMatchStorageUid !== 0) {
236  $localPath = substr($localPath, $bestMatchLength);
237  }
238  return $bestMatchStorageUid;
239  }
240 
244  protected function initializeLocalStorageCache()
245  {
247  $storageRepository = GeneralUtility::makeInstance(StorageRepository::class);
249  $storageObjects = $storageRepository->findByStorageType('Local');
250 
251  $storageCache = [];
252  foreach ($storageObjects as $localStorage) {
253  $configuration = $localStorage->getConfiguration();
254  $storageCache[$localStorage->getUid()] = $configuration['basePath'];
255  }
256  $this->localDriverStorageCache = $storageCache;
257  }
258 
265  public function convertFlexFormDataToConfigurationArray($flexFormData)
266  {
267  $configuration = [];
268  if ($flexFormData) {
269  $flexFormService = GeneralUtility::makeInstance(FlexFormService::class);
270  $configuration = $flexFormService->convertFlexFormContentToArray($flexFormData);
271  }
272  return $configuration;
273  }
274 
284  public function getCollectionObject($uid, array $recordData = [])
285  {
286  if (!is_numeric($uid)) {
287  throw new \InvalidArgumentException('The UID of collection has to be numeric. UID given: "' . $uid . '"', 1314085999);
288  }
289  if (!$this->collectionInstances[$uid]) {
290  // Get mount data if not already supplied as argument to this function
291  if (empty($recordData) || $recordData['uid'] !== $uid) {
292  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_file_collection');
293  $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
294  $recordData = $queryBuilder->select('*')
295  ->from('sys_file_collection')
296  ->where(
297  $queryBuilder->expr()->eq(
298  'uid',
299  $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)
300  )
301  )
302  ->execute()
303  ->fetch();
304  if (empty($recordData)) {
305  throw new \InvalidArgumentException('No collection found for given UID: "' . $uid . '"', 1314085992);
306  }
307  }
308  $collectionObject = $this->createCollectionObject($recordData);
309  $this->collectionInstances[$uid] = $collectionObject;
310  }
311  return $this->collectionInstances[$uid];
312  }
313 
320  public function createCollectionObject(array $collectionData)
321  {
323  $registry = GeneralUtility::makeInstance(Collection\FileCollectionRegistry::class);
324 
326  $class = $registry->getFileCollectionClass($collectionData['type']);
327 
328  return $class::create($collectionData);
329  }
330 
338  public function createStorageObject(array $storageRecord, array $storageConfiguration = null)
339  {
340  if (!$storageConfiguration) {
341  $storageConfiguration = $this->convertFlexFormDataToConfigurationArray($storageRecord['configuration']);
342  }
343  $driverType = $storageRecord['driver'];
344  $driverObject = $this->getDriverObject($driverType, $storageConfiguration);
345  return GeneralUtility::makeInstance(ResourceStorage::class, $driverObject, $storageRecord);
346  }
347 
356  public function createFolderObject(ResourceStorage $storage, $identifier, $name)
357  {
358  return GeneralUtility::makeInstance(Folder::class, $storage, $identifier, $name);
359  }
360 
372  public function getFileObject($uid, array $fileData = [])
373  {
374  if (!is_numeric($uid)) {
375  throw new \InvalidArgumentException('The UID of file has to be numeric. UID given: "' . $uid . '"', 1300096564);
376  }
377  if (!$this->fileInstances[$uid]) {
378  // Fetches data in case $fileData is empty
379  if (empty($fileData)) {
380  $fileData = $this->getFileIndexRepository()->findOneByUid($uid);
381  if ($fileData === false) {
382  throw new Exception\FileDoesNotExistException('No file found for given UID: ' . $uid, 1317178604);
383  }
384  }
385  $this->fileInstances[$uid] = $this->createFileObject($fileData);
386  }
387  return $this->fileInstances[$uid];
388  }
389 
397  public function getFileObjectFromCombinedIdentifier($identifier)
398  {
399  if (!isset($identifier) || !is_string($identifier) || $identifier === '') {
400  throw new \InvalidArgumentException('Invalid file identifier given. It must be of type string and not empty. "' . gettype($identifier) . '" given.', 1401732564);
401  }
402  $parts = GeneralUtility::trimExplode(':', $identifier);
403  if (count($parts) === 2) {
404  $storageUid = $parts[0];
405  $fileIdentifier = $parts[1];
406  } else {
407  // We only got a path: Go into backwards compatibility mode and
408  // use virtual Storage (uid=0)
409  $storageUid = 0;
410  $fileIdentifier = $parts[0];
411  }
412 
413  // please note that getStorageObject() might modify $fileIdentifier when
414  // auto-detecting the best-matching storage to use
415  return $this->getFileObjectByStorageAndIdentifier($storageUid, $fileIdentifier);
416  }
417 
427  public function getFileObjectByStorageAndIdentifier($storageUid, &$fileIdentifier)
428  {
429  $storage = $this->getStorageObject($storageUid, [], $fileIdentifier);
430  if (!$storage->isWithinProcessingFolder($fileIdentifier)) {
431  $fileData = $this->getFileIndexRepository()->findOneByStorageUidAndIdentifier($storage->getUid(), $fileIdentifier);
432  if ($fileData === false) {
433  $fileObject = $this->getIndexer($storage)->createIndexEntry($fileIdentifier);
434  } else {
435  $fileObject = $this->getFileObject($fileData['uid'], $fileData);
436  }
437  } else {
438  $fileObject = $this->getProcessedFileRepository()->findByStorageAndIdentifier($storage, $fileIdentifier);
439  }
440 
441  return $fileObject;
442  }
443 
464  public function retrieveFileOrFolderObject($input)
465  {
466  // Remove PATH_site because absolute paths under Windows systems contain ':'
467  // This is done in all considered sub functions anyway
468  $input = str_replace(PATH_site, '', $input);
469 
470  if (GeneralUtility::isFirstPartOfStr($input, 'file:')) {
471  $input = substr($input, 5);
472  return $this->retrieveFileOrFolderObject($input);
473  }
475  return $this->getFileObject($input);
476  }
477  if (strpos($input, ':') > 0) {
478  list($prefix) = explode(':', $input);
480  // path or folder in a valid storageUID
481  return $this->getObjectFromCombinedIdentifier($input);
482  }
483  if ($prefix === 'EXT') {
484  $input = GeneralUtility::getFileAbsFileName($input);
485  if (empty($input)) {
486  return null;
487  }
488 
489  $input = PathUtility::getRelativePath(PATH_site, PathUtility::dirname($input)) . PathUtility::basename($input);
490  return $this->getFileObjectFromCombinedIdentifier($input);
491  }
492  return null;
493  }
494  // this is a backwards-compatible way to access "0-storage" files or folders
495  // eliminate double slashes, /./ and /../
496  $input = PathUtility::getCanonicalPath(ltrim($input, '/'));
497  if (@is_file(PATH_site . $input)) {
498  // only the local file
499  return $this->getFileObjectFromCombinedIdentifier($input);
500  }
501  // only the local path
502  return $this->getFolderObjectFromCombinedIdentifier($input);
503  }
504 
512  public function getFolderObjectFromCombinedIdentifier($identifier)
513  {
514  $parts = GeneralUtility::trimExplode(':', $identifier);
515  if (count($parts) === 2) {
516  $storageUid = $parts[0];
517  $folderIdentifier = $parts[1];
518  } else {
519  // We only got a path: Go into backwards compatibility mode and
520  // use virtual Storage (uid=0)
521  $storageUid = 0;
522 
523  // please note that getStorageObject() might modify $folderIdentifier when
524  // auto-detecting the best-matching storage to use
525  $folderIdentifier = $parts[0];
526  // make sure to not use an absolute path, and remove PATH_site if it is prepended
527  if (GeneralUtility::isFirstPartOfStr($folderIdentifier, PATH_site)) {
528  $folderIdentifier = PathUtility::stripPathSitePrefix($parts[0]);
529  }
530  }
531  return $this->getStorageObject($storageUid, [], $folderIdentifier)->getFolder($folderIdentifier);
532  }
533 
540  public function getStorageObjectFromCombinedIdentifier($identifier)
541  {
542  $parts = GeneralUtility::trimExplode(':', $identifier);
543  $storageUid = count($parts) === 2 ? $parts[0] : null;
544  return $this->getStorageObject($storageUid);
545  }
546 
555  public function getObjectFromCombinedIdentifier($identifier)
556  {
557  list($storageId, $objectIdentifier) = GeneralUtility::trimExplode(':', $identifier);
558  $storage = $this->getStorageObject($storageId);
559  if ($storage->hasFile($objectIdentifier)) {
560  return $storage->getFile($objectIdentifier);
561  }
562  if ($storage->hasFolder($objectIdentifier)) {
563  return $storage->getFolder($objectIdentifier);
564  }
565  throw new Exception\ResourceDoesNotExistException('Object with identifier "' . $identifier . '" does not exist in storage', 1329647780);
566  }
567 
576  public function createFileObject(array $fileData, ResourceStorage $storage = null)
577  {
579  if (array_key_exists('storage', $fileData) && MathUtility::canBeInterpretedAsInteger($fileData['storage'])) {
580  $storageObject = $this->getStorageObject((int)$fileData['storage']);
581  } elseif ($storage !== null) {
582  $storageObject = $storage;
583  $fileData['storage'] = $storage->getUid();
584  } else {
585  throw new \RuntimeException('A file needs to reside in a Storage', 1381570997);
586  }
587  $fileObject = GeneralUtility::makeInstance(File::class, $fileData, $storageObject);
588  return $fileObject;
589  }
590 
602  public function getFileReferenceObject($uid, array $fileReferenceData = [], $raw = false)
603  {
604  if (!is_numeric($uid)) {
605  throw new \InvalidArgumentException(
606  'The reference UID for the file (sys_file_reference) has to be numeric. UID given: "' . $uid . '"',
607  1300086584
608  );
609  }
610  if (!$this->fileReferenceInstances[$uid]) {
611  // Fetches data in case $fileData is empty
612  if (empty($fileReferenceData)) {
613  $fileReferenceData = $this->getFileReferenceData($uid, $raw);
614  if (!is_array($fileReferenceData)) {
615  throw new Exception\ResourceDoesNotExistException(
616  'No file reference (sys_file_reference) was found for given UID: "' . $uid . '"',
617  1317178794
618  );
619  }
620  }
621  $this->fileReferenceInstances[$uid] = $this->createFileReferenceObject($fileReferenceData);
622  }
623  return $this->fileReferenceInstances[$uid];
624  }
625 
634  public function createFileReferenceObject(array $fileReferenceData)
635  {
637  $fileReferenceObject = GeneralUtility::makeInstance(FileReference::class, $fileReferenceData);
638  return $fileReferenceObject;
639  }
640 
648  protected function getFileReferenceData($uid, $raw = false)
649  {
650  if (!$raw && TYPO3_MODE === 'BE') {
651  $fileReferenceData = BackendUtility::getRecordWSOL('sys_file_reference', $uid);
652  } elseif (!$raw && is_object($GLOBALS['TSFE'])) {
653  $fileReferenceData = $GLOBALS['TSFE']->sys_page->checkRecord('sys_file_reference', $uid);
654  } else {
655  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_file_reference');
656  $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
657  $fileReferenceData = $queryBuilder->select('*')
658  ->from('sys_file_reference')
659  ->where(
660  $queryBuilder->expr()->eq(
661  'uid',
662  $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)
663  )
664  )
665  ->execute()
666  ->fetch();
667  }
668  return $fileReferenceData;
669  }
670 
676  protected function getFileIndexRepository()
677  {
679  }
680 
686  protected function getProcessedFileRepository()
687  {
688  return GeneralUtility::makeInstance(ProcessedFileRepository::class);
689  }
690 
697  protected function getIndexer(ResourceStorage $storage)
698  {
699  return GeneralUtility::makeInstance(Index\Indexer::class, $storage);
700  }
701 }
createStorageObject(array $storageRecord, array $storageConfiguration=null)
static getRecordWSOL($table, $uid, $fields= '*', $where= '', $useDeleteClause=true, $unsetMovePointers=false)
getCollectionObject($uid, array $recordData=[])
$GLOBALS['TYPO3_CONF_VARS']['BE']['toolbarItems'][1435433106]
getFileObjectByStorageAndIdentifier($storageUid, &$fileIdentifier)
static isFirstPartOfStr($str, $partStr)
static trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
createFolderObject(ResourceStorage $storage, $identifier, $name)
__construct(Dispatcher $signalSlotDispatcher=null)
emitPreProcessStorageSignal($uid, $recordData, $fileIdentifier)
getFileObject($uid, array $fileData=[])
getFileReferenceObject($uid, array $fileReferenceData=[], $raw=false)
static getRelativePath($sourcePath, $targetPath)
Definition: PathUtility.php:70
static makeInstance($className,...$constructorArguments)
emitPostProcessStorageSignal(ResourceStorage $storageObject)
static getCommonPrefix(array $paths)