TYPO3 CMS  TYPO3_8-7
InstallUtility.php
Go to the documentation of this file.
1 <?php
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 
24 
29 {
34 
39 
43  protected $dependencyUtility;
44 
49 
53  protected $listUtility;
54 
58  protected $databaseUtility;
59 
64 
68  protected $packageManager;
69 
73  protected $cacheManager;
74 
79 
83  protected $registry;
84 
88  public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManager $objectManager)
89  {
90  $this->objectManager = $objectManager;
91  }
92 
96  public function injectInstallToolSqlParser(\TYPO3\CMS\Install\Service\SqlSchemaMigrationService $installToolSqlParser)
97  {
98  $this->installToolSqlParser = $installToolSqlParser;
99  }
100 
104  public function injectDependencyUtility(\TYPO3\CMS\Extensionmanager\Utility\DependencyUtility $dependencyUtility)
105  {
106  $this->dependencyUtility = $dependencyUtility;
107  }
108 
112  public function injectFileHandlingUtility(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility $fileHandlingUtility)
113  {
114  $this->fileHandlingUtility = $fileHandlingUtility;
115  }
116 
120  public function injectListUtility(\TYPO3\CMS\Extensionmanager\Utility\ListUtility $listUtility)
121  {
122  $this->listUtility = $listUtility;
123  }
124 
128  public function injectDatabaseUtility(\TYPO3\CMS\Extensionmanager\Utility\DatabaseUtility $databaseUtility)
129  {
130  $this->databaseUtility = $databaseUtility;
131  }
132 
136  public function injectExtensionRepository(\TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository $extensionRepository)
137  {
138  $this->extensionRepository = $extensionRepository;
139  }
140 
144  public function injectPackageManager(\TYPO3\CMS\Core\Package\PackageManager $packageManager)
145  {
146  $this->packageManager = $packageManager;
147  }
148 
152  public function injectCacheManager(\TYPO3\CMS\Core\Cache\CacheManager $cacheManager)
153  {
154  $this->cacheManager = $cacheManager;
155  }
156 
160  public function injectSignalSlotDispatcher(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher $signalSlotDispatcher)
161  {
162  $this->signalSlotDispatcher = $signalSlotDispatcher;
163  }
164 
168  public function injectRegistry(\TYPO3\CMS\Core\Registry $registry)
169  {
170  $this->registry = $registry;
171  }
172 
180  public function install(...$extensionKeys)
181  {
182  $flushCaches = false;
183  foreach ($extensionKeys as $extensionKey) {
184  $this->loadExtension($extensionKey);
185  $extension = $this->enrichExtensionWithDetails($extensionKey, false);
186  $this->saveDefaultConfiguration($extensionKey);
187  if (!empty($extension['clearcacheonload']) || !empty($extension['clearCacheOnLoad'])) {
188  $flushCaches = true;
189  }
190  }
191 
192  if ($flushCaches) {
193  $this->cacheManager->flushCaches();
194  } else {
195  $this->cacheManager->flushCachesInGroup('system');
196  }
197  $this->reloadCaches();
198  $this->updateDatabase($extensionKeys);
199 
200  foreach ($extensionKeys as $extensionKey) {
201  $this->processExtensionSetup($extensionKey);
202  $this->emitAfterExtensionInstallSignal($extensionKey);
203  }
204  }
205 
209  public function processExtensionSetup($extensionKey)
210  {
211  $extension = $this->enrichExtensionWithDetails($extensionKey, false);
212  $this->ensureConfiguredDirectoriesExist($extension);
213  $this->importInitialFiles($extension['siteRelPath'] ?? '', $extensionKey);
214  $this->importStaticSqlFile($extension['siteRelPath']);
215  $this->importT3DFile($extension['siteRelPath']);
216  }
217 
224  public function uninstall($extensionKey)
225  {
226  $dependentExtensions = $this->dependencyUtility->findInstalledExtensionsThatDependOnMe($extensionKey);
227  if (is_array($dependentExtensions) && !empty($dependentExtensions)) {
228  throw new ExtensionManagerException(
229  \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate(
230  'extensionList.uninstall.dependencyError',
231  'extensionmanager',
232  [$extensionKey, implode(',', $dependentExtensions)]
233  ),
234  1342554622
235  );
236  }
237  $this->unloadExtension($extensionKey);
238  }
239 
246  public function isLoaded($extensionKey)
247  {
248  return $this->packageManager->isPackageActive($extensionKey);
249  }
250 
254  public function reloadAvailableExtensions()
255  {
256  $this->listUtility->reloadAvailableExtensions();
257  }
258 
264  protected function loadExtension($extensionKey)
265  {
266  $this->packageManager->activatePackage($extensionKey);
267  }
268 
274  protected function unloadExtension($extensionKey)
275  {
276  $this->packageManager->deactivatePackage($extensionKey);
277  $this->emitAfterExtensionUninstallSignal($extensionKey);
278  $this->cacheManager->flushCachesInGroup('system');
279  }
280 
286  protected function emitAfterExtensionInstallSignal($extensionKey)
287  {
288  $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterExtensionInstall', [$extensionKey, $this]);
289  }
290 
296  protected function emitAfterExtensionUninstallSignal($extensionKey)
297  {
298  $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterExtensionUninstall', [$extensionKey, $this]);
299  }
300 
307  public function isAvailable($extensionKey)
308  {
309  return $this->packageManager->isPackageAvailable($extensionKey);
310  }
311 
321  public function reloadPackageInformation($extensionKey)
322  {
323  if ($this->packageManager->isPackageAvailable($extensionKey)) {
324  $this->reloadOpcache();
325  $this->packageManager->reloadPackageInformation($extensionKey);
326  }
327  }
328 
338  public function enrichExtensionWithDetails($extensionKey, $loadTerInformation = true)
339  {
340  $extension = $this->getExtensionArray($extensionKey);
341  if (!$loadTerInformation) {
342  $availableAndInstalledExtensions = $this->listUtility->enrichExtensionsWithEmConfInformation([$extensionKey => $extension]);
343  } else {
344  $availableAndInstalledExtensions = $this->listUtility->enrichExtensionsWithEmConfAndTerInformation([$extensionKey => $extension]);
345  }
346 
347  if (!isset($availableAndInstalledExtensions[$extensionKey])) {
348  throw new ExtensionManagerException(
349  'Please check your uploaded extension "' . $extensionKey . '". The configuration file "ext_emconf.php" seems to be invalid.',
350  1391432222
351  );
352  }
353 
354  return $availableAndInstalledExtensions[$extensionKey];
355  }
356 
362  protected function getExtensionArray($extensionKey)
363  {
364  $availableExtensions = $this->listUtility->getAvailableExtensions();
365  if (isset($availableExtensions[$extensionKey])) {
366  return $availableExtensions[$extensionKey];
367  }
368  throw new ExtensionManagerException('Extension ' . $extensionKey . ' is not available', 1342864081);
369  }
370 
376  protected function ensureConfiguredDirectoriesExist(array $extension)
377  {
378  $this->fileHandlingUtility->ensureConfiguredDirectoriesExist($extension);
379  }
380 
387  public function processDatabaseUpdates(array $extension)
388  {
389  $extTablesSqlFile = PATH_site . $extension['siteRelPath'] . 'ext_tables.sql';
390  $extTablesSqlContent = '';
391  if (file_exists($extTablesSqlFile)) {
392  $extTablesSqlContent .= file_get_contents($extTablesSqlFile);
393  }
394  if ($extTablesSqlContent !== '') {
395  try {
396  $this->updateDbWithExtTablesSql($extTablesSqlContent);
397  } catch (\TYPO3\CMS\Core\Database\Schema\Exception\StatementException $e) {
398  throw new ExtensionManagerException(
399  $e->getMessage(),
400  1476340371
401  );
402  }
403  }
404 
405  $this->importStaticSqlFile($extension['siteRelPath']);
406  $this->importT3DFile($extension['siteRelPath']);
407  }
408 
416  protected function emitTablesDefinitionIsBeingBuiltSignal($extensionKey)
417  {
418  $signalReturn = $this->signalSlotDispatcher->dispatch(__CLASS__, 'tablesDefinitionIsBeingBuilt', [[], $extensionKey]);
419  // This is important to support old associated returns
420  $signalReturn = array_values($signalReturn);
421  $sqlString = $signalReturn[0];
422  if (!is_array($sqlString)) {
423  throw new ExtensionManagerException(
424  sprintf(
425  'The signal %s of class %s returned a value of type %s, but array was expected.',
426  'tablesDefinitionIsBeingBuilt',
427  __CLASS__,
428  gettype($sqlString)
429  ),
430  1382360258
431  );
432  }
433  return $sqlString;
434  }
435 
439  public function reloadCaches()
440  {
441  $this->reloadOpcache();
442  \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::loadExtLocalconf(false);
444  ->loadBaseTca(false)
445  ->loadExtTables(false);
446  }
447 
451  protected function reloadOpcache()
452  {
453  GeneralUtility::makeInstance(OpcodeCacheService::class)->clearAllActive();
454  }
455 
462  protected function updateDatabase(array $extensionKeys)
463  {
464  $sqlReader = GeneralUtility::makeInstance(SqlReader::class);
465  $schemaMigrator = GeneralUtility::makeInstance(SchemaMigrator::class);
466  $sqlStatements = [];
467  $sqlStatements[] = $sqlReader->getTablesDefinitionString();
468  foreach ($extensionKeys as $extensionKey) {
469  $sqlStatements += $this->emitTablesDefinitionIsBeingBuiltSignal($extensionKey);
470  }
471  $sqlStatements = $sqlReader->getCreateTableStatementArray(implode(LF . LF, array_filter($sqlStatements)));
472  $updateStatements = $schemaMigrator->getUpdateSuggestions($sqlStatements);
473 
474  $updateStatements = array_merge_recursive(...array_values($updateStatements));
475  $selectedStatements = [];
476  foreach (['add', 'change', 'create_table', 'change_table'] as $action) {
477  if (empty($updateStatements[$action])) {
478  continue;
479  }
480  $selectedStatements = array_merge(
481  $selectedStatements,
482  array_combine(array_keys($updateStatements[$action]), array_fill(0, count($updateStatements[$action]), true))
483  );
484  }
485 
486  $schemaMigrator->migrate($sqlStatements, $selectedStatements);
487  }
488 
494  protected function saveDefaultConfiguration($extensionKey)
495  {
497  $configUtility = $this->objectManager->get(\TYPO3\CMS\Extensionmanager\Utility\ConfigurationUtility::class);
498  $configUtility->saveDefaultConfiguration($extensionKey);
499  }
500 
506  public function updateDbWithExtTablesSql($rawDefinitions)
507  {
508  $sqlReader = GeneralUtility::makeInstance(SqlReader::class);
509  $statements = $sqlReader->getCreateTableStatementArray($rawDefinitions);
510  if (count($statements) !== 0) {
511  $schemaMigrationService = GeneralUtility::makeInstance(SchemaMigrator::class);
512  $schemaMigrationService->install($statements);
513  }
514  }
515 
521  public function importStaticSql($rawDefinitions)
522  {
523  $sqlReader = GeneralUtility::makeInstance(SqlReader::class);
524  $statements = $sqlReader->getStatementArray($rawDefinitions);
525 
526  $schemaMigrationService = GeneralUtility::makeInstance(SchemaMigrator::class);
527  $schemaMigrationService->importStaticData($statements, true);
528  }
529 
536  public function removeExtension($extension)
537  {
538  $absolutePath = $this->fileHandlingUtility->getAbsoluteExtensionPath($extension);
539  if ($this->fileHandlingUtility->isValidExtensionPath($absolutePath)) {
540  if ($this->packageManager->isPackageAvailable($extension)) {
541  // Package manager deletes the extension and removes the entry from PackageStates.php
542  $this->packageManager->deletePackage($extension);
543  } else {
544  // The extension is not listed in PackageStates.php, we can safely remove it
545  $this->fileHandlingUtility->removeDirectory($absolutePath);
546  }
547  } else {
548  throw new ExtensionManagerException('No valid extension path given.', 1342875724);
549  }
550  }
551 
558  public function getExtensionSqlDataDump($extension)
559  {
560  $extension = $this->enrichExtensionWithDetails($extension);
561  $filePrefix = PATH_site . $extension['siteRelPath'];
562  $sqlData['extTables'] = $this->getSqlDataDumpForFile($filePrefix . 'ext_tables.sql');
563  $sqlData['staticSql'] = $this->getSqlDataDumpForFile($filePrefix . 'ext_tables_static+adt.sql');
564  return $sqlData;
565  }
566 
573  protected function getSqlDataDumpForFile($sqlFile)
574  {
575  $sqlData = '';
576  if (file_exists($sqlFile)) {
577  $sqlContent = file_get_contents($sqlFile);
578  $fieldDefinitions = $this->installToolSqlParser->getFieldDefinitions_fileContent($sqlContent);
579  $sqlData = $this->databaseUtility->dumpStaticTables($fieldDefinitions);
580  }
581  return $sqlData;
582  }
583 
591  public function isUpdateAvailable(Extension $extensionData)
592  {
593  return (bool)$this->getUpdateableVersion($extensionData);
594  }
595 
603  public function getUpdateableVersion(Extension $extensionData)
604  {
605  // Only check for update for TER extensions
606  $version = $extensionData->getIntegerVersion();
607 
609  $extensionUpdates = $this->extensionRepository->findByVersionRangeAndExtensionKeyOrderedByVersion(
610  $extensionData->getExtensionKey(),
611  $version,
612  0,
613  false
614  );
615  if ($extensionUpdates->count() > 0) {
616  foreach ($extensionUpdates as $extensionUpdate) {
617  try {
618  $this->dependencyUtility->checkDependencies($extensionUpdate);
619  if (!$this->dependencyUtility->hasDependencyErrors()) {
620  return $extensionUpdate;
621  }
622  } catch (ExtensionManagerException $e) {
623  }
624  }
625  }
626  return false;
627  }
628 
635  protected function importT3DFile($extensionSiteRelPath)
636  {
637  $registryKeysToCheck = [
638  $extensionSiteRelPath . 'Initialisation/data.t3d',
639  $extensionSiteRelPath . 'Initialisation/dataImported',
640  ];
641  foreach ($registryKeysToCheck as $registryKeyToCheck) {
642  if ($this->registry->get('extensionDataImport', $registryKeyToCheck)) {
643  // Data was imported before => early return
644  return;
645  }
646  }
647  $importFileToUse = null;
648  $possibleImportFiles = [
649  $extensionSiteRelPath . 'Initialisation/data.t3d',
650  $extensionSiteRelPath . 'Initialisation/data.xml'
651  ];
652  foreach ($possibleImportFiles as $possibleImportFile) {
653  if (!file_exists(PATH_site . $possibleImportFile)) {
654  continue;
655  }
656  $importFileToUse = $possibleImportFile;
657  }
658  if ($importFileToUse !== null) {
660  $importExportUtility = $this->objectManager->get(ImportExportUtility::class);
661  try {
662  $importResult = $importExportUtility->importT3DFile(PATH_site . $importFileToUse, 0);
663  $this->registry->set('extensionDataImport', $extensionSiteRelPath . 'Initialisation/dataImported', 1);
664  $this->emitAfterExtensionT3DImportSignal($importFileToUse, $importResult);
665  } catch (\ErrorException $e) {
667  $logger = $this->objectManager->get(\TYPO3\CMS\Core\Log\LogManager::class)->getLogger(__CLASS__);
668  $logger->log(\TYPO3\CMS\Core\Log\LogLevel::WARNING, $e->getMessage());
669  }
670  }
671  }
672 
679  protected function emitAfterExtensionT3DImportSignal($importFileToUse, $importResult)
680  {
681  $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterExtensionT3DImport', [$importFileToUse, $importResult, $this]);
682  }
683 
690  protected function importStaticSqlFile($extensionSiteRelPath)
691  {
692  $extTablesStaticSqlRelFile = $extensionSiteRelPath . 'ext_tables_static+adt.sql';
693  if (!$this->registry->get('extensionDataImport', $extTablesStaticSqlRelFile)) {
694  $extTablesStaticSqlFile = PATH_site . $extTablesStaticSqlRelFile;
695  $shortFileHash = '';
696  if (file_exists($extTablesStaticSqlFile)) {
697  $extTablesStaticSqlContent = file_get_contents($extTablesStaticSqlFile);
698  $shortFileHash = md5($extTablesStaticSqlContent);
699  $this->importStaticSql($extTablesStaticSqlContent);
700  }
701  $this->registry->set('extensionDataImport', $extTablesStaticSqlRelFile, $shortFileHash);
702  $this->emitAfterExtensionStaticSqlImportSignal($extTablesStaticSqlRelFile);
703  }
704  }
705 
711  protected function emitAfterExtensionStaticSqlImportSignal($extTablesStaticSqlRelFile)
712  {
713  $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterExtensionStaticSqlImport', [$extTablesStaticSqlRelFile, $this]);
714  }
715 
723  protected function importInitialFiles($extensionSiteRelPath, $extensionKey)
724  {
725  $importRelFolder = $extensionSiteRelPath . 'Initialisation/Files';
726  if (!$this->registry->get('extensionDataImport', $importRelFolder)) {
727  $importFolder = PATH_site . $importRelFolder;
728  if (file_exists($importFolder)) {
729  $destinationRelPath = $GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'] . $extensionKey;
730  $destinationAbsolutePath = PATH_site . $destinationRelPath;
731  if (!file_exists($destinationAbsolutePath) &&
732  GeneralUtility::isAllowedAbsPath($destinationAbsolutePath)
733  ) {
734  GeneralUtility::mkdir($destinationAbsolutePath);
735  }
736  GeneralUtility::copyDirectory($importRelFolder, $destinationRelPath);
737  $this->registry->set('extensionDataImport', $importRelFolder, 1);
738  $this->emitAfterExtensionFileImportSignal($destinationAbsolutePath);
739  }
740  }
741  }
742 
748  protected function emitAfterExtensionFileImportSignal($destinationAbsolutePath)
749  {
750  $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterExtensionFileImport', [$destinationAbsolutePath, $this]);
751  }
752 }
injectExtensionRepository(\TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository $extensionRepository)
injectPackageManager(\TYPO3\CMS\Core\Package\PackageManager $packageManager)
emitAfterExtensionFileImportSignal($destinationAbsolutePath)
injectFileHandlingUtility(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility $fileHandlingUtility)
injectDependencyUtility(\TYPO3\CMS\Extensionmanager\Utility\DependencyUtility $dependencyUtility)
injectInstallToolSqlParser(\TYPO3\CMS\Install\Service\SqlSchemaMigrationService $installToolSqlParser)
injectCacheManager(\TYPO3\CMS\Core\Cache\CacheManager $cacheManager)
injectListUtility(\TYPO3\CMS\Extensionmanager\Utility\ListUtility $listUtility)
injectRegistry(\TYPO3\CMS\Core\Registry $registry)
enrichExtensionWithDetails($extensionKey, $loadTerInformation=true)
static copyDirectory($source, $destination)
injectDatabaseUtility(\TYPO3\CMS\Extensionmanager\Utility\DatabaseUtility $databaseUtility)
static makeInstance($className,... $constructorArguments)
injectSignalSlotDispatcher(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher $signalSlotDispatcher)
emitAfterExtensionStaticSqlImportSignal($extTablesStaticSqlRelFile)
importInitialFiles($extensionSiteRelPath, $extensionKey)
injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManager $objectManager)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
emitAfterExtensionT3DImportSignal($importFileToUse, $importResult)