[BUGFIX] Start nighly test run at 3am rather than 0:03am
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Database / ReferenceIndex.php
index 6f115ef..973d83c 100644 (file)
@@ -15,25 +15,24 @@ namespace TYPO3\CMS\Core\Database;
  */
 
 use Doctrine\DBAL\DBALException;
+use Psr\Log\LoggerAwareInterface;
+use Psr\Log\LoggerAwareTrait;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Core\Cache\CacheManager;
 use TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools;
+use TYPO3\CMS\Core\Database\Platform\PlatformInformation;
 use TYPO3\CMS\Core\DataHandling\DataHandler;
 use TYPO3\CMS\Core\Messaging\FlashMessage;
-use TYPO3\CMS\Core\Messaging\FlashMessageService;
+use TYPO3\CMS\Core\Messaging\FlashMessageRendererResolver;
 use TYPO3\CMS\Core\Registry;
-use TYPO3\CMS\Core\Resource\File;
-use TYPO3\CMS\Core\Resource\Folder;
-use TYPO3\CMS\Core\Resource\ResourceFactory;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
-use TYPO3\CMS\Core\Utility\PathUtility;
 use TYPO3\CMS\Extbase\SignalSlot\Dispatcher;
 
 /**
  * Reference index processing and relation extraction
  *
  * NOTICE: When the reference index is updated for an offline version the results may not be correct.
- * First, lets assumed that the reference update happens in LIVE workspace (ALWAYS update from Live workspace if you analyse whole database!)
+ * First, lets assumed that the reference update happens in LIVE workspace (ALWAYS update from Live workspace if you analyze whole database!)
  * Secondly, lets assume that in a Draft workspace you have changed the data structure of a parent page record - this is (in TemplaVoila) inherited by subpages.
  * When in the LIVE workspace the data structure for the records/pages in the offline workspace will not be evaluated to the right one simply because the data
  * structure is taken from a rootline traversal and in the Live workspace that will NOT include the changed DataStructure! Thus the evaluation will be based
@@ -42,8 +41,10 @@ use TYPO3\CMS\Extbase\SignalSlot\Dispatcher;
  * maintaining the index for workspace records. Or we can say that the index is precise for all Live elements while glitches might happen in an offline workspace?
  * Anyway, I just wanted to document this finding - I don't think we can find a solution for it. And its very TemplaVoila specific.
  */
-class ReferenceIndex
+class ReferenceIndex implements LoggerAwareInterface
 {
+    use LoggerAwareTrait;
+
     /**
      * Definition of tables to exclude from the ReferenceIndex
      *
@@ -59,7 +60,6 @@ class ReferenceIndex
      */
     protected static $excludedTables = [
         'sys_log' => true,
-        'sys_history' => true,
         'tx_extensionmanager_domain_model_extension' => true
     ];
 
@@ -95,7 +95,9 @@ class ReferenceIndex
      * This array holds the FlexForm references of a record
      *
      * @var array
-     * @see getRelations(),FlexFormTools::traverseFlexFormXMLData(),getRelations_flexFormCallBack()
+     * @see getRelations()
+     * @see FlexFormTools::traverseFlexFormXMLData()
+     * @see getRelations_flexFormCallBack()
      */
     public $temp_flexRelations = [];
 
@@ -112,7 +114,8 @@ class ReferenceIndex
      * An index of all found references of a single record created in createEntryData() and accumulated in generateRefIndexData()
      *
      * @var array
-     * @see createEntryData(),generateRefIndexData()
+     * @see createEntryData()
+     * @see generateRefIndexData()
      */
     public $relations = [];
 
@@ -142,16 +145,22 @@ class ReferenceIndex
     /**
      * Runtime Cache to store and retrieve data computed for a single request
      *
-     * @var \TYPO3\CMS\Core\Cache\Frontend\VariableFrontend
+     * @var \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface
+     */
+    protected $runtimeCache;
+
+    /**
+     * Enables $runtimeCache and $recordCache
+     * @var bool
      */
-    protected $runtimeCache = null;
+    protected $useRuntimeCache = false;
 
     /**
      * Constructor
      */
     public function __construct()
     {
-        $this->runtimeCache = GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_runtime');
+        $this->runtimeCache = GeneralUtility::makeInstance(CacheManager::class)->getCache('runtime');
     }
 
     /**
@@ -169,7 +178,8 @@ class ReferenceIndex
      * Gets the current workspace id
      *
      * @return int
-     * @see updateRefIndexTable(),createEntryData()
+     * @see updateRefIndexTable()
+     * @see createEntryData()
      */
     public function getWorkspaceId()
     {
@@ -212,7 +222,7 @@ class ReferenceIndex
 
         // Fetch tableRelationFields and save them in cache if not there yet
         $cacheId = static::$cachePrefixTableRelationFields . $tableName;
-        if (!$this->runtimeCache->has($cacheId)) {
+        if (!$this->useRuntimeCache || !$this->runtimeCache->has($cacheId)) {
             $tableRelationFields = $this->fetchTableRelationFields($tableName);
             $this->runtimeCache->set($cacheId, $tableRelationFields);
         } else {
@@ -280,16 +290,22 @@ class ReferenceIndex
                 $result['deletedNodes'] = count($hashList);
                 $result['deletedNodes_hashList'] = implode(',', $hashList);
                 if (!$testOnly) {
-                    $queryBuilder = $connection->createQueryBuilder();
-                    $queryBuilder
-                        ->delete('sys_refindex')
-                        ->where(
-                            $queryBuilder->expr()->in(
-                                'hash',
-                                $queryBuilder->createNamedParameter($hashList, Connection::PARAM_STR_ARRAY)
+                    $maxBindParameters = PlatformInformation::getMaxBindParameters($connection->getDatabasePlatform());
+                    foreach (array_chunk($hashList, $maxBindParameters - 10, true) as $chunk) {
+                        if (empty($chunk)) {
+                            continue;
+                        }
+                        $queryBuilder = $connection->createQueryBuilder();
+                        $queryBuilder
+                            ->delete('sys_refindex')
+                            ->where(
+                                $queryBuilder->expr()->in(
+                                    'hash',
+                                    $queryBuilder->createNamedParameter($chunk, Connection::PARAM_STR_ARRAY)
+                                )
                             )
-                        )
-                        ->execute();
+                            ->execute();
+                    }
                 }
             }
         }
@@ -328,13 +344,41 @@ class ReferenceIndex
     }
 
     /**
+     * Returns the amount of references for the given record
+     *
+     * @param string $tableName
+     * @param int $uid
+     * @return int
+     */
+    public function getNumberOfReferencedRecords(string $tableName, int $uid): int
+    {
+        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_refindex');
+        return (int)$queryBuilder
+            ->count('*')->from('sys_refindex')
+            ->where(
+                $queryBuilder->expr()->eq(
+                    'ref_table',
+                    $queryBuilder->createNamedParameter($tableName, \PDO::PARAM_STR)
+                ),
+                $queryBuilder->expr()->eq(
+                    'ref_uid',
+                    $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)
+                ),
+                $queryBuilder->expr()->eq(
+                    'deleted',
+                    $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
+                )
+            )->execute()->fetchColumn(0);
+    }
+
+    /**
      * Calculate the relations for a record of a given table
      *
      * @param string $tableName Table being processed
      * @param array $record Record from $tableName
      * @return array
      */
-    protected function generateDataUsingRecord(string $tableName, array $record) : array
+    protected function generateDataUsingRecord(string $tableName, array $record): array
     {
         $this->relations = [];
         $deleteField = $GLOBALS['TCA'][$tableName]['ctrl']['delete'];
@@ -351,11 +395,6 @@ class ReferenceIndex
                 case 'db':
                     $this->createEntryDataForDatabaseRelationsUsingRecord($tableName, $record, $fieldName, '', $deleted, $fieldRelations['itemArray']);
                     break;
-                case 'file_reference':
-                    // not used (see getRelations()), but fallback to file
-                case 'file':
-                    $this->createEntryDataForFileRelationsUsingRecord($tableName, $record, $fieldName, '', $deleted, $fieldRelations['newValueFiles']);
-                    break;
                 case 'flex':
                     // DB references in FlexForms
                     if (is_array($fieldRelations['flexFormRels']['db'])) {
@@ -363,13 +402,6 @@ class ReferenceIndex
                             $this->createEntryDataForDatabaseRelationsUsingRecord($tableName, $record, $fieldName, $flexPointer, $deleted, $subList);
                         }
                     }
-                    // File references in FlexForms
-                    // @todo #65463 Test correct handling of file references in FlexForms
-                    if (is_array($fieldRelations['flexFormRels']['file'])) {
-                        foreach ($fieldRelations['flexFormRels']['file'] as $flexPointer => $subList) {
-                            $this->createEntryDataForFileRelationsUsingRecord($tableName, $record, $fieldName, $flexPointer, $deleted, $subList);
-                        }
-                    }
                     // Soft references in FlexForms
                     // @todo #65464 Test correct handling of soft references in FlexForms
                     if (is_array($fieldRelations['flexFormRels']['softrefs'])) {
@@ -397,9 +429,9 @@ class ReferenceIndex
      * @param string $field Fieldname of source record (where reference is located)
      * @param string $flexPointer Pointer to location inside FlexForm structure where reference is located in [field]
      * @param int $deleted Whether record is deleted-flagged or not
-     * @param string $ref_table For database references; the tablename the reference points to. Special keyword "_FILE" indicates that "ref_string" is a file reference either absolute or relative to PATH_site. Special keyword "_STRING" indicates some special usage (typ. softreference) where "ref_string" is used for the value.
-     * @param int $ref_uid For database references; The UID of the record (zero "ref_table" is "_FILE" or "_STRING")
-     * @param string $ref_string For "_FILE" or "_STRING" references: The filepath (relative to PATH_site or absolute) or other string.
+     * @param string $ref_table For database references; the tablename the reference points to. Special keyword "_STRING" indicates some special usage (typ. softreference) where "ref_string" is used for the value.
+     * @param int $ref_uid For database references; The UID of the record (zero "ref_table" is "_STRING")
+     * @param string $ref_string For "_STRING" references: The string.
      * @param int $sort The sorting order of references if many (the "group" or "select" TCA types). -1 if no sorting order is specified.
      * @param string $softref_key If the reference is a soft reference, this is the soft reference parser key. Otherwise empty.
      * @param string $softref_id Soft reference ID for key. Might be useful for replace operations.
@@ -434,9 +466,9 @@ class ReferenceIndex
      * @param string $fieldName Fieldname of source record (where reference is located)
      * @param string $flexPointer Pointer to location inside FlexForm structure where reference is located in [$field]
      * @param int $deleted Whether record is deleted-flagged or not
-     * @param string $referencedTable In database references the tablename the reference points to. Keyword "_FILE" indicates that $referenceString is a file reference, keyword "_STRING" indicates special usage (typ. SoftReference) in $referenceString
-     * @param int $referencedUid In database references the UID of the record (zero $referencedTable is "_FILE" or "_STRING")
-     * @param string $referenceString For "_FILE" or "_STRING" references: The filepath (relative to PATH_site or absolute) or other string.
+     * @param string $referencedTable In database references the tablename the reference points to. Keyword "_STRING" indicates special usage (typ. SoftReference) in $referenceString
+     * @param int $referencedUid In database references the UID of the record (zero $referencedTable is "_STRING")
+     * @param string $referenceString For "_STRING" references: The string.
      * @param int $sort The sorting order of references if many (the "group" or "select" TCA types). -1 if no sorting order is specified.
      * @param string $softReferenceKey If the reference is a soft reference, this is the soft reference parser key. Otherwise empty.
      * @param string $softReferenceId Soft reference ID for key. Might be useful for replace operations.
@@ -507,64 +539,7 @@ class ReferenceIndex
     protected function createEntryDataForDatabaseRelationsUsingRecord(string $tableName, array $record, string $fieldName, string $flexPointer, int $deleted, array $items)
     {
         foreach ($items as $sort => $i) {
-            $this->relations[] = $this->createEntryDataUsingRecord($tableName, $record, $fieldName, $flexPointer, $deleted, $i['table'], $i['id'], '', $sort);
-        }
-    }
-
-    /**
-     * Enter file references to ->relations array
-     *
-     * @param string $table Tablename of source record (where reference is located)
-     * @param int $uid UID of source record (where reference is located)
-     * @param string $fieldName Fieldname of source record (where reference is located)
-     * @param string $flexPointer Pointer to location inside FlexForm structure where reference is located in [field]
-     * @param int $deleted Whether record is deleted-flagged or not
-     * @param array $items Data array with file relations
-     */
-    public function createEntryData_fileRels($table, $uid, $fieldName, $flexPointer, $deleted, $items)
-    {
-        $uid = $uid ? (int)$uid : 0;
-        if (!$uid) {
-            return;
-        }
-        $this->createEntryDataForFileRelationsUsingRecord(
-            (string)$table,
-            $this->getRecordRawCached($table, $uid),
-            (string)$fieldName,
-            (string)$flexPointer,
-            $deleted ? (int)$deleted : 0,
-            (array)$items
-        );
-    }
-
-    /**
-     * Add file references to ->relations array based on fetched record
-     *
-     * @param string $tableName Tablename of source record (where reference is located)
-     * @param array $record Record from $tableName
-     * @param string $fieldName Fieldname of source record (where reference is located)
-     * @param string $flexPointer Pointer to location inside FlexForm structure where reference is located in $fieldName
-     * @param int $deleted Whether record is deleted-flagged or not
-     * @param array $items Data array with file relations
-     */
-    protected function createEntryDataForFileRelationsUsingRecord(string $tableName, array $record, string $fieldName, string $flexPointer, int $deleted, array $items)
-    {
-        foreach ($items as $sort => $i) {
-            $filePath = $i['ID_absFile'];
-            if (GeneralUtility::isFirstPartOfStr($filePath, PATH_site)) {
-                $filePath = PathUtility::stripPathSitePrefix($filePath);
-            }
-            $this->relations[] = $this->createEntryDataUsingRecord(
-                $tableName,
-                $record,
-                $fieldName,
-                $flexPointer,
-                $deleted,
-                '_FILE',
-                0,
-                $filePath,
-                $sort
-            );
+            $this->relations[] = $this->createEntryDataUsingRecord($tableName, $record, $fieldName, $flexPointer, $deleted, $i['table'], (int)$i['id'], '', $sort);
         }
     }
 
@@ -613,12 +588,7 @@ class ReferenceIndex
                         switch ((string)$el['subst']['type']) {
                             case 'db':
                                 list($referencedTable, $referencedUid) = explode(':', $el['subst']['recordRef']);
-                                $this->relations[] = $this->createEntryDataUsingRecord($tableName, $record, $fieldName, $flexPointer, $deleted, $referencedTable, $referencedUid, '', -1, $spKey, $subKey);
-                                break;
-                            case 'file_reference':
-                                // not used (see getRelations()), but fallback to file
-                            case 'file':
-                                $this->relations[] = $this->createEntryDataUsingRecord($tableName, $record, $fieldName, $flexPointer, $deleted, '_FILE', 0, $el['subst']['relFileName'], -1, $spKey, $subKey);
+                                $this->relations[] = $this->createEntryDataUsingRecord($tableName, $record, $fieldName, $flexPointer, $deleted, $referencedTable, (int)$referencedUid, '', -1, $spKey, $subKey);
                                 break;
                             case 'string':
                                 $this->relations[] = $this->createEntryDataUsingRecord($tableName, $record, $fieldName, $flexPointer, $deleted, '_STRING', 0, $el['subst']['tokenValue'], -1, $spKey, $subKey);
@@ -639,7 +609,7 @@ class ReferenceIndex
     /**
      * Returns relation information for a $table/$row-array
      * Traverses all fields in input row which are configured in TCA/columns
-     * It looks for hard relations to files and records in the TCA types "select" and "group"
+     * It looks for hard relations to records in the TCA types "select" and "group"
      *
      * @param string $table Table name
      * @param array $row Row from table
@@ -655,36 +625,6 @@ class ReferenceIndex
         foreach ($row as $field => $value) {
             if ($this->shouldExcludeTableColumnFromReferenceIndex($table, $field, $onlyField) === false) {
                 $conf = $GLOBALS['TCA'][$table]['columns'][$field]['config'];
-                // Add files
-                $resultsFromFiles = $this->getRelations_procFiles($value, $conf, $uid);
-                if (!empty($resultsFromFiles)) {
-                    // We have to fill different arrays here depending on the result.
-                    // internal_type file is still a relation of type file and
-                    // since http://forge.typo3.org/issues/49538 internal_type file_reference
-                    // is a database relation to a sys_file record
-                    $fileResultsFromFiles = [];
-                    $dbResultsFromFiles = [];
-                    foreach ($resultsFromFiles as $resultFromFiles) {
-                        if (isset($resultFromFiles['table']) && $resultFromFiles['table'] === 'sys_file') {
-                            $dbResultsFromFiles[] = $resultFromFiles;
-                        } else {
-                            // Creates an entry for the field with all the files:
-                            $fileResultsFromFiles[] = $resultFromFiles;
-                        }
-                    }
-                    if (!empty($fileResultsFromFiles)) {
-                        $outRow[$field] = [
-                            'type' => 'file',
-                            'newValueFiles' => $fileResultsFromFiles
-                        ];
-                    }
-                    if (!empty($dbResultsFromFiles)) {
-                        $outRow[$field] = [
-                            'type' => 'db',
-                            'itemArray' => $dbResultsFromFiles
-                        ];
-                    }
-                }
                 // Add a softref definition for link fields if the TCA does not specify one already
                 if ($conf['type'] === 'input' && $conf['renderType'] === 'inputLink' && empty($conf['softref'])) {
                     $conf['softref'] = 'typolink';
@@ -698,17 +638,16 @@ class ReferenceIndex
                         'itemArray' => $resultsFromDatabase
                     ];
                 }
-                // For "flex" fieldtypes we need to traverse the structure looking for file and db references of course!
+                // For "flex" fieldtypes we need to traverse the structure looking for db references of course!
                 if ($conf['type'] === 'flex') {
                     // Get current value array:
                     // NOTICE: failure to resolve Data Structures can lead to integrity problems with the reference index. Please look up
                     // the note in the JavaDoc documentation for the function FlexFormTools->getDataStructureIdentifier()
                     $currentValueArray = GeneralUtility::xml2array($value);
-                    // Traversing the XML structure, processing files:
+                    // Traversing the XML structure, processing:
                     if (is_array($currentValueArray)) {
                         $this->temp_flexRelations = [
                             'db' => [],
-                            'file' => [],
                             'softrefs' => []
                         ];
                         // Create and call iterator object:
@@ -749,13 +688,14 @@ class ReferenceIndex
     }
 
     /**
-     * Callback function for traversing the FlexForm structure in relation to finding file and DB references!
+     * Callback function for traversing the FlexForm structure in relation to finding DB references!
      *
      * @param array $dsArr Data structure for the current value
      * @param mixed $dataValue Current value
      * @param array $PA Additional configuration used in calling function
      * @param string $structurePath Path of value in DS structure
-     * @see DataHandler::checkValue_flex_procInData_travDS(),FlexFormTools::traverseFlexFormXMLData()
+     * @see DataHandler::checkValue_flex_procInData_travDS()
+     * @see FlexFormTools::traverseFlexFormXMLData()
      */
     public function getRelations_flexFormCallBack($dsArr, $dataValue, $PA, $structurePath)
     {
@@ -768,29 +708,6 @@ class ReferenceIndex
             $PA['uid'],
             $PA['field']
         ];
-        // Add files
-        $resultsFromFiles = $this->getRelations_procFiles($dataValue, $dsConf, $uid);
-        if (!empty($resultsFromFiles)) {
-            // We have to fill different arrays here depending on the result.
-            // internal_type file is still a relation of type file and
-            // since http://forge.typo3.org/issues/49538 internal_type file_reference
-            // is a database relation to a sys_file record
-            $fileResultsFromFiles = [];
-            $dbResultsFromFiles = [];
-            foreach ($resultsFromFiles as $resultFromFiles) {
-                if (isset($resultFromFiles['table']) && $resultFromFiles['table'] === 'sys_file') {
-                    $dbResultsFromFiles[] = $resultFromFiles;
-                } else {
-                    $fileResultsFromFiles[] = $resultFromFiles;
-                }
-            }
-            if (!empty($fileResultsFromFiles)) {
-                $this->temp_flexRelations['file'][$structurePath] = $fileResultsFromFiles;
-            }
-            if (!empty($dbResultsFromFiles)) {
-                $this->temp_flexRelations['db'][$structurePath] = $dbResultsFromFiles;
-            }
-        }
         // Add a softref definition for link fields if the TCA does not specify one already
         if ($dsConf['type'] === 'input' && $dsConf['renderType'] === 'inputLink' && empty($dsConf['softref'])) {
             $dsConf['softref'] = 'typolink';
@@ -826,66 +743,6 @@ class ReferenceIndex
     }
 
     /**
-     * Check field configuration if it is a file relation field and extract file relations if any
-     *
-     * @param string $value Field value
-     * @param array $conf Field configuration array of type "TCA/columns
-     * @param int $uid Field uid
-     * @return bool|array If field type is OK it will return an array with the files inside. Else FALSE
-     */
-    public function getRelations_procFiles($value, $conf, $uid)
-    {
-        if ($conf['type'] !== 'group' || ($conf['internal_type'] !== 'file' && $conf['internal_type'] !== 'file_reference')) {
-            return false;
-        }
-
-        // Collect file values in array:
-        if ($conf['MM']) {
-            $theFileValues = [];
-            $dbAnalysis = GeneralUtility::makeInstance(RelationHandler::class);
-            $dbAnalysis->start('', 'files', $conf['MM'], $uid);
-            foreach ($dbAnalysis->itemArray as $someval) {
-                if ($someval['id']) {
-                    $theFileValues[] = $someval['id'];
-                }
-            }
-        } else {
-            $theFileValues = explode(',', $value);
-        }
-        // Traverse the files and add them:
-        $uploadFolder = $conf['internal_type'] === 'file' ? $conf['uploadfolder'] : '';
-        $destinationFolder = $this->destPathFromUploadFolder($uploadFolder);
-        $newValueFiles = [];
-        foreach ($theFileValues as $file) {
-            if (trim($file)) {
-                $realFile = $destinationFolder . '/' . trim($file);
-                $newValueFile = [
-                    'filename' => basename($file),
-                    'ID' => md5($realFile),
-                    'ID_absFile' => $realFile
-                ];
-                // Set sys_file and id for referenced files
-                if ($conf['internal_type'] === 'file_reference') {
-                    try {
-                        $file = ResourceFactory::getInstance()->retrieveFileOrFolderObject($file);
-                        if ($file instanceof File || $file instanceof Folder) {
-                            // For setting this as sys_file relation later, the keys filename, ID and ID_absFile
-                            // have not to be included, because the are not evaluated for db relations.
-                            $newValueFile = [
-                                'table' => 'sys_file',
-                                'id' => $file->getUid()
-                            ];
-                        }
-                    } catch (\Exception $e) {
-                    }
-                }
-                $newValueFiles[] = $newValueFile;
-            }
-        }
-        return $newValueFiles;
-    }
-
-    /**
      * Check field configuration if it is a DB relation field and extract DB relations if any
      *
      * @param string $value Field value
@@ -899,13 +756,15 @@ class ReferenceIndex
         // Get IRRE relations
         if (empty($conf)) {
             return false;
-        } elseif ($conf['type'] === 'inline' && !empty($conf['foreign_table']) && empty($conf['MM'])) {
+        }
+        if ($conf['type'] === 'inline' && !empty($conf['foreign_table']) && empty($conf['MM'])) {
             $dbAnalysis = GeneralUtility::makeInstance(RelationHandler::class);
             $dbAnalysis->setUseLiveReferenceIds(false);
             $dbAnalysis->start($value, $conf['foreign_table'], '', $uid, $table, $conf);
             return $dbAnalysis->itemArray;
             // DB record lists:
-        } elseif ($this->isDbReferenceField($conf)) {
+        }
+        if ($this->isDbReferenceField($conf)) {
             $allowedTables = $conf['type'] === 'group' ? $conf['allowed'] : $conf['foreign_table'];
             if ($conf['MM_opposite_field']) {
                 return [];
@@ -1000,14 +859,6 @@ class ReferenceIndex
                                 return $error;
                             }
                             break;
-                        case 'file_reference':
-                            // not used (see getRelations()), but fallback to file
-                        case 'file':
-                            $error = $this->setReferenceValue_fileRels($referenceRecord, $fieldRelation['newValueFiles'], $newValue, $dataArray);
-                            if ($error) {
-                                return $error;
-                            }
-                            break;
                         case 'flex':
                             // DB references in FlexForms
                             if (is_array($fieldRelation['flexFormRels']['db'][$referenceRecord['flexpointer']])) {
@@ -1016,13 +867,6 @@ class ReferenceIndex
                                     return $error;
                                 }
                             }
-                            // File references in FlexForms
-                            if (is_array($fieldRelation['flexFormRels']['file'][$referenceRecord['flexpointer']])) {
-                                $error = $this->setReferenceValue_fileRels($referenceRecord, $fieldRelation['flexFormRels']['file'][$referenceRecord['flexpointer']], $newValue, $dataArray, $referenceRecord['flexpointer']);
-                                if ($error) {
-                                    return $error;
-                                }
-                            }
                             // Soft references in FlexForms
                             if ($referenceRecord['softref_key'] && is_array($fieldRelation['flexFormRels']['softrefs'][$referenceRecord['flexpointer']]['keys'][$referenceRecord['softref_key']])) {
                                 $error = $this->setReferenceValue_softreferences($referenceRecord, $fieldRelation['flexFormRels']['softrefs'][$referenceRecord['flexpointer']], $newValue, $dataArray, $referenceRecord['flexpointer']);
@@ -1042,21 +886,19 @@ class ReferenceIndex
                     // Data Array, now ready to be sent to DataHandler
                     if ($returnDataArray) {
                         return $dataArray;
-                    } else {
-                        // Execute CMD array:
-                        $dataHandler = GeneralUtility::makeInstance(DataHandler::class);
-                        $dataHandler->dontProcessTransformations = true;
-                        $dataHandler->bypassWorkspaceRestrictions = true;
-                        $dataHandler->bypassFileHandling = true;
-                        // Otherwise this cannot update things in deleted records...
-                        $dataHandler->bypassAccessCheckForRecords = true;
-                        // Check has been done previously that there is a backend user which is Admin and also in live workspace
-                        $dataHandler->start($dataArray, []);
-                        $dataHandler->process_datamap();
-                        // Return errors if any:
-                        if (!empty($dataHandler->errorLog)) {
-                            return LF . 'DataHandler:' . implode((LF . 'DataHandler:'), $dataHandler->errorLog);
-                        }
+                    }
+                    // Execute CMD array:
+                    $dataHandler = GeneralUtility::makeInstance(DataHandler::class);
+                    $dataHandler->dontProcessTransformations = true;
+                    $dataHandler->bypassWorkspaceRestrictions = true;
+                    // Otherwise this cannot update things in deleted records...
+                    $dataHandler->bypassAccessCheckForRecords = true;
+                    // Check has been done previously that there is a backend user which is Admin and also in live workspace
+                    $dataHandler->start($dataArray, []);
+                    $dataHandler->process_datamap();
+                    // Return errors if any:
+                    if (!empty($dataHandler->errorLog)) {
+                        return LF . 'DataHandler:' . implode(LF . 'DataHandler:', $dataHandler->errorLog);
                     }
                 }
             }
@@ -1108,51 +950,10 @@ class ReferenceIndex
     }
 
     /**
-     * Setting a value for a reference for a FILE field:
-     *
-     * @param array $refRec sys_refindex record
-     * @param array $itemArray Array of references from that field
-     * @param string $newValue Value to substitute current value with (or NULL to unset it)
-     * @param array $dataArray Data array in which the new value is set (passed by reference)
-     * @param string $flexPointer Flexform pointer, if in a flex form field.
-     * @return string Error message if any, otherwise FALSE = OK
-     */
-    public function setReferenceValue_fileRels($refRec, $itemArray, $newValue, &$dataArray, $flexPointer = '')
-    {
-        $ID_absFile = PathUtility::stripPathSitePrefix($itemArray[$refRec['sorting']]['ID_absFile']);
-        if ($ID_absFile === (string)$refRec['ref_string'] && $refRec['ref_table'] === '_FILE') {
-            // Setting or removing value:
-            // Remove value:
-            if ($newValue === null) {
-                unset($itemArray[$refRec['sorting']]);
-            } else {
-                $itemArray[$refRec['sorting']]['filename'] = $newValue;
-            }
-            // Traverse and compile new list of records:
-            $saveValue = [];
-            foreach ($itemArray as $fileInfo) {
-                $saveValue[] = $fileInfo['filename'];
-            }
-            // Set in data array:
-            if ($flexPointer) {
-                $flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
-                $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']]['data'] = [];
-                $flexFormTools->setArrayValueByPath(substr($flexPointer, 0, -1), $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']]['data'], implode(',', $saveValue));
-            } else {
-                $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']] = implode(',', $saveValue);
-            }
-        } else {
-            return 'ERROR: either "' . $refRec['ref_table'] . '" was not "_FILE" or file PATH_site+"' . $refRec['ref_string'] . '" did not match that of the record ("' . $itemArray[$refRec['sorting']]['ID_absFile'] . '") in sorting index "' . $refRec['sorting'] . '"';
-        }
-
-        return false;
-    }
-
-    /**
      * Setting a value for a soft reference token
      *
      * @param array $refRec sys_refindex record
-     * @param array $softref Array of soft reference occurencies
+     * @param array $softref Array of soft reference occurrences
      * @param string $newValue Value to substitute current value with
      * @param array $dataArray Data array in which the new value is set (passed by reference)
      * @param string $flexPointer Flexform pointer, if in a flex form field.
@@ -1173,7 +974,7 @@ class ReferenceIndex
             }
         }
         // Set in data array:
-        if (!strstr($softref['tokenizedContent'], '{softref:')) {
+        if (strpos($softref['tokenizedContent'], '{softref:') === false) {
             if ($flexPointer) {
                 $flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
                 $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']]['data'] = [];
@@ -1222,8 +1023,6 @@ class ReferenceIndex
         return
             $this->isDbReferenceField($configuration)
             ||
-            ($configuration['type'] === 'group' && ($configuration['internal_type'] === 'file' || $configuration['internal_type'] === 'file_reference')) // getRelations_procFiles
-            ||
             ($configuration['type'] === 'input' && $configuration['renderType'] === 'inputLink') // getRelations_procDB
             ||
             $configuration['type'] === 'flex'
@@ -1265,20 +1064,6 @@ class ReferenceIndex
     }
 
     /**
-     * Returns destination path to an upload folder given by $folder
-     *
-     * @param string $folder Folder relative to PATH_site
-     * @return string Input folder prefixed with PATH_site. No checking for existence is done. Output must be a folder without trailing slash.
-     */
-    public function destPathFromUploadFolder($folder)
-    {
-        if (!$folder) {
-            return substr(PATH_site, 0, -1);
-        }
-        return PATH_site . $folder;
-    }
-
-    /**
      * Updating Index (External API)
      *
      * @param bool $testOnly If set, only a test
@@ -1322,9 +1107,11 @@ class ReferenceIndex
                     ->from($tableName)
                     ->execute();
             } catch (DBALException $e) {
-                // Table exists in $TCA but does not exist in the database
-                // @todo: improve / change message and add actual sql error?
-                GeneralUtility::sysLog(sprintf('Table "%s" exists in $TCA but does not exist in the database. You should run the Database Analyzer in the Install Tool to fix this.', $tableName), 'core', GeneralUtility::SYSLOG_SEVERITY_ERROR);
+                // Table exists in TCA but does not exist in the database
+                $msg = 'Table "' .
+                        $tableName .
+                        '" exists in TCA but does not exist in the database. You should run the Database Analyzer in the Install Tool to fix this.';
+                $this->logger->error($msg, ['exception' => $e]);
                 continue;
             }
 
@@ -1348,14 +1135,7 @@ class ReferenceIndex
 
             // Subselect based queries only work on the same connection
             if ($refIndexConnectionName !== $tableConnectionName) {
-                GeneralUtility::sysLog(
-                    sprintf(
-                        'Not checking table "%s" for lost indexes, "sys_refindex" table uses a different connection',
-                        $tableName
-                    ),
-                    'core',
-                    GeneralUtility::SYSLOG_SEVERITY_ERROR
-                );
+                $this->logger->error('Not checking table "' . $tableName . '" for lost indexes, "sys_refindex" table uses a different connection');
                 continue;
             }
 
@@ -1449,12 +1229,9 @@ class ReferenceIndex
             $recordsCheckedString,
             $errorCount ? FlashMessage::ERROR : FlashMessage::OK
         );
-        /** @var $flashMessageService \TYPO3\CMS\Core\Messaging\FlashMessageService */
-        $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
-        /** @var $defaultFlashMessageQueue \TYPO3\CMS\Core\Messaging\FlashMessageQueue */
-        $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
-        $defaultFlashMessageQueue->enqueue($flashMessage);
-        $bodyContent = $defaultFlashMessageQueue->renderFlashMessages();
+
+        $flashMessageRenderer = GeneralUtility::makeInstance(FlashMessageRendererResolver::class)->resolve();
+        $bodyContent = $flashMessageRenderer->render([$flashMessage]);
         if ($cli_echo) {
             echo $recordsCheckedString . ($errorCount ? 'Updates: ' . $errorCount : 'Index Integrity was perfect!') . LF;
         }
@@ -1477,6 +1254,10 @@ class ReferenceIndex
      * - Relations may change during indexing, which is why only the origin record is cached and all relations are re-process even when repeating
      *   indexing of the same origin record
      *
+     * Please note that the cache is disabled by default but can be enabled using $this->enableRuntimeCaches()
+     * due to possible side-effects while handling references that were changed during one single
+     * request.
+     *
      * @param string $tableName
      * @param int $uid
      * @return array|false
@@ -1484,13 +1265,15 @@ class ReferenceIndex
     protected function getRecordRawCached(string $tableName, int $uid)
     {
         $recordCacheId = $tableName . ':' . $uid;
-        if (!isset($this->recordCache[$recordCacheId])) {
+        if (!$this->useRuntimeCache || !isset($this->recordCache[$recordCacheId])) {
 
             // Fetch fields of the table which might contain relations
             $cacheId = static::$cachePrefixTableRelationFields . $tableName;
-            if (!$this->runtimeCache->has($cacheId)) {
+            if (!$this->useRuntimeCache || !$this->runtimeCache->has($cacheId)) {
                 $tableRelationFields = $this->fetchTableRelationFields($tableName);
-                $this->runtimeCache->set($cacheId, $tableRelationFields);
+                if ($this->useRuntimeCache) {
+                    $this->runtimeCache->set($cacheId, $tableRelationFields);
+                }
             } else {
                 $tableRelationFields = $this->runtimeCache->get($cacheId);
             }
@@ -1580,6 +1363,24 @@ class ReferenceIndex
     }
 
     /**
+     * Enables the runtime-based caches
+     * Could lead to side effects, depending if the reference index instance is run multiple times
+     * while records would be changed.
+     */
+    public function enableRuntimeCache()
+    {
+        $this->useRuntimeCache = true;
+    }
+
+    /**
+     * Disables the runtime-based cache
+     */
+    public function disableRuntimeCache()
+    {
+        $this->useRuntimeCache = false;
+    }
+
+    /**
      * Returns the current BE user.
      *
      * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication