From f654ab772fa572f761bcfd1af573848f0e2b9478 Mon Sep 17 00:00:00 2001 From: Helmut Hummel <helmut.hummel@typo3.org> Date: Wed, 30 Sep 2015 19:56:47 +0200 Subject: [PATCH] [TASK] Refactor class information generator to be testable Add a simple test as benefit. Resolves: #70233 Releases: master Change-Id: I92d093261d6c5909dbe91cf3661ae8cfa852216d Reviewed-on: http://review.typo3.org/43668 Reviewed-by: Wouter Wolters <typo3@wouterwolters.nl> Tested-by: Wouter Wolters <typo3@wouterwolters.nl> Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch> Tested-by: Christian Kuhn <lolli@schwarzbu.ch> --- .../Classes/Core/ClassLoadingInformation.php | 34 ++++++-- .../Core/ClassLoadingInformationGenerator.php | 84 +++++++------------ .../ClassLoadingInformationGeneratorTest.php | 37 +++++++- .../test_extension/Resources/PHP/Test.php | 19 +++++ .../Fixtures/test_extension/composer.json | 20 +++++ .../Fixtures/test_extension/ext_emconf.php | 19 +++++ 6 files changed, 151 insertions(+), 62 deletions(-) create mode 100644 typo3/sysext/core/Tests/Unit/Core/Fixtures/test_extension/Resources/PHP/Test.php create mode 100644 typo3/sysext/core/Tests/Unit/Core/Fixtures/test_extension/composer.json create mode 100644 typo3/sysext/core/Tests/Unit/Core/Fixtures/test_extension/ext_emconf.php diff --git a/typo3/sysext/core/Classes/Core/ClassLoadingInformation.php b/typo3/sysext/core/Classes/Core/ClassLoadingInformation.php index e3704e3d4698..1525c3603333 100644 --- a/typo3/sysext/core/Classes/Core/ClassLoadingInformation.php +++ b/typo3/sysext/core/Classes/Core/ClassLoadingInformation.php @@ -14,9 +14,10 @@ namespace TYPO3\CMS\Core\Core; * The TYPO3 project - inspiring people to share! */ -use Composer\Autoload\ClassLoader as ComposerClassLoader; +use Composer\Autoload\ClassLoader; use Helhum\ClassAliasLoader\ClassAliasMap; use TYPO3\CMS\Core\Package\PackageInterface; +use TYPO3\CMS\Core\Package\PackageManager; use TYPO3\CMS\Core\Utility\GeneralUtility; /** @@ -69,8 +70,11 @@ class ClassLoadingInformation { */ static public function dumpClassLoadingInformation() { self::ensureAutoloadInfoDirExists(); + $composerClassLoader = static::getClassLoader(); + $activeExtensionPackages = static::getActiveExtensionPackages(); + /** @var ClassLoadingInformationGenerator $generator */ - $generator = GeneralUtility::makeInstance(ClassLoadingInformationGenerator::class); + $generator = GeneralUtility::makeInstance(ClassLoadingInformationGenerator::class, $composerClassLoader, $activeExtensionPackages, PATH_site); $classInfoFiles = $generator->buildAutoloadInformationFiles(); GeneralUtility::writeFile(self::getClassLoadingInformationDirectory() . self::AUTOLOAD_CLASSMAP_FILENAME, $classInfoFiles['classMapFile']); GeneralUtility::writeFile(self::getClassLoadingInformationDirectory() . self::AUTOLOAD_PSR4_FILENAME, $classInfoFiles['psr-4File']); @@ -121,9 +125,10 @@ class ClassLoadingInformation { */ static public function registerTransientClassLoadingInformationForPackage(PackageInterface $package) { $composerClassLoader = static::getClassLoader(); + $activeExtensionPackages = static::getActiveExtensionPackages(); /** @var ClassLoadingInformationGenerator $generator */ - $generator = GeneralUtility::makeInstance(ClassLoadingInformationGenerator::class); + $generator = GeneralUtility::makeInstance(ClassLoadingInformationGenerator::class, $composerClassLoader, $activeExtensionPackages, PATH_site); $classInformation = $generator->buildClassLoadingInformationForPackage($package); $composerClassLoader->addClassMap($classInformation['classMap']); @@ -173,11 +178,30 @@ class ClassLoadingInformation { /** * Internal method calling the bootstrap to fetch the composer class loader * - * @return ComposerClassLoader + * @return ClassLoader * @throws \TYPO3\CMS\Core\Exception */ static protected function getClassLoader() { - return Bootstrap::getInstance()->getEarlyInstance(ComposerClassLoader::class); + return Bootstrap::getInstance()->getEarlyInstance(ClassLoader::class); + } + + /** + * Get all packages except the protected ones, as they are covered already + * + * @return PackageInterface[] + */ + static protected function getActiveExtensionPackages() { + $activeExtensionPackages = []; + /** @var PackageManager $packageManager */ + $packageManager = Bootstrap::getInstance()->getEarlyInstance(PackageManager::class); + foreach ($packageManager->getActivePackages() as $package) { + if ($package->getValueFromComposerManifest('type') === 'typo3-cms-framework') { + // Skip all core packages as the class loading info is prepared for them already + continue; + } + $activeExtensionPackages[] = $package; + } + return $activeExtensionPackages; } } diff --git a/typo3/sysext/core/Classes/Core/ClassLoadingInformationGenerator.php b/typo3/sysext/core/Classes/Core/ClassLoadingInformationGenerator.php index 69200914b7c5..05e5116b743e 100644 --- a/typo3/sysext/core/Classes/Core/ClassLoadingInformationGenerator.php +++ b/typo3/sysext/core/Classes/Core/ClassLoadingInformationGenerator.php @@ -15,15 +15,14 @@ namespace TYPO3\CMS\Core\Core; */ use Composer\Autoload\ClassMapGenerator; -use Composer\Autoload\ClassLoader as ComposerClassLoader; +use Composer\Autoload\ClassLoader; use TYPO3\CMS\Core\Package\PackageInterface; -use TYPO3\CMS\Core\Package\PackageManager; use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Core\Utility\PathUtility; /** * Generates class loading information (class maps, class aliases etc.) and writes it to files * for further inclusion in the bootstrap + * @internal */ class ClassLoadingInformationGenerator { @@ -32,6 +31,27 @@ class ClassLoadingInformationGenerator { */ protected $activeExtensionPackages; + /** + * @var ClassLoader + */ + protected $classLoader; + + /** + * @var string + */ + protected $installationRoot; + + /** + * @param ClassLoader $classLoader + * @param array $activeExtensionPackages + * @param string $installationRoot + */ + public function __construct(ClassLoader $classLoader, array $activeExtensionPackages = array(), $installationRoot) { + $this->classLoader = $classLoader; + $this->activeExtensionPackages = $activeExtensionPackages; + $this->installationRoot = $installationRoot; + } + /** * Returns class loading information for a single package * @@ -43,8 +63,7 @@ class ClassLoadingInformationGenerator { $classMap = array(); $psr4 = array(); $packagePath = $package->getPackagePath(); - - $manifest = $this->getPackageManager()->getComposerManifest($packagePath); + $manifest = $package->getValueFromComposerManifest(); if (empty($manifest->autoload)) { // Legacy mode: Scan the complete extension directory for class files @@ -52,7 +71,7 @@ class ClassLoadingInformationGenerator { } else { $autoloadDefinition = json_decode(json_encode($manifest->autoload), TRUE); if (!empty($autoloadDefinition['psr-4']) && is_array($autoloadDefinition['psr-4'])) { - $classLoaderPrefixesPsr4 = $this->getClassLoader()->getPrefixesPsr4(); + $classLoaderPrefixesPsr4 = $this->classLoader->getPrefixesPsr4(); foreach ($autoloadDefinition['psr-4'] as $namespacePrefix => $path) { $namespacePath = $packagePath . $path; if ($useRelativePaths) { @@ -182,7 +201,7 @@ return array( EOF; $classMap = array(); $psr4 = array(); - foreach ($this->getActiveExtensionPackages() as $package) { + foreach ($this->activeExtensionPackages as $package) { $classLoadingInformation = $this->buildClassLoadingInformationForPackage($package, TRUE); $classMap = array_merge($classMap, $classLoadingInformation['classMap']); $psr4 = array_merge($psr4, $classLoadingInformation['psr-4']); @@ -214,7 +233,7 @@ EOF; protected function makePathRelative($packagePath, $realPathOfClassFile, $relativeToRoot = TRUE) { $realPathOfClassFile = GeneralUtility::fixWindowsFilePath($realPathOfClassFile); $packageRealPath = GeneralUtility::fixWindowsFilePath(realpath($packagePath)); - $relativePackagePath = rtrim(PathUtility::stripPathSitePrefix($packagePath), '/'); + $relativePackagePath = rtrim(substr($packagePath, strlen($this->installationRoot)), '/'); if ($relativeToRoot) { $relativePathToClassFile = $relativePackagePath . '/' . ltrim(substr($realPathOfClassFile, strlen($packageRealPath)), '/'); } else { @@ -244,7 +263,7 @@ EOF; public function buildClassAliasMapFile() { $aliasToClassNameMapping = array(); $classNameToAliasMapping = array(); - foreach ($this->getActiveExtensionPackages() as $package) { + foreach ($this->activeExtensionPackages as $package) { $aliasMappingForPackage = $this->buildClassAliasMapForPackage($package); $aliasToClassNameMapping = array_merge($aliasToClassNameMapping, $aliasMappingForPackage['aliasToClassNameMapping']); $classNameToAliasMapping = array_merge($classNameToAliasMapping, $aliasMappingForPackage['classNameToAliasMapping']); @@ -259,51 +278,4 @@ EOF; return $fileContent; } - /** - * Get all packages except the protected ones, as they are covered already - * - * @return PackageInterface[] - */ - protected function getActiveExtensionPackages() { - if ($this->activeExtensionPackages === NULL) { - $this->activeExtensionPackages = array(); - foreach ($this->getPackageManager()->getActivePackages() as $package) { - if ($this->isFrameworkPackage($package)) { - // Skip all core packages as the class loading info is prepared for them already - continue; - } - $this->activeExtensionPackages[] = $package; - } - } - - return $this->activeExtensionPackages; - } - - /** - * Check if the package is a framework package (located in typo3/sysext) - * - * @param PackageInterface $package - * @return bool - */ - protected function isFrameworkPackage(PackageInterface $package) { - return $package->getValueFromComposerManifest('type') === 'typo3-cms-framework'; - } - - /** - * @return PackageManager - * @throws \TYPO3\CMS\Core\Exception - */ - protected function getPackageManager() { - return Bootstrap::getInstance()->getEarlyInstance(PackageManager::class); - } - - /** - * Internal method calling the bootstrap to fetch the composer class loader - * - * @return ComposerClassLoader - * @throws \TYPO3\CMS\Core\Exception - */ - protected function getClassLoader() { - return Bootstrap::getInstance()->getEarlyInstance(ComposerClassLoader::class); - } } diff --git a/typo3/sysext/core/Tests/Unit/Core/ClassLoadingInformationGeneratorTest.php b/typo3/sysext/core/Tests/Unit/Core/ClassLoadingInformationGeneratorTest.php index cb0536a2209b..7015471c4eee 100644 --- a/typo3/sysext/core/Tests/Unit/Core/ClassLoadingInformationGeneratorTest.php +++ b/typo3/sysext/core/Tests/Unit/Core/ClassLoadingInformationGeneratorTest.php @@ -10,7 +10,10 @@ namespace TYPO3\CMS\Core\Tests\Unit\Core; * * * The TYPO3 project - inspiring people to share! * * */ + +use Composer\Autoload\ClassLoader; use TYPO3\CMS\Core\Core\ClassLoadingInformationGenerator; +use TYPO3\CMS\Core\Package\PackageInterface; use TYPO3\CMS\Core\Tests\UnitTestCase; @@ -48,9 +51,41 @@ class ClassLoadingInformationGeneratorTest extends UnitTestCase { * @param bool $expectedResult */ public function isIgnoredClassNameIgnoresTestClasses($className, $expectedResult) { - $generator = $this->getAccessibleMock(ClassLoadingInformationGenerator::class, ['dummy']); + $generator = $this->getAccessibleMock( + ClassLoadingInformationGenerator::class, + ['dummy'], + [$this->getMock(ClassLoader::class), $this->createPackagesMock(), __DIR__] + ); $this->assertEquals($expectedResult, $generator->_call('isIgnoredClassName', $className)); } + /** + * @test + */ + public function autoloadFilesAreBuildCorrectly() { + /** @var ClassLoader|\PHPUnit_Framework_MockObject_MockObject $classLoaderMock */ + $classLoaderMock = $this->getMock(ClassLoader::class); + $generator = new ClassLoadingInformationGenerator($classLoaderMock, $this->createPackagesMock(), __DIR__); + $files = $generator->buildAutoloadInformationFiles(); + + $this->assertArrayHasKey('psr-4File', $files); + $this->assertArrayHasKey('classMapFile', $files); + $this->assertContains('\'TYPO3\\\\CMS\\\\TestExtension\\\\\' => array($typo3InstallDir . \'/Fixtures/test_extension/Classes/\')', $files['psr-4File']); + $this->assertContains('$typo3InstallDir . \'/Fixtures/test_extension/Resources/PHP/Test.php\'', $files['classMapFile']); + } + + /** + * @return PackageInterface[] + */ + protected function createPackagesMock() { + $packageStub = $this->getMock(PackageInterface::class); + $packageStub->expects($this->any())->method('getPackagePath')->willReturn(__DIR__ . '/Fixtures/test_extension/'); + $packageStub->expects($this->any())->method('getValueFromComposerManifest')->willReturn( + json_decode(file_get_contents(__DIR__ . '/Fixtures/test_extension/composer.json')) + ); + + return [$packageStub]; + } + } diff --git a/typo3/sysext/core/Tests/Unit/Core/Fixtures/test_extension/Resources/PHP/Test.php b/typo3/sysext/core/Tests/Unit/Core/Fixtures/test_extension/Resources/PHP/Test.php new file mode 100644 index 000000000000..304459ed721c --- /dev/null +++ b/typo3/sysext/core/Tests/Unit/Core/Fixtures/test_extension/Resources/PHP/Test.php @@ -0,0 +1,19 @@ +<?php +namespace TYPO3\CMS\Core\Tests\Unit\Core\Fixtures\test_extension\Resources\PHP; + +/* * + * This script belongs to the TYPO3 Flow framework. * + * * + * It is free software; you can redistribute it and/or modify it under * + * the terms of the GNU Lesser General Public License, either version 3 * + * of the License, or (at your option) any later version. * + * * + * The TYPO3 project - inspiring people to share! * + * */ + +/** + * Class Test + */ +class Test { + +} \ No newline at end of file diff --git a/typo3/sysext/core/Tests/Unit/Core/Fixtures/test_extension/composer.json b/typo3/sysext/core/Tests/Unit/Core/Fixtures/test_extension/composer.json new file mode 100644 index 000000000000..fb6d1c88d458 --- /dev/null +++ b/typo3/sysext/core/Tests/Unit/Core/Fixtures/test_extension/composer.json @@ -0,0 +1,20 @@ +{ + "name": "typo3/cms-core-test", + "type": "typo3-cms-extension", + "description": "TYPO3 Core Test", + "homepage": "https://typo3.org", + "license": ["GPL-2.0+"], + + "require": { + "php" : ">=5.5.0" + }, + "replace": { + "core": "*" + }, + "autoload": { + "psr-4": { + "TYPO3\\CMS\\TestExtension\\": "Classes/" + }, + "classmap": ["Resources/PHP/"] + } +} diff --git a/typo3/sysext/core/Tests/Unit/Core/Fixtures/test_extension/ext_emconf.php b/typo3/sysext/core/Tests/Unit/Core/Fixtures/test_extension/ext_emconf.php new file mode 100644 index 000000000000..29fcff435202 --- /dev/null +++ b/typo3/sysext/core/Tests/Unit/Core/Fixtures/test_extension/ext_emconf.php @@ -0,0 +1,19 @@ +<?php +$EM_CONF[$_EXTKEY] = array( + 'title' => 'TYPO3 Core Test', + 'description' => 'Test Extension', + 'category' => 'be', + 'state' => 'stable', + 'uploadfolder' => 0, + 'createDirs' => '', + 'clearCacheOnLoad' => 0, + 'author' => 'Helmut Hummel', + 'author_email' => 'helmut@typo3.org', + 'author_company' => '', + 'version' => '7.6.0', + 'constraints' => array( + 'depends' => array(), + 'conflicts' => array(), + 'suggests' => array(), + ), +); -- GitLab