‪TYPO3CMS  ‪main
BulkExtensionRepositoryWriter.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use Doctrine\DBAL\Exception as DBALException;
27 
33 class ‪BulkExtensionRepositoryWriter implements \SplObserver
34 {
38  private const ‪TABLE_NAME = 'tx_extensionmanager_domain_model_extension';
39 
41 
47  protected ‪$sumRecords = 0;
48 
54  protected ‪$arrRows = [];
55 
61  protected static ‪$fieldNames = [
62  'extension_key',
63  'version',
64  'integer_version',
65  'current_version',
66  'alldownloadcounter',
67  'downloadcounter',
68  'title',
69  'ownerusername',
70  'author_name',
71  'author_email',
72  'authorcompany',
73  'last_updated',
74  'md5hash',
75  'remote',
76  'state',
77  'review_state',
78  'category',
79  'description',
80  'serialized_dependencies',
81  'update_comment',
82  'documentation_link',
83  'distribution_image',
84  'distribution_welcome_image',
85  ];
86 
93  protected ‪$maxRowsPerChunk = 50;
94 
100  protected ‪$remoteIdentifier;
101 
105  protected ‪$extensionRepository;
106 
110  protected ‪$extensionModel;
111 
115  protected ‪$connectionPool;
116 
123  protected ‪$minimumDateToImport;
124 
130  public function ‪__construct(
131  ‪ExtensionRepository $repository,
132  ‪Extension $extension,
135  ) {
136  $this->extensionRepository = $repository;
137  $this->extensionModel = $extension;
138  $this->connectionPool = ‪$connectionPool;
139  $this->parser = ‪$parser;
140  $this->parser->‪attach($this);
141 
142  $connection = $this->connectionPool->getConnectionForTable(self::TABLE_NAME);
144  $connection->getDatabasePlatform()
145  );
146  $countOfBindParamsPerRow = count(self::$fieldNames);
147  // flush at least chunks of 50 elements - in case the currently used
148  // database platform does not support that, the threshold is lowered
149  $this->maxRowsPerChunk = (int)min(
150  $this->maxRowsPerChunk,
151  floor($maxBindParameters / $countOfBindParamsPerRow)
152  );
153  // Only import extensions that are compatible with 7.6 or higher.
154  // TER only allows to publish extensions with compatibility if the TYPO3 version has been released
155  // And 7.6 was released on 10th of November 2015.
156  // This effectively reduces the number of extensions imported into this TYPO3 installation
157  // by more than 70%. As long as the extensions.xml from TER includes these files, we need to "hack" this
158  // within TYPO3 Core.
159  // For TYPO3 v11.0, this date could be set to 2018-10-02 (v9 LTS release).
160  // Also see https://decisions.typo3.org/t/reduce-size-of-extension-manager-db-table/329/
161  $this->minimumDateToImport = strtotime('2017-04-04T00:00:00+00:00');
162  }
163 
170  public function import(string $localExtensionListFile, string ‪$remoteIdentifier): void
171  {
172  // Remove all existing entries of this remote from the database
173  $this->connectionPool
174  ->getConnectionForTable(self::TABLE_NAME)
175  ->delete(
176  self::TABLE_NAME,
177  ['remote' => ‪$remoteIdentifier],
179  );
180  $this->remoteIdentifier = ‪$remoteIdentifier;
181  $zlibStream = 'compress.zlib://';
182  $this->sumRecords = 0;
183  $this->parser->parseXml($zlibStream . $localExtensionListFile);
184  // flush last rows to database if existing
185  if (!empty($this->arrRows)) {
186  $this->connectionPool
187  ->getConnectionForTable(self::TABLE_NAME)
188  ->bulkInsert(
189  'tx_extensionmanager_domain_model_extension',
190  $this->arrRows,
191  self::$fieldNames
192  );
193  }
195  }
196 
202  protected function ‪loadIntoDatabase(ExtensionXmlParser $subject): void
203  {
204  if ($this->sumRecords !== 0 && $this->sumRecords % $this->maxRowsPerChunk === 0) {
205  $this->connectionPool
206  ->getConnectionForTable(self::TABLE_NAME)
207  ->bulkInsert(
208  self::TABLE_NAME,
209  $this->arrRows,
210  self::$fieldNames
211  );
212  $this->arrRows = [];
213  }
214  if (!$subject->isValidVersionNumber()) {
215  // Skip in case extension version is not valid
216  return;
217  }
218  $versionRepresentations = VersionNumberUtility::convertVersionStringToArray($subject->getVersion());
219  // order must match that of self::$fieldNames!
220  $this->arrRows[] = [
221  $subject->getExtkey(),
222  $subject->getVersion(),
223  $versionRepresentations['version_int'],
224  // initialize current_version, correct value computed later:
225  0,
226  $subject->getAlldownloadcounter(),
227  $subject->getDownloadcounter(),
228  $subject->getTitle(),
229  $subject->getOwnerusername(),
230  $subject->getAuthorname(),
231  $subject->getAuthoremail(),
232  $subject->getAuthorcompany(),
233  $subject->getLastuploaddate(),
234  $subject->getT3xfilemd5(),
236  $this->extensionModel->getDefaultState($subject->getState() ?: ''),
237  $subject->getReviewstate(),
238  $this->extensionModel->getCategoryIndexFromStringOrNumber($subject->getCategory() ?: ''),
239  $subject->getDescription() ?: '',
240  $subject->getDependencies() ?: '',
241  $subject->getUploadcomment() ?: '',
242  $subject->getDocumentationLink() ?: '',
243  $subject->getDistributionImage() ?: '',
244  $subject->getDistributionWelcomeImage() ?: '',
245  ];
247  }
248 
254  public function ‪update(\SplSubject $subject): void
255  {
256  if ($subject instanceof ‪ExtensionXmlParser) {
257  if ((int)$subject->getLastuploaddate() > $this->minimumDateToImport) {
258  $this->‪loadIntoDatabase($subject);
259  }
260  }
261  }
262 
271  {
272  $uidsOfCurrentVersion = $this->‪fetchMaximalVersionsForAllExtensions($remoteIdentifier);
273  $queryBuilder = $this->connectionPool->getQueryBuilderForTable(self::TABLE_NAME);
274  $connection = $this->connectionPool->getConnectionForTable(self::TABLE_NAME);
276  $connection->getDatabasePlatform()
277  );
278 
279  foreach (array_chunk($uidsOfCurrentVersion, $maxBindParameters - 10) as $chunk) {
280  $queryBuilder
281  ->update(self::TABLE_NAME)
282  ->where(
283  $queryBuilder->expr()->in(
284  'uid',
285  $queryBuilder->createNamedParameter($chunk, ‪Connection::PARAM_INT_ARRAY)
286  )
287  )
288  ->set('current_version', 1)
289  ->executeStatement();
290  }
291  }
292 
298  protected function ‪fetchMaximalVersionsForAllExtensions(string ‪$remoteIdentifier): array
299  {
300  $queryBuilder = $this->connectionPool->getQueryBuilderForTable(self::TABLE_NAME);
301 
302  $queryResult = $queryBuilder
303  ->select('a.uid AS uid')
304  ->from(self::TABLE_NAME, 'a')
305  ->leftJoin(
306  'a',
307  self::TABLE_NAME,
308  'b',
309  $queryBuilder->expr()->and(
310  $queryBuilder->expr()->eq('a.remote', $queryBuilder->quoteIdentifier('b.remote')),
311  $queryBuilder->expr()->eq('a.extension_key', $queryBuilder->quoteIdentifier('b.extension_key')),
312  $queryBuilder->expr()->lt('a.integer_version', $queryBuilder->quoteIdentifier('b.integer_version'))
313  )
314  )
315  ->where(
316  $queryBuilder->expr()->eq(
317  'a.remote',
318  $queryBuilder->createNamedParameter(‪$remoteIdentifier)
319  ),
320  $queryBuilder->expr()->isNull('b.extension_key')
321  )
322  ->orderBy('a.uid')
323  ->executeQuery();
324 
325  $extensionUids = [];
326  while ($row = $queryResult->fetchAssociative()) {
327  $extensionUids[] = $row['uid'];
328  }
329 
330  return $extensionUids;
331  }
332 }
‪TYPO3\CMS\Core\Utility\VersionNumberUtility
Definition: VersionNumberUtility.php:26
‪TYPO3\CMS\Extensionmanager\Domain\Repository\BulkExtensionRepositoryWriter
Definition: BulkExtensionRepositoryWriter.php:34
‪TYPO3\CMS\Extensionmanager\Domain\Repository\BulkExtensionRepositoryWriter\fetchMaximalVersionsForAllExtensions
‪fetchMaximalVersionsForAllExtensions(string $remoteIdentifier)
Definition: BulkExtensionRepositoryWriter.php:289
‪TYPO3\CMS\Extensionmanager\Parser\ExtensionXmlParser\getExtkey
‪getExtkey()
Definition: ExtensionXmlParser.php:362
‪TYPO3\CMS\Extensionmanager\Domain\Repository\BulkExtensionRepositoryWriter\markExtensionWithMaximumVersionAsCurrent
‪markExtensionWithMaximumVersionAsCurrent(string $remoteIdentifier)
Definition: BulkExtensionRepositoryWriter.php:261
‪TYPO3\CMS\Extensionmanager\Domain\Repository\BulkExtensionRepositoryWriter\update
‪update(\SplSubject $subject)
Definition: BulkExtensionRepositoryWriter.php:245
‪TYPO3\CMS\Core\Database\Platform\PlatformInformation\getMaxBindParameters
‪static getMaxBindParameters(DoctrineAbstractPlatform $platform)
Definition: PlatformInformation.php:106
‪TYPO3\CMS\Extensionmanager\Parser\ExtensionXmlParser\attach
‪attach(\SplObserver $observer)
Definition: ExtensionXmlParser.php:267
‪TYPO3\CMS\Extensionmanager\Domain\Model\Extension
Definition: Extension.php:30
‪TYPO3\CMS\Extensionmanager\Parser\ExtensionXmlParser\getT3xfilemd5
‪getT3xfilemd5()
Definition: ExtensionXmlParser.php:402
‪TYPO3\CMS\Extensionmanager\Parser\ExtensionXmlParser\getCategory
‪getCategory()
Definition: ExtensionXmlParser.php:330
‪TYPO3\CMS\Extensionmanager\Domain\Repository\BulkExtensionRepositoryWriter\$minimumDateToImport
‪int $minimumDateToImport
Definition: BulkExtensionRepositoryWriter.php:114
‪TYPO3\CMS\Extensionmanager\Parser\ExtensionXmlParser\getVersion
‪getVersion()
Definition: ExtensionXmlParser.php:426
‪TYPO3\CMS\Extensionmanager\Parser\ExtensionXmlParser\getDownloadcounter
‪getDownloadcounter()
Definition: ExtensionXmlParser.php:354
‪TYPO3\CMS\Extensionmanager\Parser\ExtensionXmlParser\getAlldownloadcounter
‪getAlldownloadcounter()
Definition: ExtensionXmlParser.php:298
‪TYPO3\CMS\Extensionmanager\Domain\Repository
Definition: BulkExtensionRepositoryWriter.php:18
‪TYPO3\CMS\Extensionmanager\Parser\ExtensionXmlParser\getState
‪getState()
Definition: ExtensionXmlParser.php:394
‪TYPO3\CMS\Extensionmanager\Parser\ExtensionXmlParser\getAuthorname
‪getAuthorname()
Definition: ExtensionXmlParser.php:322
‪TYPO3\CMS\Extensionmanager\Parser\ExtensionXmlParser\getDependencies
‪getDependencies()
Definition: ExtensionXmlParser.php:338
‪TYPO3\CMS\Core\Database\Connection\PARAM_STR
‪const PARAM_STR
Definition: Connection.php:57
‪TYPO3\CMS\Extensionmanager\Parser\ExtensionXmlParser\getOwnerusername
‪getOwnerusername()
Definition: ExtensionXmlParser.php:378
‪TYPO3\CMS\Extensionmanager\Domain\Repository\BulkExtensionRepositoryWriter\$maxRowsPerChunk
‪int $maxRowsPerChunk
Definition: BulkExtensionRepositoryWriter.php:89
‪TYPO3\CMS\Extensionmanager\Domain\Repository\BulkExtensionRepositoryWriter\loadIntoDatabase
‪loadIntoDatabase(ExtensionXmlParser $subject)
Definition: BulkExtensionRepositoryWriter.php:193
‪TYPO3\CMS\Extensionmanager\Parser\ExtensionXmlParser\getDescription
‪getDescription()
Definition: ExtensionXmlParser.php:346
‪TYPO3\CMS\Extensionmanager\Domain\Repository\BulkExtensionRepositoryWriter\$arrRows
‪array $arrRows
Definition: BulkExtensionRepositoryWriter.php:52
‪TYPO3\CMS\Extensionmanager\Parser\ExtensionXmlParser\getAuthoremail
‪getAuthoremail()
Definition: ExtensionXmlParser.php:314
‪TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository
Definition: ExtensionRepository.php:37
‪TYPO3\CMS\Extensionmanager\Parser\ExtensionXmlParser\getTitle
‪getTitle()
Definition: ExtensionXmlParser.php:410
‪TYPO3\CMS\Extensionmanager\Parser\ExtensionXmlParser\getUploadcomment
‪getUploadcomment()
Definition: ExtensionXmlParser.php:418
‪TYPO3\CMS\Extensionmanager\Domain\Repository\BulkExtensionRepositoryWriter\$fieldNames
‪static array $fieldNames
Definition: BulkExtensionRepositoryWriter.php:58
‪TYPO3\CMS\Extensionmanager\Parser\ExtensionXmlParser\getReviewstate
‪getReviewstate()
Definition: ExtensionXmlParser.php:386
‪TYPO3\CMS\Extensionmanager\Parser\ExtensionXmlParser\getAuthorcompany
‪getAuthorcompany()
Definition: ExtensionXmlParser.php:306
‪TYPO3\CMS\Core\Database\Connection
Definition: Connection.php:41
‪TYPO3\CMS\Extensionmanager\Parser\ExtensionXmlParser\getDistributionImage
‪getDistributionImage()
Definition: ExtensionXmlParser.php:451
‪TYPO3\CMS\Extensionmanager\Parser\ExtensionXmlParser\isValidVersionNumber
‪isValidVersionNumber()
Definition: ExtensionXmlParser.php:434
‪TYPO3\CMS\Extensionmanager\Domain\Repository\BulkExtensionRepositoryWriter\$extensionRepository
‪ExtensionRepository $extensionRepository
Definition: BulkExtensionRepositoryWriter.php:99
‪TYPO3\CMS\Extensionmanager\Domain\Repository\BulkExtensionRepositoryWriter\$remoteIdentifier
‪string $remoteIdentifier
Definition: BulkExtensionRepositoryWriter.php:95
‪TYPO3\CMS\Extensionmanager\Parser\ExtensionXmlParser
Definition: ExtensionXmlParser.php:31
‪TYPO3\CMS\Core\Database\Platform\PlatformInformation
Definition: PlatformInformation.php:33
‪TYPO3\CMS\Extensionmanager\Domain\Repository\BulkExtensionRepositoryWriter\$parser
‪ExtensionXmlParser $parser
Definition: BulkExtensionRepositoryWriter.php:40
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Extensionmanager\Domain\Repository\BulkExtensionRepositoryWriter\$sumRecords
‪int $sumRecords
Definition: BulkExtensionRepositoryWriter.php:46
‪TYPO3\CMS\Extensionmanager\Domain\Repository\BulkExtensionRepositoryWriter\TABLE_NAME
‪const TABLE_NAME
Definition: BulkExtensionRepositoryWriter.php:38
‪TYPO3\CMS\Extensionmanager\Parser\ExtensionXmlParser\getLastuploaddate
‪getLastuploaddate()
Definition: ExtensionXmlParser.php:370
‪TYPO3\CMS\Extensionmanager\Domain\Repository\BulkExtensionRepositoryWriter\$extensionModel
‪Extension $extensionModel
Definition: BulkExtensionRepositoryWriter.php:103
‪TYPO3\CMS\Extensionmanager\Parser\ExtensionXmlParser\getDistributionWelcomeImage
‪getDistributionWelcomeImage()
Definition: ExtensionXmlParser.php:459
‪TYPO3\CMS\Core\Database\Connection\PARAM_INT_ARRAY
‪const PARAM_INT_ARRAY
Definition: Connection.php:72
‪TYPO3\CMS\Extensionmanager\Parser\ExtensionXmlParser\getDocumentationLink
‪getDocumentationLink()
Definition: ExtensionXmlParser.php:443
‪TYPO3\CMS\Extensionmanager\Domain\Repository\BulkExtensionRepositoryWriter\__construct
‪__construct(ExtensionRepository $repository, Extension $extension, ConnectionPool $connectionPool, ExtensionXmlParser $parser)
Definition: BulkExtensionRepositoryWriter.php:121
‪TYPO3\CMS\Extensionmanager\Domain\Repository\BulkExtensionRepositoryWriter\$connectionPool
‪ConnectionPool $connectionPool
Definition: BulkExtensionRepositoryWriter.php:107