diff --git a/typo3/sysext/backend/Classes/Form/Element/UserSysFileStorageIsPublicElement.php b/typo3/sysext/backend/Classes/Form/Element/UserSysFileStorageIsPublicElement.php index 620b1c4d313e6adc671782951b95ff92c6bb4370..203254d8f2173fd643c7c677837d48631c7ed727 100644 --- a/typo3/sysext/backend/Classes/Form/Element/UserSysFileStorageIsPublicElement.php +++ b/typo3/sysext/backend/Classes/Form/Element/UserSysFileStorageIsPublicElement.php @@ -21,7 +21,7 @@ use TYPO3\CMS\Core\Localization\LanguageService; use TYPO3\CMS\Core\Messaging\FlashMessage; use TYPO3\CMS\Core\Messaging\FlashMessageService; use TYPO3\CMS\Core\Resource\Exception\InvalidPathException; -use TYPO3\CMS\Core\Resource\ResourceFactory; +use TYPO3\CMS\Core\Resource\StorageRepository; use TYPO3\CMS\Core\Utility\GeneralUtility; /** @@ -61,7 +61,7 @@ class UserSysFileStorageIsPublicElement extends AbstractFormElement $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class); $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier(); try { - $storage = GeneralUtility::makeInstance(ResourceFactory::class)->getStorageObject((int)$row['uid']); + $storage = GeneralUtility::makeInstance(StorageRepository::class)->findByUid((int)$row['uid']); $storageRecord = $storage->getStorageRecord(); $isPublic = $storage->isPublic() && $storageRecord['is_public']; diff --git a/typo3/sysext/core/Classes/Resource/File.php b/typo3/sysext/core/Classes/Resource/File.php index e4607c041ca4d86c9cdc21af1b19f7085f16f7c6..1863dd28ad4e53fabbf48d6bd7fb9f6403b6c58c 100644 --- a/typo3/sysext/core/Classes/Resource/File.php +++ b/typo3/sysext/core/Classes/Resource/File.php @@ -195,7 +195,7 @@ class File extends AbstractFile $this->getType(); } if (array_key_exists('storage', $properties) && in_array('storage', $this->updatedProperties)) { - $this->storage = GeneralUtility::makeInstance(ResourceFactory::class)->getStorageObject($properties['storage']); + $this->storage = GeneralUtility::makeInstance(StorageRepository::class)->findByUid((int)$properties['storage']); } } diff --git a/typo3/sysext/core/Classes/Resource/Index/FileIndexRepository.php b/typo3/sysext/core/Classes/Resource/Index/FileIndexRepository.php index fa3a17d1cb17a1a020cd7c1c4b725117c31fc82a..afe14c8dcd03241703342de69973b8dfa5202fe1 100644 --- a/typo3/sysext/core/Classes/Resource/Index/FileIndexRepository.php +++ b/typo3/sysext/core/Classes/Resource/Index/FileIndexRepository.php @@ -26,7 +26,6 @@ use TYPO3\CMS\Core\Resource\Event\AfterFileUpdatedInIndexEvent; use TYPO3\CMS\Core\Resource\File; use TYPO3\CMS\Core\Resource\FileInterface; use TYPO3\CMS\Core\Resource\Folder; -use TYPO3\CMS\Core\Resource\ResourceFactory; use TYPO3\CMS\Core\Resource\ResourceStorage; use TYPO3\CMS\Core\SingletonInterface; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -61,16 +60,6 @@ class FileIndexRepository implements SingletonInterface 'mime_type', 'name', 'sha1', 'size', 'creation_date', 'modification_date', 'folder_hash' ]; - /** - * Gets the Resource Factory - * - * @return ResourceFactory - */ - protected function getResourceFactory() - { - return GeneralUtility::makeInstance(ResourceFactory::class); - } - /** * Returns an Instance of the Repository * @@ -86,18 +75,6 @@ class FileIndexRepository implements SingletonInterface $this->eventDispatcher = $eventDispatcher; } - /** - * Retrieves Index record for a given $combinedIdentifier - * - * @param string $combinedIdentifier - * @return array|bool - */ - public function findOneByCombinedIdentifier($combinedIdentifier) - { - [$storageUid, $identifier] = GeneralUtility::trimExplode(':', $combinedIdentifier, false, 2); - return $this->findOneByStorageUidAndIdentifier($storageUid, $identifier); - } - /** * Retrieves Index record for a given $fileUid * @@ -121,21 +98,6 @@ class FileIndexRepository implements SingletonInterface return is_array($row) ? $row : false; } - /** - * Retrieves Index record for a given $storageUid and $identifier - * - * @param int $storageUid - * @param string $identifier - * @return array|bool - * - * @internal only for use from FileRepository - */ - public function findOneByStorageUidAndIdentifier($storageUid, $identifier) - { - $identifierHash = $this->getResourceFactory()->getStorageObject($storageUid)->hashFileIdentifier($identifier); - return $this->findOneByStorageUidAndIdentifierHash($storageUid, $identifierHash); - } - /** * Retrieves Index record for a given $storageUid and $identifier * @@ -163,6 +125,21 @@ class FileIndexRepository implements SingletonInterface return is_array($row) ? $row : false; } + /** + * Retrieves Index record for a given $storageUid and $identifier + * + * @param ResourceStorage $storage + * @param string $identifier + * @return array|bool + * + * @internal only for use from FileRepository + */ + public function findOneByStorageAndIdentifier(ResourceStorage $storage, $identifier) + { + $identifierHash = $storage->hashFileIdentifier($identifier); + return $this->findOneByStorageUidAndIdentifierHash($storage->getUid(), $identifierHash); + } + /** * Retrieves Index record for a given $fileObject * @@ -173,9 +150,7 @@ class FileIndexRepository implements SingletonInterface */ public function findOneByFileObject(FileInterface $fileObject) { - $storageUid = $fileObject->getStorage()->getUid(); - $identifierHash = $fileObject->getHashedIdentifier(); - return $this->findOneByStorageUidAndIdentifierHash($storageUid, $identifierHash); + return $this->findOneByStorageAndIdentifier($fileObject->getStorage(), $fileObject->getIdentifier()); } /** diff --git a/typo3/sysext/core/Classes/Resource/Index/Indexer.php b/typo3/sysext/core/Classes/Resource/Index/Indexer.php index c9a156fa2b20b1decdc2a3fd21c9b5cdb09f341e..c72d91d7a3ac590757678979a004e02e1649188f 100644 --- a/typo3/sysext/core/Classes/Resource/Index/Indexer.php +++ b/typo3/sysext/core/Classes/Resource/Index/Indexer.php @@ -213,7 +213,7 @@ class Indexer implements LoggerAwareInterface // Get the modification time for file-identifier from the storage $modificationTime = $this->storage->getFileInfoByIdentifier($fileIdentifier, ['mtime']); // Look if the the modification time in FS is higher than the one in database (key needed on timestamps) - $indexRecord = $this->getFileIndexRepository()->findOneByStorageUidAndIdentifier($this->storage->getUid(), $fileIdentifier); + $indexRecord = $this->getFileIndexRepository()->findOneByStorageAndIdentifier($this->storage, $fileIdentifier); if ($indexRecord !== false) { $this->identifiedFileUids[] = $indexRecord['uid']; diff --git a/typo3/sysext/core/Classes/Resource/ProcessedFile.php b/typo3/sysext/core/Classes/Resource/ProcessedFile.php index db2990e81926ce276b7370d61636aaf2c2c24160..48563f109b32e9caa26843e9a10837367d41f475 100644 --- a/typo3/sysext/core/Classes/Resource/ProcessedFile.php +++ b/typo3/sysext/core/Classes/Resource/ProcessedFile.php @@ -143,7 +143,7 @@ class ProcessedFile extends AbstractFile $this->properties = $databaseRow; if (!empty($databaseRow['storage']) && (int)$this->storage->getUid() !== (int)$databaseRow['storage']) { - $this->storage = GeneralUtility::makeInstance(ResourceFactory::class)->getStorageObject($databaseRow['storage']); + $this->storage = GeneralUtility::makeInstance(StorageRepository::class)->findByUid($databaseRow['storage']); } } diff --git a/typo3/sysext/core/Classes/Resource/ProcessedFileRepository.php b/typo3/sysext/core/Classes/Resource/ProcessedFileRepository.php index 7a3c2f31d08a427e0417047c3f7d8d47c89803fc..8f1e2cf6fe2c57f913c3ac5d33400ef837d841bb 100644 --- a/typo3/sysext/core/Classes/Resource/ProcessedFileRepository.php +++ b/typo3/sysext/core/Classes/Resource/ProcessedFileRepository.php @@ -88,7 +88,6 @@ class ProcessedFileRepository extends AbstractRepository implements LoggerAwareI protected function createDomainObject(array $databaseRow) { $originalFile = $this->factory->getFileObject((int)$databaseRow['original']); - $originalFile->setStorage($this->factory->getStorageObject($originalFile->getProperty('storage'))); $taskType = $databaseRow['task_type']; // Allow deserialization of Area class, since Area objects get serialized in configuration // TODO: This should be changed to json encode and decode at some point diff --git a/typo3/sysext/core/Classes/Resource/ResourceFactory.php b/typo3/sysext/core/Classes/Resource/ResourceFactory.php index 926de99a915a79c2f46c663edd96b960d2076b19..9e8b325a3fcf63b916263842ecfd75952b1564cd 100644 --- a/typo3/sysext/core/Classes/Resource/ResourceFactory.php +++ b/typo3/sysext/core/Classes/Resource/ResourceFactory.php @@ -15,19 +15,15 @@ namespace TYPO3\CMS\Core\Resource; -use Psr\EventDispatcher\EventDispatcherInterface; use TYPO3\CMS\Backend\Utility\BackendUtility; +use TYPO3\CMS\Core\Collection\CollectionInterface; use TYPO3\CMS\Core\Core\Environment; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction; use TYPO3\CMS\Core\Resource\Collection\FileCollectionRegistry; -use TYPO3\CMS\Core\Resource\Driver\DriverRegistry; -use TYPO3\CMS\Core\Resource\Event\AfterResourceStorageInitializationEvent; -use TYPO3\CMS\Core\Resource\Event\BeforeResourceStorageInitializationEvent; use TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException; use TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException; use TYPO3\CMS\Core\Resource\Index\FileIndexRepository; -use TYPO3\CMS\Core\Resource\Index\Indexer; use TYPO3\CMS\Core\Service\FlexFormService; use TYPO3\CMS\Core\SingletonInterface; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -39,11 +35,6 @@ use TYPO3\CMS\Core\Utility\PathUtility; */ class ResourceFactory implements SingletonInterface { - /** - * @var ResourceStorage[] - */ - protected $storageInstances = []; - /** * @var Collection\AbstractFileCollection[] */ @@ -60,40 +51,13 @@ class ResourceFactory implements SingletonInterface protected $fileReferenceInstances = []; /** - * A list of the base paths of "local" driver storages. Used to make the detection of base paths easier. - * - * @var array - */ - protected $localDriverStorageCache; - - /** - * @var EventDispatcherInterface - */ - protected $eventDispatcher; - - /** - * @param EventDispatcherInterface $eventDispatcher + * @var StorageRepository */ - public function __construct(EventDispatcherInterface $eventDispatcher) - { - $this->eventDispatcher = $eventDispatcher; - } + protected $storageRepository; - /** - * Creates a driver object for a specified storage object. - * - * @param string $driverIdentificationString The driver class (or identifier) to use. - * @param array $driverConfiguration The configuration of the storage - * @return Driver\DriverInterface - * @throws \InvalidArgumentException - */ - public function getDriverObject($driverIdentificationString, array $driverConfiguration) + public function __construct(StorageRepository $storageRepository) { - /** @var Driver\DriverRegistry $driverRegistry */ - $driverRegistry = GeneralUtility::makeInstance(DriverRegistry::class); - $driverClass = $driverRegistry->getDriverClass($driverIdentificationString); - $driverObject = GeneralUtility::makeInstance($driverClass, $driverConfiguration); - return $driverObject; + $this->storageRepository = $storageRepository; } /** @@ -105,20 +69,13 @@ class ResourceFactory implements SingletonInterface * TYPO3 installation. * * @return ResourceStorage|null + * @internal It is recommended to use the StorageRepository in the future, and this is only kept as backwards-compat layer */ public function getDefaultStorage() { - /** @var StorageRepository $storageRepository */ - $storageRepository = GeneralUtility::makeInstance(StorageRepository::class); - - $allStorages = $storageRepository->findAll(); - foreach ($allStorages as $storage) { - if ($storage->isDefault()) { - return $storage; - } - } - return null; + return $this->storageRepository->getDefaultStorage(); } + /** * Creates an instance of the storage from given UID. The $recordData can * be supplied to increase performance. @@ -129,114 +86,22 @@ class ResourceFactory implements SingletonInterface * * @throws \InvalidArgumentException * @return ResourceStorage + * @internal It is recommended to use the StorageRepository in the future, and this is only kept as backwards-compat layer */ public function getStorageObject($uid, array $recordData = [], &$fileIdentifier = null) { - if (!is_numeric($uid)) { - throw new \InvalidArgumentException('The UID of storage has to be numeric. UID given: "' . $uid . '"', 1314085991); - } - $uid = (int)$uid; - if ($uid === 0 && $fileIdentifier !== null) { - $uid = $this->findBestMatchingStorageByLocalPath($fileIdentifier); - } - if (empty($this->storageInstances[$uid])) { - $storageConfiguration = null; - /** @var BeforeResourceStorageInitializationEvent $event */ - $event = $this->eventDispatcher->dispatch(new BeforeResourceStorageInitializationEvent($uid, $recordData, $fileIdentifier)); - $recordData = $event->getRecord(); - $uid = $event->getStorageUid(); - $fileIdentifier = $event->getFileIdentifier(); - // If the built-in storage with UID=0 is requested: - if ($uid === 0) { - $recordData = [ - 'uid' => 0, - 'pid' => 0, - 'name' => 'Fallback Storage', - 'description' => 'Internal storage, mounting the main TYPO3_site directory.', - 'driver' => 'Local', - 'processingfolder' => 'typo3temp/assets/_processed_/', - // legacy code - 'configuration' => '', - 'is_online' => true, - 'is_browsable' => true, - 'is_public' => true, - 'is_writable' => true, - 'is_default' => false, - ]; - $storageConfiguration = [ - 'basePath' => '/', - 'pathType' => 'relative' - ]; - } elseif ($recordData === [] || (int)$recordData['uid'] !== $uid) { - /** @var StorageRepository $storageRepository */ - $storageRepository = GeneralUtility::makeInstance(StorageRepository::class); - $recordData = $storageRepository->fetchRowByUid($uid); - } - $storageObject = $this->createStorageObject($recordData, $storageConfiguration); - $storageObject = $this->eventDispatcher - ->dispatch(new AfterResourceStorageInitializationEvent($storageObject)) - ->getStorage(); - $this->storageInstances[$uid] = $storageObject; - } - return $this->storageInstances[$uid]; + return $this->storageRepository->getStorageObject($uid, $recordData, $fileIdentifier); } /** - * Checks whether a file resides within a real storage in local file system. - * If no match is found, uid 0 is returned which is a fallback storage pointing to fileadmin in public web path. - * - * The file identifier is adapted accordingly to match the new storage's base path. + * Converts a flexform data string to a flat array with key value pairs. * - * @param string $localPath - * - * @return int - */ - protected function findBestMatchingStorageByLocalPath(&$localPath) - { - if ($this->localDriverStorageCache === null) { - $this->initializeLocalStorageCache(); - } - - $bestMatchStorageUid = 0; - $bestMatchLength = 0; - foreach ($this->localDriverStorageCache as $storageUid => $basePath) { - $matchLength = strlen(PathUtility::getCommonPrefix([$basePath, $localPath])); - $basePathLength = strlen($basePath); - - if ($matchLength >= $basePathLength && $matchLength > $bestMatchLength) { - $bestMatchStorageUid = (int)$storageUid; - $bestMatchLength = $matchLength; - } - } - if ($bestMatchStorageUid !== 0) { - $localPath = substr($localPath, $bestMatchLength); - } - return $bestMatchStorageUid; - } - - /** - * Creates an array mapping all uids to the basePath of storages using the "local" driver. - */ - protected function initializeLocalStorageCache() - { - /** @var StorageRepository $storageRepository */ - $storageRepository = GeneralUtility::makeInstance(StorageRepository::class); - /** @var ResourceStorage[] $storageObjects */ - $storageObjects = $storageRepository->findByStorageType('Local'); - - $storageCache = []; - foreach ($storageObjects as $localStorage) { - $configuration = $localStorage->getConfiguration(); - $storageCache[$localStorage->getUid()] = $configuration['basePath']; - } - $this->localDriverStorageCache = $storageCache; - } - - /** - * Converts a flexform data string to a flat array with key value pairs + * It is recommended to not use this functionality directly, and instead implement this code yourself, as this + * code has nothing to do with a Public API for Resources. * * @param string $flexFormData * @return array Array with key => value pairs of the field data in the FlexForm + * @internal */ public function convertFlexFormDataToConfigurationArray($flexFormData) { @@ -291,7 +156,7 @@ class ResourceFactory implements SingletonInterface * Creates a collection object. * * @param array $collectionData The database row of the sys_file_collection record. - * @return Collection\AbstractFileCollection + * @return Collection\AbstractFileCollection|CollectionInterface */ public function createCollectionObject(array $collectionData) { @@ -308,17 +173,13 @@ class ResourceFactory implements SingletonInterface * Creates a storage object from a storage database row. * * @param array $storageRecord - * @param array $storageConfiguration Storage configuration (if given, this won't be extracted from the FlexForm value but the supplied array used instead) + * @param array|null $storageConfiguration Storage configuration (if given, this won't be extracted from the FlexForm value but the supplied array used instead) * @return ResourceStorage + * @internal It is recommended to use the StorageRepository in the future, and this is only kept as backwards-compat layer */ public function createStorageObject(array $storageRecord, array $storageConfiguration = null) { - if (!$storageConfiguration) { - $storageConfiguration = $this->convertFlexFormDataToConfigurationArray($storageRecord['configuration']); - } - $driverType = $storageRecord['driver']; - $driverObject = $this->getDriverObject($driverType, $storageConfiguration); - return GeneralUtility::makeInstance(ResourceStorage::class, $driverObject, $storageRecord, $this->eventDispatcher); + return $this->storageRepository->createStorageObject($storageRecord, $storageConfiguration); } /** @@ -328,6 +189,7 @@ class ResourceFactory implements SingletonInterface * @param string $identifier The path to the folder. Might also be a simple unique string, depending on the storage driver. * @param string $name The name of the folder (e.g. the folder name) * @return Folder + * @internal it is recommended to access the ResourceStorage object directly and access ->getFolder($identifier) this method is kept for backwards compatibility */ public function createFolderObject(ResourceStorage $storage, $identifier, $name) { @@ -385,10 +247,8 @@ class ResourceFactory implements SingletonInterface $storageUid = 0; $fileIdentifier = $parts[0]; } - - // please note that getStorageObject() might modify $fileIdentifier when - // auto-detecting the best-matching storage to use - return $this->getFileObjectByStorageAndIdentifier($storageUid, $fileIdentifier); + return $this->storageRepository->getStorageObject($storageUid, [], $fileIdentifier) + ->getFileByIdentifier($fileIdentifier); } /** @@ -396,25 +256,17 @@ class ResourceFactory implements SingletonInterface * If the file is outside of the process folder, it gets indexed and returned as file object afterwards * If the file is within processing folder, the file object will be directly returned * - * @param int $storageUid + * @param ResourceStorage|int $storage * @param string $fileIdentifier * @return File|ProcessedFile|null + * @internal It is recommended to use the StorageRepository in the future, and this is only kept as backwards-compat layer */ - public function getFileObjectByStorageAndIdentifier($storageUid, &$fileIdentifier) + public function getFileObjectByStorageAndIdentifier($storage, &$fileIdentifier) { - $storage = $this->getStorageObject($storageUid, [], $fileIdentifier); - if (!$storage->isWithinProcessingFolder($fileIdentifier)) { - $fileData = $this->getFileIndexRepository()->findOneByStorageUidAndIdentifier($storage->getUid(), $fileIdentifier); - if ($fileData === false) { - $fileObject = $this->getIndexer($storage)->createIndexEntry($fileIdentifier); - } else { - $fileObject = $this->getFileObject($fileData['uid'], $fileData); - } - } else { - $fileObject = $this->getProcessedFileRepository()->findByStorageAndIdentifier($storage, $fileIdentifier); + if (!($storage instanceof ResourceStorage)) { + $storage = $this->storageRepository->getStorageObject($storage, [], $fileIdentifier); } - - return $fileObject; + return $storage->getFileByIdentifier($fileIdentifier); } /** @@ -504,7 +356,7 @@ class ResourceFactory implements SingletonInterface $folderIdentifier = PathUtility::stripPathSitePrefix($parts[0]); } } - return $this->getStorageObject($storageUid, [], $folderIdentifier)->getFolder($folderIdentifier); + return $this->storageRepository->getStorageObject($storageUid, [], $folderIdentifier)->getFolder($folderIdentifier); } /** @@ -512,12 +364,13 @@ class ResourceFactory implements SingletonInterface * * @param string $identifier An identifier of the form [storage uid]:[object identifier] * @return ResourceStorage + * @internal It is recommended to use the StorageRepository in the future, and this is only kept as backwards-compat layer */ public function getStorageObjectFromCombinedIdentifier($identifier) { $parts = GeneralUtility::trimExplode(':', $identifier); $storageUid = count($parts) === 2 ? $parts[0] : null; - return $this->getStorageObject($storageUid); + return $this->storageRepository->findByUid($storageUid); } /** @@ -531,7 +384,7 @@ class ResourceFactory implements SingletonInterface public function getObjectFromCombinedIdentifier($identifier) { [$storageId, $objectIdentifier] = GeneralUtility::trimExplode(':', $identifier); - $storage = $this->getStorageObject($storageId); + $storage = $this->storageRepository->findByUid($storageId); if ($storage->hasFile($objectIdentifier)) { return $storage->getFile($objectIdentifier); } @@ -552,7 +405,7 @@ class ResourceFactory implements SingletonInterface public function createFileObject(array $fileData, ResourceStorage $storage = null) { if (array_key_exists('storage', $fileData) && MathUtility::canBeInterpretedAsInteger($fileData['storage'])) { - $storageObject = $this->getStorageObject((int)$fileData['storage']); + $storageObject = $this->storageRepository->findByUid((int)$fileData['storage']); } elseif ($storage !== null) { $storageObject = $storage; $fileData['storage'] = $storage->getUid(); @@ -653,25 +506,4 @@ class ResourceFactory implements SingletonInterface { return FileIndexRepository::getInstance(); } - - /** - * Returns an instance of the ProcessedFileRepository - * - * @return ProcessedFileRepository - */ - protected function getProcessedFileRepository() - { - return GeneralUtility::makeInstance(ProcessedFileRepository::class); - } - - /** - * Returns an instance of the Indexer - * - * @param ResourceStorage $storage - * @return Index\Indexer - */ - protected function getIndexer(ResourceStorage $storage) - { - return GeneralUtility::makeInstance(Indexer::class, $storage); - } } diff --git a/typo3/sysext/core/Classes/Resource/ResourceStorage.php b/typo3/sysext/core/Classes/Resource/ResourceStorage.php index 5edbefa2e45c751aef8d60027343e89779cbc7ce..d1667f59478627994289442de88ff5835471ad39 100644 --- a/typo3/sysext/core/Classes/Resource/ResourceStorage.php +++ b/typo3/sysext/core/Classes/Resource/ResourceStorage.php @@ -80,6 +80,7 @@ use TYPO3\CMS\Core\Resource\Search\Result\FileSearchResult; use TYPO3\CMS\Core\Resource\Search\Result\FileSearchResultInterface; use TYPO3\CMS\Core\Resource\Security\FileNameValidator; use TYPO3\CMS\Core\Resource\Service\FileProcessingService; +use TYPO3\CMS\Core\Service\FlexFormService; use TYPO3\CMS\Core\Utility\Exception\NotImplementedMethodException; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\PathUtility; @@ -230,7 +231,13 @@ class ResourceStorage implements ResourceStorageInterface { $this->storageRecord = $storageRecord; $this->eventDispatcher = $eventDispatcher ?? GeneralUtility::getContainer()->get(EventDispatcherInterface::class); - $this->configuration = $this->getResourceFactoryInstance()->convertFlexFormDataToConfigurationArray($storageRecord['configuration'] ?? ''); + if (is_array($storageRecord['configuration'] ?? null)) { + $this->configuration = $storageRecord['configuration']; + } elseif (!empty($storageRecord['configuration'] ?? '')) { + $this->configuration = GeneralUtility::makeInstance(FlexFormService::class)->convertFlexFormContentToArray($storageRecord['configuration']); + } else { + $this->configuration = []; + } $this->capabilities = ($this->storageRecord['is_browsable'] ?? null ? self::CAPABILITY_BROWSABLE : 0) | ($this->storageRecord['is_public'] ?? null ? self::CAPABILITY_PUBLIC : 0) | @@ -555,7 +562,7 @@ class ResourceStorage implements ResourceStorageInterface throw new FolderDoesNotExistException('Folder for file mount ' . $folderIdentifier . ' does not exist.', 1334427099); } $data = $this->driver->getFolderInfoByIdentifier($folderIdentifier); - $folderObject = $this->getResourceFactoryInstance()->createFolderObject($this, $data['identifier'], $data['name']); + $folderObject = $this->createFolderObject($data['identifier'], $data['name']); // Use the canonical identifier instead of the user provided one! $folderIdentifier = $folderObject->getIdentifier(); if ( @@ -1260,7 +1267,7 @@ class ResourceStorage implements ResourceStorageInterface } $fileIdentifier = $this->driver->addFile($localFilePath, $targetFolder->getIdentifier(), $targetFileName, $removeOriginal); - $file = $this->getResourceFactoryInstance()->getFileObjectByStorageAndIdentifier($this->getUid(), $fileIdentifier); + $file = $this->getFileByIdentifier($fileIdentifier); if ($replaceExisting && $file instanceof File) { $this->getIndexer()->updateIndexEntry($file); @@ -1443,13 +1450,38 @@ class ResourceStorage implements ResourceStorageInterface */ public function getFile($identifier) { - $file = $this->getFileFactory()->getFileObjectByStorageAndIdentifier($this->getUid(), $identifier); + $file = $this->getFileByIdentifier($identifier); if (!$this->driver->fileExists($identifier)) { $file->setMissing(true); } return $file; } + /** + * Gets a file object from storage by file identifier + * If the file is outside of the process folder, it gets indexed and returned as file object afterwards + * If the file is within processing folder, the file object will be directly returned + * + * @param string $fileIdentifier + * @return File|ProcessedFile|null + */ + public function getFileByIdentifier(string $fileIdentifier) + { + if (!$this->isWithinProcessingFolder($fileIdentifier)) { + $fileData = $this->getFileIndexRepository()->findOneByStorageAndIdentifier($this, $fileIdentifier); + if ($fileData === false) { + return $this->getIndexer()->createIndexEntry($fileIdentifier); + } + return $this->getResourceFactoryInstance()->getFileObject($fileData['uid'], $fileData); + } + return $this->getProcessedFileRepository()->findByStorageAndIdentifier($this, $fileIdentifier); + } + + protected function getProcessedFileRepository(): ProcessedFileRepository + { + return GeneralUtility::makeInstance(ProcessedFileRepository::class); + } + /** * Gets information about a file. * @@ -1539,7 +1571,7 @@ class ResourceStorage implements ResourceStorageInterface public function getFileInFolder($fileName, Folder $folder) { $identifier = $this->driver->getFileInFolder($fileName, $folder->getIdentifier()); - return $this->getFileFactory()->getFileObjectByStorageAndIdentifier($this->getUid(), $identifier); + return $this->getFileByIdentifier($identifier); } /** @@ -1571,7 +1603,7 @@ class ResourceStorage implements ResourceStorageInterface if (isset($rows[$identifier])) { $fileObject = $this->getFileFactory()->getFileObject($rows[$identifier]['uid'], $rows[$identifier]); } else { - $fileObject = $this->getFileFactory()->getFileObjectByStorageAndIdentifier($this->getUid(), $identifier); + $fileObject = $this->getFileByIdentifier($identifier); } if ($fileObject instanceof FileInterface) { $key = $fileObject->getName(); @@ -1658,7 +1690,7 @@ class ResourceStorage implements ResourceStorageInterface if (empty($processingFolderIdentifier) || (int)$storageUid !== $this->getUid()) { continue; } - $potentialProcessingFolder = $this->getResourceFactoryInstance()->createFolderObject($this, $processingFolderIdentifier, $processingFolderIdentifier); + $potentialProcessingFolder = $this->createFolderObject($processingFolderIdentifier, $processingFolderIdentifier); if ($potentialProcessingFolder->getStorage() === $this && $potentialProcessingFolder->getIdentifier() !== $this->getProcessingFolder()->getIdentifier()) { $this->processingFolders[] = $potentialProcessingFolder; } @@ -1824,7 +1856,7 @@ class ResourceStorage implements ResourceStorageInterface $this->eventDispatcher->dispatch( new AfterFileCreatedEvent($newFileIdentifier, $targetFolderObject) ); - return $this->getResourceFactoryInstance()->getFileObjectByStorageAndIdentifier($this->getUid(), $newFileIdentifier); + return $this->getFileByIdentifier($newFileIdentifier); } /** @@ -1919,7 +1951,7 @@ class ResourceStorage implements ResourceStorageInterface $tempPath = $file->getForLocalProcessing(); $newFileObjectIdentifier = $this->driver->addFile($tempPath, $targetFolder->getIdentifier(), $sanitizedTargetFileName); } - $newFileObject = $this->getResourceFactoryInstance()->getFileObjectByStorageAndIdentifier($this->getUid(), $newFileObjectIdentifier); + $newFileObject = $this->getFileByIdentifier($newFileObjectIdentifier); $this->eventDispatcher->dispatch( new AfterFileCopiedEvent($file, $targetFolder, $newFileObjectIdentifier, $newFileObject) @@ -2508,7 +2540,7 @@ class ResourceStorage implements ResourceStorageInterface public function getFolder($identifier, $returnInaccessibleFolderObject = false) { $data = $this->driver->getFolderInfoByIdentifier($identifier); - $folder = $this->getResourceFactoryInstance()->createFolderObject($this, $data['identifier'] ?? null, $data['name'] ?? null); + $folder = $this->createFolderObject($data['identifier'] ?? '', $data['name'] ?? ''); try { $this->assureFolderReadPermission($folder); @@ -2585,7 +2617,7 @@ class ResourceStorage implements ResourceStorageInterface $mount = reset($this->fileMounts); return $mount['folder']; } - return $this->getResourceFactoryInstance()->createFolderObject($this, $this->driver->getRootLevelFolder(), ''); + return $this->createFolderObject($this->driver->getRootLevelFolder(), ''); } /** @@ -2716,7 +2748,7 @@ class ResourceStorage implements ResourceStorageInterface try { if (strpos($processingFolder, ':') !== false) { [$storageUid, $processingFolderIdentifier] = explode(':', $processingFolder, 2); - $storage = $this->getResourceFactoryInstance()->getStorageObject($storageUid); + $storage = GeneralUtility::makeInstance(StorageRepository::class)->findByUid((int)$storageUid); if ($storage->hasFolder($processingFolderIdentifier)) { $this->processingFolder = $storage->getFolder($processingFolderIdentifier); } else { @@ -2750,7 +2782,7 @@ class ResourceStorage implements ResourceStorageInterface } } else { $data = $this->driver->getFolderInfoByIdentifier($processingFolder); - $this->processingFolder = $this->getResourceFactoryInstance()->createFolderObject($this, $data['identifier'], $data['name']); + $this->processingFolder = $this->createFolderObject($data['identifier'], $data['name']); } } } catch (InsufficientFolderWritePermissionsException|ResourcePermissionsUnavailableException $e) { @@ -2921,4 +2953,16 @@ class ResourceStorage implements ResourceStorageInterface return $recyclerFolder; } + + /** + * Creates a folder to directly access (a part of) a storage. + * + * @param string $identifier The path to the folder. Might also be a simple unique string, depending on the storage driver. + * @param string $name The name of the folder (e.g. the folder name) + * @return Folder + */ + protected function createFolderObject(string $identifier, string $name) + { + return GeneralUtility::makeInstance(Folder::class, $this, $identifier, $name); + } } diff --git a/typo3/sysext/core/Classes/Resource/StorageRepository.php b/typo3/sysext/core/Classes/Resource/StorageRepository.php index 78d4ffa78ca713856d45ce4f261b7171256e8b09..aa20cabc26b4e57a52140b51ee09ea65fac7d571 100644 --- a/typo3/sysext/core/Classes/Resource/StorageRepository.php +++ b/typo3/sysext/core/Classes/Resource/StorageRepository.php @@ -1,5 +1,7 @@ <?php +declare(strict_types=1); + /* * This file is part of the TYPO3 CMS project. * @@ -15,19 +17,24 @@ namespace TYPO3\CMS\Core\Resource; +use Psr\EventDispatcher\EventDispatcherInterface; use Psr\Log\LoggerAwareInterface; use Psr\Log\LoggerAwareTrait; use TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools; use TYPO3\CMS\Core\Core\Environment; use TYPO3\CMS\Core\Database\ConnectionPool; -use TYPO3\CMS\Core\Database\Query\Restriction\FrontendRestrictionContainer; +use TYPO3\CMS\Core\Resource\Driver\DriverInterface; use TYPO3\CMS\Core\Resource\Driver\DriverRegistry; +use TYPO3\CMS\Core\Resource\Event\AfterResourceStorageInitializationEvent; +use TYPO3\CMS\Core\Resource\Event\BeforeResourceStorageInitializationEvent; +use TYPO3\CMS\Core\Service\FlexFormService; use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Core\Utility\PathUtility; /** - * Repository for accessing the file mounts + * Repository for accessing the file storages */ -class StorageRepository extends AbstractRepository implements LoggerAwareInterface +class StorageRepository implements LoggerAwareInterface { use LoggerAwareTrait; @@ -37,9 +44,9 @@ class StorageRepository extends AbstractRepository implements LoggerAwareInterfa protected $storageRowCache; /** - * @var string + * @var array|null */ - protected $objectType = ResourceStorage::class; + protected $localDriverStorageCache; /** * @var string @@ -47,38 +54,73 @@ class StorageRepository extends AbstractRepository implements LoggerAwareInterfa protected $table = 'sys_file_storage'; /** - * @var string + * @var DriverRegistry */ - protected $typeField = 'driver'; + protected $driverRegistry; /** - * @var string + * @var EventDispatcherInterface */ - protected $driverField = 'driver'; + protected $eventDispatcher; /** - * @param int $uid + * @var ResourceStorage[] + */ + protected $storageInstances; + + public function __construct(EventDispatcherInterface $eventDispatcher, DriverRegistry $driverRegistry) + { + $this->eventDispatcher = $eventDispatcher; + $this->driverRegistry = $driverRegistry; + } + + /** + * Returns the Default Storage + * + * The Default Storage is considered to be the replacement for the fileadmin/ construct. + * It is automatically created with the setting fileadminDir from install tool. + * getDefaultStorage->getDefaultFolder() will get you fileadmin/user_upload/ in a standard + * TYPO3 installation. * * @return ResourceStorage|null */ - public function findByUid($uid) + public function getDefaultStorage(): ?ResourceStorage + { + $allStorages = $this->findAll(); + foreach ($allStorages as $storage) { + if ($storage->isDefault()) { + return $storage; + } + } + return null; + } + + public function findByUid(int $uid): ?ResourceStorage { $this->initializeLocalCache(); - if (isset($this->storageRowCache[$uid])) { - return $this->factory->getStorageObject($uid, $this->storageRowCache[$uid]); + if (isset($this->storageRowCache[$uid]) || $uid === 0) { + return $this->getStorageObject($uid, $this->storageRowCache[$uid] ?? []); } return null; } /** - * Only for use in ResourceFactory::getStorageObject + * Gets a storage object from a combined identifier * - * @internal + * @param string $identifier An identifier of the form [storage uid]:[object identifier] + * @return ResourceStorage|null + */ + public function findByCombinedIdentifier(string $identifier): ?ResourceStorage + { + $parts = GeneralUtility::trimExplode(':', $identifier); + return count($parts) === 2 ? $this->findByUid((int)$parts[0]) : null; + } + + /** * @param int $uid - * * @return array */ - public function fetchRowByUid(int $uid): array + protected function fetchRecordDataByUid(int $uid): array { $this->initializeLocalCache(); if (!isset($this->storageRowCache[$uid])) { @@ -97,10 +139,6 @@ class StorageRepository extends AbstractRepository implements LoggerAwareInterfa $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) ->getQueryBuilderForTable($this->table); - if ($this->getEnvironmentMode() === 'FE') { - $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class)); - } - $result = $queryBuilder ->select('*') ->from($this->table) @@ -153,16 +191,13 @@ class StorageRepository extends AbstractRepository implements LoggerAwareInterfa { $this->initializeLocalCache(); - /** @var Driver\DriverRegistry $driverRegistry */ - $driverRegistry = GeneralUtility::makeInstance(DriverRegistry::class); - $storageObjects = []; foreach ($this->storageRowCache as $storageRow) { if ($storageRow['driver'] !== $storageType) { continue; } - if ($driverRegistry->driverExists($storageRow['driver'])) { - $storageObjects[] = $this->factory->getStorageObject($storageRow['uid'], $storageRow); + if ($this->driverRegistry->driverExists($storageRow['driver'])) { + $storageObjects[] = $this->getStorageObject($storageRow['uid'], $storageRow); } else { $this->logger->warning( sprintf('Could not instantiate storage "%s" because of missing driver.', [$storageRow['name']]), @@ -183,13 +218,10 @@ class StorageRepository extends AbstractRepository implements LoggerAwareInterfa { $this->initializeLocalCache(); - /** @var Driver\DriverRegistry $driverRegistry */ - $driverRegistry = GeneralUtility::makeInstance(DriverRegistry::class); - $storageObjects = []; foreach ($this->storageRowCache as $storageRow) { - if ($driverRegistry->driverExists($storageRow['driver'])) { - $storageObjects[] = $this->factory->getStorageObject($storageRow['uid'], $storageRow); + if ($this->driverRegistry->driverExists($storageRow['driver'])) { + $storageObjects[] = $this->getStorageObject($storageRow['uid'], $storageRow); } else { $this->logger->warning( sprintf('Could not instantiate storage "%s" because of missing driver.', [$storageRow['name']]), @@ -226,9 +258,7 @@ class StorageRepository extends AbstractRepository implements LoggerAwareInterfa ] ]; - /** @var FlexFormTools $flexObj */ - $flexObj = GeneralUtility::makeInstance(FlexFormTools::class); - $flexFormXml = $flexObj->flexArray2Xml($flexFormData, true); + $flexFormXml = GeneralUtility::makeInstance(FlexFormTools::class)->flexArray2Xml($flexFormData, true); // create the record $field_values = [ @@ -256,17 +286,6 @@ class StorageRepository extends AbstractRepository implements LoggerAwareInterfa return (int)$dbConnection->lastInsertId($this->table); } - /** - * Creates an object managed by this repository. - * - * @param array $databaseRow - * @return ResourceStorage - */ - protected function createDomainObject(array $databaseRow) - { - return $this->factory->getStorageObject($databaseRow['uid'], $databaseRow); - } - /** * Test if the local filesystem is case sensitive * @@ -296,4 +315,169 @@ class StorageRepository extends AbstractRepository implements LoggerAwareInterfa return $caseSensitive; } + + /** + * Creates an instance of the storage from given UID. The $recordData can + * be supplied to increase performance. + * + * @param int $uid The uid of the storage to instantiate. + * @param array $recordData The record row from database. + * @param mixed|null $fileIdentifier Identifier for a file. Used for auto-detection of a storage, but only if $uid === 0 (Local default storage) is used + * @throws \InvalidArgumentException + * @return ResourceStorage + */ + public function getStorageObject($uid, array $recordData = [], &$fileIdentifier = null): ResourceStorage + { + if (!is_numeric($uid)) { + throw new \InvalidArgumentException('The UID of storage has to be numeric. UID given: "' . $uid . '"', 1314085991); + } + $uid = (int)$uid; + if ($uid === 0 && $fileIdentifier !== null) { + $uid = $this->findBestMatchingStorageByLocalPath($fileIdentifier); + } + if (empty($this->storageInstances[$uid])) { + $storageConfiguration = null; + $storageObject = null; + /** @var BeforeResourceStorageInitializationEvent $event */ + $event = $this->eventDispatcher->dispatch(new BeforeResourceStorageInitializationEvent($uid, $recordData, $fileIdentifier)); + $recordData = $event->getRecord(); + $uid = $event->getStorageUid(); + $fileIdentifier = $event->getFileIdentifier(); + // If the built-in storage with UID=0 is requested: + if ($uid === 0) { + $recordData = [ + 'uid' => 0, + 'pid' => 0, + 'name' => 'Fallback Storage', + 'description' => 'Internal storage, mounting the main TYPO3_site directory.', + 'driver' => 'Local', + 'processingfolder' => 'typo3temp/assets/_processed_/', + // legacy code + 'configuration' => '', + 'is_online' => true, + 'is_browsable' => true, + 'is_public' => true, + 'is_writable' => true, + 'is_default' => false, + ]; + $storageConfiguration = [ + 'basePath' => '/', + 'pathType' => 'relative' + ]; + } elseif ($recordData === [] || (int)$recordData['uid'] !== $uid) { + $recordData = $this->fetchRecordDataByUid($uid); + } + $storageObject = $this->createStorageObject($recordData, $storageConfiguration); + $storageObject = $this->eventDispatcher + ->dispatch(new AfterResourceStorageInitializationEvent($storageObject)) + ->getStorage(); + $this->storageInstances[$uid] = $storageObject; + } + return $this->storageInstances[$uid]; + } + + /** + * Checks whether a file resides within a real storage in local file system. + * If no match is found, uid 0 is returned which is a fallback storage pointing to fileadmin in public web path. + * + * The file identifier is adapted accordingly to match the new storage's base path. + * + * @param string $localPath + * @return int + */ + protected function findBestMatchingStorageByLocalPath(&$localPath): int + { + if ($this->localDriverStorageCache === null) { + $this->initializeLocalStorageCache(); + } + + $bestMatchStorageUid = 0; + $bestMatchLength = 0; + foreach ($this->localDriverStorageCache as $storageUid => $basePath) { + $matchLength = strlen((string)PathUtility::getCommonPrefix([$basePath, $localPath])); + $basePathLength = strlen($basePath); + + if ($matchLength >= $basePathLength && $matchLength > $bestMatchLength) { + $bestMatchStorageUid = (int)$storageUid; + $bestMatchLength = $matchLength; + } + } + if ($bestMatchStorageUid !== 0) { + $localPath = substr($localPath, $bestMatchLength); + } + return $bestMatchStorageUid; + } + + /** + * Creates an array mapping all uids to the basePath of storages using the "local" driver. + */ + protected function initializeLocalStorageCache(): void + { + $storageObjects = $this->findByStorageType('Local'); + + $storageCache = []; + foreach ($storageObjects as $localStorage) { + $configuration = $localStorage->getConfiguration(); + $storageCache[$localStorage->getUid()] = $configuration['basePath']; + } + $this->localDriverStorageCache = $storageCache; + } + + /** + * Creates a storage object from a storage database row. + * + * @param array $storageRecord + * @param array|null $storageConfiguration Storage configuration (if given, this won't be extracted from the FlexForm value but the supplied array used instead) + * @return ResourceStorage + * @internal this method is only public for having access to ResourceFactory->createStorageObject(). In TYPO3 v12 this method can be changed to protected again. + */ + public function createStorageObject(array $storageRecord, array $storageConfiguration = null): ResourceStorage + { + if (!$storageConfiguration && !empty($storageRecord['configuration'])) { + $storageConfiguration = $this->convertFlexFormDataToConfigurationArray($storageRecord['configuration']); + } + $driverType = $storageRecord['driver']; + $driverObject = $this->getDriverObject($driverType, $storageConfiguration); + $storageRecord['configuration'] = $storageConfiguration; + return GeneralUtility::makeInstance(ResourceStorage::class, $driverObject, $storageRecord, $this->eventDispatcher); + } + + /** + * Converts a flexform data string to a flat array with key value pairs + * + * @param string $flexFormData + * @return array Array with key => value pairs of the field data in the FlexForm + */ + protected function convertFlexFormDataToConfigurationArray(string $flexFormData): array + { + if ($flexFormData) { + return GeneralUtility::makeInstance(FlexFormService::class)->convertFlexFormContentToArray($flexFormData); + } + return []; + } + + /** + * Creates a driver object for a specified storage object. + * + * @param string $driverIdentificationString The driver class (or identifier) to use. + * @param array $driverConfiguration The configuration of the storage + * @return DriverInterface + */ + protected function getDriverObject(string $driverIdentificationString, array $driverConfiguration): DriverInterface + { + $driverClass = $this->driverRegistry->getDriverClass($driverIdentificationString); + /** @var DriverInterface $driverObject */ + $driverObject = GeneralUtility::makeInstance($driverClass, $driverConfiguration); + return $driverObject; + } + + /** + * @param array $storageRecord + * @return ResourceStorage + * @internal + */ + public function createFromRecord(array $storageRecord): ResourceStorage + { + return $this->createStorageObject($storageRecord); + } } diff --git a/typo3/sysext/core/Classes/ServiceProvider.php b/typo3/sysext/core/Classes/ServiceProvider.php index b2083eb43f3c6205333bf6f42b45a7b196ea6ba0..db4fd70a1a3748664273b2ba826f5f33d55b2a09 100644 --- a/typo3/sysext/core/Classes/ServiceProvider.php +++ b/typo3/sysext/core/Classes/ServiceProvider.php @@ -265,13 +265,16 @@ class ServiceProvider extends AbstractServiceProvider public static function getResourceFactory(ContainerInterface $container): Resource\ResourceFactory { return self::new($container, Resource\ResourceFactory::class, [ - $container->get(EventDispatcherInterface::class) + $container->get(Resource\StorageRepository::class) ]); } public static function getStorageRepository(ContainerInterface $container): Resource\StorageRepository { - return self::new($container, Resource\StorageRepository::class); + return self::new($container, Resource\StorageRepository::class, [ + $container->get(EventDispatcherInterface::class), + $container->get(Resource\Driver\DriverRegistry::class), + ]); } public static function getDependencyOrderingService(ContainerInterface $container): Service\DependencyOrderingService diff --git a/typo3/sysext/core/Classes/Utility/File/ExtendedFileUtility.php b/typo3/sysext/core/Classes/Utility/File/ExtendedFileUtility.php index 2216cd5fba5d65106d7e4118a5bf22ce22d7136f..4037d029b2ca8ef9b07fda553cc2e2cae8fb9df2 100644 --- a/typo3/sysext/core/Classes/Utility/File/ExtendedFileUtility.php +++ b/typo3/sysext/core/Classes/Utility/File/ExtendedFileUtility.php @@ -1090,9 +1090,7 @@ class ExtendedFileUtility extends BasicFileUtility 'size' => $uploadedFileData['size'][$i] ]; try { - /** @var File $fileObject */ $fileObject = $targetFolderObject->addUploadedFile($fileInfo, (string)$this->existingFilesConflictMode); - $fileObject = GeneralUtility::makeInstance(ResourceFactory::class)->getFileObjectByStorageAndIdentifier($targetFolderObject->getStorage()->getUid(), $fileObject->getIdentifier()); if ($this->existingFilesConflictMode->equals(DuplicationBehavior::REPLACE)) { $this->getIndexer($fileObject->getStorage())->updateIndexEntry($fileObject); } diff --git a/typo3/sysext/core/Documentation/Changelog/master/Breaking-92289-DecoupleLogicOfResourceFactoryIntoStorageRepository.rst b/typo3/sysext/core/Documentation/Changelog/master/Breaking-92289-DecoupleLogicOfResourceFactoryIntoStorageRepository.rst new file mode 100644 index 0000000000000000000000000000000000000000..e7a0e3e181f318453736615e4a2499a3f8fb1e4c --- /dev/null +++ b/typo3/sysext/core/Documentation/Changelog/master/Breaking-92289-DecoupleLogicOfResourceFactoryIntoStorageRepository.rst @@ -0,0 +1,64 @@ +.. include:: ../../Includes.txt + +=========================================================================== +Breaking: #92289 - Decouple logic of ResourceFactory into StorageRepository +=========================================================================== + +See :issue:`92289` + +Description +=========== + +The ResourceFactory was initially created for the File Abstraction Layer (FAL) +as a Factory class, which created PHP objects. + +However, in the recent years, it became more apparent that it is more useful to +separate the concerns of the creation and retrieving of existing information. + +For this reason, the StorageRepository is now handling the creation of +ResourceStorage objects. This layer accesses the Database and the needed Driver +objects and configuration. + +The StorageRepository class does not extend from AbstractRepository anymore, +and is available standalone. + +Most of the logic in the ResourceFactory concerning Storages has been moved to +StorageRepository, which has a lot of options available now. + +The following methods within ResourceFactory have been marked +as internal, and are kept for backwards-compatibility without deprecation: + +* :php:`ResourceFactory->getDefaultStorage()` +* :php:`ResourceFactory->getStorageObject()` +* :php:`ResourceFactory->convertFlexFormDataToConfigurationArray()` +* :php:`ResourceFactory->createStorageObject()` +* :php:`ResourceFactory->createFolderObject()` +* :php:`ResourceFactory->getFileObjectByStorageAndIdentifier()` +* :php:`ResourceFactory->getStorageObjectFromCombinedIdentifier()` + +The following method has been removed +* :php:`ResourceFactory->getDriverObject()` + + +Impact +====== + +Calling the removed method will throw a fatal error. + +Checking StorageRepository for an instance of AbstractRepository +will have different results. + + +Affected Installations +====================== + +TYPO3 installations with specific third-party extensions working with the FAL +API directly might use the existing functionality. + + +Migration +========= + +Migrate to the StorageRepository API in the third-party extension code. + +.. index:: FAL, PHP-API, FullyScanned, ext:core diff --git a/typo3/sysext/core/Tests/Functional/Resource/ResourceStorageTest.php b/typo3/sysext/core/Tests/Functional/Resource/ResourceStorageTest.php index 4006006ff299744012482245c9b9be9162109fab..04ef0f206412a0170e6bbe004cfc22e98bb42faf 100644 --- a/typo3/sysext/core/Tests/Functional/Resource/ResourceStorageTest.php +++ b/typo3/sysext/core/Tests/Functional/Resource/ResourceStorageTest.php @@ -47,7 +47,7 @@ class ResourceStorageTest extends FunctionalTestCase { $this->importDataSet('PACKAGE:typo3/testing-framework/Resources/Core/Functional/Fixtures/sys_file_storage.xml'); $this->setUpBackendUserFromFixture(1); - $subject = (new StorageRepository())->findByUid(1); + $subject = GeneralUtility::makeInstance(StorageRepository::class)->findByUid(1); $subject->setEvaluatePermissions(false); GeneralUtility::mkdir_deep(Environment::getPublicPath() . '/fileadmin/_processed_'); @@ -93,7 +93,7 @@ class ResourceStorageTest extends FunctionalTestCase clearstatcache(); $file = GeneralUtility::makeInstance(ResourceFactory::class)->getFileObjectFromCombinedIdentifier('1:/' . $targetDirectory . '/' . $fileName); - $subject = (new StorageRepository())->findByUid(1); + $subject = GeneralUtility::makeInstance(StorageRepository::class)->findByUid(1); $subject->setEvaluatePermissions(true); // read_only = true -> no write access for user, so checking for second argument true should assert false @@ -160,7 +160,7 @@ class ResourceStorageTest extends FunctionalTestCase $this->importDataSet('PACKAGE:typo3/testing-framework/Resources/Core/Functional/Fixtures/sys_file_storage.xml'); $this->setUpBackendUserFromFixture(1); - $subject = (new StorageRepository())->findByUid(1); + $subject = GeneralUtility::makeInstance(StorageRepository::class)->findByUid(1); $processingFolder = $subject->getProcessingFolder(); self::assertInstanceOf(Folder::class, $processingFolder); @@ -175,7 +175,7 @@ class ResourceStorageTest extends FunctionalTestCase $this->importDataSet('PACKAGE:typo3/testing-framework/Resources/Core/Functional/Fixtures/sys_file_storage.xml'); $this->setUpBackendUserFromFixture(1); - $subject = (new StorageRepository())->findByUid(1); + $subject = GeneralUtility::makeInstance(StorageRepository::class)->findByUid(1); $folder = new Folder($subject, '/foo/' . $folderIdentifier . '/', $folderIdentifier); $role = $subject->getRole($folder); @@ -190,7 +190,7 @@ class ResourceStorageTest extends FunctionalTestCase { $this->importDataSet('PACKAGE:typo3/testing-framework/Resources/Core/Functional/Fixtures/sys_file_storage.xml'); $this->setUpBackendUserFromFixture(1); - $subject = (new StorageRepository())->findByUid(1); + $subject = GeneralUtility::makeInstance(StorageRepository::class)->findByUid(1); GeneralUtility::mkdir_deep(Environment::getPublicPath() . '/fileadmin/foo'); file_put_contents(Environment::getPublicPath() . '/fileadmin/foo/bar.txt', 'myData'); @@ -209,7 +209,7 @@ class ResourceStorageTest extends FunctionalTestCase { $this->importDataSet('PACKAGE:typo3/testing-framework/Resources/Core/Functional/Fixtures/sys_file_storage.xml'); $this->setUpBackendUserFromFixture(1); - $subject = (new StorageRepository())->findByUid(1); + $subject = GeneralUtility::makeInstance(StorageRepository::class)->findByUid(1); $this->expectException(\InvalidArgumentException::class); $this->expectExceptionCode(1325689164); @@ -223,7 +223,7 @@ class ResourceStorageTest extends FunctionalTestCase { $this->importDataSet('PACKAGE:typo3/testing-framework/Resources/Core/Functional/Fixtures/sys_file_storage.xml'); $this->setUpBackendUserFromFixture(1); - $subject = (new StorageRepository())->findByUid(1); + $subject = GeneralUtility::makeInstance(StorageRepository::class)->findByUid(1); GeneralUtility::mkdir_deep(Environment::getPublicPath() . '/fileadmin/foo'); GeneralUtility::mkdir_deep(Environment::getPublicPath() . '/fileadmin/_recycler_'); @@ -244,7 +244,7 @@ class ResourceStorageTest extends FunctionalTestCase { $this->importDataSet('PACKAGE:typo3/testing-framework/Resources/Core/Functional/Fixtures/sys_file_storage.xml'); $this->setUpBackendUserFromFixture(1); - $subject = (new StorageRepository())->findByUid(1); + $subject = GeneralUtility::makeInstance(StorageRepository::class)->findByUid(1); GeneralUtility::mkdir_deep(Environment::getPublicPath() . '/fileadmin/foo'); file_put_contents(Environment::getPublicPath() . '/fileadmin/foo/bar.txt', 'myData'); @@ -394,7 +394,7 @@ class ResourceStorageTest extends FunctionalTestCase $this->importDataSet('PACKAGE:typo3/testing-framework/Resources/Core/Functional/Fixtures/sys_file_storage.xml'); $this->importDataSet(__DIR__ . '/Fixtures/FileSearch.xml'); $this->setUpBackendUserFromFixture(1); - $subject = (new StorageRepository())->findByUid(1); + $subject = GeneralUtility::makeInstance(StorageRepository::class)->findByUid(1); $subject->setFileAndFolderNameFilters($filters); GeneralUtility::mkdir_deep(Environment::getPublicPath() . '/fileadmin/bar/bla'); diff --git a/typo3/sysext/core/Tests/Unit/Resource/FileTest.php b/typo3/sysext/core/Tests/Unit/Resource/FileTest.php index 2479ceed0e6b699760206bc2457fbf8fd2b9126a..7582c1627e3d74f911bebe8e0d5cc568670f86b7 100644 --- a/typo3/sysext/core/Tests/Unit/Resource/FileTest.php +++ b/typo3/sysext/core/Tests/Unit/Resource/FileTest.php @@ -21,8 +21,8 @@ use TYPO3\CMS\Core\Resource\File; use TYPO3\CMS\Core\Resource\Folder; use TYPO3\CMS\Core\Resource\Index\MetaDataRepository; use TYPO3\CMS\Core\Resource\MetaDataAspect; -use TYPO3\CMS\Core\Resource\ResourceFactory; use TYPO3\CMS\Core\Resource\ResourceStorage; +use TYPO3\CMS\Core\Resource\StorageRepository; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\StringUtility; use TYPO3\TestingFramework\Core\Unit\UnitTestCase; @@ -166,12 +166,12 @@ class FileTest extends UnitTestCase ->setConstructorArgs([$fileProperties, $this->storageMock]) ->getMock(); $mockedNewStorage = $this->createMock(ResourceStorage::class); - $mockedResourceFactory = $this->createMock(ResourceFactory::class); - $mockedResourceFactory + $mockedStorageRepository = $this->createMock(StorageRepository::class); + $mockedStorageRepository ->expects(self::once()) - ->method('getStorageObject') + ->method('findByUid') ->willReturn($mockedNewStorage); - GeneralUtility::setSingletonInstance(ResourceFactory::class, $mockedResourceFactory); + GeneralUtility::addInstance(StorageRepository::class, $mockedStorageRepository); $subject->updateProperties(['storage' => 'different']); self::assertSame($mockedNewStorage, $subject->getStorage()); diff --git a/typo3/sysext/core/Tests/Unit/Resource/ResourceFactoryTest.php b/typo3/sysext/core/Tests/Unit/Resource/ResourceFactoryTest.php index 051e2727e776340e4626e031531a3e7785cad227..83532bc5f343775415f383d3a8f7235629201706 100644 --- a/typo3/sysext/core/Tests/Unit/Resource/ResourceFactoryTest.php +++ b/typo3/sysext/core/Tests/Unit/Resource/ResourceFactoryTest.php @@ -15,10 +15,7 @@ namespace TYPO3\CMS\Core\Tests\Unit\Resource; -use Psr\EventDispatcher\EventDispatcherInterface; use TYPO3\CMS\Core\Core\Environment; -use TYPO3\CMS\Core\Resource\Driver\AbstractDriver; -use TYPO3\CMS\Core\Resource\Driver\DriverRegistry; use TYPO3\CMS\Core\Resource\ResourceFactory; use TYPO3\CMS\Core\Resource\ResourceStorage; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -65,39 +62,18 @@ class ResourceFactoryTest extends UnitTestCase parent::tearDown(); } - /********************************** - * Storage Collections - **********************************/ /** * @test */ - public function createStorageCollectionObjectCreatesCollectionWithCorrectArguments() + public function createFolderCreatesObjectWithCorrectArguments() { $mockedMount = $this->createMock(ResourceStorage::class); $path = StringUtility::getUniqueId('path_'); $name = StringUtility::getUniqueId('name_'); - $storageCollection = $this->subject->createFolderObject($mockedMount, $path, $name, 0); - self::assertSame($mockedMount, $storageCollection->getStorage()); - self::assertEquals($path, $storageCollection->getIdentifier()); - self::assertEquals($name, $storageCollection->getName()); - } - - /********************************** - * Drivers - **********************************/ - /** - * @test - */ - public function getDriverObjectAcceptsDriverClassName() - { - $mockedDriver = $this->getMockForAbstractClass(AbstractDriver::class); - $driverFixtureClass = get_class($mockedDriver); - GeneralUtility::addInstance($driverFixtureClass, $mockedDriver); - $mockedRegistry = $this->createMock(DriverRegistry::class); - $mockedRegistry->expects(self::once())->method('getDriverClass')->with(self::equalTo($driverFixtureClass))->willReturn($driverFixtureClass); - GeneralUtility::setSingletonInstance(DriverRegistry::class, $mockedRegistry); - $obj = $this->subject->getDriverObject($driverFixtureClass, []); - self::assertInstanceOf(AbstractDriver::class, $obj); + $folderObject = $this->subject->createFolderObject($mockedMount, $path, $name); + self::assertSame($mockedMount, $folderObject->getStorage()); + self::assertEquals($path, $folderObject->getIdentifier()); + self::assertEquals($name, $folderObject->getName()); } /*********************************** @@ -159,74 +135,4 @@ class ResourceFactoryTest extends UnitTestCase $this->filesCreated[] = Environment::getPublicPath() . '/' . $filename; $this->subject->retrieveFileOrFolderObject($filename); } - - /*********************************** - * Storage AutoDetection - ***********************************/ - - /** - * @param array $storageConfiguration - * @param string $path - * @param int $expectedStorageId - * @test - * @dataProvider storageDetectionDataProvider - */ - public function findBestMatchingStorageByLocalPathReturnsDefaultStorageIfNoMatchIsFound(array $storageConfiguration, $path, $expectedStorageId) - { - $resourceFactory = new ResourceFactory($this->prophesize(EventDispatcherInterface::class)->reveal()); - $mock = \Closure::bind(static function (ResourceFactory $resourceFactory) use (&$path, $storageConfiguration) { - $resourceFactory->localDriverStorageCache = $storageConfiguration; - return $resourceFactory->findBestMatchingStorageByLocalPath($path); - }, null, ResourceFactory::class); - self::assertSame($expectedStorageId, $mock($resourceFactory)); - } - - /** - * @return array - */ - public function storageDetectionDataProvider() - { - return [ - 'NoLocalStoragesReturnDefaultStorage' => [ - [], - 'my/dummy/Image.png', - 0 - ], - 'NoMatchReturnsDefaultStorage' => [ - [1 => 'fileadmin/', 2 => 'fileadmin2/public/'], - 'my/dummy/Image.png', - 0 - ], - 'MatchReturnsTheMatch' => [ - [1 => 'fileadmin/', 2 => 'other/public/'], - 'fileadmin/dummy/Image.png', - 1 - ], - 'TwoFoldersWithSameStartReturnsCorrect' => [ - [1 => 'fileadmin/', 2 => 'fileadmin/public/'], - 'fileadmin/dummy/Image.png', - 1 - ], - 'NestedStorageReallyReturnsTheBestMatching' => [ - [1 => 'fileadmin/', 2 => 'fileadmin/public/'], - 'fileadmin/public/Image.png', - 2 - ], - 'CommonPrefixButWrongPath' => [ - [1 => 'fileadmin/', 2 => 'uploads/test/'], - 'uploads/bogus/dummy.png', - 0 - ], - 'CommonPrefixRightPath' => [ - [1 => 'fileadmin/', 2 => 'uploads/test/'], - 'uploads/test/dummy.png', - 2 - ], - 'FindStorageFromWindowsPath' => [ - [1 => 'fileadmin/', 2 => 'uploads/test/'], - 'uploads\\test\\dummy.png', - 2 - ], - ]; - } } diff --git a/typo3/sysext/core/Tests/Unit/Resource/ResourceStorageTest.php b/typo3/sysext/core/Tests/Unit/Resource/ResourceStorageTest.php index 55527c40d78740cfde098aa160775f6816e892ce..683b8df18728c0d5173b39d1eca2c2a989161400 100644 --- a/typo3/sysext/core/Tests/Unit/Resource/ResourceStorageTest.php +++ b/typo3/sysext/core/Tests/Unit/Resource/ResourceStorageTest.php @@ -836,16 +836,15 @@ class ResourceStorageTest extends BaseTestCase 'bar', 'bar_01' )); - $resourceFactory = $this->createMock(ResourceFactory::class); $this->prepareSubject( [], true, $mockedDriver, - $resourceFactory, + null, [], - ['getUniqueName'] + ['getUniqueName', 'createFolderObject'] ); - $resourceFactory->expects(self::once())->method('createFolderObject')->willReturn(new Folder($this->subject, '', '')); + $this->subject->expects(self::once())->method('createFolderObject')->willReturn(new Folder($this->subject, '', '')); /** @var File $file */ $file = new File(['identifier' => 'foo', 'name' => 'foo'], $this->subject); $this->subject->expects(self::any())->method('getUniqueName')->willReturn('bar_01'); diff --git a/typo3/sysext/core/Tests/Unit/Resource/StorageRepositoryTest.php b/typo3/sysext/core/Tests/Unit/Resource/StorageRepositoryTest.php new file mode 100644 index 0000000000000000000000000000000000000000..54828cfdcde912fd57bff892def9c547ffe02212 --- /dev/null +++ b/typo3/sysext/core/Tests/Unit/Resource/StorageRepositoryTest.php @@ -0,0 +1,127 @@ +<?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\Tests\Unit\Resource; + +use Psr\EventDispatcher\EventDispatcherInterface; +use TYPO3\CMS\Core\Resource\Driver\AbstractDriver; +use TYPO3\CMS\Core\Resource\Driver\DriverRegistry; +use TYPO3\CMS\Core\Resource\StorageRepository; +use TYPO3\TestingFramework\Core\Unit\UnitTestCase; + +/** + * Test case + */ +class StorageRepositoryTest extends UnitTestCase +{ + /********************************** + * Drivers + **********************************/ + /** + * @test + */ + public function getDriverObjectAcceptsDriverClassName() + { + $mockedDriver = $this->getMockForAbstractClass(AbstractDriver::class); + $driverFixtureClass = get_class($mockedDriver); + $registry = new DriverRegistry(); + $registry->registerDriverClass($driverFixtureClass); + $subject = $this->getAccessibleMock( + StorageRepository::class, + ['dummy'], + [ + $this->prophesize(EventDispatcherInterface::class)->reveal(), + $registry + ] + ); + $obj = $subject->_call('getDriverObject', $driverFixtureClass, []); + self::assertInstanceOf(AbstractDriver::class, $obj); + } + + /*********************************** + * Storage AutoDetection + ***********************************/ + + /** + * @param array $storageConfiguration + * @param string $path + * @param int $expectedStorageId + * @test + * @dataProvider storageDetectionDataProvider + */ + public function findBestMatchingStorageByLocalPathReturnsDefaultStorageIfNoMatchIsFound(array $storageConfiguration, $path, $expectedStorageId) + { + $subject = new StorageRepository( + $this->prophesize(EventDispatcherInterface::class)->reveal(), + $this->prophesize(DriverRegistry::class)->reveal() + ); + $mock = \Closure::bind(static function (StorageRepository $storageRepository) use (&$path, $storageConfiguration) { + $storageRepository->localDriverStorageCache = $storageConfiguration; + return $storageRepository->findBestMatchingStorageByLocalPath($path); + }, null, StorageRepository::class); + self::assertSame($expectedStorageId, $mock($subject)); + } + + /** + * @return array + */ + public function storageDetectionDataProvider() + { + return [ + 'NoLocalStoragesReturnDefaultStorage' => [ + [], + 'my/dummy/Image.png', + 0 + ], + 'NoMatchReturnsDefaultStorage' => [ + [1 => 'fileadmin/', 2 => 'fileadmin2/public/'], + 'my/dummy/Image.png', + 0 + ], + 'MatchReturnsTheMatch' => [ + [1 => 'fileadmin/', 2 => 'other/public/'], + 'fileadmin/dummy/Image.png', + 1 + ], + 'TwoFoldersWithSameStartReturnsCorrect' => [ + [1 => 'fileadmin/', 2 => 'fileadmin/public/'], + 'fileadmin/dummy/Image.png', + 1 + ], + 'NestedStorageReallyReturnsTheBestMatching' => [ + [1 => 'fileadmin/', 2 => 'fileadmin/public/'], + 'fileadmin/public/Image.png', + 2 + ], + 'CommonPrefixButWrongPath' => [ + [1 => 'fileadmin/', 2 => 'uploads/test/'], + 'uploads/bogus/dummy.png', + 0 + ], + 'CommonPrefixRightPath' => [ + [1 => 'fileadmin/', 2 => 'uploads/test/'], + 'uploads/test/dummy.png', + 2 + ], + 'FindStorageFromWindowsPath' => [ + [1 => 'fileadmin/', 2 => 'uploads/test/'], + 'uploads\\test\\dummy.png', + 2 + ], + ]; + } +} diff --git a/typo3/sysext/filelist/Classes/Controller/FileListController.php b/typo3/sysext/filelist/Classes/Controller/FileListController.php index 5191b6a69562f7730f71a420945d840eba5fe62f..1fcaa5ebe8fd4f6046de33d3ed8be1ccd968ba66 100644 --- a/typo3/sysext/filelist/Classes/Controller/FileListController.php +++ b/typo3/sysext/filelist/Classes/Controller/FileListController.php @@ -35,6 +35,7 @@ use TYPO3\CMS\Core\Resource\Folder; use TYPO3\CMS\Core\Resource\ResourceFactory; use TYPO3\CMS\Core\Resource\ResourceStorage; use TYPO3\CMS\Core\Resource\Search\FileSearchDemand; +use TYPO3\CMS\Core\Resource\StorageRepository; use TYPO3\CMS\Core\Resource\Utility\ListUtility; use TYPO3\CMS\Core\Type\Bitmask\JsConfirmation; use TYPO3\CMS\Core\Utility\File\ExtendedFileUtility; @@ -173,14 +174,12 @@ class FileListController extends ActionController implements LoggerAwareInterfac try { if ($combinedIdentifier) { $this->getBackendUser()->evaluateUserSpecificFileFilterSettings(); - $resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class); - $storage = $resourceFactory->getStorageObjectFromCombinedIdentifier($combinedIdentifier); + $storage = GeneralUtility::makeInstance(StorageRepository::class)->findByCombinedIdentifier($combinedIdentifier); $identifier = substr($combinedIdentifier, strpos($combinedIdentifier, ':') + 1); if (!$storage->hasFolder($identifier)) { $identifier = $storage->getFolderIdentifierFromFileIdentifier($identifier); } - - $this->folderObject = $resourceFactory->getFolderObjectFromCombinedIdentifier($storage->getUid() . ':' . $identifier); + $this->folderObject = $storage->getFolder($identifier); // Disallow access to fallback storage 0 if ($storage->getUid() === 0) { throw new InsufficientFolderAccessPermissionsException( diff --git a/typo3/sysext/form/Tests/Functional/Hooks/FormFileExtensionUpdateTest.php b/typo3/sysext/form/Tests/Functional/Hooks/FormFileExtensionUpdateTest.php index fb1efde8bbd6ee422f4e4ede5900bf491b040fa8..f33adedd0c5316920b8608eef86e7a23ff85e8e1 100644 --- a/typo3/sysext/form/Tests/Functional/Hooks/FormFileExtensionUpdateTest.php +++ b/typo3/sysext/form/Tests/Functional/Hooks/FormFileExtensionUpdateTest.php @@ -24,7 +24,7 @@ use TYPO3\CMS\Core\Core\Bootstrap; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Database\ReferenceIndex; use TYPO3\CMS\Core\Resource\Folder; -use TYPO3\CMS\Core\Resource\ResourceFactory; +use TYPO3\CMS\Core\Resource\StorageRepository; use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\StringUtility; @@ -81,7 +81,7 @@ class FormFileExtensionUpdateTest extends FunctionalTestCase Bootstrap::initializeLanguageObject(); $folderIdentifier = 'form_definitions'; - $storage = GeneralUtility::makeInstance(ResourceFactory::class)->getStorageObject(1); + $storage = GeneralUtility::makeInstance(StorageRepository::class)->getStorageObject(1); if ($storage->hasFolder($folderIdentifier)) { $storage->getFolder($folderIdentifier)->delete(true); diff --git a/typo3/sysext/frontend/Tests/Functional/Imaging/GifBuilderTest.php b/typo3/sysext/frontend/Tests/Functional/Imaging/GifBuilderTest.php index 8436de9f8ea83783eaa636478c34f865b8169cf3..07cfdc9596b5a7e550dc5a49181445f76142b25b 100644 --- a/typo3/sysext/frontend/Tests/Functional/Imaging/GifBuilderTest.php +++ b/typo3/sysext/frontend/Tests/Functional/Imaging/GifBuilderTest.php @@ -19,6 +19,7 @@ namespace TYPO3\CMS\Frontend\Tests\Functional\Imaging; use TYPO3\CMS\Core\Core\Environment; use TYPO3\CMS\Core\Resource\StorageRepository; +use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Frontend\Imaging\GifBuilder; use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase; @@ -42,7 +43,7 @@ class GifBuilderTest extends FunctionalTestCase Environment::getPublicPath() . '/fileadmin/kasper-skarhoj1.jpg' ); - $storageRepository = (new StorageRepository())->findByUid(1); + $storageRepository = GeneralUtility::makeInstance(StorageRepository::class)->findByUid(1); $file = $storageRepository->getFile('kasper-skarhoj1.jpg'); self::assertFalse($file->isMissing()); diff --git a/typo3/sysext/frontend/Tests/Functional/SiteHandling/TypoLinkGeneratorTest.php b/typo3/sysext/frontend/Tests/Functional/SiteHandling/TypoLinkGeneratorTest.php index bc6eac335cc43e378c6eebd283af02020dd9b75b..d3651e20a10f4de2ecda7ab72085d800fd6c68e9 100644 --- a/typo3/sysext/frontend/Tests/Functional/SiteHandling/TypoLinkGeneratorTest.php +++ b/typo3/sysext/frontend/Tests/Functional/SiteHandling/TypoLinkGeneratorTest.php @@ -126,7 +126,7 @@ class TypoLinkGeneratorTest extends AbstractTestCase */ private function setUpFileStorage() { - $storageRepository = new StorageRepository(); + $storageRepository = GeneralUtility::makeInstance(StorageRepository::class); $storageId = $storageRepository->createLocalStorage( 'fileadmin', 'fileadmin/', diff --git a/typo3/sysext/impexp/Classes/Import.php b/typo3/sysext/impexp/Classes/Import.php index faa726f72c10694131b63d9584c1a20804a9b8f5..1f1d470a54b24905373a23161f859a50c46ea581 100644 --- a/typo3/sysext/impexp/Classes/Import.php +++ b/typo3/sysext/impexp/Classes/Import.php @@ -28,6 +28,7 @@ use TYPO3\CMS\Core\Resource\ResourceFactory; use TYPO3\CMS\Core\Resource\ResourceStorage; use TYPO3\CMS\Core\Resource\Security\FileNameValidator; use TYPO3\CMS\Core\Resource\StorageRepository; +use TYPO3\CMS\Core\Service\FlexFormService; use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\MathUtility; @@ -219,7 +220,7 @@ class Import extends ImportExport $defaultStorageUid = null; // get default storage - $defaultStorage = GeneralUtility::makeInstance(ResourceFactory::class)->getDefaultStorage(); + $defaultStorage = GeneralUtility::makeInstance(StorageRepository::class)->getDefaultStorage(); if ($defaultStorage !== null) { $defaultStorageUid = $defaultStorage->getUid(); } @@ -246,7 +247,7 @@ class Import extends ImportExport && (bool)$storageObject->isWritable() === (bool)$storageRecord['is_writable'] && (bool)$storageObject->isOnline() === (bool)$storageRecord['is_online'] ) { - $storageRecordConfiguration = GeneralUtility::makeInstance(ResourceFactory::class)->convertFlexFormDataToConfigurationArray($storageRecord['configuration']); + $storageRecordConfiguration = GeneralUtility::makeInstance(FlexFormService::class)->convertFlexFormContentToArray($storageRecord['configuration'] ?? ''); $storageObjectConfiguration = $storageObject->getConfiguration(); // compare the properties: pathType and basePath if ($storageRecordConfiguration['pathType'] === $storageObjectConfiguration['pathType'] @@ -305,7 +306,7 @@ class Import extends ImportExport // storage object will check whether the target folder exists and set the // isOnline flag depending on the outcome. $storageRecord['uid'] = 0; - $resourceStorage = GeneralUtility::makeInstance(ResourceFactory::class)->createStorageObject($storageRecord); + $resourceStorage = GeneralUtility::makeInstance(StorageRepository::class)->createStorageObject($storageRecord); if (!$resourceStorage->isOnline()) { $configuration = $resourceStorage->getConfiguration(); $messages['resourceStorageFolderMissing_' . $storageRecordUid] = @@ -335,7 +336,7 @@ class Import extends ImportExport // fetch fresh storage records from database $storageRecords = $this->fetchStorageRecords(); - $defaultStorage = GeneralUtility::makeInstance(ResourceFactory::class)->getDefaultStorage(); + $defaultStorage = GeneralUtility::makeInstance(StorageRepository::class)->getDefaultStorage(); $sanitizedFolderMappings = []; @@ -379,9 +380,9 @@ class Import extends ImportExport // mapping. Only in this case we could be sure, that it's a local, online and writable storage. if ($useStorageFromStorageRecords && isset($storageRecords[$fileRecord['storage']])) { /** @var \TYPO3\CMS\Core\Resource\ResourceStorage $storage */ - $storage = GeneralUtility::makeInstance(ResourceFactory::class)->getStorageObject($fileRecord['storage'], $storageRecords[$fileRecord['storage']]); + $storage = GeneralUtility::makeInstance(StorageRepository::class)->getStorageObject($fileRecord['storage'], $storageRecords[$fileRecord['storage']]); } elseif ($this->isFallbackStorage($fileRecord['storage'])) { - $storage = GeneralUtility::makeInstance(ResourceFactory::class)->getStorageObject(0); + $storage = GeneralUtility::makeInstance(StorageRepository::class)->findByUid(0); } elseif ($defaultStorage !== null) { $storage = $defaultStorage; } else { diff --git a/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php b/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php index 786b02a316a90690855b4f4ea27cc93d2354b100..83821fb4cb132433ac3dcf57d832da8bf5510cf5 100644 --- a/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php +++ b/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php @@ -4493,4 +4493,11 @@ return [ 'Deprecation-92132-DeprecatedShortcutPHPAPI.rst' ], ], + 'TYPO3\CMS\Core\Resource\ResourceFactory->getDriverObject' => [ + 'numberOfMandatoryArguments' => 0, + 'maximumNumberOfArguments' => 0, + 'restFiles' => [ + 'Breaking-92289-DecoupleLogicOfResourceFactoryIntoStorageRepository.rst' + ], + ], ]; diff --git a/typo3/sysext/scheduler/Classes/Task/FileStorageExtractionAdditionalFieldProvider.php b/typo3/sysext/scheduler/Classes/Task/FileStorageExtractionAdditionalFieldProvider.php index 5bf48ab3525c85a3bbe7c97c168b251255a1ce73..2f8be8c4119596e1827e84aa4bfd5dd9c32a76ec 100644 --- a/typo3/sysext/scheduler/Classes/Task/FileStorageExtractionAdditionalFieldProvider.php +++ b/typo3/sysext/scheduler/Classes/Task/FileStorageExtractionAdditionalFieldProvider.php @@ -17,7 +17,6 @@ namespace TYPO3\CMS\Scheduler\Task; use TYPO3\CMS\Core\Resource\Index\ExtractorInterface; use TYPO3\CMS\Core\Resource\Index\ExtractorRegistry; -use TYPO3\CMS\Core\Resource\ResourceFactory; use TYPO3\CMS\Core\Resource\StorageRepository; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\MathUtility; @@ -163,7 +162,7 @@ class FileStorageExtractionAdditionalFieldProvider implements AdditionalFieldPro ) { return false; } - if (GeneralUtility::makeInstance(ResourceFactory::class)->getStorageObject($submittedData['scheduler_fileStorageIndexing_storage']) === null) { + if (GeneralUtility::makeInstance(StorageRepository::class)->findByUid($submittedData['scheduler_fileStorageIndexing_storage']) === null) { return false; } if (!MathUtility::isIntegerInRange($submittedData['scheduler_fileStorageIndexing_fileCount'], 1, 9999)) { diff --git a/typo3/sysext/scheduler/Classes/Task/FileStorageExtractionTask.php b/typo3/sysext/scheduler/Classes/Task/FileStorageExtractionTask.php index 1ad0059a807d2130da06672ffc3307135a717154..0d2cb90084440dcbc02ffcb4070b32bdc4d54b3e 100644 --- a/typo3/sysext/scheduler/Classes/Task/FileStorageExtractionTask.php +++ b/typo3/sysext/scheduler/Classes/Task/FileStorageExtractionTask.php @@ -16,8 +16,8 @@ namespace TYPO3\CMS\Scheduler\Task; use TYPO3\CMS\Core\Resource\Index\Indexer; -use TYPO3\CMS\Core\Resource\ResourceFactory; use TYPO3\CMS\Core\Resource\ResourceStorage; +use TYPO3\CMS\Core\Resource\StorageRepository; use TYPO3\CMS\Core\Utility\GeneralUtility; /** @@ -49,7 +49,7 @@ class FileStorageExtractionTask extends AbstractTask { $success = false; if ((int)$this->storageUid > 0) { - $storage = GeneralUtility::makeInstance(ResourceFactory::class)->getStorageObject($this->storageUid); + $storage = GeneralUtility::makeInstance(StorageRepository::class)->findByUid($this->storageUid); $currentEvaluatePermissionsValue = $storage->getEvaluatePermissions(); $storage->setEvaluatePermissions(false); $indexer = $this->getIndexer($storage); diff --git a/typo3/sysext/scheduler/Classes/Task/FileStorageIndexingAdditionalFieldProvider.php b/typo3/sysext/scheduler/Classes/Task/FileStorageIndexingAdditionalFieldProvider.php index 2d38b08e8518723f096338433c14b5c0b82eb0b6..67f7426773cca4328328aab2d8b01adc5c5d00d4 100644 --- a/typo3/sysext/scheduler/Classes/Task/FileStorageIndexingAdditionalFieldProvider.php +++ b/typo3/sysext/scheduler/Classes/Task/FileStorageIndexingAdditionalFieldProvider.php @@ -15,7 +15,6 @@ namespace TYPO3\CMS\Scheduler\Task; -use TYPO3\CMS\Core\Resource\ResourceFactory; use TYPO3\CMS\Core\Resource\StorageRepository; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\MathUtility; @@ -95,7 +94,7 @@ class FileStorageIndexingAdditionalFieldProvider implements AdditionalFieldProvi if (!MathUtility::canBeInterpretedAsInteger($value)) { return false; } - if (GeneralUtility::makeInstance(ResourceFactory::class)->getStorageObject($submittedData['scheduler_fileStorageIndexing_storage']) !== null) { + if (GeneralUtility::makeInstance(StorageRepository::class)->findByUid($submittedData['scheduler_fileStorageIndexing_storage']) !== null) { return true; } return false; diff --git a/typo3/sysext/scheduler/Classes/Task/FileStorageIndexingTask.php b/typo3/sysext/scheduler/Classes/Task/FileStorageIndexingTask.php index 6ff91cbafd6d22271d917538595ac47147cb5767..175dc630cafd92e7347903a748d167ffefd1d572 100644 --- a/typo3/sysext/scheduler/Classes/Task/FileStorageIndexingTask.php +++ b/typo3/sysext/scheduler/Classes/Task/FileStorageIndexingTask.php @@ -16,8 +16,8 @@ namespace TYPO3\CMS\Scheduler\Task; use TYPO3\CMS\Core\Resource\Index\Indexer; -use TYPO3\CMS\Core\Resource\ResourceFactory; use TYPO3\CMS\Core\Resource\ResourceStorage; +use TYPO3\CMS\Core\Resource\StorageRepository; use TYPO3\CMS\Core\Utility\GeneralUtility; /** @@ -41,7 +41,7 @@ class FileStorageIndexingTask extends AbstractTask public function execute() { if ((int)$this->storageUid > 0) { - $storage = GeneralUtility::makeInstance(ResourceFactory::class)->getStorageObject($this->storageUid); + $storage = GeneralUtility::makeInstance(StorageRepository::class)->findByUid($this->storageUid); $currentEvaluatePermissionsValue = $storage->getEvaluatePermissions(); $storage->setEvaluatePermissions(false); $indexer = $this->getIndexer($storage);