Passed
Push — master ( fcbfb0...64542d )
by
unknown
13:36
created

getExtensionsSuitableForTypo3Version()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 6
nc 3
nop 1
dl 0
loc 10
rs 10
c 0
b 0
f 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\Extensionmanager\Domain\Repository;
17
18
use TYPO3\CMS\Core\Database\ConnectionPool;
19
use TYPO3\CMS\Core\Utility\GeneralUtility;
20
use TYPO3\CMS\Core\Utility\VersionNumberUtility;
21
use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper;
22
use TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface;
23
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
24
use TYPO3\CMS\Extbase\Persistence\QueryResultInterface;
25
use TYPO3\CMS\Extbase\Persistence\Repository;
26
use TYPO3\CMS\Extensionmanager\Domain\Model\Extension;
27
28
/**
29
 * A repository for extensions
30
 * @internal This class is a specific domain repository implementation and is not part of the Public TYPO3 API.
31
 */
32
class ExtensionRepository extends Repository
33
{
34
    /**
35
     * @var string
36
     */
37
    const TABLE_NAME = 'tx_extensionmanager_domain_model_extension';
38
39
    /**
40
     * Do not include pid in queries
41
     */
42
    public function initializeObject()
43
    {
44
        $defaultQuerySettings = $this->objectManager->get(QuerySettingsInterface::class);
45
        $defaultQuerySettings->setRespectStoragePage(false);
46
        $this->setDefaultQuerySettings($defaultQuerySettings);
47
    }
48
49
    /**
50
     * Count all extensions
51
     *
52
     * @return int
53
     */
54
    public function countAll()
55
    {
56
        $query = $this->createQuery();
57
        $query = $this->addDefaultConstraints($query);
58
        return $query->execute()->count();
59
    }
60
61
    /**
62
     * Finds all extensions
63
     *
64
     * @return array|QueryResultInterface
65
     */
66
    public function findAll()
67
    {
68
        $query = $this->createQuery();
69
        $query = $this->addDefaultConstraints($query);
70
        $query->setOrderings(
71
            [
72
                'lastUpdated' => QueryInterface::ORDER_DESCENDING
73
            ]
74
        );
75
        return $query->execute();
76
    }
77
78
    /**
79
     * Find an extension by extension key ordered by version
80
     *
81
     * @param string $extensionKey
82
     * @return QueryResultInterface
83
     */
84
    public function findByExtensionKeyOrderedByVersion($extensionKey)
85
    {
86
        $query = $this->createQuery();
87
        $query->matching(
88
            $query->logicalAnd(
89
                $query->equals('extensionKey', $extensionKey),
90
                $query->greaterThanOrEqual('reviewState', 0)
0 ignored issues
show
Unused Code introduced by
The call to TYPO3\CMS\Extbase\Persis...Interface::logicalAnd() has too many arguments starting with $query->greaterThanOrEqual('reviewState', 0). ( Ignorable by Annotation )

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

90
            $query->/** @scrutinizer ignore-call */ 
91
                    logicalAnd(

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
91
            )
92
        );
93
        $query->setOrderings(['integerVersion' => QueryInterface::ORDER_DESCENDING]);
94
        return $query->execute();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $query->execute() also could return the type array which is incompatible with the documented return type TYPO3\CMS\Extbase\Persistence\QueryResultInterface.
Loading history...
95
    }
96
97
    /**
98
     * Find the current version by extension key
99
     *
100
     * @param string $extensionKey
101
     * @return array|QueryResultInterface
102
     */
103
    public function findOneByCurrentVersionByExtensionKey($extensionKey)
104
    {
105
        $query = $this->createQuery();
106
        $query->matching(
107
            $query->logicalAnd(
108
                $query->equals('extensionKey', $extensionKey),
109
                $query->greaterThanOrEqual('reviewState', 0),
0 ignored issues
show
Unused Code introduced by
The call to TYPO3\CMS\Extbase\Persis...Interface::logicalAnd() has too many arguments starting with $query->greaterThanOrEqual('reviewState', 0). ( Ignorable by Annotation )

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

109
            $query->/** @scrutinizer ignore-call */ 
110
                    logicalAnd(

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
110
                $query->equals('currentVersion', 1)
111
            )
112
        );
113
        $query->setLimit(1);
114
        return $query->execute()->getFirst();
115
    }
116
117
    /**
118
     * Find one extension by extension key and version
119
     *
120
     * @param string $extensionKey
121
     * @param string $version (example: 4.3.10)
122
     * @return array|QueryResultInterface
123
     */
124
    public function findOneByExtensionKeyAndVersion($extensionKey, $version)
125
    {
126
        $query = $this->createQuery();
127
        // Hint: This method must not filter out insecure extensions, if needed,
128
        // it should be done on a different level, or with a helper method.
129
        $query->matching($query->logicalAnd(
130
            $query->equals('extensionKey', $extensionKey),
131
            $query->equals('version', $version)
0 ignored issues
show
Unused Code introduced by
The call to TYPO3\CMS\Extbase\Persis...Interface::logicalAnd() has too many arguments starting with $query->equals('version', $version). ( Ignorable by Annotation )

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

131
        $query->matching($query->/** @scrutinizer ignore-call */ logicalAnd(

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
132
        ));
133
        return $query->setLimit(1)->execute()->getFirst();
134
    }
135
136
    /**
137
     * Find an extension by title, author name or extension key
138
     * This is the function used by the TER search. It is using a
139
     * scoring for the matches to sort the extension with an
140
     * exact key match on top
141
     *
142
     * @param string $searchString The string to search for extensions
143
     * @return mixed
144
     */
145
    public function findByTitleOrAuthorNameOrExtensionKey($searchString)
146
    {
147
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
148
            ->getQueryBuilderForTable(self::TABLE_NAME);
149
150
        $searchPlaceholderForLike = '%' . $queryBuilder->escapeLikeWildcards($searchString) . '%';
151
152
        $searchConstraints = [
153
            'extension_key' => $queryBuilder->expr()->eq(
154
                'extension_key',
155
                $queryBuilder->createNamedParameter($searchString, \PDO::PARAM_STR)
156
            ),
157
            'extension_key_like' => $queryBuilder->expr()->like(
158
                'extension_key',
159
                $queryBuilder->createNamedParameter($searchPlaceholderForLike, \PDO::PARAM_STR)
160
            ),
161
            'title' => $queryBuilder->expr()->like(
162
                'title',
163
                $queryBuilder->createNamedParameter($searchPlaceholderForLike, \PDO::PARAM_STR)
164
            ),
165
            'description' => $queryBuilder->expr()->like(
166
                'description',
167
                $queryBuilder->createNamedParameter($searchPlaceholderForLike, \PDO::PARAM_STR)
168
            ),
169
            'author_name' => $queryBuilder->expr()->like(
170
                'author_name',
171
                $queryBuilder->createNamedParameter($searchPlaceholderForLike, \PDO::PARAM_STR)
172
            ),
173
        ];
174
175
        $caseStatement = 'CASE ' .
176
            'WHEN ' . $searchConstraints['extension_key'] . ' THEN 16 ' .
177
            'WHEN ' . $searchConstraints['extension_key_like'] . ' THEN 8 ' .
178
            'WHEN ' . $searchConstraints['title'] . ' THEN 4 ' .
179
            'WHEN ' . $searchConstraints['description'] . ' THEN 2 ' .
180
            'WHEN ' . $searchConstraints['author_name'] . ' THEN 1 ' .
181
            'END AS ' . $queryBuilder->quoteIdentifier('position');
182
183
        $result = $queryBuilder
184
            ->select('*')
185
            ->addSelectLiteral($caseStatement)
186
            ->from(self::TABLE_NAME)
187
            ->where(
188
                $queryBuilder->expr()->orX(...array_values($searchConstraints)),
189
                $queryBuilder->expr()->eq('current_version', $queryBuilder->createNamedParameter(1, \PDO::PARAM_INT)),
190
                $queryBuilder->expr()->gte('review_state', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT))
191
            )
192
            ->orderBy('position', 'DESC')
193
            ->execute()
194
            ->fetchAll();
195
196
        $dataMapper = GeneralUtility::makeInstance(DataMapper::class);
197
        return $dataMapper->map(Extension::class, $result);
198
    }
199
200
    /**
201
     * Find an extension between a certain version range ordered by version number
202
     *
203
     * @param string $extensionKey
204
     * @param int $lowestVersion
205
     * @param int $highestVersion
206
     * @param bool $includeCurrentVersion
207
     * @return QueryResultInterface
208
     */
209
    public function findByVersionRangeAndExtensionKeyOrderedByVersion($extensionKey, $lowestVersion = 0, $highestVersion = 0, $includeCurrentVersion = true)
210
    {
211
        $query = $this->createQuery();
212
        $constraint = null;
213
        if ($lowestVersion !== 0 && $highestVersion !== 0) {
214
            if ($includeCurrentVersion) {
215
                $constraint = $query->logicalAnd($query->lessThanOrEqual('integerVersion', $highestVersion), $query->greaterThanOrEqual('integerVersion', $lowestVersion), $query->equals('extensionKey', $extensionKey));
0 ignored issues
show
Unused Code introduced by
The call to TYPO3\CMS\Extbase\Persis...Interface::logicalAnd() has too many arguments starting with $query->greaterThanOrEqu...rsion', $lowestVersion). ( Ignorable by Annotation )

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

215
                /** @scrutinizer ignore-call */ 
216
                $constraint = $query->logicalAnd($query->lessThanOrEqual('integerVersion', $highestVersion), $query->greaterThanOrEqual('integerVersion', $lowestVersion), $query->equals('extensionKey', $extensionKey));

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
216
            } else {
217
                $constraint = $query->logicalAnd($query->lessThanOrEqual('integerVersion', $highestVersion), $query->greaterThan('integerVersion', $lowestVersion), $query->equals('extensionKey', $extensionKey));
218
            }
219
        } elseif ($lowestVersion === 0 && $highestVersion !== 0) {
220
            if ($includeCurrentVersion) {
221
                $constraint = $query->logicalAnd($query->lessThanOrEqual('integerVersion', $highestVersion), $query->equals('extensionKey', $extensionKey));
222
            } else {
223
                $constraint = $query->logicalAnd($query->lessThan('integerVersion', $highestVersion), $query->equals('extensionKey', $extensionKey));
224
            }
225
        } elseif ($lowestVersion !== 0 && $highestVersion === 0) {
226
            if ($includeCurrentVersion) {
227
                $constraint = $query->logicalAnd($query->greaterThanOrEqual('integerVersion', $lowestVersion), $query->equals('extensionKey', $extensionKey));
228
            } else {
229
                $constraint = $query->logicalAnd($query->greaterThan('integerVersion', $lowestVersion), $query->equals('extensionKey', $extensionKey));
230
            }
231
        } elseif ($lowestVersion === 0 && $highestVersion === 0) {
232
            $constraint = $query->equals('extensionKey', $extensionKey);
233
        }
234
        if ($constraint) {
235
            $query->matching($query->logicalAnd($constraint, $query->greaterThanOrEqual('reviewState', 0)));
236
        }
237
        $query->setOrderings([
238
            'integerVersion' => QueryInterface::ORDER_DESCENDING
239
        ]);
240
        return $query->execute();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $query->execute() also could return the type array which is incompatible with the documented return type TYPO3\CMS\Extbase\Persistence\QueryResultInterface.
Loading history...
241
    }
242
243
    /**
244
     * Finds all extensions with category "distribution" not published by the TYPO3 CMS Team
245
     *
246
     * @param bool $showUnsuitableDistributions
247
     * @return Extension[]
248
     */
249
    public function findAllCommunityDistributions(bool $showUnsuitableDistributions = false): array
250
    {
251
        $query = $this->createQuery();
252
        $query->matching(
253
            $query->logicalAnd(
254
                $query->equals('category', Extension::DISTRIBUTION_CATEGORY),
255
                $query->logicalNot($query->equals('ownerusername', 'typo3v4'))
0 ignored issues
show
Unused Code introduced by
The call to TYPO3\CMS\Extbase\Persis...Interface::logicalAnd() has too many arguments starting with $query->logicalNot($quer...rusername', 'typo3v4')). ( Ignorable by Annotation )

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

255
            $query->/** @scrutinizer ignore-call */ 
256
                    logicalAnd(

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
256
            )
257
        );
258
259
        $query->setOrderings([
260
            'alldownloadcounter' => QueryInterface::ORDER_DESCENDING
261
        ]);
262
263
        return $this->filterYoungestVersionOfExtensionList($query->execute()->toArray(), $showUnsuitableDistributions);
264
    }
265
266
    /**
267
     * Finds all extensions with category "distribution" that are published by the TYPO3 CMS Team
268
     *
269
     * @param bool $showUnsuitableDistributions
270
     * @return Extension[]
271
     */
272
    public function findAllOfficialDistributions(bool $showUnsuitableDistributions = false): array
273
    {
274
        $query = $this->createQuery();
275
        $query->matching(
276
            $query->logicalAnd(
277
                $query->equals('category', Extension::DISTRIBUTION_CATEGORY),
278
                $query->equals('ownerusername', 'typo3v4')
0 ignored issues
show
Unused Code introduced by
The call to TYPO3\CMS\Extbase\Persis...Interface::logicalAnd() has too many arguments starting with $query->equals('ownerusername', 'typo3v4'). ( Ignorable by Annotation )

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

278
            $query->/** @scrutinizer ignore-call */ 
279
                    logicalAnd(

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
279
            )
280
        );
281
282
        $query->setOrderings([
283
            'alldownloadcounter' => QueryInterface::ORDER_DESCENDING
284
        ]);
285
286
        return $this->filterYoungestVersionOfExtensionList($query->execute()->toArray(), $showUnsuitableDistributions);
287
    }
288
289
    /**
290
     * Count extensions with a certain key between a given version range
291
     *
292
     * @param string $extensionKey
293
     * @param int $lowestVersion
294
     * @param int $highestVersion
295
     * @return int
296
     */
297
    public function countByVersionRangeAndExtensionKey($extensionKey, $lowestVersion = 0, $highestVersion = 0)
298
    {
299
        return $this->findByVersionRangeAndExtensionKeyOrderedByVersion($extensionKey, $lowestVersion, $highestVersion)->count();
300
    }
301
302
    /**
303
     * Find highest version available of an extension
304
     *
305
     * @param string $extensionKey
306
     * @return Extension
307
     */
308
    public function findHighestAvailableVersion($extensionKey)
309
    {
310
        $query = $this->createQuery();
311
        $query->matching($query->logicalAnd($query->equals('extensionKey', $extensionKey), $query->greaterThanOrEqual('reviewState', 0)));
0 ignored issues
show
Unused Code introduced by
The call to TYPO3\CMS\Extbase\Persis...Interface::logicalAnd() has too many arguments starting with $query->greaterThanOrEqual('reviewState', 0). ( Ignorable by Annotation )

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

311
        $query->matching($query->/** @scrutinizer ignore-call */ logicalAnd($query->equals('extensionKey', $extensionKey), $query->greaterThanOrEqual('reviewState', 0)));

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
312
        $query->setOrderings([
313
            'integerVersion' => QueryInterface::ORDER_DESCENDING
314
        ]);
315
        return $query->setLimit(1)->execute()->getFirst();
316
    }
317
318
    /**
319
     * Adds default constraints to the query - in this case it
320
     * enables us to always just search for the latest version of an extension
321
     *
322
     * @param QueryInterface $query the query to adjust
323
     * @return QueryInterface
324
     */
325
    protected function addDefaultConstraints(QueryInterface $query): QueryInterface
326
    {
327
        if ($query->getConstraint()) {
328
            $query->matching($query->logicalAnd(
329
                $query->getConstraint(),
330
                $query->equals('current_version', true),
0 ignored issues
show
Unused Code introduced by
The call to TYPO3\CMS\Extbase\Persis...Interface::logicalAnd() has too many arguments starting with $query->equals('current_version', true). ( Ignorable by Annotation )

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

330
            $query->matching($query->/** @scrutinizer ignore-call */ logicalAnd(

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
331
                $query->greaterThanOrEqual('reviewState', 0)
332
            ));
333
        } else {
334
            $query->matching($query->logicalAnd(
335
                $query->equals('current_version', true),
336
                $query->greaterThanOrEqual('reviewState', 0)
337
            ));
338
        }
339
        return $query;
340
    }
341
342
    /**
343
     * Get extensions (out of a given list) that are suitable for the current TYPO3 version
344
     *
345
     * @param Extension[] $extensions List of extensions to check
346
     * @return Extension[] List of extensions suitable for current TYPO3 version
347
     */
348
    protected function getExtensionsSuitableForTypo3Version(array $extensions): array
349
    {
350
        $suitableExtensions = [];
351
        foreach ($extensions as $extension) {
352
            $dependency = $extension->getTypo3Dependency();
353
            if ($dependency !== null && $dependency->isVersionCompatible(VersionNumberUtility::getNumericTypo3Version())) {
354
                $suitableExtensions[] = $extension;
355
            }
356
        }
357
        return $suitableExtensions;
358
    }
359
360
    /**
361
     * Get a list of various extensions in various versions and returns
362
     * a filtered list containing the extension-version combination with
363
     * the highest version number.
364
     *
365
     * @param Extension[] $extensions
366
     * @param bool $showUnsuitable
367
     * @return Extension[]
368
     */
369
    protected function filterYoungestVersionOfExtensionList(array $extensions, bool $showUnsuitable): array
370
    {
371
        if (!$showUnsuitable) {
372
            $extensions = $this->getExtensionsSuitableForTypo3Version($extensions);
373
        }
374
        $filteredExtensions = [];
375
        foreach ($extensions as $extension) {
376
            $extensionKey = $extension->getExtensionKey();
377
            if (!array_key_exists($extensionKey, $filteredExtensions)) {
378
                $filteredExtensions[$extensionKey] = $extension;
379
                continue;
380
            }
381
            $currentVersion = $filteredExtensions[$extensionKey]->getVersion();
382
            $newVersion = $extension->getVersion();
383
            if (version_compare($newVersion, $currentVersion, '>')) {
384
                $filteredExtensions[$extensionKey] = $extension;
385
            }
386
        }
387
        return $filteredExtensions;
388
    }
389
}
390