>> ot:cron run clean-temp-files --preview * >>> ot:cron run clean-temp-files */ class CleanTempFiles extends BaseCronJob { /** * Delay before removing a temporary file (in days). */ private const DELETE_OLDER_THAN = 60; #[Pure] public function __construct( private Connection $connection, private FileRepository $fileRepository, private LocalStorage $storage, ) { parent::__construct(); } /** * Preview the result of the execution, without actually deleting anything. * * @throws \Exception */ public function preview(): void { $maxDate = DatesUtils::new(); $maxDate->sub(new \DateInterval('P'.self::DELETE_OLDER_THAN.'D')); $files = $this->listFilesToDelete($maxDate); $total = count($files); $this->ui->print($total.' temporary files to be removed'); $this->ui->print('> Printing the first and last 50 :'); $i = 0; foreach ($files as $file) { ++$i; if ($i > 50 && ($total - $i) > 50) { continue; } if (($total - $i) === 50) { $this->ui->print(' (...)'); } $this->ui->print(' * '.$file->getPath()); } } /** * Proceed to the deletion of the files and the purge of the DB. * * @throws \Exception */ public function execute(): void { $maxDate = DatesUtils::new(); $maxDate->sub(new \DateInterval('P'.self::DELETE_OLDER_THAN.'D')); $files = $this->listFilesToDelete($maxDate); $this->logger->info(count($files).' temporary files to be removed'); $this->deleteFiles($files); } /** * List the files to delete in the DB. * * @return array * * @throws \Exception */ protected function listFilesToDelete(\DateTime $maxDate): array { $this->ui->print('List temporary files created before '.$maxDate->format('c')); $queryBuilder = $this->fileRepository->createQueryBuilder('f'); $queryBuilder->select(); $this->getQueryConditions($queryBuilder, $maxDate); return $queryBuilder->getQuery()->getResult(); } /** * Delete the files. * * @param array $files */ protected function deleteFiles(array $files): void { $total = count($files); $this->logger->info($total.' temporary files to be removed'); $this->connection->setAutoCommit(false); $queryBuilder = $this->fileRepository->createQueryBuilder('f'); $this->logger->info('Deleting files...'); $i = 0; $deleted = 0; $this->ui->progress(0, $total); foreach ($files as $file) { $this->connection->beginTransaction(); $this->ui->progress($i, $total); ++$i; try { // Delete from disk $this->storage->hardDelete($file); // Remove from DB $queryBuilder->delete()->where('f.id = :id')->setParameter('id', $file->getId()); $this->connection->commit(); ++$deleted; } catch (\RuntimeException|\InvalidArgumentException $e) { // Non blocking errors $this->connection->rollback(); $this->logger->error('ERROR : '.$e->getMessage()); } catch (\Exception $exception) { $this->connection->rollback(); throw $exception; } } $this->logger->info($deleted.' files deleted'); } protected function getQueryConditions(QueryBuilder $queryBuilder, \DateTime $maxDate): void { $queryBuilder ->andWhere( $queryBuilder->expr()->orX( $queryBuilder->expr()->eq('f.isTemporaryFile', ':temporaryTrue'), $queryBuilder->expr()->eq('f.status', ':status') ) ) ->andWhere($queryBuilder->expr()->eq('f.host', ':host')) ->andWhere( $queryBuilder->expr()->orX( $queryBuilder->expr()->lt('f.createDate', ':maxDate'), $queryBuilder->expr()->isNull('f.createDate') ) ) ->setParameter('temporaryTrue', true) ->setParameter('host', FileHostEnum::AP2I) ->setParameter('status', FileStatusEnum::DELETED) ->setParameter('maxDate', $maxDate->format('Y-m-d')) ; } }