[TASK] Add extension precedence 51/39151/11
authorNicole Cordes <typo3@cordes.co>
Fri, 1 May 2015 11:29:12 +0000 (13:29 +0200)
committerHelmut Hummel <helmut.hummel@typo3.org>
Sat, 2 May 2015 20:58:35 +0000 (22:58 +0200)
This patch re-adds the possibility to overwrite system or global
extensions with local ones from typoconf/ext.

Releases: master, 6.2
Resolves: #59147
Change-Id: I1f01677791db26d2be4e70c0a81d96e42ca5147c
Reviewed-on: http://review.typo3.org/39151
Reviewed-by: Markus Klein <markus.klein@typo3.org>
Tested-by: Markus Klein <markus.klein@typo3.org>
Reviewed-by: Helmut Hummel <helmut.hummel@typo3.org>
Tested-by: Helmut Hummel <helmut.hummel@typo3.org>
typo3/sysext/core/Classes/Package/DependencyResolver.php
typo3/sysext/core/Classes/Package/PackageManager.php
typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Package.php
typo3/sysext/core/Tests/Unit/Package/DependencyResolverTest.php

index 424b6cd..b5492e8 100644 (file)
@@ -13,6 +13,7 @@ namespace TYPO3\CMS\Core\Package;
  *
  * The TYPO3 project - inspiring people to share!
  */
+use TYPO3\CMS\Core\Core\Bootstrap;
 
 /**
  * This class takes care about dependencies between packages.
@@ -204,8 +205,8 @@ class DependencyResolver {
                                $rootPackageKeys[] = $packageKey;
                        }
                }
-               $extensionPackageKeys = $this->getPackageKeysInBasePath($packageStateConfiguration, '', array(self::SYSEXT_FOLDER));
-               $frameworkPackageKeys = $this->getPackageKeysInBasePath($packageStateConfiguration, self::SYSEXT_FOLDER);
+               $frameworkPackageKeys = $this->findFrameworkPackages($packageStateConfiguration);
+               $extensionPackageKeys = array_diff(array_keys($packageStateConfiguration), $frameworkPackageKeys);
                foreach ($extensionPackageKeys as $packageKey) {
                        // Remove framework packages from list
                        $packageKeysWithoutFramework = array_diff(
@@ -231,7 +232,7 @@ class DependencyResolver {
         * @return array
         */
        protected function buildDependencyGraph(array $packageStateConfiguration) {
-               $frameworkPackageKeys = $this->getPackageKeysInBasePath($packageStateConfiguration, self::SYSEXT_FOLDER);
+               $frameworkPackageKeys = $this->findFrameworkPackages($packageStateConfiguration);
                $dependencyGraph = $this->buildDependencyGraphForPackages($packageStateConfiguration, $frameworkPackageKeys);
                $packageStateConfiguration = $this->addDependencyToFrameworkToAllExtensions($packageStateConfiguration, $dependencyGraph);
 
@@ -240,7 +241,25 @@ class DependencyResolver {
                return $dependencyGraph;
        }
 
+       /**
+        * @param array $packageStateConfiguration
+        * @return array
+        * @throws \TYPO3\CMS\Core\Exception
+        */
+       protected function findFrameworkPackages(array $packageStateConfiguration) {
+               $frameworkPackageKeys = array();
+               /** @var PackageManager $packageManager */
+               $packageManager = Bootstrap::getInstance()->getEarlyInstance(\TYPO3\Flow\Package\PackageManager::class);
+               foreach ($packageStateConfiguration as $packageKey => $packageConfiguration) {
+                       /** @var Package $package */
+                       $package = $packageManager->getPackage($packageKey);
+                       if ($package->isPartOfFactoryDefault() || $package->isPartOfMinimalUsableSystem() || strpos($packageConfiguration['packagePath'], self::SYSEXT_FOLDER) === 0) {
+                               $frameworkPackageKeys[] = $packageKey;
+                       }
+               }
 
+               return $frameworkPackageKeys;
+       }
 
        /**
         * Get the number of incoming edges in the dependency graph
@@ -260,31 +279,4 @@ class DependencyResolver {
                return $incomingEdgeCount;
        }
 
-       /**
-        * Get packages of specific type
-        *
-        * @param array $packageStateConfiguration
-        * @param string $basePath Base path of package. Empty string for all types
-        * @param array $excludedPaths Array of package base paths to exclude
-        * @return array List of packages
-        */
-       protected function getPackageKeysInBasePath(array $packageStateConfiguration, $basePath, array $excludedPaths = array()) {
-               $packageKeys = array();
-               foreach ($packageStateConfiguration as $packageKey => $package) {
-                       if (($basePath === '' || strpos($package['packagePath'], $basePath) === 0)) {
-                               $isExcluded = FALSE;
-                               foreach ($excludedPaths as $excludedPath) {
-                                       if (strpos($package['packagePath'], $excludedPath) === 0) {
-                                               $isExcluded = TRUE;
-                                               break;
-                                       }
-                               }
-                               if (!$isExcluded) {
-                                       $packageKeys[] = $packageKey;
-                               }
-                       }
-               }
-               return $packageKeys;
-       }
-
 }
index d6a0c1a..f1b1898 100644 (file)
@@ -82,6 +82,8 @@ class PackageManager extends \TYPO3\Flow\Package\PackageManager implements \TYPO
         * Constructor
         */
        public function __construct() {
+               // The order of paths is crucial for allowing overriding of system extension by local extensions.
+               // Pay attention if you change order of the paths here.
                $this->packagesBasePaths = array(
                        'local'     => PATH_typo3conf . 'ext',
                        'global'    => PATH_typo3 . 'ext',
@@ -338,7 +340,8 @@ class PackageManager extends \TYPO3\Flow\Package\PackageManager implements \TYPO
                        $this->scanPackagesInPath($packagesBasePath, $packagePaths);
                }
 
-               foreach ($packagePaths as $packagePath => $composerManifestPath) {
+               foreach ($packagePaths as $composerManifestPath) {
+                       $packagePath = $composerManifestPath;
                        $packagesBasePath = PATH_site;
                        foreach ($this->packagesBasePaths as $basePath) {
                                if (strpos($packagePath, $basePath) === 0) {
@@ -396,8 +399,11 @@ class PackageManager extends \TYPO3\Flow\Package\PackageManager implements \TYPO
                                $filename = $fileInfo->getFilename();
                                if ($filename[0] !== '.') {
                                        $currentPath = \TYPO3\Flow\Utility\Files::getUnixStylePath($fileInfo->getPathName()) . '/';
-                                       if (file_exists($currentPath . 'ext_emconf.php')) {
-                                               $collectedExtensionPaths[$currentPath] = $currentPath;
+                                       // Only add the extension if we have an EMCONF and the extension is not yet registered.
+                                       // This is crucial in order to allow overriding of system extension by local extensions
+                                       // and strongly depends on the order of paths defined in $this->packagesBasePaths.
+                                       if (file_exists($currentPath . 'ext_emconf.php') && !isset($collectedExtensionPaths[$filename])) {
+                                               $collectedExtensionPaths[$filename] = $currentPath;
                                        }
                                }
                        }
index 6a637e2..33c49b6 100644 (file)
@@ -449,7 +449,7 @@ class Package implements PackageInterface {
         * @return array
         */
        public function __sleep() {
-               $properties = get_class_vars(__CLASS__);
+               $properties = get_class_vars(get_class($this));
                unset($properties['packageManager']);
                return array_keys($properties);
        }
index d599230..9c975ae 100644 (file)
@@ -31,15 +31,8 @@ class DependencyResolverTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
         * @dataProvider buildDependencyGraphBuildsCorrectGraphDataProvider
         */
        public function buildDependencyGraphBuildsCorrectGraph(array $unsortedPackageStatesConfiguration, array $frameworkPackageKeys, array $expectedGraph) {
-               $packageKeys = array_keys($unsortedPackageStatesConfiguration);
-
-               $basePathAssignment = array(
-                       array($unsortedPackageStatesConfiguration, '', array(DependencyResolver::SYSEXT_FOLDER), array_diff($packageKeys, $frameworkPackageKeys)),
-                       array($unsortedPackageStatesConfiguration, DependencyResolver::SYSEXT_FOLDER, array(), $frameworkPackageKeys),
-               );
-
-               $dependencyResolver = $this->getAccessibleMock(DependencyResolver::class, array('getPackageKeysInBasePath'));
-               $dependencyResolver->expects($this->any())->method('getPackageKeysInBasePath')->will($this->returnValueMap($basePathAssignment));
+               $dependencyResolver = $this->getAccessibleMock(DependencyResolver::class, array('findFrameworkPackages'));
+               $dependencyResolver->expects($this->any())->method('findFrameworkPackages')->willReturn($frameworkPackageKeys);
                $dependencyGraph = $dependencyResolver->_call('buildDependencyGraph', $unsortedPackageStatesConfiguration);
 
                $this->assertEquals($expectedGraph, $dependencyGraph);
@@ -50,15 +43,8 @@ class DependencyResolverTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
         * @dataProvider packageSortingDataProvider
         */
        public function sortPackageStatesConfigurationByDependencyMakesSureThatDependantPackagesAreStandingBeforeAPackageInTheInternalPackagesAndPackagesConfigurationArrays($unsortedPackageStatesConfiguration, $frameworkPackageKeys, $expectedSortedPackageStatesConfiguration) {
-               $packageKeys = array_keys($unsortedPackageStatesConfiguration);
-
-               $basePathAssignment = array(
-                       array($unsortedPackageStatesConfiguration, '', array(DependencyResolver::SYSEXT_FOLDER), array_diff($packageKeys, $frameworkPackageKeys)),
-                       array($unsortedPackageStatesConfiguration, DependencyResolver::SYSEXT_FOLDER, array(), $frameworkPackageKeys),
-               );
-
-               $dependencyResolver = $this->getAccessibleMock(DependencyResolver::class, array('getPackageKeysInBasePath'));
-               $dependencyResolver->expects($this->any())->method('getPackageKeysInBasePath')->will($this->returnValueMap($basePathAssignment));
+               $dependencyResolver = $this->getAccessibleMock(DependencyResolver::class, array('findFrameworkPackages'));
+               $dependencyResolver->expects($this->any())->method('findFrameworkPackages')->willReturn($frameworkPackageKeys);
                $sortedPackageStatesConfiguration = $dependencyResolver->_call('sortPackageStatesConfigurationByDependency', $unsortedPackageStatesConfiguration);
 
                $this->assertEquals($expectedSortedPackageStatesConfiguration, $sortedPackageStatesConfiguration, 'The package states configurations have not been ordered according to their dependencies!');
@@ -69,7 +55,8 @@ class DependencyResolverTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
         * @dataProvider buildDependencyGraphForPackagesBuildsCorrectGraphDataProvider
         */
        public function buildDependencyGraphForPackagesBuildsCorrectGraph($packages, $expectedGraph) {
-               $dependencyResolver = $this->getAccessibleMock(DependencyResolver::class, array('dummy'));
+               $dependencyResolver = $this->getAccessibleMock(DependencyResolver::class, array('findFrameworkPackages'));
+               $dependencyResolver->expects($this->any())->method('findFrameworkPackages')->willReturn(array());
                $dependencyGraph = $dependencyResolver->_call('buildDependencyGraphForPackages', $packages, array_keys($packages));
 
                $this->assertEquals($expectedGraph, $dependencyGraph);
@@ -91,15 +78,8 @@ class DependencyResolverTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                        ),
                );
 
-               $packageKeys = array_keys($unsortedPackageStatesConfiguration);
-
-               $basePathAssignment = array(
-                       array($unsortedPackageStatesConfiguration, '', array(DependencyResolver::SYSEXT_FOLDER), $packageKeys),
-                       array($unsortedPackageStatesConfiguration, DependencyResolver::SYSEXT_FOLDER, array(), array()),
-               );
-
-               $dependencyResolver = $this->getAccessibleMock(DependencyResolver::class, array('getActivePackageKeysOfType'));
-               $dependencyResolver->expects($this->any())->method('getActivePackageKeysOfType')->will($this->returnValueMap($basePathAssignment));
+               $dependencyResolver = $this->getAccessibleMock(DependencyResolver::class, array('findFrameworkPackages'));
+               $dependencyResolver->expects($this->any())->method('findFrameworkPackages')->willReturn(array());
                $dependencyResolver->_call('sortPackageStatesConfigurationByDependency', $unsortedPackageStatesConfiguration);
        }