TYPO3 CMS  TYPO3_7-6
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 
23 
28 {
33 
38 
42  protected $dependencyUtility;
43 
48 
52  protected $listUtility;
53 
57  protected $databaseUtility;
58 
63 
67  protected $packageManager;
68 
72  protected $cacheManager;
73 
78 
82  protected $registry;
83 
87  public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManager $objectManager)
88  {
89  $this->objectManager = $objectManager;
90  }
91 
95  public function injectInstallToolSqlParser(\TYPO3\CMS\Install\Service\SqlSchemaMigrationService $installToolSqlParser)
96  {
97  $this->installToolSqlParser = $installToolSqlParser;
98  }
99 
103  public function injectDependencyUtility(\TYPO3\CMS\Extensionmanager\Utility\DependencyUtility $dependencyUtility)
104  {
105  $this->dependencyUtility = $dependencyUtility;
106  }
107 
111  public function injectFileHandlingUtility(\TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility $fileHandlingUtility)
112  {
113  $this->fileHandlingUtility = $fileHandlingUtility;
114  }
115 
119  public function injectListUtility(\TYPO3\CMS\Extensionmanager\Utility\ListUtility $listUtility)
120  {
121  $this->listUtility = $listUtility;
122  }
123 
127  public function injectDatabaseUtility(\TYPO3\CMS\Extensionmanager\Utility\DatabaseUtility $databaseUtility)
128  {
129  $this->databaseUtility = $databaseUtility;
130  }
131 
135  public function injectExtensionRepository(\TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository $extensionRepository)
136  {
137  $this->extensionRepository = $extensionRepository;
138  }
139 
143  public function injectPackageManager(\TYPO3\CMS\Core\Package\PackageManager $packageManager)
144  {
145  $this->packageManager = $packageManager;
146  }
147 
151  public function injectCacheManager(\TYPO3\CMS\Core\Cache\CacheManager $cacheManager)
152  {
153  $this->cacheManager = $cacheManager;
154  }
155 
159  public function injectSignalSlotDispatcher(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher $signalSlotDispatcher)
160  {
161  $this->signalSlotDispatcher = $signalSlotDispatcher;
162  }
163 
167  public function injectRegistry(\TYPO3\CMS\Core\Registry $registry)
168  {
169  $this->registry = $registry;
170  }
171 
180  public function install($extensionKey)
181  {
182  $extension = $this->enrichExtensionWithDetails($extensionKey, false);
183  $this->loadExtension($extensionKey);
184  if (!empty($extension['clearcacheonload']) || !empty($extension['clearCacheOnLoad'])) {
185  $this->cacheManager->flushCaches();
186  } else {
187  $this->cacheManager->flushCachesInGroup('system');
188  }
189  $this->reloadCaches();
190  $this->processExtensionSetup($extensionKey);
191 
192  $this->emitAfterExtensionInstallSignal($extensionKey);
193  }
194 
198  public function processExtensionSetup($extensionKey)
199  {
200  $extension = $this->enrichExtensionWithDetails($extensionKey, false);
201  $this->ensureConfiguredDirectoriesExist($extension);
202  $this->importInitialFiles($extension['siteRelPath'], $extensionKey);
203  $this->processDatabaseUpdates($extension);
204  $this->processRuntimeDatabaseUpdates($extensionKey);
205  $this->saveDefaultConfiguration($extensionKey);
206  }
207 
215  public function uninstall($extensionKey)
216  {
217  $dependentExtensions = $this->dependencyUtility->findInstalledExtensionsThatDependOnMe($extensionKey);
218  if (is_array($dependentExtensions) && !empty($dependentExtensions)) {
219  throw new ExtensionManagerException(
220  \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate(
221  'extensionList.uninstall.dependencyError',
222  'extensionmanager',
223  [$extensionKey, implode(',', $dependentExtensions)]
224  ),
225  1342554622
226  );
227  } else {
228  $this->unloadExtension($extensionKey);
229  }
230  }
231 
238  public function isLoaded($extensionKey)
239  {
240  return $this->packageManager->isPackageActive($extensionKey);
241  }
242 
246  public function reloadAvailableExtensions()
247  {
248  $this->listUtility->reloadAvailableExtensions();
249  }
250 
257  protected function loadExtension($extensionKey)
258  {
259  $this->packageManager->activatePackage($extensionKey);
260  }
261 
268  protected function unloadExtension($extensionKey)
269  {
270  $this->packageManager->deactivatePackage($extensionKey);
271  $this->emitAfterExtensionUninstallSignal($extensionKey);
272  $this->cacheManager->flushCachesInGroup('system');
273  }
274 
280  protected function emitAfterExtensionInstallSignal($extensionKey)
281  {
282  $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterExtensionInstall', [$extensionKey, $this]);
283  }
284 
290  protected function emitAfterExtensionUninstallSignal($extensionKey)
291  {
292  $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterExtensionUninstall', [$extensionKey, $this]);
293  }
294 
301  public function isAvailable($extensionKey)
302  {
303  return $this->packageManager->isPackageAvailable($extensionKey);
304  }
305 
315  public function reloadPackageInformation($extensionKey)
316  {
317  if ($this->packageManager->isPackageAvailable($extensionKey)) {
318  $this->reloadOpcache();
319  $this->packageManager->reloadPackageInformation($extensionKey);
320  }
321  }
322 
332  public function enrichExtensionWithDetails($extensionKey, $loadTerInformation = true)
333  {
334  $extension = $this->getExtensionArray($extensionKey);
335  if (!$loadTerInformation) {
336  $availableAndInstalledExtensions = $this->listUtility->enrichExtensionsWithEmConfInformation([$extensionKey => $extension]);
337  } else {
338  $availableAndInstalledExtensions = $this->listUtility->enrichExtensionsWithEmConfAndTerInformation([$extensionKey => $extension]);
339  }
340 
341  if (!isset($availableAndInstalledExtensions[$extensionKey])) {
342  throw new ExtensionManagerException(
343  'Please check your uploaded extension "' . $extensionKey . '". The configuration file "ext_emconf.php" seems to be invalid.',
344  1391432222
345  );
346  }
347 
348  return $availableAndInstalledExtensions[$extensionKey];
349  }
350 
356  protected function getExtensionArray($extensionKey)
357  {
358  $availableExtensions = $this->listUtility->getAvailableExtensions();
359  if (isset($availableExtensions[$extensionKey])) {
360  return $availableExtensions[$extensionKey];
361  } else {
362  throw new ExtensionManagerException('Extension ' . $extensionKey . ' is not available', 1342864081);
363  }
364  }
365 
371  protected function ensureConfiguredDirectoriesExist(array $extension)
372  {
373  $this->fileHandlingUtility->ensureConfiguredDirectoriesExist($extension);
374  }
375 
382  public function processDatabaseUpdates(array $extension)
383  {
384  $extTablesSqlFile = PATH_site . $extension['siteRelPath'] . 'ext_tables.sql';
385  $extTablesSqlContent = '';
386  if (file_exists($extTablesSqlFile)) {
387  $extTablesSqlContent .= GeneralUtility::getUrl($extTablesSqlFile);
388  }
389  if ($extTablesSqlContent !== '') {
390  $this->updateDbWithExtTablesSql($extTablesSqlContent);
391  }
392 
393  $this->importStaticSqlFile($extension['siteRelPath']);
394  $this->importT3DFile($extension['siteRelPath']);
395  }
396 
403  protected function processRuntimeDatabaseUpdates($extensionKey)
404  {
405  $sqlString = $this->emitTablesDefinitionIsBeingBuiltSignal($extensionKey);
406  if (!empty($sqlString)) {
407  $this->updateDbWithExtTablesSql(implode(LF . LF . LF . LF, $sqlString));
408  }
409  }
410 
418  protected function emitTablesDefinitionIsBeingBuiltSignal($extensionKey)
419  {
420  $signalReturn = $this->signalSlotDispatcher->dispatch(__CLASS__, 'tablesDefinitionIsBeingBuilt', [[], $extensionKey]);
421  // This is important to support old associated returns
422  $signalReturn = array_values($signalReturn);
423  $sqlString = $signalReturn[0];
424  if (!is_array($sqlString)) {
425  throw new ExtensionManagerException(
426  sprintf(
427  'The signal %s of class %s returned a value of type %s, but array was expected.',
428  'tablesDefinitionIsBeingBuilt',
429  __CLASS__,
430  gettype($sqlString)
431  ),
432  1382360258
433  );
434  }
435  return $sqlString;
436  }
437 
443  public function reloadCaches()
444  {
445  $this->reloadOpcache();
446  \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::loadExtLocalconf(false);
447  \TYPO3\CMS\Core\Core\Bootstrap::getInstance()->loadExtensionTables(false);
448  }
449 
453  protected function reloadOpcache()
454  {
455  GeneralUtility::makeInstance(OpcodeCacheService::class)->clearAllActive();
456  }
457 
464  protected function saveDefaultConfiguration($extensionKey)
465  {
467  $configUtility = $this->objectManager->get(\TYPO3\CMS\Extensionmanager\Utility\ConfigurationUtility::class);
468  $configUtility->saveDefaultConfiguration($extensionKey);
469  }
470 
478  public function updateDbWithExtTablesSql($rawDefinitions)
479  {
480  $fieldDefinitionsFromFile = $this->installToolSqlParser->getFieldDefinitions_fileContent($rawDefinitions);
481  if (!empty($fieldDefinitionsFromFile)) {
482  $fieldDefinitionsFromCurrentDatabase = $this->installToolSqlParser->getFieldDefinitions_database();
483  $diff = $this->installToolSqlParser->getDatabaseExtra($fieldDefinitionsFromFile, $fieldDefinitionsFromCurrentDatabase);
484  $updateStatements = $this->installToolSqlParser->getUpdateSuggestions($diff);
485  $db = $this->getDatabaseConnection();
486  $errors = [];
487  foreach ((array)$updateStatements['add'] as $string) {
488  $res = $db->admin_query($string);
489  if ($res === false) {
490  $errors[] = 'Error: You have an error in your SQL syntax;' . $string;
491  }
492  }
493  foreach ((array)$updateStatements['change'] as $string) {
494  $res = $db->admin_query($string);
495  if ($res === false) {
496  $errors[] = 'Error: You have an error in your SQL syntax;' . $string;
497  }
498  }
499  foreach ((array)$updateStatements['create_table'] as $string) {
500  $res = $db->admin_query($string);
501  if ($res === false) {
502  $errors[] = 'Error: You have an error in your SQL syntax;' . $string;
503  }
504  }
505  if (!empty($errors)) {
506  throw new SqlErrorException(implode(PHP_EOL, $errors), 1509303611);
507  }
508  }
509  }
510 
518  public function importStaticSql($rawDefinitions)
519  {
520  $statements = $this->installToolSqlParser->getStatementarray($rawDefinitions, 1);
521  list($statementsPerTable, $insertCount) = $this->installToolSqlParser->getCreateTables($statements, 1);
522  $db = $this->getDatabaseConnection();
523  $errors = [];
524  // Traverse the tables
525  foreach ($statementsPerTable as $table => $query) {
526  $db->admin_query('DROP TABLE IF EXISTS ' . $table);
527  $res = $db->admin_query($query);
528  if ($res === false) {
529  $errors[] = 'Error: You have an error in your SQL syntax;' . PHP_EOL . $query;
530  } elseif ($insertCount[$table]) {
531  $insertStatements = $this->installToolSqlParser->getTableInsertStatements($statements, $table);
532  foreach ($insertStatements as $statement) {
533  $db->admin_query($statement);
534  }
535  }
536  }
537  if (!empty($errors)) {
538  throw new SqlErrorException(implode(PHP_EOL, $errors), 1509303783);
539  }
540  }
541 
549  public function removeExtension($extension)
550  {
551  $absolutePath = $this->fileHandlingUtility->getAbsoluteExtensionPath($extension);
552  if ($this->fileHandlingUtility->isValidExtensionPath($absolutePath)) {
553  if ($this->packageManager->isPackageAvailable($extension)) {
554  // Package manager deletes the extension and removes the entry from PackageStates.php
555  $this->packageManager->deletePackage($extension);
556  } else {
557  // The extension is not listed in PackageStates.php, we can safely remove it
558  $this->fileHandlingUtility->removeDirectory($absolutePath);
559  }
560  } else {
561  throw new ExtensionManagerException('No valid extension path given.', 1342875724);
562  }
563  }
564 
571  public function getExtensionSqlDataDump($extension)
572  {
573  $extension = $this->enrichExtensionWithDetails($extension);
574  $filePrefix = PATH_site . $extension['siteRelPath'];
575  $sqlData['extTables'] = $this->getSqlDataDumpForFile($filePrefix . 'ext_tables.sql');
576  $sqlData['staticSql'] = $this->getSqlDataDumpForFile($filePrefix . 'ext_tables_static+adt.sql');
577  return $sqlData;
578  }
579 
586  protected function getSqlDataDumpForFile($sqlFile)
587  {
588  $sqlData = '';
589  if (file_exists($sqlFile)) {
590  $sqlContent = GeneralUtility::getUrl($sqlFile);
591  $fieldDefinitions = $this->installToolSqlParser->getFieldDefinitions_fileContent($sqlContent);
592  $sqlData = $this->databaseUtility->dumpStaticTables($fieldDefinitions);
593  }
594  return $sqlData;
595  }
596 
604  public function isUpdateAvailable(Extension $extensionData)
605  {
606  return (bool)$this->getUpdateableVersion($extensionData);
607  }
608 
616  public function getUpdateableVersion(Extension $extensionData)
617  {
618  // Only check for update for TER extensions
619  $version = $extensionData->getIntegerVersion();
620 
622  $extensionUpdates = $this->extensionRepository->findByVersionRangeAndExtensionKeyOrderedByVersion(
623  $extensionData->getExtensionKey(),
624  $version,
625  0,
626  false
627  );
628  if ($extensionUpdates->count() > 0) {
629  foreach ($extensionUpdates as $extensionUpdate) {
630  try {
631  $this->dependencyUtility->checkDependencies($extensionUpdate);
632  if (!$this->dependencyUtility->hasDependencyErrors()) {
633  return $extensionUpdate;
634  }
635  } catch (ExtensionManagerException $e) {
636  }
637  }
638  }
639  return false;
640  }
641 
649  protected function importT3DFile($extensionSiteRelPath)
650  {
651  $registryKeysToCheck = [
652  $extensionSiteRelPath . 'Initialisation/data.t3d',
653  $extensionSiteRelPath . 'Initialisation/dataImported',
654  ];
655  foreach ($registryKeysToCheck as $registryKeyToCheck) {
656  if ($this->registry->get('extensionDataImport', $registryKeyToCheck)) {
657  // Data was imported before => early return
658  return;
659  }
660  }
661  $importFileToUse = null;
662  $possibleImportFiles = [
663  $extensionSiteRelPath . 'Initialisation/data.t3d',
664  $extensionSiteRelPath . 'Initialisation/data.xml'
665  ];
666  foreach ($possibleImportFiles as $possibleImportFile) {
667  if (!file_exists(PATH_site . $possibleImportFile)) {
668  continue;
669  }
670  $importFileToUse = $possibleImportFile;
671  }
672  if ($importFileToUse !== null) {
674  $importExportUtility = $this->objectManager->get(ImportExportUtility::class);
675  try {
676  $importResult = $importExportUtility->importT3DFile(PATH_site . $importFileToUse, 0);
677  $this->registry->set('extensionDataImport', $extensionSiteRelPath . 'Initialisation/dataImported', 1);
678  $this->emitAfterExtensionT3DImportSignal($importFileToUse, $importResult);
679  } catch (\ErrorException $e) {
681  $logger = $this->objectManager->get(\TYPO3\CMS\Core\Log\LogManager::class)->getLogger(__CLASS__);
682  $logger->log(\TYPO3\CMS\Core\Log\LogLevel::WARNING, $e->getMessage());
683  }
684  }
685  }
686 
693  protected function emitAfterExtensionT3DImportSignal($importFileToUse, $importResult)
694  {
695  $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterExtensionT3DImport', [$importFileToUse, $importResult, $this]);
696  }
697 
705  protected function importStaticSqlFile($extensionSiteRelPath)
706  {
707  $extTablesStaticSqlRelFile = $extensionSiteRelPath . 'ext_tables_static+adt.sql';
708  if (!$this->registry->get('extensionDataImport', $extTablesStaticSqlRelFile)) {
709  $extTablesStaticSqlFile = PATH_site . $extTablesStaticSqlRelFile;
710  $shortFileHash = '';
711  if (file_exists($extTablesStaticSqlFile)) {
712  $extTablesStaticSqlContent = GeneralUtility::getUrl($extTablesStaticSqlFile);
713  $shortFileHash = md5($extTablesStaticSqlContent);
714  $this->importStaticSql($extTablesStaticSqlContent);
715  }
716  $this->registry->set('extensionDataImport', $extTablesStaticSqlRelFile, $shortFileHash);
717  $this->emitAfterExtensionStaticSqlImportSignal($extTablesStaticSqlRelFile);
718  }
719  }
720 
726  protected function emitAfterExtensionStaticSqlImportSignal($extTablesStaticSqlRelFile)
727  {
728  $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterExtensionStaticSqlImport', [$extTablesStaticSqlRelFile, $this]);
729  }
730 
738  protected function importInitialFiles($extensionSiteRelPath, $extensionKey)
739  {
740  $importRelFolder = $extensionSiteRelPath . 'Initialisation/Files';
741  if (!$this->registry->get('extensionDataImport', $importRelFolder)) {
742  $importFolder = PATH_site . $importRelFolder;
743  if (file_exists($importFolder)) {
744  $destinationRelPath = $GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'] . $extensionKey;
745  $destinationAbsolutePath = PATH_site . $destinationRelPath;
746  if (!file_exists($destinationAbsolutePath) &&
747  GeneralUtility::isAllowedAbsPath($destinationAbsolutePath)
748  ) {
749  GeneralUtility::mkdir($destinationAbsolutePath);
750  }
751  GeneralUtility::copyDirectory($importRelFolder, $destinationRelPath);
752  $this->registry->set('extensionDataImport', $importRelFolder, 1);
753  $this->emitAfterExtensionFileImportSignal($destinationAbsolutePath);
754  }
755  }
756  }
757 
763  protected function emitAfterExtensionFileImportSignal($destinationAbsolutePath)
764  {
765  $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterExtensionFileImport', [$destinationAbsolutePath, $this]);
766  }
767 
771  protected function getDatabaseConnection()
772  {
773  return $GLOBALS['TYPO3_DB'];
774  }
775 }
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)
injectSignalSlotDispatcher(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher $signalSlotDispatcher)
emitAfterExtensionStaticSqlImportSignal($extTablesStaticSqlRelFile)
importInitialFiles($extensionSiteRelPath, $extensionKey)
injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManager $objectManager)
static getUrl($url, $includeHeader=0, $requestHeaders=false, &$report=null)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
emitAfterExtensionT3DImportSignal($importFileToUse, $importResult)