‪TYPO3CMS  ‪main
CleanUpLocalProcessedFilesCommand.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use Symfony\Component\Console\Command\Command;
21 use Symfony\Component\Console\Helper\QuestionHelper;
22 use Symfony\Component\Console\Input\InputInterface;
23 use Symfony\Component\Console\Input\InputOption;
24 use Symfony\Component\Console\Output\OutputInterface;
25 use Symfony\Component\Console\Question\ConfirmationQuestion;
26 use Symfony\Component\Console\Style\SymfonyStyle;
29 
31 {
32  public function ‪__construct(
33  private readonly ‪CleanUpLocalProcessedFilesService $cleanProcessedFilesService
34  ) {
35  parent::__construct();
36  }
37 
38  protected function ‪configure(): void
39  {
40  $this
41  ->setDescription(
42  'Deletes local processed files from local storage that are no longer referenced and ' .
43  'deletes references to processed files that do no longer exist'
44  )
45  ->setHelp('If you want to get more detailed information, use the --verbose option.')
46  ->addOption(
47  'dry-run',
48  null,
49  InputOption::VALUE_NONE,
50  'If set, the records and files which would be deleted are displayed.'
51  )
52  ->addOption(
53  'force',
54  'f',
55  InputOption::VALUE_NONE,
56  'Force cleanup. When set the confirmation question will be skipped. When using --no-interaction, --force will be set automatically.'
57  );
58  }
59 
64  protected function ‪execute(InputInterface $input, OutputInterface ‪$output): int
65  {
66  $files = $this->cleanProcessedFilesService->getFilesToClean();
67  $records = $this->cleanProcessedFilesService->getRecordsToClean();
68 
69  if (‪$output->isVerbose()) {
70  foreach ($records as ‪$record) {
71  ‪$output->writeln('[RECORD] Would delete ' . ‪$record['identifier'] . ' UID:' . ‪$record['uid']);
72  }
73 
75  foreach ($files as $file) {
76  $path = ‪PathUtility::stripPathSitePrefix($file->getRealPath());
77  ‪$output->writeln('[FILE] Would delete ' . $path);
78  }
79  }
80 
81  if ($files === [] && $records === []) {
82  ‪$output->writeln('<fg=green>✓</> No processed files or processed records found. Nothing to be done');
83 
84  return Command::SUCCESS;
85  }
86 
87  ‪$output->writeln('Found <options=bold,underscore>' . count($files) . ' files</> and <options=bold,underscore>' . (count($records) + count($files)) . ' processed records</>');
88 
89  if ($input->getOption('dry-run')) {
90  return Command::SUCCESS;
91  }
92 
93  // Do not ask for confirmation when running the command in EXT:scheduler
94  if (!$input->isInteractive()) {
95  $input->setOption('force', true);
96  }
97 
98  if (!$input->getOption('force')) {
100  $questionHelper = $this->getHelper('question');
101  $fileDeleteQuestion = new ConfirmationQuestion(
102  'Are you sure you want to delete these processed files and records [default: no] ? ',
103  false
104  );
105  $answerDeleteFiles = $questionHelper->ask($input, ‪$output, $fileDeleteQuestion);
106  }
107 
108  if (($answerDeleteFiles ?? false) || $input->getOption('force')) {
109  [$success, $error] = $this->‪deleteFile($input, ‪$output, $files);
110  // Reload the list auf records to get the files deleted using deleteFile() as well
111  $recordsIncludingDeleted = $this->cleanProcessedFilesService->getRecordsToClean();
112  $deletedRecordsCount = $this->cleanProcessedFilesService->deleteRecord(array_column($recordsIncludingDeleted, 'uid'));
113 
114  $failedRecordCount = count($records) - $deletedRecordsCount;
115  $failedRecords = '';
116  if ($failedRecordCount > 0) {
117  $failedRecords = 'Failed to delete <fg=red>' . $failedRecordCount . '</> records.';
118  }
119 
120  $failedFiles = '';
121  if (count($error) > 0) {
122  $failedFiles = 'Failed to delete <fg=red>' . count($error) . '</> files.';
123  }
124 
125  ‪$output->writeln('');
126 
127  if (count($records) > 0) {
128  ‪$output->writeln('Deleted <fg=green>' . $deletedRecordsCount . '</> processed records. ' . $failedRecords);
129  }
130 
131  if (count($files) > 0) {
132  ‪$output->writeln('Deleted <fg=green>' . count($success) . '</> processed files. ' . $failedFiles);
133  }
134  }
135 
136  return Command::SUCCESS;
137  }
138 
139  protected function ‪deleteFile(InputInterface $input, OutputInterface ‪$output, array $files): array
140  {
141  $isVerbose = ‪$output->isVerbose();
142  if (!$isVerbose) {
143  $io = new SymfonyStyle($input, ‪$output);
144  $progressBar = $io->createProgressBar(count($files));
145  }
146  $error = [];
147  $success = [];
148 
149  foreach ($files as $file) {
150  $path = ‪PathUtility::stripPathSitePrefix($file->getRealPath());
151  if (unlink($file->getRealPath()) === false) {
152  $error[] = $file;
153  $isVerbose ? ‪$output->writeln('[FILE] Failed to delete ' . $path) : $progressBar->advance();
154  } else {
155  $success[] = $file;
156  $isVerbose ? ‪$output->writeln('[FILE] Successfully deleted ' . $path) : $progressBar->advance();
157  }
158  }
159 
160  return [$success, $error];
161  }
162 }
‪TYPO3\CMS\Core\Utility\PathUtility\stripPathSitePrefix
‪static stripPathSitePrefix(string $path)
Definition: PathUtility.php:428
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:27
‪TYPO3\CMS\Lowlevel\Command\CleanUpLocalProcessedFilesCommand\execute
‪execute(InputInterface $input, OutputInterface $output)
Definition: CleanUpLocalProcessedFilesCommand.php:64
‪TYPO3\CMS\Lowlevel\Command\CleanUpLocalProcessedFilesCommand\deleteFile
‪deleteFile(InputInterface $input, OutputInterface $output, array $files)
Definition: CleanUpLocalProcessedFilesCommand.php:139
‪TYPO3\CMS\Webhooks\Message\$record
‪identifier readonly int readonly array $record
Definition: PageModificationMessage.php:36
‪TYPO3\CMS\Lowlevel\Command\CleanUpLocalProcessedFilesCommand
Definition: CleanUpLocalProcessedFilesCommand.php:31
‪$output
‪$output
Definition: annotationChecker.php:119
‪TYPO3\CMS\Lowlevel\Command\CleanUpLocalProcessedFilesCommand\__construct
‪__construct(private readonly CleanUpLocalProcessedFilesService $cleanProcessedFilesService)
Definition: CleanUpLocalProcessedFilesCommand.php:32
‪TYPO3\CMS\Lowlevel\Command\CleanUpLocalProcessedFilesCommand\configure
‪configure()
Definition: CleanUpLocalProcessedFilesCommand.php:38
‪TYPO3\CMS\Lowlevel\Service\CleanUpLocalProcessedFilesService
Definition: CleanUpLocalProcessedFilesService.php:35
‪TYPO3\CMS\Lowlevel\Command
Definition: CleanFlexFormsCommand.php:18