TYPO3CMS  8
 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 
246  protected function initializeLocalStorageCache()
247  {
249  $storageRepository = GeneralUtility::makeInstance(StorageRepository::class);
251  $storageObjects = $storageRepository->findByStorageType('Local');
252 
253  $storageCache = [];
254  foreach ($storageObjects as $localStorage) {
255  $configuration = $localStorage->getConfiguration();
256  $storageCache[$localStorage->getUid()] = $configuration['basePath'];
257  }
258  $this->localDriverStorageCache = $storageCache;
259  }
260 
267  public function convertFlexFormDataToConfigurationArray($flexFormData)
268  {
269  $configuration = [];
270  if ($flexFormData) {
271  $flexFormService = GeneralUtility::makeInstance(FlexFormService::class);
272  $configuration = $flexFormService->convertFlexFormContentToArray($flexFormData);
273  }
274  return $configuration;
275  }
276 
286  public function getCollectionObject($uid, array $recordData = [])
287  {
288  if (!is_numeric($uid)) {
289  throw new \InvalidArgumentException('The UID of collection has to be numeric. UID given: "' . $uid . '"', 1314085999);
290  }
291  if (!$this->collectionInstances[$uid]) {
292  // Get mount data if not already supplied as argument to this function
293  if (empty($recordData) || $recordData['uid'] !== $uid) {
294  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_file_collection');
295  $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
296  $recordData = $queryBuilder->select('*')
297  ->from('sys_file_collection')
298  ->where(
299  $queryBuilder->expr()->eq(
300  'uid',
301  $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)
302  )
303  )
304  ->execute()
305  ->fetch();
306  if (empty($recordData)) {
307  throw new \InvalidArgumentException('No collection found for given UID: "' . $uid . '"', 1314085992);
308  }
309  }
310  $collectionObject = $this->createCollectionObject($recordData);
311  $this->collectionInstances[$uid] = $collectionObject;
312  }
313  return $this->collectionInstances[$uid];
314  }
315 
322  public function createCollectionObject(array $collectionData)
323  {
325  $registry = GeneralUtility::makeInstance(Collection\FileCollectionRegistry::class);
326 
328  $class = $registry->getFileCollectionClass($collectionData['type']);
329 
330  return $class::create($collectionData);
331  }
332 
340  public function createStorageObject(array $storageRecord, array $storageConfiguration = null)
341  {
342  if (!$storageConfiguration) {
343  $storageConfiguration = $this->convertFlexFormDataToConfigurationArray($storageRecord['configuration']);
344  }
345  $driverType = $storageRecord['driver'];
346  $driverObject = $this->getDriverObject($driverType, $storageConfiguration);
347  return GeneralUtility::makeInstance(ResourceStorage::class, $driverObject, $storageRecord);
348  }
349 
358  public function createFolderObject(ResourceStorage $storage, $identifier, $name)
359  {
360  return GeneralUtility::makeInstance(Folder::class, $storage, $identifier, $name);
361  }
362 
374  public function getFileObject($uid, array $fileData = [])
375  {
376  if (!is_numeric($uid)) {
377  throw new \InvalidArgumentException('The UID of file has to be numeric. UID given: "' . $uid . '"', 1300096564);
378  }
379  if (!$this->fileInstances[$uid]) {
380  // Fetches data in case $fileData is empty
381  if (empty($fileData)) {
382  $fileData = $this->getFileIndexRepository()->findOneByUid($uid);
383  if ($fileData === false) {
384  throw new Exception\FileDoesNotExistException('No file found for given UID: ' . $uid, 1317178604);
385  }
386  }
387  $this->fileInstances[$uid] = $this->createFileObject($fileData);
388  }
389  return $this->fileInstances[$uid];
390  }
391 
399  public function getFileObjectFromCombinedIdentifier($identifier)
400  {
401  if (!isset($identifier) || !is_string($identifier) || $identifier === '') {
402  throw new \InvalidArgumentException('Invalid file identifier given. It must be of type string and not empty. "' . gettype($identifier) . '" given.', 1401732564);
403  }
404  $parts = GeneralUtility::trimExplode(':', $identifier);
405  if (count($parts) === 2) {
406  $storageUid = $parts[0];
407  $fileIdentifier = $parts[1];
408  } else {
409  // We only got a path: Go into backwards compatibility mode and
410  // use virtual Storage (uid=0)
411  $storageUid = 0;
412  $fileIdentifier = $parts[0];
413  }
414 
415  // please note that getStorageObject() might modify $fileIdentifier when
416  // auto-detecting the best-matching storage to use
417  return $this->getFileObjectByStorageAndIdentifier($storageUid, $fileIdentifier);
418  }
419 
429  public function getFileObjectByStorageAndIdentifier($storageUid, &$fileIdentifier)
430  {
431  $storage = $this->getStorageObject($storageUid, [], $fileIdentifier);
432  if (!$storage->isWithinProcessingFolder($fileIdentifier)) {
433  $fileData = $this->getFileIndexRepository()->findOneByStorageUidAndIdentifier($storage->getUid(), $fileIdentifier);
434  if ($fileData === false) {
435  $fileObject = $this->getIndexer($storage)->createIndexEntry($fileIdentifier);
436  } else {
437  $fileObject = $this->getFileObject($fileData['uid'], $fileData);
438  }
439  } else {
440  $fileObject = $this->getProcessedFileRepository()->findByStorageAndIdentifier($storage, $fileIdentifier);
441  }
442 
443  return $fileObject;
444  }
445 
466  public function retrieveFileOrFolderObject($input)
467  {
468  // Remove PATH_site because absolute paths under Windows systems contain ':'
469  // This is done in all considered sub functions anyway
470  $input = str_replace(PATH_site, '', $input);
471 
472  if (GeneralUtility::isFirstPartOfStr($input, 'file:')) {
473  $input = substr($input, 5);
474  return $this->retrieveFileOrFolderObject($input);
475  } elseif (MathUtility::canBeInterpretedAsInteger($input)) {
476  return $this->getFileObject($input);
477  } elseif (strpos($input, ':') > 0) {
478  list($prefix) = explode(':', $input);
480  // path or folder in a valid storageUID
481  return $this->getObjectFromCombinedIdentifier($input);
482  } elseif ($prefix == 'EXT') {
483  $input = GeneralUtility::getFileAbsFileName($input);
484  if (empty($input)) {
485  return null;
486  }
487 
488  $input = PathUtility::getRelativePath(PATH_site, PathUtility::dirname($input)) . PathUtility::basename($input);
489  return $this->getFileObjectFromCombinedIdentifier($input);
490  } else {
491  return null;
492  }
493  } else {
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  } else {
501  // only the local path
502  return $this->getFolderObjectFromCombinedIdentifier($input);
503  }
504  }
505  }
506 
514  public function getFolderObjectFromCombinedIdentifier($identifier)
515  {
516  $parts = GeneralUtility::trimExplode(':', $identifier);
517  if (count($parts) === 2) {
518  $storageUid = $parts[0];
519  $folderIdentifier = $parts[1];
520  } else {
521  // We only got a path: Go into backwards compatibility mode and
522  // use virtual Storage (uid=0)
523  $storageUid = 0;
524 
525  // please note that getStorageObject() might modify $folderIdentifier when
526  // auto-detecting the best-matching storage to use
527  $folderIdentifier = $parts[0];
528  // make sure to not use an absolute path, and remove PATH_site if it is prepended
529  if (GeneralUtility::isFirstPartOfStr($folderIdentifier, PATH_site)) {
530  $folderIdentifier = PathUtility::stripPathSitePrefix($parts[0]);
531  }
532  }
533  return $this->getStorageObject($storageUid, [], $folderIdentifier)->getFolder($folderIdentifier);
534  }
535 
542  public function getStorageObjectFromCombinedIdentifier($identifier)
543  {
544  $parts = GeneralUtility::trimExplode(':', $identifier);
545  $storageUid = count($parts) === 2 ? $parts[0] : null;
546  return $this->getStorageObject($storageUid);
547  }
548 
557  public function getObjectFromCombinedIdentifier($identifier)
558  {
559  list($storageId, $objectIdentifier) = GeneralUtility::trimExplode(':', $identifier);
560  $storage = $this->getStorageObject($storageId);
561  if ($storage->hasFile($objectIdentifier)) {
562  return $storage->getFile($objectIdentifier);
563  } elseif ($storage->hasFolder($objectIdentifier)) {
564  return $storage->getFolder($objectIdentifier);
565  } else {
566  throw new Exception\ResourceDoesNotExistException('Object with identifier "' . $identifier . '" does not exist in storage', 1329647780);
567  }
568  }
569 
578  public function createFileObject(array $fileData, ResourceStorage $storage = null)
579  {
581  if (array_key_exists('storage', $fileData) && MathUtility::canBeInterpretedAsInteger($fileData['storage'])) {
582  $storageObject = $this->getStorageObject((int)$fileData['storage']);
583  } elseif ($storage !== null) {
584  $storageObject = $storage;
585  $fileData['storage'] = $storage->getUid();
586  } else {
587  throw new \RuntimeException('A file needs to reside in a Storage', 1381570997);
588  }
589  $fileObject = GeneralUtility::makeInstance(File::class, $fileData, $storageObject);
590  return $fileObject;
591  }
592 
604  public function getFileReferenceObject($uid, array $fileReferenceData = [], $raw = false)
605  {
606  if (!is_numeric($uid)) {
607  throw new \InvalidArgumentException(
608  'The reference UID for the file (sys_file_reference) has to be numeric. UID given: "' . $uid . '"',
609  1300086584
610  );
611  }
612  if (!$this->fileReferenceInstances[$uid]) {
613  // Fetches data in case $fileData is empty
614  if (empty($fileReferenceData)) {
615  $fileReferenceData = $this->getFileReferenceData($uid, $raw);
616  if (!is_array($fileReferenceData)) {
617  throw new Exception\ResourceDoesNotExistException(
618  'No file reference (sys_file_reference) was found for given UID: "' . $uid . '"',
619  1317178794
620  );
621  }
622  }
623  $this->fileReferenceInstances[$uid] = $this->createFileReferenceObject($fileReferenceData);
624  }
625  return $this->fileReferenceInstances[$uid];
626  }
627 
636  public function createFileReferenceObject(array $fileReferenceData)
637  {
639  $fileReferenceObject = GeneralUtility::makeInstance(FileReference::class, $fileReferenceData);
640  return $fileReferenceObject;
641  }
642 
650  protected function getFileReferenceData($uid, $raw = false)
651  {
652  if (!$raw && TYPO3_MODE === 'BE') {
653  $fileReferenceData = BackendUtility::getRecordWSOL('sys_file_reference', $uid);
654  } elseif (!$raw && is_object($GLOBALS['TSFE'])) {
655  $fileReferenceData = $GLOBALS['TSFE']->sys_page->checkRecord('sys_file_reference', $uid);
656  } else {
657  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_file_reference');
658  $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
659  $fileReferenceData = $queryBuilder->select('*')
660  ->from('sys_file_reference')
661  ->where(
662  $queryBuilder->expr()->eq(
663  'uid',
664  $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)
665  )
666  )
667  ->execute()
668  ->fetch();
669  }
670  return $fileReferenceData;
671  }
672 
678  protected function getFileIndexRepository()
679  {
681  }
682 
688  protected function getProcessedFileRepository()
689  {
690  return GeneralUtility::makeInstance(ProcessedFileRepository::class);
691  }
692 
699  protected function getIndexer(ResourceStorage $storage)
700  {
701  return GeneralUtility::makeInstance(Index\Indexer::class, $storage);
702  }
703 }
createStorageObject(array $storageRecord, array $storageConfiguration=null)
static getRecordWSOL($table, $uid, $fields= '*', $where= '', $useDeleteClause=true, $unsetMovePointers=false)
getCollectionObject($uid, array $recordData=[])
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)
if(TYPO3_MODE=== 'BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
static getRelativePath($sourcePath, $targetPath)
Definition: PathUtility.php:70
static makeInstance($className,...$constructorArguments)
emitPostProcessStorageSignal(ResourceStorage $storageObject)
static getFileAbsFileName($filename, $_=null, $_2=null)
static getCommonPrefix(array $paths)