2 declare(strict_types = 1);
18 use Symfony\Component\Console\Command\Command;
19 use Symfony\Component\Console\Input\InputInterface;
20 use Symfony\Component\Console\Input\InputOption;
21 use Symfony\Component\Console\Output\OutputInterface;
22 use Symfony\Component\Console\Style\SymfonyStyle;
41 ->setDescription(
'Find all file references from records pointing to a missing (non-existing) file.')
44 - a perfect integrity of the reference index table (always update the reference index table before using this tool!)
45 - relevant soft reference parsers applied everywhere file references are used inline
47 Files may be missing for these reasons (except software bugs):
48 - someone manually deleted the file inside fileadmin/ or another user maintained folder. If the reference was a soft reference (opposite to a DataHandler managed file relation from "group" type fields), technically it is not an error although it might be a mistake that someone did so.
49 - someone manually deleted the file inside the uploads/ folder (typically containing managed files) which is an error since no user interaction should take place there.
51 Manual repair suggestions (using --dry-run):
52 - Managed files: You might be able to locate the file and re-insert it in the correct location. However, no automatic fix can do that for you.
53 - Soft References: You should investigate each case and edit the content accordingly. A soft reference to a file could be in an HTML image tag (for example <img src="missing_file.jpg" />) and you would have to either remove the whole tag, change the filename or re-create the missing file.
55 If the option "--dry-run" is not set, all managed files (TCA/FlexForm attachments) will silently remove the reference
56 from the record since the file is missing. For this reason you might prefer a manual approach instead.
57 All soft references with missing files require manual fix if you consider it an error.
59 If you want to get more detailed information, use the --verbose option.')
63 InputOption::VALUE_NONE,
64 'If this option is set, the references will not be removed, but just the output which files would be deleted are shown'
69 InputOption::VALUE_NONE,
70 'Setting this option automatically updates the reference index and does not ask on command line. Alternatively, use -n to avoid the interactive mode'
88 $io =
new SymfonyStyle($input,
$output);
89 $io->title($this->getDescription());
91 $dryRun = $input->hasOption(
'dry-run') && $input->getOption(
'dry-run') !=
false ? true :
false;
98 if (count($missingSoftReferencedFiles)) {
99 $io->note(
'Found ' . count($missingSoftReferencedFiles) .
' soft-referenced files that need manual repair.');
100 $io->listing($missingSoftReferencedFiles);
105 if (count($missingReferencedFiles)) {
106 $io->note(
'Found ' . count($missingReferencedFiles) .
' references to non-existing files.');
109 $io->success(
'All references were updated accordingly.');
112 if (!count($missingSoftReferencedFiles) && !count($missingReferencedFiles)) {
113 $io->success(
'Nothing to do, no missing files found. Everything is in place.');
129 $io->note(
'Finding missing files referenced by TYPO3 requires a clean reference index (sys_refindex)');
130 if ($input->hasOption(
'update-refindex') && $input->getOption(
'update-refindex')) {
131 $updateReferenceIndex =
true;
132 } elseif ($input->isInteractive()) {
133 $updateReferenceIndex = $io->confirm(
'Should the reference index be updated right now?',
false);
135 $updateReferenceIndex =
false;
139 if ($updateReferenceIndex) {
140 $referenceIndex = GeneralUtility::makeInstance(ReferenceIndex::class);
141 $referenceIndex->updateIndex(
false, !$io->isQuiet());
143 $io->writeln(
'Reference index is assumed to be up to date, continuing.');
156 $missingReferences = [];
158 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
159 ->getQueryBuilderForTable(
'sys_refindex');
161 $result = $queryBuilder
163 ->from(
'sys_refindex')
165 $queryBuilder->expr()->eq(
'ref_table', $queryBuilder->createNamedParameter(
'_FILE', \PDO::PARAM_STR)),
166 $queryBuilder->expr()->isNull(
'softref_key')
171 while ($record = $result->fetch()) {
178 return $missingReferences;
189 $missingReferences = [];
191 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
192 ->getQueryBuilderForTable(
'sys_refindex');
194 $result = $queryBuilder
196 ->from(
'sys_refindex')
198 $queryBuilder->expr()->eq(
'ref_table', $queryBuilder->createNamedParameter(
'_FILE', \PDO::PARAM_STR)),
199 $queryBuilder->expr()->isNotNull(
'softref_key')
204 while ($record = $result->fetch()) {
210 return $missingReferences;
221 if (strpos($fileName,
'#') !==
false) {
222 [$fileName] = explode(
'#', $fileName);
236 foreach ($missingManagedFiles as $fileName => $references) {
237 if ($io->isVeryVerbose()) {
238 $io->writeln(
'Deleting references to missing file "' . $fileName .
'"');
240 foreach ($references as $hash => $recordReference) {
241 $io->writeln(
'Removing reference in record "' . $recordReference .
'"');
243 $sysRefObj = GeneralUtility::makeInstance(ReferenceIndex::class);
244 $error = $sysRefObj->setReferenceValue($hash,
null);
246 $io->error(
'ReferenceIndex::setReferenceValue() reported "' . $error .
'"');
261 return $record[
'tablename'] .
':' . $record[
'recuid'] .
':' . $record[
'field'] .
':' . $record[
'flexpointer'] .
':' . $record[
'softref_key'] . ($record[
'deleted'] ?
' (DELETED)' :
'');