Commit 2049dac1 authored by Benni Mack's avatar Benni Mack Committed by Georg Ringer
Browse files

[TASK] EM: Reduce dependencies when writing t3x files

The method unpackExtensionFromExtensionDataArray()
is only used once, but with the second argument
set to "null", so all complexity down to EmConfUtility
is reduced.

Same goes for `isValidExtensionPath` in FileHandlingUtility
which is not related to FileHandlingUtility and does not
need to be exposed.

Resolves: #93059
Releases: master
Change-Id: I035e2faa81d6312c09ae5d040335784ec389f7c5
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/67093


Tested-by: default avatarTYPO3com <noreply@typo3.com>
Tested-by: Oliver Bartsch's avatarOliver Bartsch <bo@cedev.de>
Tested-by: Georg Ringer's avatarGeorg Ringer <georg.ringer@gmail.com>
Reviewed-by: Oliver Bartsch's avatarOliver Bartsch <bo@cedev.de>
Reviewed-by: Georg Ringer's avatarGeorg Ringer <georg.ringer@gmail.com>
parent c1d4294e
...@@ -172,7 +172,7 @@ class TerExtensionRemote implements ExtensionDownloaderRemoteInterface, Listable ...@@ -172,7 +172,7 @@ class TerExtensionRemote implements ExtensionDownloaderRemoteInterface, Listable
} }
$extensionData = $this->decodeExchangeData($downloadedContent); $extensionData = $this->decodeExchangeData($downloadedContent);
if (!empty($extensionData['extKey']) && is_string($extensionData['extKey'])) { if (!empty($extensionData['extKey']) && is_string($extensionData['extKey'])) {
$fileHandler->unpackExtensionFromExtensionDataArray($extensionData, null, $pathType); $fileHandler->unpackExtensionFromExtensionDataArray($extensionData['extKey'], $extensionData, $pathType);
} else { } else {
throw new VerificationFailedException('Downloaded t3x file could not be extracted', 1334426698); throw new VerificationFailedException('Downloaded t3x file could not be extracted', 1334426698);
} }
......
...@@ -16,7 +16,6 @@ ...@@ -16,7 +16,6 @@
namespace TYPO3\CMS\Extensionmanager\Utility; namespace TYPO3\CMS\Extensionmanager\Utility;
use TYPO3\CMS\Core\SingletonInterface; use TYPO3\CMS\Core\SingletonInterface;
use TYPO3\CMS\Extensionmanager\Domain\Model\Extension;
/** /**
* Utility for dealing with ext_emconf * Utility for dealing with ext_emconf
...@@ -47,24 +46,19 @@ class EmConfUtility implements SingletonInterface ...@@ -47,24 +46,19 @@ class EmConfUtility implements SingletonInterface
/** /**
* Generates the content for the ext_emconf.php file * Generates the content for the ext_emconf.php file
* Sets dependencies from TER data if any
* *
* @param array $extensionData * @param string $extensionKey
* @param Extension $extension Extension object from TER data * @param array $emConf
* @return string * @return string
* @internal
*/ */
public function constructEmConf(array $extensionData, Extension $extension = null) public function constructEmConf(string $extensionKey, array $emConf)
{ {
if (is_object($extension) && empty($extensionData['EM_CONF']['constraints'])) { $emConf = $this->fixEmConf($emConf);
$extensionData['EM_CONF']['constraints'] = unserialize($extension->getSerializedDependencies(), ['allowed_classes' => false]);
}
$emConf = $this->fixEmConf($extensionData['EM_CONF']);
$emConf = var_export($emConf, true); $emConf = var_export($emConf, true);
$code = '<?php return '<?php
/*************************************************************** /***************************************************************
* Extension Manager/Repository config file for ext "' . $extensionData['extKey'] . '". * Extension Manager/Repository config file for ext "' . $extensionKey . '".
* *
* Auto generated ' . date('d-m-Y H:i') . ' * Auto generated ' . date('d-m-Y H:i') . '
* *
...@@ -76,7 +70,6 @@ class EmConfUtility implements SingletonInterface ...@@ -76,7 +70,6 @@ class EmConfUtility implements SingletonInterface
$EM_CONF[$_EXTKEY] = ' . $emConf . '; $EM_CONF[$_EXTKEY] = ' . $emConf . ';
'; ';
return $code;
} }
/** /**
......
...@@ -85,20 +85,21 @@ class FileHandlingUtility implements SingletonInterface, LoggerAwareInterface ...@@ -85,20 +85,21 @@ class FileHandlingUtility implements SingletonInterface, LoggerAwareInterface
/** /**
* Unpack an extension in t3x data format and write files * Unpack an extension in t3x data format and write files
* *
* @param string $extensionKey
* @param array $extensionData * @param array $extensionData
* @param Extension|null $extension
* @param string $pathType * @param string $pathType
*/ */
public function unpackExtensionFromExtensionDataArray(array $extensionData, Extension $extension = null, $pathType = 'Local') public function unpackExtensionFromExtensionDataArray(string $extensionKey, array $extensionData, $pathType = 'Local')
{ {
$extensionDir = $this->makeAndClearExtensionDir($extensionData['extKey'], $pathType); $files = $extensionData['FILES'] ?? [];
$files = $this->extractFilesArrayFromExtensionData($extensionData); $emConfData = $extensionData['EM_CONF'] ?? [];
$extensionDir = $this->makeAndClearExtensionDir($extensionKey, $pathType);
$directories = $this->extractDirectoriesFromExtensionData($files); $directories = $this->extractDirectoriesFromExtensionData($files);
$files = array_diff_key($files, array_flip($directories)); $files = array_diff_key($files, array_flip($directories));
$this->createDirectoriesForExtensionFiles($directories, $extensionDir); $this->createDirectoriesForExtensionFiles($directories, $extensionDir);
$this->writeExtensionFiles($files, $extensionDir); $this->writeExtensionFiles($files, $extensionDir);
$this->writeEmConfToFile($extensionData, $extensionDir, $extension); $this->writeEmConfToFile($extensionKey, $emConfData, $extensionDir);
$this->reloadPackageInformation($extensionData['extKey']); $this->reloadPackageInformation($extensionKey);
} }
/** /**
...@@ -119,17 +120,6 @@ class FileHandlingUtility implements SingletonInterface, LoggerAwareInterface ...@@ -119,17 +120,6 @@ class FileHandlingUtility implements SingletonInterface, LoggerAwareInterface
return array_unique($directories); return array_unique($directories);
} }
/**
* Returns the "FILES" part from the data array
*
* @param array $extensionData
* @return mixed
*/
protected function extractFilesArrayFromExtensionData(array $extensionData)
{
return $extensionData['FILES'];
}
/** /**
* Loops over an array of directories and creates them in the given root path * Loops over an array of directories and creates them in the given root path
* It also creates nested directory structures * It also creates nested directory structures
...@@ -137,7 +127,7 @@ class FileHandlingUtility implements SingletonInterface, LoggerAwareInterface ...@@ -137,7 +127,7 @@ class FileHandlingUtility implements SingletonInterface, LoggerAwareInterface
* @param array $directories * @param array $directories
* @param string $rootPath * @param string $rootPath
*/ */
protected function createDirectoriesForExtensionFiles(array $directories, $rootPath) protected function createDirectoriesForExtensionFiles(array $directories, string $rootPath)
{ {
foreach ($directories as $directory) { foreach ($directories as $directory) {
$this->createNestedDirectory($rootPath . $directory); $this->createNestedDirectory($rootPath . $directory);
...@@ -191,7 +181,6 @@ class FileHandlingUtility implements SingletonInterface, LoggerAwareInterface ...@@ -191,7 +181,6 @@ class FileHandlingUtility implements SingletonInterface, LoggerAwareInterface
$this->removeDirectory($extDirPath); $this->removeDirectory($extDirPath);
} }
$this->addDirectory($extDirPath); $this->addDirectory($extDirPath);
return $extDirPath; return $extDirPath;
} }
...@@ -259,44 +248,17 @@ class FileHandlingUtility implements SingletonInterface, LoggerAwareInterface ...@@ -259,44 +248,17 @@ class FileHandlingUtility implements SingletonInterface, LoggerAwareInterface
/** /**
* Constructs emConf and writes it to corresponding file * Constructs emConf and writes it to corresponding file
* In case the file has been extracted already, the properties of the meta data take precedence but are merged with the present ext_emconf.php
* *
* @param array $extensionData * @param string $extensionKey
* @param array $emConfData
* @param string $rootPath * @param string $rootPath
* @param Extension|null $extension
*/ */
protected function writeEmConfToFile(array $extensionData, $rootPath, Extension $extension = null) protected function writeEmConfToFile(string $extensionKey, array $emConfData, string $rootPath)
{ {
$emConfFileData = []; $emConfContent = $this->emConfUtility->constructEmConf($extensionKey, $emConfData);
if (file_exists($rootPath . 'ext_emconf.php')) {
$emConfFileData = $this->emConfUtility->includeEmConf(
$extensionData['extKey'],
$rootPath
);
$emConfFileData = is_array($emConfFileData) ? $emConfFileData : [];
}
$extensionData['EM_CONF'] = array_replace_recursive($emConfFileData, $extensionData['EM_CONF']);
$emConfContent = $this->emConfUtility->constructEmConf($extensionData, $extension);
GeneralUtility::writeFile($rootPath . 'ext_emconf.php', $emConfContent); GeneralUtility::writeFile($rootPath . 'ext_emconf.php', $emConfContent);
} }
/**
* Is the given path a valid path for extension installation
*
* @param string $path the absolute (!) path in question
* @return bool
*/
public function isValidExtensionPath($path)
{
$allowedPaths = Extension::returnAllowedInstallPaths();
foreach ($allowedPaths as $allowedPath) {
if (GeneralUtility::isFirstPartOfStr($path, $allowedPath)) {
return true;
}
}
return false;
}
/** /**
* Returns relative path * Returns relative path
* *
......
...@@ -37,6 +37,7 @@ use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; ...@@ -37,6 +37,7 @@ use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\PathUtility; use TYPO3\CMS\Core\Utility\PathUtility;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility; use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
use TYPO3\CMS\Extensionmanager\Domain\Model\Extension;
use TYPO3\CMS\Extensionmanager\Event\AfterExtensionDatabaseContentHasBeenImportedEvent; use TYPO3\CMS\Extensionmanager\Event\AfterExtensionDatabaseContentHasBeenImportedEvent;
use TYPO3\CMS\Extensionmanager\Event\AfterExtensionFilesHaveBeenImportedEvent; use TYPO3\CMS\Extensionmanager\Event\AfterExtensionFilesHaveBeenImportedEvent;
use TYPO3\CMS\Extensionmanager\Event\AfterExtensionStaticDatabaseContentHasBeenImportedEvent; use TYPO3\CMS\Extensionmanager\Event\AfterExtensionStaticDatabaseContentHasBeenImportedEvent;
...@@ -422,7 +423,7 @@ class InstallUtility implements SingletonInterface, LoggerAwareInterface ...@@ -422,7 +423,7 @@ class InstallUtility implements SingletonInterface, LoggerAwareInterface
public function removeExtension($extension) public function removeExtension($extension)
{ {
$absolutePath = $this->enrichExtensionWithDetails($extension)['packagePath']; $absolutePath = $this->enrichExtensionWithDetails($extension)['packagePath'];
if ($this->fileHandlingUtility->isValidExtensionPath($absolutePath)) { if ($this->isValidExtensionPath($absolutePath)) {
if ($this->packageManager->isPackageAvailable($extension)) { if ($this->packageManager->isPackageAvailable($extension)) {
// Package manager deletes the extension and removes the entry from PackageStates.php // Package manager deletes the extension and removes the entry from PackageStates.php
$this->packageManager->deletePackage($extension); $this->packageManager->deletePackage($extension);
...@@ -593,4 +594,21 @@ class InstallUtility implements SingletonInterface, LoggerAwareInterface ...@@ -593,4 +594,21 @@ class InstallUtility implements SingletonInterface, LoggerAwareInterface
$siteConfiguration->write($newSite->getIdentifier(), $configuration); $siteConfiguration->write($newSite->getIdentifier(), $configuration);
} }
} }
/**
* Is the given path a valid path for extension installation
*
* @param string $path the absolute (!) path in question
* @return bool
*/
protected function isValidExtensionPath($path): bool
{
$allowedPaths = Extension::returnAllowedInstallPaths();
foreach ($allowedPaths as $allowedPath) {
if (GeneralUtility::isFirstPartOfStr($path, $allowedPath)) {
return true;
}
}
return false;
}
} }
...@@ -29,12 +29,8 @@ class EmConfUtilityTest extends UnitTestCase ...@@ -29,12 +29,8 @@ class EmConfUtilityTest extends UnitTestCase
*/ */
public function constructEmConfAddsCommentBlock() public function constructEmConfAddsCommentBlock()
{ {
$extensionData = [
'extKey' => 'key',
'EM_CONF' => [],
];
$subject = new EmConfUtility(); $subject = new EmConfUtility();
$emConf = $subject->constructEmConf($extensionData); $emConf = $subject->constructEmConf('key', []);
self::assertStringContainsString('Extension Manager/Repository config file for ext', $emConf); self::assertStringContainsString('Extension Manager/Repository config file for ext', $emConf);
} }
...@@ -59,7 +55,7 @@ class EmConfUtilityTest extends UnitTestCase ...@@ -59,7 +55,7 @@ class EmConfUtilityTest extends UnitTestCase
]; ];
$subject = new EmConfUtility(); $subject = new EmConfUtility();
$_EXTKEY = 'seminars'; $_EXTKEY = 'seminars';
$result = $subject->constructEmConf(['EM_CONF' => $input, 'extKey' => $_EXTKEY]); $result = $subject->constructEmConf($_EXTKEY, $input);
eval(substr($result, 7)); eval(substr($result, 7));
$result = $EM_CONF[$_EXTKEY]; $result = $EM_CONF[$_EXTKEY];
self::assertEquals($expected, $result); self::assertEquals($expected, $result);
...@@ -88,7 +84,7 @@ class EmConfUtilityTest extends UnitTestCase ...@@ -88,7 +84,7 @@ class EmConfUtilityTest extends UnitTestCase
$subject = new EmConfUtility(); $subject = new EmConfUtility();
$_EXTKEY = 'seminars'; $_EXTKEY = 'seminars';
$result = $subject->constructEmConf(['EM_CONF' => $input, 'extKey' => $_EXTKEY]); $result = $subject->constructEmConf($_EXTKEY, $input);
eval(substr($result, 7)); eval(substr($result, 7));
$result = $EM_CONF[$_EXTKEY]; $result = $EM_CONF[$_EXTKEY];
self::assertEquals($expected, $result); self::assertEquals($expected, $result);
......
...@@ -99,7 +99,7 @@ class FileHandlingUtilityTest extends UnitTestCase ...@@ -99,7 +99,7 @@ class FileHandlingUtilityTest extends UnitTestCase
$this->expectExceptionCode(1337280417); $this->expectExceptionCode(1337280417);
$fileHandlerMock = $this->getAccessibleMock(FileHandlingUtility::class, ['removeDirectory', 'addDirectory']); $fileHandlerMock = $this->getAccessibleMock(FileHandlingUtility::class, ['removeDirectory', 'addDirectory']);
$languageServiceMock = $this->getMockBuilder(LanguageService::class)->disableOriginalConstructor()->getMock(); $languageServiceMock = $this->getMockBuilder(LanguageService::class)->disableOriginalConstructor()->getMock();
$fileHandlerMock->_set('languageService', $languageServiceMock); $fileHandlerMock->injectLanguageService($languageServiceMock);
$fileHandlerMock->_call('makeAndClearExtensionDir', 'testing123', 'fakepath'); $fileHandlerMock->_call('makeAndClearExtensionDir', 'testing123', 'fakepath');
} }
...@@ -169,22 +169,18 @@ class FileHandlingUtilityTest extends UnitTestCase ...@@ -169,22 +169,18 @@ class FileHandlingUtilityTest extends UnitTestCase
*/ */
public function unpackExtensionFromExtensionDataArrayCreatesTheExtensionDirectory() public function unpackExtensionFromExtensionDataArrayCreatesTheExtensionDirectory()
{ {
$extensionData = [ $extensionKey = 'test';
'extKey' => 'test'
];
$fileHandlerMock = $this->getAccessibleMock(FileHandlingUtility::class, [ $fileHandlerMock = $this->getAccessibleMock(FileHandlingUtility::class, [
'makeAndClearExtensionDir', 'makeAndClearExtensionDir',
'writeEmConfToFile', 'writeEmConfToFile',
'extractFilesArrayFromExtensionData',
'extractDirectoriesFromExtensionData', 'extractDirectoriesFromExtensionData',
'createDirectoriesForExtensionFiles', 'createDirectoriesForExtensionFiles',
'writeExtensionFiles', 'writeExtensionFiles',
'reloadPackageInformation', 'reloadPackageInformation',
]); ]);
$fileHandlerMock->expects(self::once())->method('extractFilesArrayFromExtensionData')->willReturn([]);
$fileHandlerMock->expects(self::once())->method('extractDirectoriesFromExtensionData')->willReturn([]); $fileHandlerMock->expects(self::once())->method('extractDirectoriesFromExtensionData')->willReturn([]);
$fileHandlerMock->expects(self::once())->method('makeAndClearExtensionDir')->with($extensionData['extKey']); $fileHandlerMock->expects(self::once())->method('makeAndClearExtensionDir')->with($extensionKey)->willReturn('my_path');
$fileHandlerMock->_call('unpackExtensionFromExtensionDataArray', $extensionData); $fileHandlerMock->unpackExtensionFromExtensionDataArray($extensionKey, []);
} }
/** /**
...@@ -193,9 +189,8 @@ class FileHandlingUtilityTest extends UnitTestCase ...@@ -193,9 +189,8 @@ class FileHandlingUtilityTest extends UnitTestCase
public function unpackExtensionFromExtensionDataArrayStripsDirectoriesFromFilesArray() public function unpackExtensionFromExtensionDataArrayStripsDirectoriesFromFilesArray()
{ {
$extensionData = [ $extensionData = [
'extKey' => 'test' 'extKey' => 'test',
]; 'FILES' => [
$files = [
'ChangeLog' => [ 'ChangeLog' => [
'name' => 'ChangeLog', 'name' => 'ChangeLog',
'size' => 4559, 'size' => 4559,
...@@ -217,6 +212,7 @@ class FileHandlingUtilityTest extends UnitTestCase ...@@ -217,6 +212,7 @@ class FileHandlingUtilityTest extends UnitTestCase
'is_executable' => false, 'is_executable' => false,
'content' => 'some content to write' 'content' => 'some content to write'
], ],
]
]; ];
$cleanedFiles = [ $cleanedFiles = [
'ChangeLog' => [ 'ChangeLog' => [
...@@ -242,36 +238,17 @@ class FileHandlingUtilityTest extends UnitTestCase ...@@ -242,36 +238,17 @@ class FileHandlingUtilityTest extends UnitTestCase
$fileHandlerMock = $this->getAccessibleMock(FileHandlingUtility::class, [ $fileHandlerMock = $this->getAccessibleMock(FileHandlingUtility::class, [
'makeAndClearExtensionDir', 'makeAndClearExtensionDir',
'writeEmConfToFile', 'writeEmConfToFile',
'extractFilesArrayFromExtensionData',
'extractDirectoriesFromExtensionData', 'extractDirectoriesFromExtensionData',
'createDirectoriesForExtensionFiles', 'createDirectoriesForExtensionFiles',
'writeExtensionFiles', 'writeExtensionFiles',
'reloadPackageInformation', 'reloadPackageInformation',
]); ]);
$fileHandlerMock->expects(self::once())->method('extractFilesArrayFromExtensionData')->willReturn($files);
$fileHandlerMock->expects(self::once())->method('extractDirectoriesFromExtensionData')->willReturn($directories); $fileHandlerMock->expects(self::once())->method('extractDirectoriesFromExtensionData')->willReturn($directories);
$fileHandlerMock->expects(self::once())->method('createDirectoriesForExtensionFiles')->with($directories); $fileHandlerMock->expects(self::once())->method('createDirectoriesForExtensionFiles')->with($directories);
$fileHandlerMock->expects(self::once())->method('makeAndClearExtensionDir')->with($extensionData['extKey'])->willReturn('my_path');
$fileHandlerMock->expects(self::once())->method('writeExtensionFiles')->with($cleanedFiles); $fileHandlerMock->expects(self::once())->method('writeExtensionFiles')->with($cleanedFiles);
$fileHandlerMock->expects(self::once())->method('reloadPackageInformation')->with('test'); $fileHandlerMock->expects(self::once())->method('reloadPackageInformation')->with('test');
$fileHandlerMock->_call('unpackExtensionFromExtensionDataArray', $extensionData); $fileHandlerMock->unpackExtensionFromExtensionDataArray('test', $extensionData);
}
/**
* @test
*/
public function extractFilesArrayFromExtensionDataReturnsFileArray()
{
$extensionData = [
'key' => 'test',
'FILES' => [
'filename1' => 'dummycontent',
'filename2' => 'dummycontent2'
]
];
$fileHandlerMock = $this->getAccessibleMock(FileHandlingUtility::class, ['makeAndClearExtensionDir']);
$extractedFiles = $fileHandlerMock->_call('extractFilesArrayFromExtensionData', $extensionData);
self::assertArrayHasKey('filename1', $extractedFiles);
self::assertArrayHasKey('filename2', $extractedFiles);
} }
/** /**
...@@ -376,20 +353,16 @@ class FileHandlingUtilityTest extends UnitTestCase ...@@ -376,20 +353,16 @@ class FileHandlingUtilityTest extends UnitTestCase
public function writeEmConfWritesEmConfFile() public function writeEmConfWritesEmConfFile()
{ {
$extKey = $this->createFakeExtension(); $extKey = $this->createFakeExtension();
$extensionData = [ $emConfData = [
'extKey' => $extKey,
'EM_CONF' => [
'title' => 'Plugin cache engine', 'title' => 'Plugin cache engine',
'description' => 'Provides an interface to cache plugin content elements based on 4.3 caching framework', 'description' => 'Provides an interface to cache plugin content elements based on 4.3 caching framework',
'category' => 'Frontend', 'category' => 'Frontend',
]
]; ];
$rootPath = $this->fakedExtensions[$extKey]['packagePath']; $rootPath = $this->fakedExtensions[$extKey]['packagePath'];
$emConfUtilityMock = $this->getAccessibleMock(EmConfUtility::class, ['constructEmConf']); /** @var FileHandlingUtility $fileHandlerMock */
$emConfUtilityMock->expects(self::once())->method('constructEmConf')->with($extensionData)->willReturn(var_export($extensionData['EM_CONF'], true));
$fileHandlerMock = $this->getAccessibleMock(FileHandlingUtility::class, ['makeAndClearExtensionDir']); $fileHandlerMock = $this->getAccessibleMock(FileHandlingUtility::class, ['makeAndClearExtensionDir']);
$fileHandlerMock->_set('emConfUtility', $emConfUtilityMock); $fileHandlerMock->injectEmConfUtility(new EmConfUtility());
$fileHandlerMock->_call('writeEmConfToFile', $extensionData, $rootPath); $fileHandlerMock->_call('writeEmConfToFile', $extKey, $emConfData, $rootPath);
self::assertTrue(file_exists($rootPath . 'ext_emconf.php')); self::assertTrue(file_exists($rootPath . 'ext_emconf.php'));
} }
} }
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment