[CLEANUP] Alwas put null at the last position
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Resource / ProcessedFileRepository.php
1 <?php
2 namespace TYPO3\CMS\Core\Resource;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Database\ConnectionPool;
18 use TYPO3\CMS\Core\Log\LogManager;
19 use TYPO3\CMS\Core\Utility\GeneralUtility;
20
21 /**
22 * Repository for accessing files
23 * it also serves as the public API for the indexing part of files in general
24 */
25 class ProcessedFileRepository extends AbstractRepository
26 {
27 /**
28 * The main object type of this class. In some cases (fileReference) this
29 * repository can also return FileReference objects, implementing the
30 * common FileInterface.
31 *
32 * @var string
33 */
34 protected $objectType = ProcessedFile::class;
35
36 /**
37 * Main File object storage table. Note that this repository also works on
38 * the sys_file_reference table when returning FileReference objects.
39 *
40 * @var string
41 */
42 protected $table = 'sys_file_processedfile';
43
44 /**
45 * As determining the table columns is a costly operation this is done only once during runtime and cached then
46 *
47 * @var array
48 * @see cleanUnavailableColumns()
49 */
50 protected $tableColumns = [];
51
52 /**
53 * Creates this object.
54 */
55 public function __construct()
56 {
57 parent::__construct();
58 }
59
60 /**
61 * Creates a ProcessedFile object from a file object and a processing configuration
62 *
63 * @param FileInterface $originalFile
64 * @param string $taskType
65 * @param array $configuration
66 * @return ProcessedFile
67 */
68 public function createNewProcessedFileObject(FileInterface $originalFile, $taskType, array $configuration)
69 {
70 return GeneralUtility::makeInstance(
71 $this->objectType,
72 $originalFile,
73 $taskType,
74 $configuration
75 );
76 }
77
78 /**
79 * @param array $databaseRow
80 * @return ProcessedFile
81 */
82 protected function createDomainObject(array $databaseRow)
83 {
84 $originalFile = $this->factory->getFileObject((int)$databaseRow['original']);
85 $originalFile->setStorage($this->factory->getStorageObject($originalFile->getProperty('storage')));
86 $taskType = $databaseRow['task_type'];
87 $configuration = unserialize($databaseRow['configuration']);
88
89 return GeneralUtility::makeInstance(
90 $this->objectType,
91 $originalFile,
92 $taskType,
93 $configuration,
94 $databaseRow
95 );
96 }
97
98 /**
99 * @param ResourceStorage $storage
100 * @param string $identifier
101 *
102 * @return ProcessedFile|null
103 */
104 public function findByStorageAndIdentifier(ResourceStorage $storage, $identifier)
105 {
106 $processedFileObject = null;
107 if ($storage->hasFile($identifier)) {
108 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->table);
109 $databaseRow = $queryBuilder
110 ->select('*')
111 ->from($this->table)
112 ->where(
113 $queryBuilder->expr()->eq(
114 'storage',
115 $queryBuilder->createNamedParameter($storage->getUid(), \PDO::PARAM_INT)
116 ),
117 $queryBuilder->expr()->eq(
118 'identifier',
119 $queryBuilder->createNamedParameter($identifier, \PDO::PARAM_STR)
120 )
121 )
122 ->execute()
123 ->fetch();
124
125 if ($databaseRow) {
126 $processedFileObject = $this->createDomainObject($databaseRow);
127 }
128 }
129 return $processedFileObject;
130 }
131 /**
132 * Adds a processedfile object in the database
133 *
134 * @param ProcessedFile $processedFile
135 */
136 public function add($processedFile)
137 {
138 if ($processedFile->isPersisted()) {
139 $this->update($processedFile);
140 } else {
141 $insertFields = $processedFile->toArray();
142 $insertFields['crdate'] = $insertFields['tstamp'] = time();
143 $insertFields = $this->cleanUnavailableColumns($insertFields);
144
145 $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($this->table);
146
147 $connection->insert(
148 $this->table,
149 $insertFields
150 );
151
152 $uid = $connection->lastInsertId($this->table);
153 $processedFile->updateProperties(['uid' => $uid]);
154 }
155 }
156
157 /**
158 * Updates an existing file object in the database
159 *
160 * @param ProcessedFile $processedFile
161 */
162 public function update($processedFile)
163 {
164 if ($processedFile->isPersisted()) {
165 $uid = (int)$processedFile->getUid();
166 $updateFields = $this->cleanUnavailableColumns($processedFile->toArray());
167 $updateFields['tstamp'] = time();
168
169 $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($this->table);
170 $connection->update(
171 $this->table,
172 $updateFields,
173 [
174 'uid' => (int)$uid
175 ]
176 );
177 }
178 }
179
180 /**
181 * @param \TYPO3\CMS\Core\Resource\File|\TYPO3\CMS\Core\Resource\FileInterface $file
182 * @param string $taskType The task that should be executed on the file
183 * @param array $configuration
184 *
185 * @return ProcessedFile
186 */
187 public function findOneByOriginalFileAndTaskTypeAndConfiguration(FileInterface $file, $taskType, array $configuration)
188 {
189 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->table);
190
191 $databaseRow = $queryBuilder
192 ->select('*')
193 ->from($this->table)
194 ->where(
195 $queryBuilder->expr()->eq(
196 'original',
197 $queryBuilder->createNamedParameter($file->getUid(), \PDO::PARAM_INT)
198 ),
199 $queryBuilder->expr()->eq('task_type', $queryBuilder->createNamedParameter($taskType, \PDO::PARAM_STR)),
200 $queryBuilder->expr()->eq(
201 'configurationsha1',
202 $queryBuilder->createNamedParameter(sha1(serialize($configuration)), \PDO::PARAM_STR)
203 )
204 )
205 ->execute()
206 ->fetch();
207
208 if (is_array($databaseRow)) {
209 $processedFile = $this->createDomainObject($databaseRow);
210 } else {
211 $processedFile = $this->createNewProcessedFileObject($file, $taskType, $configuration);
212 }
213 return $processedFile;
214 }
215
216 /**
217 * @param FileInterface $file
218 * @return ProcessedFile[]
219 * @throws \InvalidArgumentException
220 */
221 public function findAllByOriginalFile(FileInterface $file)
222 {
223 if (!$file instanceof File) {
224 throw new \InvalidArgumentException('Parameter is no File object but got type "'
225 . (is_object($file) ? get_class($file) : gettype($file)) . '"', 1382006142);
226 }
227
228 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->table);
229 $result = $queryBuilder
230 ->select('*')
231 ->from($this->table)
232 ->where(
233 $queryBuilder->expr()->eq(
234 'original',
235 $queryBuilder->createNamedParameter($file->getUid(), \PDO::PARAM_INT)
236 )
237 )
238 ->execute();
239
240 $itemList = [];
241 while ($row = $result->fetch()) {
242 $itemList[] = $this->createDomainObject($row);
243 }
244 return $itemList;
245 }
246
247 /**
248 * Removes all processed files and also deletes the associated physical files
249 *
250 * @param int|null $storageUid If not NULL, only the processed files of the given storage are removed
251 * @return int Number of failed deletions
252 */
253 public function removeAll($storageUid = null)
254 {
255 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->table);
256 $result = $queryBuilder
257 ->select('*')
258 ->from($this->table)
259 ->where(
260 $queryBuilder->expr()->neq('identifier', $queryBuilder->createNamedParameter('', \PDO::PARAM_STR))
261 )
262 ->execute();
263
264 $logger = $this->getLogger();
265 $errorCount = 0;
266
267 while ($row = $result->fetch()) {
268 if ($storageUid && (int)$storageUid !== (int)$row['storage']) {
269 continue;
270 }
271 try {
272 $file = $this->createDomainObject($row);
273 $file->getStorage()->setEvaluatePermissions(false);
274 $file->delete(true);
275 } catch (\Exception $e) {
276 $logger->error(
277 'Failed to delete file "' . $row['identifier'] . '" in storage uid ' . $row['storage'] . '.',
278 [
279 'exception' => $e
280 ]
281 );
282 ++$errorCount;
283 }
284 }
285
286 GeneralUtility::makeInstance(ConnectionPool::class)
287 ->getConnectionForTable($this->table)
288 ->truncate($this->table);
289
290 return $errorCount;
291 }
292
293 /**
294 * Removes all array keys which cannot be persisted
295 *
296 * @param array $data
297 *
298 * @return array
299 */
300 protected function cleanUnavailableColumns(array $data)
301 {
302 // As determining the table columns is a costly operation this is done only once during runtime and cached then
303 if (empty($this->tableColumns[$this->table])) {
304 $this->tableColumns[$this->table] = GeneralUtility::makeInstance(ConnectionPool::class)
305 ->getConnectionForTable($this->table)
306 ->getSchemaManager()
307 ->listTableColumns($this->table);
308 }
309
310 return array_intersect_key($data, $this->tableColumns[$this->table]);
311 }
312
313 /**
314 * @return \TYPO3\CMS\Core\Log\Logger
315 */
316 protected function getLogger()
317 {
318 return GeneralUtility::makeInstance(LogManager::class)->getLogger(__CLASS__);
319 }
320 }