Passed
Push — master ( 21fb18...e0b12e )
by
unknown
17:31
created

statsFromTypo3tempProcessed()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 11
c 1
b 0
f 0
dl 0
loc 18
rs 9.9
cc 2
nc 2
nop 0
1
<?php
2
3
/*
4
 * This file is part of the TYPO3 CMS project.
5
 *
6
 * It is free software; you can redistribute it and/or modify it under
7
 * the terms of the GNU General Public License, either version 2
8
 * of the License, or any later version.
9
 *
10
 * For the full copyright and license information, please read the
11
 * LICENSE.txt file that was distributed with this source code.
12
 *
13
 * The TYPO3 project - inspiring people to share!
14
 */
15
16
namespace TYPO3\CMS\Install\Service;
17
18
use Symfony\Component\Finder\Finder;
19
use Symfony\Component\Finder\SplFileInfo;
20
use TYPO3\CMS\Core\Core\Environment;
21
use TYPO3\CMS\Core\Resource\ProcessedFileRepository;
22
use TYPO3\CMS\Core\Resource\ResourceStorage;
23
use TYPO3\CMS\Core\Resource\StorageRepository;
24
use TYPO3\CMS\Core\Utility\GeneralUtility;
25
26
/**
27
 * Service class to manage typo3temp/assets and FAL storage
28
 * processed file statistics / cleanup.
29
 * @internal This class is only meant to be used within EXT:install and is not part of the TYPO3 Core API.
30
 */
31
class Typo3tempFileService
32
{
33
    private $processedFileRepository;
34
    private $storageRepository;
35
36
    public function __construct(ProcessedFileRepository $processedFileRepository, StorageRepository $storageRepository)
37
    {
38
        $this->processedFileRepository = $processedFileRepository;
39
        $this->storageRepository = $storageRepository;
40
    }
41
42
    /**
43
     * Returns a list of directory names in typo3temp/assets and their number of files
44
     *
45
     * @return array
46
     */
47
    public function getDirectoryStatistics(): array
48
    {
49
        return array_merge(
50
            $this->statsFromTypo3temp(),
51
            $this->statsFromStorages()
52
        );
53
    }
54
55
    public function getStatsFromStorageByUid(int $storageUid): array
56
    {
57
        if ($storageUid === 0) {
58
            return $this->statsFromTypo3tempProcessed();
59
        }
60
61
        $storage = $this->storageRepository->findByUid($storageUid);
62
        return $this->getStatsFromStorage($storage);
0 ignored issues
show
Bug introduced by
It seems like $storage can also be of type null; however, parameter $storage of TYPO3\CMS\Install\Servic...::getStatsFromStorage() does only seem to accept TYPO3\CMS\Core\Resource\ResourceStorage, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

62
        return $this->getStatsFromStorage(/** @scrutinizer ignore-type */ $storage);
Loading history...
63
    }
64
65
    /**
66
     * Directory statistics for typo3temp/assets folders with some
67
     * special handling for legacy processed file storage _processed_
68
     *
69
     * @return array
70
     */
71
    protected function statsFromTypo3temp(): array
72
    {
73
        $stats = [];
74
        $typo3TempAssetsPath = '/typo3temp/assets/';
75
        $basePath = Environment::getPublicPath() . $typo3TempAssetsPath;
76
        if (is_dir($basePath)) {
77
            $dirFinder = new Finder();
78
            $dirsInAssets = $dirFinder->directories()->in($basePath)->depth(0)->sortByName();
79
            foreach ($dirsInAssets as $dirInAssets) {
80
                /** @var SplFileInfo $dirInAssets */
81
                $fileFinder = new Finder();
82
                $fileCount = $fileFinder->files()->in($dirInAssets->getPathname())->count();
83
                $folderName = $dirInAssets->getFilename();
84
                $stat = [
85
                    'directory' => $typo3TempAssetsPath . $folderName,
86
                    'numberOfFiles' => $fileCount,
87
                ];
88
                if ($folderName === '_processed_') {
89
                    // The processed file storage for legacy files (eg. TCA type=group internal_type=file)
90
                    // gets the storageUid set, so this one can be removed via FAL functionality
91
                    $stat['storageUid'] = 0;
92
                }
93
                $stats[] = $stat;
94
            }
95
        }
96
        return $stats;
97
    }
98
99
    /**
100
     * Directory statistics for typo3temp/assets/_processed_ folder
101
     *
102
     * @return array
103
     */
104
    protected function statsFromTypo3tempProcessed(): array
105
    {
106
        $typo3TempProcessedAssetsPath = '/typo3temp/assets/_processed_/';
107
108
        $stats = [
109
            'storageUid' => 0,
110
            'directory' => $typo3TempProcessedAssetsPath,
111
        ];
112
113
        $basePath = Environment::getPublicPath() . $typo3TempProcessedAssetsPath;
114
        if (is_dir($basePath)) {
115
            $fileFinder = new Finder();
116
            $stats['numberOfFiles'] = $fileFinder->files()->in($basePath)->count();
117
        } else {
118
            $stats['numberOfFiles'] = 0;
119
        }
120
121
        return $stats;
122
    }
123
124
    /**
125
     * Directory statistics for configured FAL storages.
126
     *
127
     * @return array
128
     */
129
    protected function statsFromStorages(): array
130
    {
131
        $stats = [];
132
        $storages = $this->storageRepository->findAll();
133
        foreach ($storages as $storage) {
134
            if ($storage->isOnline()) {
135
                $stats[] = $this->getStatsFromStorage($storage);
136
            }
137
        }
138
        return $stats;
139
    }
140
141
    protected function getStatsFromStorage(ResourceStorage $storage): array
142
    {
143
        $storageConfiguration = $storage->getConfiguration();
144
        $storageBasePath = rtrim($storageConfiguration['basePath'], '/');
145
        $processedPath = '/' . $storageBasePath . $storage->getProcessingFolder()->getIdentifier();
146
        $numberOfFiles = $this->processedFileRepository->countByStorage($storage);
147
148
        return [
149
            'directory' => $processedPath,
150
            'numberOfFiles' => $numberOfFiles,
151
            'storageUid' => $storage->getUid()
152
        ];
153
    }
154
155
    /**
156
     * Clear processed files. The sys_file_processedfile table is cleared for
157
     * given storage uid and the physical files of local processed storages are deleted.
158
     *
159
     * @return int 0 if all went well, if >0 this number of files that could not be deleted
160
     */
161
    public function clearProcessedFiles(int $storageUid): int
162
    {
163
        $repository = GeneralUtility::makeInstance(ProcessedFileRepository::class);
164
        return $repository->removeAll($storageUid);
165
    }
166
167
    /**
168
     * Clears files and folders in a typo3temp/assets/ folder (not _processed_!)
169
     *
170
     * @param string $folderName
171
     * @return bool TRUE if all went well
172
     * @throws \RuntimeException If folder path is not valid
173
     */
174
    public function clearAssetsFolder(string $folderName)
175
    {
176
        $basePath = Environment::getPublicPath() . $folderName;
177
        if (empty($folderName)
178
            || !GeneralUtility::isAllowedAbsPath($basePath)
179
            || strpos($folderName, '/typo3temp/assets/') !== 0
180
        ) {
181
            throw new \RuntimeException(
182
                'Path to folder ' . $folderName . ' not allowed.',
183
                1501781453
184
            );
185
        }
186
        if (!is_dir($basePath)) {
187
            throw new \RuntimeException(
188
                'Folder path ' . $basePath . ' does not exist or is no directory.',
189
                1501781454
190
            );
191
        }
192
193
        // first remove directories
194
        foreach ((new Finder())->directories()->in($basePath)->depth(0) as $directory) {
195
            /** @var SplFileInfo $directory */
196
            GeneralUtility::rmdir($directory->getPathname(), true);
197
        }
198
199
        // then remove files directly in the main dir
200
        foreach ((new Finder())->files()->in($basePath)->depth(0) as $file) {
201
            /** @var SplFileInfo $file */
202
            $path = $file->getPathname();
203
            @unlink($path);
204
        }
205
        return true;
206
    }
207
}
208