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
}
$extensionData = $this->decodeExchangeData($downloadedContent);
if (!empty($extensionData['extKey']) && is_string($extensionData['extKey'])) {
$fileHandler->unpackExtensionFromExtensionDataArray($extensionData, null, $pathType);
$fileHandler->unpackExtensionFromExtensionDataArray($extensionData['extKey'], $extensionData, $pathType);
} else {
throw new VerificationFailedException('Downloaded t3x file could not be extracted', 1334426698);
}
......
......@@ -16,7 +16,6 @@
namespace TYPO3\CMS\Extensionmanager\Utility;
use TYPO3\CMS\Core\SingletonInterface;
use TYPO3\CMS\Extensionmanager\Domain\Model\Extension;
/**
* Utility for dealing with ext_emconf
......@@ -47,24 +46,19 @@ class EmConfUtility implements SingletonInterface
/**
* Generates the content for the ext_emconf.php file
* Sets dependencies from TER data if any
*
* @param array $extensionData
* @param Extension $extension Extension object from TER data
* @param string $extensionKey
* @param array $emConf
* @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'])) {
$extensionData['EM_CONF']['constraints'] = unserialize($extension->getSerializedDependencies(), ['allowed_classes' => false]);
}
$emConf = $this->fixEmConf($extensionData['EM_CONF']);
$emConf = $this->fixEmConf($emConf);
$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') . '
*
......@@ -76,7 +70,6 @@ class EmConfUtility implements SingletonInterface
$EM_CONF[$_EXTKEY] = ' . $emConf . ';
';
return $code;
}
/**
......
......@@ -85,20 +85,21 @@ class FileHandlingUtility implements SingletonInterface, LoggerAwareInterface
/**
* Unpack an extension in t3x data format and write files
*
* @param string $extensionKey
* @param array $extensionData
* @param Extension|null $extension
* @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 = $this->extractFilesArrayFromExtensionData($extensionData);
$files = $extensionData['FILES'] ?? [];
$emConfData = $extensionData['EM_CONF'] ?? [];
$extensionDir = $this->makeAndClearExtensionDir($extensionKey, $pathType);
$directories = $this->extractDirectoriesFromExtensionData($files);
$files = array_diff_key($files, array_flip($directories));
$this->createDirectoriesForExtensionFiles($directories, $extensionDir);
$this->writeExtensionFiles($files, $extensionDir);
$this->writeEmConfToFile($extensionData, $extensionDir, $extension);
$this->reloadPackageInformation($extensionData['extKey']);
$this->writeEmConfToFile($extensionKey, $emConfData, $extensionDir);
$this->reloadPackageInformation($extensionKey);
}
/**
......@@ -119,17 +120,6 @@ class FileHandlingUtility implements SingletonInterface, LoggerAwareInterface
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
* It also creates nested directory structures
......@@ -137,7 +127,7 @@ class FileHandlingUtility implements SingletonInterface, LoggerAwareInterface
* @param array $directories
* @param string $rootPath
*/
protected function createDirectoriesForExtensionFiles(array $directories, $rootPath)
protected function createDirectoriesForExtensionFiles(array $directories, string $rootPath)
{
foreach ($directories as $directory) {
$this->createNestedDirectory($rootPath . $directory);
......@@ -191,7 +181,6 @@ class FileHandlingUtility implements SingletonInterface, LoggerAwareInterface
$this->removeDirectory($extDirPath);
}
$this->addDirectory($extDirPath);
return $extDirPath;
}
......@@ -259,44 +248,17 @@ class FileHandlingUtility implements SingletonInterface, LoggerAwareInterface
/**
* 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 Extension|null $extension
*/
protected function writeEmConfToFile(array $extensionData, $rootPath, Extension $extension = null)
protected function writeEmConfToFile(string $extensionKey, array $emConfData, string $rootPath)
{
$emConfFileData = [];
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);
$emConfContent = $this->emConfUtility->constructEmConf($extensionKey, $emConfData);
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
*
......
......@@ -37,6 +37,7 @@ use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\PathUtility;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
use TYPO3\CMS\Extensionmanager\Domain\Model\Extension;
use TYPO3\CMS\Extensionmanager\Event\AfterExtensionDatabaseContentHasBeenImportedEvent;
use TYPO3\CMS\Extensionmanager\Event\AfterExtensionFilesHaveBeenImportedEvent;
use TYPO3\CMS\Extensionmanager\Event\AfterExtensionStaticDatabaseContentHasBeenImportedEvent;
......@@ -422,7 +423,7 @@ class InstallUtility implements SingletonInterface, LoggerAwareInterface
public function removeExtension($extension)
{
$absolutePath = $this->enrichExtensionWithDetails($extension)['packagePath'];
if ($this->fileHandlingUtility->isValidExtensionPath($absolutePath)) {
if ($this->isValidExtensionPath($absolutePath)) {
if ($this->packageManager->isPackageAvailable($extension)) {
// Package manager deletes the extension and removes the entry from PackageStates.php
$this->packageManager->deletePackage($extension);
......@@ -593,4 +594,21 @@ class InstallUtility implements SingletonInterface, LoggerAwareInterface
$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
*/
public function constructEmConfAddsCommentBlock()
{
$extensionData = [
'extKey' => 'key',
'EM_CONF' => [],
];
$subject = new EmConfUtility();
$emConf = $subject->constructEmConf($extensionData);
$emConf = $subject->constructEmConf('key', []);
self::assertStringContainsString('Extension Manager/Repository config file for ext', $emConf);
}
......@@ -59,7 +55,7 @@ class EmConfUtilityTest extends UnitTestCase
];
$subject = new EmConfUtility();
$_EXTKEY = 'seminars';
$result = $subject->constructEmConf(['EM_CONF' => $input, 'extKey' => $_EXTKEY]);
$result = $subject->constructEmConf($_EXTKEY, $input);
eval(substr($result, 7));
$result = $EM_CONF[$_EXTKEY];
self::assertEquals($expected, $result);
......@@ -88,7 +84,7 @@ class EmConfUtilityTest extends UnitTestCase
$subject = new EmConfUtility();
$_EXTKEY = 'seminars';
$result = $subject->constructEmConf(['EM_CONF' => $input, 'extKey' => $_EXTKEY]);
$result = $subject->constructEmConf($_EXTKEY, $input);
eval(substr($result, 7));
$result = $EM_CONF[$_EXTKEY];
self::assertEquals($expected, $result);
......
......@@ -99,7 +99,7 @@ class FileHandlingUtilityTest extends UnitTestCase
$this->expectExceptionCode(1337280417);
$fileHandlerMock = $this->getAccessibleMock(FileHandlingUtility::class, ['removeDirectory', 'addDirectory']);
$languageServiceMock = $this->getMockBuilder(LanguageService::class)->disableOriginalConstructor()->getMock();
$fileHandlerMock->_set('languageService', $languageServiceMock);
$fileHandlerMock->injectLanguageService($languageServiceMock);
$fileHandlerMock->_call('makeAndClearExtensionDir', 'testing123', 'fakepath');
}
......@@ -169,22 +169,18 @@ class FileHandlingUtilityTest extends UnitTestCase
*/
public function unpackExtensionFromExtensionDataArrayCreatesTheExtensionDirectory()
{
$extensionData = [
'extKey' => 'test'
];
$extensionKey = 'test';
$fileHandlerMock = $this->getAccessibleMock(FileHandlingUtility::class, [
'makeAndClearExtensionDir',
'writeEmConfToFile',
'extractFilesArrayFromExtensionData',
'extractDirectoriesFromExtensionData',
'createDirectoriesForExtensionFiles',
'writeExtensionFiles',
'reloadPackageInformation',
]);
$fileHandlerMock->expects(self::once())->method('extractFilesArrayFromExtensionData')->willReturn([]);
$fileHandlerMock->expects(self::once())->method('extractDirectoriesFromExtensionData')->willReturn([]);
$fileHandlerMock->expects(self::once())->method('makeAndClearExtensionDir')->with($extensionData['extKey']);
$fileHandlerMock->_call('unpackExtensionFromExtensionDataArray', $extensionData);
$fileHandlerMock->expects(self::once())->method('makeAndClearExtensionDir')->with($extensionKey)->willReturn('my_path');
$fileHandlerMock->unpackExtensionFromExtensionDataArray($extensionKey, []);
}
/**
......@@ -193,30 +189,30 @@ class FileHandlingUtilityTest extends UnitTestCase
public function unpackExtensionFromExtensionDataArrayStripsDirectoriesFromFilesArray()
{
$extensionData = [
'extKey' => 'test'
];
$files = [
'ChangeLog' => [
'name' => 'ChangeLog',
'size' => 4559,
'mtime' => 1219448527,
'is_executable' => false,
'content' => 'some content to write'
],
'doc/' => [
'name' => 'doc/',
'size' => 0,
'mtime' => 1219448527,
'is_executable' => false,
'content' => ''
],
'doc/ChangeLog' => [
'name' => 'ChangeLog',
'size' => 4559,
'mtime' => 1219448527,
'is_executable' => false,
'content' => 'some content to write'
],
'extKey' => 'test',
'FILES' => [
'ChangeLog' => [
'name' => 'ChangeLog',
'size' => 4559,
'mtime' => 1219448527,
'is_executable' => false,
'content' => 'some content to write'
],
'doc/' => [
'name' => 'doc/',
'size' => 0,
'mtime' => 1219448527,
'is_executable' => false,
'content' => ''
],
'doc/ChangeLog' => [
'name' => 'ChangeLog',
'size' => 4559,
'mtime' => 1219448527,
'is_executable' => false,
'content' => 'some content to write'
],
]
];
$cleanedFiles = [
'ChangeLog' => [
......@@ -242,36 +238,17 @@ class FileHandlingUtilityTest extends UnitTestCase
$fileHandlerMock = $this->getAccessibleMock(FileHandlingUtility::class, [
'makeAndClearExtensionDir',
'writeEmConfToFile',
'extractFilesArrayFromExtensionData',
'extractDirectoriesFromExtensionData',
'createDirectoriesForExtensionFiles',
'writeExtensionFiles',
'reloadPackageInformation',
]);
$fileHandlerMock->expects(self::once())->method('extractFilesArrayFromExtensionData')->willReturn($files);
$fileHandlerMock->expects(self::once())->method('extractDirectoriesFromExtensionData')->willReturn($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('reloadPackageInformation')->with('test');
$fileHandlerMock->_call('unpackExtensionFromExtensionDataArray', $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);
$fileHandlerMock->unpackExtensionFromExtensionDataArray('test', $extensionData);
}
/**
......@@ -376,20 +353,16 @@ class FileHandlingUtilityTest extends UnitTestCase
public function writeEmConfWritesEmConfFile()
{
$extKey = $this->createFakeExtension();
$extensionData = [
'extKey' => $extKey,
'EM_CONF' => [
'title' => 'Plugin cache engine',
'description' => 'Provides an interface to cache plugin content elements based on 4.3 caching framework',
'category' => 'Frontend',
]
$emConfData = [
'title' => 'Plugin cache engine',
'description' => 'Provides an interface to cache plugin content elements based on 4.3 caching framework',
'category' => 'Frontend',
];
$rootPath = $this->fakedExtensions[$extKey]['packagePath'];
$emConfUtilityMock = $this->getAccessibleMock(EmConfUtility::class, ['constructEmConf']);
$emConfUtilityMock->expects(self::once())->method('constructEmConf')->with($extensionData)->willReturn(var_export($extensionData['EM_CONF'], true));
/** @var FileHandlingUtility $fileHandlerMock */
$fileHandlerMock = $this->getAccessibleMock(FileHandlingUtility::class, ['makeAndClearExtensionDir']);
$fileHandlerMock->_set('emConfUtility', $emConfUtilityMock);
$fileHandlerMock->_call('writeEmConfToFile', $extensionData, $rootPath);
$fileHandlerMock->injectEmConfUtility(new EmConfUtility());
$fileHandlerMock->_call('writeEmConfToFile', $extKey, $emConfData, $rootPath);
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