VersionUtility.php 8.27 KB
Newer Older
1
<?php
Tomas Norre Mikkelsen's avatar
Tomas Norre Mikkelsen committed
2

3
4
namespace T3o\TerFe2\Utility;

5
6
/*
 * This file is part of the TYPO3 CMS project.
7
 *
8
9
10
 * It is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License, either version 2
 * of the License, or any later version.
11
 *
12
13
 * For the full copyright and license information, please read the
 * LICENSE.txt file that was distributed with this source code.
14
 *
15
16
 * The TYPO3 project - inspiring people to share!
 */
17

18
use T3o\TerFe2\Service\LTSVersionService;
19
20
21
22
use T3o\TerFe2\Domain\Model\Download;
use T3o\TerFe2\Domain\Model\Extension;
use T3o\TerFe2\Domain\Repository\DownloadRepository;
use T3o\TerFe2\Domain\Repository\ExtensionRepository;
23
use TYPO3\CMS\Core\Utility\GeneralUtility;
24
25
use TYPO3\CMS\Extbase\Object\ObjectManager;
use TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager;
26

27
28
29
/**
 * Utilities to manage versions
 */
30
class VersionUtility
31
{
32
33
    const DOWNLOAD_SOURCE_TER = 1;
    const DOWNLOAD_SOURCE_PACKAGIST = 2;
34
    const DOWNLOAD_SOURCE_EM = 3;
35

36
37
38
39
40
41
42
43
44
45
46
47
48
    /**
     * Returns the three part version number (string) from an integer, eg 4012003 -> '4.12.3'
     *
     * @param int $versionInteger Integer representation of version number
     * @return string Version number as format x.x.x
     * @throws \InvalidArgumentException if $versionInteger is not an integer
     */
    public static function convertIntegerToVersionNumber(int $versionInteger): string
    {
        $versionString = str_pad($versionInteger, 9, '0', STR_PAD_LEFT);
        $parts = [
            substr($versionString, 0, 3),
            substr($versionString, 3, 3),
Tomas Norre Mikkelsen's avatar
Tomas Norre Mikkelsen committed
49
            substr($versionString, 6, 3),
50
51
52
53
54
        ];

        return (int)$parts[0] . '.' . (int)$parts[1] . '.' . (int)$parts[2];
    }

Thomas Löffler's avatar
Thomas Löffler committed
55
    /**
56
     * @param \T3o\TerFe2\Domain\Model\Extension $extension
Thomas Löffler's avatar
Thomas Löffler committed
57
58
59
     * @param int $mainVersion
     * @return bool
     */
60
    public static function doesExtensionSupportTypo3Version(\T3o\TerFe2\Domain\Model\Extension $extension, int $mainVersion): bool
Thomas Löffler's avatar
Thomas Löffler committed
61
    {
62
        foreach ($extension->getVersions() as $version) {
Thomas Löffler's avatar
Thomas Löffler committed
63
            if ($version->getTypo3Dependency() === null || $version->getReviewState() === \T3o\TerFe2\Domain\Model\Version::VERSION_IS_INSECURE) {
64
65
66
                continue;
            }

67
            if ($version->doesSupportTypo3Version($mainVersion)) {
68
69
70
                return true;
            }
        }
Thomas Löffler's avatar
Thomas Löffler committed
71

72
        return false;
Thomas Löffler's avatar
Thomas Löffler committed
73
    }
Tomas Norre Mikkelsen's avatar
Tomas Norre Mikkelsen committed
74
    
75
76
77
78
79
80
81
82
83
84
85
86
    /**
     * Generates a list with compatible TYPO3 versions from given dependencies
     *
     * @param array $dependencies
     * @return string
     */
    public static function getCompatibleTypo3Versions(array $dependencies): string
    {
        $compatibleTypo3Versions = [];

        foreach ($dependencies as $relation) {
            if ((string)($relation['kind'] ?? '') !== 'depends'
87
                || !in_array((string)($relation['extensionKey'] ?? ''), ['typo3', 'cms'], true)
88
89
90
            ) {
                continue;
            }
91
92
93
94
95

            $versionRange = (string)($relation['versionRange'] ?? '');

            // Assume all LTS versions are supported
            if ($versionRange === '*') {
96
                $compatibleTypo3Versions = self::getAllLtsVersionsAsMajor();
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
                continue;
            }

            // Extract all supported versions form minimal and maximal
            if (strpos($versionRange, '-') !== false) {
                [$min, $max] = explode('-', $versionRange);
                if ($min === null || $min === '0' || $min === '0.0.0') {
                    // Skip if minimal version is not valid
                    continue;
                }
                $min = self::getMajorVersion($min);
                if ($min <= 0 || $max === null) {
                    // Skip if major version could not be extracted or maximal version is not valid
                    continue;
                }
                if ($max === '0' || $max === '0.0.0') {
113
                    // Assume that all LTS versions between the minimal and the latest LTS are compatible.
114
115
116
117
118
119
120
                    $compatibleTypo3Versions = self::getVersionRange($min);
                    // Also break the foreach as all information was gathered.
                    break;
                }
                $max = self::getMajorVersion($max);
                if ($max <= 0) {
                    // Only add minimal since maximal could not be extracted
Oliver Bartsch's avatar
Oliver Bartsch committed
121
122
                    $compatibleTypo3Versions[] = $min;
                    continue;
123
124
125
126
127
128
129
130
131
132
133
134
135
                }
                // Add all major versions between min and max
                $compatibleTypo3Versions = self::getVersionRange($min, $max);
                // Also break the foreach as all information was gathered.
                break;
            }

            // Assume only the minimal version is supported
            if (strpos($versionRange, '.') !== false) {
                $major = self::getMajorVersion($versionRange);
                if ($major > 0) {
                    $compatibleTypo3Versions[] = $major;
                }
136
137
138
139
140
            }
        }

        return implode(',', array_unique($compatibleTypo3Versions));
    }
141
142
143
144
145
146

    /**
     * Generates an array of LTS versions with only their major number
     *
     * @return array
     */
147
    protected static function getAllLtsVersionsAsMajor(): array
148
149
150
151
152
153
154
155
156
157
158
159
160
    {
        $ltsVersions = GeneralUtility::makeInstance(LTSVersionService::class)->getAllLTSVersions();
        asort($ltsVersions, SORT_NUMERIC);
        foreach ($ltsVersions as &$ltsVersion) {
            $ltsVersion = self::getMajorVersion(self::convertIntegerToVersionNumber($ltsVersion));
        }
        return $ltsVersions;
    }

    /**
     * Generate range for min and max by removing invalid version numbers
     *
     * @param int $min
Oliver Bartsch's avatar
Oliver Bartsch committed
161
     * @param int|null $max
162
163
164
165
     * @return array
     */
    protected static function getVersionRange(int $min, int $max = null): array
    {
166
        $allLtsVersions = self::getAllLtsVersionsAsMajor();
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
        $max = (int)($max ?? end($allLtsVersions) ?: 0);

        if ($max <= 0) {
            return [];
        }

        $range = range($min, $max);
        foreach ($range as $key => $version) {
            if (!in_array($version, $allLtsVersions, true)) {
                unset($range[$key]);
            }
        }

        return $range;
    }

    /**
     * Extract the major version from version string
     *
     * @param string $versionString
     * @return int
     */
    protected static function getMajorVersion(string $versionString): int
    {
        $parts = explode('.', $versionString) ?: [];
        return (int)array_shift($parts);
    }
Tomas Norre Mikkelsen's avatar
Tomas Norre Mikkelsen committed
194

195
    /**
Tomas Norre Mikkelsen's avatar
Tomas Norre Mikkelsen committed
196
     * @param string $extensionKey
197
198
199
200
201
202
203
204
205
206
207
208
     * @param int $versionId
     * @param int $source
     * @param int $counter
     *
     * @throws \TYPO3\CMS\Extbase\Persistence\Exception\IllegalObjectTypeException
     * @throws \TYPO3\CMS\Extbase\Persistence\Exception\InvalidQueryException
     * @throws \TYPO3\CMS\Extbase\Persistence\Exception\UnknownObjectException
     */
    public static function updateVersionCounter(String $extensionKey, int $versionId, int $source = self::DOWNLOAD_SOURCE_TER, int $counter = 1)
    {
        $objectManager = GeneralUtility::makeInstance(ObjectManager::class);
        $persistenceManager = $objectManager->get(PersistenceManager::class);
Tomas Norre Mikkelsen's avatar
Tomas Norre Mikkelsen committed
209

210
211
212
213
        $month = date('Ym');
        $downloadRepository = $objectManager->get(DownloadRepository::class);

        /** @var Download $downloadInfo */
Tomas Norre Mikkelsen's avatar
Tomas Norre Mikkelsen committed
214
        $downloadInfo = $downloadRepository->findDownloadCounterByMonthAndExtensionKey($month, $extensionKey, $versionId, $source);
215
216
217
218
219
220
221
222
223
224

        if (null === $downloadInfo) {
            $downloadInfo = new Download();
            $downloadInfo->setExtensionKey($extensionKey);
            $downloadInfo->setSource($source);
            $downloadInfo->setMonth($month);
            $downloadInfo->setVersionId($versionId);
            $downloadInfo->setCounter($counter);
            $downloadRepository->add($downloadInfo);
        } elseif ($downloadInfo->getCounter() > 0) {
225
226
227
228
229
230
            // If source = PACKAGIST we set the Packagist data direct no count.
            if ($source === self::DOWNLOAD_SOURCE_PACKAGIST) {
                $downloadInfo->setCounter($counter);
            } else {
                $downloadInfo->setCounter($downloadInfo->getCounter() + 1);
            }
231
232
233
234
            $downloadRepository->update($downloadInfo);
        }
        $persistenceManager->persistAll();
    }
235
}