Completed
Push — master ( 522461...c89cc2 )
by Fabien
02:24
created

CacheService::getQueryBuilder()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
namespace Fab\Media\Cache;
4
5
/*
6
 * This file is part of the Fab/Media project under GPLv2 or later.
7
 *
8
 * For the full copyright and license information, please read the
9
 * LICENSE.md file that was distributed with this source code.
10
 */
11
12
use Fab\Vidi\Service\DataService;
13
use Fab\Vidi\Utility\BackendUtility;
14
use TYPO3\CMS\Core\Database\ConnectionPool;
15
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
16
use TYPO3\CMS\Core\Resource\File;
17
use TYPO3\CMS\Core\Utility\GeneralUtility;
18
19
/**
20
 * Service dealing with cache related to a File.
21
 */
22
class CacheService
23
{
24
25
    /**
26
     * Traverse all files and initialize cache values.
27
     *
28
     * @return int
29
     */
30
    public function warmUp()
31
    {
32
        /** @var QueryBuilder $queryBuilder */
33
        $queryBuilder = $this->getQueryBuilder('sys_file');
34
        $rows = $queryBuilder
35
            ->select('*')
36
            ->from('sys_file')
37
            ->where('storage > 0')
38
            ->execute()
39
            ->fetchAll();
40
41
        $counter = 0;
42
        foreach ($rows as $row) {
43
44
            $fileIdentifier = $row['uid'];
45
            $totalNumberOfReferences = $this->getFileReferenceService()->countTotalReferences($fileIdentifier);
46
47
            $values = array(
48
                'number_of_references' => $totalNumberOfReferences
49
            );
50
51
            $this->getDataService()->update(
52
                'sys_file',
53
                $values,
54
                [
55
                    'uid' => $fileIdentifier
56
                ]
57
            );
58
            $counter++;
59
        }
60
61
        return $counter;
62
    }
63
64
    /**
65
     * Clear all possible cache related to a file.
66
     * This method is useful when replacing a file for instance.
67
     *
68
     * @param File $file
69
     * @return void
70
     */
71
    public function clearCache(File $file)
72
    {
73
74
        $this->clearCachePages($file);
75
        $this->flushProcessedFiles($file);
76
    }
77
78
    /**
79
     * Remove all processed files that belong to the given File object.
80
     *
81
     * @param File $file
82
     * @return void
83
     */
84
    protected function flushProcessedFiles(File $file)
85
    {
86
87
        /** @var $processedFile \TYPO3\CMS\Core\Resource\ProcessedFile */
88
        foreach ($this->getProcessedFileRepository()->findAllByOriginalFile($file) as $processedFile) {
89
            if ($processedFile->exists()) {
90
                $processedFile->delete(true);
91
            }
92
93
            $this->getDataService()->delete(
94
                'sys_file_processedfile',
95
                [
96
                    'uid' => (int)$processedFile->getUid()
97
                ]
98
            );
99
        }
100
    }
101
102
    /**
103
     * Return a processed file repository
104
     *
105
     * @return \TYPO3\CMS\Core\Resource\ProcessedFileRepository|object
106
     */
107
    protected function getProcessedFileRepository()
108
    {
109
        return GeneralUtility::makeInstance(\TYPO3\CMS\Core\Resource\ProcessedFileRepository::class);
110
    }
111
112
    /**
113
     * Returns the file references.
114
     *
115
     * @param File $file
116
     * @return void
117
     */
118
    protected function clearCachePages($file)
0 ignored issues
show
Unused Code introduced by
The parameter $file is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
119
    {
120
121
        /** @var $tce \TYPO3\CMS\Core\DataHandling\DataHandler */
122
        $tce = GeneralUtility::makeInstance(\TYPO3\CMS\Core\DataHandling\DataHandler::class);
123
        $tce->start([], []);
124
125
        #$pages = array_merge(
126
        #    $this->findPagesWithFileReferences($file),
127
        #    $this->findPagesWithSoftReferences($file)
128
        #);
129
130
        // Previous code which does not work in TYPO3 CMS 7 LTS.
131
        // It is adviced to use "registerPageCacheClearing" but how?
132
        #foreach (array_unique($pages) as $page) {
133
        #    $tce->clear_cache('pages', $page);
134
        #}
135
        $tce->clear_cacheCmd('pages');
136
    }
137
138
    /**
139
     * Find all pages which contains file references to the given $file.
140
     *
141
     * @param File $file
142
     * @return array
143
     */
144
    protected function findPagesWithFileReferences($file)
145
    {
146
147
        /** @var QueryBuilder $queryBuilder */
148
        $queryBuilder = $this->getQueryBuilder('sys_file_reference');
149
        $rows = $queryBuilder
150
            ->select('pid')
151
            ->from('sys_file_reference')
152
            ->groupBy('pid') // no support for distinct
153
            ->andWhere(
154
                'pid > 0',
155
                'uid_local = ' . $file->getUid()
156
            )
157
            ->execute()
158
            ->fetchAll();
159
160
        foreach ($rows as $row) {
161
            $pages[] = $row['pid'];
0 ignored issues
show
Coding Style Comprehensibility introduced by
$pages was never initialized. Although not strictly required by PHP, it is generally a good practice to add $pages = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
162
163
        }
164
165
        return $pages;
0 ignored issues
show
Bug introduced by
The variable $pages does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
166
    }
167
168
    /**
169
     * Find all pages which have soft references to the given $file.
170
     *
171
     * @param File $file
172
     * @return array
173
     */
174
    protected function findPagesWithSoftReferences(File $file)
175
    {
176
177
        // todo here...
178
        $subClauseParts = array(
179
            'deleted = 0',
180
            '(softref_key = "rtehtmlarea_images" OR softref_key = "typolink_tag")',
181
            'ref_table = "sys_file"',
182
            'tablename = "tt_content"',
183
            'ref_uid = ' . $file->getUid(),
184
        );
185
186
        $rows = $this->getDatabaseConnection()->exec_SELECTquery(
0 ignored issues
show
Bug introduced by
The method getDatabaseConnection() does not seem to exist on object<Fab\Media\Cache\CacheService>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
187
            'DISTINCT pid',
188
            'tt_content',
189
            sprintf('uid IN (SELECT recuid FROM sys_refindex WHERE %s) %s',
190
                implode(' AND ', $subClauseParts),
191
                $this->getWhereClauseForEnabledFields('tt_content')
192
            )
193
        );
194
195
        // Compute result
196
        $pages = [];
197
        while ($affectedPage = $this->getDatabaseConnection()->sql_fetch_assoc($rows)) {
0 ignored issues
show
Bug introduced by
The method getDatabaseConnection() does not seem to exist on object<Fab\Media\Cache\CacheService>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
198
            $pages[] = $affectedPage['pid'];
199
        }
200
201
        return $pages;
202
    }
203
204
    /**
205
     * Get the WHERE clause for the enabled fields given a $tableName.
206
     *
207
     * @param string $tableName
208
     * @return string
209
     */
210
    protected function getWhereClauseForEnabledFields($tableName)
211
    {
212
        if ($this->isFrontendMode()) {
213
            // frontend context
214
            $whereClause = $this->getPageRepository()->deleteClause($tableName);
215
        } else {
216
            // backend context
217
            $whereClause = BackendUtility::deleteClause($tableName);
218
        }
219
        return $whereClause;
220
    }
221
222
    /**
223
     * Returns whether the current mode is Frontend
224
     *
225
     * @return string
226
     */
227
    protected function isFrontendMode()
228
    {
229
        return TYPO3_MODE == 'FE';
230
    }
231
232
    /**
233
     * Returns an instance of the page repository.
234
     *
235
     * @return \TYPO3\CMS\Frontend\Page\PageRepository
236
     */
237
    protected function getPageRepository()
238
    {
239
        return $GLOBALS['TSFE']->sys_page;
240
    }
241
242
    /**
243
     * @param string $tableName
244
     * @return object|QueryBuilder
245
     */
246
    protected function getQueryBuilder($tableName): QueryBuilder
247
    {
248
        /** @var ConnectionPool $connectionPool */
249
        $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
250
        return $connectionPool->getQueryBuilderForTable($tableName);
251
    }
252
253
    /**
254
     * @return object|DataService
255
     */
256
    protected function getDataService(): DataService
257
    {
258
        return GeneralUtility::makeInstance(DataService::class);
259
    }
260
261
    /**
262
     * @return \Fab\Media\Resource\FileReferenceService|object
263
     */
264
    protected function getFileReferenceService()
265
    {
266
        return GeneralUtility::makeInstance(\Fab\Media\Resource\FileReferenceService::class);
267
    }
268
}
269